about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-15 10:47:50 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-07-15 10:47:50 +0000
commit8a4cd7627e78d87b44c52bf53786c6891ded6866 (patch)
treed9b3e038554bf10dc4f83c1dcc9e656e6f3f101f /src/game
parent60b252c584adc35f02ce2e00ea4556cc2a81958d (diff)
downloadzcatch-8a4cd7627e78d87b44c52bf53786c6891ded6866.tar.gz
zcatch-8a4cd7627e78d87b44c52bf53786c6891ded6866.zip
added score and time limit. cleaned up the code aswell abit
Diffstat (limited to 'src/game')
-rw-r--r--src/game/client/game_client.cpp60
-rw-r--r--src/game/game.h20
-rw-r--r--src/game/game_variables.h5
-rw-r--r--src/game/server/game_server.cpp1423
-rw-r--r--src/game/server/game_server.h254
5 files changed, 1035 insertions, 727 deletions
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index bdd03624..67354bec 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -922,6 +922,7 @@ void modc_render()
 	}
 
 	// setup world view
+	obj_game *gameobj = 0;
 	{
 		// 1. fetch local player
 		// 2. set him to the center
@@ -942,9 +943,10 @@ void modc_render()
 					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, client_intratick());
-					break;
 				}
 			}
+			else if(item.type == OBJTYPE_GAME)
+				gameobj = (obj_game *)data;
 		}
 	}
 
@@ -981,6 +983,7 @@ void modc_render()
 	
 	// render map
 	tilemap_render(32.0f, 0);
+	
 	// render items
 	int num = snap_num_items(SNAP_CURRENT);
 	for(int i = 0; i < num; i++)
@@ -1141,8 +1144,42 @@ void modc_render()
 		}
 	}
 	
+	// render goals
+	if(gameobj)
+	{
+		gfx_mapscreen(0,0,400,300);
+		if(!gameobj->sudden_death)
+		{
+			char buf[32];
+			int time = 0;
+			if(gameobj->time_limit)
+			{
+				time = gameobj->time_limit*60 - ((client_tick()-gameobj->round_start_tick)/client_tickspeed());
+				
+				if(gameobj->game_over)
+					time  = 0;
+			}	
+			else
+				time = (client_tick()-gameobj->round_start_tick)/client_tickspeed();
+			
+			sprintf(buf, "%d:%02d", time /60, time %60);
+			float w = gfx_pretty_text_width(16, buf);
+			gfx_pretty_text(200-w/2, 2, 16, buf);
+		}
+
+		if(gameobj->sudden_death)
+		{
+			const char *text = "Sudden Death";
+			float w = gfx_pretty_text_width(16, text);
+			gfx_pretty_text(200-w/2, 2, 16, text);
+		}
+	}
+	
 	// render score board
-	if(inp_key_pressed(baselib::input::tab) || (local_player && local_player->health == -1))
+	if(inp_key_pressed(baselib::input::tab) || // user requested
+		(local_player && local_player->health == -1) || // player dead
+		(gameobj && gameobj->game_over) // game over
+		)
 	{
 		gfx_mapscreen(0, 0, width, height);
 
@@ -1157,12 +1194,23 @@ void modc_render()
 		gfx_quads_drawTL(x-10.f, y-10.f, 400.0f, 600.0f);
 		gfx_quads_end();
 		
-		//gfx_texture_set(current_font->font_texture);
 		gfx_pretty_text(x, y, 64, "Score Board");
-		y += 100.0f;
+		y += 64.0f;
+		if(gameobj && gameobj->time_limit)
+		{
+			char buf[64];
+			sprintf(buf, "Time Limit: %d min", gameobj->time_limit);
+			gfx_pretty_text(x, y, 32, buf);
+			y += 32.0f;
+		}
+		if(gameobj && gameobj->score_limit)
+		{
+			char buf[64];
+			sprintf(buf, "Score Limit: %d", gameobj->score_limit);
+			gfx_pretty_text(x, y, 32, buf);
+			y += 32.0f;
+		}
 		
-		//gfx_texture_set(-1);
-		//gfx_quads_text(10, 50, 8, "Score Board");
 		int num = snap_num_items(SNAP_CURRENT);
 		for(int i = 0; i < num; i++)
 		{
diff --git a/src/game/game.h b/src/game/game.h
index 16a30e44..9f5aeda3 100644
--- a/src/game/game.h
+++ b/src/game/game.h
@@ -26,6 +26,7 @@ inline bool col_check_point(baselib::vec2 p) { return col_check_point(p.x, p.y);
 enum
 {
 	OBJTYPE_NULL=0,
+	OBJTYPE_GAME,
 	OBJTYPE_PLAYER,
 	OBJTYPE_PROJECTILE,
 	OBJTYPE_POWERUP,
@@ -83,6 +84,17 @@ struct ev_damageind
 	int angle;
 };
 
+struct obj_game
+{
+	int round_start_tick;
+	int game_over;
+	int sudden_death;
+	int paused;
+	
+	int score_limit;
+	int time_limit;
+};
+
 struct obj_projectile
 {
 	int type;
@@ -99,7 +111,6 @@ struct obj_powerup
 
 struct obj_player
 {
-	//int name[8];
 	int local;
 	int clientid;
 
@@ -111,12 +122,9 @@ struct obj_player
 	int vx, vy;
 	int angle;
 	
-	// current active weapon
-	int weapon;
-	// current active modifier
+	int weapon; // current active weapon
 
-	// num attack ticks left of current attack
-	int attacktick;
+	int attacktick; // num attack ticks left of current attack
 	
 	int score;
 	int emote;
diff --git a/src/game/game_variables.h b/src/game/game_variables.h
index 4414d5f2..8775074e 100644
--- a/src/game/game_variables.h
+++ b/src/game/game_variables.h
@@ -3,5 +3,6 @@ MACRO_CONFIG_INT(key_move_right, 68, 32, 512)
 MACRO_CONFIG_INT(key_jump, 32, 32, 512)
 MACRO_CONFIG_INT(key_fire, 384, 32, 512)
 MACRO_CONFIG_INT(key_hook, 385, 32, 512)
-//MACRO_CONFIG_INT(key_fire, 'E', 32, 512)
-//MACRO_CONFIG_INT(key_hook, 'Q', 32, 512)
+
+MACRO_CONFIG_INT(scorelimit, 20, 0, 1000)
+MACRO_CONFIG_INT(timelimit, 0, 0, 1000)
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index ea3a4ae2..8cd36f77 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -1,7 +1,9 @@
 #include <stdlib.h>
 #include <string.h>
+#include <engine/config.h>
 #include "../game.h"
 #include "data.h"
+#include "game_server.h"
 
 using namespace baselib;
 
@@ -27,7 +29,7 @@ void create_damageind(vec2 p, vec2 dir, int amount);
 void create_explosion(vec2 p, int owner, int weapon, bool bnodamage);
 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);
+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)
@@ -52,7 +54,6 @@ T saturated_add(T min, T max, T current, T modifier)
 	}
 }
 
-
 // TODO: rewrite this smarter!
 void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
 {
@@ -119,144 +120,109 @@ void move_box(vec2 *inout_pos, vec2 *inout_vel, vec2 size, float elasticity)
 	*inout_vel = vel;
 }
 
-//
-class event_handler
+//////////////////////////////////////////////////
+// Event handler
+//////////////////////////////////////////////////
+event_handler::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()
-	{
-		clear();
-	}
-	
-	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]);
-		}
-	}
-};
+	clear();
+}
 
