about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-12 17:09:00 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2008-01-12 17:09:00 +0000
commit906ece7894927983b8ac69e37dd3cb82cfe7aad1 (patch)
treec3e83b60f04c5f26975106d807ad5185d20827be
parent99f0a47d6b03b005d0a7dae064cb6eb7cb4f60b5 (diff)
downloadzcatch-906ece7894927983b8ac69e37dd3cb82cfe7aad1.tar.gz
zcatch-906ece7894927983b8ac69e37dd3cb82cfe7aad1.zip
continued the cleanup
-rw-r--r--datasrc/client.dts21
-rw-r--r--datasrc/teewars.ds80
-rw-r--r--src/game/client/gc_anim.h14
-rw-r--r--src/game/client/gc_client.cpp1327
-rw-r--r--src/game/client/gc_client.h93
-rw-r--r--src/game/client/gc_hooks.cpp500
-rw-r--r--src/game/client/gc_mapres_tilemap.cpp6
-rw-r--r--src/game/client/gc_mapres_tilemap.h6
-rw-r--r--src/game/client/gc_menu.cpp14
-rw-r--r--src/game/client/gc_menu.h4
-rw-r--r--src/game/client/gc_render.cpp237
-rw-r--r--src/game/client/gc_render.h60
-rw-r--r--src/game/client/gc_render_map.cpp167
-rw-r--r--src/game/client/gc_render_obj.cpp478
-rw-r--r--src/game/editor/ed_editor.cpp (renamed from src/game/editor/editor.cpp)72
-rw-r--r--src/game/editor/ed_editor.hpp (renamed from src/game/editor/editor.hpp)101
-rw-r--r--src/game/editor/ed_layer_game.cpp2
-rw-r--r--src/game/editor/ed_layer_quads.cpp86
-rw-r--r--src/game/editor/ed_layer_tiles.cpp82
-rw-r--r--src/game/g_game.h5
-rw-r--r--src/game/g_mapitems.h138
-rw-r--r--src/game/g_mapres_col.h6
-rw-r--r--src/game/g_math.h5
-rw-r--r--src/game/g_protocol.h5
24 files changed, 1767 insertions, 1742 deletions
diff --git a/datasrc/client.dts b/datasrc/client.dts
index 6274287c..eec61fa4 100644
--- a/datasrc/client.dts
+++ b/datasrc/client.dts
@@ -80,26 +80,6 @@ struct animation {
 	instance:sequence attach = attach
 }
 
-struct gui_box {
-	int x = @1
-	int y = @2
-	int w = @3
-	int h = @4
-}
-
-struct gui_compositebox {
-	instance:gui_box rect = rect
-	instance:gui_box center = center
-}
-
-struct gui_widgets {
-	array:gui_compositebox boxes = boxes.*
-	array:gui_box misc = misc.*
-}
-
-const array:int gui_box = gui.boxes.*
-const array:int gui_misc = gui.misc.*
-
 struct data_container {
 	array:image images = images.*
 	array:spriteset spritesets = sprites.*
@@ -111,7 +91,6 @@ struct data_container {
 	array:soundset sounds = sounds.*
 	array:animation animations = animations.*
 
-	instance:gui_widgets gui = gui
 }
 
 const array:int weapon = weapons.*
diff --git a/datasrc/teewars.ds b/datasrc/teewars.ds
index efa6b668..640fd952 100644
--- a/datasrc/teewars.ds
+++ b/datasrc/teewars.ds
@@ -215,6 +215,10 @@ sounds {
 
 
 images {
+	null {
+		filename ""
+	}
+
 	game {
 		filename "data/game.png"
 	}
@@ -663,7 +667,7 @@ sprites {
 
 	}
 	
-	tees images.char_default 8 4 {
+	tees images.null 8 4 {
 		tee_body 0 0 3 3
 		tee_body_outline 3 0 3 3
 		tee_foot 6 1 2 1
@@ -827,77 +831,3 @@ animations {
 		}
 	}	
 }
-
-gui {
-	boxes {
-		button {
-			rect 0 0 48 48
-			center 16 16 16 16
-		}
-
-		screen_thick {
-			rect 0 384 48 48
-			center 16 400 16 16
-		}
-
-		screen_transparent {
-			rect 48 384 48 48
-			center 64 400 16 16
-		}
-
-		screen_info {
-			rect 96 384 48 48
-			center 112 400 16 16
-		}
-
-		screen_textbox {
-			rect 144 384 48 48
-			center 160 400 16 16
-		}
-
-		screen_list {
-			rect 192 384 48 48
-			center 208 400 16 16
-		}
-	}
-
-	misc {
-		button_big_topleft 0 0 16 16
-		button_big_topmid 16 0 16 16
-		button_big_topright 32 0 16 16
-		button_big_midleft 0 16 16 16
-		button_big_midmid 16 16 16 16
-		button_big_midright 32 16 16 16
-		button_big_btmleft 0 32 16 16
-		button_big_btmmid 16 32 16 16
-		button_big_btmright 32 32 16 16
-		slider_big_horiz_begin 0 48 16 16
-		slider_big_horiz_mid 16 48 16 16
-		slider_big_horiz_end 32 48 16 16
-		slider_small_horiz_begin 0 96 16 16
-		slider_small_horiz_mid 16 96 16 16
-		slider_small_horiz_end 32 96 16 16
-		slider_small_vert_begin 48 96 16 16
-		radio_unchecked 64 112 32 32
-		radio_checked 96 112 32 32
-		slider_small_vert_mid 48 112 16 16
-		slider_small_vert_end 48 128 16 16
-		slider_big_vert_begin 48 48 16 16
-		slider_big_handle_vert 64 48 16 32
-		slider_big_handle_horiz 80 64 32 16
-		slider_small_handle_horiz 80 48 16 16
-		slider_big_arrow_left 0 64 16 16
-		slider_big_arrow_up 16 64 16 16
-		slider_big_arrow_right 32 64 16 16
-		slider_big_arrow_down 16 80 16 16
-		slider_small_arrow_left 0 112 16 16
-		slider_small_arrow_up 16 112 16 16
-		slider_small_arrow_right 32 112 16 16
-		slider_small_arrow_down 16 128 16 16
-		slider_small_handle_vert 96 48 16 16
-		slider_big_vert_mid 48 64 16 16
-		slider_big_vert_end 48 80 16 16
-		checkbox_unchecked 64 112 32 32
-		checkbox_checked 96 112 32 32
-	}
-}
diff --git a/src/game/client/gc_anim.h b/src/game/client/gc_anim.h
new file mode 100644
index 00000000..115f7353
--- /dev/null
+++ b/src/game/client/gc_anim.h
@@ -0,0 +1,14 @@
+
+struct animstate
+{
+	keyframe body;
+	keyframe back_foot;
+	keyframe front_foot;
+	keyframe attach;
+};
+
+void anim_seq_eval(sequence *seq, float time, keyframe *frame);
+void anim_eval(animation *anim, float time, animstate *state);
+void anim_add_keyframe(keyframe *seq, keyframe *added, float amount);
+void anim_add(animstate *state, animstate *added, float amount);
+void anim_eval_add(animstate *state, animation *anim, float time, float amount);
diff --git a/src/game/client/gc_client.cpp b/src/game/client/gc_client.cpp
index 295b729f..556fabee 100644
--- a/src/game/client/gc_client.cpp
+++ b/src/game/client/gc_client.cpp
@@ -17,126 +17,52 @@ extern "C" {
 #include "../generated/gc_data.h"
 #include "gc_menu.h"
 #include "gc_skin.h"
-#include "gc_render.h"
 #include "gc_ui.h"
+#include "gc_client.h"
+#include "gc_render.h"
+#include "gc_anim.h"
 
-// sound channels
-enum
-{
-	CHN_GUI=0,
-	CHN_MUSIC,
-	CHN_WORLD,
-	CHN_GLOBAL,
-};
-
-data_container *data = 0x0;
-
-static player_input input_data = {0};
-static int input_target_lock = 0;
-
+struct data_container *data = 0;
 static int64 debug_firedelay = 0;
 
-extern void modmenu_render();
-extern void menu_init();
-
-enum
-{
-	CHATMODE_NONE=0,
-	CHATMODE_ALL,
-	CHATMODE_TEAM,
-	CHATMODE_CONSOLE,
-	CHATMODE_REMOTECONSOLE,
-};
+player_input input_data = {0};
+int input_target_lock = 0;
 
-static int chat_mode = CHATMODE_NONE;
+int chat_mode = CHATMODE_NONE;
 bool menu_active = false;
 bool menu_game_active = false;
-static int snapshot_count = 0;
 static bool emoticon_selector_active = false;
 
-static vec2 mouse_pos;
-static vec2 local_character_pos;
-static vec2 local_target_pos;
-static const obj_player_character *local_character = 0;
-static const obj_player_character *local_prev_character = 0;
+
+vec2 mouse_pos;
+vec2 local_character_pos;
+vec2 local_target_pos;
+const obj_player_character *local_character = 0;
+const obj_player_character *local_prev_character = 0;
 const obj_player_info *local_info = 0;
-static const obj_flag *flags[2] = {0,0};
+const obj_flag *flags[2] = {0,0};
 const obj_game *gameobj = 0;
 
-static int picked_up_weapon = -1;
-
-static struct client_data
-{
-	char name[64];
-	char skin_name[64];
-	int skin_id;
-	int skin_color;
-	int team;
-	int emoticon;
-	int emoticon_start;
-	player_core predicted;
-	
-	tee_render_info skin_info; // this is what the server reports
-	tee_render_info render_info; // this is what we use
-	
-	void update_render_info()
-	{
-		render_info = skin_info;
-
-		// force team colors
-		if(gameobj && gameobj->gametype != GAMETYPE_DM)
-		{
-			const int team_colors[2] = {65387, 10223467};
-			if(team >= 0 || team <= 1)
-			{
-				render_info.texture = skin_get(skin_id)->color_texture;
-				render_info.color_body = skin_get_color(team_colors[team]);
-				render_info.color_feet = skin_get_color(team_colors[team]);
-			}
-		}		
-	}
-} client_datas[MAX_CLIENTS];
+int picked_up_weapon = -1;
 
-class client_effects
+client_data client_datas[MAX_CLIENTS];
+void client_data::update_render_info()
 {
-public:
-	float zoom;
-	float currentzoom;
-	float stage;
-	int lastzoomin;
-	int lastincrease;
-
-	client_effects()
-	{
-		currentzoom = zoom = 3.0f;
-		stage = 0.0f;
-	}
+	render_info = skin_info;
 
-	float getorgzoom() { return zoom; }
-
-	float getzoom(int tick, float intratick, const obj_player_character *player)
+	// force team colors
+	if(gameobj && gameobj->gametype != GAMETYPE_DM)
 	{
-		float currentstage = ((float)player->weaponstage) * 0.1f;
-		if (currentstage < stage)
-		{
-			if ((tick - lastincrease) > (client_tickspeed() / 2))
-				stage = currentstage;
-		}
-		else
+		const int team_colors[2] = {65387, 10223467};
+		if(team >= 0 || team <= 1)
 		{
-			lastincrease = tick;
-			stage = currentstage;
+			render_info.texture = skin_get(skin_id)->color_texture;
+			render_info.color_body = skin_get_color(team_colors[team]);
+			render_info.color_feet = skin_get_color(team_colors[team]);
 		}
+	}		
+}
 
-		float targetzoom = 3.0f + stage;
-		currentzoom = LERP(currentzoom, targetzoom, 0.1);
-		return currentzoom;
-	}
-};
-
-client_effects cl_effects;
-
-inline float frandom() { return rand()/(float)(RAND_MAX); }
 
 void snd_play_random(int chn, int setid, float vol, vec2 pos)
 {
@@ -160,96 +86,6 @@ void snd_play_random(int chn, int setid, float vol, vec2 pos)
 	set->last = id;
 }
 
-// sound volume tweak
-static const float stereo_separation = 0.001f;
-static const float stereo_separation_deadzone = 200.0f;
-static const float volume_distance_falloff = 200.0f;
-static const float volume_distance_deadzone = 320.0f;
-static const float volume_gun = 0.5f;
-static const float volume_tee = 0.5f;
-static const float volume_hit = 0.5f;
-static const float volume_music = 0.8f;
-
-void sound_vol_pan(const vec2& p, float *vol, float *pan)
-{
-	vec2 player_to_ev = p - local_character_pos;
-	*pan = 0.0f;
-	*vol = 1.0f;
-
-	if(fabs(player_to_ev.x) > stereo_separation_deadzone)
-	{
-		*pan = stereo_separation * (player_to_ev.x - sign(player_to_ev.x)*stereo_separation_deadzone);
-		if(*pan < -1.0f) *pan = -1.0f;
-		if(*pan > 1.0f) *pan = 1.0f;
-	}
-
-	float len = length(player_to_ev);
-	if(len > volume_distance_deadzone)
-	{
-		*vol = volume_distance_falloff / (len - volume_distance_deadzone);
-
-		if(*vol < 0.0f) *vol = 0.0f;
-		if(*vol > 1.0f) *vol = 1.0f;
-	}
-}
-
-enum
-{
-	SPRITE_FLAG_FLIP_Y=1,
-	SPRITE_FLAG_FLIP_X=2,
-};
-
-static float sprite_w_scale;
-static float sprite_h_scale;
-
-static void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0)
-{
-	int x = spr->x+sx;
-	int y = spr->y+sy;
-	int w = spr->w;
-	int h = spr->h;
-	int cx = spr->set->gridx;
-	int cy = spr->set->gridy;
-
-	float f = sqrtf(h*h + w*w);
-	sprite_w_scale = w/f;
-	sprite_h_scale = h/f;
-	
-	float x1 = x/(float)cx;
-	float x2 = (x+w)/(float)cx;
-	float y1 = y/(float)cy;
-	float y2 = (y+h)/(float)cy;
-	float temp = 0;
-
-	if(flags&SPRITE_FLAG_FLIP_Y)
-	{
-		temp = y1;
-		y1 = y2;
-		y2 = temp;
-	}
-
-	if(flags&SPRITE_FLAG_FLIP_X)
-	{
-		temp = x1;
-		x1 = x2;
-		x2 = temp;
-	}
-	
-	gfx_quads_setsubset(x1, y1, x2, y2);
-}
-
-void select_sprite(int id, int flags=0, int sx=0, int sy=0)
-{
-	if(id < 0 || id > data->num_sprites)
-		return;
-	select_sprite(&data->sprites[id], flags, sx, sy);
-}
-
-void draw_sprite(float x, float y, float size)
-{
-	gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale);
-}
-
 class damage_indicators
 {
 public:
@@ -477,8 +313,14 @@ public:
 		}
 	}
 };
+
 static projectile_particles proj_particles;
 
+void reset_projectile_particles() // TODO: remove
+{
+	proj_particles.reset();
+}
+
 static char chat_input[512];
 static unsigned chat_input_len;
 static const int chat_max_lines = 10;
@@ -534,22 +376,11 @@ void chat_add_line(int client_id, int team, const char *line)
 	}
 }
 
-struct killmsg
-{
-	int weapon;
-	int victim;
-	int killer;
-	int mode_special; // for CTF, if the guy is carrying a flag for example
-	int tick;
-};
 
-static const int killmsg_max = 5;
 killmsg killmsgs[killmsg_max];
-static int killmsg_current = 0;
+int killmsg_current = 0;
 
