diff -Nru teeworlds-0.5.2-src/src/game/server/entities/bomb.cpp defusion 0.52/src/game/server/entities/bomb.cpp
--- teeworlds-0.5.2-src/src/game/server/entities/bomb.cpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/entities/bomb.cpp	2009-03-23 15:33:45.000000000 +0000
@@ -0,0 +1,31 @@
+#include <game/server/gamecontext.hpp>
+#include "bomb.hpp"
+
+BOMB::BOMB (vec2 _pos)
+:	ENTITY (NETOBJTYPE_FLAG)
+{
+	proximity_radius = phys_size;
+	pos = _pos;
+	carrying_character = 0x0;
+	vel = vec2 (0,0);
+	team = 0;
+	activating_timer = -1;
+	deactivating_timer = -1;
+}
+
+void BOMB::reset()
+{
+	game.world.destroy_entity (this);
+}
+
+void BOMB::snap (int snapping_client)
+{
+	NETOBJ_FLAG *bomb = static_cast<NETOBJ_FLAG *> (snap_new_item (NETOBJTYPE_FLAG, team, sizeof (NETOBJ_FLAG)));
+	bomb->x = static_cast<int> (pos.x);
+	bomb->y = static_cast<int> (pos.y);
+	bomb->team = team;
+	bomb->carried_by = team ? -1: -2;
+	
+	if (carrying_character && !team && carrying_character->player)
+		bomb->carried_by = carrying_character->player->client_id;
+}
diff teeworlds-0.5.2-src/src/game/server/entities/bomb.hpp defusion 0.52/src/game/server/entities/bomb.hpp
--- teeworlds-0.5.2-src/src/game/server/entities/bomb.hpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/entities/bomb.hpp	2009-03-23 15:33:49.000000000 +0000
@@ -0,0 +1,22 @@
+#ifndef GAME_SERVER_ENTITY_BOMB_H
+#define GAME_SERVER_ENTITY_BOMB_H
+
+#include <game/server/entity.hpp>
+
+class BOMB : public ENTITY
+{
+public:
+	static const int phys_size = 14;
+	CHARACTER *carrying_character;
+	vec2 vel;
+	int team;
+	int activating_timer;
+	int deactivating_timer;
+	
+	BOMB (vec2 _pos);
+
+	virtual void reset ();
+	virtual void snap (int snapping_client);
+};
+
+#endif
diff -Nru teeworlds-0.5.2-src/src/game/server/entities/spickup.cpp defusion 0.52/src/game/server/entities/spickup.cpp
--- teeworlds-0.5.2-src/src/game/server/entities/spickup.cpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/entities/spickup.cpp	2009-03-23 15:33:54.000000000 +0000
@@ -0,0 +1,193 @@
+#include <engine/e_config.h>
+#include <engine/e_server_interface.h>
+#include <game/server/gamecontext.hpp>
+#include "spickup.hpp"
+
+SPECIAL_PICKUP::SPECIAL_PICKUP (int _type, int _subtype, int _pickup_type, vec2 _pos, int _items)
+:	ENTITY (NETOBJTYPE_PICKUP),
+	type (_type),
+	subtype (_subtype),
+	pickup_type (_pickup_type),
+	items (_items)
+{
+	proximity_radius = phys_size;
+	pos = _pos;
+	active_timer = _pickup_type == SPICKUP_DROP || !config.sv_de_shop_time ? -1 : config.sv_de_shop_time * server_tickspeed ();
+	vel = vec2 (0, 0);
+}
+
+void SPECIAL_PICKUP::reset ()
+{
+	if (pickup_type == SPICKUP_DROP)
+		game.world.destroy_entity (this);
+	else
+		active_timer = !config.sv_de_shop_time ? -1 : config.sv_de_shop_time * server_tickspeed ();
+}
+
+void SPECIAL_PICKUP::tick ()
+{
+	if (!active_timer)
+		return;
+
+	if (active_timer != -1)
+		--active_timer;
+
+	if (pickup_type == SPICKUP_DROP)
+	{
+		//	update pickup position
+		vel.y += game.world.core.tuning.gravity;
+		move_box (&pos, &vel, vec2 (phys_size, phys_size), 0.5f);
+
+		// pickup hits death-tile, delete it
+		if (col_get (static_cast<int> (pos.x), static_cast<int> (pos.y)) & COLFLAG_DEATH)
+		{
+			game.world.destroy_entity (this);
+			return;
+		}
+	}
+
+	//	pick it up
+	CHARACTER *ch = game.world.closest_character (pos, 20.0f, 0);
+	if (ch && ch->alive)
+	{
+		switch (type)
+		{
+		case POWERUP_ARMOR:
+			if (ch->player->score >= COSTS_ARMOR && ch->increase_armor (items))
+			{
+				if (pickup_type == SPICKUP_BUY)
+				{
+					ch->player->score -= COSTS_ARMOR;
+					char chat_msg[128];
+					str_format (chat_msg, sizeof (chat_msg), "bought ARMOR, score: %i (-%i)", ch->player->score, COSTS_ARMOR);
+					game.send_chat_target (ch->player->client_id, chat_msg);
+				}
+				else if (pickup_type == SPICKUP_DROP)
+					game.world.destroy_entity (this);
+				game.create_sound (pos, SOUND_PICKUP_ARMOR, cmask_one (ch->player->client_id));
+			}
+			break;
+		case POWERUP_WEAPON:
+			if ((subtype == WEAPON_SHOTGUN || subtype == WEAPON_GRENADE || subtype == WEAPON_RIFLE) &&
+				(ch->weapons[subtype].ammo < data->weapons.id[subtype].maxammo || !ch->weapons[subtype].got))
+			{
+				if (pickup_type == SPICKUP_BUY)
+				{
+					const int costs = ch->weapons[subtype].got ? subtype == WEAPON_SHOTGUN ? COSTS_SHOTGUN_AMMO :
+																subtype == WEAPON_GRENADE ? COSTS_GRENADE_AMMO :
+																COSTS_RIFLE_AMMO :
+																subtype == WEAPON_SHOTGUN ? COSTS_SHOTGUN :
+																subtype == WEAPON_GRENADE ? COSTS_GRENADE :
+																COSTS_RIFLE;
+					if (costs <= ch->player->score)
+					{
+						ch->player->score -= costs;
+						char chat_msg[128] = {0};
+						switch (subtype)
+						{
+						case WEAPON_SHOTGUN:
+							{
+								if (costs == COSTS_SHOTGUN_AMMO)
+									str_format (chat_msg, sizeof (chat_msg), "bought SHOTGUN-AMMO, score: %i (-%i)", ch->player->score, COSTS_SHOTGUN_AMMO);
+								else
+									str_format (chat_msg, sizeof (chat_msg), "bought SHOTGUN, score: %i (-%i)", ch->player->score, COSTS_SHOTGUN);
+							}
+							break;
+						case WEAPON_GRENADE:
+							{
+								if (costs == COSTS_GRENADE_AMMO)
+									str_format (chat_msg, sizeof (chat_msg), "bought GRENADE-AMMO, score: %i (-%i)", ch->player->score, COSTS_GRENADE_AMMO);
+								else
+									str_format (chat_msg, sizeof (chat_msg), "bought GRENADE, score: %i (-%i)", ch->player->score, COSTS_GRENADE);
+							}
+							break;
+						case WEAPON_RIFLE:
+							{
+								if (costs == COSTS_RIFLE_AMMO)
+									str_format (chat_msg, sizeof (chat_msg), "bought RIFLE-AMMO, score: %i (-%i)", ch->player->score, COSTS_RIFLE_AMMO);
+								else
+									str_format (chat_msg, sizeof (chat_msg), "bought RIFLE, score: %i (-%i)", ch->player->score, COSTS_RIFLE);
+							}
+						}
+						game.send_chat_target (ch->player->client_id, chat_msg);
+					}
+					else 
+						break;
+				}
+				else if (pickup_type == SPICKUP_DROP)
+				{
+					//	only pick up empty dropped weapons if the player doesn't have it yet
+					if (!items && ch->weapons[subtype].got)
+						break;
+					game.world.destroy_entity (this);
+				}
+				ch->weapons[subtype].got = true;
+				ch->weapons[subtype].ammo = min (data->weapons.id[subtype].maxammo, ch->weapons[subtype].ammo + items);
+				if (subtype == WEAPON_GRENADE)
+					game.create_sound (pos, SOUND_PICKUP_GRENADE, cmask_one (ch->player->client_id));
+				else
+					game.create_sound (pos, SOUND_PICKUP_SHOTGUN, cmask_one (ch->player->client_id));
+				game.send_weapon_pickup (ch->player->client_id, subtype);
+			}
+			break;
+		case POWERUP_NINJA:
+			if (ch->player->team == 1 && !ch->player->stats.defusion_knife && ch->player->score >= COSTS_DEFUSION_KNIFE)
+			{
+				ch->player->stats.defusion_knife = true;
+				if (pickup_type == SPICKUP_BUY)
+				{
+					ch->player->score -= COSTS_DEFUSION_KNIFE;
+					char chat_msg[128];
+					str_format (chat_msg, sizeof (chat_msg), "bought DEFUSION_KNIFE, score: %i (-%i)", ch->player->score, COSTS_DEFUSION_KNIFE);
+					game.send_chat_target (ch->player->client_id, chat_msg);
+				}
+				else if (pickup_type == SPICKUP_DROP)
+					game.world.destroy_entity (this);
+				game.create_sound (pos, SOUND_PICKUP_NINJA, cmask_one (ch->player->client_id));
+			}
+			break;
+		}	
+	}
+}
+
+void SPECIAL_PICKUP::snap (int snapping_client)
+{
+	if (!active_timer)
+		return;
+
+	//	dont show buyable pickups if the player doesnt have the money
+	if (pickup_type == SPICKUP_BUY)
+	{
+		if (game.players[snapping_client]->team == -1 || !game.players[snapping_client]->get_character () ||
+			!game.players[snapping_client]->get_character ()->alive)
+			return;
+
+		switch (type)
+		{
+		case POWERUP_ARMOR:
+			if (game.players[snapping_client]->get_character ()->armor == 10 || game.players[snapping_client]->score < COSTS_ARMOR)
+				return;
+			break;
+		case POWERUP_WEAPON:
+			if ((subtype == WEAPON_SHOTGUN || subtype == WEAPON_GRENADE || subtype == WEAPON_RIFLE) &&
+				((game.players[snapping_client]->get_character ()->weapons[subtype].ammo == data->weapons.id[subtype].maxammo &&
+				game.players[snapping_client]->get_character ()->weapons[subtype].got) ||
+				game.players[snapping_client]->score < (game.players[snapping_client]->get_character ()->weapons[subtype].got ? 
+				subtype == WEAPON_SHOTGUN ? COSTS_SHOTGUN_AMMO : subtype == WEAPON_GRENADE ? COSTS_GRENADE_AMMO : COSTS_RIFLE_AMMO :
+				subtype == WEAPON_SHOTGUN ? COSTS_SHOTGUN : subtype == WEAPON_GRENADE ? COSTS_GRENADE : COSTS_RIFLE)))
+				return;
+			break;
+		case POWERUP_NINJA:
+			if (game.players[snapping_client]->team == 0 || game.players[snapping_client]->stats.defusion_knife ||
+				game.players[snapping_client]->score < COSTS_DEFUSION_KNIFE)
+				return;
+			break;
+		}
+	}	
+
+	NETOBJ_PICKUP *pu = static_cast<NETOBJ_PICKUP *> (snap_new_item (NETOBJTYPE_PICKUP, id, sizeof (NETOBJ_PICKUP)));
+	pu->x = static_cast<int> (pos.x);
+	pu->y = static_cast<int> (pos.y);
+	pu->type = type;
+	pu->subtype = subtype;
+}
diff -Nru teeworlds-0.5.2-src/src/game/server/entities/spickup.hpp defusion 0.52/src/game/server/entities/spickup.hpp
--- teeworlds-0.5.2-src/src/game/server/entities/spickup.hpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/entities/spickup.hpp	2009-03-23 15:33:58.000000000 +0000
@@ -0,0 +1,37 @@
+#ifndef GAME_SERVER_ENTITY_SPICKUP_H
+#define GAME_SERVER_ENTITY_SPICKUP_H
+
+#include <game/server/entity.hpp>
+
+enum
+{
+	COSTS_ARMOR = 300,
+	COSTS_SHOTGUN = 300,
+	COSTS_SHOTGUN_AMMO = 150,
+	COSTS_GRENADE = 500,
+	COSTS_GRENADE_AMMO = 250,
+	COSTS_RIFLE = 600,
+	COSTS_RIFLE_AMMO = 300,
+	COSTS_DEFUSION_KNIFE = 150,
+	SPICKUP_BUY = 0,
+	SPICKUP_DROP = 1
+};
+
+class SPECIAL_PICKUP : public ENTITY
+{
+public:
+	static const int phys_size = 14;
+	const int type;
+	const int subtype;
+	const int pickup_type; 
+	const int items;
+	int active_timer;
+	vec2 vel;
+	SPECIAL_PICKUP (int _type, int _subtype, int _pickup_type, vec2 _pos, int _items);
+	
+	virtual void reset ();
+	virtual void tick ();
+	virtual void snap (int snapping_client);
+};
+
+#endif
diff -Nru teeworlds-0.5.2-src/src/game/server/gamecontroller.hpp defusion 0.52/src/game/server/gamecontroller.hpp
--- teeworlds-0.5.2-src/src/game/server/gamecontroller.hpp	2009-10-26 19:04:30.000000000 +0100
+++ defusion 0.52/src/game/server/gamecontroller.hpp	2009-10-27 15:50:37.000000000 +0000
@@ -10,9 +10,9 @@
 */
 class GAMECONTROLLER
 {
+protected:
 	vec2 spawn_points[3][64];
 	int num_spawn_points[3];
-protected:
 	struct SPAWNEVAL
 	{
 		SPAWNEVAL()
diff -Nru teeworlds-0.5.2-src/src/game/server/gamemodes/de.cpp defusion 0.52/src/game/server/gamemodes/de.cpp
--- teeworlds-0.5.2-src/src/game/server/gamemodes/de.cpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/gamemodes/de.cpp	2009-08-10 13:28:19.000000000 +0100
@@ -0,0 +1,610 @@
+#include <string.h>
+#include <engine/e_config.h>
+#include <engine/e_server_interface.h>
+#include <game/mapitems.hpp>
+#include <game/server/gamecontext.hpp>
+#include <game/server/entities/spickup.hpp>
+#include "de.hpp"
+
+GAMECONTROLLER_DE::GAMECONTROLLER_DE ()
+{
+	bomb = 0;
+	explosion_timer = -1;
+	arm_pos = vec2 (0,0);
+	update_timer = 0;
+	gametype = "DE";
+	game_flags = GAMEFLAG_TEAMS | GAMEFLAG_FLAGS;
+}
+
+bool GAMECONTROLLER_DE::on_entity (int index, vec2 pos)
+{
+	switch (index)
+	{
+	case ENTITY_SPAWN:
+	case ENTITY_SPAWN_RED:
+	case ENTITY_SPAWN_BLUE: GAMECONTROLLER::on_entity (index, pos); break;
+	case ENTITY_ARMOR_1: game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_ARMOR, 0, SPICKUP_BUY, pos, 10)); break;
+	case ENTITY_WEAPON_SHOTGUN: game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_WEAPON, WEAPON_SHOTGUN, SPICKUP_BUY, pos, data->weapons.id[WEAPON_SHOTGUN].maxammo)); break;
+	case ENTITY_WEAPON_GRENADE: game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_WEAPON, WEAPON_GRENADE, SPICKUP_BUY, pos, data->weapons.id[WEAPON_GRENADE].maxammo)); break;
+	case ENTITY_WEAPON_RIFLE: game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_WEAPON, WEAPON_RIFLE, SPICKUP_BUY, pos, data->weapons.id[WEAPON_RIFLE].maxammo)); break;
+	case ENTITY_POWERUP_NINJA: game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_NINJA, 0, SPICKUP_BUY, pos, 0)); break;
+	case ENTITY_FLAGSTAND_RED: bombspots[0] = BOMBSPOT (pos, true); break;
+	case ENTITY_FLAGSTAND_BLUE: bombspots[1] = BOMBSPOT (pos, true); break;
+	default: return false;
+	}
+
+	return true;
+}
+
+int GAMECONTROLLER_DE::on_character_death (class CHARACTER *victim, class PLAYER *killer, int weaponid)
+{
+	// do scoreing
+	if (killer && killer->get_character () != victim)
+	{
+		char chat_msg[128];
+		if (victim->team == killer->team)
+		{
+			killer->score += SCORE_TEAMKILL;	// teamkill
+			str_format (chat_msg, sizeof (chat_msg), "killed a teammate, score: %i (%i)", killer->score, SCORE_TEAMKILL);
+		}
+		else
+		{
+			killer->score += SCORE_KILL;	// normal kill
+			str_format (chat_msg, sizeof (chat_msg), "killed an enemy, score: %i (+%i)", killer->score, SCORE_KILL);
+		}
+		game.send_chat_target (killer->client_id, chat_msg);
+	}
+
+	//	drop active "main"-weapon
+	if (victim->active_weapon == WEAPON_SHOTGUN || victim->active_weapon == WEAPON_GRENADE || victim->active_weapon == WEAPON_RIFLE)
+		game.world.insert_entity (new SPECIAL_PICKUP (POWERUP_WEAPON, victim->active_weapon, SPICKUP_DROP, victim->pos, victim->weapons[victim->active_weapon].ammo));
+
+	--victim->player->stats.lifes;
+
+	if (!bomb || !bomb->carrying_character)
+		return 0;
+
+	int had_flag = 0;
+	if (killer && bomb->carrying_character == killer->get_character())
+		had_flag |= 2;
+	if (bomb->carrying_character == victim)
+	{
+		//	drop bomb
+		if (bomb->team == 0)
+			game.create_sound_global (SOUND_CTF_DROP);
+		if (bomb->activating_timer != -1 || bomb->deactivating_timer != -1)
+			game.send_broadcast ("", victim->player->client_id);
+		bomb->carrying_character = 0;
+		bomb->vel = vec2 (0, 0);
+
+		had_flag |= 1;
+	}
+
+	return had_flag;
+}
+
+void GAMECONTROLLER_DE::on_character_spawn (class CHARACTER *chr)
+{
+	chr->health = 10;
+	chr->armor = chr->player->stats.armor;	
+	chr->weapons[WEAPON_HAMMER].got = chr->player->stats.got[WEAPON_HAMMER];
+	chr->weapons[WEAPON_HAMMER].ammo = chr->player->stats.ammo[WEAPON_HAMMER];
+	chr->weapons[WEAPON_GUN].got = chr->player->stats.got[WEAPON_GUN];
+	chr->weapons[WEAPON_GUN].ammo = chr->player->stats.ammo[WEAPON_GUN];
+	chr->weapons[WEAPON_SHOTGUN].got = chr->player->stats.got[WEAPON_SHOTGUN];
+	chr->weapons[WEAPON_SHOTGUN].ammo = chr->player->stats.ammo[WEAPON_SHOTGUN];
+	chr->weapons[WEAPON_GRENADE].got = chr->player->stats.got[WEAPON_GRENADE];
+	chr->weapons[WEAPON_GRENADE].ammo = chr->player->stats.ammo[WEAPON_GRENADE];
+	chr->weapons[WEAPON_RIFLE].got = chr->player->stats.got[WEAPON_RIFLE];
+	chr->weapons[WEAPON_RIFLE].ammo = chr->player->stats.ammo[WEAPON_RIFLE];
+	chr->player->score += SCORE_SPAWN;
+}
+
+void GAMECONTROLLER_DE::tick()
+{
+	if (game_over_tick != -1)
+	{
+		//	game over.. wait for restart
+		if ((round_count + 1) == config.sv_rounds_per_map)
+		{
+			if (server_tick () > game_over_tick + server_tickspeed () * 10)
+			{
+				cyclemap();
+				startround();
+				teamscore[0] = 0;
+				teamscore[1] = 0;
+				round_count = 0;
+			}
+		}	//	round over.. wait for next one
+		else if (server_tick () > game_over_tick + server_tickspeed () * 5)
+		{
+			game.send_broadcast	("", -1);
+			startround();
+			round_count++;
+		}
+	}
+	else
+	{
+		do_wincheck ();
+
+		//	create a bomb and put it on a dm spawn point
+		if (!bomb)
+		{
+			if (num_spawn_points[0])
+			{
+				explosion_timer = -1;
+				game.world.insert_entity (bomb = new BOMB (spawn_points[0][server_tick () % num_spawn_points[0]]));
+			}
+		}
+		else
+		{
+			//	update bomb position
+			if (bomb->team == 0 && bomb->carrying_character)
+				bomb->pos = bomb->carrying_character->pos;
+			else
+			{	
+				bomb->vel.y += game.world.core.tuning.gravity;
+				move_box(&bomb->pos, &bomb->vel, vec2 (bomb->phys_size, bomb->phys_size), 0.5f);
+
+				// bomb hits death-tile, back to start
+				if (col_get (static_cast<int> (bomb->pos.x), static_cast<int> (bomb->pos.y)) & COLFLAG_DEATH)
+				{
+					bomb->reset ();
+					bomb = 0;
+					return;
+				}
+			}
+
+			//	update bomb explosion timer process
+			if (explosion_timer != -1 && !(--explosion_timer % server_tickspeed ()))
+			{
+				if (!explosion_timer)
+				{
+					//	explosion
+					for (int i = 0; i < MAX_CLIENTS; ++i)
+					{
+						if (!game.players[i] || game.players[i]->team == -1 || !game.players[i]->get_character () ||
+							!game.players[i]->get_character ()->alive)
+							continue;
+
+						if (!explosion_timer && distance (bomb->pos, game.players[i]->get_character ()->pos) <= (config.sv_de_bombspot_size << 1))
+							game.players[i]->kill_character (WEAPON_GAME);
+					}
+					game.create_sound_global (SOUND_GRENADE_EXPLODE, -1);
+				}
+				else
+					game.create_sound (bomb->pos, SOUND_WEAPON_NOAMMO, cmask_all());
+			}
+
+			if (bomb->carrying_character)
+			{
+				if (bomb->team == 0)
+				{
+					//	check if player still in a bombing zone
+					if ((bombspots[0].enabled && distance (bomb->pos, bombspots[0].pos) <= config.sv_de_bombspot_size) ||
+						(bombspots[1].enabled && distance (bomb->pos, bombspots[1].pos) <= config.sv_de_bombspot_size))
+					{
+						if (bomb->activating_timer > 0)
+						{
+							//	update arming process
+							if (distance (bomb->pos, arm_pos) > 32.0f + bomb->carrying_character->phys_size)
+							{
+								if (bomb->activating_timer <= (config.sv_de_activating_time - 1) * server_tickspeed ())
+								{
+									update_timer = 10 * server_tickspeed ();
+									char bc_msg [128];
+									str_format (bc_msg, sizeof (bc_msg), "Bomb armed in %i seconds", config.sv_de_activating_time);
+									game.send_broadcast (bc_msg, bomb->carrying_character->player->client_id);
+								}
+								bomb->activating_timer = config.sv_de_activating_time * server_tickspeed ();
+								arm_pos = bomb->pos;
+							}
+
+							if (!--bomb->activating_timer)
+							{
+								//	bomb is armed
+								bomb->carrying_character->player->score += SCORE_ACTIVATE_BOMB;
+								bomb->team = 1;
+								bomb->vel = vec2 (0, 0);
+								bomb->activating_timer = -1;
+								explosion_timer = config.sv_de_explosion_time * server_tickspeed ();
+								game.send_broadcast ("Teerrorists armed the bomb", -1);
+								char chat_msg[128];
+								str_format (chat_msg, sizeof (chat_msg), "armed the bomb, score: %i (+%i)", bomb->carrying_character->player->score, SCORE_ACTIVATE_BOMB);
+								game.send_chat_target (bomb->carrying_character->player->client_id, chat_msg);
+								game.create_sound_global (SOUND_CHAT_SERVER, -1);
+								bomb->carrying_character = 0;
+							}
+							else if (!(bomb->activating_timer % server_tickspeed ()) || !--update_timer)
+							{	//	update arming broadcast
+								char bc_msg [128];
+								str_format (bc_msg, sizeof (bc_msg), "Bomb armed in %i seconds", bomb->activating_timer / server_tickspeed ());
+								game.send_broadcast (bc_msg, bomb->carrying_character->player->client_id);
+								game.create_sound_global (SOUND_RIFLE_BOUNCE, bomb->carrying_character->player->client_id);
+								update_timer = 10 * server_tickspeed ();
+							}		
+						}
+						else
+						{
+							//	start arming the bomb
+							bomb->activating_timer = config.sv_de_activating_time * server_tickspeed ();
+							arm_pos = bomb->pos;
+							update_timer = 10 * server_tickspeed ();
+							char bc_msg [128];
+							str_format (bc_msg, sizeof (bc_msg), "Bomb armed in %i seconds", config.sv_de_activating_time);
+							game.send_broadcast (bc_msg, bomb->carrying_character->player->client_id);
+							game.create_sound_global (SOUND_WEAPON_SWITCH, bomb->carrying_character->player->client_id);
+						}
+					}
+					else if (bomb->activating_timer != -1)	
+					{
+						//	stop arming the bomb
+						bomb->activating_timer = -1;
+						game.send_broadcast ("", bomb->carrying_character->player->client_id);
+						game.create_sound_global (SOUND_WEAPON_SPAWN, bomb->carrying_character->player->client_id);
+					}
+				}
+				else if (bomb->team == 1)
+				{
+					//	check if player still near/defusing the bomb
+					if (distance (bomb->pos, bomb->carrying_character->pos) <= 32.0f + bomb->carrying_character->phys_size)
+					{
+						//	update defusing process
+						--bomb->deactivating_timer;
+						if (!bomb->deactivating_timer)
+						{	
+							//	bomb defused
+							explosion_timer = -1;
+							bomb->carrying_character->player->score += SCORE_DEACTIVATE_BOMB;
+							char chat_msg[128];
+							str_format (chat_msg, sizeof (chat_msg), "defused the bomb, score: %i (+%i)", bomb->carrying_character->player->score, SCORE_DEACTIVATE_BOMB);
+							game.send_chat_target (bomb->carrying_character->player->client_id, chat_msg);
+						}
+						else if (!(bomb->deactivating_timer % server_tickspeed ()))
+						{
+							char bc_msg [128];
+							str_format (bc_msg, sizeof (bc_msg), "Bomb defused in %i seconds", bomb->deactivating_timer / server_tickspeed ());
+							game.send_broadcast (bc_msg, bomb->carrying_character->player->client_id);
+							game.create_sound_global (SOUND_HOOK_NOATTACH, bomb->carrying_character->player->client_id);
+						}
+					}
+					else
+					{
+						//	stop defusinging process
+						bomb->deactivating_timer = -1;
+						game.send_broadcast ("", bomb->carrying_character->player->client_id);
+						game.create_sound_global (SOUND_WEAPON_SPAWN, bomb->carrying_character->player->client_id);
+						bomb->carrying_character = 0;
+					}
+				}
+			}
+			else
+			{
+				//	find all players that are near the bomb
+				CHARACTER *close_characters[MAX_CLIENTS];
+				const int max_num = game.world.find_entities (bomb->pos, 32.0f, reinterpret_cast<ENTITY**> (close_characters), MAX_CLIENTS, NETOBJTYPE_CHARACTER);
+				int curr_num = 0;
+				for (int i = 0; i < max_num; ++i)
+				{
+					if (!close_characters[i]->alive || close_characters[i]->team != bomb->team || col_intersect_line (bomb->pos, close_characters[i]->pos, NULL, NULL))
+						continue;
+					close_characters[curr_num++] = close_characters[i];
+				}
+				if (curr_num)
+				{
+					bomb->carrying_character = close_characters[server_tick () % curr_num];
+					if (bomb->team == 0)
+					{
+						//take the bomb
+						bomb->activating_timer = -1;
+						game.create_sound_global (SOUND_CTF_GRAB_PL, bomb->carrying_character->player->client_id);
+					}
+					else
+					{
+						//	start defusing the bomb
+						bomb->deactivating_timer = config.sv_de_deactivating_time * (bomb->carrying_character->player->stats.defusion_knife ? server_tickspeed () / 2 : server_tickspeed ());
+						char bc_msg [128];
+						str_format (bc_msg, sizeof (bc_msg), "Bomb defused in %.2f seconds", static_cast<float> (bomb->deactivating_timer) / static_cast<float> (server_tickspeed ()));
+						game.send_broadcast (bc_msg, bomb->carrying_character->player->client_id);
+						game.create_sound_global (SOUND_WEAPON_SWITCH, bomb->carrying_character->player->client_id);
+					}
+				}
+			}
+		}		
+	}
+
+	// update browse info
+	server_setbrowseinfo (gametype, !round_count ? 0 : round_count * 100 / config.sv_rounds_per_map);
+}
+
+void GAMECONTROLLER_DE::snap (int snapping_client)
+{
+	NETOBJ_GAME *gameobj = static_cast<NETOBJ_GAME *> (snap_new_item (NETOBJTYPE_GAME, 0, sizeof (NETOBJ_GAME)));
+	gameobj->paused = game.world.paused;
+	gameobj->game_over = game_over_tick != -1  && (round_count + 1) == config.sv_rounds_per_map ? 1 : 0;
+	gameobj->sudden_death = sudden_death;
+	
+	gameobj->score_limit = 0;
+	gameobj->time_limit = config.sv_timelimit;
+	gameobj->round_start_tick = 
+		game_over_tick != -1 ? server_tick () - config.sv_timelimit * 60 * server_tickspeed () :
+		explosion_timer == -1 ? round_start_tick : 
+		config.sv_timelimit ? server_tick () + explosion_timer - config.sv_timelimit * 60 * server_tickspeed () :
+		server_tick () - (explosion_timer + server_tickspeed () - 1);
+	gameobj->flags = game_flags;
+	
+	gameobj->warmup = 0;
+	
+	gameobj->round_num = config.sv_rounds_per_map;
+	gameobj->round_current = round_count + 1;
+	
+	
+	if (snapping_client == -1)
+	{
+		// we are recording a demo, just set the scores
+		gameobj->teamscore_red = teamscore[0];
+		gameobj->teamscore_blue = teamscore[1];
+	}
+	else
+	{
+		// TODO: this little hack should be removed
+		gameobj->teamscore_red = is_teamplay () ? teamscore[0] : game.players[snapping_client]->score;
+		gameobj->teamscore_blue = teamscore[1];
+	}
+}
+
+bool GAMECONTROLLER_DE::can_spawn (PLAYER *player, vec2 *out_pos)
+{
+	return player->stats.lifes > 0 ? GAMECONTROLLER::can_spawn (player, out_pos) : false;
+}
+
+void GAMECONTROLLER_DE::post_reset ()
+{
+	for(int i = 0; i < MAX_CLIENTS; i++)
+		if(game.players[i])
+		{
+			game.players[i]->respawn ();
+			if (!game.players[i]->stats.lifes)
+				game.players[i]->stats.lifes = 1;
+		}
+	bomb = 0;
+}
+
+void GAMECONTROLLER_DE::do_wincheck ()
+{
+	int win = WIN_T_DEAD | WIN_CT_DEAD;
+
+	//	check if bombing team has players who are alive
+	bool has_people[2] = {false, false};
+	for (int i = 0; i < MAX_CLIENTS; ++i)
+	{
+		if (game.players[i] && game.players[i]->team != -1)
+		{
+			has_people[game.players[i]->team] = true;
+			if (game.players[i]->stats.lifes)
+				win &= game.players[i]->team == 0 ? WIN_CT_DEAD : WIN_T_DEAD;
+		}
+	}
+	if (win & WIN_T_DEAD && (!has_people[0] || explosion_timer != -1))
+		win &= WIN_CT_DEAD;
+	if (win & WIN_CT_DEAD && !has_people[1])
+		win &= WIN_T_DEAD;
+
+	//	check if the bomb exploded
+	if (!explosion_timer)
+		win |= WIN_EXPLOSION;
+
+	//	check if the bomb got defused
+	if (bomb && !bomb->deactivating_timer)
+		win |= WIN_DEFUSE;
+
+	//	check if times up
+	if (explosion_timer == -1 && config.sv_timelimit > 0 && server_tick () - round_start_tick >= config.sv_timelimit * server_tickspeed() * 60)
+		win |= WIN_TIMESUP;
+
+	if (win)
+		endround (!has_people[0] && !has_people[1] ? 0 : win);		//	no win/score on an empty server
+}
+
+void GAMECONTROLLER_DE::startround ()
+{
+	game.world.reset_requested = true;
+
+	const bool switch_team = config.sv_de_switch_team_rounds ? !((round_count + 1) % config.sv_de_switch_team_rounds) : 0;
+	for(int i = 0; i < MAX_CLIENTS; i++)
+	{
+		if(game.players[i])
+		{
+			game.players[i]->stats.got[WEAPON_HAMMER] = true;
+			game.players[i]->stats.ammo[WEAPON_HAMMER] = -1;
+			game.players[i]->stats.got[WEAPON_GUN] = true;
+			game.players[i]->stats.ammo[WEAPON_GUN] = 10;
+			if (switch_team && game.players[i]->team != -1)
+			{
+				game.players[i]->team ^= 1;
+				game.players[i]->score = 0;
+			}
+			if (game.players[i]->team == -1 || !game.players[i]->stats.lifes || switch_team)
+			{
+				game.players[i]->stats.armor = 0;
+				game.players[i]->stats.got[WEAPON_SHOTGUN] = false;
+				game.players[i]->stats.ammo[WEAPON_SHOTGUN] = 0;
+				game.players[i]->stats.got[WEAPON_GRENADE] = false;
+				game.players[i]->stats.ammo[WEAPON_GRENADE] = 0;
+				game.players[i]->stats.got[WEAPON_RIFLE] = false;
+				game.players[i]->stats.ammo[WEAPON_RIFLE] = 0;
+				game.players[i]->stats.defusion_knife = false;
+			}
+			else
+			{
+				game.players[i]->stats.armor = game.players[i]->get_character ()->armor;
+				game.players[i]->stats.got[WEAPON_SHOTGUN] = game.players[i]->get_character ()->weapons[WEAPON_SHOTGUN].got;
+				game.players[i]->stats.ammo[WEAPON_SHOTGUN] = game.players[i]->get_character ()->weapons[WEAPON_SHOTGUN].ammo;
+				game.players[i]->stats.got[WEAPON_GRENADE] = game.players[i]->get_character ()->weapons[WEAPON_GRENADE].got;
+				game.players[i]->stats.ammo[WEAPON_GRENADE] = game.players[i]->get_character ()->weapons[WEAPON_GRENADE].ammo;
+				game.players[i]->stats.got[WEAPON_RIFLE] = game.players[i]->get_character ()->weapons[WEAPON_RIFLE].got;
+				game.players[i]->stats.ammo[WEAPON_RIFLE] = game.players[i]->get_character ()->weapons[WEAPON_RIFLE].ammo;
+			}
+		}
+	}
+	if (switch_team)
+	{
+		game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "Teams got switched");
+		const int temp = teamscore[0];
+		teamscore[0] = teamscore[1];
+		teamscore[1] = temp;
+	}
+	
+	round_start_tick = server_tick ();
+	game_over_tick = -1;
+	game.world.paused = false;
+
+	// do team-balancing
+	if (config.sv_teambalance_time)
+	{
+		int t[2] = {0,0};
+		int tscore[2] = {0,0};
+		for (int i = 0; i < MAX_CLIENTS; i++)
+		{
+			if(game.players[i] && game.players[i]->team != -1)
+			{
+				t[game.players[i]->team]++;
+				tscore[game.players[i]->team] += game.players[i]->score;
+			}
+		}
+		
+		// are teams unbalanced?
+		if (abs (t[0]-t[1]) >= 2)
+		{
+			int m = (t[0] > t[1]) ? 0 : 1;
+			int num_balance = abs (t[0]-t[1]) / 2;
+			
+			do
+			{
+				PLAYER *p = 0;
+				int pd = tscore[m];
+				for (int i = 0; i < MAX_CLIENTS; i++)
+				{
+					if (!game.players[i])
+						continue;
+					
+					// remember the player who would cause lowest score-difference
+					if (game.players[i]->team == m && (!p || abs((tscore[m^1]+game.players[i]->score) - (tscore[m]-game.players[i]->score)) < pd))
+					{
+						p = game.players[i];
+						pd = abs((tscore[m^1]+p->score) - (tscore[m]-p->score));
+					}
+				}
+				
+				// move the player to other team without losing his score
+				// TODO: change in player::set_team needed: player won't lose score on team-change
+				int score_before = p->score;
+				p->set_team (m^1);
+				p->score = score_before;
+			} 
+			while (--num_balance);
+			
+			game.send_chat(-1, GAMECONTEXT::CHAT_ALL, "Teams have been balanced");
+		}
+	}
+	explosion_timer = -1;
+	dbg_msg ("game","start round type='%s' teamplay='%d'", gametype, game_flags & GAMEFLAG_TEAMS);
+}
+
+void GAMECONTROLLER_DE::endround (int win_condition)
+{
+	int win_team = 0;
+
+	//	process win conditions
+	if (win_condition & WIN_DEFUSE)
+	{
+		++teamscore[1];
+		win_team = 1;
+		if ((round_count + 1) != config.sv_rounds_per_map)
+		{
+			game.send_broadcast	(
+				"    ***  Round Over  *** \n"
+				"Counter-Teerrorists Win\n"
+				"     Bomb was defused   "
+				, -1);
+		}
+		else
+			game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "Counter-Teerrorists defused the Bomb");
+	}
+	else if (win_condition & WIN_EXPLOSION)
+	{
+		++teamscore[0];
+		win_team = 0;
+		if ((round_count + 1) != config.sv_rounds_per_map)
+		{
+			game.send_broadcast	(
+				"***  Round Over  ***\n"
+				"   Teerrorists Win   \n"
+				"   Bomb exploded    "
+				, -1);
+		}
+		else
+			game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "Teerrorists detonated the bomb");
+	}
+	else if (win_condition & WIN_TIMESUP)
+	{
+		++teamscore[1];
+		win_team = 1;
+		if ((round_count + 1) != config.sv_rounds_per_map)
+		{
+			game.send_broadcast	(
+				"    ***  Round Over  *** \n"
+				"Counter-Teerrorists Win\n"
+				"      Bomb not planted   "
+				, -1);
+		}
+		else
+			game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "Teerrorists couldn't plant the bomb");
+	}
+	else if (win_condition & WIN_T_DEAD)
+	{
+		++teamscore[1];
+		win_team = 1;
+		if ((round_count + 1) != config.sv_rounds_per_map)
+		{
+			game.send_broadcast	(
+				"    ***  Round Over  *** \n"
+				"Counter-Teerrorists Win \n"
+				" All Teerrorists are dead"
+				, -1);
+		}
+		else
+			game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "All Teerrorists are dead");
+	}
+	else if (win_condition & WIN_CT_DEAD)
+	{
+		++teamscore[0];
+		win_team = 0;
+		if ((round_count + 1) != config.sv_rounds_per_map)
+		{
+			game.send_broadcast	(
+				"            ***   Round Over  ***         \n"
+				"                Teerrorists Win            \n"
+				"All Counter-Teerrorists are dead"
+				, -1);
+		}
+		else
+			game.send_chat (-1, GAMECONTEXT::CHAT_ALL, "All Counter-Teerrorists are dead");
+	}
+
+	if ((round_count + 1) == config.sv_rounds_per_map)
+		game.send_broadcast	("", -1);
+
+	for (int i = 0; i < MAX_CLIENTS; ++i)
+	{
+		if (!game.players[i] || game.players[i]->team == -1)
+			continue;
+		
+		if (game.players[i]->team == win_team)
+			game.players[i]->score += SCORE_WIN;
+	}
+
+	game.create_sound_global (SOUND_CTF_CAPTURE, -1);
+	game.world.paused = true;
+	game_over_tick = server_tick ();
+}
diff -Nru teeworlds-0.5.2-src/src/game/server/gamemodes/de.hpp defusion 0.52/src/game/server/gamemodes/de.hpp
--- teeworlds-0.5.2-src/src/game/server/gamemodes/de.hpp	1970-01-01 01:00:00.000000000 +0100
+++ defusion 0.52/src/game/server/gamemodes/de.hpp	2009-03-23 15:34:17.000000000 +0000
@@ -0,0 +1,51 @@
+#include <game/server/gamecontroller.hpp>
+#include <game/server/entities/bomb.hpp>
+
+enum
+{
+	NUM_BOMBSPOTS = 2,
+	SCORE_SPAWN = 400,
+	SCORE_WIN = 500,
+	SCORE_KILL = 100,
+	SCORE_TEAMKILL = -200,
+	SCORE_ACTIVATE_BOMB = 150,
+	SCORE_DEACTIVATE_BOMB = 150,
+	WIN_T_DEAD = 1,
+	WIN_CT_DEAD = 2,
+	WIN_EXPLOSION = 4,
+	WIN_DEFUSE = 8,
+	WIN_TIMESUP = 16
+};
+
+class GAMECONTROLLER_DE : public GAMECONTROLLER
+{
+public:
+	class BOMB *bomb;
+	struct BOMBSPOT
+	{
+		BOMBSPOT (vec2 _pos = vec2 (0, 0), bool activate = false)
+		{
+			pos = _pos;
+			enabled = activate;
+		}
+		vec2 pos;
+		bool enabled;
+	};
+	BOMBSPOT bombspots [NUM_BOMBSPOTS];
+	int explosion_timer;
+	vec2 arm_pos;
+	int update_timer;
+	
+	GAMECONTROLLER_DE ();
+	virtual void tick ();
+	
+	virtual bool on_entity (int index, vec2 pos);
+	virtual int on_character_death (class CHARACTER *victim, class PLAYER *killer, int weapon);
+	virtual void on_character_spawn (class CHARACTER *chr);
+	virtual void snap (int snapping_client);
+	virtual bool can_spawn (PLAYER *player, vec2 *out_pos);
+	virtual void post_reset ();
+	void do_wincheck ();
+	void startround ();
+	void endround (int win_condition);
+};
diff -Nru teeworlds-0.5.2-src/src/game/server/hooks.cpp defusion 0.52/src/game/server/hooks.cpp
--- teeworlds-0.5.2-src/src/game/server/hooks.cpp	2009-10-26 19:04:30.000000000 +0100
+++ defusion 0.52/src/game/server/hooks.cpp	2009-10-27 15:53:12.000000000 +0000
@@ -22,6 +22,7 @@
 #include "gamemodes/tdm.hpp"
 #include "gamemodes/ctf.hpp"
 #include "gamemodes/mod.hpp"