-static event_handler events;
+void *event_handler::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;
+}
 
-// a basic entity
-class entity
+void event_handler::clear()
 {
-private:
-	friend class game_world;
-	friend class player;
-	entity *prev_entity;
-	entity *next_entity;
-
-	entity *prev_type_entity;
-	entity *next_type_entity;
-
-	int index;
-	static int current_id;
-protected:
-	int id;
-	
-public:
-	float proximity_radius;
-	unsigned flags;
-	int objtype;
-	vec2 pos;
+	num_events = 0;
+	current_offset = 0;
+}
 
-	enum
-	{
-		FLAG_DESTROY=0x00000001,
-		FLAG_ALIVE=0x00000002,
-	};
-	
-	entity(int objtype)
+void event_handler::snap(int snapping_client)
+{
+	for(int i = 0; i < num_events; i++)
 	{
-		this->objtype = objtype;
-		pos = vec2(0,0);
-		flags = FLAG_ALIVE;
-		proximity_radius = 0;
-		
-		current_id++;
-		id = current_id;
-		
-		next_entity = 0;
-		prev_entity = 0;
-		prev_type_entity = 0;
-		next_type_entity = 0;
+		void *d = snap_new_item(types[i], i, sizes[i]);
+		mem_copy(d, &data[offsets[i]], sizes[i]);
 	}
+}
+
+event_handler events;
+
+//////////////////////////////////////////////////
+// Entity
+//////////////////////////////////////////////////
+entity::entity(int objtype)
+{
+	this->objtype = objtype;
+	pos = vec2(0,0);
+	flags = FLAG_ALIVE;
+	proximity_radius = 0;
 	
-	void set_flag(unsigned flag) { flags |= flag; }
-	void clear_flag(unsigned flag) { flags &= ~flag; }
+	current_id++;
+	id = current_id;
 	
-	virtual ~entity()
-	{
-	}
+	next_entity = 0;
+	prev_entity = 0;
+	prev_type_entity = 0;
+	next_type_entity = 0;
+}
 
-	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, int weapon) { return true; }
-};
+entity::~entity()
+{
+}
 
 int entity::current_id = 1;
 
-// game world. handles all entities
-class game_world
+//////////////////////////////////////////////////
+// game world
+//////////////////////////////////////////////////
+game_world::game_world()
 {
-public:
-	enum
-	{
-		NUM_ENT_TYPES=10,
-	};
+	paused = false;
+	reset_requested = false;
+	first_entity = 0x0;
+	for(int i = 0; i < NUM_ENT_TYPES; i++)
+		first_entity_types[i] = 0;
+}
 
