about summary refs log tree commit diff
path: root/src/game/server/game_server.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/game/server/game_server.cpp')
-rw-r--r--src/game/server/game_server.cpp1680
1 files changed, 359 insertions, 1321 deletions
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index df17169c..88cbb471 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -1,9 +1,13 @@
 #include <stdlib.h>
 #include <string.h>
 #include "../game.h"
+#include "data.h"
 
 using namespace baselib;
 
+// ---------
+const bool debug_bots = false;
+
 // --------- PHYSICS TWEAK! --------
 const float ground_control_speed = 7.0f;
 const float ground_control_accel = 2.0f;
@@ -25,6 +29,30 @@ 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);
 
+template<typename T>
+T saturated_add(T min, T max, T current, T modifier)
+{
+	if(modifier < 0)
+	{
+		if(current < min)
+			return current;
+		current += modifier;
+		if(current < min)
+			current = min;
+		return current;
+	}
+	else
+	{
+		if(current > max)
+			return current;
+		current += modifier;
+		if(current > max)
+			current = max;
+		return current;
+	}
+}
+
+
 // TODO: rewrite this smarter!
 void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
 {
@@ -91,27 +119,6 @@ void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
 	*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
 {
@@ -128,7 +135,7 @@ class event_handler
 public:
 	event_handler()
 	{
-		num_events = 0;
+		clear();
 	}
 	
 	void *create(int type, int size)
@@ -159,27 +166,6 @@ public:
 };
 
 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
@@ -190,12 +176,15 @@ private:
 	entity *prev_entity;
 	entity *next_entity;
 	int index;
+	static int current_id;
+protected:
+	int id;
 	
 public:
-	vec2 pos;
 	float proximity_radius;
 	unsigned flags;
 	int objtype;
+	vec2 pos;
 
 	enum
 	{
@@ -208,6 +197,9 @@ public:
 		pos = vec2(0,0);
 		flags = 0;
 		proximity_radius = 0;
+		
+		current_id++;
+		id = current_id;
 	}
 	
 	virtual ~entity()
@@ -216,35 +208,14 @@ public:
 
 	virtual void destroy() { delete this; }
 	virtual void tick() {}
+	virtual void tick_defered() {}
+		
 	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);
-};
+int entity::current_id = 1;
 
 // game world. handles all entities
 class game_world
@@ -283,6 +254,7 @@ public:
 				if (ent->objtype != types[i])
 					continue;
 
+				// TODO: this seams like it could be done several times unnessesary
 				if(distance(ent->pos, pos) < radius+ent->proximity_radius)
 				{
 					ents[num] = ent;
@@ -309,9 +281,6 @@ public:
 	void destroy_entity(entity *ent)
 	{
 		ent->flags |= entity::FLAG_DESTROY;
-		// call destroy
-		//remove_entity(ent);
-		//ent->destroy();
 	}
 	
 	void remove_entity(entity *ent)
@@ -337,6 +306,9 @@ public:
 		// update all objects
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 			ent->tick();
+			
+		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+			ent->tick_defered();
 		
 		// destroy objects marked for destruction
 		entity *ent = first_entity;
@@ -355,6 +327,21 @@ public:
 
 static game_world world;
 
+// TODO: move to seperate file
+class powerup : public entity
+{
+public:
+	static const int phys_size = 14;
+	
+	int type;
+	int subtype; // weapon type for instance?
+	int spawntick;
+	powerup(int _type, int _subtype = 0);
+	
+	virtual void tick();
+	virtual void snap(int snapping_client);
+};
+
 // projectile entity
 class projectile : public entity
 {
@@ -363,10 +350,10 @@ public:
 	{
 		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
 	};
+	
 	vec2 vel;
-	entity* powner;
+	entity *powner; // this is nasty, could be removed when client quits
 	int lifespan;
-	int id;
 	int owner;
 	int type;
 	int flags;
@@ -374,11 +361,9 @@ public:
 	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)
+	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;
@@ -398,20 +383,19 @@ public:
 		vel.y += 0.25f;
 		pos += vel;
 		lifespan--;
+		
 		// check player intersection as well
-		entity* targetplayer = (entity*)intersect_player(oldpos, pos, oldpos, powner);
+		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);
 		}
 	}
@@ -421,639 +405,85 @@ public:
 		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->vx = (int)vel.x; // TODO: should be an angle
 		proj->vy = (int)vel.y;
 		proj->type = type;
 	}
 };
 
 // player entity