-extern unsigned char internal_data[];
-
-void create_air_jump_effect(vec2 pos)
+void effect_air_jump(vec2 pos)
 {
 	const int count = 12;
 	for(int i = 0; i <= count; i++)
@@ -560,83 +391,15 @@ void create_air_jump_effect(vec2 pos)
 	}
 }
 
-
-extern void draw_round_rect(float x, float y, float w, float h, float r);
 extern int render_popup(const char *caption, const char *text, const char *button_text);
-void render_loading(float percent);
-
-extern "C" void modc_init()
-{
-	static FONT_SET default_font;
-
-	int before = gfx_memory_usage();
-	font_set_load(&default_font, "fonts/default_font%d.tfnt", "fonts/default_font%d.png", "fonts/default_font%d_b.png", 14, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 36);
-	dbg_msg("font", "gfx memory usage: %d", gfx_memory_usage()-before);
-	
-	gfx_text_set_default_font(&default_font);
-
-	menu_init();
-	
-	// setup sound channels
-	snd_set_channel(CHN_GUI, 1.0f, 0.0f);
-	snd_set_channel(CHN_MUSIC, 1.0f, 0.0f);
-	snd_set_channel(CHN_WORLD, 0.9f, 1.0f);
-	snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f);
-
-	// load the data container
-	data = load_data_from_memory(internal_data);
-
-	// TODO: should be removed
-	snd_set_listener_pos(0.0f, 0.0f);
-
-	float total = data->num_sounds+data->num_images;
-	float current = 0;
-
-	// load textures
-	for(int i = 0; i < data->num_images; i++)
-	{
-		render_loading(current/total);
-		data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO);
-		current++;
-	}
-	
-	// load sounds
-	for(int s = 0; s < data->num_sounds; s++)
-	{
-		render_loading(current/total);
-		for(int i = 0; i < data->sounds[s].num_sounds; i++)
-		{
-			int id;
-			//if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0)
-			id = snd_load_wv(data->sounds[s].sounds[i].filename);
-			//else
-			//	id = snd_load_wav(data->sounds[s].sounds[i].filename);
 
-			data->sounds[s].sounds[i].id = id;
-		}
-
-		current++;
-	}
-
-	skin_init();
-}
-
-extern "C" void modc_entergame()
-{
-}
-
-extern "C" void modc_shutdown()
-{
-	// shutdown the menu
-}
-
-static void process_events(int s)
+void process_events(int snaptype)
 {
-	int num = snap_num_items(s);
+	int num = snap_num_items(snaptype);
 	for(int index = 0; index < num; index++)
 	{
 		SNAP_ITEM item;
-		const void *data = snap_get_item(s, index, &item);
+		const void *data = snap_get_item(snaptype, index, &item);
 
 		if(item.type == EVENT_DAMAGEINDICATION)
 		{
@@ -646,7 +409,7 @@ static void process_events(int s)
 		else if(item.type == EVENT_AIR_JUMP)
 		{
 			ev_common *ev = (ev_common *)data;
-			create_air_jump_effect(vec2(ev->x, ev->y));
+			effect_air_jump(vec2(ev->x, ev->y));
 		}
 		else if(item.type == EVENT_EXPLOSION)
 		{
@@ -762,139 +525,7 @@ static void process_events(int s)
 	}
 }
 
-static player_core predicted_prev_player;
-static player_core predicted_player;
-static int predicted_tick = 0;
-static int last_new_predicted_tick = -1;
-
-extern "C" void modc_predict()
-{
-	player_core before_prev_player = predicted_prev_player;
-	player_core before_player = predicted_player;
-
-
-	// repredict player
-	world_core world;
-	int local_cid = -1;
-
-	// search for players
-	for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
-	{
-		SNAP_ITEM item;
-		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-		int client_id = item.id;
-
-		if(item.type == OBJTYPE_PLAYER_CHARACTER)
-		{
-			const obj_player_character *character = (const obj_player_character *)data;
-			client_datas[client_id].predicted.world = &world;
-			world.players[client_id] = &client_datas[client_id].predicted;
-
-			client_datas[client_id].predicted.read(character);
-		}
-		else if(item.type == OBJTYPE_PLAYER_INFO)
-		{
-			const obj_player_info *info = (const obj_player_info *)data;
-			if(info->local)
-				local_cid = client_id;
-		}
-	}
-
-	// predict
-	for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
-	{
-		// fetch the local
-		if(tick == client_predtick() && world.players[local_cid])
-			predicted_prev_player = *world.players[local_cid];
-		
-		// first calculate where everyone should move
-		for(int c = 0; c < MAX_CLIENTS; c++)
-		{
-			if(!world.players[c])
-				continue;
-
-			mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
-			if(local_cid == c)
-			{
-				// apply player input
-				int *input = client_get_input(tick);
-				if(input)
-					world.players[c]->input = *((player_input*)input);
-			}
-
-			world.players[c]->tick();
-		}
-
-		// move all players and quantize their data
-		for(int c = 0; c < MAX_CLIENTS; c++)
-		{
-			if(!world.players[c])
-				continue;
-
-			
-			// TODO: this should be moved into the g_game
-			// but not done to preserve the nethash
-			if(length(world.players[c]->vel) > 150.0f)
-				world.players[c]->vel = normalize(world.players[c]->vel) * 150.0f;
-
-			world.players[c]->move();
-			world.players[c]->quantize();
-		}
-		
-		if(tick > last_new_predicted_tick)
-		{
-			last_new_predicted_tick = tick;
-			
-			if(local_cid != -1 && world.players[local_cid])
-			{
-				vec2 pos = world.players[local_cid]->pos;
-				int events = world.players[local_cid]->triggered_events;
-				if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
-				if(events&COREEVENT_AIR_JUMP)
-				{
-					create_air_jump_effect(pos);
-					snd_play_random(CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos);
-				}
-				//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
-				if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos);
-				if(events&COREEVENT_HOOK_ATTACH_GROUND) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
-				//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
-			}
-
-
-			/*
-			dbg_msg("predict", "%d %d %d", tick,
-				(int)world.players[c]->pos.x, (int)world.players[c]->pos.y,
-				(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
-		}
-		
-		if(tick == client_predtick() && world.players[local_cid])
-			predicted_player = *world.players[local_cid];
-	}
-	
-	if(config.debug && predicted_tick == client_predtick())
-	{
-		if(predicted_player.pos.x != before_player.pos.x ||
-			predicted_player.pos.y != before_player.pos.y)
-		{
-			dbg_msg("client", "prediction error, (%d %d) (%d %d)", 
-				(int)before_player.pos.x, (int)before_player.pos.y,
-				(int)predicted_player.pos.x, (int)predicted_player.pos.y);
-		}
-
-		if(predicted_prev_player.pos.x != before_prev_player.pos.x ||
-			predicted_prev_player.pos.y != before_prev_player.pos.y)
-		{
-			dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", 
-				(int)before_prev_player.pos.x, (int)before_prev_player.pos.y,
-				(int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y);
-		}
-	}
-	
-	predicted_tick = client_predtick();
-}
-
-static void clear_object_pointers()
+void clear_object_pointers()
 {
 	// clear out the invalid pointers
 	local_character = 0;
@@ -905,69 +536,6 @@ static void clear_object_pointers()
 	gameobj = 0;
 }
 
-extern "C" void modc_newsnapshot()
-{
-	snapshot_count++;
-	process_events(SNAP_CURRENT);
-
-	if(config.dbg_stress)
-	{
-		if((client_tick()%250) == 0)
-		{
-			msg_pack_start(MSG_SAY, MSGFLAG_VITAL);
-			msg_pack_int(-1);
-			msg_pack_string("galenskap!!!!", 512);
-			msg_pack_end();
-			client_send_msg();
-		}
-	}
-
-	clear_object_pointers();
-
-	// setup world view
-	{
-		// 1. fetch local player
-		// 2. set him to the center
-		int num = snap_num_items(SNAP_CURRENT);
-		for(int i = 0; i < num; i++)
-		{
-			SNAP_ITEM item;
-			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
-
-			if(item.type == OBJTYPE_PLAYER_INFO)
-			{
-				const obj_player_info *info = (const obj_player_info *)data;
-				
-				client_datas[info->clientid].team = info->team;
-				
-				if(info->local)
-				{
-					local_info = info;
-					const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id);
-					if(data)
-					{
-						local_character = (const obj_player_character *)data;
-						local_character_pos = vec2(local_character->x, local_character->y);
-
-						const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id);
-						if(p)
-							local_prev_character = (obj_player_character *)p;
-					}
-				}
-			}
-			else if(item.type == OBJTYPE_GAME)
-				gameobj = (obj_game *)data;
-			else if(item.type == OBJTYPE_FLAG)
-			{
-				flags[item.id%2] = (const obj_flag *)data;
-			}
-		}
-	}
-
-	for(int i = 0; i < MAX_CLIENTS; i++)
-		client_datas[i].update_render_info();
-}
-
 void send_info(bool start)
 {
 	if(start)
@@ -991,121 +559,6 @@ void send_emoticon(int emoticon)
 	client_send_msg();
 }
 
-static void render_projectile(const obj_projectile *current, int itemid)
-{
-	if(debug_firedelay)
-	{
-		debug_firedelay = time_get()-debug_firedelay;
-		dbg_msg("game", "firedelay=%.2f ms", debug_firedelay/(float)time_freq()*1000.0f);
-		debug_firedelay = 0;
-	}
-	
-	gfx_texture_set(data->images[IMAGE_GAME].id);
-	gfx_quads_begin();
-
-	// get positions
-	float gravity = -400;
-	if(current->type != WEAPON_ROCKET)
-		gravity = -100;
-	if(current->type == WEAPON_BOMB)
-		gravity = 0;
-
-	float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED;
-	vec2 startpos(current->x, current->y);
-	vec2 startvel(current->vx, current->vy);
-	vec2 pos = calc_pos(startpos, startvel, gravity, ct);
-	vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f);
-
-	select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj);
-	vec2 vel = pos-prevpos;
-	//vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
-
-	// add particle for this projectile
-	proj_particles.addparticle(current->type, itemid, pos, vel);
-
-	if(length(vel) > 0.00001f)
-		gfx_quads_setrotation(get_angle(vel));
-	else
-		gfx_quads_setrotation(0);
-
-	// TODO: do this, but nice
-	//temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f);
-
-	gfx_quads_draw(pos.x, pos.y, 32, 32);
-	gfx_quads_setrotation(0);
-	gfx_quads_end();
-}
-
-static void render_powerup(const obj_powerup *prev, const obj_powerup *current)
-{
-	gfx_texture_set(data->images[IMAGE_GAME].id);
-	gfx_quads_begin();
-	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
-	float angle = 0.0f;
-	float size = 64.0f;
-	if (current->type == POWERUP_WEAPON)
-	{
-		angle = 0; //-pi/6;//-0.25f * pi * 2.0f;
-		select_sprite(data->weapons[current->subtype%data->num_weapons].sprite_body);
-		size = data->weapons[current->subtype%data->num_weapons].visual_size;
-	}
-	else
-	{
-		const int c[] = {
-			SPRITE_POWERUP_HEALTH,
-			SPRITE_POWERUP_ARMOR,
-			SPRITE_POWERUP_WEAPON,
-			SPRITE_POWERUP_NINJA,
-			SPRITE_POWERUP_TIMEFIELD
-			};
-		select_sprite(c[current->type]);
-
-		if(c[current->type] == SPRITE_POWERUP_NINJA)
-		{
-			proj_particles.addparticle(0, 0,
-				pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f),
-				vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f));
-			size *= 2.0f;
-			pos.x += 10.0f;
-		}
-	}
-
-	gfx_quads_setrotation(angle);
-
-	float offset = pos.y/32.0f + pos.x/32.0f;
-	pos.x += cosf(client_localtime()*2.0f+offset)*2.5f;
-	pos.y += sinf(client_localtime()*2.0f+offset)*2.5f;
-	draw_sprite(pos.x, pos.y, size);
-	gfx_quads_end();
-}
-
-static void render_flag(const obj_flag *prev, const obj_flag *current)
-{
-	float angle = 0.0f;
-	float size = 42.0f;
-
-    gfx_blend_normal();
-    gfx_texture_set(data->images[IMAGE_GAME].id);
-    gfx_quads_begin();
-
-	if(current->team == 0) // red team
-		select_sprite(SPRITE_FLAG_RED);
-	else
-		select_sprite(SPRITE_FLAG_BLUE);
-
-	gfx_quads_setrotation(angle);
-
-	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
-
-	if(local_info && current->carried_by == local_info->clientid)
-		pos = local_character_pos;
-
-    //gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1);
-    //draw_sprite(pos.x, pos.y, size);
-    gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2);
-    gfx_quads_end();
-}
-
 void anim_seq_eval(sequence *seq, float time, keyframe *frame)
 {
 	if(seq->num_frames == 0)
@@ -1178,127 +631,6 @@ void anim_eval_add(animstate *state, animation *anim, float time, float amount)
 	anim_add(state, &add, amount);
 }
 
-static void render_hand(tee_render_info *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset)
-{
-	// for drawing hand
-	//const skin *s = skin_get(skin_id);
-	
-	float basesize = 10.0f;
-	//dir = normalize(hook_pos-pos);
-
-	vec2 hand_pos = center_pos + dir;
-	float angle = get_angle(dir);
-	if (dir.x < 0)
-		angle -= angle_offset;
-	else
-		angle += angle_offset;
-
-	vec2 dirx = dir;
-	vec2 diry(-dir.y,dir.x);
-
-	if (dir.x < 0)
-		diry = -diry;
-
-	hand_pos += dirx * post_rot_offset.x;
-	hand_pos += diry * post_rot_offset.y;
-
-	//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
-	gfx_texture_set(info->texture);
-	gfx_quads_begin();
-	gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a);
-
-	// two passes
-	for (int i = 0; i < 2; i++)
-	{
-		bool outline = i == 0;
-
-		select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0);
-		gfx_quads_setrotation(angle);
-		gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize);
-	}
-
-	gfx_quads_setrotation(0);
-	gfx_quads_end();
-}
-
-void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos)
-{
-	vec2 direction = dir;
-	vec2 position = pos;
-
-	//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
-	gfx_texture_set(info->texture);
-	gfx_quads_begin();
-	//gfx_quads_draw(pos.x, pos.y-128, 128, 128);
-
-	// first pass we draw the outline
-	// second pass we draw the filling
-	for(int p = 0; p < 2; p++)
-	{
-		int outline = p==0 ? 1 : 0;
-
-		for(int f = 0; f < 2; f++)
-		{
-			float animscale = info->size * 1.0f/64.0f;
-			float basesize = info->size;
-			if(f == 1)
-			{
-				gfx_quads_setrotation(anim->body.angle*pi*2);
-
-				// draw body
-				gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a);
-				vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale;
-				select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0);
-				gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize);
-
-				// draw eyes
-				if(p == 1)
-				{
-					switch (emote)
-					{
-						case EMOTE_PAIN:
-							select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0);
-							break;
-						case EMOTE_HAPPY:
-							select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0);
-							break;
-						case EMOTE_SURPRISE:
-							select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0);
-							break;
-						case EMOTE_ANGRY:
-							select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0);
-							break;
-						default:
-							select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0);
-							break;
-					}
-					
-					float eyescale = basesize*0.40f;
-					float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale;
-					float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize;
-					vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize;
-					gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h);
-					gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h);
-				}
-			}
-
-			// draw feet
-			gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a);
-			select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0);
-
-			keyframe *foot = f ? &anim->front_foot : &anim->back_foot;
-
-			float w = basesize;
-			float h = basesize/2;
-
-			gfx_quads_setrotation(foot->angle*pi*2);
-			gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h);
-		}
-	}
-
-	gfx_quads_end();
-}
-
 void draw_circle(float x, float y, float r, int segments)
 {
 	float f_segments = (float)segments;
@@ -1322,373 +654,6 @@ void draw_circle(float x, float y, float r, int segments)
 	}
 }
 
-void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners)
-{
-	int num = 8;
-	for(int i = 0; i < num; i+=2)
-	{
-		float a1 = i/(float)num * pi/2;
-		float a2 = (i+1)/(float)num * pi/2;
-		float a3 = (i+2)/(float)num * pi/2;
-		float ca1 = cosf(a1);
-		float ca2 = cosf(a2);
-		float ca3 = cosf(a3);
-		float sa1 = sinf(a1);
-		float sa2 = sinf(a2);
-		float sa3 = sinf(a3);
-
-		if(corners&1) // TL
-		gfx_quads_draw_freeform(
-			x+r, y+r,
-			x+(1-ca1)*r, y+(1-sa1)*r,
-			x+(1-ca3)*r, y+(1-sa3)*r,
-			x+(1-ca2)*r, y+(1-sa2)*r);
-
-		if(corners&2) // TR
-		gfx_quads_draw_freeform(
-			x+w-r, y+r,
-			x+w-r+ca1*r, y+(1-sa1)*r,
-			x+w-r+ca3*r, y+(1-sa3)*r,
-			x+w-r+ca2*r, y+(1-sa2)*r);
-
-		if(corners&4) // BL
-		gfx_quads_draw_freeform(
-			x+r, y+h-r,
-			x+(1-ca1)*r, y+h-r+sa1*r,
-			x+(1-ca3)*r, y+h-r+sa3*r,
-			x+(1-ca2)*r, y+h-r+sa2*r);
-
-		if(corners&8) // BR
-		gfx_quads_draw_freeform(
-			x+w-r, y+h-r,
-			x+w-r+ca1*r, y+h-r+sa1*r,
-			x+w-r+ca3*r, y+h-r+sa3*r,
-			x+w-r+ca2*r, y+h-r+sa2*r);
-	}
-
-	gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center
-	gfx_quads_drawTL(x+r, y, w-r*2, r); // top
-	gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom
-	gfx_quads_drawTL(x, y+r, r, h-r*2); // left
-	gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right
-	
-	if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL
-	if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR
-	if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL
-	if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR
-}
-
-void draw_round_rect(float x, float y, float w, float h, float r)
-{
-	draw_round_rect_ext(x,y,w,h,r,0xf);
-}
-
-
-static void render_player(
-	const obj_player_character *prev_char,
-	const obj_player_character *player_char,
-	const obj_player_info *prev_info,
-	const obj_player_info *player_info
-	)
-{
-	obj_player_character prev;
-	obj_player_character player;
-	prev = *prev_char;
-	player = *player_char;
-
-	obj_player_info info = *player_info;
-
-	float intratick = client_intratick();
-	float ticktime = client_ticktime();
-
-	if(player.health < 0) // dont render dead players
-		return;
-
-	if(info.local && config.cl_predict)
-	{
-		if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over))
-		{
-		}
-		else
-		{
-			// apply predicted results
-			predicted_player.write(&player);
-			predicted_prev_player.write(&prev);
-			intratick = client_intrapredtick();
-		}
-	}
-
-	vec2 direction = get_direction(player.angle);
-	float angle = player.angle/256.0f;
-	vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick);
-
-	if(prev.health < 0) // Don't flicker from previous position
-		position = vec2(player.x, player.y);
-
-	bool stationary = player.vx < 1 && player.vx > -1;
-	bool inair = col_check_point(player.x, player.y+16) == 0;
-
-	// evaluate animation
-	float walk_time = fmod(position.x, 100.0f)/100.0f;
-	animstate state;
-	anim_eval(&data->animations[ANIM_BASE], 0, &state);
-
-	if(inair)
-		anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here
-	else if(stationary)
-		anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here
-	else
-		anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f);
-
-	if (player.weapon == WEAPON_HAMMER)
-	{
-		float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f);
-		anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f);
-	}
-	if (player.weapon == WEAPON_NINJA)
-	{
-		float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f);
-		anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f);
-	}
-
-	// draw hook
-	if (prev.hook_state>0 && player.hook_state>0)
-	{
-		gfx_texture_set(data->images[IMAGE_GAME].id);
-		gfx_quads_begin();
-		//gfx_quads_begin();
-
-		vec2 pos = position;
-		vec2 hook_pos;
-		
-		if(player_char->hooked_player != -1)
-		{
-			if(local_info && player_char->hooked_player == local_info->clientid)
-			{
-				hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y),
-					vec2(predicted_player.pos.x, predicted_player.pos.y), client_intrapredtick());
-			}
-			else
-				hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick());
-		}
-		else
-			hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick);
-
-		float d = distance(pos, hook_pos);
-		vec2 dir = normalize(pos-hook_pos);
-
-		gfx_quads_setrotation(get_angle(dir)+pi);
-
-		// render head
-		select_sprite(SPRITE_HOOK_HEAD);
-		gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16);
-
-		// render chain
-		select_sprite(SPRITE_HOOK_CHAIN);
-		for(float f = 24; f < d; f += 24)
-		{
-			vec2 p = hook_pos + dir*f;
-			gfx_quads_draw(p.x, p.y,24,16);
-		}
-
-		gfx_quads_setrotation(0);
-		gfx_quads_end();
-
-		render_hand(&client_datas[info.clientid].render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0));
-	}
-
-	// draw gun
-	{
-		gfx_texture_set(data->images[IMAGE_GAME].id);
-		gfx_quads_begin();
-		gfx_quads_setrotation(state.attach.angle*pi*2+angle);
-
-		// normal weapons
-		int iw = clamp(player.weapon, 0, NUM_WEAPONS-1);
-		select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
-
-		vec2 dir = direction;
-		float recoil = 0.0f;
-		vec2 p;
-		if (player.weapon == WEAPON_HAMMER)
-		{
-			// Static position for hammer
-			p = position;
-			p.y += data->weapons[iw].offsety;
-			// if attack is under way, bash stuffs
-			if(direction.x < 0)
-			{
-				gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
-				p.x -= data->weapons[iw].offsetx;
-			}
-			else
-			{
-				gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
-			}
-			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
-		}
-		else if (player.weapon == WEAPON_NINJA)
-		{
-			p = position;
-			p.y += data->weapons[iw].offsety;
-
-			if(direction.x < 0)
-			{
-				gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
-				p.x -= data->weapons[iw].offsetx;
-			}
-			else
-			{
-				gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
-			}
-			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
-
-			// HADOKEN
-			if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
-			{
-				int itex = rand() % data->weapons[iw].nummuzzlesprites;
-				float alpha = 1.0f;
-				if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
-				{
-					vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y);
-					dir = normalize(dir);
-					float hadokenangle = get_angle(dir);
-					gfx_quads_setrotation(hadokenangle);
-					//float offsety = -data->weapons[iw].muzzleoffsety;
-					select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 0);
-					vec2 diry(-dir.y,dir.x);
-					p = position;
-					float offsetx = data->weapons[iw].muzzleoffsetx;
-					p -= dir * offsetx;
-					draw_sprite(p.x, p.y, 160.0f);
-				}
-			}
-		}
-		else
-		{
-			// TODO: should be an animation
-			recoil = 0;
-			float a = (client_tick()-player.attacktick+intratick)/5.0f;
-			if(a < 1)
-				recoil = sinf(a*pi);
-			p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f;
-			p.y += data->weapons[iw].offsety;
-			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
-		}
-
-		if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
-		{
-			// check if we're firing stuff
-			if (true)//prev.attackticks)
-			{
-				float alpha = 0.0f;
-				int phase1tick = (client_tick() - player.attacktick);
-				if (phase1tick < (data->weapons[iw].muzzleduration + 3))
-				{
-					float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration);
-					alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t)));
-				}
-
-				int itex = rand() % data->weapons[iw].nummuzzlesprites;
-				if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
-				{
-					float offsety = -data->weapons[iw].muzzleoffsety;
-					select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
-					if(direction.x < 0)
-						offsety = -offsety;
-
-					vec2 diry(-dir.y,dir.x);
-					vec2 muzzlepos = p + dir * data->weapons[iw].muzzleoffsetx + diry * offsety;
-
-					draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size);
-					/*gfx_setcolor(1.0f,1.0f,1.0f,alpha);
-					vec2 diry(-dir.y,dir.x);
-					p += dir * muzzleparams[player.weapon].offsetx + diry * offsety;
-					gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/
-				}
-			}
-		}
-		gfx_quads_end();
-
-		switch (player.weapon)
-		{
-			case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
-			case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break;
-			case WEAPON_ROCKET: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
-		}
-
-	}
-
-	// render the "shadow" tee
-	if(info.local && config.debug)
-	{
-		vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick());
-		tee_render_info ghost = client_datas[info.clientid].render_info;
-		ghost.color_body.a = 0.5f;
-		ghost.color_feet.a = 0.5f;
-		render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost
-	}
-
-	// render the tee
-	render_tee(&state, &client_datas[info.clientid].render_info, player.emote, direction, position);
-
-	if(player.state == STATE_CHATTING)
-	{
-		gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
-		gfx_quads_begin();
-		select_sprite(SPRITE_DOTDOT);
-		gfx_quads_draw(position.x + 24, position.y - 40, 64,64);
-		gfx_quads_end();
-	}
-
-	if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
-	{
-		gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
-		gfx_quads_begin();
-
-		int since_start = client_tick() - client_datas[info.clientid].emoticon_start;
-		int from_end = client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
-
-		float a = 1;
-
-		if (from_end < client_tickspeed() / 5)
-			a = from_end / (client_tickspeed() / 5.0);
-
-		float h = 1;
-		if (since_start < client_tickspeed() / 10)
-			h = since_start / (client_tickspeed() / 10.0);
-
-		float wiggle = 0;
-		if (since_start < client_tickspeed() / 5)
-			wiggle = since_start / (client_tickspeed() / 5.0);
-
-		float wiggle_angle = sin(5*wiggle);
-
-		gfx_quads_setrotation(pi/6*wiggle_angle);
-
-		gfx_setcolor(1.0f,1.0f,1.0f,a);
-		// client_datas::emoticon is an offset from the first emoticon
-		select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon);
-		gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
-		gfx_quads_end();
-	}
-	
-	// render name plate
-	if(!info.local && config.cl_nameplates)
-	{
-		//gfx_text_color
-		float a = 1;
-		if(config.cl_nameplates_always == 0)
-			a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f);
-			
-		const char *name = client_datas[info.clientid].name;
-		float tw = gfx_text_width(0, 28.0f, name, -1);
-		gfx_text_color(1,1,1,a);
-		gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1);
-		gfx_text_color(1,1,1,1);
-	}
-}
-
 void render_moon(float x, float y)
 {
 	gfx_texture_set(data->images[IMAGE_MOON].id);
@@ -2570,8 +1535,6 @@ void render_game()
 	// pseudo format
 	// ZOOM ZOOM
 	float zoom = 3.0;
-	if(local_character)
-		cl_effects.getzoom(client_tick(), client_intratick(), local_character);
 
 	// DEBUG TESTING
 	if(zoom > 3.01f)
@@ -2989,7 +1952,7 @@ void render_game()
 
 	if (menu_active)
 	{
-		modmenu_render();
+		menu_render();
 		return;
 	}
 
@@ -3073,211 +2036,3 @@ void render_game()
 	}
 }
 
-extern "C" void modc_render()
-{
-	// this should be moved around abit
-	if(client_state() == CLIENTSTATE_ONLINE)
-	{
-		render_game();
-
-		// handle team switching
-		// TODO: FUGLY!!!
-		if(config.cl_team != -10)
-		{
-			msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL);
-			msg_pack_int(config.cl_team);
-			msg_pack_end();
-			client_send_msg();
-		}
-	}
-	else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING)
-	{
-		modmenu_render();
-	}
-
-	//
-	config.cl_team = -10;
-}
-
-
-void menu_do_disconnected();
-void menu_do_connecting();
-void menu_do_connected();
-
-extern "C" int modc_snap_input(int *data)
-{
-	picked_up_weapon = -1;
-
-	if(!input_target_lock)
-	{
-		input_data.target_x = (int)mouse_pos.x;
-		input_data.target_y = (int)mouse_pos.y;
-		
-		if(!input_data.target_x && !input_data.target_y)
-			input_data.target_y = 1;
-	}
-	input_target_lock = 0;
-
-	if(chat_mode != CHATMODE_NONE)
-		input_data.state = STATE_CHATTING;
-	else if(menu_active)
-		input_data.state = STATE_IN_MENU;
-	else
-	{
-		input_data.state = STATE_PLAYING;
-		input_data.left = inp_key_state(config.key_move_left);
-		input_data.right = inp_key_state(config.key_move_right);
-		input_data.hook = inp_key_state(config.key_hook);
-		input_data.jump  = inp_key_state(config.key_jump);
-	}
-
-	// stress testing
-	if(config.dbg_stress)
-	{
-		float t = client_localtime();
-		mem_zero(&input_data, sizeof(input_data));
-
-		input_data.left = ((int)t/2)&1;
-		input_data.right = ((int)t/2+1)&1;
-		input_data.jump = ((int)t);
-		input_data.fire = ((int)(t*10));
-		input_data.hook = ((int)(t*2))&1;
-		input_data.wanted_weapon = ((int)t)%NUM_WEAPONS;
-		input_data.target_x = (int)(sinf(t*3)*100.0f);
-		input_data.target_y = (int)(cosf(t*3)*100.0f);
-	}
-
-	// copy and return size	
-	mem_copy(data, &input_data, sizeof(input_data));
-	return sizeof(input_data);
-}
-
-extern "C" void modc_statechange(int state, int old)
-{
-	clear_object_pointers();
-	
-	if(state == CLIENTSTATE_OFFLINE)
-	{
-	 	menu_do_disconnected();
-	 	menu_game_active = false;
-	}
-	else if(state == CLIENTSTATE_LOADING)
-		menu_do_connecting();
-	else if(state == CLIENTSTATE_CONNECTING)
-		menu_do_connecting();
-	else if (state == CLIENTSTATE_ONLINE)
-	{
-		menu_active = false;
-	 	menu_game_active = true;
-	 	snapshot_count = 0;
-	 	
-		menu_do_connected();
-	}
-}
-
-extern "C" void modc_message(int msg)
-{
-	if(msg == MSG_CHAT)
-	{
-		int cid = msg_unpack_int();
-		int team = msg_unpack_int();
-		const char *message = msg_unpack_string();
-		dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message);
-		chat_add_line(cid, team, message);
-
-		if(cid >= 0)
-			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0);
-		else
-			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0);
-	}
-	else if(msg == MSG_SETINFO)
-	{
-		int cid = msg_unpack_int();
-		const char *name = msg_unpack_string();
-		const char *skinname = msg_unpack_string();
-		
-		strncpy(client_datas[cid].name, name, 64);
-		strncpy(client_datas[cid].skin_name, skinname, 64);
-		
-		int use_custom_color = msg_unpack_int();
-		client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int());
-		client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int());
-		client_datas[cid].skin_info.size = 64;
-		
-		// find new skin
-		client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name);
-		if(client_datas[cid].skin_id < 0)
-			client_datas[cid].skin_id = 0;
-		
-		if(use_custom_color)
-			client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture;
-		else
-		{
-			client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture;
-			client_datas[cid].skin_info.color_body = vec4(1,1,1,1);
-			client_datas[cid].skin_info.color_feet = vec4(1,1,1,1);
-		}
-		
-		client_datas[cid].update_render_info();
-	}
-    else if(msg == MSG_WEAPON_PICKUP)
-    {
-        int weapon = msg_unpack_int();
-
-        picked_up_weapon = weapon+1;
-    }
-	else if(msg == MSG_READY_TO_ENTER)
-	{
-		client_entergame();
-	}
-	else if(msg == MSG_KILLMSG)
-	{
-		killmsg_current = (killmsg_current+1)%killmsg_max;
-		killmsgs[killmsg_current].killer = msg_unpack_int();
-		killmsgs[killmsg_current].victim = msg_unpack_int();
-		killmsgs[killmsg_current].weapon = msg_unpack_int();
-		killmsgs[killmsg_current].mode_special = msg_unpack_int();
-		killmsgs[killmsg_current].tick = client_tick();
-	}
-	else if (msg == MSG_EMOTICON)
-	{
-		int cid = msg_unpack_int();
-		int emoticon = msg_unpack_int();
-		client_datas[cid].emoticon = emoticon;
-		client_datas[cid].emoticon_start = client_tick();
-	}
-	else if(msg == MSG_SOUND_GLOBAL)
-	{
-		int soundid = msg_unpack_int();
-		snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0));
-	}
-}
-
-extern "C" void modc_connected()
-{
-	// init some stuff
-	col_init(32);
-	img_init();
-	tilemap_init();
-	chat_reset();
-
-	proj_particles.reset();
-	
-	clear_object_pointers();
-	last_new_predicted_tick = -1;
-
-	for(int i = 0; i < MAX_CLIENTS; i++)
-	{
-		client_datas[i].name[0] = 0;
-		client_datas[i].team = 0;
-		client_datas[i].emoticon = 0;
-		client_datas[i].emoticon_start = -1;
-	}
-
-	for(int i = 0; i < killmsg_max; i++)
-		killmsgs[i].tick = -100000;
-		
-	send_info(true);
-}
-
-extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; }
diff --git a/src/game/client/gc_client.h b/src/game/client/gc_client.h
new file mode 100644
index 00000000..d34ccc78
--- /dev/null
+++ b/src/game/client/gc_client.h
@@ -0,0 +1,93 @@
+#include <game/g_vmath.h>
+#include <game/g_protocol.h>
+#include <game/g_game.h>
+
+#include <game/client/gc_render.h>
+
+// sound channels
+enum
+{
+	CHN_GUI=0,
+	CHN_MUSIC,
+	CHN_WORLD,
+	CHN_GLOBAL,
+};
+
+extern struct data_container *data;
+
+extern vec2 mouse_pos;
+extern vec2 local_character_pos;
+extern vec2 local_target_pos;
+
+// snap pointers
+extern const obj_player_character *local_character;
+extern const obj_player_character *local_prev_character;
+extern const obj_player_info *local_info;
+extern const obj_flag *flags[2];
+extern const obj_game *gameobj;
+
+// predicted players
+extern player_core predicted_prev_player;
+extern player_core predicted_player;
+
+// input
+extern int picked_up_weapon;
+extern player_input input_data;
+extern int input_target_lock;
+
+// chat
+enum
+{
+	CHATMODE_NONE=0,
+	CHATMODE_ALL,
+	CHATMODE_TEAM,
+	CHATMODE_CONSOLE,
+	CHATMODE_REMOTECONSOLE,
+};
+
+extern int chat_mode;
+void chat_add_line(int client_id, int team, const char *line);
+void chat_reset();
+
+// client data
+struct client_data
+{
+	char name[64];
+	char skin_name[64];
+	int skin_id;
+	int skin_color;
+	int team;
+	int emoticon;
+	int emoticon_start;
+	player_core predicted;
+	
+	tee_render_info skin_info; // this is what the server reports
+	tee_render_info render_info; // this is what we use
+	
+	void update_render_info();
+};
+
+extern client_data client_datas[MAX_CLIENTS];
+
+// kill messages
+struct killmsg
+{
+	int weapon;
+	int victim;
+	int killer;
+	int mode_special; // for CTF, if the guy is carrying a flag for example
+	int tick;
+};
+
+const int killmsg_max = 5;
+extern killmsg killmsgs[killmsg_max];
+extern int killmsg_current;
+
+// various helpers
+void snd_play_random(int chn, int setid, float vol, vec2 pos);
+void process_events(int snaptype);
+void clear_object_pointers();
+void reset_projectile_particles();
+void send_info(bool start);
+
+void effect_air_jump(vec2 pos);
diff --git a/src/game/client/gc_hooks.cpp b/src/game/client/gc_hooks.cpp
new file mode 100644
index 00000000..febb6478
--- /dev/null
+++ b/src/game/client/gc_hooks.cpp
@@ -0,0 +1,500 @@
+#include <string.h>
+
+#include <engine/e_interface.h>
+
+extern "C" {
+	#include <engine/e_config.h>
+	#include <engine/client/ec_font.h>
+};
+
+#include <game/generated/gc_data.h>
+#include <game/g_game.h>
+#include <game/g_version.h>
+
+#include "gc_client.h"
+#include "gc_skin.h"
+#include "gc_render.h"
+#include "gc_mapres_image.h"
+#include "gc_mapres_tilemap.h"
+
+extern unsigned char internal_data[];
+
+extern void menu_init();
+extern bool menu_active;
+extern bool menu_game_active;
+
+
+extern "C" void modc_init()
+{
+	static FONT_SET default_font;
+
+	int before = gfx_memory_usage();
+	font_set_load(&default_font, "fonts/default_font%d.tfnt", "fonts/default_font%d.png", "fonts/default_font%d_b.png", 14, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 36);
+	dbg_msg("font", "gfx memory usage: %d", gfx_memory_usage()-before);
+	
+	gfx_text_set_default_font(&default_font);
+
+	menu_init();
+	
+	// setup sound channels
+	snd_set_channel(CHN_GUI, 1.0f, 0.0f);
+	snd_set_channel(CHN_MUSIC, 1.0f, 0.0f);
+	snd_set_channel(CHN_WORLD, 0.9f, 1.0f);
+	snd_set_channel(CHN_GLOBAL, 1.0f, 0.0f);
+
+	// load the data container
+	data = load_data_from_memory(internal_data);
+
+	// TODO: should be removed
+	snd_set_listener_pos(0.0f, 0.0f);
+
+	float total = data->num_sounds+data->num_images;
+	float current = 0;
+
+	// load textures
+	for(int i = 0; i < data->num_images; i++)
+	{
+		render_loading(current/total);
+		data->images[i].id = gfx_load_texture(data->images[i].filename, IMG_AUTO);
+		current++;
+	}
+	
+	// load sounds
+	for(int s = 0; s < data->num_sounds; s++)
+	{
+		render_loading(current/total);
+		for(int i = 0; i < data->sounds[s].num_sounds; i++)
+		{
+			int id;
+			//if (strcmp(data->sounds[s].sounds[i].filename + strlen(data->sounds[s].sounds[i].filename) - 3, ".wv") == 0)
+			id = snd_load_wv(data->sounds[s].sounds[i].filename);
+			//else
+			//	id = snd_load_wav(data->sounds[s].sounds[i].filename);
+
+			data->sounds[s].sounds[i].id = id;
+		}
+
+		current++;
+	}
+
+	skin_init();
+}
+
+extern "C" void modc_entergame()
+{
+}
+
+extern "C" void modc_shutdown()
+{
+	// shutdown the menu
+}
+
+
+player_core predicted_prev_player;
+player_core predicted_player;
+static int predicted_tick = 0;
+static int last_new_predicted_tick = -1;
+
+extern "C" void modc_predict()
+{
+	player_core before_prev_player = predicted_prev_player;
+	player_core before_player = predicted_player;
+
+	// repredict player
+	world_core world;
+	int local_cid = -1;
+
+	// search for players
+	for(int i = 0; i < snap_num_items(SNAP_CURRENT); i++)
+	{
+		SNAP_ITEM item;
+		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
+		int client_id = item.id;
+
+		if(item.type == OBJTYPE_PLAYER_CHARACTER)
+		{
+			const obj_player_character *character = (const obj_player_character *)data;
+			client_datas[client_id].predicted.world = &world;
+			world.players[client_id] = &client_datas[client_id].predicted;
+
+			client_datas[client_id].predicted.read(character);
+		}
+		else if(item.type == OBJTYPE_PLAYER_INFO)
+		{
+			const obj_player_info *info = (const obj_player_info *)data;
+			if(info->local)
+				local_cid = client_id;
+		}
+	}
+
+	// predict
+	for(int tick = client_tick()+1; tick <= client_predtick(); tick++)
+	{
+		// fetch the local
+		if(tick == client_predtick() && world.players[local_cid])
+			predicted_prev_player = *world.players[local_cid];
+		
+		// first calculate where everyone should move
+		for(int c = 0; c < MAX_CLIENTS; c++)
+		{
+			if(!world.players[c])
+				continue;
+
+			mem_zero(&world.players[c]->input, sizeof(world.players[c]->input));
+			if(local_cid == c)
+			{
+				// apply player input
+				int *input = client_get_input(tick);
+				if(input)
+					world.players[c]->input = *((player_input*)input);
+			}
+
+			world.players[c]->tick();
+		}
+
+		// move all players and quantize their data
+		for(int c = 0; c < MAX_CLIENTS; c++)
+		{
+			if(!world.players[c])
+				continue;
+
+			
+			// TODO: this should be moved into the g_game
+			// but not done to preserve the nethash
+			if(length(world.players[c]->vel) > 150.0f)
+				world.players[c]->vel = normalize(world.players[c]->vel) * 150.0f;
+
+			world.players[c]->move();
+			world.players[c]->quantize();
+		}
+		
+		if(tick > last_new_predicted_tick)
+		{
+			last_new_predicted_tick = tick;
+			
+			if(local_cid != -1 && world.players[local_cid])
+			{
+				vec2 pos = world.players[local_cid]->pos;
+				int events = world.players[local_cid]->triggered_events;
+				if(events&COREEVENT_GROUND_JUMP) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
+				if(events&COREEVENT_AIR_JUMP)
+				{
+					effect_air_jump(pos);
+					snd_play_random(CHN_WORLD, SOUND_PLAYER_AIRJUMP, 1.0f, pos);
+				}
+				//if(events&COREEVENT_HOOK_LAUNCH) snd_play_random(CHN_WORLD, SOUND_HOOK_LOOP, 1.0f, pos);
+				if(events&COREEVENT_HOOK_ATTACH_PLAYER) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_PLAYER, 1.0f, pos);
+				if(events&COREEVENT_HOOK_ATTACH_GROUND) snd_play_random(CHN_WORLD, SOUND_HOOK_ATTACH_GROUND, 1.0f, pos);
+				//if(events&COREEVENT_HOOK_RETRACT) snd_play_random(CHN_WORLD, SOUND_PLAYER_JUMP, 1.0f, pos);
+			}
+
+
+			/*
+			dbg_msg("predict", "%d %d %d", tick,
+				(int)world.players[c]->pos.x, (int)world.players[c]->pos.y,
+				(int)world.players[c]->vel.x, (int)world.players[c]->vel.y);*/
+		}
+		
+		if(tick == client_predtick() && world.players[local_cid])
+			predicted_player = *world.players[local_cid];
+	}
+	
+	if(config.debug && predicted_tick == client_predtick())
+	{
+		if(predicted_player.pos.x != before_player.pos.x ||
+			predicted_player.pos.y != before_player.pos.y)
+		{
+			dbg_msg("client", "prediction error, (%d %d) (%d %d)", 
+				(int)before_player.pos.x, (int)before_player.pos.y,
+				(int)predicted_player.pos.x, (int)predicted_player.pos.y);
+		}
+
+		if(predicted_prev_player.pos.x != before_prev_player.pos.x ||
+			predicted_prev_player.pos.y != before_prev_player.pos.y)
+		{
+			dbg_msg("client", "prediction error, prev (%d %d) (%d %d)", 
+				(int)before_prev_player.pos.x, (int)before_prev_player.pos.y,
+				(int)predicted_prev_player.pos.x, (int)predicted_prev_player.pos.y);
+		}
+	}
+	
+	predicted_tick = client_predtick();
+}
+
+
+extern "C" void modc_newsnapshot()
+{
+	static int snapshot_count = 0;
+	snapshot_count++;
+	
+	process_events(SNAP_CURRENT);
+
+	if(config.dbg_stress)
+	{
+		if((client_tick()%250) == 0)
+		{
+			msg_pack_start(MSG_SAY, MSGFLAG_VITAL);
+			msg_pack_int(-1);
+			msg_pack_string("galenskap!!!!", 512);
+			msg_pack_end();
+			client_send_msg();
+		}
+	}
+
+	clear_object_pointers();
+
+	// setup world view
+	{
+		// 1. fetch local player
+		// 2. set him to the center
+		int num = snap_num_items(SNAP_CURRENT);
+		for(int i = 0; i < num; i++)
+		{
+			SNAP_ITEM item;
+			const void *data = snap_get_item(SNAP_CURRENT, i, &item);
+
+			if(item.type == OBJTYPE_PLAYER_INFO)
+			{
+				const obj_player_info *info = (const obj_player_info *)data;
+				
+				client_datas[info->clientid].team = info->team;
+				
+				if(info->local)
+				{
+					local_info = info;
+					const void *data = snap_find_item(SNAP_CURRENT, OBJTYPE_PLAYER_CHARACTER, item.id);
+					if(data)
+					{
+						local_character = (const obj_player_character *)data;
+						local_character_pos = vec2(local_character->x, local_character->y);
+
+						const void *p = snap_find_item(SNAP_PREV, OBJTYPE_PLAYER_CHARACTER, item.id);
+						if(p)
+							local_prev_character = (obj_player_character *)p;
+					}
+				}
+			}
+			else if(item.type == OBJTYPE_GAME)
+				gameobj = (obj_game *)data;
+			else if(item.type == OBJTYPE_FLAG)
+			{
+				flags[item.id%2] = (const obj_flag *)data;
+			}
+		}
+	}
+
+	for(int i = 0; i < MAX_CLIENTS; i++)
+		client_datas[i].update_render_info();
+}
+
+
+
+extern "C" void modc_render()
+{
+	// this should be moved around abit
+	if(client_state() == CLIENTSTATE_ONLINE)
+	{
+		render_game();
+
+		// handle team switching
+		// TODO: FUGLY!!!
+		if(config.cl_team != -10)
+		{
+			msg_pack_start(MSG_SETTEAM, MSGFLAG_VITAL);
+			msg_pack_int(config.cl_team);
+			msg_pack_end();
+			client_send_msg();
+		}
+	}
+	else // if (client_state() != CLIENTSTATE_CONNECTING && client_state() != CLIENTSTATE_LOADING)
+	{
+		menu_render();
+	}
+
+	//
+	config.cl_team = -10;
+}
+
+
+extern "C" int modc_snap_input(int *data)
+{
+	picked_up_weapon = -1;
+
+	if(!input_target_lock)
+	{
+		input_data.target_x = (int)mouse_pos.x;
+		input_data.target_y = (int)mouse_pos.y;
+		
+		if(!input_data.target_x && !input_data.target_y)
+			input_data.target_y = 1;
+	}
+	input_target_lock = 0;
+
+	if(chat_mode != CHATMODE_NONE)
+		input_data.state = STATE_CHATTING;
+	else if(menu_active)
+		input_data.state = STATE_IN_MENU;
+	else
+	{
+		input_data.state = STATE_PLAYING;
+		input_data.left = inp_key_state(config.key_move_left);
+		input_data.right = inp_key_state(config.key_move_right);
+		input_data.hook = inp_key_state(config.key_hook);
+		input_data.jump  = inp_key_state(config.key_jump);
+	}
+
+	// stress testing
+	if(config.dbg_stress)
+	{
+		float t = client_localtime();
+		mem_zero(&input_data, sizeof(input_data));
+
+		input_data.left = ((int)t/2)&1;
+		input_data.right = ((int)t/2+1)&1;
+		input_data.jump = ((int)t);
+		input_data.fire = ((int)(t*10));
+		input_data.hook = ((int)(t*2))&1;
+		input_data.wanted_weapon = ((int)t)%NUM_WEAPONS;
+		input_data.target_x = (int)(sinf(t*3)*100.0f);
+		input_data.target_y = (int)(cosf(t*3)*100.0f);
+	}
+
+	// copy and return size	
+	mem_copy(data, &input_data, sizeof(input_data));
+	return sizeof(input_data);
+}
+
+void menu_do_disconnected();
+void menu_do_connecting();
+void menu_do_connected();
+
+extern "C" void modc_statechange(int state, int old)
+{
+	clear_object_pointers();
+	
+	if(state == CLIENTSTATE_OFFLINE)
+	{
+	 	menu_do_disconnected();
+	 	menu_game_active = false;
+	}
+	else if(state == CLIENTSTATE_LOADING)
+		menu_do_connecting();
+	else if(state == CLIENTSTATE_CONNECTING)
+		menu_do_connecting();
+	else if (state == CLIENTSTATE_ONLINE)
+	{
+		menu_active = false;
+	 	menu_game_active = true;
+	 	//snapshot_count = 0;
+	 	
+		menu_do_connected();
+	}
+}
+
+extern "C" void modc_message(int msg)
+{
+	if(msg == MSG_CHAT)
+	{
+		int cid = msg_unpack_int();
+		int team = msg_unpack_int();
+		const char *message = msg_unpack_string();
+		dbg_msg("message", "chat cid=%d team=%d msg='%s'", cid, team, message);
+		chat_add_line(cid, team, message);
+
+		if(cid >= 0)
+			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_CLIENT].sounds[0].id, 0);
+		else
+			snd_play(CHN_GUI, data->sounds[SOUND_CHAT_SERVER].sounds[0].id, 0);
+	}
+	else if(msg == MSG_SETINFO)
+	{
+		int cid = msg_unpack_int();
+		const char *name = msg_unpack_string();
+		const char *skinname = msg_unpack_string();
+		
+		strncpy(client_datas[cid].name, name, 64);
+		strncpy(client_datas[cid].skin_name, skinname, 64);
+		
+		int use_custom_color = msg_unpack_int();
+		client_datas[cid].skin_info.color_body = skin_get_color(msg_unpack_int());
+		client_datas[cid].skin_info.color_feet = skin_get_color(msg_unpack_int());
+		client_datas[cid].skin_info.size = 64;
+		
+		// find new skin
+		client_datas[cid].skin_id = skin_find(client_datas[cid].skin_name);
+		if(client_datas[cid].skin_id < 0)
+			client_datas[cid].skin_id = 0;
+		
+		if(use_custom_color)
+			client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->color_texture;
+		else
+		{
+			client_datas[cid].skin_info.texture = skin_get(client_datas[cid].skin_id)->org_texture;
+			client_datas[cid].skin_info.color_body = vec4(1,1,1,1);
+			client_datas[cid].skin_info.color_feet = vec4(1,1,1,1);
+		}
+		
+		client_datas[cid].update_render_info();
+	}
+    else if(msg == MSG_WEAPON_PICKUP)
+    {
+        int weapon = msg_unpack_int();
+
+        picked_up_weapon = weapon+1;
+    }
+	else if(msg == MSG_READY_TO_ENTER)
+	{
+		client_entergame();
+	}
+	else if(msg == MSG_KILLMSG)
+	{
+		killmsg_current = (killmsg_current+1)%killmsg_max;
+		killmsgs[killmsg_current].killer = msg_unpack_int();
+		killmsgs[killmsg_current].victim = msg_unpack_int();
+		killmsgs[killmsg_current].weapon = msg_unpack_int();
+		killmsgs[killmsg_current].mode_special = msg_unpack_int();
+		killmsgs[killmsg_current].tick = client_tick();
+	}
+	else if (msg == MSG_EMOTICON)
+	{
+		int cid = msg_unpack_int();
+		int emoticon = msg_unpack_int();
+		client_datas[cid].emoticon = emoticon;
+		client_datas[cid].emoticon_start = client_tick();
+	}
+	else if(msg == MSG_SOUND_GLOBAL)
+	{
+		int soundid = msg_unpack_int();
+		snd_play_random(CHN_GLOBAL, soundid, 1.0f, vec2(0,0));
+	}
+}
+
+extern "C" void modc_connected()
+{
+	// init some stuff
+	col_init(32);
+	
+	img_init();
+	tilemap_init();
+	chat_reset();
+
+	reset_projectile_particles();
+	
+	clear_object_pointers();
+	last_new_predicted_tick = -1;
+
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		client_datas[i].name[0] = 0;
+		client_datas[i].team = 0;
+		client_datas[i].emoticon = 0;
+		client_datas[i].emoticon_start = -1;
+	}
+
+	for(int i = 0; i < killmsg_max; i++)
+		killmsgs[i].tick = -100000;
+		
+	send_info(true);
+}
+
+extern "C" const char *modc_net_version() { return TEEWARS_NETVERSION; }
diff --git a/src/game/client/gc_mapres_tilemap.cpp b/src/game/client/gc_mapres_tilemap.cpp
index d94add17..99976b64 100644
--- a/src/game/client/gc_mapres_tilemap.cpp
+++ b/src/game/client/gc_mapres_tilemap.cpp
@@ -5,6 +5,12 @@
 #include "gc_mapres_image.h"
 #include "../g_mapres.h"
 