-	entity *first_entity;
-	entity *first_entity_types[NUM_ENT_TYPES];
-	
-	game_world()
+int game_world::find_entities(vec2 pos, float radius, entity **ents, int max)
+{
+	int num = 0;
+	for(entity *ent = first_entity; ent; ent = ent->next_entity)
 	{
-		first_entity = 0x0;
-		for(int i = 0; i < NUM_ENT_TYPES; i++)
-			first_entity_types[i] = 0;
+		if(!(ent->flags&entity::FLAG_ALIVE))
+			continue;
+			
+		if(distance(ent->pos, pos) < radius+ent->proximity_radius)
+		{
+			ents[num] = ent;
+			num++;
+			if(num == max)
+				break;
+		}
 	}
 	
-	int find_entities(vec2 pos, float radius, entity **ents, int max)
+	return num;
+}
+
+int game_world::find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes)
+{
+	int num = 0;
+	for(int t = 0; t < maxtypes; t++)
 	{
-		int num = 0;
-		for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		for(entity *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity)
 		{
 			if(!(ent->flags&entity::FLAG_ALIVE))
 				continue;
-				
+			
 			if(distance(ent->pos, pos) < radius+ent->proximity_radius)
 			{
 				ents[num] = ent;
@@ -265,81 +231,94 @@ public:
 					break;
 			}
 		}
-		
-		return num;
-	}
-
-	int find_entities(vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes)
-	{
-		int num = 0;
-		for(int t = 0; t < maxtypes; t++)
-		{
-			for(entity *ent = first_entity_types[types[t]]; ent; ent = ent->next_type_entity)
-			{
-				if(!(ent->flags&entity::FLAG_ALIVE))
-					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;
-
-		// into typelist aswell
-		if(first_entity_types[ent->objtype])
-			first_entity_types[ent->objtype]->prev_type_entity = ent;
-		ent->next_type_entity = first_entity_types[ent->objtype];
-		ent->prev_type_entity = 0x0;
-		first_entity_types[ent->objtype] = ent;
-	}
+	return num;
+}
+
+void game_world::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;
+
+	// into typelist aswell
+	if(first_entity_types[ent->objtype])
+		first_entity_types[ent->objtype]->prev_type_entity = ent;
+	ent->next_type_entity = first_entity_types[ent->objtype];
+	ent->prev_type_entity = 0x0;
+	first_entity_types[ent->objtype] = ent;
+}
+
+void game_world::destroy_entity(entity *ent)
+{
+	ent->set_flag(entity::FLAG_DESTROY);
+}
+
+void game_world::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;
+
+	if(ent->prev_type_entity)
+		ent->prev_type_entity->next_type_entity = ent->next_type_entity;
+	else
+		first_entity_types[ent->objtype] = ent->next_type_entity;
+	if(ent->next_type_entity)
+		ent->next_type_entity->prev_type_entity = ent->prev_type_entity;
+}
+
+//
+void game_world::snap(int snapping_client)
+{
+	for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		ent->snap(snapping_client);
+}
+
+void game_world::reset()
+{
+	// reset all entities
+	for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		ent->reset();
+	remove_entities();
 	
-	void destroy_entity(entity *ent)
-	{
-		ent->set_flag(entity::FLAG_DESTROY);
-	}
+	for(entity *ent = first_entity; ent; ent = ent->next_entity)
+		ent->post_reset();
+	remove_entities();
 	
-	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;
+	reset_requested = false;
+}
 
-		if(ent->prev_type_entity)
-			ent->prev_type_entity->next_type_entity = ent->next_type_entity;
-		else
-			first_entity_types[ent->objtype] = ent->next_type_entity;
-		if(ent->next_type_entity)
-			ent->next_type_entity->prev_type_entity = ent->prev_type_entity;
-	}
-	
-	//
-	void snap(int snapping_client)
+void game_world::remove_entities()
+{
+	// destroy objects marked for destruction
+	entity *ent = first_entity;
+	while(ent)
 	{
-		for(entity *ent = first_entity; ent; ent = ent->next_entity)
-			ent->snap(snapping_client);
+		entity *next = ent->next_entity;
+		if(ent->flags&entity::FLAG_DESTROY)
+		{
+			remove_entity(ent);
+			ent->destroy();
+		}
+		ent = next;
 	}
+}
+
+void game_world::tick()
+{
+	if(reset_requested)
+		reset();
 	
-	void tick()
+	if(!paused)
 	{
 		// update all objects
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
@@ -347,643 +326,654 @@ public:
 			
 		for(entity *ent = first_entity; ent; ent = ent->next_entity)
 			ent->tick_defered();
-		
-		// 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;
-		}
 	}
-};
+	
+	remove_entities();
+}
 
-static game_world world;
+game_world world;
 
-// TODO: move to seperate file
-class powerup : public entity
+//////////////////////////////////////////////////
+// game object
+//////////////////////////////////////////////////
+gameobject::gameobject()
+: entity(OBJTYPE_GAME)
 {
-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);
-};
+	game_over_tick = -1;
+	sudden_death = 0;
+	round_start_tick = server_tick();
+}
 
-// projectile entity
-class projectile : public entity
+void gameobject::endround()
 {
-public:
-	enum
-	{
-		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
-	};
-	
-	vec2 vel;
-	entity *powner; // this is nasty, could be removed when client quits
-	int lifespan;
-	int owner;
-	int type;
-	int flags;
-	int damage;
-	int sound_impact;
-	int weapon;
-	float force;
+	world.paused = true;
+	game_over_tick = server_tick();
+	sudden_death = 0;
+}
+
+void gameobject::resetgame()
+{
+	world.reset_requested = true;
+}
+
+void gameobject::startround()
+{
+	resetgame();
 	
-	projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner, int damage, int flags, float force, int sound_impact, int weapon)
-	: entity(OBJTYPE_PROJECTILE)
+	round_start_tick = server_tick();
+	sudden_death = 0;
+	game_over_tick = -1;
+	world.paused = false;
+}
+
+void gameobject::post_reset()
+{
+	for(int i = 0; i < MAX_CLIENTS; i++)
 	{
-		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;
-		this->weapon = weapon;
-		world.insert_entity(this);
+		if(players[i].client_id != -1)
+			players[i].respawn();
 	}
+}
 
