about summary refs log tree commit diff
path: root/src/game
diff options
context:
space:
mode:
authorMagnus Auvinen <magnus.auvinen@gmail.com>2007-08-14 18:37:16 +0000
committerMagnus Auvinen <magnus.auvinen@gmail.com>2007-08-14 18:37:16 +0000
commit2cde04ddcec3f3c083527c464f93bf8c30b6e790 (patch)
tree2666b20bf713f7d5244af1aec9f2d2f54d193f35 /src/game
parent8809084d253be4e9923307a13c8830c593dfefc0 (diff)
downloadzcatch-2cde04ddcec3f3c083527c464f93bf8c30b6e790.tar.gz
zcatch-2cde04ddcec3f3c083527c464f93bf8c30b6e790.zip
merged over all stuff from 0.2 to trunk
Diffstat (limited to 'src/game')
-rw-r--r--src/game/client/game_client.cpp150
-rw-r--r--src/game/client/menu.cpp28
-rw-r--r--src/game/server/game_server.cpp156
-rw-r--r--src/game/server/game_server.h8
4 files changed, 256 insertions, 86 deletions
diff --git a/src/game/client/game_client.cpp b/src/game/client/game_client.cpp
index dc09815f..52a58ad5 100644
--- a/src/game/client/game_client.cpp
+++ b/src/game/client/game_client.cpp
@@ -61,10 +61,10 @@ void snd_play_random(int setid, float vol, float pan)
 }
 
 // sound volume tweak
-static const float stereo_separation = 0.01f;
-static const float stereo_separation_deadzone = 512.0f;
-static const float volume_distance_falloff = 100.0f;
-static const float volume_distance_deadzone = 512.0f;
+static const float stereo_separation = 0.001f;
+static const float stereo_separation_deadzone = 200.0f;
+static const float volume_distance_falloff = 200.0f;
+static const float volume_distance_deadzone = 320.0f;
 static const float volume_gun = 0.5f;
 static const float volume_tee = 0.5f;
 static const float volume_hit = 0.5f;
@@ -434,16 +434,59 @@ static int killmsg_current = 0;
 
 extern unsigned char internal_data[];
 
+
+extern void draw_round_rect(float x, float y, float w, float h, float r);
+extern int render_popup(const char *caption, const char *text, const char *button_text);
+
+static void render_loading(float percent)
+{
+	gfx_clear(0.65f,0.78f,0.9f);
+	gfx_mapscreen(0,0,800.0f,600.0f);
+
+	float tw;
+
+	float w = 700;
+	float h = 200;
+	float x = 800/2-w/2;
+	float y = 600/2-h/2;
+
+	gfx_blend_normal();
+	
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_quads_setcolor(0,0,0,0.50f);
+	draw_round_rect(x, y, w, h, 40.0f);
+	gfx_quads_end();
+
+	const char *caption = "Loading";
+	
+	tw = gfx_pretty_text_width(48.0f, caption);
+	ui_do_label(x+w/2-tw/2, y+20, caption, 48.0f);
+
+	gfx_texture_set(-1);
+	gfx_quads_begin();
+	gfx_quads_setcolor(1,1,1,1.0f);
+	draw_round_rect(x+40, y+h-75, (w-80)*percent, 25, 5.0f);
+	gfx_quads_end();
+
+	gfx_swap();
+}
+
 void modc_init()
 {
 	// load the data container
 	data = load_data_from_memory(internal_data);
 
 	// TODO: should be removed
-	music_menu = snd_load_wav("data/audio/menu_music.wav");
+	music_menu = snd_load_wav("data/audio/music_menu.wav");
+
+	float total = data->num_sounds+data->num_images;
+	float current = 0;
 
 	// load sounds
 	for(int s = 0; s < data->num_sounds; s++)
+	{
+		render_loading(current/total);
 		for(int i = 0; i < data->sounds[s].num_sounds; i++)
 		{
 			int id;
@@ -454,10 +497,17 @@ void modc_init()
 
 			data->sounds[s].sounds[i].id = id;
 		}
+		
+		current++;
+	}
 	
 	// load textures
 	for(int i = 0; i < data->num_images; i++)
+	{
+		render_loading(current/total);
 		data->images[i].id = gfx_load_texture(data->images[i].filename);
+		current++;
+	}
 }
 
 void modc_entergame()