+#include "gamemodes/de.hpp"
 
 TUNING_PARAMS tuning;
 
@@ -531,7 +532,9 @@
 	//players = new PLAYER[MAX_CLIENTS];
 
 	// select gametype
-	if(strcmp(config.sv_gametype, "mod") == 0)
+	if(strcmp(config.sv_gametype, "de") == 0)
+		game.controller = new GAMECONTROLLER_DE;
+	else if(strcmp(config.sv_gametype, "mod") == 0)
 		game.controller = new GAMECONTROLLER_MOD;
 	else if(strcmp(config.sv_gametype, "ctf") == 0)
 		game.controller = new GAMECONTROLLER_CTF;
diff -Nru teeworlds-0.5.2-src/src/game/server/player.cpp defusion 0.52/src/game/server/player.cpp
--- teeworlds-0.5.2-src/src/game/server/player.cpp	2009-10-26 19:04:30.000000000 +0100
+++ defusion 0.52/src/game/server/player.cpp	2009-10-27 15:55:20.000000000 +0000
@@ -62,6 +62,9 @@
 	}
 	else if(spawning && respawn_tick <= server_tick())
 		try_respawn();
+
+	if (!game.world.paused && !stats.lifes && die_tick + server_tickspeed () == server_tick ())
+		game.send_broadcast ("You died, wait for next round to respawn", client_id);
 }
 
 void PLAYER::snap(int snapping_client)
