about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-17 23:09:49 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-17 23:09:49 +0000
commit57c47659930c5e01ae5d3e8cef51c06d28d20508 (patch)
treee67a413ec70dbf4562178ad3603619f7b2b825c2
parent253a5639ae170a16055e2c8a76d12335342f6d51 (diff)
downloadzcatch-57c47659930c5e01ae5d3e8cef51c06d28d20508.tar.gz
zcatch-57c47659930c5e01ae5d3e8cef51c06d28d20508.zip
editor update and other stuff
-rw-r--r--src/engine/e_interface.h1128
-rw-r--r--src/game/client/gc_menu.cpp9
-rw-r--r--src/game/client/gc_menu.h13
-rw-r--r--src/game/client/gc_render.cpp19
-rw-r--r--src/game/client/gc_render.h5
-rw-r--r--src/game/client/gc_render_map.cpp104
-rw-r--r--src/game/editor/ed_editor.cpp899
-rw-r--r--src/game/editor/ed_editor.hpp106
-rw-r--r--src/game/editor/ed_io.cpp471
-rw-r--r--src/game/editor/ed_layer_quads.cpp141
-rw-r--r--src/game/editor/ed_layer_tiles.cpp2
-rw-r--r--src/game/editor/ed_popups.cpp385
-rw-r--r--src/game/g_game.h44
-rw-r--r--src/game/g_mapitems.h25
-rw-r--r--src/game/server/gs_common.h4
-rw-r--r--src/game/server/gs_game.cpp35
-rw-r--r--src/game/server/gs_game_ctf.cpp2
-rw-r--r--src/game/server/gs_game_dm.cpp34
-rw-r--r--src/game/server/gs_game_tdm.cpp4
19 files changed, 2321 insertions, 1109 deletions
diff --git a/src/engine/e_interface.h b/src/engine/e_interface.h
index 701930ee..534c29e1 100644
--- a/src/engine/e_interface.h
+++ b/src/engine/e_interface.h
@@ -110,40 +110,71 @@ void perf_start(PERFORMACE_INFO *info);
 void perf_end();
 void perf_dump();
 
-/*
-struct rect
-{
-    float x, y, w, h;
-};
+int gfx_init();
+void gfx_shutdown();
+void gfx_swap();
+int gfx_window_active();
+int gfx_window_open();
 
-struct rect *ui_screen();
-typedef void (*rect_fun)(const struct rect *r);
-void ui_foreach_rect(rect_fun fun);
-void ui_scale(float scale);
-void ui_hsplit_t(const struct rect *original, int pixels, struct rect *top, struct rect *bottom);
-void ui_hsplit_b(const struct rect *original, int pixels, struct rect *top, struct rect *bottom);
-void ui_vsplit_l(const struct rect *original, int pixels, struct rect *left, struct rect *right);
-void ui_vsplit_r(const struct rect *original, int pixels, struct rect *left, struct rect *right);
-void ui_margin(const struct rect *original, int pixels, struct rect *new_rect);*/
+void gfx_set_vsync(int val);
+void gfx_mask_op(int mask, int write);
+void gfx_clear_mask(int fill);
+
+int snd_init();
+int snd_shutdown();
+int snd_update();
+
+int map_load(const char *mapname);
+void map_unload();
+
+void map_set(void *m);
 
-/* image loaders */
-int gfx_load_png(IMAGE_INFO *img, const char *filename);
 
 /*
 	Group: Graphics
 */
 
-int gfx_init();
-void gfx_shutdown();
-void gfx_swap();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float gfx_screenaspect();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int gfx_get_video_modes(VIDEO_MODE *list, int maxcount);
-void gfx_set_vsync(int val);
 
-int gfx_window_active();
-int gfx_window_open();
+/* image loaders */
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
+int gfx_load_png(IMAGE_INFO *img, const char *filename);
 
 /* textures */
 /*
@@ -170,6 +201,8 @@ int gfx_load_texture(const char *filename, int store_format);
 		w - Width of the texture.
 		h - Height of the texture.
 		data - Pointer to the pixel data.
+		format - Format of the pixel data.
+		store_format - The format to store the texture on the graphics card.
 	
 	Returns:
 		An ID to the texture. -1 on failure.
@@ -182,7 +215,6 @@ int gfx_load_texture(const char *filename, int store_format);
 		<gfx_unload_texture>
 */
 int gfx_load_texture_raw(int w, int h, int format, const void *data, int store_format);
-/*int gfx_load_mip_texture_raw(int w, int h, int format, const void *data);*/
 
 /*
 	Function: gfx_texture_set
@@ -208,6 +240,17 @@ void gfx_texture_set(int id);
 */
 int gfx_unload_texture(int id);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_clear(float r, float g, float b);
 
 /*
@@ -387,29 +430,115 @@ void gfx_quads_drawTL(float x, float y, float width, float height);
 */
 void gfx_quads_draw(float x, float y, float w, float h);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_quads_draw_freeform(
 	float x0, float y0,
 	float x1, float y1,
 	float x2, float y2,
 	float x3, float y3);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_quads_text(float x, float y, float size, const char *text);
 
 /* sound (client) */
-int snd_init();
-int snd_update();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void snd_set_channel(int cid, float vol, float pan);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int snd_load_wv(const char *filename);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int snd_play_at(int cid, int sid, int flags, float x, float y);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int snd_play(int cid, int sid, int flags);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void snd_stop(int id);
-void snd_set_listener_pos(float x, float y);
 
-int snd_shutdown();
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
+void snd_set_listener_pos(float x, float y);
 
 /*
 	Group: Input
@@ -425,6 +554,17 @@ int snd_shutdown();
 */
 void inp_mouse_relative(int *x, int *y);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_mouse_scroll();
 
 /*
@@ -446,9 +586,6 @@ int inp_key_pressed(int key);
 	Group: Map
 */
 
-int map_load(const char *mapname);
-void map_unload();
-
 /*
 	Function: map_is_loaded
 		Checks if a map is loaded.
@@ -519,6 +656,18 @@ void map_get_type(int type, int *start, int *num);
 		A pointer to the raw data, otherwise 0.
 */
 void *map_get_data(int index);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void *map_get_data_swapped(int index);
 
 /*
@@ -745,36 +894,267 @@ void modc_render();
 void modc_statechange(int new_state, int old_state);
 
 /* undocumented callbacks */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void modc_connected();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void modc_message(int msg);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void modc_predict();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int modc_snap_input(int *data);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void mods_message(int msg, int client_id);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void mods_connected(int client_id);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
 
+	See Also:
+		<other_func>
+*/
 const char *modc_net_version();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *mods_net_version();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *mods_version();
 
 /* server */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int server_getclientinfo(int client_id, CLIENT_INFO *info);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *server_clientname(int client_id);
 
 /* grabs the latest input for the client. not withholding anything */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int *server_latestinput(int client_id, int *size);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void server_setclientname(int client_id, const char *name);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void server_setclientscore(int client_id, int score);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void server_setbrowseinfo(int game_type, int progression);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void server_kick(int client_id, const char *reason);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int server_tick();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int server_tickspeed();
 
 /* input */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_was_pressed(int key);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_down(int key);
 
 
@@ -785,21 +1165,126 @@ typedef struct
 	int key;
 } INPUTEVENT;
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_num_events();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 INPUTEVENT inp_get_event(int index);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void inp_clear_events();
 
 void inp_update();
 void inp_init();
 void inp_mouse_mode_absolute();
 void inp_mouse_mode_relative();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_mouse_doubleclick();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_presses(int key);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_releases(int key);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_state(int key);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *inp_key_name(int k);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int inp_key_code(const char *key_name);
 
 /* message packing */
@@ -809,10 +1294,70 @@ enum
 };
 
 void msg_pack_start_system(int msg, int flags);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void msg_pack_start(int msg, int flags);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void msg_pack_int(int i);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void msg_pack_string(const char *p, int limit);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void msg_pack_raw(const void *data, int size);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void msg_pack_end();
 
 typedef struct
@@ -827,74 +1372,557 @@ const MSG_INFO *msg_get_info();
 
 /* message unpacking */
 int msg_unpack_start(const void *data, int data_size, int *system);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int msg_unpack_int();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *msg_unpack_string();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const unsigned char *msg_unpack_raw(int size);
 
 /* message sending */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int server_send_msg(int client_id); /* client_id == -1 == broadcast */
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_send_msg();
 
 /* client */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_tick();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_predtick();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float client_intratick(); 
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float client_ticktime();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float client_intrapredtick();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_tickspeed();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float client_frametime();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float client_localtime();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_direct_input(int *input, int size);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_state();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 const char *client_error_string();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int *client_get_input(int tick);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_connection_problems();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_connect(const char *address);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_disconnect();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_quit();
+
 void client_entergame();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_rcon(const char *cmd);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
 
+	See Also:
+		<other_func>
+*/
 void client_serverbrowse_refresh(int lan);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 SERVER_INFO *client_serverbrowse_sorted_get(int index);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_serverbrowse_sorted_num();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 SERVER_INFO *client_serverbrowse_get(int index);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_serverbrowse_num();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int client_serverbrowse_num_requests();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void client_serverbrowse_update();
 
 /* undocumented graphics stuff */
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_text(void *font, float x, float y, float size, const char *text, int max_width);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 float gfx_text_width(void *font, float size, const char *text, int length);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_text_color(float r, float g, float b, float a);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_text_set_default_font(void *font);
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_getscreen(float *tl_x, float *tl_y, float *br_x, float *br_y);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int gfx_memory_usage();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_screenshot();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_lines_begin();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_lines_draw(float x0, float y0, float x1, float y1);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_lines_end();
 
-void gfx_mask_op(int mask, int write);
-void gfx_clear_mask(int fill);
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
 
+	See Also:
+		<other_func>
+*/
 void gfx_clip_enable(int x, int y, int w, int h);
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_clip_disable();
 
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void gfx_quads_setsubset_free(
 	float x0, float y0,
 	float x1, float y1,
@@ -902,12 +1930,46 @@ void gfx_quads_setsubset_free(
 	float x3, float y3);
 
 /* server snap id */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 int snap_new_id();
+
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void snap_free_id(int id);
 
 /* other */
+/*
+	Function: TODO
+	
+	Arguments:
+		arg1 - desc
+	
+	Returns:
+
+	See Also:
+		<other_func>
+*/
 void map_unload_data(int index);
-void map_set(void *m);
+
 
 #ifdef __cplusplus
 }
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index c82f1f6f..09c55d9c 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -70,15 +70,6 @@ enum
 	PAGE_SYSTEM,
 };
 
-static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
-{
-	gfx_texture_set(-1);
-	gfx_quads_begin();
-	gfx_setcolor(color.r, color.g, color.b, color.a);
-	draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners);
-	gfx_quads_end();
-}
-
 static void ui_draw_browse_icon(int what, const RECT *r)
 {
 	gfx_texture_set(data->images[IMAGE_BROWSEICONS].id);
diff --git a/src/game/client/gc_menu.h b/src/game/client/gc_menu.h
index bd6352de..ba12894c 100644
--- a/src/game/client/gc_menu.h
+++ b/src/game/client/gc_menu.h
@@ -1,16 +1,5 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 #ifndef __MENU_H
 #define __MENU_H
-/*
-void draw_image_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra);
-void draw_single_part_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra);
-void draw_menu_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra);
-void draw_teewars_button(void *id, const char *text, int checked, float x, float y, float w, float h, void *extra);
-int ui_do_key_reader(void *id, float x, float y, float w, float h, int key);
-int ui_do_combo_box(void *id, float x, float y, float w, char *lines, int line_count, int selected_index);
-int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int str_size);
-int ui_do_check_box(void *id, float x, float y, float w, float h, int value);
-int do_scroll_bar_horiz(void *id, float x, float y, float width, int steps, int last_index);
-int do_scroll_bar_vert(void *id, float x, float y, float height, int steps, int last_index);
-*/
+
 #endif
diff --git a/src/game/client/gc_render.cpp b/src/game/client/gc_render.cpp
index 9f55ec5f..1999cb0a 100644
--- a/src/game/client/gc_render.cpp
+++ b/src/game/client/gc_render.cpp
@@ -125,6 +125,15 @@ void draw_round_rect(float x, float y, float w, float h, float r)
 	draw_round_rect_ext(x,y,w,h,r,0xf);
 }
 
+void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
+{
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_setcolor(color.r, color.g, color.b, color.a);
+	draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners);
+	gfx_quads_end();
+}
+
 void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos)
 {
 	vec2 direction = dir;
@@ -246,6 +255,14 @@ static void mapscreen_to_group(float center_x, float center_y, MAPITEM_GROUP *gr
 	gfx_mapscreen(points[0], points[1], points[2], points[3]);
 }
 
+static void envelope_eval(float time_offset, int env, float *channels)
+{
+	channels[0] = 0;
+	channels[1] = 0;
+	channels[2] = 0;
+	channels[3] = 0;
+}
+
 void render_layers(float center_x, float center_y, int pass)
 {
 	bool passed_gamelayer = false;
@@ -299,7 +316,7 @@ void render_layers(float center_x, float center_y, int pass)
 					else
 						gfx_texture_set(img_get(qlayer->image));
 					QUAD *quads = (QUAD *)map_get_data_swapped(qlayer->data);
-					render_quads(quads, qlayer->num_quads);
+					render_quads(quads, qlayer->num_quads, envelope_eval);
 				}
 			}
 		}
diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h
index feb04641..5294b89e 100644
--- a/src/game/client/gc_render.h
+++ b/src/game/client/gc_render.h
@@ -4,6 +4,7 @@
 
 #include "../g_vmath.h"
 #include "../g_mapitems.h"
+#include "gc_ui.h"
 
 struct tee_render_info
 {
@@ -30,6 +31,7 @@ void draw_sprite(float x, float y, float size);
 // rects
 void draw_round_rect(float x, float y, float w, float h, float r);
 void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners);
+void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding);
 
 // larger rendering methods
 void menu_render();
@@ -49,7 +51,8 @@ void render_player(
 	const struct obj_player_info *prev_info, const struct obj_player_info *player_info);
 	
 // map render methods (gc_render_map.cpp)