+// TODO: move to separate file
 class player : public entity
 {
 public:
 	static const int phys_size = 28;
-	enum
+	
+	enum // what are these?
 	{
-		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,
-						(int)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
+	
+	// weapon info
+	struct weaponstat
 	{
-		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;
+		bool got;
+		int ammo;
+	} weapons[NUM_WEAPONS];
+	int active_weapon;
+	int reload_timer;
+	int attack_tick;
+	
+	// we need a defered position so we can handle the physics correctly
+	vec2 defered_pos;
+	vec2 vel;
+	vec2 direction;
 
+	//
 	int client_id;
-	int flags;
-
 	char name[32];
+
+	// input	
 	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 damage_taken_tick;
 
 	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;
+	// hooking stuff
+	enum
+	{
+		HOOK_RETRACTED=-1,
+		HOOK_IDLE=0,
+		HOOK_FLYING,
+		HOOK_GRABBED
+	};
+	
+	int hook_state; 
+	player *hooked_player;
 	vec2 hook_pos;
 	vec2 hook_dir;
 
-	player() :
-		entity(OBJTYPE_PLAYER)
+	//
+	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;
+		//equip_time = 0;
+		
+		release_hooked();
+		release_hooks();
+		
 		proximity_radius = phys_size;
 		name[0] = 'n';
 		name[1] = 'o';
@@ -1065,32 +495,21 @@ public:
 		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; }
+	virtual void destroy() {  }
 		
 	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);
 		
+		// get spawn point
 		int start, num;
 		map_get_type(1, &start, &num);
 		
@@ -1101,30 +520,17 @@ public:
 		}
 		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
+			
+		// init weapons
+		mem_zero(&weapons, sizeof(weapons));
+		weapons[WEAPON_HAMMER].got = true;
+		weapons[WEAPON_HAMMER].ammo = -1;
+		weapons[WEAPON_GUN].got = true;
+		weapons[WEAPON_GUN].ammo = 10;
+		active_weapon = WEAPON_GUN;
+		reload_timer = 0;
 		