-	void tick()
+void gameobject::tick()
+{
+	if(game_over_tick == -1)
 	{
-		vec2 oldpos = pos;
-		vel.y += 0.25f;
-		pos += vel;
-		lifespan--;
+		// game is running
 		
-		// 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))
+		// gather some stats
+		int topscore = 0;
+		int topscore_count = 0;
+		for(int i = 0; i < MAX_CLIENTS; i++)
 		{
-			if (lifespan >= 0)
-				create_sound(pos, sound_impact);
-				
-			if (flags & PROJECTILE_FLAGS_EXPLODE)
-				create_explosion(oldpos, owner, weapon, false);
-			else if (targetplayer)
-				targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner, weapon);
-				
-			world.destroy_entity(this);
+			if(players[i].client_id != -1)
+			{
+				if(players[i].score > topscore)
+				{
+					topscore = players[i].score;
+					topscore_count = 1;
+				}
+				else if(players[i].score == topscore)
+					topscore_count++;
+			}
+		}
+		
+		// check score win condition
+		if((config.scorelimit > 0 && topscore >= config.scorelimit) ||
+			(config.timelimit > 0 && (server_tick()-round_start_tick) >= config.timelimit*server_tickspeed()*60))
+		{
+			if(topscore_count == 1)
+				endround();
+			else
+				sudden_death = 1;
 		}
 	}
-	
-	void snap(int snapping_client)
+	else
 	{
-		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; // TODO: should be an angle
-		proj->vy = (int)vel.y;
-		proj->type = type;
+		// game over.. wait for restart
+		if(server_tick() > game_over_tick+server_tickspeed()*10)
+			startround();
 	}
-};
+}
 
-// player entity
-// TODO: move to separate file
-class player : public entity
+void gameobject::snap(int snapping_client)
 {
-public:
-	static const int phys_size = 28;
+	obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, id, sizeof(obj_game));
+	game->paused = world.paused;
+	game->game_over = game_over_tick==-1?0:1;
+	game->sudden_death = sudden_death;
 	
-	enum // what are these?
-	{
-		WEAPON_PROJECTILETYPE_GUN		= 0,
-		WEAPON_PROJECTILETYPE_ROCKET	= 1,
-		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
-	};
+	game->score_limit = config.scorelimit;
+	game->time_limit = config.timelimit;
+	game->round_start_tick = round_start_tick;
+}
+
+gameobject gameobj;
+
+//////////////////////////////////////////////////
+// projectile
+//////////////////////////////////////////////////
+projectile::projectile(int type, int owner, vec2 pos, vec2 vel, int span, entity* powner,
+	int damage, int flags, float force, int sound_impact, int weapon)
+: entity(OBJTYPE_PROJECTILE)
+{
+	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;
+	this->weapon = weapon;
+	world.insert_entity(this);
+}
+
+void projectile::reset()
+{
+	world.destroy_entity(this);
+}
+
+void projectile::tick()
+{
+	vec2 oldpos = pos;
+	vel.y += 0.25f;
+	pos += vel;
+	lifespan--;
 	
-	// weapon info
-	struct weaponstat
+	// 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))
 	{
-		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;
-	char name[64];
-
-	// input	
-	player_input previnput;
-	player_input input;
-	int jumped;
-	
-	int damage_taken_tick;
+		if (lifespan >= 0)
+			create_sound(pos, sound_impact);
+			
+		if (flags & PROJECTILE_FLAGS_EXPLODE)
+			create_explosion(oldpos, owner, weapon, false);
+		else if (targetplayer)
+			targetplayer->take_damage(normalize(vel) * max(0.001f, force), damage, owner, weapon);
+			
+		world.destroy_entity(this);
+	}
+}
 
-	int health;
-	int armor;
+void projectile::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; // TODO: should be an angle
+	proj->vy = (int)vel.y;
+	proj->type = type;
+}
 
-	int score;
+//////////////////////////////////////////////////
+// player
+//////////////////////////////////////////////////
+// TODO: move to separate file
+player::player()
+: entity(OBJTYPE_PLAYER)
+{
+	init();
+}
+
+void player::init()
+{
+	proximity_radius = phys_size;
+	name[0] = 'n';
+	name[1] = 'o';
+	name[2] = 'o';
+	name[3] = 'b';
+	name[4] = 0;
+	client_id = -1;
+	reset();
+}
+
+void player::reset()
+{
+	release_hooked();
+	release_hooks();
 	
-	bool dead;
-	int die_tick;
+	pos = vec2(0.0f, 0.0f);
+	vel = vec2(0.0f, 0.0f);
+	direction = vec2(0.0f, 1.0f);
+	score = 0;
+	dead = true;
+	die_tick = 0;
+}
 
-	// hooking stuff
-	enum
-	{
-		HOOK_RETRACTED=-1,
-		HOOK_IDLE=0,
-		HOOK_FLYING,
-		HOOK_GRABBED
-	};
+void player::destroy() {  }
 	
-	int hook_state; 
-	player *hooked_player;
-	vec2 hook_pos;
-	vec2 hook_dir;
-
-	//
-	player()
-	: entity(OBJTYPE_PLAYER)
-	{
-		reset();
-	}
+void player::respawn()
+{
+	health = PLAYER_MAXHEALTH;
+	armor = 0;
+	jumped = 0;
+	dead = false;
+	set_flag(entity::FLAG_ALIVE);
 	
-	void reset()
-	{
-		//equip_time = 0;
-		
-		release_hooked();
-		release_hooks();
-		
-		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;
-		score = 0;
-		dead = true;
-		die_tick = 0;
-	}
+	mem_zero(&input, sizeof(input));
+	vel = vec2(0.0f, 0.0f);
 	
-	virtual void destroy() {  }
-		
-	void respawn()
-	{
-		health = PLAYER_MAXHEALTH;
-		armor = 0;
-		jumped = 0;
-		dead = false;
-		set_flag(entity::FLAG_ALIVE);
-		
-		mem_zero(&input, sizeof(input));
-		vel = vec2(0.0f, 0.0f);
-		
-		// get spawn point
-		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((float)sp->x, (float)sp->y);
-		}
-		else
-			pos = vec2(100.0f, -60.0f);
-		defered_pos = pos;
-			
-		// 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;
-		
-		create_sound(pos, SOUND_PLAYER_SPAWN);
-	}
+	// get spawn point
+	int start, num;
+	map_get_type(1, &start, &num);
 	