+enum
+{
+	TILEFLAG_VFLIP=1,
+	TILEFLAG_HFLIP=2,
+};
+	
 int tilemap_init()
 {
 	return 0;
diff --git a/src/game/client/gc_mapres_tilemap.h b/src/game/client/gc_mapres_tilemap.h
index a13495ed..5dfbd6b5 100644
--- a/src/game/client/gc_mapres_tilemap.h
+++ b/src/game/client/gc_mapres_tilemap.h
@@ -8,12 +8,6 @@ int tilemap_init();
 // renders the tilemaps
 void tilemap_render(float scale, int fg);
 
-enum
-{
-	TILEFLAG_VFLIP=1,
-	TILEFLAG_HFLIP=2,
-};
-
 struct mapres_tilemap
 {
 	int image;
diff --git a/src/game/client/gc_menu.cpp b/src/game/client/gc_menu.cpp
index 34558402..e0c2a19f 100644
--- a/src/game/client/gc_menu.cpp
+++ b/src/game/client/gc_menu.cpp
@@ -23,6 +23,7 @@ extern "C" {
 
 #include "../generated/gc_data.h"
 #include "gc_render.h"
+#include "gc_anim.h"
 #include "gc_skin.h"
 #include "gc_ui.h"
 #include <mastersrv/mastersrv.h>
@@ -73,11 +74,6 @@ enum
 	PAGE_SYSTEM,
 };
 
-extern void select_sprite(int id, int flags=0, int sx=0, int sy=0);
-
-extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners);
-extern void draw_round_rect(float x, float y, float w, float h, float r);
-
 static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
 {
 	gfx_texture_set(-1);
@@ -259,12 +255,6 @@ int ui_do_edit_box(void *id, const RECT *rect, char *str, int str_size, bool hid
 		r = 1;
 	}
 
-	int box_type;
-	if (ui_active_item() == id || ui_hot_item() == id || ui_last_active_item() == id)
-		box_type = GUI_BOX_SCREEN_INFO;
-	else
-		box_type = GUI_BOX_SCREEN_TEXTBOX;
-
 	bool just_got_active = false;
 	
 	if(ui_active_item() == id)
@@ -1972,7 +1962,7 @@ int menu2_render()
 	return 0;
 }
 
-void modmenu_render()
+void menu_render()
 {
 	static int mouse_x = 0;
 	static int mouse_y = 0;
diff --git a/src/game/client/gc_menu.h b/src/game/client/gc_menu.h
index 5c68c53d..bd6352de 100644
--- a/src/game/client/gc_menu.h
+++ b/src/game/client/gc_menu.h
@@ -1,7 +1,7 @@
 /* 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);
@@ -12,5 +12,5 @@ int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int
 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
new file mode 100644
index 00000000..9a4c35d5
--- /dev/null
+++ b/src/game/client/gc_render.cpp
@@ -0,0 +1,237 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <math.h>
+#include <engine/e_interface.h>
+#include <engine/e_config.h>
+#include "../generated/gc_data.h"
+#include "../g_protocol.h"
+#include "../g_math.h"
+#include "gc_render.h"
+#include "gc_anim.h"
+#include "gc_client.h"
+
+static float sprite_w_scale;
+static float sprite_h_scale;
+
+void select_sprite(sprite *spr, int flags, int sx, int sy)
+{
+	int x = spr->x+sx;
+	int y = spr->y+sy;
+	int w = spr->w;
+	int h = spr->h;
+	int cx = spr->set->gridx;
+	int cy = spr->set->gridy;
+
+	float f = sqrtf(h*h + w*w);
+	sprite_w_scale = w/f;
+	sprite_h_scale = h/f;
+	
+	float x1 = x/(float)cx;
+	float x2 = (x+w)/(float)cx;
+	float y1 = y/(float)cy;
+	float y2 = (y+h)/(float)cy;
+	float temp = 0;
+
+	if(flags&SPRITE_FLAG_FLIP_Y)
+	{
+		temp = y1;
+		y1 = y2;
+		y2 = temp;
+	}
+
+	if(flags&SPRITE_FLAG_FLIP_X)
+	{
+		temp = x1;
+		x1 = x2;
+		x2 = temp;
+	}
+	
+	gfx_quads_setsubset(x1, y1, x2, y2);
+}
+
+void select_sprite(int id, int flags, int sx, int sy)
+{
+	if(id < 0 || id > data->num_sprites)
+		return;
+	select_sprite(&data->sprites[id], flags, sx, sy);
+}
+
+void draw_sprite(float x, float y, float size)
+{
+	gfx_quads_draw(x, y, size*sprite_w_scale, size*sprite_h_scale);
+}
+
+
+
+void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners)
+{
+	int num = 8;
+	for(int i = 0; i < num; i+=2)
+	{
+		float a1 = i/(float)num * pi/2;
+		float a2 = (i+1)/(float)num * pi/2;
+		float a3 = (i+2)/(float)num * pi/2;
+		float ca1 = cosf(a1);
+		float ca2 = cosf(a2);
+		float ca3 = cosf(a3);
+		float sa1 = sinf(a1);
+		float sa2 = sinf(a2);
+		float sa3 = sinf(a3);
+
+		if(corners&1) // TL
+		gfx_quads_draw_freeform(
+			x+r, y+r,
+			x+(1-ca1)*r, y+(1-sa1)*r,
+			x+(1-ca3)*r, y+(1-sa3)*r,
+			x+(1-ca2)*r, y+(1-sa2)*r);
+
+		if(corners&2) // TR
+		gfx_quads_draw_freeform(
+			x+w-r, y+r,
+			x+w-r+ca1*r, y+(1-sa1)*r,
+			x+w-r+ca3*r, y+(1-sa3)*r,
+			x+w-r+ca2*r, y+(1-sa2)*r);
+
+		if(corners&4) // BL
+		gfx_quads_draw_freeform(
+			x+r, y+h-r,
+			x+(1-ca1)*r, y+h-r+sa1*r,
+			x+(1-ca3)*r, y+h-r+sa3*r,
+			x+(1-ca2)*r, y+h-r+sa2*r);
+
+		if(corners&8) // BR
+		gfx_quads_draw_freeform(
+			x+w-r, y+h-r,
+			x+w-r+ca1*r, y+h-r+sa1*r,
+			x+w-r+ca3*r, y+h-r+sa3*r,
+			x+w-r+ca2*r, y+h-r+sa2*r);
+	}
+
+	gfx_quads_drawTL(x+r, y+r, w-r*2, h-r*2); // center
+	gfx_quads_drawTL(x+r, y, w-r*2, r); // top
+	gfx_quads_drawTL(x+r, y+h-r, w-r*2, r); // bottom
+	gfx_quads_drawTL(x, y+r, r, h-r*2); // left
+	gfx_quads_drawTL(x+w-r, y+r, r, h-r*2); // right
+	
+	if(!(corners&1)) gfx_quads_drawTL(x, y, r, r); // TL
+	if(!(corners&2)) gfx_quads_drawTL(x+w, y, -r, r); // TR
+	if(!(corners&4)) gfx_quads_drawTL(x, y+h, r, -r); // BL
+	if(!(corners&8)) gfx_quads_drawTL(x+w, y+h, -r, -r); // BR
+}
+
+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 render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos)
+{
+	vec2 direction = dir;
+	vec2 position = pos;
+
+	//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
+	gfx_texture_set(info->texture);
+	gfx_quads_begin();
+	//gfx_quads_draw(pos.x, pos.y-128, 128, 128);
+
+	// first pass we draw the outline
+	// second pass we draw the filling
+	for(int p = 0; p < 2; p++)
+	{
+		int outline = p==0 ? 1 : 0;
+
+		for(int f = 0; f < 2; f++)
+		{
+			float animscale = info->size * 1.0f/64.0f;
+			float basesize = info->size;
+			if(f == 1)
+			{
+				gfx_quads_setrotation(anim->body.angle*pi*2);
+
+				// draw body
+				gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a);
+				vec2 body_pos = position + vec2(anim->body.x, anim->body.y)*animscale;
+				select_sprite(outline?SPRITE_TEE_BODY_OUTLINE:SPRITE_TEE_BODY, 0, 0, 0);
+				gfx_quads_draw(body_pos.x, body_pos.y, basesize, basesize);
+
+				// draw eyes
+				if(p == 1)
+				{
+					switch (emote)
+					{
+						case EMOTE_PAIN:
+							select_sprite(SPRITE_TEE_EYE_PAIN, 0, 0, 0);
+							break;
+						case EMOTE_HAPPY:
+							select_sprite(SPRITE_TEE_EYE_HAPPY, 0, 0, 0);
+							break;
+						case EMOTE_SURPRISE:
+							select_sprite(SPRITE_TEE_EYE_SURPRISE, 0, 0, 0);
+							break;
+						case EMOTE_ANGRY:
+							select_sprite(SPRITE_TEE_EYE_ANGRY, 0, 0, 0);
+							break;
+						default:
+							select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, 0);
+							break;
+					}
+					
+					float eyescale = basesize*0.40f;
+					float h = emote == EMOTE_BLINK ? basesize*0.15f : eyescale;
+					float eyeseparation = (0.075f - 0.010f*fabs(direction.x))*basesize;
+					vec2 offset = vec2(direction.x*0.125f, -0.05f+direction.y*0.10f)*basesize;
+					gfx_quads_draw(body_pos.x-eyeseparation+offset.x, body_pos.y+offset.y, eyescale, h);
+					gfx_quads_draw(body_pos.x+eyeseparation+offset.x, body_pos.y+offset.y, -eyescale, h);
+				}
+			}
+
+			// draw feet
+			gfx_setcolor(info->color_feet.r, info->color_feet.g, info->color_feet.b, info->color_feet.a);
+			select_sprite(outline?SPRITE_TEE_FOOT_OUTLINE:SPRITE_TEE_FOOT, 0, 0, 0);
+
+			keyframe *foot = f ? &anim->front_foot : &anim->back_foot;
+
+			float w = basesize;
+			float h = basesize/2;
+
+			gfx_quads_setrotation(foot->angle*pi*2);
+			gfx_quads_draw(position.x+foot->x*animscale, position.y+foot->y*animscale, w, h);
+		}
+	}
+
+	gfx_quads_end();
+}
+
+static void calc_screen_params(float amount, float wmax, float hmax, float aspect, float *w, float *h)
+{
+	float f = sqrt(amount) / sqrt(aspect);
+	*w = f*aspect;
+	*h = f;
+	
+	// limit the view
+	if(*w > wmax)
+	{
+		*w = wmax;
+		*h = *w/aspect;
+	}
+	
+	if(*h > hmax)
+	{
+		*h = hmax;
+		*w = *h*aspect;
+	}
+}
+
+void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
+	float offset_x, float offset_y, float aspect, float zoom, float *points)
+{
+	float width, height;
+	calc_screen_params(1300*1000, 1500, 1050, aspect, &width, &height);
+	center_x *= parallax_x;
+	center_y *= parallax_y;
+	width *= zoom;
+	height *= zoom;
+	points[0] = offset_x+center_x-width/2;
+	points[1] = offset_y+center_y-height/2;
+	points[2] = offset_x+center_x+width/2;
+	points[3] = offset_y+center_y+height/2;
+}
diff --git a/src/game/client/gc_render.h b/src/game/client/gc_render.h
index fc85d49a..2f17f6b5 100644
--- a/src/game/client/gc_render.h
+++ b/src/game/client/gc_render.h
@@ -1,17 +1,9 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
-struct animstate
-{
-	keyframe body;
-	keyframe back_foot;
-	keyframe front_foot;
-	keyframe attach;
-};
+#ifndef GAME_CLIENT_RENDER_H
+#define GAME_CLIENT_RENDER_H
 
-void anim_seq_eval(sequence *seq, float time, keyframe *frame);
-void anim_eval(animation *anim, float time, animstate *state);
-void anim_add_keyframe(keyframe *seq, keyframe *added, float amount);
-void anim_add(animstate *state, animstate *added, float amount);
-void anim_eval_add(animstate *state, animation *anim, float time, float amount);
+#include "../g_vmath.h"
+#include "../g_mapitems.h"
 
 struct tee_render_info
 {
@@ -21,4 +13,46 @@ struct tee_render_info
 	float size;
 };
 
-void render_tee(animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos);
+// sprite renderings
+enum
+{
+	SPRITE_FLAG_FLIP_Y=1,
+	SPRITE_FLAG_FLIP_X=2,
+};
+
+typedef struct sprite;
+
+void select_sprite(sprite *spr, int flags=0, int sx=0, int sy=0);
+void select_sprite(int id, int flags=0, int sx=0, int sy=0);
+
+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);
+
+// larger rendering methods
+void menu_render();
+void render_game();
+void render_world(float center_x, float center_y, float zoom);
+void render_loading(float percent);
+
+// object render methods (gc_render_obj.cpp)
+void render_tee(class animstate *anim, tee_render_info *info, int emote, vec2 dir, vec2 pos);
+void render_flag(const struct obj_flag *prev, const struct obj_flag *current);
+void render_powerup(const struct obj_powerup *prev, const struct obj_powerup *current);
+void render_projectile(const struct obj_projectile *current, int itemid);
+void render_player(
+	const struct obj_player_character *prev_char, const struct obj_player_character *player_char,
+	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_tilemap(TILE *tiles, int w, int h, float scale);
+
+// helpers
+void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
+	float offset_x, float offset_y, float aspect, float zoom, float *points);
+
+
+#endif
diff --git a/src/game/client/gc_render_map.cpp b/src/game/client/gc_render_map.cpp
new file mode 100644
index 00000000..fcb8c272
--- /dev/null
+++ b/src/game/client/gc_render_map.cpp
@@ -0,0 +1,167 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <engine/e_interface.h>
+#include "../g_math.h"
+#include "gc_client.h"
+
+static void rotate(POINT *center, POINT *point, float rotation)
+{
+	int x = point->x - center->x;
+	int y = point->y - center->y;
+	point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x);
+	point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
+}
+
+void render_quads(QUAD *quads, int num_quads)
+{
+	gfx_quads_begin();
+	float conv = 1/255.0f;
+	for(int i = 0; i < num_quads; i++)
+	{
+		QUAD *q = &quads[i];
+		
+		gfx_quads_setsubset_free(
+			fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y),
+			fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y),
+			fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y),
+			fx2f(q->texcoords[3].x), fx2f(q->texcoords[3].y)
+		);
+
+		float r=1, g=1, b=1, a=1;
+		float offset_x = 0;
+		float offset_y = 0;
+		float rot = 0;
+		
+		/*
+		// TODO: fix this
+		if(editor.animate)
+		{
+			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);
+			}
+		}*/
+		
+		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);
+		gfx_setcolorvertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a);
+		gfx_setcolorvertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a);
+
+		POINT *points = q->points;
+	
+		if(rot != 0)
+		{
+			static POINT rotated[4];
+			rotated[0] = q->points[0];
+			rotated[1] = q->points[1];
+			rotated[2] = q->points[2];
+			rotated[3] = q->points[3];
+			points = rotated;
+			
+			rotate(&q->points[4], &rotated[0], rot);
+			rotate(&q->points[4], &rotated[1], rot);
+			rotate(&q->points[4], &rotated[2], rot);
+			rotate(&q->points[4], &rotated[3], rot);
+		}
+		
+		gfx_quads_draw_freeform(
+			fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y,
+			fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y,
+			fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y,
+			fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y
+		);
+	}
+	gfx_quads_end();	
+}
+
+
+void render_tilemap(TILE *tiles, int w, int h, float scale)
+{
+			//gfx_texture_set(img_get(tmap->image));
+	float screen_x0, screen_y0, screen_x1, screen_y1;
+	gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
+
+	// calculate the final pixelsize for the tiles	
+	float tile_pixelsize = 1024/32.0f;
+	float final_tilesize = scale/(screen_x1-screen_x0) * gfx_screenwidth();
+	float final_tilesize_scale = final_tilesize/tile_pixelsize;
+	
+	gfx_quads_begin();
+	
+	int starty = (int)(screen_y0/scale)-1;
+	int startx = (int)(screen_x0/scale)-1;
+	int endy = (int)(screen_y1/scale)+1;
+	int endx = (int)(screen_x1/scale)+1;
+	
+	// adjust the texture shift according to mipmap level
+	float texsize = 1024.0f;
+	float frac = (1.25f/texsize) * (1/final_tilesize_scale);
+	float nudge = (0.5f/texsize) * (1/final_tilesize_scale);
+
+	for(int y = starty; y < endy; y++)
+		for(int x = startx; x < endx; x++)
+		{
+			int mx = x;
+			int my = y;
+			if(mx<0)
+				continue; // mx = 0;
+			if(mx>=w)
+				continue; // mx = w-1;
+			if(my<0)
+				continue; // my = 0;
+			if(my>=h)
+				continue; // my = h-1;
+			
+			int c = mx + my*w;
+				
+			unsigned char index = tiles[c].index;
+			if(index)
+			{
+				unsigned char flags = tiles[c].flags;
+				int tx = index%16;
+				int ty = index/16;
+				int px0 = tx*(1024/16);
+				int py0 = ty*(1024/16);
+				int px1 = (tx+1)*(1024/16)-1;
+				int py1 = (ty+1)*(1024/16)-1;
+				
+				float u0 = nudge + px0/texsize+frac;
+				float v0 = nudge + py0/texsize+frac;
+				float u1 = nudge + px1/texsize-frac;
+				float v1 = nudge + py1/texsize-frac;
+				
+				if(flags&TILEFLAG_VFLIP)
+				{
+					float tmp = u0;
+					u0 = u1;
+					u1 = tmp;
+				}
+
+				if(flags&TILEFLAG_HFLIP)
+				{
+					float tmp = v0;
+					v0 = v1;
+					v1 = tmp;
+				}
+				
+				gfx_quads_setsubset(u0,v0,u1,v1);
+
+				gfx_quads_drawTL(x*scale, y*scale, scale, scale);
+			}
+		}
+	
+	gfx_quads_end();
+}
diff --git a/src/game/client/gc_render_obj.cpp b/src/game/client/gc_render_obj.cpp
new file mode 100644
index 00000000..ba248907
--- /dev/null
+++ b/src/game/client/gc_render_obj.cpp
@@ -0,0 +1,478 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#include <math.h>
+#include <engine/e_interface.h>
+#include <engine/e_config.h>
+#include "../generated/gc_data.h"
+#include "../g_protocol.h"
+#include "../g_math.h"
+#include "gc_render.h"
+#include "gc_anim.h"
+#include "gc_client.h"
+
+
+void render_projectile(const obj_projectile *current, int itemid)
+{
+	/*
+	if(debug_firedelay)
+	{
+		debug_firedelay = time_get()-debug_firedelay;
+		dbg_msg("game", "firedelay=%.2f ms", debug_firedelay/(float)time_freq()*1000.0f);
+		debug_firedelay = 0;
+	}*/
+	
+	gfx_texture_set(data->images[IMAGE_GAME].id);
+	gfx_quads_begin();
+
+	// get positions
+	float gravity = -400;
+	if(current->type != WEAPON_ROCKET)
+		gravity = -100;
+	if(current->type == WEAPON_BOMB)
+		gravity = 0;
+
+	float ct = (client_tick()-current->start_tick)/(float)SERVER_TICK_SPEED + client_ticktime()*1/(float)SERVER_TICK_SPEED;
+	vec2 startpos(current->x, current->y);
+	vec2 startvel(current->vx, current->vy);
+	vec2 pos = calc_pos(startpos, startvel, gravity, ct);
+	vec2 prevpos = calc_pos(startpos, startvel, gravity, ct-0.001f);
+
+	select_sprite(data->weapons[current->type%data->num_weapons].sprite_proj);
+	vec2 vel = pos-prevpos;
+	//vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
+
+	// add particle for this projectile
+	//proj_particles.addparticle(current->type, itemid, pos, vel);
+
+	if(length(vel) > 0.00001f)
+		gfx_quads_setrotation(get_angle(vel));
+	else
+		gfx_quads_setrotation(0);
+
+	// TODO: do this, but nice
+	//temp_system.new_particle(pos, vec2(0,0), 0.3f, 14.0f, 0, 0.95f);
+
+	gfx_quads_draw(pos.x, pos.y, 32, 32);
+	gfx_quads_setrotation(0);
+	gfx_quads_end();
+}
+
+void render_powerup(const obj_powerup *prev, const obj_powerup *current)
+{
+	gfx_texture_set(data->images[IMAGE_GAME].id);
+	gfx_quads_begin();
+	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
+	float angle = 0.0f;
+	float size = 64.0f;
+	if (current->type == POWERUP_WEAPON)
+	{
+		angle = 0; //-pi/6;//-0.25f * pi * 2.0f;
+		select_sprite(data->weapons[current->subtype%data->num_weapons].sprite_body);
+		size = data->weapons[current->subtype%data->num_weapons].visual_size;
+	}
+	else
+	{
+		const int c[] = {
+			SPRITE_POWERUP_HEALTH,
+			SPRITE_POWERUP_ARMOR,
+			SPRITE_POWERUP_WEAPON,
+			SPRITE_POWERUP_NINJA,
+			SPRITE_POWERUP_TIMEFIELD
+			};
+		select_sprite(c[current->type]);
+
+		if(c[current->type] == SPRITE_POWERUP_NINJA)
+		{
+			/*
+			proj_particles.addparticle(0, 0,
+				pos+vec2((frandom()-0.5f)*80.0f, (frandom()-0.5f)*20.0f),
+				vec2((frandom()-0.5f)*10.0f, (frandom()-0.5f)*10.0f));*/
+			size *= 2.0f;
+			pos.x += 10.0f;
+		}
+	}
+
+	gfx_quads_setrotation(angle);
+
+	float offset = pos.y/32.0f + pos.x/32.0f;
+	pos.x += cosf(client_localtime()*2.0f+offset)*2.5f;
+	pos.y += sinf(client_localtime()*2.0f+offset)*2.5f;
+	draw_sprite(pos.x, pos.y, size);
+	gfx_quads_end();
+}
+
+void render_flag(const obj_flag *prev, const obj_flag *current)
+{
+	float angle = 0.0f;
+	float size = 42.0f;
+
+    gfx_blend_normal();
+    gfx_texture_set(data->images[IMAGE_GAME].id);
+    gfx_quads_begin();
+
+	if(current->team == 0) // red team
+		select_sprite(SPRITE_FLAG_RED);
+	else
+		select_sprite(SPRITE_FLAG_BLUE);
+
+	gfx_quads_setrotation(angle);
+
+	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), client_intratick());
+
+	if(local_info && current->carried_by == local_info->clientid)
+		pos = local_character_pos;
+
+    //gfx_setcolor(current->team ? 0 : 1,0,current->team ? 1 : 0,1);
+    //draw_sprite(pos.x, pos.y, size);
+    gfx_quads_draw(pos.x, pos.y-size*0.75f, size, size*2);
+    gfx_quads_end();
+}
+
+
+
+static void render_hand(tee_render_info *info, vec2 center_pos, vec2 dir, float angle_offset, vec2 post_rot_offset)
+{
+	// for drawing hand
+	//const skin *s = skin_get(skin_id);
+	
+	float basesize = 10.0f;
+	//dir = normalize(hook_pos-pos);
+
+	vec2 hand_pos = center_pos + dir;
+	float angle = get_angle(dir);
+	if (dir.x < 0)
+		angle -= angle_offset;
+	else
+		angle += angle_offset;
+
+	vec2 dirx = dir;
+	vec2 diry(-dir.y,dir.x);
+
+	if (dir.x < 0)
+		diry = -diry;
+
+	hand_pos += dirx * post_rot_offset.x;
+	hand_pos += diry * post_rot_offset.y;
+
+	//gfx_texture_set(data->images[IMAGE_CHAR_DEFAULT].id);
+	gfx_texture_set(info->texture);
+	gfx_quads_begin();
+	gfx_setcolor(info->color_body.r, info->color_body.g, info->color_body.b, info->color_body.a);
+
+	// two passes
+	for (int i = 0; i < 2; i++)
+	{
+		bool outline = i == 0;
+
+		select_sprite(outline?SPRITE_TEE_HAND_OUTLINE:SPRITE_TEE_HAND, 0, 0, 0);
+		gfx_quads_setrotation(angle);
+		gfx_quads_draw(hand_pos.x, hand_pos.y, 2*basesize, 2*basesize);
+	}
+
+	gfx_quads_setrotation(0);
+	gfx_quads_end();
+}
+
+void render_player(
+	const obj_player_character *prev_char,
+	const obj_player_character *player_char,
+	const obj_player_info *prev_info,
+	const obj_player_info *player_info
+	)
+{
+	obj_player_character prev;
+	obj_player_character player;
+	prev = *prev_char;
+	player = *player_char;
+
+	obj_player_info info = *player_info;
+
+	float intratick = client_intratick();
+	float ticktime = client_ticktime();
+
+	if(player.health < 0) // dont render dead players
+		return;
+
+	if(info.local && config.cl_predict)
+	{
+		if(!local_character || (local_character->health < 0) || (gameobj && gameobj->game_over))
+		{
+		}
+		else
+		{
+			// apply predicted results
+			predicted_player.write(&player);
+			predicted_prev_player.write(&prev);
+			intratick = client_intrapredtick();
+		}
+	}
+
+	vec2 direction = get_direction(player.angle);
+	float angle = player.angle/256.0f;
+	vec2 position = mix(vec2(prev.x, prev.y), vec2(player.x, player.y), intratick);
+
+	if(prev.health < 0) // Don't flicker from previous position
+		position = vec2(player.x, player.y);
+
+	bool stationary = player.vx < 1 && player.vx > -1;
+	bool inair = col_check_point(player.x, player.y+16) == 0;
+
+	// evaluate animation
+	float walk_time = fmod(position.x, 100.0f)/100.0f;
+	animstate state;
+	anim_eval(&data->animations[ANIM_BASE], 0, &state);
+
+	if(inair)
+		anim_eval_add(&state, &data->animations[ANIM_INAIR], 0, 1.0f); // TODO: some sort of time here
+	else if(stationary)
+		anim_eval_add(&state, &data->animations[ANIM_IDLE], 0, 1.0f); // TODO: some sort of time here
+	else
+		anim_eval_add(&state, &data->animations[ANIM_WALK], walk_time, 1.0f);
+
+	if (player.weapon == WEAPON_HAMMER)
+	{
+		float a = clamp((client_tick()-player.attacktick+ticktime)/10.0f, 0.0f, 1.0f);
+		anim_eval_add(&state, &data->animations[ANIM_HAMMER_SWING], a, 1.0f);
+	}
+	if (player.weapon == WEAPON_NINJA)
+	{
+		float a = clamp((client_tick()-player.attacktick+ticktime)/40.0f, 0.0f, 1.0f);
+		anim_eval_add(&state, &data->animations[ANIM_NINJA_SWING], a, 1.0f);
+	}
+
+	// draw hook
+	if (prev.hook_state>0 && player.hook_state>0)
+	{
+		gfx_texture_set(data->images[IMAGE_GAME].id);
+		gfx_quads_begin();
+		//gfx_quads_begin();
+
+		vec2 pos = position;
+		vec2 hook_pos;
+		
+		if(player_char->hooked_player != -1)
+		{
+			if(local_info && player_char->hooked_player == local_info->clientid)
+			{
+				hook_pos = mix(vec2(predicted_prev_player.pos.x, predicted_prev_player.pos.y),
+					vec2(predicted_player.pos.x, predicted_player.pos.y), client_intrapredtick());
+			}
+			else
+				hook_pos = mix(vec2(prev_char->hook_x, prev_char->hook_y), vec2(player_char->hook_x, player_char->hook_y), client_intratick());
+		}
+		else
+			hook_pos = mix(vec2(prev.hook_x, prev.hook_y), vec2(player.hook_x, player.hook_y), intratick);
+
+		float d = distance(pos, hook_pos);
+		vec2 dir = normalize(pos-hook_pos);
+
+		gfx_quads_setrotation(get_angle(dir)+pi);
+
+		// render head
+		select_sprite(SPRITE_HOOK_HEAD);
+		gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16);
+
+		// render chain
+		select_sprite(SPRITE_HOOK_CHAIN);
+		for(float f = 24; f < d; f += 24)
+		{
+			vec2 p = hook_pos + dir*f;
+			gfx_quads_draw(p.x, p.y,24,16);
+		}
+
+		gfx_quads_setrotation(0);
+		gfx_quads_end();
+
+		render_hand(&client_datas[info.clientid].render_info, position, normalize(hook_pos-pos), -pi/2, vec2(20, 0));
+	}
+
+	// draw gun
+	{
+		gfx_texture_set(data->images[IMAGE_GAME].id);
+		gfx_quads_begin();
+		gfx_quads_setrotation(state.attach.angle*pi*2+angle);
+
+		// normal weapons
+		int iw = clamp(player.weapon, 0, NUM_WEAPONS-1);
+		select_sprite(data->weapons[iw].sprite_body, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
+
+		vec2 dir = direction;
+		float recoil = 0.0f;
+		vec2 p;
+		if (player.weapon == WEAPON_HAMMER)
+		{
+			// Static position for hammer
+			p = position;
+			p.y += data->weapons[iw].offsety;
+			// if attack is under way, bash stuffs
+			if(direction.x < 0)
+			{
+				gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
+				p.x -= data->weapons[iw].offsetx;
+			}
+			else
+			{
+				gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
+			}
+			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
+		}
+		else if (player.weapon == WEAPON_NINJA)
+		{
+			p = position;
+			p.y += data->weapons[iw].offsety;
+
+			if(direction.x < 0)
+			{
+				gfx_quads_setrotation(-pi/2-state.attach.angle*pi*2);
+				p.x -= data->weapons[iw].offsetx;
+			}
+			else
+			{
+				gfx_quads_setrotation(-pi/2+state.attach.angle*pi*2);
+			}
+			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
+
+			// HADOKEN
+			if ((client_tick()-player.attacktick) <= (SERVER_TICK_SPEED / 6) && data->weapons[iw].nummuzzlesprites)
+			{
+				int itex = rand() % data->weapons[iw].nummuzzlesprites;
+				float alpha = 1.0f;
+				if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
+				{
+					vec2 dir = vec2(player_char->x,player_char->y) - vec2(prev_char->x, prev_char->y);
+					dir = normalize(dir);
+					float hadokenangle = get_angle(dir);
+					gfx_quads_setrotation(hadokenangle);
+					//float offsety = -data->weapons[iw].muzzleoffsety;
+					select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, 0);
+					vec2 diry(-dir.y,dir.x);
+					p = position;
+					float offsetx = data->weapons[iw].muzzleoffsetx;
+					p -= dir * offsetx;
+					draw_sprite(p.x, p.y, 160.0f);
+				}
+			}
+		}
+		else
+		{
+			// TODO: should be an animation
+			recoil = 0;
+			float a = (client_tick()-player.attacktick+intratick)/5.0f;
+			if(a < 1)
+				recoil = sinf(a*pi);
+			p = position + dir * data->weapons[iw].offsetx - dir*recoil*10.0f;
+			p.y += data->weapons[iw].offsety;
+			draw_sprite(p.x, p.y, data->weapons[iw].visual_size);
+		}
+
+		if (player.weapon == WEAPON_GUN || player.weapon == WEAPON_SHOTGUN)
+		{
+			// check if we're firing stuff
+			if (true)//prev.attackticks)
+			{
+				float alpha = 0.0f;
+				int phase1tick = (client_tick() - player.attacktick);
+				if (phase1tick < (data->weapons[iw].muzzleduration + 3))
+				{
+					float t = ((((float)phase1tick) + intratick)/(float)data->weapons[iw].muzzleduration);
+					alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t)));
+				}
+
+				int itex = rand() % data->weapons[iw].nummuzzlesprites;
+				if (alpha > 0.0f && data->weapons[iw].sprite_muzzle[itex].psprite)
+				{
+					float offsety = -data->weapons[iw].muzzleoffsety;
+					select_sprite(data->weapons[iw].sprite_muzzle[itex].psprite, direction.x < 0 ? SPRITE_FLAG_FLIP_Y : 0);
+					if(direction.x < 0)
+						offsety = -offsety;
+
+					vec2 diry(-dir.y,dir.x);
+					vec2 muzzlepos = p + dir * data->weapons[iw].muzzleoffsetx + diry * offsety;
+
+					draw_sprite(muzzlepos.x, muzzlepos.y, data->weapons[iw].visual_size);
+					/*gfx_setcolor(1.0f,1.0f,1.0f,alpha);
+					vec2 diry(-dir.y,dir.x);
+					p += dir * muzzleparams[player.weapon].offsetx + diry * offsety;
+					gfx_quads_draw(p.x,p.y,muzzleparams[player.weapon].sizex, muzzleparams[player.weapon].sizey);*/
+				}
+			}
+		}
+		gfx_quads_end();
+
+		switch (player.weapon)
+		{
+			case WEAPON_GUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -3*pi/4, vec2(-15, 4)); break;
+			case WEAPON_SHOTGUN: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-5, 4)); break;
+			case WEAPON_ROCKET: render_hand(&client_datas[info.clientid].render_info, p, direction, -pi/2, vec2(-4, 7)); break;
+		}
+
+	}
+
+	// render the "shadow" tee
+	if(info.local && config.debug)
+	{
+		vec2 ghost_position = mix(vec2(prev_char->x, prev_char->y), vec2(player_char->x, player_char->y), client_intratick());
+		tee_render_info ghost = client_datas[info.clientid].render_info;
+		ghost.color_body.a = 0.5f;
+		ghost.color_feet.a = 0.5f;
+		render_tee(&state, &ghost, player.emote, direction, ghost_position); // render ghost
+	}
+
+	// render the tee
+	render_tee(&state, &client_datas[info.clientid].render_info, player.emote, direction, position);
+
+	if(player.state == STATE_CHATTING)
+	{
+		gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
+		gfx_quads_begin();
+		select_sprite(SPRITE_DOTDOT);
+		gfx_quads_draw(position.x + 24, position.y - 40, 64,64);
+		gfx_quads_end();
+	}
+
+	if (client_datas[info.clientid].emoticon_start != -1 && client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() > client_tick())
+	{
+		gfx_texture_set(data->images[IMAGE_EMOTICONS].id);
+		gfx_quads_begin();
+
+		int since_start = client_tick() - client_datas[info.clientid].emoticon_start;
+		int from_end = client_datas[info.clientid].emoticon_start + 2 * client_tickspeed() - client_tick();
+
+		float a = 1;
+
+		if (from_end < client_tickspeed() / 5)
+			a = from_end / (client_tickspeed() / 5.0);
+
+		float h = 1;
+		if (since_start < client_tickspeed() / 10)
+			h = since_start / (client_tickspeed() / 10.0);
+
+		float wiggle = 0;
+		if (since_start < client_tickspeed() / 5)
+			wiggle = since_start / (client_tickspeed() / 5.0);
+
+		float wiggle_angle = sin(5*wiggle);
+
+		gfx_quads_setrotation(pi/6*wiggle_angle);
+
+		gfx_setcolor(1.0f,1.0f,1.0f,a);
+		// client_datas::emoticon is an offset from the first emoticon
+		select_sprite(SPRITE_OOP + client_datas[info.clientid].emoticon);
+		gfx_quads_draw(position.x, position.y - 23 - 32*h, 64, 64*h);
+		gfx_quads_end();
+	}
+	
+	// render name plate
+	if(!info.local && config.cl_nameplates)
+	{
+		//gfx_text_color
+		float a = 1;
+		if(config.cl_nameplates_always == 0)
+			a = clamp(1-powf(distance(local_target_pos, position)/200.0f,16.0f), 0.0f, 1.0f);
+			
+		const char *name = client_datas[info.clientid].name;
+		float tw = gfx_text_width(0, 28.0f, name, -1);
+		gfx_text_color(1,1,1,a);
+		gfx_text(0, position.x-tw/2.0f, position.y-60, 28.0f, name, -1);
+		gfx_text_color(1,1,1,1);
+	}
+}
diff --git a/src/game/editor/editor.cpp b/src/game/editor/ed_editor.cpp
index 5a9ebf45..8c6c0c73 100644
--- a/src/game/editor/editor.cpp
+++ b/src/game/editor/ed_editor.cpp
@@ -1,3 +1,5 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -15,8 +17,9 @@ extern "C" {
 //#include "game/mapres_col.h"
 #include <game/g_mapres.h>
 #include <game/g_game.h>
+#include <game/client/gc_render.h>
 
-#include "editor.hpp"
+#include "ed_editor.hpp"
 
 static int checker_texture = 0;
 static int background_texture = 0;
@@ -116,9 +119,6 @@ int LAYERGROUP::swap_layers(int index0, int index1)
 /********************************************************
  OTHER
 *********************************************************/
-extern void draw_round_rect_ext(float x, float y, float w, float h, float r, int corners);
-extern void draw_round_rect(float x, float y, float w, float h, float r);
-
 static void ui_draw_rect(const RECT *r, vec4 color, int corners, float rounding)
 {
 	gfx_texture_set(-1);
@@ -1961,67 +1961,6 @@ void EDITOR::make_game_group(LAYERGROUP *group)
 	editor.game_group->name = "Game";
 }
 
-typedef struct 
-{
-	int version;
-	int width;
-	int height;
-	int external;
-	int image_name;
-	int image_data;
-} MAPITEM_IMAGE;
-
-typedef struct
-{
-	int version;
-	int offset_x;
-	int offset_y;
-	int parallax_x;
-	int parallax_y;
-
-	int start_layer;
-	int num_layers;
-} MAPITEM_GROUP;
-
-typedef struct
-{
-	int version;
-	int type;
-	int flags;
-} MAPITEM_LAYER;
-
-typedef struct
-{
-	MAPITEM_LAYER layer;
-	int version;
-	
-	int width;
-	int height;
-	int flags;
-	
-	COLOR color;
-	int color_env;
-	int color_env_offset;
-	
-	int image;
-	int data;
-} MAPITEM_LAYER_TILEMAP;
-
-typedef struct
-{
-	MAPITEM_LAYER layer;
-	int version;
-	
-	int num_quads;
-	int data;
-	int image;
-} MAPITEM_LAYER_QUADS;
-
-typedef struct
-{
-	int version;
-} MAPITEM_VERSION;
-
 template<typename T>
 static int make_version(int i, const T &v)
 { return (i<<16)+sizeof(T); }
@@ -2411,6 +2350,7 @@ extern "C" void editor_init()
 	editor.show_envelope_editor = 1;
 #endif
 
+/*
 	if(1)
 	{
 		float w, h;
@@ -2426,7 +2366,7 @@ extern "C" void editor_init()
 		calc_screen_params(amount, max, max, 9.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
 		calc_screen_params(amount, max, max, 16.0f/3.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
 		calc_screen_params(amount, max, max, 3.0f/16.0f, &w, &h); dbg_msg("", "%f %f %f", w, h, w*h);
-	}
+	}*/
 }
 
 extern "C" void editor_update_and_render()