-		/*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);
+		create_sound(pos, SOUND_PLAYER_SPAWN);
 	}
 	
 	bool is_grounded()
@@ -1136,470 +542,261 @@ public:
 		return false;
 	}
 
-	// Disable weapon activation if this returns true
-	int handlemodifiers()
+	// releases the hooked player
+	void release_hooked()
 	{
-		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;
+		hook_state = HOOK_IDLE;
+		hooked_player = 0x0;
 	}
 
-	void handleweapon()
+	// release all hooks to this player	
+	void release_hooks()
 	{
-		// handle weapon
-		if(input.fire && (!previnput.fire || lweapons[iactiveweapon].flags & WEAPON_AUTOFIRE) && 
-			!(flags & PLAYER_FLAGS_ISEQUIPPING) && !reload_timeout)
+		// TODO: loop thru players only
+		for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
 		{
-			if(fire_timeout == 0)
+			if(ent && ent->objtype == OBJTYPE_PLAYER)
 			{
-				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;
-				}
+				player *p = (player*)ent;
+				if(p->hooked_player == this)
+					p->release_hooked();
 			}
 		}
-
-		// update active weapon
-		lweapons[iactiveweapon].update(this, fire_timeout);
 	}
-
-	void handlehook()
+	
+	void handle_weapons()
 	{
-		// handle hook
-		if(input.hook)
+		// check reload timer
+		if(reload_timer)
 		{
-			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];
-						}
-					}
-				}
+			reload_timer--;
+			return;
+		}
 
-				//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);
+		// switch weapon if wanted		
+		if(input.activeweapon >= 0 && input.activeweapon < NUM_WEAPONS && weapons[input.activeweapon].got)
+			active_weapon = input.activeweapon;
 
-					// 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)
+		if(input.fire)
+		{
+			if(reload_timer == 0)
 			{
-				// hmm, force the targetplayer towards us if possible, otherwise us towards them if they are already hooked
-				if (phookedplayer)
+				// fire!
+				if(weapons[active_weapon].ammo)
 				{
-					if (phookedplayer->hooking > 1)
+					switch(active_weapon)
 					{
-						// 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
+						case WEAPON_HAMMER:
+							break;
+
+						case WEAPON_GUN:
+							new projectile(WEAPON_PROJECTILETYPE_GUN,
+								client_id,
+								pos+vec2(0,0),
+								direction*30.0f,
+								100,
+								this,
+								1, 0, 0, -1);
+							break;
+						case WEAPON_ROCKET:
+							new projectile(WEAPON_PROJECTILETYPE_ROCKET,
+								client_id,
+								pos+vec2(0,0),
+								direction*15.0f,
+								100,
+								this,
+								1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, -1);						
+							break;
+						case WEAPON_SHOTGUN:
+							for(int i = 0; i < 3; i++)
+							{
+								new projectile(WEAPON_PROJECTILETYPE_SHOTGUN,
+									client_id,
+									pos+vec2(0,0),
+									direction*(20.0f+(i+1)*2.0f),
+									100,
+									this,
+									1, 0, 0, -1);
+							}
+							break;
 					}
-					hook_pos = phookedplayer->pos;
-					// if hooked player dies, release the hook
+					
+					weapons[active_weapon].ammo--;
 				}
 				else
 				{
-					hooking = -1;
-					phookedplayer = 0;
+					// click!!! click
 				}
-			}
-			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;
+				attack_tick = server_tick();
+				reload_timer = 10; // make this variable depending on weapon
 			}
 		}
 	}
-
-	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++;
+		// TODO: rework the input to be more robust
+		// TODO: remove this tick count, it feels weird
 		
 		// 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)
+		float max_speed = grounded ? ground_control_speed : air_control_speed;
+		float accel = grounded ? ground_control_accel : air_control_accel;
+		float friction = grounded ? ground_friction : air_friction;
+		
+		// handle movement
+		if(input.left)
+			vel.x = saturated_add(-max_speed, max_speed, vel.x, -accel);
+		if(input.right)
+			vel.x = saturated_add(-max_speed, max_speed, vel.x, accel);
+			
+		if(!input.left && !input.right)
+			vel.x *= friction;
+		
+		// handle jumping
+		if(input.jump)
 		{
-			equip_time--;
-			if (equip_time <= 0)
+			if(!jumped && grounded)
 			{
-				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;
-				}
+				create_sound(pos, SOUND_PLAYER_JUMP);
+				vel.y = -ground_jump_speed;
+				jumped++;
 			}
 		}
-		else if (input.activeweapon && (unsigned int)iactiveweapon != (input.activeweapon & ~0x80000000))
+		else
+			jumped = 0;
+			
+		// do hook
+		if(input.hook)
 		{
-			input.activeweapon &= ~0x80000000;
-			// check which weapon to activate
-			if (input.activeweapon >= 0 && input.activeweapon < WEAPON_NUMWEAPONS && 
-				(lweapons[input.activeweapon].flags & WEAPON_ISACTIVE))
+			if(hook_state == HOOK_IDLE)
 			{
-				inextweapon = input.activeweapon;
-				equip_time = SERVER_TICK_SPEED * lweapons[iactiveweapon].unequiptime;
-				// unequip current
-				flags |= PLAYER_FLAGS_ISEQUIPPING;
-
-				create_sound(pos, sound_player_switchweapon);
+				hook_state = HOOK_FLYING;
+				hook_pos = pos;
+				hook_dir = direction;
 			}
-		}
-
-		// 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))
+			else if(hook_state == HOOK_FLYING)
 			{
-				if(grounded)
+				vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
+
+				// Check against other players first
+				for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
 				{
-					// 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(ent && ent->objtype == OBJTYPE_PLAYER)
 					{
-						if(vel.x < ground_control_speed)
+						player *p = (player*)ent;
+						if(p != this && distance(p->pos, new_pos) < p->phys_size)
 						{
-							vel.x += ground_control_accel;
-							if(vel.x > ground_control_speed)
-								vel.x = ground_control_speed;
+							hook_state = HOOK_GRABBED;
+							hooked_player = p;
+							break;
 						}
 					}
-					else
-						vel.x *= ground_friction; // ground fiction
 				}
-				else
+				
+				if(hook_state == HOOK_FLYING)
 				{
-					// air movement
-					if(input.left)
+					// check against ground
+					if(col_intersect_line(hook_pos, new_pos, &new_pos))
 					{
-						if(vel.x > -air_control_speed)
-							vel.x -= air_control_accel;
+						hook_state = HOOK_GRABBED;
+						hook_pos = new_pos;	
 					}
-					else if(input.right)
+					else if(distance(pos, new_pos) > hook_length)
 					{
-						if(vel.x < air_control_speed)
-							vel.x += air_control_accel;
+						hook_state = HOOK_RETRACTED;
 					}
 					else
-						vel.x *= air_friction; // air fiction
+						hook_pos = new_pos;
 				}
 				
-				if(input.jump)
+				if(hook_state == HOOK_GRABBED)
+					create_sound(pos, SOUND_HOOK_ATTACH);
+			}
+		}
+		else
+		{
+			release_hooked();
+			hook_pos = pos;
+		}
+			
+		if(hook_state == HOOK_GRABBED)
+		{
+			if(hooked_player)
+				hook_pos = hooked_player->pos;
+
+			float d = distance(pos, hook_pos);
+			vec2 dir = normalize(pos - hook_pos);		
+			if(d > 10.0f) // TODO: fix tweakable variable
+			{
+				float accel = hook_drag_accel * (d/hook_length);
+				vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x*0.75f);
+				vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
+			}
+		}
+			
+		// fix influence of other players, collision + hook
+		// TODO: loop thru players only
+		for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
+		{
+			if(ent && ent->objtype == OBJTYPE_PLAYER)
+			{
+				player *p = (player*)ent;
+				if(p == this)
+					continue; // make sure that we don't nudge our self
+				
+				// handle player <-> player collision
+				float d = distance(pos, p->pos);
+				vec2 dir = normalize(pos - p->pos);
+				if(d < phys_size*1.25f)
 				{
-					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++;
-					}
+					float a = phys_size*1.25f - d;
+					vel = vel + dir*a;
 				}
-				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)
+				// handle hook influence
+				if(p->hooked_player == this)
 				{
-					player *p = (player*)ent;
-					if(p != this)
+					if(d > phys_size*1.50f) // TODO: fix tweakable variable
 					{
-						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;
-							}
-						}
+						float accel = hook_drag_accel * (d/hook_length);
+						vel.x = saturated_add(-hook_drag_speed, hook_drag_speed, vel.x, -accel*dir.x);
+						vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
 					}
 				}
-			}*/
-			
-			// gravity
-			if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEGRAVITY))
-				vel.y += gravity;
+			}
 		}
 		