-void render_quads(QUAD *quads, int num_quads);
+void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result);
+void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels));
 void render_tilemap(TILE *tiles, int w, int h, float scale, int flags);
 
 // helpers
diff --git a/src/game/client/gc_render_map.cpp b/src/game/client/gc_render_map.cpp
index 4345aa03..931fdd18 100644
--- a/src/game/client/gc_render_map.cpp
+++ b/src/game/client/gc_render_map.cpp
@@ -3,6 +3,70 @@
 #include "../g_math.h"
 #include "gc_client.h"
 
+void render_eval_envelope(ENVPOINT *points, int num_points, int channels, float time, float *result)
+{
+	if(num_points == 0)
+	{
+		result[0] = 0;
+		result[1] = 0;
+		result[2] = 0;
+		result[3] = 0;
+		return;
+	}
+	
+	if(num_points == 1)
+	{
+		result[0] = fx2f(points[0].values[0]);
+		result[1] = fx2f(points[0].values[1]);
+		result[2] = fx2f(points[0].values[2]);
+		result[3] = fx2f(points[0].values[3]);
+		return;
+	}
+	
+	time = fmod(time, points[num_points-1].time/1000.0f)*1000.0f;
+	for(int i = 0; i < num_points-1; i++)
+	{
+		if(time >= points[i].time && time <= points[i+1].time)
+		{
+			float delta = points[i+1].time-points[i].time;
+			float a = (time-points[i].time)/delta;
+
+
+			if(points[i].curvetype == CURVETYPE_SMOOTH)
+				a = -2*a*a*a + 3*a*a; // second hermite basis
+			else if(points[i].curvetype == CURVETYPE_SLOW)
+				a = a*a*a;
+			else if(points[i].curvetype == CURVETYPE_FAST)
+			{
+				a = 1-a;
+				a = 1-a*a*a;
+			}
+			else if (points[i].curvetype == CURVETYPE_STEP)
+				a = 0;
+			else
+			{
+				// linear
+			}
+					
+			for(int c = 0; c < channels; c++)
+			{
+				float v0 = fx2f(points[i].values[c]);
+				float v1 = fx2f(points[i+1].values[c]);
+				result[c] = v0 + (v1-v0) * a;
+			}
+			
+			return;
+		}
+	}
+	
+	result[0] = fx2f(points[num_points-1].values[0]);
+	result[1] = fx2f(points[num_points-1].values[1]);
+	result[2] = fx2f(points[num_points-1].values[2]);
+	result[3] = fx2f(points[num_points-1].values[3]);
+	return;
+}
+
+
 static void rotate(POINT *center, POINT *point, float rotation)
 {
 	int x = point->x - center->x;
@@ -11,7 +75,7 @@ static void rotate(POINT *center, POINT *point, float rotation)
 	point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
 }
 
-void render_quads(QUAD *quads, int num_quads)
+void render_quads(QUAD *quads, int num_quads, void (*eval)(float time_offset, int env, float *channels))
 {
 	gfx_quads_begin();
 	float conv = 1/255.0f;
@@ -31,29 +95,25 @@ void render_quads(QUAD *quads, int num_quads)
 		float offset_y = 0;
 		float rot = 0;
 		
-		/*
 		// TODO: fix this
-		if(editor.animate)
+		if(q->pos_env >= 0)
 		{
-			if(q->pos_env >= 0 && q->pos_env < editor.map.envelopes.len())
-			{
-				ENVELOPE *e = editor.map.envelopes[q->pos_env];
-				float t = editor.animate_time+q->pos_env_offset/1000.0f;
-				offset_x = e->eval(t, 0);
-				offset_y = e->eval(t, 1);
-				rot = e->eval(t, 2);
-			}
-			
-			if(q->color_env >= 0 && q->color_env < editor.map.envelopes.len())
-			{
-				ENVELOPE *e = editor.map.envelopes[q->color_env];
-				float t = editor.animate_time+q->color_env_offset/1000.0f;
-				r = e->eval(t, 0);
-				g = e->eval(t, 1);
-				b = e->eval(t, 2);
-				a = e->eval(t, 3);
-			}
-		}*/
+			float channels[4];
+			eval(q->pos_env_offset/1000.0f, q->pos_env, channels);
+			offset_x = channels[0];
+			offset_y = channels[1];
+			rot = channels[2];
+		}
+		
+		if(q->color_env >= 0)
+		{
+			float channels[4];
+			eval(q->color_env_offset/1000.0f, q->color_env, channels);
+			r = channels[0];
+			g = channels[1];
+			b = channels[2];
+			a = channels[3];
+		}
 		
 		gfx_setcolorvertex(0, q->colors[0].r*conv*r, q->colors[0].g*conv*g, q->colors[0].b*conv*b, q->colors[0].a*conv*a);
 		gfx_setcolorvertex(1, q->colors[1].r*conv*r, q->colors[1].g*conv*g, q->colors[1].b*conv*b, q->colors[1].a*conv*a);
diff --git a/src/game/editor/ed_editor.cpp b/src/game/editor/ed_editor.cpp
index bf6918fa..6af991b9 100644
--- a/src/game/editor/ed_editor.cpp
+++ b/src/game/editor/ed_editor.cpp
@@ -24,16 +24,7 @@ static int cursor_texture = 0;
 static int entities_texture = 0;
 
 
-// popup menu handling
-static struct
-{
-	RECT rect;
-	void *id;
-	int (*func)(RECT rect);
-	int is_menu;
-	void *extra;
-} ui_popups[8];
-static int ui_num_popups = 0;
+static const void *ui_got_context = 0;
 
 EDITOR editor;
 
@@ -128,17 +119,6 @@ int LAYERGROUP::swap_layers(int index0, int index1)
  OTHER
 *********************************************************/
 
-static int ui_got_context = 0;
-
-static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
-{
-	gfx_texture_set(-1);
-	gfx_quads_begin();
-	gfx_setcolor(color.r, color.g, color.b, color.a);
-	draw_round_rect_ext(r->x,r->y,r->w,r->h,rounding*ui_scale(), corners);
-	gfx_quads_end();
-}
-
 // copied from gc_menu.cpp, should be more generalized
 extern int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, float font_size, bool hidden=false);
 
@@ -260,7 +240,7 @@ int do_editor_button(const void *id, const char *text, int checked, const RECT *
 	if(ui_mouse_inside(r))
 	{
 		if(flags&BUTTON_CONTEXT)
-			ui_got_context = 1;
+			ui_got_context = id;
 		if(tooltip)
 			editor.tooltip = tooltip;
 	}
@@ -280,13 +260,6 @@ static void render_background(RECT view, int texture, float size, float brightne
 	gfx_quads_end();
 }
 
-
-static int selected_layer = 0;
-static int selected_group = 0;
-static int selected_quad = -1;
-int selected_points = 0;
-static int selected_envelope = 0;
-
 static LAYERGROUP brush;
 static LAYER_TILES tileset_picker(16, 16);
 
@@ -346,60 +319,6 @@ static int ui_do_value_selector(void *id, RECT *r, const char *label, int curren
 	return current;
 }
 
-static void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra=0)
-{
-	dbg_msg("", "invoked");
-	ui_popups[ui_num_popups].id = id;
-	ui_popups[ui_num_popups].is_menu = flags;
-	ui_popups[ui_num_popups].rect.x = x;
-	ui_popups[ui_num_popups].rect.y = y;
-	ui_popups[ui_num_popups].rect.w = w;
-	ui_popups[ui_num_popups].rect.h = h;
-	ui_popups[ui_num_popups].func = func;
-	ui_popups[ui_num_popups].extra = extra;
-	ui_num_popups++;
-}
-
-static void ui_do_popup_menu()
-{
-	for(int i = 0; i < ui_num_popups; i++)
-	{
-		bool inside = ui_mouse_inside(&ui_popups[i].rect);
-		ui_set_hot_item(&ui_popups[i].id);
-		
-		if(ui_active_item() == &ui_popups[i].id)
-		{
-			if(!ui_mouse_button(0))
-			{
-				if(!inside)
-					ui_num_popups--;
-				ui_set_active_item(0);
-			}
-		}
-		else if(ui_hot_item() == &ui_popups[i].id)
-		{
-			if(ui_mouse_button(0))
-				ui_set_active_item(&ui_popups[i].id);
-		}
-		
-		int corners = CORNER_ALL;
-		if(ui_popups[i].is_menu)
-			corners = CORNER_R|CORNER_B;
-		
-		RECT r = ui_popups[i].rect;
-		ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f);
-		ui_margin(&r, 1.0f, &r);
-		ui_draw_rect(&r, vec4(0,0,0,0.75f), corners, 3.0f);
-		ui_margin(&r, 4.0f, &r);
-		
-		if(ui_popups[i].func(r))
-			ui_num_popups--;
-			
-		if(inp_key_down(KEY_ESC))
-			ui_num_popups--;
-	}
-}
-
 LAYERGROUP *EDITOR::get_selected_group()
 {
 	if(selected_group >= 0 && selected_group < editor.map.groups.len())
@@ -540,53 +459,6 @@ static void do_toolbar(RECT toolbar)
 				}
 			}
 		}
-
-		ui_vsplit_l(&toolbar, 10.0f, &button, &toolbar);
-		ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar);
-		static int sq_button = 0;
-		if(do_editor_button(&sq_button, "Sq. Quad", editor.get_selected_quad()?0:-1, &button, draw_editor_button, 0, "Squares the current quad"))
-		{
-			QUAD *q = editor.get_selected_quad();
-			if(q)
-			{
-				int top = q->points[0].y;
-				int left = q->points[0].x;
-				int bottom = q->points[0].y;
-				int right = q->points[0].x;
-				
-				for(int k = 1; k < 4; k++)
-				{
-					if(q->points[k].y < top) top = q->points[k].y;
-					if(q->points[k].x < left) left = q->points[k].x;
-					if(q->points[k].y > bottom) bottom = q->points[k].y;
-					if(q->points[k].x > right) right = q->points[k].x;
-				}
-				
-				q->points[0].x = left; q->points[0].y = top;
-				q->points[1].x = right; q->points[1].y = top;
-				q->points[2].x = left; q->points[2].y = bottom;
-				q->points[3].x = right; q->points[3].y = bottom;
-			}
-		}
-
-		/*
-		ui_vsplit_l(&toolbar, 20.0f, &button, &toolbar);
-		ui_vsplit_l(&toolbar, 60.0f, &button, &toolbar);
-		bool in_gamegroup = editor.game_group->layers.find(tlayer) != -1;
-		static int col_button = 0;
-		if(do_editor_button(&col_button, "Make Col", (in_gamegroup&&tlayer)?0:-1, &button, draw_editor_button, 0, "Constructs collision from the current layer"))
-		{
-			LAYER_TILES *gl = editor.game_layer;
-			int w = min(gl->width, tlayer->width);
-			int h = min(gl->height, tlayer->height);
-			dbg_msg("", "w=%d h=%d", w, h);
-			for(int y = 0; y < h; y++)
-				for(int x = 0; x < w; x++)
-				{
-					if(gl->tiles[y*gl->width+x].index <= TILE_SOLID)
-						gl->tiles[y*gl->width+x].index = tlayer->tiles[y*tlayer->width+x].index?TILE_SOLID:TILE_AIR;
-				}
-		}*/
 	}
 }
 
@@ -606,6 +478,7 @@ static void do_quad(QUAD *q, int index)
 		OP_MOVE_ALL,
 		OP_MOVE_PIVOT,
 		OP_ROTATE,
+		OP_CONTEXT_MENU,
 	};
 	
 	// some basic values
@@ -628,7 +501,7 @@ static void do_quad(QUAD *q, int index)
 		ui_set_hot_item(id);
 
 	// draw selection background	
-	if(selected_quad == index)
+	if(editor.selected_quad == index)
 	{
 		gfx_setcolor(0,0,0,1);
 		gfx_quads_draw(center_x, center_y, 7.0f, 7.0f);
@@ -664,17 +537,33 @@ static void do_quad(QUAD *q, int index)
 		last_wx = wx;
 		last_wy = wy;
 		
-		if(!ui_mouse_button(0))
+		if(operation == OP_CONTEXT_MENU)
 		{
-			editor.lock_mouse = false;
-			operation = OP_NONE;
-			ui_set_active_item(0);
+			if(!ui_mouse_button(1))
+			{
+				static int quad_popup_id = 0;
+				ui_invoke_popup_menu(&quad_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 150, popup_quad);
+				editor.lock_mouse = false;
+				operation = OP_NONE;
+				ui_set_active_item(0);
+			}
 		}
+		else
+		{
+			if(!ui_mouse_button(0))
+			{
+				editor.lock_mouse = false;
+				operation = OP_NONE;
+				ui_set_active_item(0);
+			}
+		}			
 
 		gfx_setcolor(1,1,1,1);
 	}
 	else if(ui_hot_item() == id)
 	{
+		ui_got_context = id;
+		
 		gfx_setcolor(1,1,1,1);
 		editor.tooltip = "Left mouse button to move. Hold shift to move pivot. Hold ctrl to rotate";
 		
@@ -696,11 +585,17 @@ static void do_quad(QUAD *q, int index)
 				operation = OP_MOVE_ALL;
 				
 			ui_set_active_item(id);
-			selected_quad = index;
-			editor.props = PROPS_QUAD;
+			editor.selected_quad = index;
 			last_wx = wx;
 			last_wy = wy;
 		}
+		
+		if(ui_mouse_button(1))
+		{
+			editor.selected_quad = index;
+			operation = OP_CONTEXT_MENU;
+			ui_set_active_item(id);
+		}
 	}
 	else
 		gfx_setcolor(0,1,0,1);
@@ -724,7 +619,7 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 		ui_set_hot_item(id);
 
 	// draw selection background	