-	bool is_grounded()
+	if(num)
 	{
-		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;
+		mapres_spawnpoint *sp = (mapres_spawnpoint*)map_get_item(start + (rand()%num), NULL, NULL);
+		pos = vec2((float)sp->x, (float)sp->y);
 	}
+	else
+		pos = vec2(100.0f, -60.0f);
+	defered_pos = pos;
+		
+	// 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;
+	
+	create_sound(pos, SOUND_PLAYER_SPAWN);
+}
 
-	// releases the hooked player
-	void release_hooked()
-	{
-		hook_state = HOOK_IDLE;
-		hooked_player = 0x0;
-	}
+bool player::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;
+}
 
-	// release all hooks to this player	
-	void release_hooks()
+// releases the hooked player
+void player::release_hooked()
+{
+	hook_state = HOOK_IDLE;
+	hooked_player = 0x0;
+}
+
+// release all hooks to this player	
+void player::release_hooks()
+{
+	// TODO: loop thru players only
+	for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
 	{
-		// TODO: loop thru players only
-		for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
+		if(ent && ent->objtype == OBJTYPE_PLAYER)
 		{
-			if(ent && ent->objtype == OBJTYPE_PLAYER)
-			{
-				player *p = (player*)ent;
-				if(p->hooked_player == this)
-					p->release_hooked();
-			}
+			player *p = (player*)ent;
+			if(p->hooked_player == this)
+				p->release_hooked();
 		}
 	}
-	
-	void handle_weapons()
+}
+
+void player::handle_weapons()
+{
+	// check reload timer
+	if(reload_timer)
 	{
-		// check reload timer
-		if(reload_timer)
-		{
-			reload_timer--;
-			return;
-		}
+		reload_timer--;
+		return;
+	}
 
-		// switch weapon if wanted		
-		if(input.activeweapon >= 0 && input.activeweapon < NUM_WEAPONS && weapons[input.activeweapon].got)
-			active_weapon = input.activeweapon;
+	// switch weapon if wanted		
+	if(input.activeweapon >= 0 && input.activeweapon < NUM_WEAPONS && weapons[input.activeweapon].got)
+		active_weapon = input.activeweapon;
 
-		if(input.fire)
+	if(input.fire)
+	{
+		if(reload_timer == 0)
 		{
-			if(reload_timer == 0)
+			// fire!
+			if(weapons[active_weapon].ammo)
 			{
-				// fire!
-				if(weapons[active_weapon].ammo)
+				switch(active_weapon)
 				{
-					switch(active_weapon)
-					{
-						case WEAPON_HAMMER:
-							break;
+					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, WEAPON_GUN);
-							break;
-						case WEAPON_ROCKET:
-							new projectile(WEAPON_PROJECTILETYPE_ROCKET,
+					case WEAPON_GUN:
+						new projectile(WEAPON_PROJECTILETYPE_GUN,
+							client_id,
+							pos+vec2(0,0),
+							direction*30.0f,
+							100,
+							this,
+							1, 0, 0, -1, WEAPON_GUN);
+						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, WEAPON_ROCKET);						
+						break;
+					case WEAPON_SHOTGUN:
+						for(int i = 0; i < 3; i++)
+						{
+							new projectile(WEAPON_PROJECTILETYPE_SHOTGUN,
 								client_id,
 								pos+vec2(0,0),
-								direction*15.0f,
+								direction*(20.0f+(i+1)*2.0f),
 								100,
 								this,
-								1, projectile::PROJECTILE_FLAGS_EXPLODE, 0, -1, WEAPON_ROCKET);						
-							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, WEAPON_SHOTGUN);
-							}
-							break;
-					}
-					
-					weapons[active_weapon].ammo--;
-				}
-				else
-				{
-					// click!!! click
+								1, 0, 0, -1, WEAPON_SHOTGUN);
+						}
+						break;
 				}
 				
-				attack_tick = server_tick();
-				reload_timer = 10; // make this variable depending on weapon
+				weapons[active_weapon].ammo--;
+			}
+			else
+			{
+				// click!!! click
 			}
+			
+			attack_tick = server_tick();
+			reload_timer = 10; // make this variable depending on weapon
 		}
 	}