@@ -80,7 +83,7 @@
 	info->local = 0;
 	info->cid = client_id;
 	info->score = score;
-	info->team = team;
+	info->team = !stats.lifes && die_tick + server_tickspeed () <= server_tick () ? -1 : team;
 
 	if(client_id == snapping_client)
 		info->local = 1;	
@@ -115,7 +118,7 @@
 	if(!chr && team >= 0 && (new_input->fire&1))
 		spawning = true;
 	
-	if(!chr && team == -1)
+	if(!chr && (!stats.lifes || team == -1))
 		view_pos = vec2(new_input->target_x, new_input->target_y);
 }
 
diff -Nru teeworlds-0.5.2-src/src/game/server/player.hpp defusion 0.52/src/game/server/player.hpp
--- teeworlds-0.5.2-src/src/game/server/player.hpp	2009-10-26 19:04:30.000000000 +0100
+++ defusion 0.52/src/game/server/player.hpp	2009-10-27 15:56:09.000000000 +0000
@@ -70,6 +70,32 @@
 	void on_direct_input(NETOBJ_PLAYER_INPUT *new_input);
 	void on_predicted_input(NETOBJ_PLAYER_INPUT *new_input);
 	void on_disconnect();
+
+	//	defusion
+	struct STATS
+	{
+		STATS ()
+		{
+			lifes = 1;
+			armor = 0;
+			ammo[0] = -1;
+			ammo[1] = 10;
+			ammo[2] = 0;
+			ammo[3] = 0;
+			ammo[4] = 0;
+			got[0] = true;
+			got[1] = true;
+			got[2] = false;
+			got[3] = false;
+			got[4] = false;
+			defusion_knife = false;
+		}
+		int lifes;
+		int armor;
+		int ammo[NUM_WEAPONS - 1];
+		bool got[NUM_WEAPONS - 1];
+		bool defusion_knife;
+	} stats;
 };
 
 #endif