-	if(selected_quad == quad_index && selected_points&(1<<v))
+	if(editor.selected_quad == quad_index && editor.selected_points&(1<<v))
 	{
 		gfx_setcolor(0,0,0,1);
 		gfx_quads_draw(px, py, 7.0f, 7.0f);
@@ -734,7 +629,8 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 	{
 		OP_NONE=0,
 		OP_MOVEPOINT,
-		OP_MOVEUV
+		OP_MOVEUV,
+		OP_CONTEXT_MENU
 	};
 	
 	static bool moved;
@@ -755,7 +651,7 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 			if(operation == OP_MOVEPOINT)
 			{
 				for(int m = 0; m < 4; m++)
-					if(selected_points&(1<<m))
+					if(editor.selected_points&(1<<m))
 					{
 						q->points[m].x += f2fx(dx);
 						q->points[m].y += f2fx(dy);
@@ -764,7 +660,7 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 			else if(operation == OP_MOVEUV)
 			{
 				for(int m = 0; m < 4; m++)
-					if(selected_points&(1<<m))
+					if(editor.selected_points&(1<<m))
 					{
 						q->texcoords[m].x += f2fx(dx*0.001f);
 						q->texcoords[m].y += f2fx(dy*0.001f);
@@ -772,25 +668,37 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 			}
 		}
 		
-		if(!ui_mouse_button(0))
+		if(operation == OP_CONTEXT_MENU)
 		{
-			if(!moved)
+			if(!ui_mouse_button(1))
 			{
-				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
-					selected_points ^= 1<<v;
-				else
-					selected_points = 1<<v;
-					
-				editor.props = PROPS_QUAD_POINT;
+				static int point_popup_id = 0;
+				ui_invoke_popup_menu(&point_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 150, popup_point);
+				ui_set_active_item(0);
+			}
+		}
+		else
+		{
+			if(!ui_mouse_button(0))
+			{
+				if(!moved)
+				{
+					if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
+						editor.selected_points ^= 1<<v;
+					else
+						editor.selected_points = 1<<v;
+				}
+				editor.lock_mouse = false;
+				ui_set_active_item(0);
 			}
-			editor.lock_mouse = false;
-			ui_set_active_item(0);
 		}
 
 		gfx_setcolor(1,1,1,1);
 	}
 	else if(ui_hot_item() == id)
 	{
+		ui_got_context = id;
+		
 		gfx_setcolor(1,1,1,1);
 		editor.tooltip = "Left mouse button to move. Hold shift to move the texture.";
 		
@@ -806,17 +714,22 @@ static void do_quad_point(QUAD *q, int quad_index, int v)
 			else
 				operation = OP_MOVEPOINT;
 				
-			if(!(selected_points&(1<<v)))
+			if(!(editor.selected_points&(1<<v)))
 			{
 				if(inp_key_pressed(KEY_LSHIFT) || inp_key_pressed(KEY_RSHIFT))
-					selected_points |= 1<<v;
+					editor.selected_points |= 1<<v;
 				else
-					selected_points = 1<<v;
+					editor.selected_points = 1<<v;
 				moved = true;
 			}
 															
-			editor.props = PROPS_QUAD_POINT;
-			selected_quad = quad_index;
+			editor.selected_quad = quad_index;
+		}
+		else if(ui_mouse_button(1))
+		{
+			operation = OP_CONTEXT_MENU;
+			editor.selected_quad = quad_index;
+			ui_set_active_item(id);
 		}
 	}
 	else
@@ -1015,7 +928,7 @@ static void do_map_editor(RECT view, RECT toolbar)
 					}
 					else
 					{
-						editor.map.groups[selected_group]->mapscreen();
+						//editor.map.groups[selected_group]->mapscreen();
 						for(int k = 0; k < num_edit_layers; k++)
 							edit_layers[k]->brush_selecting(r);
 						gfx_mapscreen(ui_screen()->x, ui_screen()->y, ui_screen()->w, ui_screen()->h);
@@ -1180,13 +1093,13 @@ static void do_map_editor(RECT view, RECT toolbar)
 			mem_copy(last_points, points, sizeof(points));
 		}
 
-		if(0)
+		if(1)
 		{
-			gfx_setcolor(1.0f,0,0,1);		
-			for(int i = 0; i < 4; i++)
+			gfx_setcolor(1,0,0,1);
+			for(int i = 0; i < 2; i++)
 			{
 				float points[4];
-				float aspects[] = {4.0f/3.0f, 5.0f/4.0f, 16.0f/10.0f, 16.0f/9.0f};
+				float aspects[] = {4.0f/3.0f, 16.0f/10.0f, 5.0f/4.0f, 16.0f/9.0f};
 				float aspect = aspects[i];
 				
 				mapscreen_to_world(
@@ -1203,6 +1116,7 @@ static void do_map_editor(RECT view, RECT toolbar)
 				gfx_lines_draw(r.x+r.w, r.y, r.x+r.w, r.y+r.h);
 				gfx_lines_draw(r.x+r.w, r.y+r.h, r.x, r.y+r.h);
 				gfx_lines_draw(r.x, r.y+r.h, r.x, r.y);
+				gfx_setcolor(0,1,0,1);
 			}
 		}
 			
@@ -1213,50 +1127,6 @@ static void do_map_editor(RECT view, RECT toolbar)
 	ui_clip_disable();
 }
 
