about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
Diffstat (limited to 'src/game')
-rw-r--r--src/game/game.h231
-rw-r--r--src/game/game_client.cpp1992
-rw-r--r--src/game/game_server.cpp2122
-rw-r--r--src/game/mapres.h7
-rw-r--r--src/game/mapres_col.cpp44
-rw-r--r--src/game/mapres_col.h10
-rw-r--r--src/game/mapres_image.cpp41
-rw-r--r--src/game/mapres_image.h18
-rw-r--r--src/game/mapres_tilemap.cpp54
-rw-r--r--src/game/mapres_tilemap.h19
10 files changed, 4538 insertions, 0 deletions
diff --git a/src/game/game.h b/src/game/game.h
new file mode 100644
index 00000000..a1817eea
--- /dev/null
+++ b/src/game/game.h
@@ -0,0 +1,231 @@
+#include <baselib/system.h>
+#include <baselib/vmath.h>
+#include <math.h>
+#include "../interface.h"
+#include "mapres_col.h"
+
+// Don't tweak :)
+const float pi = 3.14159265358979f;
+
+#define LERP(a,b,t) (a + (b-a) * t)
+#define min(a, b) ( a > b ? b : a)
+#define max(a, b) ( a > b ? a : b)
+
+inline baselib::vec2 get_direction(int angle)
+{
+	float a = angle/256.0f;
+	return baselib::vec2(cosf(a), sinf(a));
+}
+
+inline float get_angle(baselib::vec2 dir)
+{
+	float a = atan(dir.y/dir.x);
+	if(dir.x < 0)
+		a = a+pi;
+	return a;
+}
+
+inline bool col_check_point(float x, float y) { return col_check_point((int)x, (int)y); }
+inline bool col_check_point(baselib::vec2 p) { return col_check_point(p.x, p.y); }
+
+// Network stuff
+
+enum
+{
+	OBJTYPE_NULL=0,
+	OBJTYPE_PLAYER,
+	OBJTYPE_PROJECTILE,
+	OBJTYPE_POWERUP,
+	EVENT_EXPLOSION,
+	EVENT_HEALTHMOD,
+	EVENT_SOUND,
+	EVENT_SMOKE,
+};
+
+enum
+{
+	EMOTE_NORMAL=0,
+	EMOTE_BLINK,
+	EMOTE_WINK,
+	EMOTE_PAIN,
+	EMOTE_HAPPY,
+};
+
+struct player_input
+{
+	int left;
+	int right;
+	int angle;
+	int jump;
+	int fire;
+	int hook;
+	int blink;
+	int activeweapon;
+};
+
+struct ev_explosion
+{
+	int x, y;
+};
+
+struct ev_sound
+{
+	int x, y;
+	int sound; // if (0x80000000 flag is set -> looping) if (0x40000000 is set -> stop looping
+};
+
+struct ev_healthmod
+{
+	int x, y;
+	int amount;
+};
+
+struct obj_projectile
+{
+	int type;
+	int x, y;
+	int vx, vy;
+};
+
+struct obj_powerup
+{
+	int type;
+	int subtype;
+	int x, y;
+	int vx, vy;
+};
+
+struct obj_player
+{
+	int name[8];
+	
+	int local;
+	int clientid;
+
+	int health;
+	int armor;
+	int ammocount;
+	
+	int x, y;
+	int vx, vy;
+	int angle;
+	
+	// current active weapon
+	int weapon;
+	// current active modifier
+	int modifier;
+
+	// num attack ticks left of current attck
+	int attackticks;
+	int attacklen;
+	int visualtimeattack;
+	
+	int score;
+	int emote;
+	
+	int hook_active;
+	int hook_x, hook_y;
+};
+
+enum
+{
+	WEAPON_TYPE_GUN			= 0,
+	WEAPON_TYPE_ROCKET		= 1,
+	WEAPON_TYPE_SHOTGUN		= 2,
+	WEAPON_TYPE_MELEE		= 3,
+	WEAPON_NUMWEAPONS,
+	//WEAPON_TYPE_SNIPER		= 2,
+
+	POWERUP_TYPE_HEALTH			= 0,
+	POWERUP_TYPE_ARMOR			= 1,
+	POWERUP_TYPE_WEAPON			= 2,
+	POWERUP_TYPE_NINJA			= 3,
+	POWERUP_TYPE_TIMEFIELD		= 4,
+	POWERUP_TYPE_NUMPOWERUPS,
+
+	PLAYER_MAXHEALTH			= 10,
+	PLAYER_MAXARMOR				= 10,
+
+	MODIFIER_TYPE_NINJA			= 0,
+	MODIFIER_TYPE_TIMEFIELD		= 1,
+	MODIFIER_NUMMODIFIERS,
+};
+
+
+struct mapres_spawnpoint
+{
+	int x, y;
+	int type;
+};
+
+struct mapres_item
+{
+	int x, y;
+	int type;
+};
+
+enum
+{
+	MAPRES_SPAWNPOINT=1,
+	MAPRES_ITEM=2,
+	
+	ITEM_NULL=0,
+	ITEM_WEAPON_GUN=0x00010001,
+	ITEM_WEAPON_SHOTGUN=0x00010002,
+	ITEM_WEAPON_ROCKET=0x00010003,
+	ITEM_WEAPON_SNIPER=0x00010004,
+	ITEM_WEAPON_HAMMER=0x00010005,
+	ITEM_HEALTH_1 =0x00020001,
+	ITEM_HEALTH_5 =0x00020005,
+	ITEM_HEALTH_10=0x00020010,
+	ITEM_ARMOR_1=0x00030001,
+	ITEM_ARMOR_5=0x00030005,
+	ITEM_ARMOR_10=0x00030010,
+	ITEM_NINJA=0x00040001,
+};
+
+// sound categories and stuff
+enum
+{
+	SOUND_FIRE_GUN = 0,
+	SOUND_FIRE_SHOTGUN,
+	SOUND_FIRE_ROCKET,
+	SOUND_FIRE_MELEE,
+	SOUND_FIRE_NINJA,
+
+
+	// impacts with world
+	SOUND_IMPACT_PROJECTILE_GUN,
+	SOUND_IMPACT_PROJECTILE_SHOTGUN,
+	SOUND_IMPACT_PROJECTILE_ROCKET,
+
+
+	// chain ?
+
+	// Player movement
+	SOUND_PLAYER_JUMP,
+	SOUND_PLAYER_HURT_SHORT,
+	SOUND_PLAYER_HURT_LONG,
+	SOUND_PLAYER_SPAWN,
+	SOUND_PLAYER_CHAIN_LOOP,
+	SOUND_PLAYER_CHAIN_IMPACT,
+	SOUND_PLAYER_IMPACT,
+	SOUND_PLAYER_IMPACT_NINJA,
+	SOUND_PLAYER_DIE,
+	SOUND_PLAYER_SWITCHWEAPON,
+	SOUND_PLAYER_EQUIP,
+	SOUND_PLAYER_LAND,
+
+	SOUND_NUMSOUNDS,
+
+
+	// extra defs (for future?)
+	SOUND_EQUIP_GUN = SOUND_PLAYER_EQUIP,
+	SOUND_EQUIP_ROCKET = SOUND_PLAYER_EQUIP,
+	SOUND_EQUIP_SHOTGUN = SOUND_PLAYER_EQUIP,
+	SOUND_EQUIP_MELEE = SOUND_PLAYER_EQUIP,
+
+	SOUND_LOOPFLAG_STARTLOOP = 0x80000000,
+	SOUND_LOOPFLAG_STOPLOOP = 0x40000000,
+	SOUND_MASK = ~(SOUND_LOOPFLAG_STARTLOOP | SOUND_LOOPFLAG_STOPLOOP),
+};
diff --git a/src/game/game_client.cpp b/src/game/game_client.cpp
new file mode 100644
index 00000000..6e431591
--- /dev/null
+++ b/src/game/game_client.cpp
@@ -0,0 +1,1992 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include "game.h"
+#include "mapres_image.h"
+#include "mapres_tilemap.h"
+
+using namespace baselib;
+
+static int texture_char_default = 0;
+static int texture_game = 0;
+static int texture_weapon = 0;
+static int texture_sun = 0;
+static int texture_particles = 0;
+
+struct weapontexcell
+{
+	float x;
+	float y;
+	float w;
+	float h;
+};
+struct renderparams
+{
+	float sizex;
+	float sizey;
+	float offsetx;
+	float offsety;
+};
+int numcellsx = 32;
+int numcellsy = 32;
+renderparams weaponrenderparams[WEAPON_NUMWEAPONS];
+renderparams modifierrenderparams[WEAPON_NUMWEAPONS];
+
+weapontexcell weaponprojtexcoord[WEAPON_NUMWEAPONS];
+weapontexcell weapontexcoord[WEAPON_NUMWEAPONS];
+weapontexcell weapontexcoordcursor[WEAPON_NUMWEAPONS];
+
+weapontexcell poweruptexcoord[POWERUP_TYPE_NUMPOWERUPS];
+
+weapontexcell modifiertexcoord[MODIFIER_NUMMODIFIERS];
+weapontexcell modifiertexcoordcursor[MODIFIER_NUMMODIFIERS];
+
+int nummuzzletex[WEAPON_NUMWEAPONS];
+weapontexcell muzzletexcoord[WEAPON_NUMWEAPONS][3];
+renderparams muzzleparams[WEAPON_NUMWEAPONS];
+
+#define NUMHADOKENS 6
+#define NUMSTARS 2
+#define NUMPARTICLES 9
+int particlesnumcellsx = 16;
+int particlesnumcellsy = 16;
+weapontexcell chaintexcoord;
+weapontexcell chainheadtexcoord;
+weapontexcell stars[NUMSTARS];
+
+float lifemodifier[NUMPARTICLES];
+vec4 particlecolors[NUMPARTICLES];
+weapontexcell particlestexcoord[NUMPARTICLES];
+
+int charnumcellsx = 8;
+int charnumcellsy = 32;
+int charoffsety = 2;
+weapontexcell body[2];
+weapontexcell leye;
+weapontexcell reye;
+weapontexcell feet[2];
+
+int charids[16] = { 2,10,0,4,12,6,14,1,9,15,13,11,7,5,8,3 };
+
+renderparams hadokenparams[6];
+weapontexcell hadoken[6];
+
+float recoils[WEAPON_NUMWEAPONS] = { 10.0f, 10.0f, 10.0f, 10.0f };
+
+static int font_texture = 0;
+static vec2 mouse_pos;
+
+
+static vec2 local_player_pos;
+static obj_player *local_player;
+
+float frandom()
+{
+	return rand()/(float)(RAND_MAX);
+}
+
+float sign(float f)
+{
+	return f<0.0f?-1.0f:1.0f;
+}
+
+// sound helpers
+template<int N>
+class sound_kit
+{
+private:
+	int sounds[N];
+	int last_id;
+public:
+	sound_kit() : last_id(-1) { }
+	
+	int& operator[](int id) { return sounds[id]; }
+
+	inline void play_random(float vol = 1.0f, float pan = 0.0f);
+};
+
+template<>
+inline void sound_kit<1>::play_random(float vol, float pan)
+{
+	snd_play(sounds[0], SND_PLAY_ONCE, vol, pan);
+}
+
+template<int N>
+inline void sound_kit<N>::play_random(float vol, float pan)
+{
+	int id;
+	do {
+		id = rand() % N;
+	} while(id == last_id);
+	snd_play(sounds[id], SND_PLAY_ONCE, vol, pan);
+	last_id = id;
+}
+
+
+// sound volume tweak
+static const float stereo_separation = 0.01f;
+static const float stereo_separation_deadzone = 512.0f;
+static const float volume_distance_falloff = 100.0f;
+static const float volume_distance_deadzone = 512.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;
+
+// sounds
+sound_kit<3> sound_gun_fire;
+sound_kit<3> sound_shotty_fire;
+sound_kit<3> sound_flump_launch;
+sound_kit<3> sound_hammer_swing;
+sound_kit<3> sound_ninja_attack;
+
+sound_kit<3> sound_flump_explode;
+sound_kit<4> sound_ninja_hit;
+
+sound_kit<3> sound_weapon_switch;
+
+sound_kit<12> sound_pain_short;
+sound_kit<2> sound_pain_long;
+
+sound_kit<4> sound_body_jump;
+sound_kit<4> sound_body_land;
+sound_kit<2> sound_body_splat;
+
+sound_kit<7> sound_spawn;
+sound_kit<2> sound_tee_cry;
+
+sound_kit<1> sound_hook_loop;
+sound_kit<3> sound_hook_attach;
+
+void sound_vol_pan(const vec2& p, float *vol, float *pan)
+{
+	vec2 player_to_ev = p - local_player_pos;
+	*pan = 0.0f;
+	*vol = 1.0f;
+
+	if(abs(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;
+	}
+}
+
+// TODO: we should do something nicer then this
+static void cell_select_ex(int cx, int cy, float x, float y, float w, float h)
+{
+	gfx_quads_setsubset(x/(float)cx,y/(float)cy,(x+w)/(float)cx,(y+h)/(float)cy);
+}
+
+static void cell_select_ex_flip_x(int cx, int cy, float x, float y, float w, float h)
+{
+	gfx_quads_setsubset((x+w)/(float)cx,y/(float)cy,x /(float)cx,(y+h)/(float)cy);
+}
+
+static void cell_select_ex_flip_y(int cx, int cy, float x, float y, float w, float h)
+{
+	gfx_quads_setsubset(x/(float)cx,(y+h)/(float)cy,(x+w)/(float)cx,y/(float)cy);
+}
+
+static void cell_select(int x, int y, int w, int h)
+{
+	gfx_quads_setsubset(x/16.0f,y/16.0f,(x+w)/16.0f,(y+h)/16.0f);
+}
+
+inline void cell_select_flip_x(int x, int y, int w, int h)
+{
+	gfx_quads_setsubset((x+w)/16.0f,y/16.0f,(x)/16.0f,(y+h)/16.0f);
+}
+
+inline void cell_select_flip_y(int x, int y, int w, int h)
+{
+	gfx_quads_setsubset(x/16.0f,(y+h)/16.0f,(x+w)/16.0f,(y)/16.0f);
+}
+
+struct particle
+{
+	vec2 pos;
+	vec2 vel;
+	float life;
+	float max_life;
+	float size;
+	
+	float rot;
+	float rotspeed;
+	
+	float gravity;
+	float friction;
+	int iparticle;
+	
+	vec4 color;
+};
+
+void move_point(vec2 *inout_pos, vec2 *inout_vel, float elasticity)
+{
+	vec2 pos = *inout_pos;
+	vec2 vel = *inout_vel;
+	if(col_check_point(pos + vel))
+	{
+		int affected = 0;
+		if(col_check_point(pos.x + vel.x, pos.y))
+		{
+			inout_vel->x *= -elasticity;
+			affected++;
+		}
+
+		if(col_check_point(pos.x, pos.y + vel.y))
+		{
+			inout_vel->y *= -elasticity;
+			affected++;
+		}
+		
+		if(affected == 0)
+		{
+			inout_vel->x *= -elasticity;
+			inout_vel->y *= -elasticity;
+		}
+	}
+	else
+	{
+		*inout_pos = pos + vel;
+	}
+}
+
+class health_texts
+{
+public:
+	int64 lastupdate;
+	struct item
+	{
+		vec2 pos;
+		vec2 vel;
+		int amount;
+		int istar;
+		float life;
+		float startangle;
+	};
+	
+	enum
+	{
+		MAX_ITEMS=16,
+	};
+
+	health_texts()
+	{
+		lastupdate = 0;
+	}
+	
+	item items[MAX_ITEMS];
+	int num_items;
+	
+	item *create_i()
+	{
+		if (num_items < MAX_ITEMS)
+		{
+			item *p = &items[num_items];
+			num_items++;
+			return p;
+		}
+		return 0;
+	}
+	
+	void destroy_i(item *i)
+	{
+		num_items--;
+		*i = items[num_items];
+	}
+	
+	void create(vec2 pos, int amount)
+	{
+		amount = max(1,amount);
+		for (int j = 0; j < amount; j++)
+		{
+			float a = j/(float)amount-0.5f;
+			item *i = create_i();
+			if (i)
+			{
+				i->pos = pos;
+				i->pos.y -= 20.0f;
+				i->pos.x += ((float)rand()/(float)RAND_MAX) * 5.0f;
+				i->amount = amount;
+				i->life = 1.5f;
+				i->istar = rand() % NUMSTARS;
+				i->vel = vec2(((float)rand()/(float)RAND_MAX) * 50.0f,-150.0f);
+				i->startangle = (( (float)rand()/(float)RAND_MAX) - 1.0f) * 2.0f * pi;
+			}
+		}
+	}
+	
+	void render()
+	{
+		if (!lastupdate)
+			lastupdate = time_get();
+
+		int64 lasttime = lastupdate;
+		lastupdate = time_get();
+
+		float delta = (float) (lastupdate - lasttime) / (float)time_freq();
+		gfx_texture_set(texture_particles);
+		gfx_quads_begin();
+		for(int i = 0; i < num_items;)
+		{
+			items[i].vel += vec2(0,500.0f) * delta;
+			items[i].pos += items[i].vel * delta;
+			items[i].life -= delta;
+			//items[i].pos.y -= frametime*15.0f;
+			if(items[i].life < 0.0f)
+				destroy_i(&items[i]);
+			else
+			{
+				gfx_quads_setcolor(1.0f,1.0f,1.0f, items[i].life / 1.5f);
+				gfx_quads_setrotation(items[i].startangle + items[i].life * 2.0f);
+				float size = 64.0f;
+				cell_select_ex(particlesnumcellsx,particlesnumcellsy, stars[items[i].istar].x,stars[items[i].istar].y, stars[items[i].istar].w, stars[items[i].istar].h);
+				gfx_quads_draw(items[i].pos.x-size/2, items[i].pos.y-size/2, size, size);
+				/*char buf[32];
+				if(items[i].amount < 0)
+				{
+					sprintf(buf, "%d", items[i].amount*-1);
+				}
+				else
+				{
+					sprintf(buf, "%d", items[i].amount);
+				}
+				float size = 42.0f;
+				if(items[i].life > 1.25f)
+					size += 42.0f * ((items[i].life - 1.25f) * 4);
+				gfx_quads_text(items[i].pos.x-size/2, items[i].pos.y, size, buf);*/
+				i++;
+			}
+		}
+		gfx_quads_end();
+	}
+	
+};
+
+/*class texture_animator
+{
+public:
+	
+	int texture;
+	int numframes;
+	float duration;
+	float* framereltime;
+	weapontexcell* params;
+	texture_animator()
+	{
+		texture = -1;
+		numframes = 0;
+		duration = 0;
+		framereltime = 0;
+		params = 0;
+	}
+
+	~texture_animator()
+	{
+		if (params)
+			mem_free(params);
+		if (framereltime)
+			mem_free(framereltime);
+	}
+
+	void create_anim(int texture, int numframes, float duration)
+	{
+		framereltime = 0;
+		params = 0;
+		this->texture = texture;
+		this->numframes = numframes;
+		this->duration = duration;
+		if (numframes)
+		{
+			framereltime = (float*)mem_alloc(sizeof(float) * numframes,1);
+			params = (weapontexcell*)mem_alloc(sizeof(renderparams) * numframes,1);
+			float delta = 1.0f / (float)(numframes - 1);
+			for (int i = 0; i < numframes; i++)
+			{
+				framereltime[i] = delta * i;
+			}
+		}	
+	}
+
+	static void create_gunmuzzle(texture_animator& anim, int texture, float duration)
+	{
+		anim.create_anim(texture, 3, duration);
+		anim.params[0].x = 8;
+		anim.params[0].y = 4;
+		anim.params[0].w = 3;
+		anim.params[0].h = 2;
+		anim.params[1].x = 12;
+		anim.params[1].y = 4;
+		anim.params[1].w = 3;
+		anim.params[1].h = 2;
+		anim.params[2].x = 16;
+		anim.params[2].y = 4;
+		anim.params[2].w = 3;
+		anim.params[2].h = 2;
+	}
+
+	static void create_shotgunmuzzle()
+	{
+
+	}
+};*/
+
+class keyframe
+{
+public:
+	vec2 pos;
+	float angle;
+	float relativetime;
+};
+
+class anim
+{
+public:
+	keyframe* keyframes;
+	int numframes;
+	float duration;
+	anim()
+	{
+		numframes = 0;
+		keyframes = 0;
+	}
+	~anim()
+	{
+		if (keyframes)
+			mem_free(keyframes);
+	}
+
+	void create_anim(int numframes, float duration)
+	{
+		if (keyframes)
+			mem_free(keyframes);
+
+		this->numframes = numframes;
+		this->duration = duration;
+		keyframes = (keyframe*)mem_alloc(sizeof(keyframe) * numframes,1);
+		float delta = 1.0f / (float) (numframes - 1);
+		for (int i = 0; i < numframes; i++)
+		{
+			keyframes[i].pos = vec2(0.0f,0.0f);
+			keyframes[i].angle = 0;
+			keyframes[i].relativetime = delta * (float)i;
+		}
+	}
+
+	void getframes(float relativetime, keyframe*& frame1, keyframe*& frame2, float& blend)
+	{
+		for (int i = 1; i < numframes; i++)
+		{
+			if (keyframes[i-1].relativetime <= relativetime && keyframes[i].relativetime >= relativetime)
+			{
+				frame1 = &keyframes[i-1];
+				frame2 = &keyframes[i];
+				blend = (relativetime - frame1->relativetime) / (frame2->relativetime - frame1->relativetime);
+			}
+		}
+	}
+
+	void evalanim(float time, vec2& pos, float& angle)
+	{
+		float reltime = max(0.0f, min(1.0f, time / duration));
+		keyframe* frame1 = 0;
+		keyframe* frame2 = 0;
+		float blend = 0.0f;
+		getframes(reltime, frame1, frame2, blend);
+
+		if (frame1 && frame2)
+		{
+			pos = mix(frame1->pos, frame2->pos, blend);
+			angle = LERP(frame1->angle, frame2->angle, blend);
+		}
+	}
+
+	static void setup_hammer(anim& hammeranim)
+	{
+		// straight up = -0.25
+		// frame0 = standard pose		time 0
+		// frame1 = back a little		time 0.3
+		// frame2 = over head			time 0.4
+		// frame3 = on ground smashed	time 0.5
+		// frame4 = back to standard pose time 1.0
+		hammeranim.create_anim(5, 1.0f);
+		// only angles... (for now...)
+		hammeranim.keyframes[0].angle = -0.35f * pi * 2.0f;
+		hammeranim.keyframes[1].angle = -0.4f * pi * 2.0f;
+		hammeranim.keyframes[1].relativetime = 0.3f;
+		hammeranim.keyframes[2].angle = -0.25f;
+		hammeranim.keyframes[2].relativetime = 0.4f;
+		hammeranim.keyframes[3].angle = 0.0f * pi * 2.0f;
+		hammeranim.keyframes[3].relativetime = 0.5f;
+		hammeranim.keyframes[4].angle = -0.35f * pi * 2.0f;
+		hammeranim.keyframes[4].relativetime = 1.0f;
+	}
+
+	static void setup_ninja(anim& ninjanim)
+	{
+		// (straight up = -0.25)
+		// frame0 = standard pose straight back		time 0.0
+		// frame1 = overhead attack frame 1			time 0.1
+		// frame2 = attack end frame				time 0.15
+		// frame3 = attack hold frame (a bit up)	time 0.4
+		// frame4 = attack hold frame end			time 0.7
+		// frame5 = endframe						time 1.0
+		ninjanim.create_anim(6, 1.0f);
+		// only angles... (for now...)
+		ninjanim.keyframes[0].angle = -0.5f * pi * 2.0f;
+
+		ninjanim.keyframes[1].angle = -0.3f * pi * 2.0f;
+		ninjanim.keyframes[1].relativetime = 0.1f;
+
+		ninjanim.keyframes[2].angle = 0.1f * pi * 2.0f;
+		ninjanim.keyframes[2].relativetime = 0.15f;
+
+		ninjanim.keyframes[3].angle = -0.05f * pi * 2.0f;
+		ninjanim.keyframes[3].relativetime = 0.42;
+
+		ninjanim.keyframes[4].angle = -0.05f * pi * 2.0f;
+		ninjanim.keyframes[4].relativetime = 0.5f;
+
+		ninjanim.keyframes[5].angle = -0.5f * pi * 2.0f;
+		ninjanim.keyframes[5].relativetime = 1.0f;
+	}
+};
+
+static anim hammeranim;
+static anim ninjaanim;
+static health_texts healthmods;
+
+class particle_system
+{
+public:
+	enum
+	{
+		MAX_PARTICLES=1024,
+	};
+	
+	particle particles[MAX_PARTICLES];
+	int num_particles;
+	
+	particle_system()
+	{
+		num_particles = 0;
+	}
+	
+	void new_particle(vec2 pos, vec2 vel, float life, float size, float gravity, float friction)
+	{
+		if (num_particles >= MAX_PARTICLES)
+			return;
+
+		particles[num_particles].iparticle = rand() % NUMPARTICLES;
+		particles[num_particles].pos = pos;
+		particles[num_particles].vel = vel;
+		particles[num_particles].life = life - lifemodifier[particles[num_particles].iparticle] * life;
+		particles[num_particles].size = size;
+		particles[num_particles].max_life = life;
+		particles[num_particles].gravity = gravity;
+		particles[num_particles].friction = friction;
+		particles[num_particles].rot = frandom()*pi*2;
+		particles[num_particles].rotspeed = frandom() * 10.0f;
+		num_particles++;
+	}
+	
+	void update(float time_passed)
+	{
+		for(int i = 0; i < num_particles; i++)
+		{
+			particles[i].vel.y += particles[i].gravity*time_passed;
+			particles[i].vel *= particles[i].friction;
+			vec2 vel = particles[i].vel*time_passed;
+			move_point(&particles[i].pos, &vel, 0.1f+0.9f*frandom());
+			particles[i].vel = vel* (1.0f/time_passed);
+			particles[i].life += time_passed;
+			particles[i].rot += time_passed * particles[i].rotspeed;
+			
+			// check particle death
+			if(particles[i].life > particles[i].max_life)
+			{
+				num_particles--;
+				particles[i] = particles[num_particles];
+				i--;
+			}
+		}
+	}
+	
+	void render()
+	{
+		gfx_blend_additive();
+		gfx_texture_set(texture_particles);
+		gfx_quads_begin();
+		//cell_select(4,1,1,1);
+		//cell_select(0,6,2,2);
+		//gfx_quads_setrotation(get_angle(vec2(proj->vx, proj->vy)));
+		for(int i = 0; i < num_particles; i++)
+		{
+			int type = particles[i].iparticle;
+			cell_select_ex(particlesnumcellsx,particlesnumcellsy,particlestexcoord[type].x, particlestexcoord[type].y, particlestexcoord[type].w, particlestexcoord[type].h);
+			float a = 1 - particles[i].life / particles[i].max_life;
+			vec2 p = particles[i].pos;
+			//a *= length(particles[i].vel) * 0.01f;
+			gfx_quads_setrotation(particles[i].rot);
+			gfx_quads_setcolor(particlecolors[type].x,particlecolors[type].y,particlecolors[type].z,pow(a,0.75f));
+			//gfx_quads_setcolor(particlecolors[type].x * 0.5,particlecolors[type].y * 0.5,particlecolors[type].z* 0.5,pow(a,0.75f));
+			//gfx_quads_setcolor(particlecolors[type].x * 0.0,particlecolors[type].y * 0.0,particlecolors[type].z* 0.0,pow(a,0.75f));
+			//gfx_quads_setcolor(0.64f*2,0.28f*2,0.16f*2,pow(a,0.75f));
+			gfx_quads_draw(p.x, p.y,particles[i].size,particles[i].size);
+		}
+		gfx_quads_end();		
+		gfx_blend_normal();
+	}
+};
+
+static particle_system temp_system;
+ 
+void modc_init()
+{
+	// load textures
+	texture_weapon = gfx_load_texture_tga("data/tileset_weapons.tga");
+	texture_game = gfx_load_texture_tga("data/game_main.tga");
+	texture_char_default = gfx_load_texture_tga("data/char_teefault.tga");
+	texture_sun = gfx_load_texture_tga("data/sun.tga");
+	texture_particles = gfx_load_texture_tga("data/tileset_particles.tga");
+	font_texture = gfx_load_texture_tga("data/debug_font.tga");
+
+	
+	// load sounds
+	sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav");
+	sound_gun_fire[0] = snd_load_wav("data/audio/wp_gun_fire-01.wav");
+	sound_gun_fire[1] = snd_load_wav("data/audio/wp_gun_fire-02.wav");
+	sound_shotty_fire[0] = snd_load_wav("data/audio/wp_shotty_fire-01.wav");
+	sound_shotty_fire[1] = snd_load_wav("data/audio/wp_shotty_fire-02.wav");
+	sound_shotty_fire[2] = snd_load_wav("data/audio/wp_shotty_fire-03.wav");
+	sound_flump_launch[0] = snd_load_wav("data/audio/wp_flump_launch-01.wav");
+	sound_flump_launch[1] = snd_load_wav("data/audio/wp_flump_launch-02.wav");
+	sound_flump_launch[2] = snd_load_wav("data/audio/wp_flump_launch-03.wav");
+	sound_hammer_swing[0] = snd_load_wav("data/audio/wp_hammer_swing-01.wav");
+	sound_hammer_swing[1] = snd_load_wav("data/audio/wp_hammer_swing-02.wav");
+	sound_hammer_swing[2] = snd_load_wav("data/audio/wp_hammer_swing-03.wav");
+	sound_ninja_attack[0] = snd_load_wav("data/audio/wp_ninja_attack-01.wav");
+	sound_ninja_attack[1] = snd_load_wav("data/audio/wp_ninja_attack-02.wav");
+	sound_ninja_attack[2] = snd_load_wav("data/audio/wp_ninja_attack-03.wav");
+
+	sound_flump_explode[0] = snd_load_wav("data/audio/wp_flump_explo-01.wav");
+	sound_flump_explode[1] = snd_load_wav("data/audio/wp_flump_explo-02.wav");
+	sound_flump_explode[2] = snd_load_wav("data/audio/wp_flump_explo-03.wav");
+	sound_ninja_hit[0] = snd_load_wav("data/audio/wp_ninja_hit-01.wav");
+	sound_ninja_hit[1] = snd_load_wav("data/audio/wp_ninja_hit-02.wav");
+	sound_ninja_hit[2] = snd_load_wav("data/audio/wp_ninja_hit-03.wav");
+	sound_ninja_hit[3] = snd_load_wav("data/audio/wp_ninja_hit-04.wav");
+
+	sound_weapon_switch[0] = snd_load_wav("data/audio/wp_switch-01.wav");
+	sound_weapon_switch[1] = snd_load_wav("data/audio/wp_switch-02.wav");
+	sound_weapon_switch[2] = snd_load_wav("data/audio/wp_switch-03.wav");
+
+	sound_pain_short[0] = snd_load_wav("data/audio/vo_teefault_pain_short-01.wav");
+	sound_pain_short[1] = snd_load_wav("data/audio/vo_teefault_pain_short-02.wav");
+	sound_pain_short[2] = snd_load_wav("data/audio/vo_teefault_pain_short-03.wav");
+	sound_pain_short[3] = snd_load_wav("data/audio/vo_teefault_pain_short-04.wav");
+	sound_pain_short[4] = snd_load_wav("data/audio/vo_teefault_pain_short-05.wav");
+	sound_pain_short[5] = snd_load_wav("data/audio/vo_teefault_pain_short-06.wav");
+	sound_pain_short[6] = snd_load_wav("data/audio/vo_teefault_pain_short-07.wav");
+	sound_pain_short[7] = snd_load_wav("data/audio/vo_teefault_pain_short-08.wav");
+	sound_pain_short[8] = snd_load_wav("data/audio/vo_teefault_pain_short-09.wav");
+	sound_pain_short[9] = snd_load_wav("data/audio/vo_teefault_pain_short-10.wav");
+	sound_pain_short[10] = snd_load_wav("data/audio/vo_teefault_pain_short-11.wav");
+	sound_pain_short[11] = snd_load_wav("data/audio/vo_teefault_pain_short-12.wav");
+
+	sound_pain_long[0] = snd_load_wav("data/audio/vo_teefault_pain_long-01.wav");
+	sound_pain_long[1] = snd_load_wav("data/audio/vo_teefault_pain_long-02.wav");
+
+	sound_body_land[0] = snd_load_wav("data/audio/foley_land-01.wav");
+	sound_body_land[1] = snd_load_wav("data/audio/foley_land-02.wav");
+	sound_body_land[2] = snd_load_wav("data/audio/foley_land-03.wav");
+	sound_body_land[3] = snd_load_wav("data/audio/foley_land-04.wav");
+	sound_body_jump[0] = snd_load_wav("data/audio/foley_foot_left-01.wav");
+	sound_body_jump[1] = snd_load_wav("data/audio/foley_foot_left-02.wav");
+	sound_body_jump[2] = snd_load_wav("data/audio/foley_foot_left-03.wav");
+	sound_body_jump[3] = snd_load_wav("data/audio/foley_foot_left-04.wav");
+	sound_body_jump[4] = snd_load_wav("data/audio/foley_foot_right-01.wav");
+	sound_body_jump[5] = snd_load_wav("data/audio/foley_foot_right-02.wav");
+	sound_body_jump[6] = snd_load_wav("data/audio/foley_foot_right-03.wav");
+	sound_body_jump[7] = snd_load_wav("data/audio/foley_foot_right-04.wav");
+
+	sound_body_splat[1] = snd_load_wav("data/audio/foley_body_splat-02.wav");
+	sound_body_splat[2] = snd_load_wav("data/audio/foley_body_splat-03.wav");
+	sound_body_splat[3] = snd_load_wav("data/audio/foley_body_splat-04.wav");
+
+	sound_spawn[0] = snd_load_wav("data/audio/vo_teefault_spawn-01.wav");
+	sound_spawn[1] = snd_load_wav("data/audio/vo_teefault_spawn-02.wav");
+	sound_spawn[2] = snd_load_wav("data/audio/vo_teefault_spawn-03.wav");
+	sound_spawn[3] = snd_load_wav("data/audio/vo_teefault_spawn-04.wav");
+	sound_spawn[4] = snd_load_wav("data/audio/vo_teefault_spawn-05.wav");
+	sound_spawn[5] = snd_load_wav("data/audio/vo_teefault_spawn-06.wav");
+	sound_spawn[6] = snd_load_wav("data/audio/vo_teefault_spawn-07.wav");
+
+	sound_tee_cry[0] = snd_load_wav("data/audio/vo_teefault_cry-01.wav");
+	sound_tee_cry[1] = snd_load_wav("data/audio/vo_teefault_cry-02.wav");
+
+	//sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-01.wav");
+	sound_hook_loop[0] = snd_load_wav("data/audio/hook_loop-02.wav");
+	sound_hook_attach[0] = snd_load_wav("data/audio/hook_attach-01.wav");
+	sound_hook_attach[1] = snd_load_wav("data/audio/hook_attach-02.wav");
+	sound_hook_attach[2] = snd_load_wav("data/audio/hook_attach-03.wav");
+
+	poweruptexcoord[POWERUP_TYPE_HEALTH].x = 10;
+	poweruptexcoord[POWERUP_TYPE_HEALTH].y = 2;
+	poweruptexcoord[POWERUP_TYPE_HEALTH].w = 2;
+	poweruptexcoord[POWERUP_TYPE_HEALTH].h = 2;
+
+	poweruptexcoord[POWERUP_TYPE_ARMOR].x = 12;
+	poweruptexcoord[POWERUP_TYPE_ARMOR].y = 2;
+	poweruptexcoord[POWERUP_TYPE_ARMOR].w = 2;
+	poweruptexcoord[POWERUP_TYPE_ARMOR].h = 2;
+	
+	poweruptexcoord[POWERUP_TYPE_WEAPON].x = 3;
+	poweruptexcoord[POWERUP_TYPE_WEAPON].y = 0;
+	poweruptexcoord[POWERUP_TYPE_WEAPON].w = 6;
+	poweruptexcoord[POWERUP_TYPE_WEAPON].h = 2;
+
+	poweruptexcoord[POWERUP_TYPE_NINJA].x = 3;
+	poweruptexcoord[POWERUP_TYPE_NINJA].y = 10;
+	poweruptexcoord[POWERUP_TYPE_NINJA].w = 7;
+	poweruptexcoord[POWERUP_TYPE_NINJA].h = 2;
+
+	poweruptexcoord[POWERUP_TYPE_TIMEFIELD].x = 3;
+	poweruptexcoord[POWERUP_TYPE_TIMEFIELD].y = 0;
+	poweruptexcoord[POWERUP_TYPE_TIMEFIELD].w = 6;
+	poweruptexcoord[POWERUP_TYPE_TIMEFIELD].h = 2;
+
+	// Setup weapon cell coords
+	float sizemodifier = 1.0f;
+	weaponrenderparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_GUN].sizey = 30.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_GUN].offsetx = 32.0f;
+	weaponrenderparams[WEAPON_TYPE_GUN].offsety = 4.0f;
+	weapontexcoordcursor[WEAPON_TYPE_GUN].x = 0;
+	weapontexcoordcursor[WEAPON_TYPE_GUN].y = 4;
+	weapontexcoordcursor[WEAPON_TYPE_GUN].w = 2;
+	weapontexcoordcursor[WEAPON_TYPE_GUN].h = 2;
+	weapontexcoord[WEAPON_TYPE_GUN].x = 2;
+	weapontexcoord[WEAPON_TYPE_GUN].y = 4;
+	weapontexcoord[WEAPON_TYPE_GUN].w = 4;
+	weapontexcoord[WEAPON_TYPE_GUN].h = 2;
+	weaponprojtexcoord[WEAPON_TYPE_GUN].x = 6;
+	weaponprojtexcoord[WEAPON_TYPE_GUN].y = 4;
+	weaponprojtexcoord[WEAPON_TYPE_GUN].w = 2;
+	weaponprojtexcoord[WEAPON_TYPE_GUN].h = 2;
+
+	nummuzzletex[WEAPON_TYPE_GUN] = 3;
+	muzzletexcoord[WEAPON_TYPE_GUN][0].x = 8;
+	muzzletexcoord[WEAPON_TYPE_GUN][0].y = 4;
+	muzzletexcoord[WEAPON_TYPE_GUN][0].w = 3;
+	muzzletexcoord[WEAPON_TYPE_GUN][0].h = 2;
+	muzzletexcoord[WEAPON_TYPE_GUN][1].x = 12;
+	muzzletexcoord[WEAPON_TYPE_GUN][1].y = 4;
+	muzzletexcoord[WEAPON_TYPE_GUN][1].w = 3;
+	muzzletexcoord[WEAPON_TYPE_GUN][1].h = 2;
+	muzzletexcoord[WEAPON_TYPE_GUN][2].x = 16;
+	muzzletexcoord[WEAPON_TYPE_GUN][2].y = 4;
+	muzzletexcoord[WEAPON_TYPE_GUN][2].w = 3;
+	muzzletexcoord[WEAPON_TYPE_GUN][2].h = 2;
+
+	muzzleparams[WEAPON_TYPE_GUN].sizex = 60.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_GUN].sizey = 40.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_GUN].offsetx = 50.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_GUN].offsety = 6.0f * sizemodifier;
+
+	sizemodifier = 1.3f;
+	weaponrenderparams[WEAPON_TYPE_ROCKET].sizex = 70.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_ROCKET].sizey = 20.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_ROCKET].offsetx = 24.0f;
+	weaponrenderparams[WEAPON_TYPE_ROCKET].offsety = -2.0f;
+	weapontexcoordcursor[WEAPON_TYPE_ROCKET].x = 0;
+	weapontexcoordcursor[WEAPON_TYPE_ROCKET].y = 8;
+	weapontexcoordcursor[WEAPON_TYPE_ROCKET].w = 2;
+	weapontexcoordcursor[WEAPON_TYPE_ROCKET].h = 2;
+	weapontexcoord[WEAPON_TYPE_ROCKET].x = 2;
+	weapontexcoord[WEAPON_TYPE_ROCKET].y = 8;
+	weapontexcoord[WEAPON_TYPE_ROCKET].w = 7;
+	weapontexcoord[WEAPON_TYPE_ROCKET].h = 2;
+	weaponprojtexcoord[WEAPON_TYPE_ROCKET].x = 10;
+	weaponprojtexcoord[WEAPON_TYPE_ROCKET].y = 8;
+	weaponprojtexcoord[WEAPON_TYPE_ROCKET].w = 2;
+	weaponprojtexcoord[WEAPON_TYPE_ROCKET].h = 2;
+
+	/*weaponrenderparams[WEAPON_TYPE_SNIPER].sizex = 60.0f;
+	weaponrenderparams[WEAPON_TYPE_SNIPER].sizey = 20.0f;
+	weaponrenderparams[WEAPON_TYPE_SNIPER].offsetx = 16.0f;
+	weaponrenderparams[WEAPON_TYPE_SNIPER].offsety = 4.0f;
+	weapontexcoordcursor[WEAPON_TYPE_SNIPER].x = 0;
+	weapontexcoordcursor[WEAPON_TYPE_SNIPER].y = 6;
+	weapontexcoordcursor[WEAPON_TYPE_SNIPER].w = 2;
+	weapontexcoordcursor[WEAPON_TYPE_SNIPER].h = 2;
+	weapontexcoord[WEAPON_TYPE_SNIPER].x = 3;
+	weapontexcoord[WEAPON_TYPE_SNIPER].y = 6;
+	weapontexcoord[WEAPON_TYPE_SNIPER].w = 6;
+	weapontexcoord[WEAPON_TYPE_SNIPER].h = 2;
+	weaponprojtexcoord[WEAPON_TYPE_SNIPER].x = 10;
+	weaponprojtexcoord[WEAPON_TYPE_SNIPER].y = 6;
+	weaponprojtexcoord[WEAPON_TYPE_SNIPER].w = 1;
+	weaponprojtexcoord[WEAPON_TYPE_SNIPER].h = 1;*/
+
+	weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizex = 80.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_SHOTGUN].sizey = 20.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsetx = 24.0f;
+	weaponrenderparams[WEAPON_TYPE_SHOTGUN].offsety = -2.0f;
+	weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].x = 0;
+	weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].y = 6;
+	weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].w = 2;
+	weapontexcoordcursor[WEAPON_TYPE_SHOTGUN].h = 2;
+	weapontexcoord[WEAPON_TYPE_SHOTGUN].x = 2;
+	weapontexcoord[WEAPON_TYPE_SHOTGUN].y = 6;
+	weapontexcoord[WEAPON_TYPE_SHOTGUN].w = 8;
+	weapontexcoord[WEAPON_TYPE_SHOTGUN].h = 2;
+	weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].x = 10;
+	weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].y = 6;
+	weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].w = 2;
+	weaponprojtexcoord[WEAPON_TYPE_SHOTGUN].h = 2;
+
+	nummuzzletex[WEAPON_TYPE_SHOTGUN] = 3;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].x = 12;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].y = 6;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].w = 3;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][0].h = 2;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].x = 16;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].y = 6;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].w = 3;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][1].h = 2;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].x = 20;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].y = 6;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].w = 3;
+	muzzletexcoord[WEAPON_TYPE_SHOTGUN][2].h = 2;
+
+	muzzleparams[WEAPON_TYPE_SHOTGUN].sizex = 60.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_SHOTGUN].sizey = 40.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_SHOTGUN].offsetx = 50.0f * sizemodifier;
+	muzzleparams[WEAPON_TYPE_SHOTGUN].offsety = 6.0f * sizemodifier;
+
+
+
+	weaponrenderparams[WEAPON_TYPE_MELEE].sizex = 60.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_MELEE].sizey = 50.0f * sizemodifier;
+	weaponrenderparams[WEAPON_TYPE_MELEE].offsetx = 20.0f;
+	weaponrenderparams[WEAPON_TYPE_MELEE].offsety = -4.0f;
+	weapontexcoordcursor[WEAPON_TYPE_MELEE].x = 0;
+	weapontexcoordcursor[WEAPON_TYPE_MELEE].y = 0;
+	weapontexcoordcursor[WEAPON_TYPE_MELEE].w = 2;
+	weapontexcoordcursor[WEAPON_TYPE_MELEE].h = 2;
+	weapontexcoord[WEAPON_TYPE_MELEE].x = 2;
+	weapontexcoord[WEAPON_TYPE_MELEE].y = 1;
+	weapontexcoord[WEAPON_TYPE_MELEE].w = 4;
+	weapontexcoord[WEAPON_TYPE_MELEE].h = 3;
+	weaponprojtexcoord[WEAPON_TYPE_MELEE].x = 0;
+	weaponprojtexcoord[WEAPON_TYPE_MELEE].y = 0;
+	weaponprojtexcoord[WEAPON_TYPE_MELEE].w = 0;
+	weaponprojtexcoord[WEAPON_TYPE_MELEE].h = 0;
+
+
+	// MODIFIERS
+	sizemodifier = 2.0;
+	modifierrenderparams[MODIFIER_TYPE_NINJA].sizex = 60.0f * sizemodifier;
+	modifierrenderparams[MODIFIER_TYPE_NINJA].sizey = 20.0f * sizemodifier;
+	modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx = 20.0f;
+	modifierrenderparams[MODIFIER_TYPE_NINJA].offsety = 4.0f;
+	modifiertexcoord[MODIFIER_TYPE_NINJA].x = 2;
+	modifiertexcoord[MODIFIER_TYPE_NINJA].y = 10;
+	modifiertexcoord[MODIFIER_TYPE_NINJA].w = 7;
+	modifiertexcoord[MODIFIER_TYPE_NINJA].h = 2;
+	modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x = 0;
+	modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y = 10;
+	modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w = 2;
+	modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h = 2;
+
+	modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizex = 60.0f * sizemodifier;
+	modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].sizey = 20.0f * sizemodifier;
+	modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsetx = 16.0f;
+	modifierrenderparams[MODIFIER_TYPE_TIMEFIELD].offsety = 4.0f;
+	modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].x = 0;
+	modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].y = 0;
+	modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].w = 0;
+	modifiertexcoord[MODIFIER_TYPE_TIMEFIELD].h = 0;
+
+	stars[0].x = 0;
+	stars[0].y = 0;
+	stars[0].w = 2;
+	stars[0].h = 2;
+
+	stars[1].x = 0;
+	stars[1].y = 2;
+	stars[1].w = 2;
+	stars[1].h = 2;
+
+	particlecolors[0].x = 0.7f;
+	particlecolors[0].y = 0.7f;
+	particlecolors[0].z = 0.7f;
+	particlecolors[0].w = 1.0f;
+	particlestexcoord[0].x = 2;
+	particlestexcoord[0].y = 0;
+	particlestexcoord[0].w = 2;
+	particlestexcoord[0].h = 2;
+	particlecolors[1].x = 1.0f;
+	particlecolors[1].y = 1.0f;
+	particlecolors[1].z = 1.0f; 
+	particlecolors[1].w = 1.0f;
+	particlestexcoord[1].x = 4;
+	particlestexcoord[1].y = 0;
+	particlestexcoord[1].w = 2;
+	particlestexcoord[1].h = 2;
+	particlecolors[2].x = 0.8f;
+	particlecolors[2].y = 0.8f;
+	particlecolors[2].z = 0.8f;
+	particlecolors[2].w = 1.0f;
+	particlestexcoord[2].x = 6;
+	particlestexcoord[2].y = 0;
+	particlestexcoord[2].w = 2;
+	particlestexcoord[2].h = 2;
+	particlecolors[3].x = 0.988f;
+	particlecolors[3].y = 1.0f;
+	particlecolors[3].z = 0.16f;
+	particlecolors[3].w = 1.0f;
+	particlestexcoord[3].x = 8;
+	particlestexcoord[3].y = 0;
+	particlestexcoord[3].w = 2;
+	particlestexcoord[3].h = 2;
+	particlecolors[4].x = 1.0f;
+	particlecolors[4].y = 1.0f;
+	particlecolors[4].z = 1.0f;
+	particlecolors[4].w = 1.0f;
+	particlestexcoord[4].x = 10;
+	particlestexcoord[4].y = 0;
+	particlestexcoord[4].w = 2;
+	particlestexcoord[4].h = 2;
+	particlecolors[5].x = 0.6f;
+	particlecolors[5].y = 0.6f;
+	particlecolors[5].z = 0.6f;
+	particlecolors[5].w = 1.0f;
+	particlestexcoord[5].x = 2;
+	particlestexcoord[5].y = 2;
+	particlestexcoord[5].w = 2;
+	particlestexcoord[5].h = 2;
+	particlecolors[6].x = 1.0f;
+	particlecolors[6].y = 1.0f;
+	particlecolors[6].z = 1.0f; 
+	particlecolors[6].w = 1.0f;
+	particlestexcoord[6].x = 4;
+	particlestexcoord[6].y = 2;
+	particlestexcoord[6].w = 2;
+	particlestexcoord[6].h = 2;
+	particlecolors[5].x = 0.9f;
+	particlecolors[5].y = 0.9f;
+	particlecolors[5].z = 0.9f;
+	particlecolors[5].w = 1.0f;
+	particlestexcoord[7].x = 6;
+	particlestexcoord[7].y = 2;
+	particlestexcoord[7].w = 2;
+	particlestexcoord[7].h = 2;
+	particlecolors[8].x = 1.0f;
+	particlecolors[8].y = 1.0f;
+	particlecolors[8].z = 1.0f;
+	particlecolors[8].w = 1.0f;
+	particlestexcoord[8].x = 8;
+	particlestexcoord[8].y = 2;
+	particlestexcoord[8].w = 2;
+	particlestexcoord[8].h = 2;
+	lifemodifier[0] = 0.5f;
+	lifemodifier[1] = 0.5f;
+	lifemodifier[2] = 0.5f;
+	lifemodifier[3] = 0.7f;
+	lifemodifier[4] = 0.7f;
+	lifemodifier[5] = 1.0f;
+	lifemodifier[6] = 1.0f;
+	lifemodifier[7] = 1.5f;
+	lifemodifier[8] = 0.4f;
+
+	chaintexcoord.x = 2;
+	chaintexcoord.y = 0;
+	chaintexcoord.w = 1;
+	chaintexcoord.h = 1;
+
+	chainheadtexcoord.x = 3;
+	chainheadtexcoord.y = 0;
+	chainheadtexcoord.w = 2;
+	chainheadtexcoord.h = 1;
+
+
+	// anims
+	anim::setup_hammer(hammeranim);
+	anim::setup_ninja(ninjaanim);
+
+	for (int i = 0; i < NUMHADOKENS; i++)
+	{
+		hadoken[i].x = 1;
+		hadoken[i].y = 12;
+		hadoken[i].w = 7;
+		hadoken[i].h = 4;
+		hadokenparams[i].sizex = 0.0f;
+		hadokenparams[i].sizey = 0.0f;
+		hadokenparams[i].offsetx = 0.0f;
+		hadokenparams[i].offsety = 0.0f;//-hadokenparams[0].sizey * 0.15f;
+	}
+
+	// hadoken
+	hadoken[0].x = 1;
+	hadoken[0].y = 12;
+	hadoken[0].w = 7;
+	hadoken[0].h = 4;
+	hadokenparams[0].sizex = 70.0f * 2.5f;
+	hadokenparams[0].sizey = 40.0f * 2.5f;
+	hadokenparams[0].offsetx = -60.0f;
+	hadokenparams[0].offsety = 0;//-hadokenparams[0].sizey * 0.15f;
+
+	hadoken[2].x = 8;
+	hadoken[2].y = 12;
+	hadoken[2].w = 8;
+	hadoken[2].h = 4;
+	hadokenparams[2].sizex = 80.0f * 2.5f;
+	hadokenparams[2].sizey = 40.0f * 2.5f;
+	hadokenparams[2].offsetx = -60.0f;
+	hadokenparams[2].offsety = 0;//-hadokenparams[1].sizey * 0.5f;
+
+	hadoken[4].x = 17;
+	hadoken[4].y = 12;
+	hadoken[4].w = 7;
+	hadoken[4].h = 4;
+	hadokenparams[4].sizex = 70.0f * 2.5f;
+	hadokenparams[4].sizey = 40.0f * 2.5f;
+	hadokenparams[4].offsetx = -60.0f;
+	hadokenparams[4].offsety = 0;//-hadokenparams[2].sizey * 0.5f;
+
+	// 0 = outline, 1 = body
+	body[0].x = 2;
+	body[0].y = 0;
+	body[0].w = 2;
+	body[0].h = 2;
+	body[1].x = 0;
+	body[1].y = 0;
+	body[1].w = 2;
+	body[1].h = 2;
+
+	feet[0].x = 4;
+	feet[0].y = 1;
+	feet[0].w = 1;
+	feet[0].h = 0.5;
+	feet[1].x = 4;
+	feet[1].y = 1.52;
+	feet[1].w = 1;
+	feet[1].h = 0.48;
+	
+	leye.x = 5;
+	leye.y = 1;
+	leye.w = 0.5;
+	leye.h = 0.5;
+	reye.x = 5;
+	reye.y = 1.5;
+	reye.w = 0.5;
+	reye.h = 0.5;
+}
+
+void modc_entergame()
+{
+	col_init(32);
+	img_init();
+	tilemap_init();
+}
+
+void modc_shutdown()
+{
+}
+
+void modc_newsnapshot()
+{
+	int num = snap_num_items(SNAP_CURRENT);
+	for(int i = 0; i < num; i++)
+	{
+		snap_item item;
+		void *data = snap_get_item(SNAP_CURRENT, i, &item);
+		
+		if(item.type == EVENT_HEALTHMOD)
+		{
+			ev_healthmod *ev = (ev_healthmod *)data;
+			healthmods.create(vec2(ev->x, ev->y), ev->amount);
+		}
+		else if(item.type == EVENT_EXPLOSION)
+		{
+			ev_explosion *ev = (ev_explosion *)data;
+			vec2 p(ev->x, ev->y);
+			
+			// center explosion
+			temp_system.new_particle(p, vec2(0,0), 0.3f, 96.0f, 0, 0.95f);
+			temp_system.new_particle(p, vec2(0,0), 0.3f, 64.0f, 0, 0.95f);
+			temp_system.new_particle(p, vec2(0,0), 0.3f, 32.0f, 0, 0.95f);
+			temp_system.new_particle(p, vec2(0,0), 0.3f, 16.0f, 0, 0.95f);
+			
+			for(int i = 0; i < 16; i++)
+			{
+				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*128.0f);
+				temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 0, 0.985f);
+			}
+			
+			for(int i = 0; i < 16; i++)
+			{
+				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(256.0f+frandom()*512.0f);
+				temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 16.0f, 128.0f, 0.985f);
+			}
+
+			for(int i = 0; i < 64; i++)
+			{
+				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(frandom()*256.0f);
+				temp_system.new_particle(p, v, 0.2f+0.25f*frandom(), 24.0f, 128.0f, 0.985f);
+			}
+		}
+		else if(item.type == EVENT_SMOKE)
+		{
+			ev_explosion *ev = (ev_explosion *)data;
+			vec2 p(ev->x, ev->y);
+			
+			// center explosion
+			vec2 v = normalize(vec2(frandom()-0.5f, -frandom()))*(32.0f+frandom()*32.0f);
+			temp_system.new_particle(p, v, 1.2f, 64.0f, 0, 0.95f);
+			v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f);
+			temp_system.new_particle(p, v, 1.2f, 32.0f, 0, 0.95f);
+			v = normalize(vec2(frandom()-0.5f, -frandom()))*(128.0f+frandom()*128.0f);
+			temp_system.new_particle(p, v, 1.2f, 16.0f, 0, 0.95f);
+			
+			for(int i = 0; i < 8; i++)
+			{
+				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(64.0f+frandom()*64.0f);
+				temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 0, 0.985f);
+			}
+			
+			for(int i = 0; i < 8; i++)
+			{
+				vec2 v = normalize(vec2(frandom()-0.5f, frandom()-0.5f))*(128.0f+frandom()*256.0f);
+				temp_system.new_particle(p, v, 0.5f+0.5f*frandom(), 16.0f, 128.0f, 0.985f);
+			}
+		}
+		else if(item.type == EVENT_SOUND)
+		{
+			ev_sound *ev = (ev_sound *)data;
+			vec2 p(ev->x, ev->y);
+			int sound = (ev->sound & SOUND_MASK);
+			bool bstartloop = (ev->sound & SOUND_LOOPFLAG_STARTLOOP) != 0;
+			bool bstoploop = (ev->sound & SOUND_LOOPFLAG_STOPLOOP) != 0;
+			float vol, pan;
+			sound_vol_pan(p, &vol, &pan);
+
+			switch(sound)
+			{
+
+			// FIRE!
+			case SOUND_FIRE_GUN:
+				sound_gun_fire.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_FIRE_SHOTGUN:
+				sound_shotty_fire.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_FIRE_ROCKET:
+				sound_flump_launch.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_FIRE_MELEE:
+				sound_hammer_swing.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_FIRE_NINJA:
+				sound_ninja_attack.play_random(volume_gun*vol, pan);
+				break;
+
+			// IMPACT
+			case SOUND_IMPACT_PROJECTILE_GUN:
+				break;
+			case SOUND_IMPACT_PROJECTILE_SHOTGUN:
+				break;
+			case SOUND_IMPACT_PROJECTILE_ROCKET:
+				sound_flump_explode.play_random(volume_hit*vol, pan);
+				break;
+
+			// PLAYER
+			case SOUND_PLAYER_JUMP:
+				sound_body_jump.play_random(volume_tee*vol, pan);
+				break;
+			case SOUND_PLAYER_HURT_SHORT:
+				sound_pain_short.play_random(volume_tee*vol, pan);
+				break;
+			case SOUND_PLAYER_HURT_LONG:
+				sound_pain_long.play_random(volume_tee*vol, pan);
+				break;
+			case SOUND_PLAYER_SPAWN:
+				sound_spawn.play_random(volume_tee*vol, pan);
+				break;
+			case SOUND_PLAYER_CHAIN_LOOP:
+				sound_hook_loop.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_PLAYER_CHAIN_IMPACT:
+				sound_hook_attach.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_PLAYER_IMPACT:
+				sound_body_land.play_random(volume_hit*vol, pan);
+				break;
+			case SOUND_PLAYER_IMPACT_NINJA:
+				sound_ninja_hit.play_random(volume_hit*vol, pan);
+				break;
+			case SOUND_PLAYER_DIE:
+				sound_body_splat.play_random(volume_tee*vol, pan);
+				break;
+			case SOUND_PLAYER_SWITCHWEAPON:
+				sound_weapon_switch.play_random(volume_gun*vol, pan);
+				break;
+			case SOUND_PLAYER_EQUIP:
+				break;
+			case SOUND_PLAYER_LAND:
+				sound_body_land.play_random(volume_tee*vol, pan);
+				break;
+			}
+		}
+	}
+}
+
+static void render_projectile(obj_projectile *prev, obj_projectile *current)
+{
+	gfx_texture_set(texture_weapon);
+	gfx_quads_begin();
+	cell_select_ex(numcellsx,numcellsy,weaponprojtexcoord[current->type].x, weaponprojtexcoord[current->type].y, weaponprojtexcoord[current->type].w, weaponprojtexcoord[current->type].h);
+	vec2 vel(current->vx, current->vy);
+	
+	// TODO: interpolare angle aswell
+	if(length(vel) > 0.00001f)
+		gfx_quads_setrotation(get_angle(vel));
+	else
+		gfx_quads_setrotation(0);
+	
+	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick());
+	gfx_quads_draw(pos.x, pos.y,32,32);
+	gfx_quads_setrotation(0);
+	gfx_quads_end();
+}
+
+static void render_powerup(obj_powerup *prev, obj_powerup *current)
+{
+	//dbg_msg("client", "rendering powerup at %d,%d", current->x, current->y);
+	
+	gfx_texture_set(texture_weapon);
+	gfx_quads_begin();
+	float angle = 0.0f;
+	float sizex = 64.0f;
+	float sizey = 64.0f;
+	if (current->type == POWERUP_TYPE_WEAPON)
+	{
+		angle = -0.25f * pi * 2.0f;
+		cell_select_ex(numcellsx,numcellsy,weapontexcoord[current->subtype].x, weapontexcoord[current->subtype].y, weapontexcoord[current->subtype].w, weapontexcoord[current->subtype].h);
+		sizex = weaponrenderparams[current->subtype].sizex;
+		sizey = weaponrenderparams[current->subtype].sizey;
+	}
+	else
+		cell_select_ex(numcellsx,numcellsy,poweruptexcoord[current->type].x, poweruptexcoord[current->type].y, poweruptexcoord[current->type].w, poweruptexcoord[current->type].h);
+	vec2 vel(current->vx, current->vy);
+	
+	gfx_quads_setrotation(angle);
+	// TODO: interpolare angle aswell
+	/*if(length(vel) > 0.00001f)
+		gfx_quads_setrotation(get_angle(vel));
+	else
+		gfx_quads_setrotation(0);*/
+	
+	vec2 pos = mix(vec2(prev->x, prev->y), vec2(current->x, current->y), snap_intratick());
+	float offset = pos.y/32.0f + pos.x/32.0f;
+	gfx_quads_draw(pos.x+cosf(client_localtime()*2.0f+offset)*2.5f, pos.y+sinf(client_localtime()*2.0f+offset)*2.5f,sizex * 0.65f,sizey * 0.65f);
+	gfx_quads_setrotation(0);
+	gfx_quads_end();
+}
+
+float getmeleeangle(vec2 direction, obj_player* prev, obj_player* player)
+{
+	vec2 meleedir(0.53, -0.84);
+	meleedir = normalize(meleedir);
+	vec2 meleedirattack(0.95, -0.3);
+	meleedirattack = normalize(meleedirattack);
+
+	if(direction.x < 0)
+	{
+		meleedir.x = -meleedir.x;
+		meleedirattack.x = -meleedirattack.x;
+	}
+
+	// 0 -> visualtimeattack go to end pose, (len - visualime) -> go back to normal pose
+
+	float angle = get_angle(meleedir);
+	if (prev->attackticks)
+	{
+		float angleattack = get_angle(meleedirattack);
+		int phase1tick = (player->attacklen - player->attackticks);
+		if (phase1tick < player->visualtimeattack)
+		{
+			float intratick = snap_intratick();
+			float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack);
+			angle = LERP(angle, angleattack, min(1.0f,max(0.0f,t)));
+		}
+		else
+		{
+			// go back to normal pose
+			int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks);
+			float intratick = snap_intratick();
+			float t = ((((float)phase2tick) + intratick)/(float)player->visualtimeattack);
+			angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t)));
+		}
+	}
+	/*if (prev->attackticks && !player->attackticks)
+	{
+		// blend back to normal
+		float angleattack = get_angle(meleedirattack);
+		angle = LERP(angleattack, angle, min(1.0f,max(0.0f,snap_intratick())));
+	}
+	else if (player->attackticks)
+	{
+		float angleattack = get_angle(meleedirattack);
+		float intratick = snap_intratick();
+		float t = ((((float)player->attackticks) - intratick)/(float)player->attacklen);
+		angle = LERP(angleattack, angle, min(1.0f,max(0.0f,t)));
+	}*/
+
+	return angle;
+}
+
+float gethammereangle(vec2 direction, obj_player* prev, obj_player* player)
+{
+	float t = 0.0f;
+	if (prev->attackticks)
+		t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen);
+
+	vec2 pos;
+	float angle = 0.0f;
+	hammeranim.evalanim(t,pos,angle);
+	if(direction.x < 0)
+		angle = pi -angle;// + ;
+	//dbg_msg("anim", "Time: %f", t);
+	return angle;
+}
+
+float getninjaangle(vec2 direction, obj_player* prev, obj_player* player)
+{
+	float t = 0.0f;
+	if (prev->attackticks)
+		t = 1.0f - ((((float)player->attackticks) - snap_intratick())/(float)player->attacklen);
+
+	vec2 pos;
+	float angle = 0.0f;
+	ninjaanim.evalanim(t,pos,angle);
+	if(direction.x < 0)
+		angle = pi -angle;// + ;
+	//dbg_msg("anim", "Time: %f", t);
+	return angle;
+}
+
+
+float getrecoil(obj_player* prev, obj_player* player)
+{
+	// attack = -10
+	float recoil = 0.0f;
+	if (prev->attackticks)
+	{
+		float attackrecoil = recoils[player->weapon];
+		int phase1tick = (player->attacklen - player->attackticks);
+		if (phase1tick < player->visualtimeattack)
+		{
+			float intratick = snap_intratick();
+			float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack);
+			recoil = LERP(0, attackrecoil, min(1.0f,max(0.0f,t)));
+		}
+		else
+		{
+			// go back to normal pose
+			int phase2tick = (player->attacklen - player->visualtimeattack - player->attackticks);
+			float intratick = snap_intratick();
+			float t = ((((float)phase2tick) + intratick)/(float)(player->attacklen - player->visualtimeattack));
+			recoil = LERP(attackrecoil, 0.0f, min(1.0f,max(0.0f,t)));
+		}
+	}
+	return recoil;
+}
+
+static void render_player(obj_player *prev, obj_player *player)
+{
+	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), snap_intratick());
+	
+	// draw hook
+	if(player->hook_active)
+	{
+		gfx_texture_set(texture_weapon);
+		gfx_quads_begin();
+		//gfx_quads_begin();
+
+		vec2 pos = position;
+		
+		vec2 hook_pos = mix(vec2(prev->hook_x, prev->hook_y), vec2(player->hook_x, player->hook_y), snap_intratick());
+		
+		float d = distance(pos, hook_pos);
+		vec2 dir = normalize(pos-hook_pos);
+
+		gfx_quads_setrotation(get_angle(dir)+pi);
+		
+		// render head
+		cell_select_ex(numcellsx,numcellsy, chainheadtexcoord.x,chainheadtexcoord.y, chainheadtexcoord.w, chainheadtexcoord.h);
+		gfx_quads_draw(hook_pos.x, hook_pos.y, 24,16);
+		
+		// render chain
+		cell_select_ex(numcellsx,numcellsy, chaintexcoord.x, chaintexcoord.y, chaintexcoord.w, chaintexcoord.h);
+		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();
+	}
+
+	// draw gun
+	{
+		gfx_texture_set(texture_weapon);
+		gfx_quads_begin();
+		gfx_quads_setrotation(angle);
+
+		if (player->modifier & (1 << MODIFIER_TYPE_NINJA))
+		{
+			float playerangle = angle;
+			// render NINJA!!! (0.53, 0.84) when idle to -> (0.95, 0.3) at the end of attack
+			if(direction.x < 0)
+				cell_select_ex_flip_y(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h);
+			else
+				cell_select_ex(numcellsx, numcellsy, modifiertexcoord[MODIFIER_TYPE_NINJA].x, modifiertexcoord[MODIFIER_TYPE_NINJA].y, modifiertexcoord[MODIFIER_TYPE_NINJA].w, modifiertexcoord[MODIFIER_TYPE_NINJA].h);
+
+			angle = getninjaangle(direction, prev, player);//getmeleeangle(direction, prev, player);
+			vec2 ninjadir = get_direction(angle * 256.0f);
+			gfx_quads_setrotation(angle);
+			vec2 p = position + vec2(0,modifierrenderparams[MODIFIER_TYPE_NINJA].offsety)+ ninjadir * modifierrenderparams[MODIFIER_TYPE_NINJA].offsetx;
+			// if attack is active hold it differently and draw speedlines behind us?
+			gfx_quads_draw(p.x,p.y/*+bob*/,modifierrenderparams[MODIFIER_TYPE_NINJA].sizex, modifierrenderparams[MODIFIER_TYPE_NINJA].sizey);
+
+			if ((player->attacklen - player->attackticks) <= (SERVER_TICK_SPEED / 5))
+			{
+				gfx_quads_setrotation(playerangle);
+				int ihadoken = rand() % NUMHADOKENS;
+				cell_select_ex(numcellsx, numcellsy, hadoken[ihadoken].x, hadoken[ihadoken].y, hadoken[ihadoken].w, hadoken[ihadoken].h);
+				vec2 p = position + vec2(0,hadokenparams[ihadoken].offsety)+ direction * hadokenparams[ihadoken].offsetx;
+				gfx_quads_draw(p.x,p.y/*+bob*/,hadokenparams[ihadoken].sizex, hadokenparams[ihadoken].sizey);
+			}
+		}
+		else
+		{
+			// normal weapons
+			if(direction.x < 0)
+				cell_select_ex_flip_y(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h);
+			else
+				cell_select_ex(numcellsx, numcellsy, weapontexcoord[player->weapon].x, weapontexcoord[player->weapon].y, weapontexcoord[player->weapon].w, weapontexcoord[player->weapon].h);
+
+			vec2 dir = direction;
+			float recoil = 0.0f;
+			if (player->weapon == WEAPON_TYPE_MELEE)
+			{
+				// if attack is under way, bash stuffs
+				//angle = getmeleeangle(direction, prev, player);
+				angle = gethammereangle(direction, prev, player);
+				gfx_quads_setrotation(angle);
+				dir = get_direction(angle * 256.0f);
+			}
+			else
+			{
+				recoil = getrecoil(prev, player);
+			}
+
+			vec2 p = position + vec2(0,weaponrenderparams[player->weapon].offsety) + dir * weaponrenderparams[player->weapon].offsetx - dir * recoil;
+			gfx_quads_draw(p.x,p.y/*+bob*/,weaponrenderparams[player->weapon].sizex, weaponrenderparams[player->weapon].sizey);
+			// draw muzzleflare
+			if (player->weapon == WEAPON_TYPE_GUN || player->weapon == WEAPON_TYPE_SHOTGUN)
+			{
+				// check if we're firing stuff
+				if (true)///prev->attackticks)
+				{
+					float alpha = 0.0f;
+					int phase1tick = (player->attacklen - player->attackticks);
+					if (phase1tick < (player->visualtimeattack + 3))
+					{
+						float intratick = snap_intratick();
+						float t = ((((float)phase1tick) + intratick)/(float)player->visualtimeattack);
+						alpha = LERP(2.0, 0.0f, min(1.0f,max(0.0f,t)));
+					}
+
+					if (alpha > 0.0f)
+					{
+						float offsety = -muzzleparams[player->weapon].offsety;
+						int itex = rand() % nummuzzletex[player->weapon];
+						if(direction.x < 0)
+						{
+							offsety = -offsety;
+							cell_select_ex_flip_y(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h);
+						}
+						else
+							cell_select_ex(numcellsx, numcellsy, muzzletexcoord[player->weapon][itex].x, muzzletexcoord[player->weapon][itex].y, muzzletexcoord[player->weapon][itex].w, muzzletexcoord[player->weapon][itex].h);
+
+						gfx_quads_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/*+bob*/,muzzleparams[player->weapon].sizex, muzzleparams[player->weapon].sizey);
+					}
+				}
+			}
+		}
+		/*else
+		{
+			// minigun
+			if(direction.x < 0)
+				cell_select_flip_y(4,4,8,2);
+			else
+				cell_select(4,4,8,2);
+			vec2 p = position + vec2(0,3);
+			gfx_quads_draw(p.x,p.y,8*8,8*2);
+		}*/
+			
+		gfx_quads_setrotation(0);
+		gfx_quads_end();
+	}
+	
+	
+	gfx_texture_set(texture_char_default);
+	gfx_quads_begin();
+	
+	float bob = 0;
+	
+	// draw foots
+	const float cyclelength = 128.0f;
+	const float steplength = 26;
+	const float lift = 4.0f;
+	bool stationary = player->vx < 1 && player->vx > -1;
+	bool inair = col_check_point(player->x, player->y+16) == 0;
+	
+	for(int p = 0; p < 2; p++)
+	{
+		// first pass we draw the outline
+		// second pass we draw the filling
+		
+		//int v_offset = p?0:5;
+		int outline = p;// ? 1 : 0;
+		float offsety = charids[player->clientid % 16] * 2.0f;
+		
+		for(int f = 0; f < 2; f++)
+		{
+			float basesize = 10.0f;
+			if(f == 1)
+			{
+				// draw body
+				float t = fmod(position.x, cyclelength/2)/(cyclelength/2);
+				bob = -sinf(pow(t,2)*pi) * 3;
+				cell_select_ex(charnumcellsx,charnumcellsy, body[outline].x,body[outline].y + offsety,body[outline].w,body[outline].h);
+				//cell_select_ex(16,16, 0,0+v_offset,4,4);
+				//const float size = 64.0f;
+				if(stationary || inair)
+					bob = 0;
+				gfx_quads_draw(position.x, position.y-5+bob, 4*basesize, 4*basesize);
+				
+				// draw eyes
+				if(p == 1)
+				{
+					//cell_select_ex(16,16, 8,3,1,1);
+					vec2 md = get_direction(player->angle);
+					float mouse_dir_x = md.x;
+					float mouse_dir_y = md.y;
+					
+					// normal
+					cell_select_ex(charnumcellsx,charnumcellsy, leye.x,leye.y + offsety,leye.w,leye.h);
+					gfx_quads_draw(position.x-4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize);
+					cell_select_ex(charnumcellsx,charnumcellsy, reye.x,reye.y + offsety,reye.w,reye.h);
+					gfx_quads_draw(position.x+4+mouse_dir_x*4, position.y-8+mouse_dir_y*3+bob, basesize, basesize);
+				}
+			}
+
+			// draw feet
+			//cell_select_ex(16,16, 5,2+v_offset, 2,2);
+			cell_select_ex(charnumcellsx,charnumcellsy, feet[outline].x,feet[outline].y + offsety, feet[outline].w,feet[outline].h);
+			float w = basesize*2.5f;
+			float h = basesize*1.425f;
+			if(inair)
+			{
+				float r = 0.0f;
+				if(player->vy < 0.0f)
+					r = player->vy/3.0f;
+				else
+					r = player->vy/15.0f;
+
+				// clamp the rotation
+				if(r > 0.5f) r = 0.5f;
+				if(r < -0.5f) r = -0.5f;
+				
+				if(player->vx > 0.0f)
+					r *= -1.0f;
+				gfx_quads_setrotation(r);
+				gfx_quads_drawTL(position.x-4+f*7-w/2, position.y+16 - h, w, h);
+				gfx_quads_setrotation(0);
+			}
+			else if(stationary)
+			{
+				// stationary
+				gfx_quads_drawTL(position.x-7+f*14-w/2, position.y+16 - h, w, h);
+			}
+			else
+			{
+				/*
+					The walk cycle, 2 parts
+					
+						  111  
+						 1   1   
+						2     1
+						 2     1
+						  2222221 
+					GROUND GROUND GROUND
+				*/
+				
+				// moving
+				float tx = position.x+f*(cyclelength/2);
+				float t = fmod(tx, cyclelength) / cyclelength;
+				if(player->vx < 0)
+					t = 1.0f-t;
+				
+				float y;
+				float x = 0;
+				float r = 0;
+				float r_back = 1.5f;
+				
+				if(t < 0.5f)
+				{
+					// stomp down foot (part 1)
+					float st = t*2;
+					y = 1.0f-pow(st, 0.5f) + sinf(pow(st,2)*pi)*0.5f;
+					x = -steplength/2 + st*steplength;
+					r = r_back*(1-st) + sinf(pow(st,1.5f)*pi*2);
+				}
+				else
+				{
+					// lift foot up again (part 2)
+					float st = (t-0.5f)*2;
+					y = pow(st, 5.0f);
+					x = steplength/2 - st*steplength;
+					r = y*r_back;
+				}
+				
+
+				if(player->vx > 0)
+				{
+					gfx_quads_setrotation(r);
+					gfx_quads_drawTL(position.x+x-w/2, position.y+16-y*lift - h, w, h);
+				}
+				else
+				{
+					gfx_quads_setrotation(-r);
+					gfx_quads_drawTL(position.x-x-w/2, position.y+16-y*lift - h, w, h);
+				}
+				gfx_quads_setrotation(0);
+			}
+
+		}
+	}
+	
+	gfx_quads_end();
+	
+	
+}
+
+static player_input oldinput;
+static bool bfirst = true;
+void modc_render()
+{	
+	if (bfirst)
+	{
+		bfirst = false;
+		oldinput.activeweapon = 0;
+		oldinput.angle = 0;
+		oldinput.blink = 0;
+		oldinput.fire = 0;
+		oldinput.hook = 0;
+		oldinput.jump = 0;
+		oldinput.left = 0;
+		oldinput.right = 0;
+	}
+	// fetch new input
+	{
+		int x, y;
+		inp_mouse_relative(&x, &y);
+		mouse_pos += vec2(x, y);
+		float l = length(mouse_pos);
+		if(l > 600.0f)
+			mouse_pos = normalize(mouse_pos)*600.0f;
+	}
+	
+	// snap input
+	{
+		player_input input;
+		input.left = inp_key_pressed('A');
+		input.right = inp_key_pressed('D');
+		float a = atan((float)mouse_pos.y/(float)mouse_pos.x);
+		if(mouse_pos.x < 0)
+			a = a+pi;
+		input.angle = (int)(a*256.0f);
+		input.jump = inp_key_pressed(baselib::keys::space) || inp_key_pressed('W');
+		
+		input.fire = inp_mouse_button_pressed(0);// | (oldinput.fire << 16);
+		//oldinput.fire = input.fire & 0x0000ffff;
+		
+		input.hook = inp_mouse_button_pressed(1) || inp_key_pressed(baselib::keys::lctrl); // be nice to mac users O.o
+		input.blink = inp_key_pressed('S');
+		
+		// Weapon switching
+		input.activeweapon = inp_key_pressed('1') ? 0x80000000 : 0;
+		if (!input.activeweapon)
+			input.activeweapon = inp_key_pressed('2') ? 0x80000000 | 1 : 0;
+		if (!input.activeweapon)
+			input.activeweapon = inp_key_pressed('3') ? 0x80000000 | 2 : 0;
+		if (!input.activeweapon)
+			input.activeweapon = inp_key_pressed('4') ? 0x80000000 | 3 : 0;
+		/*if (!input.activeweapon)
+			input.activeweapon = inp_key_pressed('5') ? 0x80000000 | 4 : 0;*/
+
+		snap_input(&input, sizeof(input));
+	}
+
+	// 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;
+			void *data = snap_get_item(SNAP_CURRENT, i, &item);
+			
+			if(item.type == OBJTYPE_PLAYER)
+			{
+				obj_player *player = (obj_player *)data;
+				if(player->local)
+				{
+					local_player = player;
+					local_player_pos = vec2(player->x, player->y);
+
+					void *p = snap_find_item(SNAP_PREV, item.type, item.id);
+					if(p)
+						local_player_pos = mix(vec2(((obj_player *)p)->x, ((obj_player *)p)->y), local_player_pos, snap_intratick());
+					break;
+				}
+			}
+		}
+	}
+
+	// pseudo format
+	float zoom = inp_key_pressed('T') ? 1.0 : 3.0f;
+	
+	float width = 400*zoom;
+	float height = 300*zoom;
+	float screen_x = 0;
+	float screen_y = 0;
+	
+	// center at char but can be moved when mouse is far away
+	float offx = 0, offy = 0;
+	int deadzone = 300;
+	if(mouse_pos.x > deadzone) offx = mouse_pos.x-deadzone;
+	if(mouse_pos.x <-deadzone) offx = mouse_pos.x+deadzone;
+	if(mouse_pos.y > deadzone) offy = mouse_pos.y-deadzone;
+	if(mouse_pos.y <-deadzone) offy = mouse_pos.y+deadzone;
+	offx = offx*2/3;
+	offy = offy*2/3;
+		
+	screen_x = local_player_pos.x+offx;
+	screen_y = local_player_pos.y+offy;
+	
+	gfx_mapscreen(screen_x-width/2, screen_y-height/2, screen_x+width/2, screen_y+height/2);
+	
+	// draw background
+	gfx_clear(0.65f,0.78f,0.9f);
+	
+	{
+
+		vec2 pos(local_player_pos.x*0.5f, local_player_pos.y*0.5f);
+
+		gfx_texture_set(-1);
+		gfx_blend_additive();
+		gfx_quads_begin();
+		const int rays = 10;
+		gfx_quads_setcolor(1.0f,1.0f,1.0f,0.025f);
+		for(int r = 0; r < rays; r++)
+		{
+			float a = r/(float)rays + client_localtime()*0.05f;
+			float size = (1.0f/(float)rays)*0.25f;
+			vec2 dir0(sinf((a-size)*pi*2.0f), cosf((a-size)*pi*2.0f));
+			vec2 dir1(sinf((a+size)*pi*2.0f), cosf((a+size)*pi*2.0f));
+			
+			//gfx_quads_draw_freeform(0,0, -100,0, -100,-100, 0,-100);
+			
+			gfx_quads_setcolorvertex(0, 1.0f,1.0f,1.0f,0.025f);
+			gfx_quads_setcolorvertex(1, 1.0f,1.0f,1.0f,0.025f);
+			gfx_quads_setcolorvertex(2, 1.0f,1.0f,1.0f,0.0f);
+			gfx_quads_setcolorvertex(3, 1.0f,1.0f,1.0f,0.0f);
+			const float range = 1000.0f;
+			gfx_quads_draw_freeform(
+				pos.x+dir0.x, pos.y+dir0.y,
+				pos.x+dir1.x, pos.y+dir1.y,
+				pos.x+dir0.x*range, pos.y+dir0.y*range,
+				pos.x+dir1.x*range, pos.y+dir1.y*range);
+		}
+		gfx_quads_end();
+		gfx_blend_normal();
+		
+		gfx_texture_set(texture_sun);
+		gfx_quads_begin();
+		gfx_quads_draw(pos.x, pos.y, 256, 256);
+		gfx_quads_end();
+	}
+	
+	// render map
+	tilemap_render(32.0f, 0);
+#ifdef _DEBUG
+	float speed = 0.0f;
+#endif
+	// render items
+	int num = snap_num_items(SNAP_CURRENT);
+	for(int i = 0; i < num; i++)
+	{
+		snap_item item;
+		void *data = snap_get_item(SNAP_CURRENT, i, &item);
+		
+		if(item.type == OBJTYPE_PLAYER)
+		{
+			void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
+			if(prev)
+			{
+				render_player((obj_player *)prev, (obj_player *)data);
+/*#ifdef _DEBUG
+				{
+					obj_player *p = (obj_player *)prev;
+					obj_player *c = (obj_player *)data;
+					vec2 positionold = vec2(p->x, p->y);
+					vec2 poscur = vec2(c->x, c->y);
+					speed = distance(positionold,poscur);
+				}
+#endif*/
+			}
+		}
+		else if(item.type == OBJTYPE_PROJECTILE)
+		{
+			void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
+			if(prev)
+				render_projectile((obj_projectile *)prev, (obj_projectile *)data);
+		}
+		else if(item.type == OBJTYPE_POWERUP)
+		{
+			void *prev = snap_find_item(SNAP_PREV, item.type, item.id);
+			if(prev)
+				render_powerup((obj_powerup*)prev, (obj_powerup *)data);
+		}
+	}
+
+	// render particles
+	temp_system.update(client_frametime());
+	temp_system.render();
+
+	tilemap_render(32.0f, 1);
+	
+	// render health mods
+	healthmods.render();
+	
+	// render cursor 
+	// FIXME CURSOR!!!
+	
+	if(local_player)
+	{
+		gfx_texture_set(texture_weapon);
+		gfx_quads_begin();
+		if (local_player->modifier & (1 << MODIFIER_TYPE_NINJA))
+			cell_select_ex(numcellsx,numcellsy, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].x, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].y, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].w, modifiertexcoordcursor[MODIFIER_TYPE_NINJA].h);
+		else
+			cell_select_ex(numcellsx,numcellsy, weapontexcoordcursor[local_player->weapon].x, weapontexcoordcursor[local_player->weapon].y, weapontexcoordcursor[local_player->weapon].w, weapontexcoordcursor[local_player->weapon].h);
+		float cursorsize = 64;
+		gfx_quads_draw(local_player_pos.x+mouse_pos.x, local_player_pos.y+mouse_pos.y,cursorsize,cursorsize);
+
+
+		// render ammo count
+		// render gui stuff
+		gfx_quads_end();
+		gfx_quads_begin();
+		gfx_mapscreen(0,0,400,300);
+		cell_select_ex(numcellsx,numcellsy, weaponprojtexcoord[local_player->weapon].x, weaponprojtexcoord[local_player->weapon].y, weaponprojtexcoord[local_player->weapon].w, weaponprojtexcoord[local_player->weapon].h);
+		for (int i = 0; i < local_player->ammocount; i++)
+		{
+			gfx_quads_drawTL(10+i*12,34,10,10);
+		}
+		gfx_quads_end();
+
+		gfx_texture_set(texture_game);
+		gfx_quads_begin();
+		int h = 0;
+		cell_select_ex(32,16, 0,0, 4,4);
+		for(; h < local_player->health; h++)
+			gfx_quads_drawTL(10+h*12,10,10,10);
+		
+		cell_select_ex(32,16, 5,0, 4,4);
+		for(; h < 10; h++)
+			gfx_quads_drawTL(10+h*12,10,10,10);
+
+		h = 0;
+		cell_select_ex(32,16, 0,5, 4,4);
+		for(; h < local_player->armor; h++)
+			gfx_quads_drawTL(10+h*12,22,10,10);
+		
+		cell_select_ex(32,16, 5,5, 4,4);
+		for(; h < 10; h++)
+			gfx_quads_drawTL(10+h*12,22,10,10);
+		gfx_quads_end();
+
+		// render speed
+/*#ifdef _DEBUG
+		gfx_texture_set(font_texture);
+		char text[256];
+		sprintf(text,"speed: %f",speed);
+		gfx_quads_text(300,20,10,text);
+#endif*/
+	}
+	// render gui stuff
+	gfx_mapscreen(0,0,400,300);
+	// render score board
+	if(inp_key_pressed(baselib::keys::tab))
+	{
+		gfx_texture_set(font_texture);
+		gfx_quads_text(10, 50, 8, "Score Board");
+
+		int num = snap_num_items(SNAP_CURRENT);
+		int row = 1;
+		for(int i = 0; i < num; i++)
+		{
+			snap_item item;
+			void *data = snap_get_item(SNAP_CURRENT, i, &item);
+			
+			if(item.type == OBJTYPE_PLAYER)
+			{
+				obj_player *player = (obj_player *)data;
+				if(player)
+				{
+					char buf[128];
+					char name[32];
+					snap_decode_string(player->name, name, 32);
+					sprintf(buf, "%4d %s", player->score, name);
+					gfx_quads_text(10, 50 + 10 * row, 8, buf);
+					row++;
+				}
+			}
+		}
+	}
+}
diff --git a/src/game/game_server.cpp b/src/game/game_server.cpp
new file mode 100644
index 00000000..5e93165a
--- /dev/null
+++ b/src/game/game_server.cpp
@@ -0,0 +1,2122 @@
+#include <stdlib.h>
+#include <string.h>
+#include "game.h"
+
+using namespace baselib;
+
+// --------- PHYSICS TWEAK! --------
+const float ground_control_speed = 7.0f;
+const float ground_control_accel = 2.0f;
+const float ground_friction = 0.5f;
+const float ground_jump_speed = 12.0f;
+const float air_control_speed = 3.5f;
+const float air_control_accel = 1.2f;
+const float air_friction = 0.95f;
+const float hook_length = 32*10.0f;
+const float hook_fire_speed = 45.0f;
+const float hook_drag_accel = 3.0f;
+const float hook_drag_speed = 15.0f;
+const float gravity = 0.5f;
+
+class player* get_player(int index);
+void create_healthmod(vec2 p, int amount);
+void create_explosion(vec2 p, int owner = -1, bool bnodamage = false);
+void create_smoke(vec2 p);
+void create_sound(vec2 pos, int sound, int loopflags = 0);
+class player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, class entity* notthis = 0);
+
+// TODO: rewrite this smarter!
+void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
+{
+	// do the move
+	vec2 pos = *inout_pos;
+	vec2 vel = *inout_vel;
+	
+	float distance = length(vel);
+	int max = (int)distance;
+	
+	vec2 offsets[4] = {	vec2(-size.x/2, -size.y/2), vec2( size.x/2, -size.y/2),
+						vec2(-size.x/2,  size.y/2), vec2( size.x/2,  size.y/2)};
+	
+	if(distance > 0.00001f)
+	{
+		vec2 old_pos = pos;
+		for(int i = 0; i <= max; i++)
+		{
+			float amount = i/(float)max;
+			if(max == 0)
+				amount = 0;
+			
+			vec2 new_pos = pos + vel*amount; // TODO: this row is not nice
+
+			for(int p = 0; p < 4; p++)
+			{
+				vec2 np = new_pos+offsets[p];
+				vec2 op = old_pos+offsets[p];
+				if(col_check_point(np))
+				{
+					int affected = 0;
+					if(col_check_point(np.x, op.y))
+					{
+						vel.x = -vel.x*elasticity;
+						pos.x = old_pos.x;
+						new_pos.x = old_pos.x;
+						affected++;
+					}
+
+					if(col_check_point(op.x, np.y))
+					{
+						vel.y = -vel.y*elasticity;
+						pos.y = old_pos.y;
+						new_pos.y = old_pos.y;
+						affected++;
+					}
+					
+					if(!affected)
+					{
+						new_pos = old_pos;
+						pos = old_pos;
+						vel *= -elasticity;
+					}
+				}
+			}
+			
+			old_pos = new_pos;
+		}
+		
+		pos = old_pos;
+	}
+	
+	*inout_pos = pos;
+	*inout_vel = vel;
+}
+
+// TODO: rewrite this smarter!
+bool intersect_line(vec2 pos0, vec2 pos1, vec2 *out)
+{
+	float d = distance(pos0, pos1);
+	
+	for(float f = 0; f < d; f++)
+	{
+		float a = f/d;
+		vec2 pos = mix(pos0, pos1, a);
+		if(col_check_point(pos))
+		{
+			if(out)
+				*out = pos;
+			return true;
+		}
+	}
+	if(out)
+		*out = pos1;
+	return false;
+}
+
+//
+class event_handler
+{
+	static const int MAX_EVENTS = 128;
+	static const int MAX_DATASIZE = 128*4;
+
+	int types[MAX_EVENTS];  // TODO: remove some of these arrays
+	int offsets[MAX_EVENTS];
+	int sizes[MAX_EVENTS];
+	char data[MAX_DATASIZE];
+	
+	int current_offset;
+	int num_events;
+public:
+	event_handler()
+	{
+		num_events = 0;
+	}
+	
+	void *create(int type, int size)
+	{
+		void *p = &data[current_offset];
+		offsets[num_events] = current_offset;
+		types[num_events] = type;
+		sizes[num_events] = size;
+		current_offset += size;
+		num_events++;
+		return p;
+	}
+	
+	void clear()
+	{
+		num_events = 0;
+		current_offset = 0;
+	}
+	
+	void snap(int snapping_client)
+	{
+		for(int i = 0; i < num_events; i++)
+		{
+			void *d = snap_new_item(types[i], i, sizes[i]);
+			mem_copy(d, &data[offsets[i]], sizes[i]);
+		}
+	}
+};
+
+static event_handler events;
+/*
+template<typename T, int SIZE>
+class pool
+{
+	struct element
+	{
+		int next_free;
+		T data;
+	};
+	
+	element elements[SIZE];
+	int first_free;
+public:
+	pool()
+	{
+		first_free = 0;
+		for(int i = 0; i < SIZE; i++)
+			elements[i].next_free = i+1;
+		elements[SIZE-1].next_free = -1;
+	}
+};*/
+
+// a basic entity
+class entity
+{
+private:
+	friend class game_world;
+	friend class player;
+	entity *prev_entity;
+	entity *next_entity;
+	int index;
+	
+public:
+	vec2 pos;
+	float proximity_radius;
+	unsigned flags;
+	int objtype;
+
+	enum
+	{
+		FLAG_DESTROY=0x00000001,
+	};
+
+	entity(int objtype)
+	{
+		this->objtype = objtype;
+		pos = vec2(0,0);
+		flags = 0;
+		proximity_radius = 0;
+	}
+	
+	virtual ~entity()
+	{
+	}
+
+	virtual void destroy() { delete this; }
+	virtual void tick() {}
+	virtual void snap(int snapping_client) {}
+		
+	virtual bool take_damage(vec2 force, int dmg, int from) { return true; }
+};
+
+class powerup : public entity
+{
+public:
+	static const int phys_size = 14;
+	enum
+	{
+		POWERUP_FLAG_HOOKABLE = 1 << 0,
+	};
+	vec2 vel;
+	class player* playerhooked;
+	int type;
+	int id;
+	int subtype; // weapon type for instance?
+	int numitems; // number off powerup items
+	int flags;
+	int spawntick;
+	powerup(int _type, int _subtype = 0, int _numitems = 0, int _flags = 0);
+
+	static void spawnrandom(int _lifespan);
+
+	void tick();
+
+	void snap(int snapping_client);
+};
+
+// game world. handles all entities
+class game_world
+{
+public:
+	entity *first_entity;
+	game_world()
+	{
+		first_entity = 0x0;
+	}
+	
+	int find_entities(vec2 pos, float radius, entity **ents, int max)
+	{
+		int num = 0;
+		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		{
+			if(distance(ent->pos, pos) < radius+ent->proximity_radius)
+			{
+				ents[num] = ent;
+				num++;
+				if(num == max)
+					break;
+			}
+		}
+		
+		return num;
+	}
+
+	int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes)
+	{
+		int num = 0;
+		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		{
+			for (int i = 0; i < maxtypes; i++)
+			{
+				if (ent->objtype != types[i])
+					continue;
+
+				if(distance(ent->pos, pos) < radius+ent->proximity_radius)
+				{
+					ents[num] = ent;
+					num++;
+					if(num == max)
+						break;
+				}
+			}
+		}
+		
+		return num;
+	}
+	
+	void insert_entity(entity *ent)
+	{
+		// insert it
+		if(first_entity)
+			first_entity->prev_entity = ent;
+		ent->next_entity = first_entity;
+		ent->prev_entity = 0x0;
+		first_entity = ent;
+	}
+	
+	void destroy_entity(entity *ent)
+	{
+		ent->flags |= entity::FLAG_DESTROY;
+		// call destroy
+		//remove_entity(ent);
+		//ent->destroy();
+	}
+	
+	void remove_entity(entity *ent)
+	{
+		// remove
+		if(ent->prev_entity)
+			ent->prev_entity->next_entity = ent->next_entity;
+		else
+			first_entity = ent->next_entity;
+		if(ent->next_entity)
+			ent->next_entity->prev_entity = ent->prev_entity;
+	}
+	
+	//
+	void snap(int snapping_client)
+	{
+		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+			ent->snap(snapping_client);
+	}
+	
+	void tick()
+	{
+		// update all objects
+		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+			ent->tick();
+		
+		// destroy objects marked for destruction
+		entity *ent = first_entity;
+		while(ent)
+		{
+			entity *next = ent->next_entity;
+			if(ent->flags&entity::FLAG_DESTROY)
+			{
+				remove_entity(ent);
+				ent->destroy();
+			}
+			ent = next;
+		}
+	}
+};
+
+static game_world world;
+
+// projectile entity
+class projectile : public entity
+{
+public:
+	enum
+	{
+		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
+	};
+	vec2 vel;
+	entity* powner;
+	int lifespan;
+	int id;
+	int owner;
+	int type;
+	int flags;
+	int damage;
+	int sound_impact;
+	float force;
+	
+	projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags = 0, float force = 0.0f, int sound_impact = -1) :
+		entity(OBJTYPE_PROJECTILE)
+	{
+		static int current_id = 0;
+		this->id = current_id++;
+		this->type = type;
+		this->pos = pos;
+		this->vel = vel;
+		this->lifespan = span;
+		this->owner = owner;
+		this->powner = powner;
+		this->flags = flags;
+		this->force = force;
+		this->damage = damage;
+		this->sound_impact = sound_impact;
+		world.insert_entity(this);
+	}
+
+	void tick()
+	{
+		vec2 oldpos = pos;
+		vel.y += 0.25f;
+		pos += vel;
+		lifespan--;
+		// check player intersection as well
+		entity* targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner);
+		if(targetplayer || lifespan < 0 || col_check_point((int)pos.x, (int)pos.y))
+		{
+			if (lifespan >= 0)
+				create_sound(pos, sound_impact);
+			if (flags & PROJECTILE_FLAGS_EXPLODE)
+			{
+				create_explosion(oldpos, owner);
+			}
+			else if (targetplayer)
+			{
+				targetplayer->take_damage(normalize(vel) * force, damage, owner);
+			}
+			world.destroy_entity(this);
+		}
+	}
+	
+	void snap(int snapping_client)
+	{
+		obj_projectile *proj = (obj_projectile *)snap_new_item(OBJTYPE_PROJECTILE, id, sizeof(obj_projectile));
+		proj->x = (int)pos.x;
+		proj->y = (int)pos.y;
+		proj->vx = (int)vel.x;
+		proj->vy = (int)vel.y;
+		proj->type = type;
+	}
+};
+
+// player entity
+class player : public entity
+{
+public:
+	static const int phys_size = 28;
+	enum
+	{
+		WEAPON_NEEDRELOAD		= 1 << 0,
+		WEAPON_DROPONUNEQUIP	= 1 << 1,
+		WEAPON_DRAWSAMMO		= 1 << 2,
+		WEAPON_HASSECONDARY		= 1 << 3,
+		WEAPON_ISACTIVE			= 1 << 4, // has the item
+		WEAPON_AUTOFIRE			= 1 << 5,
+
+		WEAPON_PROJECTILETYPE_GUN		= 0,
+		WEAPON_PROJECTILETYPE_ROCKET	= 1,
+		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
+
+		// Gun
+
+
+		// modifiers
+		MODIFIER_HASACTIVATIONS			= 1 << 0,
+		MODIFIER_TIMELIMITED			= 1 << 1,
+		MODIFIER_ISACTIVE				= 1 << 2,
+		MODIFIER_NEEDSACTIVATION		= 1 << 3,
+
+		MODIFIER_RETURNFLAGS_OVERRIDEWEAPON		= 1 << 0,
+		MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY	= 1 << 1,
+		MODIFIER_RETURNFLAGS_OVERRIDEPOSITION	= 1 << 2,
+		MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY	= 1 << 3,
+	};
+	class weapon
+	{
+	public:
+		entity* hitobjects[10];
+		int numobjectshit; // for melee, so we don't hit the same object more than once per bash
+		int weapontype;
+		int equiptime;
+		int unequiptime;
+		int numammo;
+		int magsize;
+		int nummagazines;
+		int flags;
+		int firetime;
+		int reloadtime;
+		int projectileclass;
+		int damage;
+		int sound_fire;
+		int sound_equip;
+		int sound_impact;
+		int sound_impact_projectile;
+		int visualtimeattack;
+		float projectilevel;
+		float projectilespan;
+		float reach; // for melee
+		float force;
+		float recoilforce;
+		float projoffsety;
+		float projoffsetx;
+		
+		weapon()
+		{
+			weapontype = 0;
+			numammo = 0;
+			flags = 0;
+			reloadtime = 0;
+			projectileclass = 0;
+			numobjectshit = 0;
+			reach = 0.0f;
+			force = 5.0f;
+			damage = 1;
+			sound_fire = -1;
+			sound_equip = -1;
+			sound_impact = -1;
+			sound_impact_projectile = -1,
+			visualtimeattack = 3;
+			recoilforce = 0.0f;
+			projoffsety = 0.0f;
+			projoffsetx = 0.0f;
+		}
+
+		void setgun(int ammo = 10)
+		{
+			weapontype = WEAPON_TYPE_GUN;
+			flags = 0;//WEAPON_DRAWSAMMO;
+			numammo = ammo;
+			projectileclass = WEAPON_PROJECTILETYPE_GUN;
+			firetime = SERVER_TICK_SPEED/10;
+			magsize = 0;
+			projectilevel = 30.0f;
+			projectilespan = 50.0f * 1.0f;
+			sound_fire = SOUND_FIRE_GUN;
+			sound_equip = SOUND_EQUIP_GUN;
+			sound_impact_projectile = SOUND_IMPACT_PROJECTILE_GUN;
+			projoffsety = -10.0f;
+			projoffsetx = 24.0f;
+		}
+
+		void setrocket(int ammo = 10)
+		{
+			weapontype = WEAPON_TYPE_ROCKET;
+			flags = WEAPON_DRAWSAMMO;
+			numammo = ammo;
+			projectileclass = WEAPON_PROJECTILETYPE_ROCKET;
+			projectilevel = 15.0f;
+			projectilespan = 50.0f * 5.0f;
+			firetime = SERVER_TICK_SPEED * 4/5;
+			magsize = 0;
+			recoilforce = 5.0f;
+			sound_fire = SOUND_FIRE_ROCKET;
+			sound_equip = SOUND_EQUIP_ROCKET;
+			sound_impact_projectile = SOUND_IMPACT_PROJECTILE_ROCKET;
+			projoffsety = -17.0f;
+			projoffsetx = 24.0f;
+		}
+
+		/*void setsniper(int ammo = 10)
+		{
+			weapontype = WEAPON_TYPE_SNIPER;
+			flags = WEAPON_DRAWSAMMO | WEAPON_HASSECONDARY | WEAPON_NEEDRELOAD;
+			numammo = ammo;
+			projectileclass = WEAPON_PROJECTILETYPE_SNIPER;
+			projectilevel = 30.0f;
+			projectilespan = 50.0f * 5.0f;
+			firetime = SERVER_TICK_SPEED;
+			reloadtime = SERVER_TICK_SPEED/2;
+			magsize = 2;
+			recoilforce = 20.0f;
+		}*/
+
+		void setshotgun(int ammo = 10)
+		{
+			weapontype = WEAPON_TYPE_SHOTGUN;
+			flags = WEAPON_DRAWSAMMO | WEAPON_NEEDRELOAD;
+			numammo = ammo;
+			projectileclass = WEAPON_PROJECTILETYPE_SHOTGUN;
+			projectilevel = 30.0f;
+			projectilespan = 50.0f * 5.0f;
+			firetime = SERVER_TICK_SPEED/2;
+			reloadtime = SERVER_TICK_SPEED/2;
+			magsize = 2;
+			damage = 3;
+			recoilforce = 5.0f;
+			sound_fire = SOUND_FIRE_SHOTGUN;
+			sound_equip = SOUND_EQUIP_SHOTGUN;
+			sound_impact_projectile = SOUND_IMPACT_PROJECTILE_SHOTGUN;
+			projoffsety = -17.0f;
+			projoffsetx = 24.0f;
+		}
+
+		void setmelee(int ammo = 10)
+		{
+			weapontype = WEAPON_TYPE_MELEE;
+			flags = 0;//WEAPON_AUTOFIRE;
+			numammo = ammo;
+			projectileclass = -1;
+			firetime = SERVER_TICK_SPEED/5;
+			reloadtime = 0;
+			magsize = 2;
+			numobjectshit = 0;
+			reach = 15.0f;
+			damage = 1;
+			sound_fire = SOUND_FIRE_MELEE;
+			sound_equip = SOUND_EQUIP_MELEE;
+			sound_impact = SOUND_PLAYER_IMPACT;
+		}
+
+		void settype()
+		{
+			switch(weapontype)
+			{
+			case WEAPON_TYPE_GUN:
+				{
+					setgun();
+					break;
+				}
+			case WEAPON_TYPE_ROCKET:
+				{
+					setrocket();
+					break;
+				}
+			/*case WEAPON_TYPE_SNIPER:
+				{
+					setsniper();
+					break;
+				}*/
+			case WEAPON_TYPE_SHOTGUN:
+				{
+					setshotgun();
+					break;
+				}
+			case WEAPON_TYPE_MELEE:
+				{
+					setmelee();
+					break;
+				}
+			default:
+				break;
+			}
+		}
+
+		int activate(player* player)
+		{
+			// create sound event for fire
+			int projectileflags = 0;
+			create_sound(player->pos, sound_fire);
+
+			switch (weapontype)
+			{
+			case WEAPON_TYPE_ROCKET:
+				projectileflags |= projectile::PROJECTILE_FLAGS_EXPLODE;
+			case WEAPON_TYPE_GUN:
+			//case WEAPON_TYPE_SNIPER:
+			case WEAPON_TYPE_SHOTGUN:
+				{
+					if (flags & WEAPON_DRAWSAMMO)
+						numammo--;
+					// Create projectile
+					new projectile(projectileclass, player->client_id, player->pos+vec2(0,projoffsety)+player->direction*projoffsetx, player->direction*projectilevel, projectilespan, player, damage, projectileflags, force, sound_impact_projectile);
+					// recoil force if any
+					if (recoilforce > 0.0f)
+					{
+						vec2 dir(player->direction.x,0.5);
+						if (dir.x == 0.0f)
+							dir.x = 0.5f;
+						else
+							dir = normalize(dir);
+						player->vel -= dir * recoilforce;
+					}
+					return firetime;
+				}
+			case WEAPON_TYPE_MELEE:
+				{
+					// Start bash sequence
+					numobjectshit = 0;
+					return firetime;
+				}
+			default:
+				return 0;
+			}
+		}
+
+		void update(player* owner, int fire_timeout)
+		{
+			switch(weapontype)
+			{
+			case WEAPON_TYPE_MELEE:
+				{
+					// No more melee
+					if (fire_timeout <= 0)
+						return;
+
+					// only one that needs update (for now)
+					// do selection for the weapon and bash anything in it
+					// check if we hit anything along the way
+					int type = OBJTYPE_PLAYER;
+					entity *ents[64];
+					vec2 dir = owner->pos + owner->direction * reach;
+					float radius = length(dir * 0.5f);
+					vec2 center = owner->pos + dir * 0.5f;
+					int num = world.find_entities(center, radius, ents, 64, &type, 1);
+					
+					for (int i = 0; i < num; i++)
+					{
+						// Check if entity is a player
+						if (ents[i] == owner)
+							continue;
+						// make sure we haven't hit this object before
+						bool balreadyhit = false;
+						for (int j = 0; j < numobjectshit; j++)
+						{
+							if (hitobjects[j] == ents[i])
+								balreadyhit = true;
+						}
+						if (balreadyhit)
+							continue;
+
+						// check so we are sufficiently close
+						if (distance(ents[i]->pos, owner->pos) > (owner->phys_size * 2.0f))
+							continue;
+					
+						// hit a player, give him damage and stuffs...
+						// create sound for bash
+						create_sound(ents[i]->pos, sound_impact);
+
+						// set his velocity to fast upward (for now)
+						create_smoke(ents[i]->pos);
+						hitobjects[numobjectshit++] = ents[i];
+						ents[i]->take_damage(vec2(0,10.0f), damage, owner->client_id);
+						player* target = (player*)ents[i];
+						vec2 dir;
+						if (length(target->pos - owner->pos) > 0.0f)
+							dir = normalize(target->pos - owner->pos);
+						else
+							dir = vec2(0,-1);
+						target->vel += dir * 10.0f + vec2(0,-10.0f);
+					}
+					break;
+				}
+			default:
+				break;
+			}
+		}
+	};
+
+	class modifier
+	{
+	public:
+		vec2 activationdir;
+		entity* hitobjects[10];
+		int numobjectshit;
+		float velocity;
+		int modifiertype;
+		int duration;
+		int numactivations;
+		int activationtime;
+		int cooldown;
+		int movetime;
+		int visualtimeattack;
+		int currentactivation;
+		int currentmovetime;
+		int currentcooldown;
+		int damage;
+		int flags;
+		int sound_impact;
+		int sound_activate;
+
+		modifier()
+		{
+			modifiertype = 0;
+			duration = 0;
+			numobjectshit = 0;
+			numactivations = 0;
+			activationtime = 0;
+			cooldown = 0;
+			movetime = 0;
+			currentactivation = 0;
+			currentmovetime = 0;
+			currentcooldown =0;
+			damage = 0;
+			flags = 0;
+			activationdir = vec2(0.0f, 1.0f);
+			velocity = 0.0f;
+			visualtimeattack = 0;
+			sound_impact = -1;
+		}
+
+		void setninja()
+		{
+			modifiertype = MODIFIER_TYPE_NINJA;
+			duration = SERVER_TICK_SPEED * 15;
+			numactivations = -1;
+			movetime = SERVER_TICK_SPEED / 5;
+			activationtime = SERVER_TICK_SPEED / 2;
+			cooldown = SERVER_TICK_SPEED;
+			currentactivation = 0;
+			currentmovetime = 0;
+			numobjectshit = 0;
+			damage = 3;
+			flags = MODIFIER_TIMELIMITED | MODIFIER_NEEDSACTIVATION;
+			velocity = 50.0f;
+			visualtimeattack = 3;
+			sound_impact = SOUND_PLAYER_IMPACT_NINJA;
+			sound_activate = SOUND_FIRE_NINJA;
+		}
+
+		void settimefield()
+		{
+			modifiertype = MODIFIER_TYPE_TIMEFIELD;
+			duration = SERVER_TICK_SPEED * 10;
+			numactivations = -1;
+			activationtime = SERVER_TICK_SPEED;
+			numobjectshit = 0;
+			currentactivation = 0;
+			flags = MODIFIER_TIMELIMITED;
+			velocity = 0.0f;
+		}
+
+		void settype()
+		{
+			switch (modifiertype)
+			{
+			case MODIFIER_TYPE_NINJA:
+				{
+					setninja();
+					break;
+				}
+			case MODIFIER_TYPE_TIMEFIELD:
+				{
+					settimefield();
+					break;
+				}
+			default:
+				break;
+			}
+		}
+
+		int updateninja(player* player)
+		{
+			if (currentactivation <= 0)
+				return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
+			currentactivation--;
+			currentmovetime--;
+			
+			if (currentmovetime == 0)
+			{	
+				// reset player velocity
+				player->vel *= 0.2f;
+				//return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
+			}
+			
+			if (currentmovetime > 0)
+			{
+				// Set player velocity
+				player->vel = activationdir * velocity;
+				vec2 oldpos = player->pos;
+				move_box(&player->pos, &player->vel, vec2(player->phys_size, player->phys_size), 0.0f);
+				// reset velocity so the client doesn't predict stuff
+				player->vel = vec2(0.0f,0.0f);
+				if ((currentmovetime % 2) == 0)
+				{
+					create_smoke(player->pos);
+				}
+				
+				// check if we hit anything along the way
+				{
+					int type = OBJTYPE_PLAYER;
+					entity *ents[64];
+					vec2 dir = player->pos - oldpos;
+					float radius = length(dir * 0.5f);
+					vec2 center = oldpos + dir * 0.5f;
+					int num = world.find_entities(center, radius, ents, 64, &type, 1);
+					
+					for (int i = 0; i < num; i++)
+					{
+						// Check if entity is a player
+						if (ents[i] == player)
+							continue;
+						// make sure we haven't hit this object before
+						bool balreadyhit = false;
+						for (int j = 0; j < numobjectshit; j++)
+						{
+							if (hitobjects[j] == ents[i])
+								balreadyhit = true;
+						}
+						if (balreadyhit)
+							continue;
+
+						// check so we are sufficiently close
+						if (distance(ents[i]->pos, player->pos) > (player->phys_size * 2.0f))
+							continue;
+					
+						// hit a player, give him damage and stuffs...
+						create_sound(ents[i]->pos, sound_impact);
+						// set his velocity to fast upward (for now)
+						hitobjects[numobjectshit++] = ents[i];
+						ents[i]->take_damage(vec2(0,10.0f), damage, player->client_id);
+					}
+				}
+				return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY | MODIFIER_RETURNFLAGS_OVERRIDEPOSITION|MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY;
+			}
+
+
+			// move char, and check intersection from us to target
+			return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON | MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY;
+		}
+
+		int activateninja(player* player)
+		{
+			// ok then, activate ninja
+			activationdir = player->direction;
+			currentactivation = activationtime;
+			currentmovetime = movetime;
+			currentcooldown = cooldown;
+			// reset hit objects
+			numobjectshit = 0;
+
+			create_sound(player->pos, SOUND_FIRE_NINJA);
+
+			return MODIFIER_RETURNFLAGS_OVERRIDEWEAPON;
+		}
+
+		int activate(player* player)
+		{
+			if (flags & MODIFIER_NEEDSACTIVATION)
+			{
+				if (!numactivations)
+					return 0;
+				numactivations--;
+			}
+
+			switch (modifiertype)
+			{
+			case MODIFIER_TYPE_NINJA:
+				{
+					return activateninja(player);
+				}
+			/*case MODIFIER_TYPE_TIMEFIELD:
+				{
+					updatetimefield();
+					break;
+				}*/
+			default:
+				return 0;
+			}
+		}
+		int update(player* player)
+		{
+			switch (modifiertype)
+			{
+			case MODIFIER_TYPE_NINJA:
+				{
+					return updateninja(player);
+				}
+			/*case MODIFIER_TYPE_TIMEFIELD:
+				{
+					updatetimefield();
+					break;
+				}*/
+			default:
+				return 0;
+			}
+		}
+	};
+
+	enum
+	{
+		PLAYER_FLAGS_ISRELOADING = 1 << 0,
+		PLAYER_FLAGS_ISEQUIPPING = 1 << 1,
+	};
+
+	weapon lweapons[WEAPON_NUMWEAPONS];
+	modifier modifiers[MODIFIER_NUMMODIFIERS];
+	int iactiveweapon;
+	int inextweapon;
+	int equip_time;
+
+	int client_id;
+	int flags;
+
+	char name[32];
+	player_input previnput;
+	player_input input;
+	int tick_count;
+	int damage_taken_tick;
+
+	vec2 vel;
+	vec2 direction;
+
+	int jumped;
+	int airjumped;
+
+	//int firing;
+	int hooking;
+
+	int fire_timeout;
+	int reload_timeout;
+
+	int health;
+	int armor;
+
+	int score;
+
+	// sounds
+	int sound_player_jump;
+	int sound_player_land;
+	int sound_player_hurt_short;
+	int sound_player_hurt_long;
+	int sound_player_spawn;
+	int sound_player_chain_loop;
+	int sound_player_chain_impact;
+	int sound_player_impact;
+	int sound_player_impact_ninja;
+	int sound_player_die;
+	int sound_player_switchweapon;
+
+	player* phookedplayer;
+	powerup* phookedpowerup;
+	int numhooked;
+	vec2 hook_pos;
+	vec2 hook_dir;
+
+	player() :
+		entity(OBJTYPE_PLAYER)
+	{
+		reset();
+		
+		//firing = 0;
+		// setup weaponflags and stuff
+		lweapons[WEAPON_TYPE_GUN].setgun();
+		lweapons[WEAPON_TYPE_ROCKET].setrocket();
+		//lweapons[WEAPON_TYPE_SNIPER].setsniper();
+		lweapons[WEAPON_TYPE_SHOTGUN].setshotgun();
+		lweapons[WEAPON_TYPE_MELEE].setmelee();
+
+		modifiers[MODIFIER_TYPE_NINJA].setninja();
+		modifiers[MODIFIER_TYPE_TIMEFIELD].settimefield();
+		//modifiers[MODIFIER_TYPE_NINJA].flags |= MODIFIER_ISACTIVE;
+
+		sound_player_jump = SOUND_PLAYER_JUMP;
+		sound_player_hurt_short = SOUND_PLAYER_HURT_SHORT;
+		sound_player_hurt_long = SOUND_PLAYER_HURT_LONG;
+		sound_player_spawn = SOUND_PLAYER_SPAWN;
+		sound_player_chain_loop = SOUND_PLAYER_CHAIN_LOOP;
+		sound_player_chain_impact = SOUND_PLAYER_CHAIN_IMPACT;
+		sound_player_impact = SOUND_PLAYER_IMPACT;
+		sound_player_impact_ninja = SOUND_PLAYER_IMPACT_NINJA;
+		sound_player_die = SOUND_PLAYER_DIE;
+		sound_player_switchweapon = SOUND_PLAYER_SWITCHWEAPON;
+		sound_player_land = SOUND_PLAYER_LAND;
+	}
+	
+	void reset()
+	{
+		equip_time = 0;
+		phookedplayer = 0;
+		numhooked = 0;
+		proximity_radius = phys_size;
+		name[0] = 'n';
+		name[1] = 'o';
+		name[2] = 'o';
+		name[3] = 'b';
+		name[4] = 0;
+		
+		pos = vec2(100.0f, 0.0f);
+		vel = vec2(0.0f, 0.0f);
+		direction = vec2(0.0f, 1.0f);
+		client_id = -1;
+		tick_count = 0;
+		score = 0;
+		flags = 0;		
+	}
+	
+	virtual void destroy() { flags = 0; }
+		
+	void respawn()
+	{
+		health = PLAYER_MAXHEALTH;
+		armor = 0;
+		
+		hooking = 0;
+		phookedplayer = 0;
+		phookedpowerup = 0;
+		numhooked = 0;
+		fire_timeout = 0;
+		reload_timeout = 0;
+		iactiveweapon = 0;
+		inextweapon = -1;
+		equip_time = 0;
+		jumped = 0;
+		airjumped = 0;
+		mem_zero(&input, sizeof(input));
+		vel = vec2(0.0f, 0.0f);
+		
+		int start, num;
+		map_get_type(1, &start, &num);
+		
+		if(num)
+		{
+			mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
+			pos = vec2(sp->x, sp->y);
+		}
+		else
+			pos = vec2(100.0f, -60.0f);
+
+		// reset active flags
+		for (int i = 0; i < WEAPON_NUMWEAPONS; i++)
+		{
+			// reset and remove
+			lweapons[i].settype();
+			lweapons[i].flags &= ~WEAPON_ISACTIVE;
+		}
+
+
+		// TEMP REMOVE
+		
+		/*for (int i = 0; i < WEAPON_NUMWEAPONS; i++)
+		{
+			lweapons[i].settype();
+			lweapons[i].flags |= WEAPON_ISACTIVE;
+		}*/
+		lweapons[WEAPON_TYPE_MELEE].flags |= WEAPON_ISACTIVE;
+		// Add gun as default weapon
+		iactiveweapon = WEAPON_TYPE_GUN;
+		lweapons[WEAPON_TYPE_GUN].numammo = 10;
+		lweapons[WEAPON_TYPE_GUN].flags |= WEAPON_ISACTIVE;
+
+		create_sound(pos, sound_player_spawn);
+	}
+	
+	bool is_grounded()
+	{
+		if(col_check_point((int)(pos.x+phys_size/2), (int)(pos.y+phys_size/2+5)))
+			return true;
+		if(col_check_point((int)(pos.x-phys_size/2), (int)(pos.y+phys_size/2+5)))
+			return true;
+		return false;
+	}
+
+	// Disable weapon activation if this returns true
+	int handlemodifiers()
+	{
+		int returnflags = 0;
+		for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
+		{
+			if (modifiers[i].flags & MODIFIER_ISACTIVE)
+			{
+				modifiers[i].duration--;
+				modifiers[i].currentcooldown--;
+
+				// Check if it should activate
+				if (modifiers[i].currentcooldown <= 0 && 
+					(modifiers[i].flags & MODIFIER_NEEDSACTIVATION) &&
+					input.fire && !(previnput.fire))
+				{
+					returnflags |= modifiers[i].activate(this);
+				}
+
+				returnflags |= modifiers[i].update(this);
+
+				// remove active if timed out
+				if (modifiers[i].duration <= 0 && modifiers[i].currentactivation <= 0)
+					modifiers[i].flags &= ~MODIFIER_ISACTIVE;
+			}
+		}
+		return returnflags;
+	}
+
+	void handleweapon()
+	{
+		// handle weapon
+		if(input.fire && (!previnput.fire || lweapons[iactiveweapon].flags & WEAPON_AUTOFIRE) && 
+			!(flags & PLAYER_FLAGS_ISEQUIPPING) && !reload_timeout)
+		{
+			if(fire_timeout == 0)
+			{
+				if (lweapons[iactiveweapon].numammo || !(lweapons[iactiveweapon].flags & WEAPON_DRAWSAMMO))
+				{
+					// Decrease ammo
+					fire_timeout = lweapons[iactiveweapon].activate(this);
+				}
+				else if ((lweapons[iactiveweapon].flags & WEAPON_NEEDRELOAD) && lweapons[iactiveweapon].nummagazines)
+				{
+					// reload
+					reload_timeout = lweapons[iactiveweapon].reloadtime;
+					lweapons[iactiveweapon].nummagazines--;
+					lweapons[iactiveweapon].numammo = lweapons[iactiveweapon].magsize;
+				}
+			}
+		}
+
+		// update active weapon
+		lweapons[iactiveweapon].update(this, fire_timeout);
+	}
+
+	void handlehook()
+	{
+		// handle hook
+		if(input.hook)
+		{
+			if(hooking == 0)
+			{
+				hooking = 1;
+				hook_pos = pos;
+				hook_dir = direction;
+				// Sound
+				create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STARTLOOP);
+			}
+			else if(hooking == 1)
+			{
+				vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
+
+				// Check against other players and powerups first
+				player* targetplayer = 0;
+				powerup* targetpowerup = 0;
+				{
+					static const int typelist[2] = { OBJTYPE_PLAYER, OBJTYPE_POWERUP};
+					entity *ents[64];
+					vec2 dir = new_pos - hook_pos;
+					float radius = length(dir * 0.5f);
+					vec2 center = hook_pos + dir * 0.5f;
+					int num = world.find_entities(center, radius, ents, 64,typelist,2);
+					
+					for (int i = 0; i < num; i++)
+					{
+						// Check if entity is a player
+						if (ents[i] == this || (targetplayer && targetpowerup))
+							continue;
+
+						if (!targetplayer && ents[i]->objtype == OBJTYPE_PLAYER)
+						{
+							// temp, set hook pos to our position
+							if (((player*)ents[i])->phookedplayer != this)
+								targetplayer = (player*)ents[i];
+						}
+						else if (!targetpowerup && ents[i]->objtype == OBJTYPE_POWERUP && 
+							(((powerup*)ents[i])->flags & powerup::POWERUP_FLAG_HOOKABLE))
+						{
+							targetpowerup = (powerup*)ents[i];
+						}
+					}
+				}
+
+				//player* targetplayer = intersect_player(hook_pos, hook_pos, new_pos, this);
+				if (targetplayer)
+				{
+					// So he can't move "normally"
+					new_pos = targetplayer->pos;
+					phookedplayer = targetplayer;
+					targetplayer->numhooked++;
+					hooking = 3;
+					create_sound(pos, sound_player_chain_impact);
+
+					// stop looping chain sound
+					create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
+				}
+				else if (targetpowerup)
+				{
+					new_pos = targetpowerup->pos;
+					phookedpowerup = targetpowerup;
+					phookedpowerup->playerhooked = this;
+					hooking = 4;
+					create_sound(pos, sound_player_chain_impact);
+
+					// stop looping chain sound
+					create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
+				}
+				else if(intersect_line(hook_pos, new_pos, &new_pos))
+				{
+					hooking = 2;
+					create_sound(pos, sound_player_chain_impact);
+
+					// stop looping chain sound
+					create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
+				}
+				else if(distance(pos, new_pos) > hook_length)
+				{
+					hooking = -1;
+					create_sound(pos, sound_player_chain_loop, SOUND_LOOPFLAG_STOPLOOP);
+				}
+				
+				hook_pos = new_pos;
+			}
+			else if(hooking == 2)
+			{
+				vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
+				// the hook as more power to drag you up then down.
+				// this makes it easier to get on top of an platform
+				if(hookvel.y > 0)
+					hookvel.y *= 0.3f;
+				
+				// the hook will boost it's power if the player wants to move
+				// in that direction. otherwise it will dampen everything abit
+				if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) 
+					hookvel.x *= 0.95f;
+				else
+					hookvel.x *= 0.75f;
+				vec2 new_vel = vel+hookvel;
+				
+				// check if we are under the legal limit for the hook
+				if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
+					vel = new_vel; // no problem. apply
+			}
+			else if (hooking == 3)
+			{
+				// hmm, force the targetplayer towards us if possible, otherwise us towards them if they are already hooked
+				if (phookedplayer)
+				{
+					if (phookedplayer->hooking > 1)
+					{
+						// Drag us towards target player
+						vec2 hookvel = normalize(hook_pos-pos)*hook_drag_accel;
+						if((hookvel.x < 0 && input.left) || (hookvel.x > 0 && input.right)) 
+							hookvel.x *= 0.95f;
+						else
+							hookvel.x *= 0.75f;
+
+						// Apply the velocity
+						// the hook will boost it's power if the player wants to move
+						// in that direction. otherwise it will dampen everything abit
+						vec2 new_vel = vel+hookvel;
+						
+						// check if we are under the legal limit for the hook
+						if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
+							vel = new_vel; // no problem. apply
+					}
+					else
+					{
+						// Drag targetplayer towards us
+						vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel;
+
+						// Apply the velocity
+						// the hook will boost it's power if the player wants to move
+						// in that direction. otherwise it will dampen everything abit
+						vec2 new_vel = phookedplayer->vel+hookvel;
+						
+						// check if we are under the legal limit for the hook
+						if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
+							phookedplayer->vel = new_vel; // no problem. apply
+					}
+					hook_pos = phookedplayer->pos;
+					// if hooked player dies, release the hook
+				}
+				else
+				{
+					hooking = -1;
+					phookedplayer = 0;
+				}
+			}
+			else if (hooking == 4)
+			{
+				// Have a powerup, drag it towards us
+				vec2 hookvel = normalize(pos-hook_pos)*hook_drag_accel;
+
+				// Apply the velocity
+				// the hook will boost it's power if the player wants to move
+				// in that direction. otherwise it will dampen everything abit
+				vec2 new_vel = phookedpowerup->vel+hookvel;
+				
+				// check if we are under the legal limit for the hook
+				if(length(new_vel) < hook_drag_speed || length(new_vel) < length(vel))
+					phookedpowerup->vel = new_vel; // no problem. apply
+				hook_pos = phookedpowerup->pos;
+			}
+		}
+		else
+		{
+			hooking = 0;
+			hook_pos = pos;
+			if (phookedplayer)
+			{
+				phookedplayer->numhooked--;
+				phookedplayer = 0;
+			}
+		}
+	}
+
+	void getattackticks(int& curattack, int& attacklen, int& visualtimeattack)
+	{
+		// time left from current attack (if any)
+		// first check modifiers (ninja...)
+		for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
+		{
+			if ((modifiers[i].flags & (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION)) == (MODIFIER_ISACTIVE | MODIFIER_NEEDSACTIVATION))
+			{
+				curattack = modifiers[i].currentactivation;
+				attacklen = modifiers[i].activationtime;
+				visualtimeattack = modifiers[i].visualtimeattack;
+				return;
+			}
+		}
+
+		// otherwise current fire timeout
+		curattack = fire_timeout;
+		attacklen = lweapons[iactiveweapon].firetime;
+		visualtimeattack = lweapons[iactiveweapon].visualtimeattack;
+	}
+	
+	virtual void tick()
+	{
+		tick_count++;
+		
+		// fetch some info
+		bool grounded = is_grounded();
+		direction = get_direction(input.angle);
+		
+		// decrease reload timer
+		if(fire_timeout)
+			fire_timeout--;
+		if (reload_timeout)
+			reload_timeout--;
+
+		// Switch weapons
+		if (flags & PLAYER_FLAGS_ISEQUIPPING)
+		{
+			equip_time--;
+			if (equip_time <= 0)
+			{
+				if (inextweapon >= 0)
+				{
+					equip_time = SERVER_TICK_SPEED * lweapons[inextweapon].equiptime;
+					iactiveweapon = inextweapon;
+					inextweapon = -1;
+
+					// Send switch weapon event to client?
+				}
+				else
+				{
+					flags &= ~PLAYER_FLAGS_ISEQUIPPING;
+				}
+			}
+		}
+		else if (input.activeweapon && iactiveweapon != (input.activeweapon & ~0x80000000))
+		{
+			input.activeweapon &= ~0x80000000;
+			// check which weapon to activate
+			if (input.activeweapon >= 0 && input.activeweapon < WEAPON_NUMWEAPONS && 
+				(lweapons[input.activeweapon].flags & WEAPON_ISACTIVE))
+			{
+				inextweapon = input.activeweapon;
+				equip_time = SERVER_TICK_SPEED * lweapons[iactiveweapon].unequiptime;
+				// unequip current
+				flags |= PLAYER_FLAGS_ISEQUIPPING;
+
+				create_sound(pos, sound_player_switchweapon);
+			}
+		}
+
+		// don't do any weapon activations if modifier is currently overriding
+		int modifierflags = handlemodifiers();
+		if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEWEAPON))
+			handleweapon();
+
+		handlehook();
+		
+		// handle movement
+		if(grounded)
+		{
+			if (airjumped)
+				create_sound(pos, SOUND_PLAYER_LAND);
+			airjumped = 0;
+		}
+		
+		float elast = 0.0f;
+
+		if (!numhooked)
+		{
+			// I'm hooked by someone, so don't do any movement plz (temp)
+			if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEVELOCITY))
+			{
+				if(grounded)
+				{
+					// ground movement
+					if(input.left)
+					{
+						if(vel.x > -ground_control_speed)
+						{
+							vel.x -= ground_control_accel; 
+							if(vel.x < -ground_control_speed)
+								vel.x = -ground_control_speed;
+						}
+					}
+					else if(input.right)
+					{
+						if(vel.x < ground_control_speed)
+						{
+							vel.x += ground_control_accel;
+							if(vel.x > ground_control_speed)
+								vel.x = ground_control_speed;
+						}
+					}
+					else
+						vel.x *= ground_friction; // ground fiction
+				}
+				else
+				{
+					// air movement
+					if(input.left)
+					{
+						if(vel.x > -air_control_speed)
+							vel.x -= air_control_accel;
+					}
+					else if(input.right)
+					{
+						if(vel.x < air_control_speed)
+							vel.x += air_control_accel;
+					}
+					else
+						vel.x *= air_friction; // air fiction
+				}
+				
+				if(input.jump)
+				{
+					if(jumped == 0)
+					{
+						if(grounded)
+						{
+							create_sound(pos, sound_player_jump);
+							vel.y = -ground_jump_speed;
+							jumped++;
+						}
+						/*
+						else if(airjumped == 0)
+						{
+							vel.y = -12;
+							airjumped++;
+							jumped++;
+						}*/
+					}
+					else if (!grounded)
+					{
+						airjumped++;
+					}
+				}
+				else
+					jumped = 0;
+			}
+				
+			// meh, go through all players and stop their hook on me
+			/*
+			for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
+			{
+				if (ent && ent->objtype == OBJTYPE_PLAYER)
+				{
+					player *p = (player*)ent;
+					if(p != this)
+					{
+						float d = distance(pos, p->pos);
+						vec2 dir = normalize(pos - p->pos);
+						if(d < phys_size*1.5f)
+						{
+							float a = phys_size*1.5f - d;
+							vel = vel + dir*a;
+						}
+						
+						if(p->phookedplayer == this)
+						{
+							if(d > phys_size*2.5f)
+							{
+								elast = 0.0f;
+								vel = vel - dir*2.5f;
+							}
+						}
+					}
+				}
+			}*/
+			
+			// gravity
+			if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
+				vel.y += gravity;
+		}
+		
+		if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEPOSITION))
+			move_box(&pos, &vel, vec2(phys_size, phys_size), elast);
+	}
+	
+	void die()
+	{
+		create_sound(pos, sound_player_die);
+		// release our hooked player
+		if (phookedplayer)
+		{
+			phookedplayer->numhooked--;
+			phookedplayer = 0;
+			hooking = -1;
+			numhooked = 0;
+		}
+		respawn();
+
+		// meh, go through all players and stop their hook on me
+		for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
+		{
+			if (ent && ent->objtype == OBJTYPE_PLAYER)
+			{
+				player* p = (player*)ent;
+				if (p->phookedplayer == this)
+				{
+					p->phookedplayer = 0;
+					p->hooking = -1;
+					//p->numhooked--;
+				}
+			}
+		}
+	}
+	
+	virtual bool take_damage(vec2 force, int dmg, int from)
+	{
+		vel += force;
+
+		if(armor)
+		{
+			armor -= 1;
+			dmg--;
+		}
+		
+		if(dmg > armor)
+		{
+			dmg -= armor;
+			armor = 0;
+			health -= dmg;
+		}
+		else
+			armor -= dmg;
+		/*
+		int armordmg = (dmg+1)/2;
+		int healthdmg = dmg-armordmg;
+		if(armor < armordmg)
+		{
+			healthdmg += armordmg - armor;
+			armor = 0;
+		}
+		else
+			armor -= armordmg;
+			
+		health -= healthdmg;
+		*/
+		
+		// create healthmod indicator
+		create_healthmod(pos, dmg);
+		
+		damage_taken_tick = tick_count+50;
+		
+		// check for death
+		if(health <= 0)
+		{
+			// apply score
+			if(from != -1)
+			{
+				if(from == client_id)
+					score--;
+				else
+				{
+					player *p = get_player(from);
+					p->score++;
+				}
+			}
+			
+			die();
+			return false;
+		}
+
+		if (dmg > 2)
+			create_sound(pos, sound_player_hurt_long);
+		else
+			create_sound(pos, sound_player_hurt_short);
+
+		// spawn blood?
+
+		return true;
+	}
+
+	virtual void snap(int snaping_client)
+	{
+		obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
+
+		client_info info;
+		if(server_getclientinfo(client_id, &info))
+			snap_encode_string(info.name, player->name, strlen(info.name), 32);
+		
+		player->x = (int)pos.x;
+		player->y = (int)pos.y;
+		player->vx = (int)vel.x;
+		player->vy = (int)vel.y;
+		player->emote = EMOTE_NORMAL;
+
+		player->ammocount = lweapons[iactiveweapon].numammo;
+		player->health = 0;
+		player->armor = 0;
+		player->local = 0;
+		player->clientid = client_id;
+		player->weapon = iactiveweapon;
+		player->modifier = 0;
+		for (int i = 0; i < MODIFIER_NUMMODIFIERS; i++)
+		{
+			// add active modifiers
+			if (modifiers[i].flags & MODIFIER_ISACTIVE)
+				player->modifier |= 1 << i;
+		}
+		// get current attack ticks
+		getattackticks(player->attackticks, player->attacklen, player->visualtimeattack);
+		
+
+		if(client_id == snaping_client)
+		{
+			player->local = 1;
+			player->health = health;
+			player->armor = armor;
+		}
+		
+		if(length(vel) > 15.0f)
+			player->emote = EMOTE_HAPPY;
+		
+		if(damage_taken_tick > tick_count)
+			player->emote = EMOTE_PAIN;
+		
+		if(player->emote == EMOTE_NORMAL)
+		{
+			if((tick_count%(50*5)) < 10)
+				player->emote = EMOTE_BLINK;
+		}
+		
+		player->hook_active = hooking>0?1:0;
+		player->hook_x = (int)hook_pos.x;
+		player->hook_y = (int)hook_pos.y;
+			
+		player->angle = input.angle;
+		player->score = score;
+	}
+};
+
+// POWERUP ///////////////////////
+
+powerup::powerup(int _type, int _subtype, int _numitems, int _flags) : 
+	entity(OBJTYPE_POWERUP)
+{
+	static int current_id = 0;
+	playerhooked = 0;
+	id = current_id++;
+	vel = vec2(0.0f,0.0f);
+	type = _type;
+	subtype = _subtype;
+	numitems = _numitems;
+	flags = _flags;
+	// set radius (so it can collide and be hooked and stuff)
+	proximity_radius = phys_size;
+	spawntick = -1;
+	world.insert_entity(this);
+}
+
+void powerup::spawnrandom(int _lifespan)
+{
+	return;
+	/*
+	vec2 pos;
+	int start, num;
+	map_get_type(1, &start, &num);
+	
+	if(!num)
+		return;
+	
+	mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
+	pos = vec2(sp->x, sp->y);
+
+	// Check if there already is a powerup at that location
+	{
+		int type = OBJTYPE_POWERUP;
+		entity *ents[64];
+		int num = world.find_entities(pos, 5.0f, ents, 64,&type,1);
+		for (int i = 0; i < num; i++)
+		{
+			if (ents[i]->objtype == OBJTYPE_POWERUP)
+			{
+				// location busy
+				return;
+			}
+		}
+	}
+
+	powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan);
+	ppower->pos = pos;
+	if (ppower->type == POWERUP_TYPE_WEAPON)
+	{
+		ppower->subtype = rand() % WEAPON_NUMWEAPONS;
+		ppower->numitems = 10;
+	}
+	else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR)
+	{
+		ppower->numitems = rand() % 5;
+	}
+	ppower->flags |= POWERUP_FLAG_HOOKABLE;*/
+}
+
+void powerup::tick()
+{
+	// wait for respawn
+	if(spawntick > 0)
+	{
+		if(server_tick() > spawntick)
+			spawntick = -1;
+		else
+			return;
+	}
+	
+	vec2 oldpos = pos;
+	//vel.y += 0.25f;
+	pos += vel;
+	move_box(&pos, &vel, vec2(phys_size, phys_size), 0.0f);
+
+	// Check if a player intersected us
+	vec2 meh;
+	player* pplayer = intersect_player(pos, pos + vec2(0,16), meh, 0);
+	if (pplayer)
+	{
+		// player picked us up, is someone was hooking us, let them go
+		if (playerhooked)
+			playerhooked->hooking = -1;
+		int respawntime = -1;
+		switch (type)
+		{
+		case POWERUP_TYPE_HEALTH:
+			{
+				if(pplayer->health < PLAYER_MAXHEALTH)
+				{
+					pplayer->health = min(PLAYER_MAXHEALTH, pplayer->health + numitems);
+					respawntime = 20;
+				}
+				break;
+			}
+		case POWERUP_TYPE_ARMOR:
+			{
+				if(pplayer->armor < PLAYER_MAXARMOR)
+				{
+					pplayer->armor = min(PLAYER_MAXARMOR, pplayer->armor + numitems);
+					respawntime = 20;
+				}
+				break;
+			}
+		case POWERUP_TYPE_WEAPON:
+			{
+				if (pplayer->lweapons[subtype].flags & player::WEAPON_ISACTIVE)
+				{
+					// add ammo
+					/*
+					if (pplayer->lweapons[subtype].flags & player::WEAPON_NEEDRELOAD)
+					{
+						int numtoadd = min(numitems, pplayer->lweapons[subtype].magsize);
+						pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numtoadd);
+						pplayer->lweapons[subtype].nummagazines += (numitems - numtoadd) % pplayer->lweapons[subtype].magsize;
+					}
+					else*/
+					if(pplayer->lweapons[subtype].numammo < 10)
+					{
+						respawntime = 20;
+						pplayer->lweapons[subtype].numammo = min(10, pplayer->lweapons[subtype].numammo + numitems);
+					}
+				}
+				else
+				{
+					pplayer->lweapons[subtype].settype();
+					pplayer->lweapons[subtype].flags |= player::WEAPON_ISACTIVE;
+					respawntime = 20;
+				}
+				break;
+			}
+		case POWERUP_TYPE_NINJA:
+			{
+				respawntime = 60;
+				// reset and activate
+				pplayer->modifiers[MODIFIER_TYPE_NINJA].settype();
+				pplayer->modifiers[MODIFIER_TYPE_NINJA].flags |= player::MODIFIER_ISACTIVE;
+				break;
+			}
+		//POWERUP_TYPE_TIMEFIELD		= 4,
+		default:
+			break;
+		};
+		
+		if(respawntime >= 0)
+			spawntick = server_tick() + server_tickspeed() * respawntime;
+		//world.destroy_entity(this);
+	}
+}
+
+void powerup::snap(int snapping_client)
+{
+	if(spawntick != -1)
+		return;
+
+	obj_powerup *powerup = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup));
+	powerup->x = (int)pos.x;
+	powerup->y = (int)pos.y;
+	powerup->vx = (int)vel.x;
+	powerup->vy = (int)vel.y;
+	powerup->type = type;
+	powerup->subtype = subtype;
+}
+
+// POWERUP END ///////////////////////
+
+static player players[MAX_CLIENTS];
+
+player *get_player(int index)
+{
+	return &players[index];
+}
+
+void create_healthmod(vec2 p, int amount)
+{
+	ev_healthmod *ev = (ev_healthmod *)events.create(EVENT_HEALTHMOD, sizeof(ev_healthmod));
+	ev->x = (int)p.x;
+	ev->y = (int)p.y;
+	ev->amount = amount;
+}
+
+void create_explosion(vec2 p, int owner, bool bnodamage)
+{
+	// create the event
+	ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion));
+	ev->x = (int)p.x;
+	ev->y = (int)p.y;
+	
+	if (!bnodamage)
+	{
+		// deal damage
+		entity *ents[64];
+		const float radius = 128.0f;
+		int num = world.find_entities(p, radius, ents, 64);
+		for(int i = 0; i < num; i++)
+		{
+			vec2 diff = ents[i]->pos - p;
+			vec2 forcedir(0,1);
+			if (length(diff))
+				forcedir = normalize(diff);
+			float l = length(diff);
+			float dmg = 5 * (1 - (l/radius));
+			if((int)dmg)
+			{
+				ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner);/* && 
+						ents[i]->objtype == OBJTYPE_PLAYER &&
+						owner >= 0)
+				{
+					player *p = (player*)ents[i];
+					if(p->client_id == owner)
+						p->score--;
+					else
+						((player*)ents[owner])->score++;
+
+				}*/
+			}
+		}
+	}
+}
+
+void create_smoke(vec2 p)
+{
+	// create the event
+	ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion));
+	ev->x = (int)p.x;
+	ev->y = (int)p.y;
+}
+
+void create_sound(vec2 pos, int sound, int loopingflags)
+{
+	if (sound < 0)
+		return;
+
+	// create a sound
+	ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound));
+	ev->x = (int)pos.x;
+	ev->y = (int)pos.y;
+	ev->sound = sound | loopingflags;
+}
+
+player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
+{
+	// Find other players
+	entity *ents[64];
+	vec2 dir = pos1 - pos0;
+	float radius = length(dir * 0.5f);
+	vec2 center = pos0 + dir * 0.5f;
+	int num = world.find_entities(center, radius, ents, 64);
+	for (int i = 0; i < num; i++)
+	{
+		// Check if entity is a player
+		if (ents[i] != notthis && ents[i]->objtype == OBJTYPE_PLAYER)
+		{
+			// temp, set hook pos to our position
+			new_pos = ents[i]->pos;
+			return (player*)ents[i];
+		}
+	}
+
+	return 0;
+}
+
+// Server hooks
+static int addtick = SERVER_TICK_SPEED * 5;
+void mods_tick()
+{
+	// clear all events
+	events.clear();
+	world.tick();
+
+	if (addtick <= 0)
+	{
+		powerup::spawnrandom(SERVER_TICK_SPEED * 5);
+		addtick = SERVER_TICK_SPEED * 5;
+	}
+	addtick--;
+}
+
+void mods_snap(int client_id)
+{
+	world.snap(client_id);
+	events.snap(client_id);
+}
+
+void mods_client_input(int client_id, void *input)
+{
+	players[client_id].previnput = players[client_id].input;
+	players[client_id].input = *(player_input*)input;
+}
+
+void mods_client_enter(int client_id)
+{
+	players[client_id].reset();
+	players[client_id].client_id = client_id;
+	players[client_id].respawn();
+	world.insert_entity(&players[client_id]);
+	
+}
+
+void mods_client_drop(int client_id)
+{
+	players[client_id].client_id = -1;
+	world.remove_entity(&players[client_id]);
+}
+
+void mods_init()
+{
+	col_init(32);
+
+	int start, num;
+	map_get_type(MAPRES_ITEM, &start, &num);
+	
+	for(int i = 0; i < num; i++)
+	{
+		mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0);
+		
+		int type = -1;
+		int subtype = -1;
+		int numitems = 1;
+		
+		switch(it->type)
+		{
+		case ITEM_WEAPON_GUN:
+			type = POWERUP_TYPE_WEAPON;
+			subtype = WEAPON_TYPE_GUN;
+			break;
+		case ITEM_WEAPON_SHOTGUN:
+			type = POWERUP_TYPE_WEAPON;
+			subtype = WEAPON_TYPE_SHOTGUN;
+			numitems = 5;
+			break;
+		case ITEM_WEAPON_ROCKET:
+			type = POWERUP_TYPE_WEAPON;
+			subtype = WEAPON_TYPE_ROCKET;
+			numitems = 5;
+			break;
+		/*case ITEM_WEAPON_SNIPER:
+			type = POWERUP_TYPE_WEAPON;
+			subtype = WEAPON_TYPE_ROCKET;
+			break;*/
+		case ITEM_WEAPON_HAMMER:
+			type = POWERUP_TYPE_WEAPON;
+			subtype = WEAPON_TYPE_MELEE;
+			break;
+		
+		case ITEM_HEALTH_1:
+			type = POWERUP_TYPE_HEALTH;
+			numitems = 1;
+			break;
+		case ITEM_HEALTH_5:
+			type = POWERUP_TYPE_HEALTH;
+			numitems = 5;
+			break;
+		case ITEM_HEALTH_10:
+			type = POWERUP_TYPE_HEALTH;
+			numitems = 10;
+			break;
+		
+		case ITEM_ARMOR_1:
+			type = POWERUP_TYPE_ARMOR;
+			numitems = 1;
+			break;
+		case ITEM_ARMOR_5:
+			type = POWERUP_TYPE_ARMOR;
+			numitems = 5;
+			break;
+		case ITEM_ARMOR_10:
+			type = POWERUP_TYPE_ARMOR;
+			numitems = 10;
+			break;
+		};
+		
+		powerup* ppower = new powerup(type, subtype, numitems);
+		ppower->pos.x = it->x;
+		ppower->pos.y = it->y;
+	}
+		
+	
+	/*
+	powerup* ppower = new powerup(rand() % POWERUP_TYPE_NUMPOWERUPS,_lifespan);
+	ppower->pos = pos;
+	if (ppower->type == POWERUP_TYPE_WEAPON)
+	{
+		ppower->subtype = rand() % WEAPON_NUMWEAPONS;
+		ppower->numitems = 10;
+	}
+	else if (ppower->type == POWERUP_TYPE_HEALTH || ppower->type == POWERUP_TYPE_ARMOR)
+	{
+		ppower->numitems = rand() % 5;
+	}
+	ppower->flags |= POWERUP_FLAG_HOOKABLE;
+	*/
+	
+	//powerup::spawnrandom(SERVER_TICK_SPEED * 5);
+}
+
+void mods_shutdown() {}
+void mods_presnap() {}
+void mods_postsnap() {}
diff --git a/src/game/mapres.h b/src/game/mapres.h
new file mode 100644
index 00000000..8d09e99c
--- /dev/null
+++ b/src/game/mapres.h
@@ -0,0 +1,7 @@
+enum
+{
+	MAPRES_REGISTERED=0x8000,
+	MAPRES_IMAGE=0x8001,
+	MAPRES_TILEMAP=0x8002,
+	MAPRES_COLLISIONMAP=0x8003,
+};
diff --git a/src/game/mapres_col.cpp b/src/game/mapres_col.cpp
new file mode 100644
index 00000000..0cf71986
--- /dev/null
+++ b/src/game/mapres_col.cpp
@@ -0,0 +1,44 @@
+#include <baselib/system.h>
+#include "../interface.h"
+#include "mapres_col.h"
+#include "mapres.h"
+
+/*
+	Simple collision rutines!
+*/
+struct collision
+{
+	int w, h;
+	unsigned char *data;
+};
+
+static collision col;
+static int global_dividor;
+
+int col_init(int dividor)
+{
+	mapres_collision *c = (mapres_collision*)map_find_item(MAPRES_COLLISIONMAP,0);
+	if(!c)
+	{
+		dbg_msg("mapres_col", "failed!");
+		return 0;
+	}
+	col.w = c->width;
+	col.h = c->height;
+	global_dividor = dividor;
+	col.data = (unsigned char *)map_get_data(c->data_index);
+	return col.data ? 1 : 0;
+}
+
+int col_check_point(int x, int y)
+{
+	int nx = x/global_dividor;
+	int ny = y/global_dividor;
+	if(nx < 0 || nx >= col.w || ny >= col.h)
+		return 1;
+	
+	if(y < 0)
+		return 0; // up == sky == free
+	
+	return col.data[ny*col.w+nx];
+}
diff --git a/src/game/mapres_col.h b/src/game/mapres_col.h
new file mode 100644
index 00000000..2afad439
--- /dev/null
+++ b/src/game/mapres_col.h
@@ -0,0 +1,10 @@
+
+struct mapres_collision
+{
+	int width;
+	int height;
+	int data_index;
+};
+
+int col_init(int dividor);
+int col_check_point(int x, int y);
diff --git a/src/game/mapres_image.cpp b/src/game/mapres_image.cpp
new file mode 100644
index 00000000..baf7f09b
--- /dev/null
+++ b/src/game/mapres_image.cpp
@@ -0,0 +1,41 @@
+#include <baselib/system.h>
+#include "../interface.h"
+#include "mapres_image.h"
+#include "mapres.h"
+
+static int map_textures[64] = {0};
+static int count = 0;
+
+int img_init()
+{
+	int start, count;
+	map_get_type(MAPRES_IMAGE, &start, &count);
+	dbg_msg("mapres_image", "start=%d count=%d", start, count);
+	for(int i = 0; i < 64; i++)
+	{
+		if(map_textures[i])
+		{
+			gfx_unload_texture(map_textures[i]);
+			map_textures[i] = 0;
+		}
+	}
+
+	for(int i = 0; i < count; i++)
+	{
+		mapres_image *img = (mapres_image *)map_get_item(start+i, 0, 0);
+		void *data = map_get_data(img->image_data);
+		map_textures[i] = gfx_load_texture_raw(img->width, img->height, data);
+	}
+	
+	return count;
+}
+
+int img_num()
+{
+	return count;
+}
+
+int img_get(int index)
+{
+	return map_textures[index];
+}
diff --git a/src/game/mapres_image.h b/src/game/mapres_image.h
new file mode 100644
index 00000000..eab1559a
--- /dev/null
+++ b/src/game/mapres_image.h
@@ -0,0 +1,18 @@
+
+// loads images from the map to textures
+int img_init();
+
+// returns the number of images in the map
+int img_num();
+
+// fetches the texture id for the image
+int img_get(int index);
+
+
+class mapres_image
+{
+public:
+	int width;
+	int height;
+	int image_data;
+};
diff --git a/src/game/mapres_tilemap.cpp b/src/game/mapres_tilemap.cpp
new file mode 100644
index 00000000..0868d2e4
--- /dev/null
+++ b/src/game/mapres_tilemap.cpp
@@ -0,0 +1,54 @@
+#include "../interface.h"
+#include "mapres_tilemap.h"
+#include "mapres_image.h"
+#include "mapres.h"
+
+int tilemap_init()
+{
+	return 0;
+}
+
+void tilemap_render(float scale, int fg)
+{
+	if(!map_is_loaded())
+		return;
+	
+	// fetch indecies
+	int start, num;
+	map_get_type(MAPRES_TILEMAP, &start, &num);
+
+	// render tilemaps
+	int passed_main = 0;
+	for(int t = 0; t < num; t++)
+	{
+		mapres_tilemap *tmap = (mapres_tilemap *)map_get_item(start+t,0,0);
+		unsigned char *data = (unsigned char *)map_get_data(tmap->data);
+		
+		if(tmap->main)
+			passed_main = 1;
+
+		if((fg && passed_main) || (!fg && !passed_main))
+		{
+			gfx_texture_set(img_get(tmap->image));
+			gfx_quads_begin();
+			
+			int c = 0;
+			float frac = (1.0f/1024.0f); //2.0f;
+			for(int y = 0; y < tmap->height; y++)
+				for(int x = 0; x < tmap->width; x++, c++)
+				{
+					unsigned char d = data[c*2];
+					if(d)
+					{
+						gfx_quads_setsubset(
+							(d%16)/16.0f+frac,
+							(d/16)/16.0f+frac,
+							(d%16)/16.0f+1.0f/16.0f-frac,
+							(d/16)/16.0f+1.0f/16.0f-frac);
+						gfx_quads_drawTL(x*scale, y*scale, scale, scale);
+					}
+				}
+			gfx_quads_end();
+		}
+	}
+}
diff --git a/src/game/mapres_tilemap.h b/src/game/mapres_tilemap.h
new file mode 100644
index 00000000..6e9d81be
--- /dev/null
+++ b/src/game/mapres_tilemap.h
@@ -0,0 +1,19 @@
+
+// dependencies: image
+
+//
+int tilemap_init();
+
+// renders the tilemaps
+void tilemap_render(float scale, int fg);
+
+struct mapres_tilemap
+{
+	int image;
+	int width;
+	int height;
+	int x, y;
+	int scale;
+	int data;
+	int main;
+};