@@ -483,13 +533,15 @@ void modc_shutdown()
 {
 }
 
-void modc_newsnapshot()
+static bool must_process_events = false;
+
+static void process_events(int s)
 {
-	int num = snap_num_items(SNAP_CURRENT);
+	int num = snap_num_items(s);
 	for(int i = 0; i < num; i++)
 	{
 		snap_item item;
-		const void *data = snap_get_item(SNAP_CURRENT, i, &item);
+		const void *data = snap_get_item(s, i, &item);
 		
 		if(item.type == EVENT_DAMAGEINDICATION)
 		{
@@ -618,6 +670,26 @@ void modc_newsnapshot()
 			}
 		}
 	}
+	
+	must_process_events = false;
+}
+
+void modc_newsnapshot()
+{
+	if(must_process_events)
+		process_events(SNAP_PREV);
+	must_process_events = true;
+	
+	if(config.stress)
+	{
+		if((client_tick()%250) == 0)
+		{
+			msg_pack_start(MSG_SAY, MSGFLAG_VITAL);
+			msg_pack_string("galenskap!!!!", 512);
+			msg_pack_end();
+			client_send_msg();
+		}
+	}
 }
 
 static void render_projectile(const obj_projectile *prev, const obj_projectile *current, int itemid)
@@ -883,7 +955,7 @@ static void render_tee(animstate *anim, int skin, int emote, vec2 dir, vec2 pos)
 							select_sprite(SPRITE_TEE_EYE_NORMAL, 0, 0, shift*4);
 							break;
 					}
-					int h = emote == EMOTE_BLINK ? basesize/3 : basesize;
+					int h = emote == EMOTE_BLINK ? (int)(basesize/3) : (int)(basesize);
 					gfx_quads_draw(position.x-4+direction.x*4, position.y-8+direction.y*3, basesize, h);
 					gfx_quads_draw(position.x+4+direction.x*4, position.y-8+direction.y*3, -basesize, h);
 				}
@@ -956,6 +1028,7 @@ static void render_player(const obj_player *prev, const obj_player *player)
 {
 	if(player->health < 0) // dont render dead players
 		return;
+		
 	int skin = gametype == GAMETYPE_TDM ? skinseed + player->team : player->clientid;
 
 	vec2 direction = get_direction(player->angle);
@@ -1248,7 +1321,7 @@ void render_game()
 			int c = input::last_char(); // TODO: bypasses the engine interface
 			int k = input::last_key(); // TODO: bypasses the engine interface
 		
-			if (c >= 32 && c < 255)
+			if (!(c >= 0 && c < 32))
 			{
 				if (chat_input_len < sizeof(chat_input) - 1)
 				{
@@ -1291,13 +1364,9 @@ void render_game()
 	{
 		player_input input;
 		mem_zero(&input, sizeof(input));
-
-		float a = atan((float)mouse_pos.y/(float)mouse_pos.x);
-		if(mouse_pos.x < 0)
-			a = a+pi;
 			
-		input.target_x = (int)mouse_pos.x; //(int)(a*256.0f);
-		input.target_y = (int)mouse_pos.y; //(int)(a*256.0f);
+		input.target_x = (int)mouse_pos.x;
+		input.target_y = (int)mouse_pos.y;
 		input.activeweapon = -1;
 	
 		if(chat_active)
@@ -1313,25 +1382,30 @@ void render_game()
 			input.fire = inp_key_pressed(config.key_fire);
 			input.hook = inp_key_pressed(config.key_hook);
 
-			input.blink = inp_key_pressed('S');
-			
+			//input.blink = inp_key_pressed('S');
 			// Weapon switching
-#define TEST_WEAPON_KEY(key) if (inp_key_pressed(config.key_weapon ## key)) input.activeweapon = key-1;
-			if(config.scroll_weapon)
-			{
-				int delta = inp_mouse_scroll();
-				input.activeweapon = input.activeweapon + delta;
-
-				if(input.activeweapon > 3)
-					input.activeweapon = 3;
-				else if(input.activeweapon < 0)
-					input.activeweapon = 0;
-			}
-
-			TEST_WEAPON_KEY(1);
-			TEST_WEAPON_KEY(2);
-			TEST_WEAPON_KEY(3);
-			TEST_WEAPON_KEY(4);
+			if(inp_key_pressed(config.key_weapon1)) input.activeweapon = 0;
+			if(inp_key_pressed(config.key_weapon2)) input.activeweapon = 1;
+			if(inp_key_pressed(config.key_weapon3)) input.activeweapon = 2;
+			if(inp_key_pressed(config.key_weapon4)) input.activeweapon = 3;
+		}
+		
+		// stress testing
+		if(config.stress)
+		{
+			float t = client_localtime();
+			mem_zero(&input, sizeof(input));
+			input.left = 1;
+			input.jump = ((int)t)&1;
+			input.fire = ((int)(t*10))&1;
+			input.hook = ((int)t)&1;
+			input.activeweapon = ((int)t)%NUM_WEAPONS;
+			input.target_x = (int)(sinf(t*3)*100.0f);
+			input.target_y = (int)(cosf(t*3)*100.0f);
+			
+			//input.target_x = (int)((rand()/(float)RAND_MAX)*64-32);
+			//input.target_y = (int)((rand()/(float)RAND_MAX)*64-32);
+			
 		}
 
 		snap_input(&input, sizeof(input));
@@ -1365,6 +1439,10 @@ void render_game()
 				gameobj = (obj_game *)data;
 		}
 	}
+	
+	// everything updated, do events
+	if(must_process_events)
+		process_events(SNAP_PREV);
 
 	// pseudo format
 	float zoom = 3.0f;
@@ -1413,7 +1491,7 @@ void render_game()
 		{
 			float parallax_amount = 0.55f;
 			select_sprite(cloud_sprites[i]);
-			draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 1700.0f))+screen_x*parallax_amount,
+			draw_sprite((cloud_pos[i].x+fmod(client_localtime()*cloud_speed[i]+i*100.0f, 3000.0f))+screen_x*parallax_amount,
 				cloud_pos[i].y+screen_y*parallax_amount, 300);
 		}
 		gfx_quads_end();