-		if (!(modifierflags & MODIFIER_RETURNFLAGS_OVERRIDEPOSITION))
-			move_box(&pos, &vel, vec2(phys_size, phys_size), elast);
+		// handle weapons
+		handle_weapons();
+		
+		// add gravity
+		vel.y += gravity;
+		
+		// do the move
+		defered_pos = pos;
+		move_box(&defered_pos, &vel, vec2(phys_size, phys_size), 0);
+		return;
+	}
+	
+	virtual void tick_defered()
+	{
+		// apply the new position
+		pos = defered_pos;
 	}
 	
 	void die()
 	{
-		create_sound(pos, sound_player_die);
-		// release our hooked player
-		if (phookedplayer)
-		{
-			phookedplayer->numhooked--;
-			phookedplayer = 0;
-			hooking = -1;
-			numhooked = 0;
-		}
+		create_sound(pos, SOUND_PLAYER_DIE);
+		
+		release_hooked();
+		release_hooks();
+		
+		// TODO: insert timer here
 		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)
@@ -1620,24 +817,11 @@ public:
 		}
 		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;
+		damage_taken_tick = server_tick()+50;
 		
 		// check for death
 		if(health <= 0)
@@ -1659,12 +843,11 @@ public:
 		}
 
 		if (dmg > 2)
-			create_sound(pos, sound_player_hurt_long);
+			create_sound(pos, SOUND_PLAYER_PAIN_LONG);
 		else
-			create_sound(pos, sound_player_hurt_short);
+			create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
 
 		// spawn blood?
-
 		return true;
 	}
 
@@ -1672,33 +855,20 @@ public:
 	{
 		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->ammocount = weapons[active_weapon].ammo;
 		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);
+		player->weapon = active_weapon;
+		player->attacktick = attack_tick;
 		
-
 		if(client_id == snaping_client)
 		{
 			player->local = 1;
@@ -1709,16 +879,16 @@ public:
 		if(length(vel) > 15.0f)
 			player->emote = EMOTE_HAPPY;
 		
-		if(damage_taken_tick > tick_count)
+		if(damage_taken_tick > server_tick())
 			player->emote = EMOTE_PAIN;
 		
 		if(player->emote == EMOTE_NORMAL)
 		{
-			if((tick_count%(50*5)) < 10)
+			if((server_tick()%(50*5)) < 10)
 				player->emote = EMOTE_BLINK;
 		}
 		
-		player->hook_active = hooking>0?1:0;
+		player->hook_active = hook_state>0?1:0;
 		player->hook_x = (int)hook_pos.x;
 		player->hook_y = (int)hook_pos.y;
 			
@@ -1729,64 +899,18 @@ public:
 
 // POWERUP ///////////////////////
 
-powerup::powerup(int _type, int _subtype, int _numitems, int _flags) : 
-	entity(OBJTYPE_POWERUP)
+powerup::powerup(int _type, int _subtype)
+: entity(OBJTYPE_POWERUP)
 {
-	static int current_id = 0;
-	playerhooked = 0;
-	id = current_id++;
-	vel = vec2(0.0f,0.0f);
+	//static int current_id = 0;
 	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;*/
+	// TODO: should this be done here?
+	world.insert_entity(this);
 }
 
 void powerup::tick()
@@ -1799,84 +923,47 @@ void powerup::tick()
 		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)
 			{
-				if(pplayer->health < PLAYER_MAXHEALTH)
-				{
-					pplayer->health = min(PLAYER_MAXHEALTH, pplayer->health + numitems);
-					respawntime = 20;
-				}
-				break;
+				pplayer->health = min((int)PLAYER_MAXHEALTH, pplayer->health + 1);
+				respawntime = 20;
 			}