+}
+
+void player::tick()
+{
+	// TODO: rework the input to be more robust
+	// TODO: remove this tick count, it feels weird
+	if(dead)
+	{
+		if(server_tick()-die_tick >= server_tickspeed()*3) // auto respawn after 3 sec
+			respawn();
+		if(input.fire && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec
+			respawn();
+		return;
+	}
+	
+	// fetch some info
+	bool grounded = is_grounded();
+	direction = get_direction(input.angle);
 	
-	virtual void tick()
+	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)
 	{
-		// TODO: rework the input to be more robust
-		// TODO: remove this tick count, it feels weird
-		if(dead)
+		if(!jumped && grounded)
 		{
-			if(server_tick()-die_tick >= server_tickspeed()*3) // auto respawn after 3 sec
-				respawn();
-			if(input.fire && server_tick()-die_tick >= server_tickspeed()/2) // auto respawn after 0.5 sec
-				respawn();
-			return;
+			create_sound(pos, SOUND_PLAYER_JUMP);
+			vel.y = -ground_jump_speed;
+			jumped++;
 		}
+	}
+	else
+		jumped = 0;
 		
-		// fetch some info
-		bool grounded = is_grounded();
-		direction = get_direction(input.angle);
-		
-		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)
+	// do hook
+	if(input.hook)
+	{
+		if(hook_state == HOOK_IDLE)
 		{
-			if(!jumped && grounded)
-			{
-				create_sound(pos, SOUND_PLAYER_JUMP);
-				vel.y = -ground_jump_speed;
-				jumped++;
-			}
+			hook_state = HOOK_FLYING;
+			hook_pos = pos;
+			hook_dir = direction;
 		}
-		else
-			jumped = 0;
-			
-		// do hook
-		if(input.hook)
+		else if(hook_state == HOOK_FLYING)
 		{
-			if(hook_state == HOOK_IDLE)
-			{
-				hook_state = HOOK_FLYING;
-				hook_pos = pos;
-				hook_dir = direction;
-			}
-			else if(hook_state == HOOK_FLYING)
-			{
-				vec2 new_pos = hook_pos+hook_dir*hook_fire_speed;
+			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)
-				{
-					if(ent && ent->objtype == OBJTYPE_PLAYER)
-					{
-						player *p = (player*)ent;
-						if(p != this && distance(p->pos, new_pos) < p->phys_size)
-						{
-							hook_state = HOOK_GRABBED;
-							hooked_player = p;
-							break;
-						}
-					}
-				}
-				
-				if(hook_state == HOOK_FLYING)
+			// Check against other players first
+			for(entity *ent = world.first_entity; ent; ent = ent->next_entity)
+			{
+				if(ent && ent->objtype == OBJTYPE_PLAYER)
 				{
-					// check against ground
-					if(col_intersect_line(hook_pos, new_pos, &new_pos))
+					player *p = (player*)ent;
+					if(p != this && distance(p->pos, new_pos) < p->phys_size)
 					{
 						hook_state = HOOK_GRABBED;
-						hook_pos = new_pos;	
-					}
-					else if(distance(pos, new_pos) > hook_length)
-					{
-						hook_state = HOOK_RETRACTED;
+						hooked_player = p;
+						break;
 					}
-					else
-						hook_pos = new_pos;
 				}
-				
-				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)
+			if(hook_state == HOOK_FLYING)
 			{
-				player *p = (player*)ent;
-				if(p == this || !(p->flags&FLAG_ALIVE))
-					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)
+				// check against ground
+				if(col_intersect_line(hook_pos, new_pos, &new_pos))
 				{
-					float a = phys_size*1.25f - d;
-					vel = vel + dir*a;
+					hook_state = HOOK_GRABBED;
+					hook_pos = new_pos;	
 				}
-				
-				// handle hook influence
-				if(p->hooked_player == this)
+				else if(distance(pos, new_pos) > hook_length)
 				{
-					if(d > phys_size*1.50f) // 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);
-						vel.y = saturated_add(-hook_drag_speed, hook_drag_speed, vel.y, -accel*dir.y);
-					}
+					hook_state = HOOK_RETRACTED;
 				}
+				else
+					hook_pos = new_pos;
 			}
+			
+			if(hook_state == HOOK_GRABBED)
+				create_sound(pos, SOUND_HOOK_ATTACH);
 		}
-		
-		// 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(int killer, int weapon)
+	else
 	{
-		// send the kill message
-		msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL);
-		msg_pack_int(killer);
-		msg_pack_int(client_id);
-		msg_pack_int(weapon);
-		msg_pack_end();
-		server_send_msg(-1);
-		
-		// a nice sound
-		create_sound(pos, SOUND_PLAYER_DIE);
-		
-		// release all hooks
 		release_hooked();
-		release_hooks();
-		
-		// set dead state
-		dead = true;
-		die_tick = server_tick();
-		clear_flag(entity::FLAG_ALIVE);
+		hook_pos = pos;
 	}
-	
-	virtual bool take_damage(vec2 force, int dmg, int from, int weapon)
+		
+	if(hook_state == HOOK_GRABBED)
 	{
-		vel += force;
-
-		// create healthmod indicator
-		create_damageind(pos, normalize(force), dmg);
+		if(hooked_player)
+			hook_pos = hooked_player->pos;
 
-		if(armor)
+		float d = distance(pos, hook_pos);
+		vec2 dir = normalize(pos - hook_pos);		
+		if(d > 10.0f) // TODO: fix tweakable variable
 		{
-			armor -= 1;
-			dmg--;
+			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);
 		}
+	}
 		
-		if(dmg > armor)
-		{
-			dmg -= armor;
-			armor = 0;
-			health -= dmg;
-		}
-		else
-			armor -= dmg;
-		
-		damage_taken_tick = server_tick()+50;
-		
-		// check for death
-		if(health <= 0)
+	// 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)
 		{
-			// apply score
-			if(from != -1)
+			player *p = (player*)ent;
+			if(p == this || !(p->flags&FLAG_ALIVE))
+				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(from == client_id)
-					score--;
-				else
+				float a = phys_size*1.25f - d;
+				vel = vel + dir*a;
+			}
+			
+			// handle hook influence
+			if(p->hooked_player == this)
+			{
+				if(d > phys_size*1.50f) // TODO: fix tweakable variable
 				{
-					player *p = get_player(from);
-					p->score++;
+					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);
 				}
 			}
-			
-			die(from, weapon);
-			return false;
 		}
+	}
+	
+	// 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;
+}
 