diff -Nru teeworlds-0.5.2-src/src/game/variables.hpp defusion 0.52/src/game/variables.hpp
--- teeworlds-0.5.2-src/src/game/variables.hpp	2009-10-26 19:04:30.000000000 +0100
+++ defusion 0.52/src/game/variables.hpp	2009-10-27 15:56:45.000000000 +0000
@@ -65,6 +65,14 @@
 MACRO_CONFIG_INT(sv_vote_scorelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change score limit")
 MACRO_CONFIG_INT(sv_vote_timelimit, 0, 0, 1, CFGFLAG_SERVER, "Allow voting to change time limit")
 
+/*	defuse	*/
+MACRO_CONFIG_INT(sv_de_bombspot_size, 480, 32, 2024, CFGFLAG_SERVER, "Size of the bomb spots")
+MACRO_CONFIG_INT(sv_de_activating_time, 5, 1, 60, CFGFLAG_SERVER, "Time needed to activate the bomb")
+MACRO_CONFIG_INT(sv_de_deactivating_time, 10, 1, 60, CFGFLAG_SERVER, "Time needed to deactivate the bomb")
+MACRO_CONFIG_INT(sv_de_explosion_time, 30, 1, 120, CFGFLAG_SERVER, "Time till the bomb explodes")
+MACRO_CONFIG_INT(sv_de_shop_time, 30, 0, 1000, CFGFLAG_SERVER, "Time players can buy weapons in the shop")
+MACRO_CONFIG_INT(sv_de_switch_team_rounds, 10, 0, 100, CFGFLAG_SERVER, "Number of rounds after which the teams are switched")
+
 /* debug */
 #ifdef CONF_DEBUG /* this one can crash the server if not used correctly */
 	MACRO_CONFIG_INT(dbg_dummies, 0, 0, 15, CFGFLAG_SERVER, "")