+			break;
 		case POWERUP_TYPE_ARMOR:
+			if(pplayer->armor < PLAYER_MAXARMOR)
 			{
-				if(pplayer->armor < PLAYER_MAXARMOR)
-				{
-					pplayer->armor = min(PLAYER_MAXARMOR, pplayer->armor + numitems);
-					respawntime = 20;
-				}
-				break;
+				pplayer->armor = min((int)PLAYER_MAXARMOR, pplayer->armor + 1);
+				respawntime = 20;
 			}
+			break;
+				
 		case POWERUP_TYPE_WEAPON:
+			if(subtype >= 0 && subtype < NUM_WEAPONS)
 			{
-				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
+				if(pplayer->weapons[subtype].ammo < 10 || !pplayer->weapons[subtype].got)
 				{
-					pplayer->lweapons[subtype].settype();
-					pplayer->lweapons[subtype].flags |= player::WEAPON_ISACTIVE;
+					pplayer->weapons[subtype].got = true;
+					pplayer->weapons[subtype].ammo = min(10, pplayer->weapons[subtype].ammo + 5);
 					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,
+			break;
 		default:
 			break;
 		};
 		
 		if(respawntime >= 0)
 			spawntick = server_tick() + server_tickspeed() * respawntime;
-		//world.destroy_entity(this);
 	}
 }
 
@@ -1885,18 +972,17 @@ 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;
+	obj_powerup *up = (obj_powerup *)snap_new_item(OBJTYPE_POWERUP, id, sizeof(obj_powerup));
+	up->x = (int)pos.x;
+	up->y = (int)pos.y;
+	up->type = type; // TODO: two diffrent types? what gives?
+	up->subtype = subtype;
 }
 
 // POWERUP END ///////////////////////
 
-static player players[MAX_CLIENTS];
+static const int NUM_BOTS = 1;
+static player players[MAX_CLIENTS+NUM_BOTS];
 
 player *get_player(int index)
 {
@@ -1933,19 +1019,7 @@ void create_explosion(vec2 p, int owner, bool bnodamage)
 			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++;
-
-				}*/
-			}
+				ents[i]->take_damage(forcedir*dmg*2, (int)dmg, owner);
 		}
 	}
 }
@@ -1970,6 +1044,7 @@ void create_sound(vec2 pos, int sound, int loopingflags)
 	ev->sound = sound | loopingflags;
 }
 
+// TODO: should be more general
 player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 {
 	// Find other players
@@ -1993,19 +1068,26 @@ player* intersect_player(vec2 pos0, vec2 pos1, vec2& new_pos, entity* notthis)
 }
 
 // Server hooks
-static int addtick = SERVER_TICK_SPEED * 5;
 void mods_tick()
 {
 	// clear all events
 	events.clear();
 	world.tick();
 
-	if (addtick <= 0)
+	if(debug_bots)
 	{
-		powerup::spawnrandom(SERVER_TICK_SPEED * 5);
-		addtick = SERVER_TICK_SPEED * 5;
+		static int count = 0;
+		if(count >= 0)
+		{
+			count++;
+			if(count == 10)
+			{
+				for(int i = 0; i < NUM_BOTS; i++)
+					mods_client_enter(MAX_CLIENTS+i);
+				count = -1;
+			}
+		}
 	}
-	addtick--;
 }
 
 void mods_snap(int client_id)
@@ -2026,7 +1108,6 @@ void mods_client_enter(int client_id)
 	players[client_id].client_id = client_id;
 	players[client_id].respawn();
 	world.insert_entity(&players[client_id]);
-	
 }
 
 void mods_client_drop(int client_id)
@@ -2042,13 +1123,13 @@ void mods_init()
 	int start, num;
 	map_get_type(MAPRES_ITEM, &start, &num);
 	
+	// TODO: this is way more complicated then it should be
 	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)
 		{
@@ -2059,17 +1140,11 @@ void mods_init()
 		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;
@@ -2077,53 +1152,16 @@ void mods_init()
 		
 		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(type, subtype);
+		ppower->pos = vec2(it->x, 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() {}