-static int select_image_selected = -100;
-static int select_image_current = -100;
-
-static int popup_select_image(RECT view)
-{
-	RECT buttonbar, imageview;
-	ui_vsplit_l(&view, 80.0f, &buttonbar, &view);
-	ui_margin(&view, 10.0f, &imageview);
-	
-	int show_image = select_image_current;
-	
-	for(int i = -1; i < editor.map.images.len(); i++)
-	{
-		RECT button;
-		ui_hsplit_t(&buttonbar, 12.0f, &button, &buttonbar);
-		ui_hsplit_t(&buttonbar, 2.0f, 0, &buttonbar);
-		
-		if(ui_mouse_inside(&button))
-			show_image = i;
-			
-		if(i == -1)
-		{
-			if(do_editor_button(&editor.map.images[i], "None", i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
-				select_image_selected = -1;
-		}
-		else
-		{
-			char buf[64];
-			sprintf(buf, "%d", i);
-			if(do_editor_button(&editor.map.images[i], buf, i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
-				select_image_selected = i;
-		}
-	}
-	
-	if(show_image >= 0 && show_image < editor.map.images.len())
-		gfx_texture_set(editor.map.images[show_image]->tex_id);
-	else
-		gfx_texture_set(-1);
-	gfx_quads_begin();
-	gfx_quads_drawTL(imageview.x, imageview.y, imageview.w, imageview.h);
-	gfx_quads_end();
-
-	return 0;
-}
 
 int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val)
 {
@@ -1335,20 +1205,20 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val
 		}
 		else if(props[i].type == PROPTYPE_IMAGE)
 		{
-			if(do_editor_button(&ids[i], "Choose", 0, &shifter, draw_editor_button, 0, 0))
-			{
-				static int select_image_popup_id = 0;
-				select_image_selected = -100;
-				select_image_current = props[i].value;
-				ui_invoke_popup_menu(&select_image_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 400, 300, popup_select_image);
-			}
+			char buf[64];
+			if(props[i].value < 0)
+				strcpy(buf, "None");
+			else
+				sprintf(buf, "%d",  props[i].value);
 			
-			if(select_image_selected != -100)
+			if(do_editor_button(&ids[i], buf, 0, &shifter, draw_editor_button, 0, 0))
+				popup_select_image_invoke(props[i].value, ui_mouse_x(), ui_mouse_y());
+			
+			int r = popup_select_image_result();
+			if(r >= -1)
 			{
-				*new_val = select_image_selected;
+				*new_val = r;
 				change = i;
-				select_image_current = select_image_selected;
-				select_image_selected = -100;
 			}
 		}
 	}
@@ -1356,140 +1226,6 @@ int EDITOR::do_properties(RECT *toolbox, PROPERTY *props, int *ids, int *new_val
 	return change;
 }
 
-static int popup_group(RECT view)
-{
-	// remove group button
-	RECT button;
-	ui_hsplit_b(&view, 12.0f, &view, &button);
-	static int delete_button = 0;
-	if(do_editor_button(&delete_button, "Delete Group", 0, &button, draw_editor_button, 0, "Delete group"))
-	{
-		editor.map.delete_group(selected_group);
-		return 1;
-	}
-
-	// new tile layer
-	ui_hsplit_b(&view, 10.0f, &view, &button);
-	ui_hsplit_b(&view, 12.0f, &view, &button);
-	static int new_quad_layer_button = 0;
-	if(do_editor_button(&new_quad_layer_button, "Add Quads Layer", 0, &button, draw_editor_button, 0, "Creates a new quad layer"))
-	{
-		LAYER *l = new LAYER_QUADS;
-		editor.map.groups[selected_group]->add_layer(l);
-		selected_layer = editor.map.groups[selected_group]->layers.len()-1;
-		editor.props = PROPS_LAYER;
-		return 1;
-	}
-
-	// new quad layer
-	ui_hsplit_b(&view, 5.0f, &view, &button);
-	ui_hsplit_b(&view, 12.0f, &view, &button);
-	static int new_tile_layer_button = 0;
-	if(do_editor_button(&new_tile_layer_button, "Add Tile Layer", 0, &button, draw_editor_button, 0, "Creates a new tile layer"))
-	{
-		LAYER *l = new LAYER_TILES(50, 50);
-		editor.map.groups[selected_group]->add_layer(l);
-		selected_layer = editor.map.groups[selected_group]->layers.len()-1;
-		editor.props = PROPS_LAYER;
-		return 1;
-	}
-	
-	enum
-	{
-		PROP_ORDER=0,
-		PROP_POS_X,
-		PROP_POS_Y,
-		PROP_PARA_X,
-		PROP_PARA_Y,
-		NUM_PROPS,
-	};
-	
-	PROPERTY props[] = {
-		{"Order", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
-		{"Pos X", -editor.map.groups[selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-		{"Pos Y", -editor.map.groups[selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-		{"Para X", editor.map.groups[selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-		{"Para Y", editor.map.groups[selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-		{0},
-	};
-	
-	static int ids[NUM_PROPS] = {0};
-	int new_val = 0;
-	
-	// cut the properties that isn't needed
-	if(editor.get_selected_group()->game_group)
-		props[PROP_POS_X].name = 0;
-		
-	int prop = editor.do_properties(&view, props, ids, &new_val);
-	if(prop == PROP_ORDER)
-		selected_group = editor.map.swap_groups(selected_group, new_val);
-		
-	// these can not be changed on the game group
-	if(!editor.get_selected_group()->game_group)
-	{
-		if(prop == PROP_PARA_X)
-			editor.map.groups[selected_group]->parallax_x = new_val;
-		else if(prop == PROP_PARA_Y)
-			editor.map.groups[selected_group]->parallax_y = new_val;
-		else if(prop == PROP_POS_X)
-			editor.map.groups[selected_group]->offset_x = -new_val;
-		else if(prop == PROP_POS_Y)
-			editor.map.groups[selected_group]->offset_y = -new_val;
-	}
-	
-	return 0;
-}
-
-static int popup_layer(RECT view)
-{
-	// remove layer button
-	RECT button;
-	ui_hsplit_b(&view, 12.0f, &view, &button);
-	static int delete_button = 0;
-	if(do_editor_button(&delete_button, "Delete Layer", 0, &button, draw_editor_button, 0, "Deletes the layer"))
-	{
-		editor.map.groups[selected_group]->delete_layer(selected_layer);
-		return 1;
-	}
-
-	ui_hsplit_b(&view, 10.0f, &view, 0);
-	
-	LAYERGROUP *current_group = editor.map.groups[selected_group];
-	LAYER *current_layer = editor.get_selected_layer(0);
-	
-	enum
-	{
-		PROP_GROUP=0,
-		PROP_ORDER,
-		NUM_PROPS,
-	};
-	
-	PROPERTY props[] = {
-		{"Group", selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
-		{"Order", selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()},
-		{0},
-	};
-	
-	static int ids[NUM_PROPS] = {0};
-	int new_val = 0;
-	int prop = editor.do_properties(&view, props, ids, &new_val);		
-	
-	if(prop == PROP_ORDER)
-		selected_layer = current_group->swap_layers(selected_layer, new_val);
-	else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME)
-	{
-		if(new_val >= 0 && new_val < editor.map.groups.len())
-		{
-			current_group->layers.remove(current_layer);
-			editor.map.groups[new_val]->layers.add(current_layer);
-			selected_group = new_val;
-			selected_layer = editor.map.groups[new_val]->layers.len()-1;
-		}
-	}
-		
-	return current_layer->render_properties(&view);
-}
-
 static void render_layers(RECT toolbox, RECT toolbar, RECT view)
 {
 	RECT layersbox = toolbox;
@@ -1504,10 +1240,10 @@ static void render_layers(RECT toolbox, RECT toolbar, RECT view)
 
 	int valid_group = 0;
 	int valid_layer = 0;
-	if(selected_group >= 0 && selected_group < editor.map.groups.len())
+	if(editor.selected_group >= 0 && editor.selected_group < editor.map.groups.len())
 		valid_group = 1;
 
-	if(valid_group && selected_layer >= 0 && selected_layer < editor.map.groups[selected_group]->layers.len())
+	if(valid_group && editor.selected_layer >= 0 && editor.selected_layer < editor.map.groups[editor.selected_group]->layers.len())
 		valid_layer = 1;
 		
 	// render layers	
@@ -1521,12 +1257,11 @@ static void render_layers(RECT toolbox, RECT toolbar, RECT view)
 				editor.map.groups[g]->visible = !editor.map.groups[g]->visible;
 
 			sprintf(buf, "#%d %s", g, editor.map.groups[g]->name);
-			if(int result = do_editor_button(&editor.map.groups[g], buf, g==selected_group, &slot, draw_editor_button_r,
+			if(int result = do_editor_button(&editor.map.groups[g], buf, g==editor.selected_group, &slot, draw_editor_button_r,
 				BUTTON_CONTEXT, "Select group. Right click for properties."))
 			{
-				selected_group = g;
-				selected_layer = 0;
-				editor.props = PROPS_GROUP;
+				editor.selected_group = g;
+				editor.selected_layer = 0;
 				
 				static int group_popup_id = 0;
 				if(result == 2)
@@ -1547,12 +1282,11 @@ static void render_layers(RECT toolbox, RECT toolbar, RECT view)
 					editor.map.groups[g]->layers[i]->visible = !editor.map.groups[g]->layers[i]->visible;
 
 				sprintf(buf, "#%d %s ", i, editor.map.groups[g]->layers[i]->type_name);
-				if(int result = do_editor_button(editor.map.groups[g]->layers[i], buf, g==selected_group&&i==selected_layer, &button, draw_editor_button_r,
+				if(int result = do_editor_button(editor.map.groups[g]->layers[i], buf, g==editor.selected_group&&i==editor.selected_layer, &button, draw_editor_button_r,
 					BUTTON_CONTEXT, "Select layer. Right click for properties."))
 				{
-					selected_layer = i;
-					selected_group = g;
-					editor.props = PROPS_LAYER;
+					editor.selected_layer = i;
+					editor.selected_group = g;
 					static int layer_popup_id = 0;
 					if(result == 2)
 						ui_invoke_popup_menu(&layer_popup_id, 0, ui_mouse_x(), ui_mouse_y(), 120, 130, popup_layer);
@@ -1573,8 +1307,7 @@ static void render_layers(RECT toolbox, RECT toolbar, RECT view)
 		if(do_editor_button(&new_group_button, "Add Group", 0, &slot, draw_editor_button, 0, "Adds a new group"))
 		{
 			editor.map.new_group();
-			selected_group = editor.map.groups.len()-1;
-			editor.props = PROPS_GROUP;
+			editor.selected_group = editor.map.groups.len()-1;
 		}
 	}
 
@@ -1794,12 +1527,12 @@ static void render_modebar(RECT view)
 	{
 		ui_vsplit_l(&view, 40.0f, &button, &view);
 		static int tile_button = 0;
-		if(do_editor_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, 0, "Switch to edit layers"))
+		if(do_editor_button(&tile_button, "Layers", editor.mode == MODE_LAYERS, &button, draw_editor_button_m, 0, "Switch to edit layers."))
 			editor.mode = MODE_LAYERS;
 
 		ui_vsplit_l(&view, 40.0f, &button, &view);
 		static int img_button = 0;
-		if(do_editor_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, 0, "Switch to manage images"))
+		if(do_editor_button(&img_button, "Images", editor.mode == MODE_IMAGES, &button, draw_editor_button_r, 0, "Switch to manage images."))
 			editor.mode = MODE_IMAGES;
 	}
 
@@ -1814,21 +1547,30 @@ static void render_statusbar(RECT view)
 	RECT button;
 	ui_vsplit_r(&view, 60.0f, &view, &button);
 	static int envelope_button = 0;
-	if(do_editor_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, 0, "Toggles the envelope editor"))
+	if(do_editor_button(&envelope_button, "Envelopes", editor.show_envelope_editor, &button, draw_editor_button, 0, "Toggles the envelope editor."))
 		editor.show_envelope_editor = (editor.show_envelope_editor+1)%4;
 	
 	if(editor.tooltip)
-		ui_do_label(&view, editor.tooltip, 10.0f, -1, -1);
+	{
+		if(ui_got_context == ui_hot_item())
+		{
+			char buf[512];
+			sprintf(buf, "%s Right click for context menu.", editor.tooltip);
+			ui_do_label(&view, buf, 10.0f, -1, -1);
+		}
+		else
+			ui_do_label(&view, editor.tooltip, 10.0f, -1, -1);
+	}
 }
 
 static void render_envelopeeditor(RECT view)
 {
-	if(selected_envelope < 0) selected_envelope = 0;
-	if(selected_envelope >= editor.map.envelopes.len()) selected_envelope--;
+	if(editor.selected_envelope < 0) editor.selected_envelope = 0;
+	if(editor.selected_envelope >= editor.map.envelopes.len()) editor.selected_envelope--;
 
 	ENVELOPE *envelope = 0;
-	if(selected_envelope >= 0 && selected_envelope < editor.map.envelopes.len())
-		envelope = editor.map.envelopes[selected_envelope];
+	if(editor.selected_envelope >= 0 && editor.selected_envelope < editor.map.envelopes.len())
+		envelope = editor.map.envelopes[editor.selected_envelope];
 
 	bool show_colorbar = false;
 	if(envelope && envelope->channels == 4)
@@ -1884,17 +1626,17 @@ static void render_envelopeeditor(RECT view)
 		ui_vsplit_r(&shifter, 15.0f, &shifter, &inc);
 		ui_vsplit_l(&shifter, 15.0f, &dec, &shifter);
 		char buf[512];
-		sprintf(buf, "%d/%d", selected_envelope+1, editor.map.envelopes.len());
+		sprintf(buf, "%d/%d", editor.selected_envelope+1, editor.map.envelopes.len());
 		ui_draw_rect(&shifter, vec4(1,1,1,0.5f), 0, 0.0f);
 		ui_do_label(&shifter, buf, 10.0f, 0, -1);
 		
 		static int prev_button = 0;
 		if(do_editor_button(&prev_button, 0, 0, &dec, draw_dec_button, 0, "Previous Envelope"))
-			selected_envelope--;
+			editor.selected_envelope--;
 		
 		static int next_button = 0;
 		if(do_editor_button(&next_button, 0, 0, &inc, draw_inc_button, 0, "Next Envelope"))
-			selected_envelope++;
+			editor.selected_envelope++;
 			
 		if(envelope)
 		{
@@ -1948,7 +1690,7 @@ static void render_envelopeeditor(RECT view)
 		if(end_time < 1)
 			end_time = 1;
 		
-		envelope->find_top_bottom();
+		envelope->find_top_bottom(active_channels);
 		float top = envelope->top;
 		float bottom = envelope->bottom;
 		
@@ -1973,11 +1715,11 @@ static void render_envelopeeditor(RECT view)
 					// add point
 					int time = (int)(((ui_mouse_x()-view.x)*timescale)*1000.0f);
 					//float env_y = (ui_mouse_y()-view.y)/timescale;
+					float channels[4];
+					envelope->eval(time, channels);
 					envelope->add_point(time,
-						f2fx(envelope->eval(time, 0)),
-						f2fx(envelope->eval(time, 1)),
-						f2fx(envelope->eval(time, 2)),
-						f2fx(envelope->eval(time, 3)));
+						f2fx(channels[0]), f2fx(channels[1]),
+						f2fx(channels[2]), f2fx(channels[3]));
 				}
 				
 				editor.tooltip = "Press right mouse button to create a new point";
@@ -1988,6 +1730,7 @@ static void render_envelopeeditor(RECT view)
 
 		// render lines
 		{
+			ui_clip_enable(&view);
 			gfx_texture_set(-1);
 			gfx_lines_begin();
 			for(int c = 0; c < envelope->channels; c++)
@@ -1998,12 +1741,16 @@ static void render_envelopeeditor(RECT view)
 					gfx_setcolor(colors[c].r*0.5f,colors[c].g*0.5f,colors[c].b*0.5f,1);
 				
 				float prev_x = 0;
-				float prev_value = envelope->eval(0.000001f, c);
+				float results[4];
+				envelope->eval(0.000001f, results);
+				float prev_value = results[c];
+				
 				int steps = (int)((view.w/ui_screen()->w) * gfx_screenwidth());
 				for(int i = 1; i <= steps; i++)
 				{
 					float a = i/(float)steps;
-					float v = envelope->eval(a*end_time, c);
+					envelope->eval(a*end_time, results);
+					float v = results[c];
 					v = (v-bottom)/(top-bottom);
 					
 					gfx_lines_draw(view.x + prev_x*view.w, view.y+view.h - prev_value*view.h, view.x + a*view.w, view.y+view.h - v*view.h);
@@ -2012,6 +1759,7 @@ static void render_envelopeeditor(RECT view)
 				}
 			}
 			gfx_lines_end();
+			ui_clip_disable();
 		}
 		
 		// render curve options
@@ -2243,7 +1991,7 @@ static void render_menubar(RECT menubar)
 		*/
 }
 
-static void editor_render()
+void EDITOR::render()
 {
 	// basic start
 	gfx_clear(1.0f,0.0f,1.0f);
@@ -2329,7 +2077,7 @@ static void editor_render()
 	float my = ui_mouse_y();
 	gfx_texture_set(cursor_texture);
 	gfx_quads_begin();
-	if(ui_got_context)
+	if(ui_got_context == ui_hot_item())
 		gfx_setcolor(1,0,0,1);
 	gfx_quads_drawTL(mx,my, 16.0f, 16.0f);
 	gfx_quads_end();	
@@ -2351,6 +2099,13 @@ void EDITOR::reset(bool create_default)
 		editor.make_game_layer(new LAYER_GAME(50, 50));
 		editor.game_group->add_layer(editor.game_layer);
 	}
+	
+	selected_layer = 0;
+	selected_group = 0;
+	selected_quad = -1;
+	selected_points = 0;
+	selected_envelope = 0;
+	
 }
 
 void EDITOR::make_game_layer(LAYER *layer)
@@ -2367,381 +2122,6 @@ void EDITOR::make_game_group(LAYERGROUP *group)
 	editor.game_group->name = "Game";
 }
 
-template<typename T>
-static int make_version(int i, const T &v)
-{ return (i<<16)+sizeof(T); }
-
-// backwards compatiblity
-void editor_load_old(DATAFILE *df)
-{
-	class mapres_image
-	{
-	public:
-		int width;
-		int height;
-		int image_data;
-	};
-
-
-	struct mapres_tilemap
-	{
-		int image;
-		int width;
-		int height;
-		int x, y;
-		int scale;
-		int data;
-		int main;
-	};
-
-	enum
-	{
-		MAPRES_REGISTERED=0x8000,
-		MAPRES_IMAGE=0x8001,
-		MAPRES_TILEMAP=0x8002,
-		MAPRES_COLLISIONMAP=0x8003,
-		MAPRES_TEMP_THEME=0x8fff,
-	};
-
-	// load tilemaps
-	int game_width = 0;
-	int game_height = 0;
-	{
-		int start, num;
-		datafile_get_type(df, MAPRES_TILEMAP, &start, &num);
-		for(int t = 0; t < num; t++)
-		{
-			mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0);
-			
-			LAYER_TILES *l = new LAYER_TILES(tmap->width, tmap->height);
-			
-			if(tmap->main)
-			{
-				// move game layer to correct position
-				for(int i = 0; i < editor.map.groups[0]->layers.len()-1; i++)
-				{
-					if(editor.map.groups[0]->layers[i] == editor.game_layer)
-						editor.map.groups[0]->swap_layers(i, i+1);
-				}
-				
-				game_width = tmap->width;
-				game_height = tmap->height;
-			}
-
-			// add new layer
-			editor.map.groups[0]->add_layer(l);
-
-			// process the data
-			unsigned char *src_data = (unsigned char *)datafile_get_data(df, tmap->data);
-			TILE *dst_data = l->tiles;
-			
-			for(int y = 0; y < tmap->height; y++)
-				for(int x = 0; x < tmap->width; x++, dst_data++, src_data+=2)
-				{
-					dst_data->index = src_data[0];
-					dst_data->flags = src_data[1];
-				}
-				
-			l->image = tmap->image;
-		}
-	}
-	
-	// load images
-	{
-		int start, count;
-		datafile_get_type(df, MAPRES_IMAGE, &start, &count);
-		for(int i = 0; i < count; i++)
-		{
-			mapres_image *imgres = (mapres_image *)datafile_get_item(df, start+i, 0, 0);
-			void *data = datafile_get_data(df, imgres->image_data);
-
-			IMAGE *img = new IMAGE;
-			img->width = imgres->width;
-			img->height = imgres->height;
-			img->format = IMG_RGBA;
-			
-			// copy image data
-			img->data = mem_alloc(img->width*img->height*4, 1);
-			mem_copy(img->data, data, img->width*img->height*4);
-			img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
-			editor.map.images.add(img);
-			
-			// unload image
-			datafile_unload_data(df, imgres->image_data);
-		}
-	}
-	
-	// load entities
-	{
-		LAYER_GAME *g = editor.game_layer;
-		g->resize(game_width, game_height);
-		for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
-		{
-			// fetch entities of this class
-			int start, num;
-			datafile_get_type(df, t, &start, &num);
-
-
-			for(int i = 0; i < num; i++)
-			{
-				mapres_entity *e = (mapres_entity *)datafile_get_item(df, start+i,0,0);
-				int x = e->x/32;
-				int y = e->y/32;
-				int id = -1;
-					
-				if(t == MAPRES_SPAWNPOINT) id = ENTITY_SPAWN;
-				else if(t == MAPRES_SPAWNPOINT_RED) id = ENTITY_SPAWN_RED;
-				else if(t == MAPRES_SPAWNPOINT_BLUE) id = ENTITY_SPAWN_BLUE;
-				else if(t == MAPRES_FLAGSTAND_RED) id = ENTITY_FLAGSTAND_RED;
-				else if(t == MAPRES_FLAGSTAND_BLUE) id = ENTITY_FLAGSTAND_BLUE;
-				else if(t == MAPRES_ITEM)
-				{
-					if(e->data[0] == ITEM_WEAPON_SHOTGUN) id = ENTITY_WEAPON_SHOTGUN;
-					else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_ROCKET;
-					else if(e->data[0] == ITEM_NINJA) id = ENTITY_POWERUP_NINJA;
-					else if(e->data[0] == ITEM_ARMOR) id = ENTITY_ARMOR_1;
-					else if(e->data[0] == ITEM_HEALTH) id = ENTITY_HEALTH_1;
-				}
-						
-				if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height)
-					g->tiles[y*g->width+x].index = id+ENTITY_OFFSET;
-			}
-		}
-	}
-}
-
-int EDITOR::save(const char *filename)
-{
-	dbg_msg("editor", "saving to '%s'...", filename);
-	DATAFILE_OUT *df = datafile_create(filename);
-	if(!df)
-	{
-		dbg_msg("editor", "failed to open file '%s'...", filename);
-		return 0;
-	}
-		
-	// save version
-	{
-		MAPITEM_VERSION item;
-		item.version = 1;
-		datafile_add_item(df, MAPITEMTYPE_VERSION, 0, sizeof(item), &item);
-	}
-
-	// save images
-	for(int i = 0; i < map.images.len(); i++)
-	{
-		IMAGE *img = map.images[i];
-		MAPITEM_IMAGE item;
-		item.version = 1;
-		
-		item.width = img->width;
-		item.height = img->height;
-		item.external = 0;
-		item.image_name = -1;
-		item.image_data = datafile_add_data(df, item.width*item.height*4, img->data);
-		datafile_add_item(df, MAPITEMTYPE_IMAGE, i, sizeof(item), &item);
-	}
-	
-	// save layers
-	int layer_count = 0;
-	for(int g = 0; g < map.groups.len(); g++)
-	{
-		LAYERGROUP *group = map.groups[g];
-		MAPITEM_GROUP gitem;
-		gitem.version = 1;
-		
-		gitem.parallax_x = group->parallax_x;
-		gitem.parallax_y = group->parallax_y;
-		gitem.offset_x = group->offset_x;
-		gitem.offset_y = group->offset_y;
-		gitem.start_layer = layer_count;
-		gitem.num_layers = 0;
-		
-		for(int l = 0; l < group->layers.len(); l++)
-		{
-			if(group->layers[l]->type == LAYERTYPE_TILES)
-			{
-				dbg_msg("editor", "saving tiles layer");
-				LAYER_TILES *layer = (LAYER_TILES *)group->layers[l];
-				MAPITEM_LAYER_TILEMAP item;
-				item.version = 2;
-				
-				item.layer.flags = 0;
-				item.layer.type = layer->type;
-				
-				item.color.r = 255; // not in use right now
-				item.color.g = 255;
-				item.color.b = 255;
-				item.color.a = 255;
-				item.color_env = -1;
-				item.color_env_offset = 0;
-				
-				item.width = layer->width;
-				item.height = layer->height;
-				item.flags = layer->game;
-				item.image = layer->image;
-				item.data = datafile_add_data(df, layer->width*layer->height*sizeof(TILE), layer->tiles);
-				datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
-				
-				gitem.num_layers++;
-				layer_count++;
-			}
-			else if(group->layers[l]->type == LAYERTYPE_QUADS)
-			{
-				dbg_msg("editor", "saving quads layer");
-				LAYER_QUADS *layer = (LAYER_QUADS *)group->layers[l];
-				if(layer->quads.len())
-				{
-					MAPITEM_LAYER_QUADS item;
-					item.version = 1;
-					item.layer.flags = 0;
-					item.layer.type = layer->type;
-					item.image = layer->image;
-					
-					// add the data
-					item.num_quads = layer->quads.len();
-					item.data = datafile_add_data_swapped(df, layer->quads.len()*sizeof(QUAD), layer->quads.getptr());
-					datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
-					
-					// clean up
-					//mem_free(quads);
-
-					gitem.num_layers++;
-					layer_count++;
-				}
-			}
-		}
-		
-		datafile_add_item(df, MAPITEMTYPE_GROUP, g, sizeof(gitem), &gitem);
-	}
-	
-	// finish the data file
-	datafile_finish(df);
-	dbg_msg("editor", "done");
-	return 1;
-}
-
-
-int EDITOR::load(const char *filename)
-{
-	DATAFILE *df = datafile_load(filename);
-	if(!df)
-		return 0;
-
-	// check version
-	MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0);
-	if(!item)
-	{
-		// import old map
-		editor.reset();
-		editor_load_old(df);
-	}
-	else if(item->version == 1)
-	{
-		editor.reset(false);
-		
-		// load images
-		{
-			int start, num;
-			datafile_get_type(df, MAPITEMTYPE_IMAGE, &start, &num);
-			for(int i = 0; i < num; i++)
-			{
-				MAPITEM_IMAGE *item = (MAPITEM_IMAGE *)datafile_get_item(df, start+i, 0, 0);
-				void *data = datafile_get_data(df, item->image_data);
-				
-				IMAGE *img = new IMAGE;
-				img->width = item->width;
-				img->height = item->height;
-				img->format = IMG_RGBA;
-				
-				// copy image data
-				img->data = mem_alloc(img->width*img->height*4, 1);
-				mem_copy(img->data, data, img->width*img->height*4);
-				img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
-				editor.map.images.add(img);
-				
-				// unload image
-				datafile_unload_data(df, item->image_data);
-			}
-		}
-		
-		// load groups
-		{
-			int layers_start, layers_num;
-			datafile_get_type(df, MAPITEMTYPE_LAYER, &layers_start, &layers_num);
-			
-			int start, num;
-			datafile_get_type(df, MAPITEMTYPE_GROUP, &start, &num);
-			for(int g = 0; g < num; g++)
-			{
-				MAPITEM_GROUP *gitem = (MAPITEM_GROUP *)datafile_get_item(df, start+g, 0, 0);
-				LAYERGROUP *group = map.new_group();
-				group->parallax_x = gitem->parallax_x;
-				group->parallax_y = gitem->parallax_y;
-				group->offset_x = gitem->offset_x;
-				group->offset_y = gitem->offset_y;
-				
-				for(int l = 0; l < gitem->num_layers; l++)
-				{
-					MAPITEM_LAYER *layer_item = (MAPITEM_LAYER *)datafile_get_item(df, layers_start+gitem->start_layer+l, 0, 0);
-					if(!layer_item)
-						continue;
-						
-					if(layer_item->type == LAYERTYPE_TILES)
-					{
-						MAPITEM_LAYER_TILEMAP *tilemap_item = (MAPITEM_LAYER_TILEMAP *)layer_item;
-						LAYER_TILES *tiles = 0;
-						
-						if(tilemap_item->flags&1)
-						{
-							tiles = new LAYER_GAME(tilemap_item->width, tilemap_item->height);
-							editor.make_game_layer(tiles);
-							make_game_group(group);
-						}
-						else
-							tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height);
-						
-						group->add_layer(tiles);
-						void *data = datafile_get_data(df, tilemap_item->data);
-						tiles->image = tilemap_item->image;
-						tiles->game = tilemap_item->flags&1;
-						
-						mem_copy(tiles->tiles, data, tiles->width*tiles->height*sizeof(TILE));
-						
-						if(tiles->game && tilemap_item->version == make_version(1, *tilemap_item))
-						{
-							for(int i = 0; i < tiles->width*tiles->height; i++)
-							{
-								if(tiles->tiles[i].index)
-									tiles->tiles[i].index += ENTITY_OFFSET;
-							}
-						}
-						
-						datafile_unload_data(df, tilemap_item->data);
-					}
-					else if(layer_item->type == LAYERTYPE_QUADS)
-					{
-						MAPITEM_LAYER_QUADS *quads_item = (MAPITEM_LAYER_QUADS *)layer_item;
-						LAYER_QUADS *layer = new LAYER_QUADS;
-						layer->image = quads_item->image;
-						if(layer->image < -1 || layer->image >= map.images.len())
-							layer->image = -1;
-						void *data = datafile_get_data_swapped(df, quads_item->data);
-						group->add_layer(layer);
-						layer->quads.setsize(quads_item->num_quads);
-						mem_copy(layer->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads);
-						datafile_unload_data(df, quads_item->data);
-					}
-				}
-			}
-		}
-	}
-	
-	datafile_unload(df);
-	
-	return 0;
-}
-
 
 extern "C" void editor_init()
 {
@@ -2839,11 +2219,14 @@ extern "C" void editor_update_and_render()
 
 	if(inp_key_down(KEY_F5))
 		editor.save("data/maps/debug_test2.map");
+
+	if(inp_key_down(KEY_F6))
+		editor.load("data/maps/debug_test2.map");
 	
 	if(inp_key_down(KEY_F8))
 		editor.load("data/maps/debug_test.map");
 	
-	editor_render();
+	editor.render();
 	inp_clear_events();
 }
 
diff --git a/src/game/editor/ed_editor.hpp b/src/game/editor/ed_editor.hpp
index c0a3b494..2c391488 100644
--- a/src/game/editor/ed_editor.hpp
+++ b/src/game/editor/ed_editor.hpp
@@ -4,6 +4,7 @@
 #include <math.h>
 #include "array.h"
 #include "../g_mapitems.h"
+#include "../client/gc_render.h"
 
 extern "C" {
 	#include <engine/e_system.h>
@@ -32,30 +33,12 @@ enum
 	DIALOG_FILE,
 };
 
-typedef struct // as in file
+typedef struct
 {
 	POINT position;
 	int type;
 } ENTITY;
 
-enum
-{
-	CURVETYPE_STEP=0,
-	CURVETYPE_LINEAR,
-	CURVETYPE_SLOW,
-	CURVETYPE_FAST,
-	CURVETYPE_SMOOTH,
-	NUM_CURVETYPES,
-	
-};
-
-typedef struct // as in file
-{
-	int time; // in ms
-	int curvetype;
-	int values[4]; // 1-4 depending on envelope (22.10 fixed point)
-} ENVPOINT;
-
 class ENVELOPE
 {
 public:
@@ -86,10 +69,10 @@ public:
 	void resort()
 	{
 		qsort(points.getptr(), points.len(), sizeof(ENVPOINT), sort_comp);
-		find_top_bottom();
+		find_top_bottom(0xf);
 	}
 
-	void find_top_bottom()
+	void find_top_bottom(int channelmask)
 	{
 		top = -1000000000.0f;
 		bottom = 1000000000.0f;
@@ -97,54 +80,20 @@ public:
 		{
 			for(int c = 0; c < channels; c++)
 			{
-				float v = fx2f(points[i].values[c]);
-				if(v > top) top = v;
-				if(v < bottom) bottom = v;
+				if(channelmask&(1<<c))
+				{
+					float v = fx2f(points[i].values[c]);
+					if(v > top) top = v;
+					if(v < bottom) bottom = v;
+				}
 			}
 		}
 	}
 	
-	float eval(float time, int channel)
+	int eval(float time, float *result)
 	{
-		if(channel >= channels)
-			return 0;
-		if(points.len() == 0)
-			return 0;
-		if(points.len() == 1)
-			return points[0].values[channel];
-		
-		time = fmod(time, end_time())*1000.0f;
-		for(int i = 0; i < points.len() - 1; i++)
-		{
-			if(time >= points[i].time && time <= points[i+1].time)
-			{
-				float delta = points[i+1].time-points[i].time;
-				float a = (time-points[i].time)/delta;
-				
-				float v0 = fx2f(points[i].values[channel]);
-				float v1 = fx2f(points[i+1].values[channel]);
-				
-				if(points[i].curvetype == CURVETYPE_SMOOTH)
-					a = -2*a*a*a + 3*a*a; // second hermite basis
-				else if(points[i].curvetype == CURVETYPE_SLOW)
-					a = a*a*a;
-				else if(points[i].curvetype == CURVETYPE_FAST)
-				{
-					a = 1-a;
-					a = 1-a*a*a;
-				}
-				else if (points[i].curvetype == CURVETYPE_STEP)
-					a = 0;
-				else
-				{
-					// linear
-				}
-		
-				return v0 + (v1-v0) * a;
-			}
-		}
-		
-		return points[points.len()-1].values[channel];
+		render_eval_envelope(points.getptr(), points.len(), channels, time, result);
+		return channels;
 	}
 	
 	void add_point(int time, int v0, int v1=0, int v2=0, int v3=0)
@@ -310,12 +259,6 @@ enum
 	PROPTYPE_COLOR,
 	PROPTYPE_IMAGE,
 	PROPTYPE_ENVELOPE,
-	
-	PROPS_NONE=0,
-	PROPS_GROUP,
-	PROPS_LAYER,
-	PROPS_QUAD,
-	PROPS_QUAD_POINT,
 };
 
 class EDITOR
@@ -347,8 +290,6 @@ public:
 		animate_start = 0;
 		animate_time = 0;
 		
-		props = PROPS_NONE;
-		
 		show_envelope_editor = 0;
 	}
 	
@@ -362,6 +303,7 @@ public:
 	void reset(bool create_default=true);
 	int save(const char *filename);
 	int load(const char *filename);
+	void render();
 
 	QUAD *get_selected_quad();
 	LAYER *get_selected_layer_type(int index, int type);
@@ -395,10 +337,14 @@ public:
 	int64 animate_start;
 	float animate_time;
 	
-	int props;
-	
 	int show_envelope_editor;
 	
+	int selected_layer;
+	int selected_group;
+	int selected_quad;
+	int selected_points;
+	int selected_envelope;
+	
 	MAP map;
 };
 
@@ -468,7 +414,6 @@ public:
 	array<QUAD> quads;
 };
 
-
 class LAYER_GAME : public LAYER_TILES
 {
 public:
@@ -481,3 +426,14 @@ public:
 int do_editor_button(const void *id, const char *text, int checked, const RECT *r, ui_draw_button_func draw_func, int flags, const char *tooltip);
 void draw_editor_button(const void *id, const char *text, int checked, const RECT *r, const void *extra);
 void draw_editor_button_menuitem(const void *id, const char *text, int checked, const RECT *r, const void *extra);
+
+void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra=0);
+void ui_do_popup_menu();
+
+int popup_group(RECT view);
+int popup_layer(RECT view);
+int popup_quad(RECT view);
+int popup_point(RECT view);
+
+void popup_select_image_invoke(int current, float x, float y);
+int popup_select_image_result();
diff --git a/src/game/editor/ed_io.cpp b/src/game/editor/ed_io.cpp
new file mode 100644
index 00000000..77ae8242
--- /dev/null
+++ b/src/game/editor/ed_io.cpp
@@ -0,0 +1,471 @@
+#include "ed_editor.hpp"
+
+template<typename T>
+static int make_version(int i, const T &v)
+{ return (i<<16)+sizeof(T); }
+
+// backwards compatiblity
+void editor_load_old(DATAFILE *df)
+{
+	class mapres_image
+	{
+	public:
+		int width;
+		int height;
+		int image_data;
+	};
+
+
+	struct mapres_tilemap
+	{
+		int image;
+		int width;
+		int height;
+		int x, y;
+		int scale;
+		int data;
+		int main;
+	};
+
+	struct mapres_entity
+	{
+		int x, y;
+		int data[1];
+	};
+
+	struct mapres_spawnpoint
+	{
+		int x, y;
+	};
+
+	struct mapres_item
+	{
+		int x, y;
+		int type;
+	};
+
+	struct mapres_flagstand
+	{
+		int x, y;
+	};
+
+	enum
+	{
+		MAPRES_ENTS_START=1,
+		MAPRES_SPAWNPOINT=1,
+		MAPRES_ITEM=2,
+		MAPRES_SPAWNPOINT_RED=3,
+		MAPRES_SPAWNPOINT_BLUE=4,
+		MAPRES_FLAGSTAND_RED=5,
+		MAPRES_FLAGSTAND_BLUE=6,
+		MAPRES_ENTS_END,
+		
+		ITEM_NULL=0,
+		ITEM_WEAPON_GUN=0x00010001,
+		ITEM_WEAPON_SHOTGUN=0x00010002,
+		ITEM_WEAPON_ROCKET=0x00010003,
+		ITEM_WEAPON_SNIPER=0x00010004,
+		ITEM_WEAPON_HAMMER=0x00010005,
+		ITEM_HEALTH =0x00020001,
+		ITEM_ARMOR=0x00030001,
+		ITEM_NINJA=0x00040001,
+	};
+	
+	enum
+	{
+		MAPRES_REGISTERED=0x8000,
+		MAPRES_IMAGE=0x8001,
+		MAPRES_TILEMAP=0x8002,
+		MAPRES_COLLISIONMAP=0x8003,
+		MAPRES_TEMP_THEME=0x8fff,
+	};
+
+	// load tilemaps
+	int game_width = 0;
+	int game_height = 0;
+	{
+		int start, num;
+		datafile_get_type(df, MAPRES_TILEMAP, &start, &num);
+		for(int t = 0; t < num; t++)
+		{
+			mapres_tilemap *tmap = (mapres_tilemap *)datafile_get_item(df, start+t,0,0);
+			
+			LAYER_TILES *l = new LAYER_TILES(tmap->width, tmap->height);
+			
+			if(tmap->main)
+			{
+				// move game layer to correct position
+				for(int i = 0; i < editor.map.groups[0]->layers.len()-1; i++)
+				{
+					if(editor.map.groups[0]->layers[i] == editor.game_layer)
+						editor.map.groups[0]->swap_layers(i, i+1);
+				}
+				
+				game_width = tmap->width;
+				game_height = tmap->height;
+			}
+
+			// add new layer
+			editor.map.groups[0]->add_layer(l);
+
+			// process the data
+			unsigned char *src_data = (unsigned char *)datafile_get_data(df, tmap->data);
+			TILE *dst_data = l->tiles;
+			
+			for(int y = 0; y < tmap->height; y++)
+				for(int x = 0; x < tmap->width; x++, dst_data++, src_data+=2)
+				{
+					dst_data->index = src_data[0];
+					dst_data->flags = src_data[1];
+				}
+				
+			l->image = tmap->image;
+		}
+	}
+	
+	// load images
+	{
+		int start, count;
+		datafile_get_type(df, MAPRES_IMAGE, &start, &count);
+		for(int i = 0; i < count; i++)
+		{
+			mapres_image *imgres = (mapres_image *)datafile_get_item(df, start+i, 0, 0);
+			void *data = datafile_get_data(df, imgres->image_data);
+
+			IMAGE *img = new IMAGE;
+			img->width = imgres->width;
+			img->height = imgres->height;
+			img->format = IMG_RGBA;
+			
+			// copy image data
+			img->data = mem_alloc(img->width*img->height*4, 1);
+			mem_copy(img->data, data, img->width*img->height*4);
+			img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
+			editor.map.images.add(img);
+			
+			// unload image
+			datafile_unload_data(df, imgres->image_data);
+		}
+	}
+	
+	// load entities
+	{
+		LAYER_GAME *g = editor.game_layer;
+		g->resize(game_width, game_height);
+		for(int t = MAPRES_ENTS_START; t < MAPRES_ENTS_END; t++)
+		{
+			// fetch entities of this class
+			int start, num;
+			datafile_get_type(df, t, &start, &num);
+
+
+			for(int i = 0; i < num; i++)
+			{
+				mapres_entity *e = (mapres_entity *)datafile_get_item(df, start+i,0,0);
+				int x = e->x/32;
+				int y = e->y/32;
+				int id = -1;
+					
+				if(t == MAPRES_SPAWNPOINT) id = ENTITY_SPAWN;
+				else if(t == MAPRES_SPAWNPOINT_RED) id = ENTITY_SPAWN_RED;
+				else if(t == MAPRES_SPAWNPOINT_BLUE) id = ENTITY_SPAWN_BLUE;
+				else if(t == MAPRES_FLAGSTAND_RED) id = ENTITY_FLAGSTAND_RED;
+				else if(t == MAPRES_FLAGSTAND_BLUE) id = ENTITY_FLAGSTAND_BLUE;
+				else if(t == MAPRES_ITEM)
+				{
+					if(e->data[0] == ITEM_WEAPON_SHOTGUN) id = ENTITY_WEAPON_SHOTGUN;
+					else if(e->data[0] == ITEM_WEAPON_ROCKET) id = ENTITY_WEAPON_ROCKET;
+					else if(e->data[0] == ITEM_NINJA) id = ENTITY_POWERUP_NINJA;
+					else if(e->data[0] == ITEM_ARMOR) id = ENTITY_ARMOR_1;
+					else if(e->data[0] == ITEM_HEALTH) id = ENTITY_HEALTH_1;
+				}
+						
+				if(id > 0 && x >= 0 && x < g->width && y >= 0 && y < g->height)
+					g->tiles[y*g->width+x].index = id+ENTITY_OFFSET;
+			}
+		}
+	}
+}
+
+int EDITOR::save(const char *filename)
+{
+	dbg_msg("editor", "saving to '%s'...", filename);
+	DATAFILE_OUT *df = datafile_create(filename);
+	if(!df)
+	{
+		dbg_msg("editor", "failed to open file '%s'...", filename);
+		return 0;
+	}
+		
+	// save version
+	{
+		MAPITEM_VERSION item;
+		item.version = 1;
+		datafile_add_item(df, MAPITEMTYPE_VERSION, 0, sizeof(item), &item);
+	}
+
+	// save images
+	for(int i = 0; i < map.images.len(); i++)
+	{
+		IMAGE *img = map.images[i];
+		MAPITEM_IMAGE item;
+		item.version = 1;
+		
+		item.width = img->width;
+		item.height = img->height;
+		item.external = 0;
+		item.image_name = -1;
+		item.image_data = datafile_add_data(df, item.width*item.height*4, img->data);
+		datafile_add_item(df, MAPITEMTYPE_IMAGE, i, sizeof(item), &item);
+	}
+	
+	// save layers
+	int layer_count = 0;
+	for(int g = 0; g < map.groups.len(); g++)
+	{
+		LAYERGROUP *group = map.groups[g];
+		MAPITEM_GROUP gitem;
+		gitem.version = 1;
+		
+		gitem.parallax_x = group->parallax_x;
+		gitem.parallax_y = group->parallax_y;
+		gitem.offset_x = group->offset_x;
+		gitem.offset_y = group->offset_y;
+		gitem.start_layer = layer_count;
+		gitem.num_layers = 0;
+		
+		for(int l = 0; l < group->layers.len(); l++)
+		{
+			if(group->layers[l]->type == LAYERTYPE_TILES)
+			{
+				dbg_msg("editor", "saving tiles layer");
+				LAYER_TILES *layer = (LAYER_TILES *)group->layers[l];
+				MAPITEM_LAYER_TILEMAP item;
+				item.version = 2;
+				
+				item.layer.flags = 0;
+				item.layer.type = layer->type;
+				
+				item.color.r = 255; // not in use right now
+				item.color.g = 255;
+				item.color.b = 255;
+				item.color.a = 255;
+				item.color_env = -1;
+				item.color_env_offset = 0;
+				
+				item.width = layer->width;
+				item.height = layer->height;
+				item.flags = layer->game;
+				item.image = layer->image;
+				item.data = datafile_add_data(df, layer->width*layer->height*sizeof(TILE), layer->tiles);
+				datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
+				
+				gitem.num_layers++;
+				layer_count++;
+			}
+			else if(group->layers[l]->type == LAYERTYPE_QUADS)
+			{
+				dbg_msg("editor", "saving quads layer");
+				LAYER_QUADS *layer = (LAYER_QUADS *)group->layers[l];
+				if(layer->quads.len())
+				{
+					MAPITEM_LAYER_QUADS item;
+					item.version = 1;
+					item.layer.flags = 0;
+					item.layer.type = layer->type;
+					item.image = layer->image;
+					
+					// add the data
+					item.num_quads = layer->quads.len();
+					item.data = datafile_add_data_swapped(df, layer->quads.len()*sizeof(QUAD), layer->quads.getptr());
+					datafile_add_item(df, MAPITEMTYPE_LAYER, layer_count, sizeof(item), &item);
+					
+					// clean up
+					//mem_free(quads);
+
+					gitem.num_layers++;
+					layer_count++;
+				}
+			}
+		}
+		
+		datafile_add_item(df, MAPITEMTYPE_GROUP, g, sizeof(gitem), &gitem);
+	}
+	
+	// save envelopes
+	int point_count = 0;
+	for(int e = 0; e < map.envelopes.len(); e++)
+	{
+		MAPITEM_ENVELOPE item;
+		item.version = 1;
+		item.channels = map.envelopes[e]->channels;
+		item.start_point = point_count;
+		item.num_points = map.envelopes[e]->points.len();
+		item.name = -1;
+		
+		datafile_add_item(df, MAPITEMTYPE_ENVELOPE, e, sizeof(item), &item);
+		point_count += item.num_points;
+	}
+	
+	// save points
+	int totalsize = sizeof(ENVPOINT) * point_count;
+	ENVPOINT *points = (ENVPOINT *)mem_alloc(totalsize, 1);
+	point_count = 0;
+	
+	for(int e = 0; e < map.envelopes.len(); e++)
+	{
+		int count = map.envelopes[e]->points.len();
+		mem_copy(&points[point_count], map.envelopes[e]->points.getptr(), sizeof(ENVPOINT)*count);
+		point_count += count;
+	}
+
+	datafile_add_item(df, MAPITEMTYPE_ENVPOINTS, 0, totalsize, points);
+	
+	// finish the data file
+	datafile_finish(df);
+	dbg_msg("editor", "done");
+	return 1;
+}
+
+int EDITOR::load(const char *filename)
+{
+	DATAFILE *df = datafile_load(filename);
+	if(!df)
+		return 0;
+
+	// check version
+	MAPITEM_VERSION *item = (MAPITEM_VERSION *)datafile_find_item(df, MAPITEMTYPE_VERSION, 0);
+	if(!item)
+	{
+		// import old map
+		editor.reset();
+		editor_load_old(df);
+	}
+	else if(item->version == 1)
+	{
+		editor.reset(false);
+		
+		// load images
+		{
+			int start, num;
+			datafile_get_type(df, MAPITEMTYPE_IMAGE, &start, &num);
+			for(int i = 0; i < num; i++)
+			{
+				MAPITEM_IMAGE *item = (MAPITEM_IMAGE *)datafile_get_item(df, start+i, 0, 0);
+				void *data = datafile_get_data(df, item->image_data);
+				
+				IMAGE *img = new IMAGE;
+				img->width = item->width;
+				img->height = item->height;
+				img->format = IMG_RGBA;
+				
+				// copy image data
+				img->data = mem_alloc(img->width*img->height*4, 1);
+				mem_copy(img->data, data, img->width*img->height*4);
+				img->tex_id = gfx_load_texture_raw(img->width, img->height, img->format, img->data, IMG_AUTO);
+				editor.map.images.add(img);
+				
+				// unload image
+				datafile_unload_data(df, item->image_data);
+			}
+		}
+		
+		// load groups
+		{
+			int layers_start, layers_num;
+			datafile_get_type(df, MAPITEMTYPE_LAYER, &layers_start, &layers_num);
+			
+			int start, num;
+			datafile_get_type(df, MAPITEMTYPE_GROUP, &start, &num);
+			for(int g = 0; g < num; g++)
+			{
+				MAPITEM_GROUP *gitem = (MAPITEM_GROUP *)datafile_get_item(df, start+g, 0, 0);
+				LAYERGROUP *group = map.new_group();
+				group->parallax_x = gitem->parallax_x;
+				group->parallax_y = gitem->parallax_y;
+				group->offset_x = gitem->offset_x;
+				group->offset_y = gitem->offset_y;
+				
+				for(int l = 0; l < gitem->num_layers; l++)
+				{
+					MAPITEM_LAYER *layer_item = (MAPITEM_LAYER *)datafile_get_item(df, layers_start+gitem->start_layer+l, 0, 0);
+					if(!layer_item)
+						continue;
+						
+					if(layer_item->type == LAYERTYPE_TILES)
+					{
+						MAPITEM_LAYER_TILEMAP *tilemap_item = (MAPITEM_LAYER_TILEMAP *)layer_item;
+						LAYER_TILES *tiles = 0;
+						
+						if(tilemap_item->flags&1)
+						{
+							tiles = new LAYER_GAME(tilemap_item->width, tilemap_item->height);
+							editor.make_game_layer(tiles);
+							make_game_group(group);
+						}
+						else
+							tiles = new LAYER_TILES(tilemap_item->width, tilemap_item->height);
+						
+						group->add_layer(tiles);
+						void *data = datafile_get_data(df, tilemap_item->data);
+						tiles->image = tilemap_item->image;
+						tiles->game = tilemap_item->flags&1;
+						
+						mem_copy(tiles->tiles, data, tiles->width*tiles->height*sizeof(TILE));
+						
+						if(tiles->game && tilemap_item->version == make_version(1, *tilemap_item))
+						{
+							for(int i = 0; i < tiles->width*tiles->height; i++)
+							{
+								if(tiles->tiles[i].index)
+									tiles->tiles[i].index += ENTITY_OFFSET;
+							}
+						}
+						
+						datafile_unload_data(df, tilemap_item->data);
+					}
+					else if(layer_item->type == LAYERTYPE_QUADS)
+					{
+						MAPITEM_LAYER_QUADS *quads_item = (MAPITEM_LAYER_QUADS *)layer_item;
+						LAYER_QUADS *layer = new LAYER_QUADS;
+						layer->image = quads_item->image;
+						if(layer->image < -1 || layer->image >= map.images.len())
+							layer->image = -1;
+						void *data = datafile_get_data_swapped(df, quads_item->data);
+						group->add_layer(layer);
+						layer->quads.setsize(quads_item->num_quads);
+						mem_copy(layer->quads.getptr(), data, sizeof(QUAD)*quads_item->num_quads);
+						datafile_unload_data(df, quads_item->data);
+					}
+				}
+			}
+		}
+		
+		// load envelopes
+		{
+			ENVPOINT *points = 0;
+			
+			{
+				int start, num;
+				datafile_get_type(df, MAPITEMTYPE_ENVPOINTS, &start, &num);
+				if(num)
+					points = (ENVPOINT *)datafile_get_item(df, start, 0, 0);
+			}
+			
+			int start, num;
+			datafile_get_type(df, MAPITEMTYPE_ENVELOPE, &start, &num);
+			for(int e = 0; e < num; e++)
+			{
+				MAPITEM_ENVELOPE *item = (MAPITEM_ENVELOPE *)datafile_get_item(df, start+e, 0, 0);
+				ENVELOPE *env = new ENVELOPE(item->channels);
+				env->points.setsize(item->num_points);
+				mem_copy(env->points.getptr(), &points[item->start_point], sizeof(ENVPOINT)*item->num_points);
+				map.envelopes.add(env);
+			}
+		}
+	}
+	
+	datafile_unload(df);
+	
+	return 0;
+}
diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp
index ee75a503..ca84ffe8 100644
--- a/src/game/editor/ed_layer_quads.cpp
+++ b/src/game/editor/ed_layer_quads.cpp
@@ -14,13 +14,20 @@ LAYER_QUADS::~LAYER_QUADS()
 {
 }
 
+static void envelope_eval(float time_offset, int env, float *channels)
+{
+	ENVELOPE *e = editor.map.envelopes[env];
+	float t = editor.animate_time+time_offset;
+	e->eval(t, channels);
+}
+
 void LAYER_QUADS::render()
 {
 	gfx_texture_set(-1);
 	if(image >= 0 && image < editor.map.images.len())
 		gfx_texture_set(editor.map.images[image]->tex_id);
 		
-	render_quads(quads.getptr(), quads.len());
+	render_quads(quads.getptr(), quads.len(), envelope_eval);
 }
 
 QUAD *LAYER_QUADS::new_quad()
@@ -160,123 +167,29 @@ extern int selected_points;
 int LAYER_QUADS::render_properties(RECT *toolbox)
 {
 	// layer props
-	if(editor.props == PROPS_LAYER)
+	enum
 	{
-		enum
-		{
-			PROP_IMAGE=0,
-			NUM_PROPS,
-		};
-		
-		PROPERTY props[] = {
-			{"Image", image, PROPTYPE_IMAGE, -1, 0},
-			{0},
-		};
-		
-		static int ids[NUM_PROPS] = {0};
-		int new_val = 0;
-		int prop = editor.do_properties(toolbox, props, ids, &new_val);		
-		
-		if(prop == PROP_IMAGE)
-		{
-			if(new_val >= 0)
-				image = new_val%editor.map.images.len();
-			else
-				image = -1;
-		}
-	}
-
-	// quad props
-	QUAD *quad = editor.get_selected_quad();
-	if(editor.props == PROPS_QUAD)
-	{
-		if(quad)
-		{
-			RECT slot;
-			ui_hsplit_t(toolbox, 15.0f, &slot, toolbox);
-			ui_do_label(&slot, "Quad Props", 12.0f, -1, -1);
-			
-			enum
-			{
-				PROP_POS_ENV=0,
-				PROP_POS_ENV_OFFSET,
-				PROP_COLOR_ENV,
-				PROP_COLOR_ENV_OFFSET,
-				NUM_PROPS,
-			};
-			
-			PROPERTY props[] = {
-				{"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
-				{"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-				{"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
-				{"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
-				
-				{0},
-			};
-			
-			static int ids[NUM_PROPS] = {0};
-			int new_val = 0;
-			int prop = editor.do_properties(toolbox, props, ids, &new_val);		
-			
-			if(prop == PROP_POS_ENV) quad->pos_env = new_val;
-			if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val;
-			if(prop == PROP_COLOR_ENV) quad->color_env = new_val;
-			if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val;
-		}
-	}
+		PROP_IMAGE=0,
+		NUM_PROPS,
+	};
 	
-	// point props
-	if(editor.props == PROPS_QUAD_POINT && quad && selected_points)
-	{
-		RECT slot;
-		ui_hsplit_t(toolbox, 15.0f, &slot, toolbox);
-		ui_do_label(&slot, "Point Props", 14.0f, -1, -1);
-		
-		enum
-		{
-			PROP_COLOR=0,
-			NUM_PROPS,
-		};
-		
-		int color = 0;
+	PROPERTY props[] = {
+		{"Image", image, PROPTYPE_IMAGE, -1, 0},
+		{0},
+	};
 	
-		for(int v = 0; v < 4; v++)
-		{
-			if(selected_points&(1<<v))
-			{
-				color = 0;
-				color |= quad->colors[v].r<<24;
-				color |= quad->colors[v].g<<16;
-				color |= quad->colors[v].b<<8;
-				color |= quad->colors[v].a;
-			}
-		}
-		
-		
-		PROPERTY props[] = {
-			{"Color", color, PROPTYPE_COLOR, -1, editor.map.envelopes.len()},
-			{0},
-		};
-		
-		static int ids[NUM_PROPS] = {0};
-		int new_val = 0;
-		int prop = editor.do_properties(toolbox, props, ids, &new_val);		
-		if(prop == PROP_COLOR)
-		{
-			for(int v = 0; v < 4; v++)
-			{
-				if(selected_points&(1<<v))
-				{
-					color = 0;
-					quad->colors[v].r = (new_val>>24)&0xff;
-					quad->colors[v].g = (new_val>>16)&0xff;
-					quad->colors[v].b = (new_val>>8)&0xff;
-					quad->colors[v].a = new_val&0xff;
-				}
-			}
-		}
-	}	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	int prop = editor.do_properties(toolbox, props, ids, &new_val);		
 	
+	if(prop == PROP_IMAGE)
+	{
+		if(new_val >= 0)
+			image = new_val%editor.map.images.len();
+		else
+			image = -1;
+	}
+
 	return 0;
 }
 
diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp
index 434208fa..348f8575 100644
--- a/src/game/editor/ed_layer_tiles.cpp
+++ b/src/game/editor/ed_layer_tiles.cpp
@@ -214,7 +214,7 @@ int LAYER_TILES::render_properties(RECT *toolbox)
 	};
 	
 	PROPERTY props[] = {
-		{"Image", image, PROPTYPE_INT_STEP, 0, 0},
+		{"Image", image, PROPTYPE_IMAGE, 0, 0},
 		{"Width", width, PROPTYPE_INT_STEP, 1, 1000000000},
 		{"Height", height, PROPTYPE_INT_STEP, 1, 1000000000},
 		{0},
diff --git a/src/game/editor/ed_popups.cpp b/src/game/editor/ed_popups.cpp
new file mode 100644
index 00000000..e06637e1
--- /dev/null
+++ b/src/game/editor/ed_popups.cpp
@@ -0,0 +1,385 @@
+#include <stdio.h>
+#include "ed_editor.hpp"
+
+
+// popup menu handling
+static struct
+{
+	RECT rect;
+	void *id;
+	int (*func)(RECT rect);
+	int is_menu;
+	void *extra;
+} ui_popups[8];
+
+static int ui_num_popups = 0;
+
+void ui_invoke_popup_menu(void *id, int flags, float x, float y, float w, float h, int (*func)(RECT rect), void *extra)
+{
+	dbg_msg("", "invoked");
+	ui_popups[ui_num_popups].id = id;
+	ui_popups[ui_num_popups].is_menu = flags;
+	ui_popups[ui_num_popups].rect.x = x;
+	ui_popups[ui_num_popups].rect.y = y;
+	ui_popups[ui_num_popups].rect.w = w;
+	ui_popups[ui_num_popups].rect.h = h;
+	ui_popups[ui_num_popups].func = func;
+	ui_popups[ui_num_popups].extra = extra;
+	ui_num_popups++;
+}
+
+void ui_do_popup_menu()
+{
+	for(int i = 0; i < ui_num_popups; i++)
+	{
+		bool inside = ui_mouse_inside(&ui_popups[i].rect);
+		ui_set_hot_item(&ui_popups[i].id);
+		
+		if(ui_active_item() == &ui_popups[i].id)
+		{
+			if(!ui_mouse_button(0))
+			{
+				if(!inside)
+					ui_num_popups--;
+				ui_set_active_item(0);
+			}
+		}
+		else if(ui_hot_item() == &ui_popups[i].id)
+		{
+			if(ui_mouse_button(0))
+				ui_set_active_item(&ui_popups[i].id);
+		}
+		
+		int corners = CORNER_ALL;
+		if(ui_popups[i].is_menu)
+			corners = CORNER_R|CORNER_B;
+		
+		RECT r = ui_popups[i].rect;
+		ui_draw_rect(&r, vec4(0.5f,0.5f,0.5f,0.75f), corners, 3.0f);
+		ui_margin(&r, 1.0f, &r);
+		ui_draw_rect(&r, vec4(0,0,0,0.75f), corners, 3.0f);
+		ui_margin(&r, 4.0f, &r);
+		
+		if(ui_popups[i].func(r))
+			ui_num_popups--;
+			
+		if(inp_key_down(KEY_ESC))
+			ui_num_popups--;
+	}
+}
+
+
+int popup_group(RECT view)
+{
+	// remove group button
+	RECT button;
+	ui_hsplit_b(&view, 12.0f, &view, &button);
+	static int delete_button = 0;
+	if(do_editor_button(&delete_button, "Delete Group", 0, &button, draw_editor_button, 0, "Delete group"))
+	{
+		editor.map.delete_group(editor.selected_group);
+		return 1;
+	}
+
+	// new tile layer
+	ui_hsplit_b(&view, 10.0f, &view, &button);
+	ui_hsplit_b(&view, 12.0f, &view, &button);
+	static int new_quad_layer_button = 0;
+	if(do_editor_button(&new_quad_layer_button, "Add Quads Layer", 0, &button, draw_editor_button, 0, "Creates a new quad layer"))
+	{
+		LAYER *l = new LAYER_QUADS;
+		editor.map.groups[editor.selected_group]->add_layer(l);
+		editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1;
+		return 1;
+	}
+
+	// new quad layer
+	ui_hsplit_b(&view, 5.0f, &view, &button);
+	ui_hsplit_b(&view, 12.0f, &view, &button);
+	static int new_tile_layer_button = 0;
+	if(do_editor_button(&new_tile_layer_button, "Add Tile Layer", 0, &button, draw_editor_button, 0, "Creates a new tile layer"))
+	{
+		LAYER *l = new LAYER_TILES(50, 50);
+		editor.map.groups[editor.selected_group]->add_layer(l);
+		editor.selected_layer = editor.map.groups[editor.selected_group]->layers.len()-1;
+		return 1;
+	}
+	
+	enum
+	{
+		PROP_ORDER=0,
+		PROP_POS_X,
+		PROP_POS_Y,
+		PROP_PARA_X,
+		PROP_PARA_Y,
+		NUM_PROPS,
+	};
+	
+	PROPERTY props[] = {
+		{"Order", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
+		{"Pos X", -editor.map.groups[editor.selected_group]->offset_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		{"Pos Y", -editor.map.groups[editor.selected_group]->offset_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		{"Para X", editor.map.groups[editor.selected_group]->parallax_x, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		{"Para Y", editor.map.groups[editor.selected_group]->parallax_y, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		{0},
+	};
+	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	
+	// cut the properties that isn't needed
+	if(editor.get_selected_group()->game_group)
+		props[PROP_POS_X].name = 0;
+		
+	int prop = editor.do_properties(&view, props, ids, &new_val);
+	if(prop == PROP_ORDER)
+		editor.selected_group = editor.map.swap_groups(editor.selected_group, new_val);
+		
+	// these can not be changed on the game group
+	if(!editor.get_selected_group()->game_group)
+	{
+		if(prop == PROP_PARA_X)
+			editor.map.groups[editor.selected_group]->parallax_x = new_val;
+		else if(prop == PROP_PARA_Y)
+			editor.map.groups[editor.selected_group]->parallax_y = new_val;
+		else if(prop == PROP_POS_X)
+			editor.map.groups[editor.selected_group]->offset_x = -new_val;
+		else if(prop == PROP_POS_Y)
+			editor.map.groups[editor.selected_group]->offset_y = -new_val;
+	}
+	
+	return 0;
+}
+
+int popup_layer(RECT view)
+{
+	// remove layer button
+	RECT button;
+	ui_hsplit_b(&view, 12.0f, &view, &button);
+	static int delete_button = 0;
+	if(do_editor_button(&delete_button, "Delete Layer", 0, &button, draw_editor_button, 0, "Deletes the layer"))
+	{
+		editor.map.groups[editor.selected_group]->delete_layer(editor.selected_layer);
+		return 1;
+	}
+
+	ui_hsplit_b(&view, 10.0f, &view, 0);
+	
+	LAYERGROUP *current_group = editor.map.groups[editor.selected_group];
+	LAYER *current_layer = editor.get_selected_layer(0);
+	
+	enum
+	{
+		PROP_GROUP=0,
+		PROP_ORDER,
+		NUM_PROPS,
+	};
+	
+	PROPERTY props[] = {
+		{"Group", editor.selected_group, PROPTYPE_INT_STEP, 0, editor.map.groups.len()-1},
+		{"Order", editor.selected_layer, PROPTYPE_INT_STEP, 0, current_group->layers.len()},
+		{0},
+	};
+	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	int prop = editor.do_properties(&view, props, ids, &new_val);		
+	
+	if(prop == PROP_ORDER)
+		editor.selected_layer = current_group->swap_layers(editor.selected_layer, new_val);
+	else if(prop == PROP_GROUP && current_layer->type != LAYERTYPE_GAME)
+	{
+		if(new_val >= 0 && new_val < editor.map.groups.len())
+		{
+			current_group->layers.remove(current_layer);
+			editor.map.groups[new_val]->layers.add(current_layer);
+			editor.selected_group = new_val;
+			editor.selected_layer = editor.map.groups[new_val]->layers.len()-1;
+		}
+	}
+		
+	return current_layer->render_properties(&view);
+}
+
+int popup_quad(RECT view)
+{
+	QUAD *quad = editor.get_selected_quad();
+
+	RECT button;
+	ui_hsplit_b(&view, 12.0f, &view, &button);
+	static int sq_button = 0;
+	if(do_editor_button(&sq_button, "Square", 0, &button, draw_editor_button, 0, "Squares the current quad"))
+	{
+		int top = quad->points[0].y;
+		int left = quad->points[0].x;
+		int bottom = quad->points[0].y;
+		int right = quad->points[0].x;
+		
+		for(int k = 1; k < 4; k++)
+		{
+			if(quad->points[k].y < top) top = quad->points[k].y;
+			if(quad->points[k].x < left) left = quad->points[k].x;
+			if(quad->points[k].y > bottom) bottom = quad->points[k].y;
+			if(quad->points[k].x > right) right = quad->points[k].x;
+		}
+		
+		quad->points[0].x = left; quad->points[0].y = top;
+		quad->points[1].x = right; quad->points[1].y = top;
+		quad->points[2].x = left; quad->points[2].y = bottom;
+		quad->points[3].x = right; quad->points[3].y = bottom;
+		return 1;
+	}
+
+	//ui_vsplit_b(&toolbar, 2.0f, &button, &toolbar);
+
+
+	enum
+	{
+		PROP_POS_ENV=0,
+		PROP_POS_ENV_OFFSET,
+		PROP_COLOR_ENV,
+		PROP_COLOR_ENV_OFFSET,
+		NUM_PROPS,
+	};
+	
+	PROPERTY props[] = {
+		{"Pos. Env", quad->pos_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
+		{"Pos. TO", quad->pos_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		{"Color Env", quad->color_env, PROPTYPE_INT_STEP, -1, editor.map.envelopes.len()},
+		{"Color TO", quad->color_env_offset, PROPTYPE_INT_SCROLL, -1000000, 1000000},
+		
+		{0},
+	};
+	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	int prop = editor.do_properties(&view, props, ids, &new_val);		
+	
+	if(prop == PROP_POS_ENV) quad->pos_env = new_val;
+	if(prop == PROP_POS_ENV_OFFSET) quad->pos_env_offset = new_val;
+	if(prop == PROP_COLOR_ENV) quad->color_env = new_val;
+	if(prop == PROP_COLOR_ENV_OFFSET) quad->color_env_offset = new_val;
+	
+	return 0;
+}
+
+int popup_point(RECT view)
+{
+	QUAD *quad = editor.get_selected_quad();
+	
+	enum
+	{
+		PROP_COLOR=0,
+		NUM_PROPS,
+	};
+	
+	int color = 0;
+
+	for(int v = 0; v < 4; v++)
+	{
+		if(editor.selected_points&(1<<v))
+		{
+			color = 0;
+			color |= quad->colors[v].r<<24;
+			color |= quad->colors[v].g<<16;
+			color |= quad->colors[v].b<<8;
+			color |= quad->colors[v].a;
+		}
+	}
+	
+	
+	PROPERTY props[] = {
+		{"Color", color, PROPTYPE_COLOR, -1, editor.map.envelopes.len()},
+		{0},
+	};
+	
+	static int ids[NUM_PROPS] = {0};
+	int new_val = 0;
+	int prop = editor.do_properties(&view, props, ids, &new_val);		
+	if(prop == PROP_COLOR)
+	{
+		for(int v = 0; v < 4; v++)
+		{
+			if(editor.selected_points&(1<<v))
+			{
+				color = 0;
+				quad->colors[v].r = (new_val>>24)&0xff;
+				quad->colors[v].g = (new_val>>16)&0xff;
+				quad->colors[v].b = (new_val>>8)&0xff;
+				quad->colors[v].a = new_val&0xff;
+			}
+		}
+	}
+	
+	return 0;	
+}
+
+
+
+static int select_image_selected = -100;
+static int select_image_current = -100;
+
+int popup_select_image(RECT view)
+{
+	RECT buttonbar, imageview;
+	ui_vsplit_l(&view, 80.0f, &buttonbar, &view);
+	ui_margin(&view, 10.0f, &imageview);
+	
+	int show_image = select_image_current;
+	
+	for(int i = -1; i < editor.map.images.len(); i++)
+	{
+		RECT button;
+		ui_hsplit_t(&buttonbar, 12.0f, &button, &buttonbar);
+		ui_hsplit_t(&buttonbar, 2.0f, 0, &buttonbar);
+		
+		if(ui_mouse_inside(&button))
+			show_image = i;
+			
+		if(i == -1)
+		{
+			if(do_editor_button(&editor.map.images[i], "None", i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
+				select_image_selected = -1;
+		}
+		else
+		{
+			char buf[64];
+			sprintf(buf, "%d", i);
+			if(do_editor_button(&editor.map.images[i], buf, i==select_image_current, &button, draw_editor_button_menuitem, 0, 0))
+				select_image_selected = i;
+		}
+	}
+	
+	if(show_image >= 0 && show_image < editor.map.images.len())
+		gfx_texture_set(editor.map.images[show_image]->tex_id);
+	else
+		gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_quads_drawTL(imageview.x, imageview.y, imageview.w, imageview.h);
+	gfx_quads_end();
+
+	return 0;
+}
+
+void popup_select_image_invoke(int current, float x, float y)
+{
+	static int select_image_popup_id = 0;
+	select_image_selected = -100;
+	select_image_current = current;
+	ui_invoke_popup_menu(&select_image_popup_id, 0, x, y, 400, 300, popup_select_image);
+}
+
+int popup_select_image_result()
+{
+	if(select_image_selected == -100)
+		return -100;
+		
+	select_image_current = select_image_selected;
+	select_image_selected = -100;
+	return select_image_current;
+}
+
+
+
+
+
diff --git a/src/game/g_game.h b/src/game/g_game.h
index 395362a9..005f315b 100644
--- a/src/game/g_game.h
+++ b/src/game/g_game.h
@@ -129,48 +129,4 @@ public:
 inline bool col_check_point(float x, float y) { return col_is_solid((int)x, (int)y) != 0; }
 inline bool col_check_point(vec2 p) { return col_check_point(p.x, p.y); }
 
-struct mapres_entity
-{
-	int x, y;
-	int data[1];
-};
-
-struct mapres_spawnpoint
-{
-	int x, y;
-};
-
-struct mapres_item
-{
-	int x, y;
-	int type;
-};
-
-struct mapres_flagstand
-{
-	int x, y;
-};
-
-enum
-{
-	MAPRES_ENTS_START=1,
-	MAPRES_SPAWNPOINT=1,
-	MAPRES_ITEM=2,
-	MAPRES_SPAWNPOINT_RED=3,
-	MAPRES_SPAWNPOINT_BLUE=4,
-	MAPRES_FLAGSTAND_RED=5,
-	MAPRES_FLAGSTAND_BLUE=6,
-	MAPRES_ENTS_END,
-	
-	ITEM_NULL=0,
-	ITEM_WEAPON_GUN=0x00010001,
-	ITEM_WEAPON_SHOTGUN=0x00010002,
-	ITEM_WEAPON_ROCKET=0x00010003,
-	ITEM_WEAPON_SNIPER=0x00010004,
-	ITEM_WEAPON_HAMMER=0x00010005,
-	ITEM_HEALTH =0x00020001,
-	ITEM_ARMOR=0x00030001,
-	ITEM_NINJA=0x00040001,
-};
-
 #endif
diff --git a/src/game/g_mapitems.h b/src/game/g_mapitems.h
index d7d8fa77..8b4455ec 100644
--- a/src/game/g_mapitems.h
+++ b/src/game/g_mapitems.h
@@ -16,6 +16,15 @@ enum
 	MAPITEMTYPE_ENVELOPE,
 	MAPITEMTYPE_GROUP,
 	MAPITEMTYPE_LAYER,
+	MAPITEMTYPE_ENVPOINTS,
+	
+
+	CURVETYPE_STEP=0,
+	CURVETYPE_LINEAR,
+	CURVETYPE_SLOW,
+	CURVETYPE_FAST,
+	CURVETYPE_SMOOTH,
+	NUM_CURVETYPES,
 	
 	// game layer tiles
 	ENTITY_NULL=0,
@@ -133,6 +142,22 @@ typedef struct
 	int version;
 } MAPITEM_VERSION;
 
+typedef struct
+{
+	int time; // in ms
+	int curvetype;
+	int values[4]; // 1-4 depending on envelope (22.10 fixed point)
+} ENVPOINT;
+
+typedef struct
+{
+	int version;
+	int channels;
+	int start_point;
+	int num_points;
+	int name;
+} MAPITEM_ENVELOPE;
+
 // float to fixed
 inline int f2fx(float v) { return (int)(v*(float)(1<<10)); }
 inline float fx2f(int v) { return v*(1.0f/(1<<10)); }
diff --git a/src/game/server/gs_common.h b/src/game/server/gs_common.h
index 90363cb8..b91e777e 100644
--- a/src/game/server/gs_common.h
+++ b/src/game/server/gs_common.h
@@ -133,7 +133,9 @@ public:
 	int gametype;
 	gameobject();
 
-	void do_team_wincheck();
+	void do_team_score_wincheck();
+	void do_player_score_wincheck();
+	
 	void do_warmup(int seconds);
 	
 	void startround();
diff --git a/src/game/server/gs_game.cpp b/src/game/server/gs_game.cpp
index bae964ab..2d44a499 100644
--- a/src/game/server/gs_game.cpp
+++ b/src/game/server/gs_game.cpp
@@ -289,7 +289,40 @@ int gameobject::getteam(int notthisid)
 	return numplayers[0] > numplayers[1] ? 1 : 0;
 }
 
-void gameobject::do_team_wincheck()
+void gameobject::do_player_score_wincheck()
+{
+	if(game_over_tick == -1  && !warmup)
+	{
+		// gather some stats
+		int topscore = 0;
+		int topscore_count = 0;
+		for(int i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(players[i].client_id != -1)
+			{
+				if(players[i].score > topscore)
+				{
+					topscore = players[i].score;
+					topscore_count = 1;
+				}
+				else if(players[i].score == topscore)
+					topscore_count++;
+			}
+		}
+		
+		// check score win condition
+		if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) ||
+			(config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60))
+		{
+			if(topscore_count == 1)
+				endround();
+			else
+				sudden_death = 1;
+		}
+	}
+}
+
+void gameobject::do_team_score_wincheck()
 {
 	if(game_over_tick == -1 && !warmup)
 	{
diff --git a/src/game/server/gs_game_ctf.cpp b/src/game/server/gs_game_ctf.cpp
index fd54a97b..7581f015 100644
--- a/src/game/server/gs_game_ctf.cpp
+++ b/src/game/server/gs_game_ctf.cpp
@@ -62,7 +62,7 @@ void gameobject_ctf::tick()
 {
 	gameobject::tick();
 
-	do_team_wincheck();
+	do_team_score_wincheck();
 	
 	// do flags
 	for(int fi = 0; fi < 2; fi++)
diff --git a/src/game/server/gs_game_dm.cpp b/src/game/server/gs_game_dm.cpp
index 98317578..49de6b56 100644
--- a/src/game/server/gs_game_dm.cpp
+++ b/src/game/server/gs_game_dm.cpp
@@ -5,38 +5,6 @@
 
 void gameobject_dm::tick()
 {
-	if(game_over_tick == -1)
-	{
-		// game is running
-		
-		// gather some stats
-		int topscore = 0;
-		int topscore_count = 0;
-		for(int i = 0; i < MAX_CLIENTS; i++)
-		{
-			if(players[i].client_id != -1)
-			{
-				if(players[i].score > topscore)
-				{
-					topscore = players[i].score;
-					topscore_count = 1;
-				}
-				else if(players[i].score == topscore)
-					topscore_count++;
-			}
-		}
-		
-		// check score win condition
-		if((config.sv_scorelimit > 0 && topscore >= config.sv_scorelimit) ||
-			(config.sv_timelimit > 0 && (server_tick()-round_start_tick) >= config.sv_timelimit*server_tickspeed()*60))
-		{
-			if(topscore_count == 1)
-				endround();
-			else
-				sudden_death = 1;
-		}
-	}
-	
+	do_player_score_wincheck();
 	gameobject::tick();
 }
-
diff --git a/src/game/server/gs_game_tdm.cpp b/src/game/server/gs_game_tdm.cpp
index 7aa12e2b..fb2f569b 100644
--- a/src/game/server/gs_game_tdm.cpp
+++ b/src/game/server/gs_game_tdm.cpp
@@ -8,7 +8,6 @@ gameobject_tdm::gameobject_tdm()
 	is_teamplay = true;
 }
 
-
 int gameobject_tdm::on_player_death(class player *victim, class player *killer, int weapon)
 {
 	gameobject::on_player_death(victim, killer, weapon);
@@ -26,7 +25,6 @@ int gameobject_tdm::on_player_death(class player *victim, class player *killer,
 
 void gameobject_tdm::tick()
 {
-	do_team_wincheck();
-	
+	do_team_score_wincheck();
 	gameobject::tick();
 }