@@ -1707,7 +1785,7 @@ void render_game()
 			// sort players
 			for(int k = 0; k < num_players; k++) // ffs, bubblesort
 			{
-				for(int i = k; i < num_players-1; i++)
+				for(int i = 0; i < num_players-k-1; i++)
 				{
 					if(players[i]->score < players[i+1]->score)
 					{
diff --git a/src/game/client/menu.cpp b/src/game/client/menu.cpp
index e0b171b1..b5cbc1b6 100644
--- a/src/game/client/menu.cpp
+++ b/src/game/client/menu.cpp
@@ -414,7 +414,7 @@ int ui_do_edit_box(void *id, float x, float y, float w, float h, char *str, int
 		if (at_index > len)
 			at_index = len;
 
-		if (c >= 32 && c < 128)
+		if (!(c >= 0 && c < 32))
 		{
 			if (len < str_size - 1 && at_index < str_size - 1)
 			{
@@ -673,7 +673,7 @@ static int do_server_list(float x, float y, int *scroll_index, int *selected_ind
 	}
 
 	*scroll_index = do_scroll_bar_vert(scroll_index, x + real_width - 16, y, real_height,
-		min(num_servers - visible_items, 0), *scroll_index);
+		max(num_servers - visible_items, 0), *scroll_index);
 	
 	return r;
 }
@@ -837,8 +837,11 @@ static int settings_controls_render()
 static const int MAX_RESOLUTIONS = 128;
 static int settings_video_render_select_mode()
 {
-	video_mode modes[MAX_RESOLUTIONS];
-	int num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
+	static video_mode modes[MAX_RESOLUTIONS];
+	static int num_modes = -1;
+	
+	if(num_modes == -1)
+		num_modes = gfx_get_video_modes(modes, MAX_RESOLUTIONS);
 	
 	static int scroll_index = 0;
 	scroll_index = do_scroll_bar_vert(&scroll_index, 500, row1_y, 40 * 7, num_modes - 7, scroll_index);
@@ -1033,10 +1036,10 @@ static int settings_render(bool ingame)
 extern int gametype;
 static int ingame_main_render()
 {
-	static int menu_resume, menu_active, menu_quit, menu_settings;
-	char buf[128];
+	static int menu_resume, menu_quit, menu_settings;
 	/*if (gametype == GAMETYPE_TDM)
 	{
+		char buf[128];
 		// Switch team
 		ui_do_label(100,100,"Switch Team",40);
 		sprintf(buf,"Team: %s",local_player->team ? "A" : "B");
@@ -1255,7 +1258,7 @@ static int kerning_render()
 }
 
 
-static int render_popup(const char *caption, const char *text, const char *button_text)
+int render_popup(const char *caption, const char *text, const char *button_text)
 {
 	float tw;
 
@@ -1278,9 +1281,14 @@ static int render_popup(const char *caption, const char *text, const char *butto
 	tw = gfx_pretty_text_width(32.0f, text);
 	ui_do_label(x+w/2-tw/2, y+130, text, 32.0f);
 
-	static int back_button = 0;
-	if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button))
-		return 1;
+	if(button_text)
+	{
+		static int back_button = 0;
+		if(ui_do_button(&back_button, button_text, 0, x+w/2-100, y+220, 200, 48, draw_teewars_button))
+			return 1;
+		if(inp_key_down(input::esc) || inp_key_down(input::enter))
+			return 1;
+	}
 		
 	return 0;
 }
diff --git a/src/game/server/game_server.cpp b/src/game/server/game_server.cpp
index f6dfccd8..54e6f32f 100644
--- a/src/game/server/game_server.cpp
+++ b/src/game/server/game_server.cpp
@@ -175,6 +175,11 @@ event_handler::event_handler()
 
 void *event_handler::create(int type, int size, int target)
 {
+	if(num_events == MAX_EVENTS)
+		return 0;
+	if(current_offset+size >= MAX_DATASIZE)
+		return 0;
+
 	void *p = &data[current_offset];
 	offsets[num_events] = current_offset;
 	types[num_events] = type;
@@ -215,8 +220,7 @@ entity::entity(int objtype)
 	flags = FLAG_ALIVE;
 	proximity_radius = 0;
 	
-	current_id++;
-	id = current_id;
+	id = snap_new_id();
 	
 	next_entity = 0;
 	prev_entity = 0;
@@ -226,10 +230,9 @@ entity::entity(int objtype)
 
 entity::~entity()
 {
+	snap_free_id(id);
 }
 
-int entity::current_id = 1;
-
 //////////////////////////////////////////////////
 // game world
 //////////////////////////////////////////////////
@@ -538,7 +541,7 @@ void gameobject::tick()
 
 void gameobject::snap(int snapping_client)
 {
-	obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, id, sizeof(obj_game));
+	obj_game *game = (obj_game *)snap_new_item(OBJTYPE_GAME, 0, sizeof(obj_game));
 	game->paused = world.paused;
 	game->game_over = game_over_tick==-1?0:1;
 	game->sudden_death = sudden_death;
@@ -563,7 +566,7 @@ int gameobject::getteam(int notthisid)
 	return numplayers[0] > numplayers[1] ? 1 : 0;
 }
 
-gameobject gameobj;
+gameobject *gameobj = 0;
 
 //////////////////////////////////////////////////
 // projectile
@@ -733,6 +736,15 @@ void player::init()
 	team = 0;
 	extrapowerflags = 0;
 	ninjaactivationtick = 0;
+
+
+	latency_accum = 0;
+	latency_accum_min = 0;
+	latency_accum_max = 0;
+	latency_avg = 0;
+	latency_min = 0;
+	latency_max = 0;
+	
 	reset();
 }
 
@@ -750,10 +762,25 @@ void player::reset()
 	die_tick = 0;
 	damage_taken = 0;
 	state = STATE_UNKNOWN;
+	
+	mem_zero(&input, sizeof(input));
+	mem_zero(&previnput, sizeof(previnput));
+	
 	last_action = -1;
+	
+	emote_stop = 0;
+	damage_taken_tick = 0;
+	attack_tick = 0;
 }
 
 void player::destroy() {  }
+
+void player::set_weapon(int w)
+{
+	last_weapon = active_weapon;
+	active_weapon = w;
+}
+
 	
 void player::respawn()
 {
@@ -788,7 +815,7 @@ void player::try_respawn()
 	defered_pos = pos;
 	
 
-	health = data->playerinfo[gameobj.gametype].maxhealth;
+	health = data->playerinfo[gameobj->gametype].maxhealth;
 	armor = 0;
 	jumped = 0;
 	dead = false;
@@ -803,9 +830,11 @@ void player::try_respawn()
 	weapons[WEAPON_HAMMER].got = true;
 	weapons[WEAPON_HAMMER].ammo = -1;
 	weapons[WEAPON_GUN].got = true;
-	weapons[WEAPON_GUN].ammo = data->weapons[active_weapon].maxammo;
+	weapons[WEAPON_GUN].ammo = data->weapons[WEAPON_GUN].maxammo;
 	
 	active_weapon = WEAPON_GUN;
+	last_weapon = WEAPON_HAMMER;
+	
 	reload_timer = 0;
 
 	// Create sound and spawn effects
@@ -849,7 +878,7 @@ int player::handle_ninja()
 	if ((server_tick() - ninjaactivationtick) > (data->weapons[WEAPON_NINJA].duration * server_tickspeed() / 1000))
 	{
 		// time's up, return
-		active_weapon = WEAPON_GUN;
+		active_weapon = last_weapon;
 		return 0;
 	}
 	
@@ -935,6 +964,18 @@ int player::handle_ninja()
 
 int player::handle_weapons()
 {
+	if(config.stress)
+	{
+		for(int i = 0; i < NUM_WEAPONS; i++)
+		{
+			weapons[i].got = true;
+			weapons[i].ammo = 10;
+		}
+		
+		if(reload_timer) // twice as fast reload
+			reload_timer--;
+	}
+	
 	// check reload timer
 	if(reload_timer)
 	{
@@ -954,8 +995,8 @@ int player::handle_weapons()
 		if (active_weapon != input.activeweapon)
 			create_sound(pos, SOUND_WEAPON_SWITCH);
 
+		last_weapon = active_weapon;
 		active_weapon = input.activeweapon;
-		
 	}
 
 	if(!previnput.fire && input.fire)
@@ -1342,6 +1383,8 @@ void player::tick_defered()
 
 void player::die(int killer, int weapon)
 {
+	dbg_msg("game", "kill killer='%d:%s' victim='%d:%s' weapon=%d", killer, players[killer].name, client_id, name, weapon);
+	
 	// send the kill message
 	msg_pack_start(MSG_KILLMSG, MSGFLAG_VITAL);
 	msg_pack_int(killer);
@@ -1372,7 +1415,7 @@ bool player::take_damage(vec2 force, int dmg, int from, int weapon)
 	if(from == client_id)
 		dmg = max(1, dmg/2);
 
-	if (gameobj.gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
+	if (gameobj->gametype == GAMETYPE_TDM && from >= 0 && players[from].team == team)
 		return false;
 
 	damage_taken++;
@@ -1505,7 +1548,12 @@ void player::snap(int snaping_client)
 	player->hook_x = (int)hook_pos.x;
 	player->hook_y = (int)hook_pos.y;
 
-	float a = atan((float)input.target_y/(float)input.target_x);
+	float a = 0;
+	if(input.target_x == 0)
+		a = atan((float)input.target_y);
+	else
+		a = atan((float)input.target_y/(float)input.target_x);
+		
 	if(input.target_x < 0)
 		a = a+pi;
 		
@@ -1517,7 +1565,7 @@ void player::snap(int snaping_client)
 	player->state = state;
 }
 
-player players[MAX_CLIENTS];
+player *players;
 
 //////////////////////////////////////////////////
 // powerup
@@ -1569,18 +1617,18 @@ void powerup::tick()
 		switch (type)
 		{
 		case POWERUP_HEALTH:
-			if(pplayer->health < data->playerinfo[gameobj.gametype].maxhealth)
+			if(pplayer->health < data->playerinfo[gameobj->gametype].maxhealth)
 			{
 				create_sound(pos, SOUND_PICKUP_HEALTH, 0);
-				pplayer->health = min((int)data->playerinfo[gameobj.gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount);
+				pplayer->health = min((int)data->playerinfo[gameobj->gametype].maxhealth, pplayer->health + data->powerupinfo[type].amount);
 				respawntime = data->powerupinfo[type].respawntime;
 			}
 			break;
 		case POWERUP_ARMOR:
-			if(pplayer->armor < data->playerinfo[gameobj.gametype].maxarmor)
+			if(pplayer->armor < data->playerinfo[gameobj->gametype].maxarmor)
 			{
 				create_sound(pos, SOUND_PICKUP_ARMOR, 0);
-				pplayer->armor = min((int)data->playerinfo[gameobj.gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount);
+				pplayer->armor = min((int)data->playerinfo[gameobj->gametype].maxarmor, pplayer->armor + data->powerupinfo[type].amount);
 				respawntime = data->powerupinfo[type].respawntime;
 			}
 			break;
@@ -1607,6 +1655,7 @@ void powerup::tick()
 				// activate ninja on target player
 				pplayer->ninjaactivationtick = server_tick();
 				pplayer->weapons[WEAPON_NINJA].got = true;
+				pplayer->last_weapon = pplayer->active_weapon;
 				pplayer->active_weapon = WEAPON_NINJA;
 				respawntime = data->powerupinfo[type].respawntime;
 				create_sound(pos, SOUND_PICKUP_NINJA);
@@ -1635,7 +1684,10 @@ void powerup::tick()
 		};
 		
 		if(respawntime >= 0)
+		{
+			dbg_msg("game", "pickup player='%d:%s' item=%d/%d", pplayer->client_id, pplayer->name, type, subtype);
 			spawntick = server_tick() + server_tickspeed() * respawntime;
+		}
 	}
 }
 
@@ -1757,9 +1809,12 @@ void create_damageind(vec2 p, float angle, int amount)
 	{
 		float f = mix(s, e, float(i+1)/float(amount+2));
 		ev_damageind *ev = (ev_damageind *)events.create(EVENT_DAMAGEINDICATION, sizeof(ev_damageind));
-		ev->x = (int)p.x;
-		ev->y = (int)p.y;
-		ev->angle = (int)(f*256.0f);
+		if(ev)
+		{
+			ev->x = (int)p.x;
+			ev->y = (int)p.y;
+			ev->angle = (int)(f*256.0f);
+		}
 	}
 }
 
@@ -1767,8 +1822,11 @@ void create_explosion(vec2 p, int owner, int weapon, bool bnodamage)
 {
 	// create the event
 	ev_explosion *ev = (ev_explosion *)events.create(EVENT_EXPLOSION, sizeof(ev_explosion));
-	ev->x = (int)p.x;
-	ev->y = (int)p.y;
+	if(ev)
+	{
+		ev->x = (int)p.x;
+		ev->y = (int)p.y;
+	}
 	
 	if (!bnodamage)
 	{
@@ -1796,24 +1854,33 @@ void create_smoke(vec2 p)
 {
 	// create the event
 	ev_explosion *ev = (ev_explosion *)events.create(EVENT_SMOKE, sizeof(ev_explosion));
-	ev->x = (int)p.x;
-	ev->y = (int)p.y;
+	if(ev)
+	{
+		ev->x = (int)p.x;
+		ev->y = (int)p.y;
+	}
 }
 
 void create_spawn(vec2 p)
 {
 	// create the event
 	ev_spawn *ev = (ev_spawn *)events.create(EVENT_SPAWN, sizeof(ev_spawn));
-	ev->x = (int)p.x;
-	ev->y = (int)p.y;
+	if(ev)
+	{
+		ev->x = (int)p.x;
+		ev->y = (int)p.y;
+	}
 }
 
 void create_death(vec2 p)
 {
 	// create the event
 	ev_death *ev = (ev_death *)events.create(EVENT_DEATH, sizeof(ev_death));
-	ev->x = (int)p.x;
-	ev->y = (int)p.y;
+	if(ev)
+	{
+		ev->x = (int)p.x;
+		ev->y = (int)p.y;
+	}
 }
 
 void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags)
@@ -1823,9 +1890,12 @@ void create_targetted_sound(vec2 pos, int sound, int target, int loopingflags)
 
 	// create a sound
 	ev_sound *ev = (ev_sound *)events.create(EVENT_SOUND, sizeof(ev_sound), target);
-	ev->x = (int)pos.x;
-	ev->y = (int)pos.y;
-	ev->sound = sound | loopingflags;
+	if(ev)
+	{
+		ev->x = (int)pos.x;
+		ev->y = (int)pos.y;
+		ev->sound = sound | loopingflags;
+	}
 }
 
 void create_sound(vec2 pos, int sound, int loopingflags)
@@ -1864,7 +1934,7 @@ void mods_tick()
 	world.tick();
 	
 	if(world.paused) // make sure that the game object always updates
-		gameobj.tick();
+		gameobj->tick();
 
 	if(debug_bots)
 	{
@@ -1905,6 +1975,11 @@ void mods_client_input(int client_id, void *input)
 
 void send_chat_all(int cid, const char *msg)
 {
+	if(cid >= 0 && cid < MAX_CLIENTS)
+		dbg_msg("chat", "%d:%s: %s", cid, players[cid].name, msg);
+	else
+		dbg_msg("chat", "*** %s", msg);
+	
 	msg_pack_start(MSG_CHAT, MSGFLAG_VITAL);
 	msg_pack_int(cid);
 	msg_pack_string(msg, 512);
@@ -1928,10 +2003,13 @@ void mods_client_enter(int client_id)
 	else
 		strcpy(players[client_id].name, "(bot)");
 
-	if (gameobj.gametype == GAMETYPE_TDM)
+
+	dbg_msg("game", "join player='%d:%s'", client_id, players[client_id].name);
+
+	if (gameobj->gametype == GAMETYPE_TDM)
 	{
 		// Check which team the player should be on
-		players[client_id].team = gameobj.getteam(client_id);
+		players[client_id].team = gameobj->getteam(client_id);
 	}
 	
 	msg_pack_start(MSG_SETNAME, MSGFLAG_VITAL);
@@ -1962,8 +2040,9 @@ void mods_client_drop(int client_id)
 	char buf[512];
 	sprintf(buf, "%s has left the game", players[client_id].name);
 	send_chat_all(-1, buf);
+
+	dbg_msg("game", "leave player='%d:%s'", client_id, players[client_id].name);
 	
-	dbg_msg("mods", "client drop %d", client_id);
 	world.remove_entity(&players[client_id]);
 	players[client_id].client_id = -1;
 }
@@ -1990,6 +2069,9 @@ void mods_init()
 	data = load_data_from_memory(internal_data);
 	col_init(32);
 
+	players = new player[MAX_CLIENTS];
+	gameobj = new gameobject;
+
 	int start, num;
 	map_get_type(MAPRES_ITEM, &start, &num);
 	
@@ -1999,7 +2081,7 @@ void mods_init()
 		mapres_item *it = (mapres_item *)map_get_item(start+i, 0, 0);
 		
 		int type = -1;
-		int subtype = -1;
+		int subtype = 0;
 		
 		switch(it->type)
 		{
@@ -2043,7 +2125,7 @@ void mods_init()
 		}
 	}
 	
-	world.insert_entity(&gameobj);
+	world.insert_entity(gameobj);
 }
 
 void mods_shutdown() {}
diff --git a/src/game/server/game_server.h b/src/game/server/game_server.h
index 83824f0a..6361b3ea 100644
--- a/src/game/server/game_server.h
+++ b/src/game/server/game_server.h
@@ -35,7 +35,6 @@ private:
 	entity *next_type_entity;
 
 	int index;
-	static int current_id;
 protected:
 	int id;
 	
@@ -123,7 +122,7 @@ public:
 	virtual int getteam(int notthisid);
 };
 
-extern gameobject gameobj;
+extern gameobject *gameobj;
 
 
 // TODO: move to seperate file
@@ -214,6 +213,7 @@ public:
 		bool got;
 	} weapons[NUM_WEAPONS];
 	int active_weapon;
+	int last_weapon;
 	int reload_timer;
 	int attack_tick;
 	
@@ -291,6 +291,8 @@ public:
 	void respawn();
 
 	bool is_grounded();
+	
+	void set_weapon(int w);
 
 	void release_hooked();
 	void release_hooks();
@@ -307,7 +309,7 @@ public:
 	virtual void snap(int snaping_client);
 };
 
-extern player players[MAX_CLIENTS];
+extern player *players;
 
 // TODO: move to seperate file
 class flag : public entity