diff --git a/src/game/editor/editor.hpp b/src/game/editor/ed_editor.hpp
index 0d2af5e1..15ba1dad 100644
--- a/src/game/editor/editor.hpp
+++ b/src/game/editor/ed_editor.hpp
@@ -1,6 +1,9 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+
 #include <stdlib.h>
 #include <math.h>
 #include "array.h"
+#include "../g_mapitems.h"
 
 extern "C" {
 	#include <engine/e_system.h>
@@ -28,40 +31,8 @@ enum
 	
 	DIALOG_NONE=0,
 	DIALOG_LOAD_IMAGE,
-	
-	LAYERTYPE_INVALID=0,
-	LAYERTYPE_GAME,
-	LAYERTYPE_TILES,
-	LAYERTYPE_QUADS,
 };
 
-typedef struct
-{
-	int x, y; // 22.10 fixed point
-} POINT;
-
-// 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)); }
-
-typedef struct // as in file
-{
-	int r, g, b, a;
-} COLOR;
-
-typedef struct // as in file
-{
-	POINT points[5];
-	COLOR colors[4];
-	POINT texcoords[4];
-	
-	int pos_env;
-	int pos_env_offset;
-	
-	int color_env;
-	int color_env_offset;
-} QUAD;
-
 typedef struct // as in file
 {
 	POINT position;
@@ -70,36 +41,6 @@ typedef struct // as in file
 
 enum
 {
-	ENTITY_NULL=0,
-	ENTITY_SPAWN,
-	ENTITY_SPAWN_RED,
-	ENTITY_SPAWN_BLUE,
-	ENTITY_FLAGSTAND_RED,
-	ENTITY_FLAGSTAND_BLUE,
-	ENTITY_ARMOR_1,
-	ENTITY_HEATH_1,
-	ENTITY_WEAPON_SHOTGUN,
-	ENTITY_WEAPON_ROCKET,
-	ENTITY_POWERUP_NINJA,
-	NUM_ENTITIES,
-	
-	TILE_AIR=0,
-	TILE_SOLID,
-	TILE_NOHOOK,
-	
-	ENTITY_OFFSET=255-16*4,
-};
-
-typedef struct // as in file
-{
-	unsigned char index;
-	unsigned char flags;
-	unsigned char reserved1;
-	unsigned char reserved2;
-} TILE;
-
-enum
-{
 	CURVETYPE_STEP=0,
 	CURVETYPE_LINEAR,
 	CURVETYPE_SLOW,
@@ -531,39 +472,3 @@ public:
 
 	virtual void render_properties(RECT *toolbox);
 };
-
-
-inline void calc_screen_params(float amount, float wmax, float hmax, float aspect, float *w, float *h)
-{
-	float f = sqrt(amount) / sqrt(aspect);
-	*w = f*aspect;
-	*h = f;
-	
-	// limit the view
-	if(*w > wmax)
-	{
-		*w = wmax;
-		*h = *w/aspect;
-	}
-	
-	if(*h > hmax)
-	{
-		*h = hmax;
-		*w = *h*aspect;
-	}
-}
-
-inline void mapscreen_to_world(float center_x, float center_y, float parallax_x, float parallax_y,
-	float offset_x, float offset_y, float aspect, float zoom, float *points)
-{
-	float width, height;
-	calc_screen_params(1300*1000, 1500, 1050, aspect, &width, &height);
-	center_x *= parallax_x;
-	center_y *= parallax_y;
-	width *= zoom;
-	height *= zoom;
-	points[0] = offset_x+center_x-width/2;
-	points[1] = offset_y+center_y-height/2;
-	points[2] = offset_x+center_x+width/2;
-	points[3] = offset_y+center_y+height/2;
-}
diff --git a/src/game/editor/ed_layer_game.cpp b/src/game/editor/ed_layer_game.cpp
index 0e002559..4524cb80 100644
--- a/src/game/editor/ed_layer_game.cpp
+++ b/src/game/editor/ed_layer_game.cpp
@@ -1,5 +1,5 @@
 #include <game/client/gc_mapres_tilemap.h>
-#include "editor.hpp"
+#include "ed_editor.hpp"
 
 
 LAYER_GAME::LAYER_GAME(int w, int h)
diff --git a/src/game/editor/ed_layer_quads.cpp b/src/game/editor/ed_layer_quads.cpp
index d58a9e0f..5625876c 100644
--- a/src/game/editor/ed_layer_quads.cpp
+++ b/src/game/editor/ed_layer_quads.cpp
@@ -1,5 +1,7 @@
-#include "editor.hpp"
+#include "ed_editor.hpp"
 #include <game/g_math.h>
+#include <game/generated/gc_data.h>
+#include <game/client/gc_render.h>
 
 LAYER_QUADS::LAYER_QUADS()
 {
@@ -12,88 +14,6 @@ LAYER_QUADS::~LAYER_QUADS()
 {
 }
 
-static void rotate(POINT *center, POINT *point, float rotation)
-{
-	int x = point->x - center->x;
-	int y = point->y - center->y;
-	point->x = (int)(x * cosf(rotation) - y * sinf(rotation) + center->x);
-	point->y = (int)(x * sinf(rotation) + y * cosf(rotation) + center->y);
-}
-
-static void render_quads(QUAD *quads, int num_quads)
-{
-	gfx_quads_begin();
-	float conv = 1/255.0f;
-	for(int i = 0; i < num_quads; i++)
-	{
-		QUAD *q = &quads[i];
-		
-		gfx_quads_setsubset_free(
-			fx2f(q->texcoords[0].x), fx2f(q->texcoords[0].y),
-			fx2f(q->texcoords[1].x), fx2f(q->texcoords[1].y),
-			fx2f(q->texcoords[2].x), fx2f(q->texcoords[2].y),
-			fx2f(q->texcoords[3].x), fx2f(q->texcoords[3].y)
-		);
-
-		float r=1, g=1, b=1, a=1;
-		float offset_x = 0;
-		float offset_y = 0;
-		float rot = 0;
-		
-		if(editor.animate)
-		{
-			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);
-			}
-		}
-		
-		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);
-		gfx_setcolorvertex(2, q->colors[2].r*conv*r, q->colors[2].g*conv*g, q->colors[2].b*conv*b, q->colors[2].a*conv*a);
-		gfx_setcolorvertex(3, q->colors[3].r*conv*r, q->colors[3].g*conv*g, q->colors[3].b*conv*b, q->colors[3].a*conv*a);
-
-		POINT *points = q->points;
-	
-		if(rot != 0)
-		{
-			static POINT rotated[4];
-			rotated[0] = q->points[0];
-			rotated[1] = q->points[1];
-			rotated[2] = q->points[2];
-			rotated[3] = q->points[3];
-			points = rotated;
-			
-			rotate(&q->points[4], &rotated[0], rot);
-			rotate(&q->points[4], &rotated[1], rot);
-			rotate(&q->points[4], &rotated[2], rot);
-			rotate(&q->points[4], &rotated[3], rot);
-		}
-		
-		gfx_quads_draw_freeform(
-			fx2f(points[0].x)+offset_x, fx2f(points[0].y)+offset_y,
-			fx2f(points[1].x)+offset_x, fx2f(points[1].y)+offset_y,
-			fx2f(points[2].x)+offset_x, fx2f(points[2].y)+offset_y,
-			fx2f(points[3].x)+offset_x, fx2f(points[3].y)+offset_y
-		);
-	}
-	gfx_quads_end();	
-}
-
 void LAYER_QUADS::render()
 {
 	gfx_texture_set(-1);
diff --git a/src/game/editor/ed_layer_tiles.cpp b/src/game/editor/ed_layer_tiles.cpp
index f697e56e..583426e3 100644
--- a/src/game/editor/ed_layer_tiles.cpp
+++ b/src/game/editor/ed_layer_tiles.cpp
@@ -1,84 +1,8 @@
 #include <game/client/gc_mapres_tilemap.h>
 #include <game/g_math.h>
-#include "editor.hpp"
-
-static void render_tilemap(TILE *tiles, int w, int h, float scale)
-{
-			//gfx_texture_set(img_get(tmap->image));
-	float screen_x0, screen_y0, screen_x1, screen_y1;
-	gfx_getscreen(&screen_x0, &screen_y0, &screen_x1, &screen_y1);
-
-	// calculate the final pixelsize for the tiles	
-	float tile_pixelsize = 1024/32.0f;
-	float final_tilesize = scale/(screen_x1-screen_x0) * gfx_screenwidth();
-	float final_tilesize_scale = final_tilesize/tile_pixelsize;
-	
-	gfx_quads_begin();
-	
-	int starty = (int)(screen_y0/scale)-1;
-	int startx = (int)(screen_x0/scale)-1;
-	int endy = (int)(screen_y1/scale)+1;
-	int endx = (int)(screen_x1/scale)+1;
-	
-	// adjust the texture shift according to mipmap level
-	float texsize = 1024.0f;
-	float frac = (1.25f/texsize) * (1/final_tilesize_scale);
-	float nudge = (0.5f/texsize) * (1/final_tilesize_scale);
-
-	for(int y = starty; y < endy; y++)
-		for(int x = startx; x < endx; x++)
-		{
-			int mx = x;
-			int my = y;
-			if(mx<0)
-				continue; // mx = 0;
-			if(mx>=w)
-				continue; // mx = w-1;
-			if(my<0)
-				continue; // my = 0;
-			if(my>=h)
-				continue; // my = h-1;
-			
-			int c = mx + my*w;
-				
-			unsigned char index = tiles[c].index;
-			if(index)
-			{
-				unsigned char flags = tiles[c].flags;
-				int tx = index%16;
-				int ty = index/16;
-				int px0 = tx*(1024/16);
-				int py0 = ty*(1024/16);
-				int px1 = (tx+1)*(1024/16)-1;
-				int py1 = (ty+1)*(1024/16)-1;
-				
-				float u0 = nudge + px0/texsize+frac;
-				float v0 = nudge + py0/texsize+frac;
-				float u1 = nudge + px1/texsize-frac;
-				float v1 = nudge + py1/texsize-frac;
-				
-				if(flags&TILEFLAG_VFLIP)
-				{
-					float tmp = u0;
-					u0 = u1;
-					u1 = tmp;
-				}
-
-				if(flags&TILEFLAG_HFLIP)
-				{
-					float tmp = v0;
-					v0 = v1;
-					v1 = tmp;
-				}
-				
-				gfx_quads_setsubset(u0,v0,u1,v1);
-
-				gfx_quads_drawTL(x*scale, y*scale, scale, scale);
-			}
-		}
-	
-	gfx_quads_end();
-}
+#include <game/generated/gc_data.h>
+#include <game/client/gc_render.h>
+#include "ed_editor.hpp"
 
 LAYER_TILES::LAYER_TILES(int w, int h)
 {
diff --git a/src/game/g_game.h b/src/game/g_game.h
index 70a15a19..0e405260 100644
--- a/src/game/g_game.h
+++ b/src/game/g_game.h
@@ -1,4 +1,7 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#ifndef GAME_GAME_H
+#define GAME_GAME_H
+
 #include <engine/e_system.h>
 #include <game/g_math.h>
 #include <math.h>
@@ -169,3 +172,5 @@ enum
 	ITEM_ARMOR=0x00030001,
 	ITEM_NINJA=0x00040001,
 };
+
+#endif
diff --git a/src/game/g_mapitems.h b/src/game/g_mapitems.h
new file mode 100644
index 00000000..f6375d02
--- /dev/null
+++ b/src/game/g_mapitems.h
@@ -0,0 +1,138 @@
+/* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#ifndef GAME_MAPITEMS_H
+#define GAME_MAPITEMS_H
+
+// layer types
+enum
+{
+	LAYERTYPE_INVALID=0,
+	LAYERTYPE_GAME, // not used
+	LAYERTYPE_TILES,
+	LAYERTYPE_QUADS,
+};
+
+
+// game layer tiles
+enum
+{
+	ENTITY_NULL=0,
+	ENTITY_SPAWN,
+	ENTITY_SPAWN_RED,
+	ENTITY_SPAWN_BLUE,
+	ENTITY_FLAGSTAND_RED,
+	ENTITY_FLAGSTAND_BLUE,
+	ENTITY_ARMOR_1,
+	ENTITY_HEATH_1,
+	ENTITY_WEAPON_SHOTGUN,
+	ENTITY_WEAPON_ROCKET,
+	ENTITY_POWERUP_NINJA,
+	NUM_ENTITIES,
+	
+	TILE_AIR=0,
+	TILE_SOLID,
+	TILE_NOHOOK,
+	
+	TILEFLAG_VFLIP=1,
+	TILEFLAG_HFLIP=2,
+	
+	ENTITY_OFFSET=255-16*4,
+};
+
+typedef struct
+{
+	int x, y; // 22.10 fixed point
+} POINT;
+
+typedef struct
+{
+	int r, g, b, a;
+} COLOR;
+
+typedef struct
+{
+	POINT points[5];
+	COLOR colors[4];
+	POINT texcoords[4];
+	
+	int pos_env;
+	int pos_env_offset;
+	
+	int color_env;
+	int color_env_offset;
+} QUAD;
+
+typedef struct
+{
+	unsigned char index;
+	unsigned char flags;
+	unsigned char reserved1;
+	unsigned char reserved2;
+} TILE;
+
+typedef struct 
+{
+	int version;
+	int width;
+	int height;
+	int external;
+	int image_name;
+	int image_data;
+} MAPITEM_IMAGE;
+
+typedef struct
+{
+	int version;
+	int offset_x;
+	int offset_y;
+	int parallax_x;
+	int parallax_y;
+
+	int start_layer;
+	int num_layers;
+} MAPITEM_GROUP;
+
+typedef struct
+{
+	int version;
+	int type;
+	int flags;
+} MAPITEM_LAYER;
+
+typedef struct
+{
+	MAPITEM_LAYER layer;
+	int version;
+	
+	int width;
+	int height;
+	int flags;
+	
+	COLOR color;
+	int color_env;
+	int color_env_offset;
+	
+	int image;
+	int data;
+} MAPITEM_LAYER_TILEMAP;
+
+typedef struct
+{
+	MAPITEM_LAYER layer;
+	int version;
+	
+	int num_quads;
+	int data;
+	int image;
+} MAPITEM_LAYER_QUADS;
+
+typedef struct
+{
+	int version;
+} MAPITEM_VERSION;
+
+// 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)); }
+
+
+#endif
diff --git a/src/game/g_mapres_col.h b/src/game/g_mapres_col.h
index a2aeaee5..3312ef8e 100644
--- a/src/game/g_mapres_col.h
+++ b/src/game/g_mapres_col.h
@@ -1,4 +1,8 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
+#ifndef GAME_MAPRES_COL_H
+#define GAME_MAPRES_COL_H
+
+
 #include <game/g_vmath.h>
 
 struct mapres_collision
@@ -13,3 +17,5 @@ int col_check_point(int x, int y);
 int col_width();
 int col_height();
 bool col_intersect_line(vec2 pos0, vec2 pos1, vec2 *out);
+
+#endif
diff --git a/src/game/g_math.h b/src/game/g_math.h
index e2cf6bd8..54c23ac9 100644
--- a/src/game/g_math.h
+++ b/src/game/g_math.h
@@ -2,6 +2,8 @@
 #ifndef BASE_MATH_H
 #define BASE_MATH_H
 
+#include <stdlib.h>
+
 template <typename T>
 inline T clamp(T val, T min, T max)
 {
@@ -22,6 +24,9 @@ inline T mix(const T a, const T b, TB amount)
 {
 	return a + (b-a)*amount;
 }
+
+inline float frandom() { return rand()/(float)(RAND_MAX); }
+
 const float pi = 3.1415926535897932384626433f;
 
 template <typename T> inline T min(T a, T b) { return a<b?a:b; }
diff --git a/src/game/g_protocol.h b/src/game/g_protocol.h
index 4fcc1e5f..93073154 100644
--- a/src/game/g_protocol.h
+++ b/src/game/g_protocol.h
@@ -1,6 +1,9 @@
 /* copyright (c) 2007 magnus auvinen, see licence.txt for more info */
 // NOTE: Be very careful when editing this file as it will change the network version
 
+#ifndef GAME_PROTOCOL_H
+#define GAME_PROTOCOL_H
+
 // --------- PHYSICS TWEAK! --------
 const float ground_control_speed = 7.0f;
 const float ground_control_accel = 2.0f;
@@ -213,3 +216,5 @@ struct obj_player_info
 	int latency;
 	int latency_flux;
 };
+
+#endif