-		if (dmg > 2)
-			create_sound(pos, SOUND_PLAYER_PAIN_LONG);
-		else
-			create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
+void player::tick_defered()
+{
+	// apply the new position
+	pos = defered_pos;
+}
 
-		// spawn blood?
-		return true;
-	}
+void player::die(int killer, int weapon)
+{
+	// send the kill message
+	msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL);
+	msg_pack_int(killer);
+	msg_pack_int(client_id);
+	msg_pack_int(weapon);
+	msg_pack_end();
+	server_send_msg(-1);
+	
+	// a nice sound
+	create_sound(pos, SOUND_PLAYER_DIE);
+	
+	// release all hooks
+	release_hooked();
+	release_hooks();
+	
+	// set dead state
+	dead = true;
+	die_tick = server_tick();
+	clear_flag(entity::FLAG_ALIVE);
+}
 
-	virtual void snap(int snaping_client)
+bool player::take_damage(vec2 force, int dmg, int from, int weapon)
+{
+	vel += force;
+
+	// create healthmod indicator
+	create_damageind(pos, normalize(force), dmg);
+
+	if(armor)
 	{
-		obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
-
-		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 = weapons[active_weapon].ammo;
-		player->health = 0;
-		player->armor = 0;
-		player->local = 0;
-		player->clientid = client_id;
-		player->weapon = active_weapon;
-		player->attacktick = attack_tick;
-		
-		if(client_id == snaping_client)
-		{
-			player->local = 1;
-			player->health = health;
-			player->armor = armor;
-		}
-		
-		if(dead)
-			player->health = -1;
-		
-		if(length(vel) > 15.0f)
-			player->emote = EMOTE_HAPPY;
-		
-		if(damage_taken_tick > server_tick())
-			player->emote = EMOTE_PAIN;
-		
-		if(player->emote == EMOTE_NORMAL)
+		armor -= 1;
+		dmg--;
+	}
+	
+	if(dmg > armor)
+	{
+		dmg -= armor;
+		armor = 0;
+		health -= dmg;
+	}
+	else
+		armor -= dmg;
+	
+	damage_taken_tick = server_tick()+50;
+	
+	// check for death
+	if(health <= 0)
+	{
+		// apply score
+		if(from != -1)
 		{
-			if((server_tick()%(50*5)) < 10)
-				player->emote = EMOTE_BLINK;
+			if(from == client_id)
+				score--;
+			else
+			{
+				player *p = get_player(from);
+				p->score++;
+			}
 		}
 		
-		player->hook_active = hook_state>0?1:0;
-		player->hook_x = (int)hook_pos.x;
-		player->hook_y = (int)hook_pos.y;
-			
-		player->angle = input.angle;
-		player->score = score;
+		die(from, weapon);
+		return false;
+	}
+
+	if (dmg > 2)
+		create_sound(pos, SOUND_PLAYER_PAIN_LONG);
+	else
+		create_sound(pos, SOUND_PLAYER_PAIN_SHORT);
+
+	// spawn blood?
+	return true;
+}
+
+void player::snap(int snaping_client)
+{
+	obj_player *player = (obj_player *)snap_new_item(OBJTYPE_PLAYER, client_id, sizeof(obj_player));
+
+	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 = weapons[active_weapon].ammo;
+	player->health = 0;
+	player->armor = 0;
+	player->local = 0;
+	player->clientid = client_id;
+	player->weapon = active_weapon;
+	player->attacktick = attack_tick;
+	
+	if(client_id == snaping_client)
+	{
+		player->local = 1;
+		player->health = health;
+		player->armor = armor;
+	}
+	
+	if(dead)
+		player->health = -1;
+	
+	if(length(vel) > 15.0f)
+		player->emote = EMOTE_HAPPY;
+	
+	if(damage_taken_tick > server_tick())
+		player->emote = EMOTE_PAIN;
+	
+	if(player->emote == EMOTE_NORMAL)
+	{
+		if((server_tick()%(50*5)) < 10)
+			player->emote = EMOTE_BLINK;
 	}
-};
+	
+	player->hook_active = hook_state>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 ///////////////////////
+player players[MAX_CLIENTS];
 
+//////////////////////////////////////////////////
+// powerup
+//////////////////////////////////////////////////
 powerup::powerup(int _type, int _subtype)
 : entity(OBJTYPE_POWERUP)
 {
 	//static int current_id = 0;
 	type = _type;
 	subtype = _subtype;
+	
 	// set radius (so it can collide and be hooked and stuff)
 	proximity_radius = phys_size;
-	spawntick = -1;
+	
+	reset();
 	
 	// TODO: should this be done here?
 	world.insert_entity(this);
 }
 
+void powerup::reset()
+{
+	spawntick = -1;
+}
+
 void powerup::tick()
 {
 	// wait for respawn
@@ -1052,8 +1042,6 @@ void powerup::snap(int snapping_client)
 
 // POWERUP END ///////////////////////
 
-static player players[MAX_CLIENTS];
-
 player *get_player(int index)
 {
 	return &players[index];
@@ -1150,6 +1138,9 @@ void mods_tick()
 	// clear all events
 	events.clear();
 	world.tick();
+	
+	if(world.paused) // make sure that the game object always updates
+		gameobj.tick();
 
 	if(debug_bots)
 	{
@@ -1178,16 +1169,20 @@ void mods_snap(int 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;
+	if(!world.paused)
+	{
+		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].init();
 	players[client_id].client_id = client_id;
-	players[client_id].respawn();
 	world.insert_entity(&players[client_id]);
+	players[client_id].respawn();
+	
 	
 	client_info info; // fetch login name
 	if(server_getclientinfo(client_id, &info))
@@ -1278,6 +1273,8 @@ void mods_init()
 		powerup *ppower = new powerup(type, subtype);
 		ppower->pos = vec2(it->x, it->y);
 	}
+	
+	world.insert_entity(&gameobj);
 }
 
 void mods_shutdown() {}
diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h
new file mode 100644
index 00000000..027a140f
--- /dev/null
+++ b/src/game/server/game_server.h
@@ -0,0 +1,254 @@
+
+//
+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();
+	void *create(int type, int size);
+	void clear();
+	void snap(int snapping_client);
+};
+
+extern event_handler events;
+
+// a basic entity
+class entity
+{
+private:
+	friend class game_world;
+	friend class player;
+	entity *prev_entity;
+	entity *next_entity;
+
+	entity *prev_type_entity;
+	entity *next_type_entity;
+
+	int index;
+	static int current_id;
+protected:
+	int id;
+	
+public:
+	float proximity_radius;
+	unsigned flags;
+	int objtype;
+	baselib::vec2 pos;
+
+	enum
+	{
+		FLAG_DESTROY=0x00000001,
+		FLAG_ALIVE=0x00000002,
+	};
+	
+	entity(int objtype);
+	virtual ~entity();
+	
+	virtual void reset() {}
+	virtual void post_reset() {}
+	
+	void set_flag(unsigned flag) { flags |= flag; }
+	void clear_flag(unsigned flag) { flags &= ~flag; }
+
+	virtual void destroy() { delete this; }
+	virtual void tick() {}
+	virtual void tick_defered() {}
+		
+	virtual void snap(int snapping_client) {}
+		
+	virtual bool take_damage(baselib::vec2 force, int dmg, int from, int weapon) { return true; }
+};
+
+
+class game_world
+{
+	void reset();
+	void remove_entities();
+public:
+	enum
+	{
+		NUM_ENT_TYPES=10, // TODO: are more exact value perhaps? :)
+	};
+
+	// TODO: two lists seams kinda not good, shouldn't be needed
+	entity *first_entity;
+	entity *first_entity_types[NUM_ENT_TYPES];
+	bool paused;
+	bool reset_requested;
+	
+	game_world();
+	int find_entities(baselib::vec2 pos, float radius, entity **ents, int max);
+	int find_entities(baselib::vec2 pos, float radius, entity **ents, int max, const int* types, int maxtypes);
+
+	void insert_entity(entity *ent);
+	void destroy_entity(entity *ent);
+	void remove_entity(entity *ent);
+	
+	//
+	void snap(int snapping_client);
+	void tick();
+};
+
+extern game_world world;
+
+// game object
+class gameobject : public entity
+{
+	void resetgame();
+	void startround();
+	void endround();
+	
+	int round_start_tick;
+	int game_over_tick;
+	int sudden_death;
+	
+public:
+	gameobject();
+	virtual void post_reset();
+	virtual void tick();
+	virtual void snap(int snapping_client);
+};
+
+extern gameobject gameobj;
+
+
+// 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 reset();
+	virtual void tick();
+	virtual void snap(int snapping_client);
+};
+
+
+
+// projectile entity
+class projectile : public entity
+{
+public:
+	enum
+	{
+		PROJECTILE_FLAGS_EXPLODE = 1 << 0,
+	};
+	
+	baselib::vec2 vel;
+	entity *powner; // this is nasty, could be removed when client quits
+	int lifespan;
+	int owner;
+	int type;
+	int flags;
+	int damage;
+	int sound_impact;
+	int weapon;
+	float force;
+	
+	projectile(int type, int owner, baselib::vec2 pos, baselib::vec2 vel, int span, entity* powner,
+		int damage, int flags, float force, int sound_impact, int weapon);
+	virtual void reset();
+	virtual void tick();
+	virtual void snap(int snapping_client);
+};
+
+// player entity
+class player : public entity
+{
+public:
+	static const int phys_size = 28;
+	
+	enum // what are these?
+	{
+		WEAPON_PROJECTILETYPE_GUN		= 0,
+		WEAPON_PROJECTILETYPE_ROCKET	= 1,
+		WEAPON_PROJECTILETYPE_SHOTGUN	= 2,
+	};
+	
+	// weapon info
+	struct weaponstat
+	{
+		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
+	baselib::vec2 defered_pos;
+	baselib::vec2 vel;
+	baselib::vec2 direction;
+
+	//
+	int client_id;
+	char name[64];
+
+	// input	
+	player_input previnput;
+	player_input input;
+	int jumped;
+	
+	int damage_taken_tick;
+
+	int health;
+	int armor;
+
+	int score;
+	
+	bool dead;
+	int die_tick;
+
+	// hooking stuff
+	enum
+	{
+		HOOK_RETRACTED=-1,
+		HOOK_IDLE=0,
+		HOOK_FLYING,
+		HOOK_GRABBED
+	};
+	
+	int hook_state; 
+	player *hooked_player;
+	baselib::vec2 hook_pos;
+	baselib::vec2 hook_dir;
+
+	//
+	player();
+	void init();
+	virtual void reset();
+	virtual void destroy();
+		
+	void respawn();
+
+	bool is_grounded();
+
+	void release_hooked();
+	void release_hooks();
+	
+	void handle_weapons();
+
+	virtual void tick();
+	virtual void tick_defered();
+	
+	void die(int killer, int weapon);
+	
+	virtual bool take_damage(baselib::vec2 force, int dmg, int from, int weapon);
+	virtual void snap(int snaping_client);
+};
+
+extern player players[MAX_CLIENTS];