1 /*
2  * GameController.cpp
3  *
4  * This file is part of Leges Motus, a networked, 2D shooter set in zero gravity.
5  *
6  * Copyright 2009-2010 Andrew Ayer, Nathan Partlan, Jeffrey Pfau
7  *
8  * Leges Motus is free and open source software.  You may redistribute it and/or
9  * modify it under the terms of version 2, or (at your option) version 3, of the
10  * GNU General Public License (GPL), as published by the Free Software Foundation.
11  *
12  * Leges Motus is distributed in the hope that it will be useful, but WITHOUT ANY
13  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
14  * PARTICULAR PURPOSE.  See the full text of the GNU General Public License for
15  * further detail.
16  *
17  * For a full copy of the GNU General Public License, please see the COPYING file
18  * in the root of the source code tree.  You may also retrieve a copy from
19  * <http://www.gnu.org/licenses/gpl-2.0.txt>, or request a copy by writing to the
20  * Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
21  * 02111-1307  USA
22  *
23  */
24 
25 #include "GameController.hpp"
26 #include "GameWindow.hpp"
27 #include "ClientNetwork.hpp"
28 #include "GraphicalPlayer.hpp"
29 #include "Sprite.hpp"
30 #include "TiledGraphic.hpp"
31 #include "TableBackground.hpp"
32 #include "ClientConfiguration.hpp"
33 #include "ServerBrowser.hpp"
34 #include "TransitionManager.hpp"
35 #include "BaseMapObject.hpp"
36 #include "TextMenuItem.hpp"
37 #include "Weapon.hpp"
38 #include "GraphicMenuItem.hpp"
39 #include "common/PacketReader.hpp"
40 #include "common/PacketWriter.hpp"
41 #include "common/network.hpp"
42 #include "common/math.hpp"
43 #include "common/team.hpp"
44 #include "common/StringTokenizer.hpp"
45 #include "common/IPAddress.hpp"
46 #include "common/timer.hpp"
47 #include "common/misc.hpp"
48 #include "common/Shape.hpp"
49 #include "common/Circle.hpp"
50 #include "common/Version.hpp"
51 
52 #include "SDL_image.h"
53 
54 #include <vector>
55 #include <algorithm>
56 #include <cstdio>
57 #include <cstdlib>
58 #include <ctime>
59 #include <cstring>
60 #include <iostream>
61 #include <limits>
62 #include <sstream>
63 
64 using namespace LM;
65 using namespace std;
66 
67 const int GameController::MESSAGE_DISPLAY_TIME = 10000;
68 const unsigned int GameController::MAX_MESSAGES_TO_DISPLAY = 20;
69 const int GameController::SHOT_DISPLAY_TIME = 180;
70 const uint64_t GameController::MUZZLE_FLASH_LENGTH = 80;
71 const int GameController::GATE_WARNING_FLASH_LENGTH = 3000;
72 const double GameController::RANDOM_ROTATION_SCALE = 1.0;
73 const Color GameController::BLUE_COLOR(0.6, 0.6, 1.0);
74 const Color GameController::RED_COLOR(1.0, 0.6, 0.6);
75 const Color GameController::BRIGHT_GREEN(0.0, 0.6, 0.0, 0.8);
76 const Color GameController::BRIGHT_ORANGE(1.0, 0.5, 0.0, 0.8);
77 const Color GameController::BLUE_SHADOW(0.1, 0.1, 0.3);
78 const Color GameController::RED_SHADOW(0.3, 0.1, 0.1);
79 const Color GameController::TEXT_COLOR(1.0, 1.0, 1.0);
80 const Color GameController::TEXT_SHADOW(0.1, 0.1, 0.1);
81 const Color GameController::GREYED_COLOR(0.5, 0.5, 0.5);
82 const Color GameController::GREYED_SHADOW(0.2, 0.2, 0.2);
83 const Color GameController::TEXT_BG_COLOR(0.0, 0.0, 0.0, 0.7);
84 const Color GameController::BUTTON_HOVER_COLOR(0.6, 0.7, 0.9);
85 const Color GameController::BUTTON_HOVER_SHADOW(0.1, 0.2, 0.4);
86 const int GameController::GATE_STATUS_RECT_WIDTH = 80;
87 const int GameController::FROZEN_STATUS_RECT_WIDTH = 60;
88 const int GameController::ENERGY_BAR_WIDTH = 100;
89 const int GameController::COOLDOWN_BAR_WIDTH = 150;
90 const int GameController::STATUS_BAR_HEIGHT = 40;
91 const int GameController::DOUBLE_CLICK_TIME = 300;
92 const int GameController::NETWORK_TIMEOUT_LIMIT = 10000;
93 const int GameController::TEXT_LAYER = -4;
94 const unsigned int GameController::PING_FREQUENCY = 2000;
95 const unsigned int GameController::CHAT_TRANSITION_TIME = 200;
96 const unsigned int GameController::CHAT_LIMIT = 120;
97 
sort_resolution(pair<int,int> pairone,pair<int,int> pairtwo)98 static bool sort_resolution(pair<int, int> pairone, pair<int, int> pairtwo) {
99 	if (pairone.first == pairtwo.first) {
100 		return pairone.second < pairtwo.second;
101 	}
102 	return pairone.first < pairtwo.first;
103 }
104 
GameController(PathManager & path_manager,ClientConfiguration * config)105 GameController::GameController(PathManager& path_manager, ClientConfiguration* config) : m_path_manager(path_manager), m_network(*this) {
106 	preinit(config);
107 	init(GameWindow::get_optimal_instance(config->get_bool_value("vsync")?(GameWindow::VSYNC):0, config->get_int_value("multisample")));
108 }
109 
GameController(PathManager & path_manager,ClientConfiguration * config,int width,int height,bool fullscreen,int depth)110 GameController::GameController(PathManager& path_manager, ClientConfiguration* config, int width, int height, bool fullscreen, int depth) : m_path_manager(path_manager), m_network(*this) {
111 	preinit(config);
112 	int flags = 0;
113 	if (fullscreen) {
114 		flags |= GameWindow::FULLSCREEN;
115 	}
116 	if (config->get_bool_value("vsync")) {
117 		flags |= GameWindow::VSYNC;
118 	}
119 	init(GameWindow::get_instance(width, height, depth, flags, config->get_int_value("multisample")));
120 }
121 
122 /*
123  * Delete all of the sprites and subsystems.
124  */
~GameController()125 GameController::~GameController() {
126 	clear_players();
127 
128 	// TEMPORARY SPRITE CODE
129 	delete blue_sprite;
130 	delete blue_back_arm;
131 	delete red_sprite;
132 	delete red_back_arm;
133 	delete m_crosshairs;
134 	delete m_chat_input;
135 
136 	for (unsigned int i = 0; i < m_shots.size(); i++) {
137 		m_window->unregister_graphic(m_shots[i].first, GameWindow::LAYER_GAME);
138 		delete m_shots[i].first;
139 		m_shots.erase(m_shots.begin() + i);
140 	}
141 
142 	for (unsigned int i = 0; i < m_messages.size(); i++) {
143 		m_text_manager->remove_string(m_messages[i].message);
144 		m_transition_manager.remove_transition(m_messages[i].transition);
145 		m_messages.erase(m_messages.begin() + i);
146 	}
147 
148 	m_text_manager->remove_all_strings();
149 
150 	delete m_overlay_background;
151 	delete m_menu_back;
152 
153 	delete m_server_browser;
154 	delete m_chat_log;
155 
156 	delete m_blue_gate_status_rect;
157 	delete m_blue_gate_status_rect_back;
158 	delete m_red_gate_status_rect;
159 	delete m_red_gate_status_rect_back;
160 	delete m_frozen_status_rect;
161 	delete m_frozen_status_rect_back;
162 	delete m_energy_bar;
163 	delete m_energy_bar_back;
164 
165 	delete m_chat_window_back;
166 
167 	delete m_weapon_selector;
168 
169 	// TEMPORARY MAP CODE BY ANDREW
170 	delete m_map;
171 
172 	clear_weapons();
173 
174 	delete m_text_manager;
175 	m_sound_controller->destroy_instance();
176 	delete m_font;
177 	delete m_bold_font;
178 	delete m_menu_font;
179 	delete m_medium_font;
180 
181 	delete m_logo;
182 
183 	m_radar->unregister_with_window(m_window);
184 	delete m_radar;
185 
186 	m_graphics_cache.clear(); // Make sure the graphics cache is clear before m_window is destroyed, or bad stuff may happen (TODO: make GameWindow managed in a more sensible way)
187 
188 	// The GameWindow instance should always be destroyed last, since other stuff may depend on it.
189 	m_window->deinit_video();
190 	m_window->destroy_instance();
191 }
192 
193 /*
194  * Initialize the base of the game controller before the Window gets created
195  */
preinit(ClientConfiguration * config)196 void GameController::preinit(ClientConfiguration* config) {
197 #ifndef __WIN32
198 	GameWindow::set_icon(IMG_Load(m_path_manager.data_path("blue_head512.png", "sprites")));
199 #else
200 	GameWindow::set_icon(IMG_Load(m_path_manager.data_path("blue_head32.png", "sprites")));
201 #endif
202 	m_configuration = config;
203 }
204 
205 /*
206  * Initialize all of the subsystems, sprites, etc.
207  */
init(GameWindow * window)208 void GameController::init(GameWindow* window) {
209 	srand ( time(NULL) );
210 
211 	get_ticks();
212 
213 	initialize_key_bindings();
214 
215 	// Initial game state will be showing the main menu.
216 	m_game_state = SHOW_MENUS;
217 	m_restart = false;
218 
219 	m_screen_width = window->get_width();
220 	m_screen_height = window->get_height();
221 
222 	window->set_layer_visible(true, GameWindow::LAYER_SUPER);
223 
224 	m_join_sent_time = 0;
225 	m_last_ping_sent = 0;
226 
227 	m_client_version = LM_VERSION;
228 	m_protocol_number = PROTOCOL_VERSION;
229 
230 	m_pixel_depth = window->get_depth();
231 	m_fullscreen = window->is_fullscreen();
232 	m_quit_game = false;
233 	m_window = window;
234 
235 	m_last_damage_time = 0;
236 	m_last_recharge_time = 0;
237 	m_time_to_unfreeze = 0;
238 	m_total_time_frozen = 0;
239 	m_last_clicked = 0;
240 	m_muzzle_flash_start = 0;
241 
242 	m_cooldown_updated = false;
243 
244 	m_curr_weapon_image = NULL;
245 	m_weapon_selector = NULL;
246 	m_current_weapon = NULL;
247 
248 	m_menu_back = new TableBackground(1, m_screen_width);
249 	m_menu_back->set_row_height(0, m_screen_height);
250 	m_menu_back->set_cell_color(0, Color(0, 0, 0, 0.6));
251 	m_menu_back->set_border_width(0);
252 	m_menu_back->set_priority(10);
253 	m_menu_back->set_center_x(0);
254 	m_window->register_graphic(m_menu_back, GameWindow::LAYER_MENU);
255 
256 	m_font = new Font(m_path_manager.data_path("JuraMedium.ttf", "fonts"), 14);
257 	m_bold_font = new Font(m_path_manager.data_path("JuraDemiBold.ttf", "fonts"), 14);
258 	m_bold_font->set_font_style(true, false);
259 	m_text_manager = new TextManager(m_font, m_window);
260 
261 	m_menu_font = new Font(m_path_manager.data_path("JuraDemiBold.ttf", "fonts"), 24);
262 	m_large_menu_font = new Font(m_path_manager.data_path("JuraDemiBold.ttf", "fonts"), 30);
263 	m_medium_font = new Font(m_path_manager.data_path("JuraMedium.ttf", "fonts"), 20);
264 
265 	m_sound_controller = SoundController::get_instance(*this, m_path_manager);
266 	m_holding_gate = false;
267 	m_gate_lower_sounds[0] = -1;
268 	m_gate_lower_sounds[1] = -1;
269 	m_sound_controller->set_sound_on(m_configuration->get_bool_value("sound"));
270 
271 	m_map = new GraphicalMap(m_path_manager, m_window);
272 	m_map_width = 0;
273 	m_map_height = 0;
274 
275 	m_round_end_time = 0;
276 
277 	// Initialize all of the components of the player sprites.
278 	blue_sprite = new Sprite(m_path_manager.data_path("blue_armless.png","sprites"));
279 	blue_back_arm = new Sprite(m_path_manager.data_path("blue_backarm.png","sprites"));
280 	blue_back_arm->set_center_x(27);
281 	blue_back_arm->set_center_y(29);
282 	blue_back_arm->set_x(-5);
283 	blue_back_arm->set_y(-20);
284 	blue_back_arm->set_priority(1);
285 
286 	blue_player.add_graphic(blue_sprite, "torso");
287 	blue_player.add_graphic(blue_back_arm, "backarm");
288 	make_front_arm_graphic(blue_player, "blue_frontarm.png", NULL, NULL);
289 
290 	red_sprite = new Sprite(m_path_manager.data_path("red_armless.png","sprites"));
291 	red_back_arm = new Sprite(m_path_manager.data_path("red_backarm.png","sprites"));
292 	red_back_arm->set_center_x(27);
293 	red_back_arm->set_center_y(29);
294 	red_back_arm->set_x(-5);
295 	red_back_arm->set_y(-20);
296 	red_back_arm->set_priority(1);
297 
298 	red_player.add_graphic(red_sprite, "torso");
299 	red_player.add_graphic(red_back_arm, "backarm");
300 	make_front_arm_graphic(red_player, "red_frontarm.png", NULL, NULL);
301 
302 	m_crosshairs = new Sprite(m_path_manager.data_path("crosshairs.png", "sprites"));
303 	m_crosshairs->set_priority(-10);
304 	m_window->register_graphic(m_crosshairs, GameWindow::LAYER_SUPER);
305 
306 	m_logo = new Sprite(m_path_manager.data_path("legesmotuslogo.png", "sprites"));
307 	m_logo->set_x(m_screen_width/2);
308 	m_logo->set_y(100);
309 	m_logo->set_priority(0);
310 	m_window->register_graphic(m_logo, GameWindow::LAYER_MENU);
311 
312 	// Set the text manager to draw a shadow behind everything.
313 	ConstantCurve curve(0, 1);
314 	m_text_manager->set_active_font(m_large_menu_font);
315 	m_text_manager->set_shadow_alpha(1.0);
316 	m_text_manager->set_shadow_offset(1.0, 1.0);
317 	if (m_configuration->get_bool_value("text_shadow")) {
318 		m_text_manager->set_shadow_convolve(&curve, 5, 1.0);
319 	}
320 	m_text_manager->set_shadow_color(TEXT_SHADOW);
321 	m_text_manager->set_shadow(true);
322 
323 	// Initialize and display the message that we are contacting the metaserver.
324 	/*Text* metaservermessage = m_text_manager->place_string("Attempting to contact metaserver, please wait...", m_screen_width/2, m_screen_height/2, TextManager::CENTER, GameWindow::LAYER_SUPER, TEXT_LAYER);
325 	metaservermessage->set_invisible(false);
326 	m_window->redraw();*/
327 
328 	// Initialize all of the menu items.
329 	m_version_nag1 = NULL;
330 	m_version_nag2 = NULL;
331 
332 	// Main menu
333 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Connect to Server", "connect", 50, 200));
334 	m_item_resume = TextMenuItem::with_manager(m_text_manager, "Resume Game", "resume", 50, 240, MenuItem::DISABLED);
335 	m_main_menu.add_item(m_item_resume);
336 	m_item_disconnect = TextMenuItem::with_manager(m_text_manager, "Disconnect", "disconnect", 50, 280, MenuItem::DISABLED);
337 	m_main_menu.add_item(m_item_disconnect);
338 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Options", "submenu:options", 50, 320));
339 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Quit", "quit", 50, 360));
340 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Thanks for playing! Please visit", "", 50, 430, MenuItem::STATIC));
341 	TextMenuItem* thanks2 = TextMenuItem::with_manager(m_text_manager, "http://legesmotus.cs.brown.edu", "", 50, 470, MenuItem::STATIC);
342 	thanks2->set_plain_fg_color(Color(0.4, 1.0, 0.4));
343 	m_main_menu.add_item(thanks2);
344 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, "to leave feedback for us!", "", 50, 510, MenuItem::STATIC));
345 
346 	m_text_manager->set_active_font(m_font);
347 	m_main_menu.add_item(TextMenuItem::with_manager(m_text_manager, string("v. ").append(m_client_version), "", m_screen_width - 90, m_screen_height - 40, MenuItem::STATIC));
348 	m_text_manager->set_active_font(m_menu_font);
349 
350 	m_window->register_graphic(m_main_menu.get_graphic_group(), GameWindow::LAYER_MENU);
351 
352 	// Options menu
353 	ListMenuItem* current_lmi;
354 	m_options_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Cancel", "cancel", 50, m_screen_height - 50));
355 	m_options_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Enter Name:", "name", 50, 200));
356 	current_lmi = new ListMenuItem("sound", TextMenuItem::with_manager(m_text_manager, "Sound:", "sound", 50, 240));
357 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 210, 240));
358 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 210, 240));
359 	current_lmi->set_default_index(m_sound_controller->is_sound_on() ? 0 : 1);
360 	current_lmi->set_current_index(m_sound_controller->is_sound_on() ? 0 : 1);
361 	m_options_form.add_item("sound", current_lmi);
362 	m_options_menu.add_item(current_lmi);
363 	current_lmi = new ListMenuItem("fullscreen", TextMenuItem::with_manager(m_text_manager, "Fullscreen:", "fullscreen", 50, 280));
364 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 210, 280));
365 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 210, 280));
366 	current_lmi->set_default_index(m_configuration->get_bool_value("fullscreen") ? 0 : 1);
367 	current_lmi->set_current_index(m_configuration->get_bool_value("fullscreen") ? 0 : 1);
368 	m_options_form.add_item("fullscreen", current_lmi);
369 	m_options_menu.add_item(current_lmi);
370 	current_lmi = new ListMenuItem("text_background", TextMenuItem::with_manager(m_text_manager, "Text Background:", "text_background", 460, 200));
371 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 700, 200));
372 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 700, 200));
373 	current_lmi->set_default_index(m_configuration->get_bool_value("text_background") ? 0 : 1);
374 	current_lmi->set_current_index(m_configuration->get_bool_value("text_background") ? 0 : 1);
375 	m_options_form.add_item("text_background", current_lmi);
376 	m_options_menu.add_item(current_lmi);
377 	current_lmi = new ListMenuItem("text_shadow", TextMenuItem::with_manager(m_text_manager, "Text Shadow:", "text_shadow", 460, 240));
378 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 700, 240));
379 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 700, 240));
380 	current_lmi->set_default_index(m_configuration->get_bool_value("text_shadow") ? 0 : 1);
381 	current_lmi->set_current_index(m_configuration->get_bool_value("text_shadow") ? 0 : 1);
382 	m_options_form.add_item("text_shadow", current_lmi);
383 	m_options_menu.add_item(current_lmi);
384 	current_lmi = new ListMenuItem("text_sliding", TextMenuItem::with_manager(m_text_manager, "Text Sliding:", "text_sliding", 460, 280));
385 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 700, 280));
386 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 700, 280));
387 	current_lmi->set_default_index(m_configuration->get_bool_value("text_sliding") ? 0 : 1);
388 	current_lmi->set_current_index(m_configuration->get_bool_value("text_sliding") ? 0 : 1);
389 	m_options_form.add_item("text_sliding", current_lmi);
390 	m_options_menu.add_item(current_lmi);
391 	current_lmi = new ListMenuItem("text_sliding", TextMenuItem::with_manager(m_text_manager, "Vertical Sync:", "vsync", 50, 360));
392 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "On", "on", 240, 360));
393 	current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, "Off", "off", 240, 360));
394 	current_lmi->set_default_index(m_configuration->get_bool_value("vsync") ? 0 : 1);
395 	current_lmi->set_current_index(m_configuration->get_bool_value("vsync") ? 0 : 1);
396 	m_options_form.add_item("vsync", current_lmi);
397 	m_options_menu.add_item(current_lmi);
398 	current_lmi = new ListMenuItem("multisample", TextMenuItem::with_manager(m_text_manager, "Multisample:", "multisample", 460, 320));
399 	for (int i = 0; i <= GameWindow::MAX_MSAA; ++i) {
400 		stringstream s;
401 		s << i;
402 		current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, s.str(), s.str(), 700, 320));
403 	}
404 	current_lmi->set_default_index(min<int>(m_configuration->get_int_value("multisample"), GameWindow::MAX_MSAA));
405 	current_lmi->set_current_index(min<int>(m_configuration->get_int_value("multisample"), GameWindow::MAX_MSAA));
406 	m_options_form.add_item("multisample", current_lmi);
407 	m_options_menu.add_item(current_lmi);
408 	m_options_menu.add_item(TextMenuItem::with_manager(m_text_manager, "Apply", "apply", m_screen_width - 200, m_screen_height - 50));
409 	current_lmi = new ListMenuItem("resolution", TextMenuItem::with_manager(m_text_manager, "Resolution:", "resolution", 50, 320));
410 	m_options_form.add_item("resolution", current_lmi);
411 	m_options_menu.add_item(current_lmi);
412 
413 	// Initialize the name input box
414 	m_name_bar_back = new TableBackground(1, 0);
415 	m_name_bar_back->set_cell_color(0, Color(0, 0, 0, 0));
416 	m_name_bar_back->set_border_color(Color(0, 0, 0, 0));
417 	m_name_input = new TextInput(m_text_manager, 210, 200);
418 	m_name_input->set_window(m_window);
419 	m_name_input->set_background(m_name_bar_back);
420 	m_name_input->set_background_scale(true);
421 	m_name_input->set_background_padding(2);
422 	m_name_input->set_invisible(true);
423 	m_name_input->set_priority(0);
424 	m_name_input->set_crop_width(230);
425 	m_name_bar_back->set_priority(1);
426 	m_options_form.add_item("name", m_name_input);
427 
428 	// TODO: move this to preinit--it doesn't require a GameWindow, and should be done before one is made
429 	int depth;
430 	m_window->supported_resolutions(NULL, NULL, &depth, &m_num_resolutions);
431 	int supported_widths[m_num_resolutions];
432 	int supported_heights[m_num_resolutions];
433 	bool found_res = false;
434 	m_window->supported_resolutions(supported_widths, supported_heights, &depth, &m_num_resolutions);
435 	for (size_t i = 0; i < m_num_resolutions; i++) {
436 		if (m_screen_width == supported_widths[i] && m_screen_height == supported_heights[i]) {
437 			found_res = true;
438 		}
439 		if (supported_widths[i] < 640 || supported_heights[i] < 480) {
440 			continue;
441 		}
442 		m_resolutions.push_back(make_pair(supported_widths[i], supported_heights[i]));
443 	}
444 	m_num_resolutions = m_resolutions.size();
445 	if (found_res == false) {
446 		m_resolutions.push_back(make_pair(m_screen_width, m_screen_height));
447 		m_num_resolutions++;
448 	}
449 	sort(m_resolutions.begin(), m_resolutions.end(), sort_resolution);
450 
451 	for (size_t i = 0; i < m_num_resolutions; i++) {
452 		int width = m_resolutions[i].first;
453 		int height = m_resolutions[i].second;
454 		stringstream resolution;
455 		resolution << width << "x" << height;
456 		current_lmi->add_option(TextMenuItem::with_manager(m_text_manager, resolution.str(), resolution.str(), 210, 320));
457 		if (m_screen_width == width && m_screen_height == height) {
458 			current_lmi->set_current_index(i);
459 			current_lmi->set_default_index(i);
460 		}
461 	}
462 	m_window->register_graphic(m_options_menu.get_graphic_group(), GameWindow::LAYER_MENU);
463 	toggle_options_menu(false);
464 
465 	// Initialize the weapon selector menu.
466 	init_weapon_selector();
467 
468 	// Server browser
469 	m_server_browser = new ServerBrowser(*this, m_window, m_text_manager, m_screen_width, m_screen_height, m_font, m_medium_font, m_menu_font);
470 	m_server_browser->set_visible(false);
471 
472 	// Chat log
473 	m_chat_log = new ChatLog(*this, m_window, m_text_manager, m_screen_width, m_screen_height, m_font, m_medium_font, m_menu_font);
474 	m_chat_log->set_visible(false);
475 
476 	// Initialize the overlay.
477 	m_overlay_background = new TableBackground(3, m_screen_width - 300);
478 	m_overlay_background->set_row_height(0, 80);
479 	m_overlay_background->set_row_height(1, 43);
480 	m_overlay_background->set_row_height(2, m_screen_height - 323);
481 	m_overlay_background->set_priority(-2);
482 	m_overlay_background->set_border_color(Color(1,1,1,0.8));
483 	m_overlay_background->set_border_width(2);
484 	m_overlay_background->set_cell_color(0, Color(0.1,0.1,0.1,0.8));
485 	m_overlay_background->set_cell_color(1, Color(0.2,0.1,0.1,0.8));
486 	m_overlay_background->set_cell_color(2, Color(0.1,0.1,0.15,0.8));
487 	m_overlay_background->set_y(100);
488 	m_overlay_background->set_x(m_screen_width/2);
489 	m_overlay_background->set_border_collapse(true);
490 	m_overlay_background->set_corner_radius(20);
491 	m_window->register_graphic(m_overlay_background, GameWindow::LAYER_HUD);
492 
493 	m_overlay_scrollbar = new ScrollBar();
494 	m_overlay_scrollbar->set_priority(-3);
495 	m_overlay_scrollbar->set_length(m_overlay_background->get_row_height(2) - 20);
496 	m_overlay_scrollbar->set_x(m_overlay_background->get_x() + m_overlay_background->get_image_width()/2 - 20);
497 	m_overlay_scrollbar->set_y(m_overlay_background->get_y() + m_overlay_background->get_row_height(0) + m_overlay_background->get_row_height(1) + 5 + m_overlay_scrollbar->get_length()/2);
498 	m_overlay_scrollbar->set_section_color(ScrollBar::BUTTONS, Color(0.7,0.2,0.1));
499 	m_overlay_scrollbar->set_section_color(ScrollBar::TRACK, Color(0.2,0.1,0.1));
500 	m_overlay_scrollbar->set_section_color(ScrollBar::TRACKER, Color(0.2,0.2,0.4));
501 	m_overlay_scrollbar->set_scroll_speed(3);
502 
503 	m_overlay_scrollarea = new ScrollArea(m_overlay_background->get_image_width(), m_overlay_background->get_row_height(2) - 30, m_overlay_background->get_image_width(), 10, NULL, m_overlay_scrollbar);
504 	m_overlay_scrollarea->set_priority(TEXT_LAYER);
505 	m_overlay_scrollarea->get_group()->set_priority(TEXT_LAYER);
506 	m_overlay_scrollarea->set_x(m_overlay_background->get_x() + 5);
507 	m_overlay_scrollarea->set_y(m_overlay_background->get_y() + m_overlay_background->get_row_height(0) + m_overlay_background->get_row_height(1) + 15);
508 	m_overlay_scrollarea->set_center_x(m_overlay_scrollarea->get_width()/2);
509 	m_overlay_scrollarea->set_center_y(0);
510 
511 	m_window->register_graphic(m_overlay_scrollbar, GameWindow::LAYER_HUD);
512 	m_window->register_graphic(m_overlay_scrollarea, GameWindow::LAYER_HUD);
513 
514 	m_overlay_items["red label"] = m_text_manager->place_string("Red Team:", m_overlay_background->get_x() - m_overlay_background->get_image_width()/2 + 10, 115, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
515 	m_overlay_items["blue label"] = m_text_manager->place_string("Blue Team:", m_overlay_background->get_x(), 115, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
516 
517 	m_text_manager->set_active_font(m_medium_font);
518 	m_overlay_items["name label"] = m_text_manager->place_string("Name", m_overlay_background->get_x() - m_overlay_background->get_image_width()/2 + 10, 190, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
519 	m_overlay_items["score label"] = m_text_manager->place_string("Score", m_overlay_background->get_x(), 190, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
520 
521 	change_team_scores(0, 0);
522 	update_individual_scores();
523 
524 	// Initialize the gate warning.
525 	m_text_manager->set_active_font(m_large_menu_font);
526 	m_text_manager->set_active_color(1.0, 0.4, 0.4);
527 	m_gate_warning = m_text_manager->place_string("Your gate is going down!", m_screen_width/2, m_screen_height - 200, TextManager::CENTER, GameWindow::LAYER_HUD);
528 	m_gate_warning->set_invisible(true);
529 	m_gate_warning_time = 0;
530 
531 	// Initialize the gate status bars.
532 	m_blue_gate_status_rect = new TableBackground(1, GATE_STATUS_RECT_WIDTH);
533 	m_blue_gate_status_rect->set_row_height(0, STATUS_BAR_HEIGHT);
534 	m_blue_gate_status_rect->set_priority(-1);
535 	m_blue_gate_status_rect->set_cell_color(0, Color(0.0, 0.0, 1.0, 0.5));
536 	m_blue_gate_status_rect->set_x(m_screen_width - 2 * m_blue_gate_status_rect->get_image_width() - 20);
537 	m_blue_gate_status_rect->set_y(m_screen_height - m_blue_gate_status_rect->get_image_height() - 20);
538 	m_window->register_graphic(m_blue_gate_status_rect, GameWindow::LAYER_HUD);
539 	m_blue_gate_status_rect_back = new TableBackground(1, GATE_STATUS_RECT_WIDTH);
540 	m_blue_gate_status_rect_back->set_row_height(0, STATUS_BAR_HEIGHT);
541 	m_blue_gate_status_rect_back->set_priority(0);
542 	m_blue_gate_status_rect_back->set_cell_color(0, Color(0.1, 0.1, 0.1, 0.5));
543 	m_blue_gate_status_rect_back->set_x(m_screen_width - 2 * m_blue_gate_status_rect->get_image_width() - 20);
544 	m_blue_gate_status_rect_back->set_y(m_screen_height - m_blue_gate_status_rect->get_image_height() - 20);
545 	m_window->register_graphic(m_blue_gate_status_rect_back, GameWindow::LAYER_HUD);
546 
547 	m_red_gate_status_rect = new TableBackground(1, GATE_STATUS_RECT_WIDTH);
548 	m_red_gate_status_rect->set_row_height(0, STATUS_BAR_HEIGHT);
549 	m_red_gate_status_rect->set_priority(-1);
550 	m_red_gate_status_rect->set_cell_color(0, Color(1.0, 0.0, 0.0, 0.5));
551 	m_red_gate_status_rect->set_x(m_screen_width - m_red_gate_status_rect->get_image_width() - 10);
552 	m_red_gate_status_rect->set_y(m_screen_height - m_red_gate_status_rect->get_image_height() - 20);
553 	m_window->register_graphic(m_red_gate_status_rect, GameWindow::LAYER_HUD);
554 	m_red_gate_status_rect_back = new TableBackground(1, GATE_STATUS_RECT_WIDTH);
555 	m_red_gate_status_rect_back->set_row_height(0, STATUS_BAR_HEIGHT);
556 	m_red_gate_status_rect_back->set_priority(0);
557 	m_red_gate_status_rect_back->set_cell_color(0, Color(0.1, 0.1, 0.1, 0.5));
558 	m_red_gate_status_rect_back->set_x(m_screen_width - m_red_gate_status_rect->get_image_width() - 10);
559 	m_red_gate_status_rect_back->set_y(m_screen_height - m_red_gate_status_rect->get_image_height() - 20);
560 	m_window->register_graphic(m_red_gate_status_rect_back, GameWindow::LAYER_HUD);
561 
562 	// Initialize the gate status bar labels.
563 	m_text_manager->set_active_color(TEXT_COLOR);
564 	m_text_manager->set_active_font(m_font);
565 	m_blue_gate_status_text = m_text_manager->place_string("Blue Gate", m_blue_gate_status_rect->get_x() + 1, m_blue_gate_status_rect->get_y() + m_blue_gate_status_rect->get_image_height()/4, TextManager::CENTER, GameWindow::LAYER_HUD, TEXT_LAYER);
566 	m_red_gate_status_text = m_text_manager->place_string("Red Gate", m_red_gate_status_rect->get_x() + 2, m_red_gate_status_rect->get_y() + m_red_gate_status_rect->get_image_height()/4, TextManager::CENTER, GameWindow::LAYER_HUD, TEXT_LAYER);
567 
568 	// Initialize the frozen status bar.
569 	m_frozen_status_rect = new TableBackground(1, FROZEN_STATUS_RECT_WIDTH);
570 	m_frozen_status_rect->set_row_height(0, 20);
571 	m_frozen_status_rect->set_priority(0);
572 	m_frozen_status_rect->set_cell_color(0, Color(0.0, 0.5, 1.0, 0.5));
573 	m_frozen_status_rect->set_x(m_screen_width/2);
574 	m_frozen_status_rect->set_y(m_screen_height/2 + 50);
575 	m_window->register_graphic(m_frozen_status_rect, GameWindow::LAYER_HUD);
576 	m_frozen_status_rect_back = new TableBackground(1, FROZEN_STATUS_RECT_WIDTH);
577 	m_frozen_status_rect_back->set_row_height(0, 20);
578 	m_frozen_status_rect_back->set_priority(1);
579 	m_frozen_status_rect_back->set_cell_color(0, Color(0.1, 0.1, 0.1, 0.5));
580 	m_frozen_status_rect_back->set_x(m_frozen_status_rect->get_x());
581 	m_frozen_status_rect_back->set_y(m_frozen_status_rect->get_y());
582 	m_window->register_graphic(m_frozen_status_rect_back, GameWindow::LAYER_HUD);
583 
584 	// Initialize the frozen status bar label.
585 	m_text_manager->set_active_color(1.0, 1.0, 1.0);
586 	m_text_manager->set_active_font(m_font);
587 	m_frozen_status_text = m_text_manager->place_string("Frozen", m_frozen_status_rect->get_x() + 1, m_frozen_status_rect->get_y() + 2, TextManager::CENTER, GameWindow::LAYER_HUD);
588 
589 	// Initialize the energy bar.
590 	m_energy_text = NULL;
591 	m_energy_bar = new TableBackground(1, ENERGY_BAR_WIDTH);
592 	m_energy_bar->set_row_height(0, STATUS_BAR_HEIGHT);
593 	m_energy_bar->set_priority(-1);
594 	m_energy_bar->set_cell_color(0, BRIGHT_GREEN);
595 	m_energy_bar->set_x(ENERGY_BAR_WIDTH);
596 	m_energy_bar->set_y(m_screen_height - m_energy_bar->get_image_height() - 20);
597 	m_window->register_graphic(m_energy_bar, GameWindow::LAYER_HUD);
598 	m_energy_bar_back = new TableBackground(1, ENERGY_BAR_WIDTH);
599 	m_energy_bar_back->set_row_height(0, STATUS_BAR_HEIGHT);
600 	m_energy_bar_back->set_priority(0);
601 	m_energy_bar_back->set_cell_color(0, Color(0.1, 0.1, 0.1, 0.5));
602 	m_energy_bar_back->set_x(m_energy_bar->get_x());
603 	m_energy_bar_back->set_y(m_energy_bar->get_y());
604 	m_window->register_graphic(m_energy_bar_back, GameWindow::LAYER_HUD);
605 
606 	update_energy_bar(0);
607 
608 	// Initialize the cooldown bar.
609 	m_cooldown_bar = new TableBackground(1, COOLDOWN_BAR_WIDTH);
610 	m_cooldown_bar->set_row_height(0, STATUS_BAR_HEIGHT/2);
611 	m_cooldown_bar->set_priority(-1);
612 	m_cooldown_bar->set_cell_color(0, BRIGHT_ORANGE);
613 	m_cooldown_bar->set_x(m_energy_bar->get_x() + ENERGY_BAR_WIDTH/2 + m_cooldown_bar->get_image_width()/2 + 20);
614 	m_cooldown_bar->set_y(m_energy_bar->get_y() + m_energy_bar->get_image_height() - m_cooldown_bar->get_image_height());
615 	m_window->register_graphic(m_cooldown_bar, GameWindow::LAYER_HUD);
616 	m_cooldown_bar_back = new TableBackground(1, COOLDOWN_BAR_WIDTH);
617 	m_cooldown_bar_back->set_row_height(0, STATUS_BAR_HEIGHT/2);
618 	m_cooldown_bar_back->set_priority(0);
619 	m_cooldown_bar_back->set_cell_color(0, Color(0.1, 0.1, 0.1, 0.5));
620 	m_cooldown_bar_back->set_x(m_cooldown_bar->get_x());
621 	m_cooldown_bar_back->set_y(m_cooldown_bar->get_y());
622 	m_window->register_graphic(m_cooldown_bar_back, GameWindow::LAYER_HUD);
623 
624 	update_cooldown_bar(0);
625 
626 	// Initialize the input bar
627 	TableBackground* input_bar_back = new TableBackground(1, 0);
628 	input_bar_back->set_cell_color(0, TEXT_BG_COLOR);
629 	m_chat_input = new TextInput(m_text_manager, 20, m_screen_height - 100, CHAT_LIMIT);
630 	m_chat_input->set_window(m_window);
631 	m_chat_input->set_background(input_bar_back);
632 	m_chat_input->set_background_scale(true);
633 	m_chat_input->set_background_padding(2);
634 	m_chat_input->set_invisible(true);
635 	m_chat_input->set_priority(0);
636 	input_bar_back->set_priority(1);
637 	m_team_chatting = false;
638 
639 	// Initialize the chat window background
640 	m_chat_window_back = new TableBackground(1, 0);
641 	m_chat_window_back->set_row_height(0, 20);
642 	m_chat_window_back->set_priority(0);
643 	m_chat_window_back->set_cell_color(0, TEXT_BG_COLOR);
644 	m_chat_window_back->set_x(17);
645 	m_chat_window_back->set_y(17);
646 	m_chat_window_back->set_invisible(true);
647 	m_window->register_graphic(m_chat_window_back, GameWindow::LAYER_SUPER);
648 
649 	// TODO: add dynamically, not at allocation
650 	if (m_configuration->get_bool_value("text_sliding")) {
651 		m_chat_window_transition_x = new Transition(m_chat_window_back, &Graphic::set_width, new LinearCurve(0,0));
652 		m_chat_window_transition_x->set_curve_ownership(true);
653 		m_transition_manager.add_transition(m_chat_window_transition_x);
654 
655 		m_chat_window_transition_y = new Transition(m_chat_window_back, &Graphic::set_height, new LinearCurve(0,0));
656 		m_chat_window_transition_y->set_curve_ownership(true);
657 		m_transition_manager.add_transition(m_chat_window_transition_y);
658 	} else {
659 		m_chat_window_transition_x = NULL;
660 		m_chat_window_transition_y = NULL;
661 	}
662 
663 	// Set up the radar.
664 	m_radar = new Radar(m_path_manager, m_params.radar_scale, m_params.radar_mode);
665 	m_radar->set_x(m_screen_width - 120);
666 	m_radar->set_y(120);
667 	m_radar->register_with_window(m_window);
668 
669 	m_current_scan_id = 0;
670 	m_offline_mode = false;
671 	// TODO: better error messages if meta server address can't be resolved
672 	if (const char* metaserver_address = getenv("LM_METASERVER")) {
673 		// Address specified by $LM_METASERVER environment avariable
674 		if (!resolve_hostname(m_metaserver_address, metaserver_address)) {
675 			display_message("Unable to contact specified metaserver. Internet-wide server browsing will be disabled.");
676 			m_offline_mode = true;
677 			cerr << "Unable to resolve metaserver hostname, `" << metaserver_address << "' as specified in the $LM_METASERVER environment variable.  Internet-wide server browsing will not be enabled." << endl;
678 		}
679 	} else if (!resolve_hostname(m_metaserver_address, METASERVER_HOSTNAME, METASERVER_PORTNO)) {
680 		display_message("Unable to contact metaserver. Internet-wide server browsing will be disabled.");
681 		m_offline_mode = true;
682 		cerr << "Unable to resolve metaserver hostname.  Internet-wide server browsing will not be enabled." << endl;
683 	}
684 
685 	// We're done contacting the metaserver, remove the message.
686 	//m_text_manager->remove_string(metaservermessage);
687 
688 	m_focus = NULL;
689 
690 	#ifndef LM_NO_UPGRADE_NAG
691 	if (!m_offline_mode) {
692 		check_for_upgrade();
693 	}
694 	#endif
695 }
696 
697 /*
698  * The main game loop.
699  */
run(int lockfps)700 void GameController::run(int lockfps) {
701 	cout << "SDL window is: " << m_window->get_width() << " pixels wide and "
702 		<< m_window->get_height() << " pixels tall." << endl;
703 
704 	unsigned long startframe = get_ticks();
705 	unsigned long lastmoveframe = startframe;
706 
707 	/* 1 second / FPS = milliseconds per frame */
708 	double delay = 1000.0 / lockfps;
709 
710 	display_message("Welcome to Leges Motus!");
711 
712 	while(m_quit_game == false) {
713 		m_cooldown_updated = false;
714 
715 		process_input();
716 
717 		m_network.resend_acks();
718 		m_network.receive_packets();
719 
720 		if (m_quit_game == true) {
721 			break;
722 		}
723 
724 		if (m_join_sent_time != 0 && m_join_sent_time + 5000 < get_ticks()) {
725 			display_message("Error: Could not connect to server.", RED_COLOR, RED_SHADOW);
726 			disconnect();
727 			m_join_sent_time = 0;
728 		}
729 
730 		// Check if my player is set to unfreeze.
731 		if (!m_players.empty() && m_time_to_unfreeze < get_ticks() && m_time_to_unfreeze != 0) {
732 			unfreeze();
733 		}
734 
735 		if (!m_players.empty() && m_players[m_player_id].is_damaged() && !m_players[m_player_id].is_dead() && !m_players[m_player_id].is_frozen()) {
736 			// Figure out how much energy to recharge, based on the time elapsed since the last recharge
737 			uint64_t		now = get_ticks();
738 
739 			if (m_params.recharge_continuously || !m_last_damage_time || now - m_last_damage_time >= m_params.recharge_delay) {
740 				uint64_t	time_elapsed = 0;
741 				if (!m_params.recharge_continuously && m_last_damage_time) {
742 					time_elapsed = (now - m_last_damage_time) - m_params.recharge_delay;
743 				} else if (m_last_recharge_time) {
744 					time_elapsed = now - m_last_recharge_time;
745 				}
746 
747 				m_last_damage_time = 0;
748 				m_last_recharge_time = now - time_elapsed % m_params.recharge_rate;
749 
750 				int	recharge = m_params.recharge_amount * (time_elapsed / m_params.recharge_rate);
751 				if (recharge) {
752 					m_players[m_player_id].change_energy(recharge);
753 					update_energy_bar();
754 				}
755 			}
756 		}
757 
758 		// Update movement twice as often as graphics.
759 		unsigned long currframe = get_ticks();
760 		if ((currframe - lastmoveframe) >= (delay/2)) {
761 			move_objects((get_ticks() - lastmoveframe) / delay); // scale all position changes to keep game speed constant.
762 
763 			if (m_time_to_unfreeze != 0) {
764 				m_frozen_status_rect->set_image_width(((m_time_to_unfreeze - get_ticks())/(double)m_total_time_frozen) * FROZEN_STATUS_RECT_WIDTH);
765 			}
766 
767 			if (m_round_end_time) {
768 				// If round will end in less than 2 minutes, display a message.
769 				if (get_ticks() >= m_round_end_time - 120000 && lastmoveframe < m_round_end_time - 120000) {
770 					display_message("Round will end in TWO minutes.", RED_COLOR, RED_SHADOW, true);
771 				}
772 
773 				// If round will end in less than 30 seconds, display a message.
774 				if (get_ticks() >= m_round_end_time - 30000 && lastmoveframe < m_round_end_time - 30000) {
775 					display_message("Round will end in THIRTY seconds.", RED_COLOR, RED_SHADOW, true);
776 				}
777 
778 				static const string numeral_to_english[10] = { "ONE", "TWO", "THREE", "FOUR", "FIVE", "SIX", "SEVEN", "EIGHT", "NINE", "TEN" };
779 
780 				for (int a = 10000; a >= 1000; a-=1000) {
781 					// If round will end in less than <a> seconds, display a message.
782 					if (get_ticks() >= m_round_end_time - a && lastmoveframe < m_round_end_time - a) {
783 						display_message("Round will end in " + numeral_to_english[a/1000-1] + " seconds.", RED_COLOR, RED_SHADOW, true);
784 					}
785 				}
786 			}
787 
788 			lastmoveframe = get_ticks();
789 		}
790 
791 
792 		// Update graphics if frame rate is correct.
793 		if((currframe - startframe) >= delay) {
794 			if (m_network.is_connected() && m_join_sent_time == 0) {
795 				if ((currframe - m_last_ping_sent) >= PING_FREQUENCY) {
796 					ping_server(m_network.get_server_address());
797 					m_last_ping_sent = currframe;
798 				}
799 
800 				if (m_network.get_last_packet_time() + NETWORK_TIMEOUT_LIMIT < currframe) {
801 					display_message("Connection to the server has timed out.", RED_COLOR, RED_SHADOW);
802 					disconnect();
803 					continue;
804 				}
805 			}
806 
807 			if (!m_server_browser->is_invisible()) {
808 				m_server_browser->autoscroll(currframe - startframe);
809 			}
810 			if (!m_overlay_scrollbar->is_invisible()) {
811 				m_overlay_scrollbar->autoscroll(currframe - startframe);
812 			}
813 			if (!m_chat_log->is_invisible()) {
814 				m_chat_log->autoscroll(currframe - startframe);
815 			}
816 
817 			bool erasedone = false;
818 			// Erase messages that are too old.
819 			double height;
820 			if (m_configuration->get_bool_value("text_sliding")) {
821 				height = m_chat_window_transition_y->get_curve()->get_end();
822 			} else {
823 				height = m_chat_window_back->get_row_height(0);
824 			}
825 			for (vector<Message>::iterator iter = m_messages.begin();
826 					iter != m_messages.end();) {
827 				if (iter->timeout < currframe || m_messages.size() > MAX_MESSAGES_TO_DISPLAY) {
828 					height -= iter->message->get_image_height();
829 					m_text_manager->remove_string(iter->message);
830 					if (iter->transition != NULL) {
831 						m_transition_manager.remove_transition(iter->transition);
832 						delete iter->transition;
833 					}
834 					iter = m_messages.erase(iter);
835 					erasedone = true;
836 				} else {
837 				 iter++;
838 				}
839 			}
840 
841 
842 			if (erasedone) {
843 				m_chat_window_back->set_image_width(0);
844 				// Reposition messages that remain after removing.
845 				double max_w = 0;
846 				for (unsigned int i = 0; i < m_messages.size(); i++) {
847 					int y = 20 + (m_font->ascent() + m_font->descent() + 5) * i;
848 					if (m_configuration->get_bool_value("text_sliding")) {
849 						m_messages[i].transition->change_curve(currframe, new LinearCurve(0, y), CHAT_TRANSITION_TIME);
850 					} else {
851 						m_messages[i].message->set_y(y);
852 					}
853 					max_w = max<double>(m_messages[i].message->get_image_width() + 6, max_w);
854 				}
855 				if (m_configuration->get_bool_value("text_sliding")) {
856 					m_chat_window_transition_x->change_curve(currframe, new LinearCurve(0, max_w), CHAT_TRANSITION_TIME);
857 					m_chat_window_transition_y->change_curve(currframe, new LinearCurve(0, height), CHAT_TRANSITION_TIME);
858 				} else {
859 					m_chat_window_back->set_width(max_w);
860 					m_chat_window_back->set_height(height);
861 				}
862 				if (m_messages.size() == 0) {
863 					m_chat_window_back->set_invisible(true);
864 				}
865 			}
866 
867 			// Change shot displays based on time.
868 			for (unsigned int i = 0; i < m_shots.size(); i++) {
869 				double shot_time = (double)(SHOT_DISPLAY_TIME-(m_shots[i].second - currframe))/SHOT_DISPLAY_TIME;
870 				double shot_curve = -4.5*shot_time*(shot_time-1.0)/(shot_time+0.5); //fancy curve
871 				m_shots[i].first->set_scale_x(shot_curve);
872 				m_shots[i].first->set_scale_y(shot_curve);
873 				m_shots[i].first->set_rotation(shot_time*90.0);
874 				if (m_shots[i].second < currframe) {
875 					m_window->unregister_graphic(m_shots[i].first, GameWindow::LAYER_GAME);
876 					delete m_shots[i].first;
877 					m_shots.erase(m_shots.begin() + i);
878 				}
879 			}
880 
881 			// Change the display of the gate warning message based on time.
882 			if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - GATE_WARNING_FLASH_LENGTH) {
883 				m_gate_warning_time = 0;
884 				m_gate_warning->set_invisible(true);
885 			} else if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - (5*GATE_WARNING_FLASH_LENGTH)/6) {
886 				m_gate_warning->set_invisible(false);
887 			} else if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - (4*GATE_WARNING_FLASH_LENGTH)/6) {
888 				m_gate_warning->set_invisible(true);
889 			} else if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - (3*GATE_WARNING_FLASH_LENGTH)/6) {
890 				m_gate_warning->set_invisible(false);
891 			} else if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - (2*GATE_WARNING_FLASH_LENGTH)/6) {
892 				m_gate_warning->set_invisible(true);
893 			} else if (m_gate_warning_time != 0 && m_gate_warning_time < currframe - (1*GATE_WARNING_FLASH_LENGTH)/6) {
894 				m_gate_warning->set_invisible(false);
895 			}
896 
897 			// Update the cooldown bar.
898 			if (!m_cooldown_updated) {
899 				update_cooldown_bar();
900 			}
901 
902 			// Uncomment if framerate is needed.
903 			// the framerate:
904 			m_framerate = (1000/(currframe - startframe));
905 			m_transition_manager.update(currframe);
906 
907 			if (!m_players.empty()) {
908 				//cerr << m_players[m_player_id].get_sprite()->get_rotation() << ", ";
909 				m_players[m_player_id].set_rotation_degrees(m_players[m_player_id].get_sprite()->get_rotation());
910 
911 				// Change gun sprite if muzzle flash is done.
912 				Graphic* frontarm = m_players[m_player_id].get_sprite()->get_graphic("frontarm");
913 				if (m_muzzle_flash_start != 0 && get_ticks() - m_muzzle_flash_start >= MUZZLE_FLASH_LENGTH && frontarm->get_graphic("normal")->is_invisible()) {
914 					frontarm->get_graphic("normal")->set_invisible(false);
915 					send_animation_packet("frontarm/normal", "invisible", false);
916 					frontarm->get_graphic("firing")->set_invisible(true);
917 					send_animation_packet("frontarm/firing", "invisible", true);
918 					m_muzzle_flash_start = 0;
919 				}
920 
921 				m_crosshairs->set_x(m_mouse_x);
922 				m_crosshairs->set_y(m_mouse_y);
923 
924 				// Turn arm of player to face crosshairs.
925 				if (!m_players[m_player_id].is_frozen() && !m_players[m_player_id].is_dead()) {
926 					double	angle = get_normalized_angle(to_degrees(get_crosshairs_angle()) - m_players[m_player_id].get_rotation_degrees());
927 
928 					if (angle < 90 || angle > 270) {
929 						m_players[m_player_id].get_sprite()->set_scale_x(-1);
930 						send_animation_packet("all", "scale_x", -1);
931 						angle *= -1;
932 						angle += 55;
933 					} else {
934 						m_players[m_player_id].get_sprite()->set_scale_x(1);
935 						send_animation_packet("all", "scale_x", 1);
936 						angle -= 120;
937 					}
938 					m_players[m_player_id].get_sprite()->get_graphic("frontarm")->set_rotation(angle);
939 					send_animation_packet("frontarm", "rotation", int(round(angle))); // XXX: going double->int here
940 				}
941 				send_my_player_update();
942 				m_radar->update(currframe);
943 
944 				// Set the new offset of the window so that the player is centered.
945 				m_offset_x = m_players[m_player_id].get_x() - (m_screen_width/2.0);
946 				m_offset_y = m_players[m_player_id].get_y() - (m_screen_height/2.0);
947 				m_window->set_offset_x(m_offset_x);
948 				m_window->set_offset_y(m_offset_y);
949 			}
950 
951 			update_visible_elements();
952 
953 			m_window->redraw();
954 			startframe = get_ticks();
955 		}
956 	}
957 
958 	if (!m_restart) {
959 		disconnect();
960 	}
961 }
962 
update_visible_elements()963 void GameController::update_visible_elements() {
964 	// Show and hide elements depending on game state (started, menus, etc.)
965 	if (m_game_state == SHOW_MENUS) {
966 		set_hud_visible(false);
967 
968 		m_logo->set_invisible(false);
969 		m_window->set_layer_visible(true, GameWindow::LAYER_MENU);
970 
971 		toggle_main_menu(true);
972 		toggle_options_menu(false);
973 		m_server_browser->set_visible(false);
974 		m_weapon_selector->get_graphic_group()->set_invisible(true);
975 	} else if (m_game_state == SHOW_OPTIONS_MENU) {
976 		set_hud_visible(false);
977 
978 		m_logo->set_invisible(false);
979 		m_window->set_layer_visible(true, GameWindow::LAYER_MENU);
980 
981 		toggle_main_menu(false);
982 		toggle_options_menu(true);
983 		m_server_browser->set_visible(false);
984 		m_weapon_selector->get_graphic_group()->set_invisible(true);
985 	} else if (m_game_state == SHOW_SERVER_BROWSER) {
986 		set_hud_visible(false);
987 
988 		m_logo->set_invisible(false);
989 		m_window->set_layer_visible(true, GameWindow::LAYER_MENU);
990 
991 		toggle_main_menu(false);
992 		toggle_options_menu(false);
993 		m_server_browser->set_visible(true);
994 		m_weapon_selector->get_graphic_group()->set_invisible(true);
995 	} else {
996 
997 		set_hud_visible(true);
998 		m_window->set_layer_visible(false, GameWindow::LAYER_MENU);
999 	}
1000 }
1001 
init_weapon_selector()1002 void GameController::init_weapon_selector() {
1003 	// Initialize the weapon selector menu.
1004 	if (m_weapon_selector != NULL) {
1005 		m_window->unregister_graphic(m_weapon_selector->get_graphic_group(), GameWindow::LAYER_HUD);
1006 		delete m_weapon_selector;
1007 	}
1008 
1009 	m_weapon_selector_back = new RadialBackground(1);
1010 	m_weapon_selector_back->set_border_color(Color(0.0,0.0,0.0,0.0));
1011 	m_weapon_selector_back->set_inner_radius(80.0);
1012 	m_weapon_selector_back->set_outer_radius(70.0);
1013 	m_weapon_selector_back->set_border_radius(3.0);
1014 	m_weapon_selector_back->set_border_angle(3);
1015 	m_weapon_selector_back->set_x(m_screen_width/2);
1016 	m_weapon_selector_back->set_y(m_screen_height/2);
1017 	m_weapon_selector_back->set_rotation(180.0/m_weapons.size());
1018 
1019 	m_weapon_selector = new RadialMenu(m_weapon_selector_back, Color(0.2,0.2,0.6,0.8), Color(0.1,0.1,0.3,1));
1020 
1021 	GraphicMenuItem *cur_item;
1022 	for (map<std::string, Weapon*>::iterator it(m_weapons.begin()); it != m_weapons.end(); ++it) {
1023 		Graphic* this_graphic = m_graphics_cache.new_graphic<Sprite>(m_path_manager.data_path(it->second->hud_graphic(), "sprites"));
1024 		cur_item = new GraphicMenuItem(this_graphic, it->first);
1025 		cur_item->set_scale(0.75);
1026 		m_weapon_selector->add_item(cur_item);
1027 	}
1028 
1029 	m_weapon_selector->get_graphic_group()->set_invisible(true);
1030 	m_window->register_graphic(m_weapon_selector->get_graphic_group(), GameWindow::LAYER_HUD);
1031 }
1032 
1033 /*
1034  * Set the HUD visible or invisible.
1035  */
set_hud_visible(bool visible)1036 void GameController::set_hud_visible(bool visible) {
1037 	m_radar->set_invisible(!visible);
1038 
1039 	m_blue_gate_status_rect->set_invisible(!visible);
1040 	m_blue_gate_status_text->set_invisible(!visible);
1041 	m_blue_gate_status_rect_back->set_invisible(!visible);
1042 	m_red_gate_status_rect->set_invisible(!visible);
1043 	m_red_gate_status_text->set_invisible(!visible);
1044 	m_red_gate_status_rect_back->set_invisible(!visible);
1045 
1046 	GraphicalPlayer* player = get_player_by_id(m_player_id);
1047 
1048 	if (player == NULL) {
1049 		m_frozen_status_rect->set_invisible(true);
1050 		m_frozen_status_text->set_invisible(true);
1051 		m_frozen_status_rect_back->set_invisible(true);
1052 		m_energy_bar->set_invisible(true);
1053 		m_energy_bar_back->set_invisible(true);
1054 		m_energy_text->set_invisible(true);
1055 		m_cooldown_bar->set_invisible(true);
1056 		m_cooldown_bar_back->set_invisible(true);
1057 		if (m_curr_weapon_image != NULL) {
1058 			m_curr_weapon_image->set_invisible(true);
1059 		}
1060 		return;
1061 	}
1062 
1063 	m_energy_bar->set_invisible(!visible);
1064 	m_energy_bar_back->set_invisible(!visible);
1065 	m_energy_text->set_invisible(!visible);
1066 
1067 	m_cooldown_bar->set_invisible(!visible);
1068 	m_cooldown_bar_back->set_invisible(!visible);
1069 
1070 	if (m_curr_weapon_image != NULL) {
1071 		// Uncomment the following to show the current weapon image only when done switching
1072 		m_curr_weapon_image->set_invisible(!visible);
1073 //		if (m_last_weapon_switch == 0 || (get_ticks() - m_last_weapon_switch) > m_params.weapon_switch_delay) {
1074 //			m_curr_weapon_image->set_invisible(!visible);
1075 //		} else {
1076 //			m_curr_weapon_image->set_invisible(true);
1077 //		}
1078 	}
1079 
1080 	if (player->is_frozen() && !player->is_invisible() && m_total_time_frozen > 100) {
1081 		m_frozen_status_rect->set_invisible(!visible);
1082 		m_frozen_status_text->set_invisible(!visible);
1083 		m_frozen_status_rect_back->set_invisible(!visible);
1084 	} else {
1085 		m_frozen_status_rect->set_invisible(true);
1086 		m_frozen_status_text->set_invisible(true);
1087 		m_frozen_status_rect_back->set_invisible(true);
1088 	}
1089 }
1090 
1091 /*
1092  * Update the energy bar.
1093  */
update_energy_bar(int new_energy)1094 void GameController::update_energy_bar(int new_energy) {
1095 	if (GraphicalPlayer* player = get_player_by_id(m_player_id)) {
1096 		if (new_energy != -1) {
1097 			player->set_energy(new_energy);
1098 		} else {
1099 			new_energy = player->get_energy();
1100 		}
1101 	} else if (new_energy == -1) {
1102 		new_energy = 0;
1103 	}
1104 
1105 	m_energy_bar->set_image_width((ENERGY_BAR_WIDTH-2) * ((double)new_energy/(GraphicalPlayer::MAX_ENERGY)) + 2);
1106 
1107 	if (m_energy_text != NULL) {
1108 		m_text_manager->remove_string(m_energy_text);
1109 	}
1110 
1111 	// Re-initialize the label.
1112 	m_text_manager->set_active_color(1.0, 1.0, 1.0);
1113 	m_text_manager->set_active_font(m_font);
1114 
1115 	ostringstream energystring;
1116 	energystring << "E: " << new_energy;
1117 	m_energy_text = m_text_manager->place_string(energystring.str(), m_energy_bar->get_x(), m_energy_bar->get_y() + STATUS_BAR_HEIGHT/2, TextManager::CENTER, GameWindow::LAYER_HUD, TEXT_LAYER);
1118 	m_energy_text->set_y(m_energy_bar->get_y() + STATUS_BAR_HEIGHT/2 - m_energy_text->get_image_height()/2);
1119 }
1120 
1121 /*
1122  * Update the cooldown bar.
1123  */
update_cooldown_bar(double new_cooldown)1124 void GameController::update_cooldown_bar(double new_cooldown) {
1125 	if (m_current_weapon != NULL) {
1126 		if (new_cooldown == -1) {
1127 			if (m_current_weapon->has_limited_ammo()) {
1128 				// Show ammo consumption in cooldown bar
1129 				new_cooldown = 1.0 - m_current_weapon->get_current_ammo()/((double)m_current_weapon->get_total_ammo());
1130 			} else {
1131 				new_cooldown = m_current_weapon->get_remaining_cooldown()/((double)m_current_weapon->get_total_cooldown());
1132 			}
1133 			if ((get_ticks() - m_last_weapon_switch) < m_params.weapon_switch_delay) {
1134 				if (m_last_weapon_switch != 0 && new_cooldown < (m_params.weapon_switch_delay - (get_ticks() - m_last_weapon_switch))/(double)m_params.weapon_switch_delay) {
1135 					new_cooldown = (m_params.weapon_switch_delay - (get_ticks() - m_last_weapon_switch))/(double)m_params.weapon_switch_delay;
1136 				}
1137 			}
1138 		}
1139 	} else if (new_cooldown == -1) {
1140 		new_cooldown = 0;
1141 	}
1142 
1143 	m_cooldown_updated = true;
1144 
1145 	m_cooldown_bar->set_image_width((COOLDOWN_BAR_WIDTH-2) * (1.0-new_cooldown) + 2);
1146 }
1147 
1148 /*
1149  * Set the dimensions of the screen.
1150  */
set_screen_dimensions(int width,int height)1151 void GameController::set_screen_dimensions(int width, int height) {
1152 	m_screen_width = width;
1153 	m_screen_height = height;
1154 }
1155 
1156 /*
1157  * Process the current SDL input state.
1158  */
process_input()1159 void GameController::process_input() {
1160 	SDL_Event event;
1161 	while(SDL_PollEvent(&event) != 0) {
1162 		switch(event.type) {
1163 			case SDL_KEYDOWN:
1164 				if (event.key.keysym.sym == m_key_bindings.quit || event.key.keysym.sym == m_alt_key_bindings.quit) {
1165 					cerr << "Quit key pressed - quitting." << endl;
1166 					m_quit_game = true;
1167 				}
1168 
1169 				// If we're typing into the input bar...
1170 				if (m_focus != NULL) {
1171 					if (event.key.keysym.sym == m_key_bindings.show_overlay || event.key.keysym.sym == m_alt_key_bindings.show_overlay) {
1172 						parse_key_input();
1173 						break;
1174 					}
1175 
1176 					m_text_manager->set_active_color(TEXT_COLOR);
1177 					// If we're going back to the menu, remove the input bar.
1178 					if (event.key.keysym.sym == m_key_bindings.show_menu || event.key.keysym.sym == m_alt_key_bindings.show_menu) {
1179 						if (!m_chat_log->is_invisible()) {
1180 							m_chat_log->set_visible(false);
1181 						}
1182 						SDL_EnableUNICODE(0);
1183 						SDL_EnableKeyRepeat(0, 0); // Disable key repeat
1184 						m_chat_input->set_invisible(true);
1185 						m_chat_input->reset();
1186 						m_focus = NULL;
1187 						//m_input_text = "> ";
1188 					} else if (event.key.keysym.sym == m_key_bindings.send_chat || event.key.keysym.sym == m_alt_key_bindings.send_chat) {
1189 						if (m_focus == m_chat_input) {
1190 							if (m_input_text.find("[TEAM]> ") == 0) {
1191 								string msg = m_input_text.substr(8);
1192 								m_input_text = ((string)"> /tchat ").append(msg);
1193 							}
1194 
1195 							string message = m_chat_input->get_value();
1196 
1197 							// Check message for commands.
1198 							if (message == "/quit") {
1199 								m_quit_game = true;
1200 							} else if (message.find("/name ") == 0) {
1201 								string new_name(message.substr(6));
1202 								if (!m_network.is_connected()) {
1203 									ostringstream	msg;
1204 									msg << "You are now known as " << new_name;
1205 									display_message(msg.str());
1206 								}
1207 								set_player_name(new_name);
1208 							} else if (message.find("/team ") == 0) {
1209 								string	new_team_string(message.substr(6));
1210 								char	new_team = parse_team_string(new_team_string.c_str());
1211 								if (is_valid_team(new_team)) {
1212 									send_team_change_packet(new_team);
1213 								} else {
1214 									display_message("Please enter '/team blue' or '/team red'");
1215 								}
1216 							} else if (message == "/copying" || message == "/warranty" || message == "/legal" || message == "/copyright") {
1217 								display_legalese();
1218 							} else if (message.find("/tchat ") == 0) {
1219 								send_team_message(message.substr(7));
1220 							} else if (m_team_chatting) {
1221 								send_team_message(message);
1222 							} else {
1223 								send_message(message);
1224 							}
1225 							m_chat_input->set_invisible(true);
1226 							m_chat_input->reset();
1227 						} else if (m_focus == m_name_input) {
1228 							m_name_bar_back->set_border_color(Color(0, 0, 0, 0));
1229 						}
1230 
1231 						// Remove the input bar.
1232 						SDL_EnableUNICODE(0);
1233 						SDL_EnableKeyRepeat(0, 0); // Disable key repeat
1234 						m_focus = NULL;
1235 					} else {
1236 						if (m_focus == m_chat_input) {
1237 							m_text_manager->set_active_font(m_font);
1238 						} else if (m_focus == m_name_input) {
1239 							m_text_manager->set_active_font(m_menu_font);
1240 						}
1241 						m_focus->keyboard_event(event.key);
1242 					}
1243 				} else {
1244 					//Check which key using: event.key.keysym.sym == SDLK_<SOMETHING>
1245 					if (m_game_state == GAME_IN_PROGRESS && (event.key.keysym.sym == m_key_bindings.jump || event.key.keysym.sym == m_alt_key_bindings.jump)) {
1246 						attempt_jump();
1247 					} else if (event.key.keysym.sym == m_key_bindings.open_chat || event.key.keysym.sym == m_key_bindings.open_console || event.key.keysym.sym == m_alt_key_bindings.open_chat || event.key.keysym.sym == m_alt_key_bindings.open_console) {
1248 						SDL_EnableUNICODE(1);
1249 						SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1250 						m_text_manager->set_active_color(TEXT_COLOR);
1251 						m_team_chatting = false;
1252 						m_chat_input->reset();
1253 						m_text_manager->set_active_font(m_bold_font);
1254 						m_text_manager->set_active_color(TEXT_COLOR);
1255 						m_chat_input->set_prefix("> ");
1256 						m_chat_input->set_invisible(false);
1257 						m_focus = m_chat_input;
1258 						if (event.key.keysym.sym == m_key_bindings.open_console || event.key.keysym.sym == m_alt_key_bindings.open_console) {
1259 							m_chat_log->set_visible(true);
1260 						}
1261 					} else if (event.key.keysym.sym == m_key_bindings.open_team_chat || event.key.keysym.sym == m_alt_key_bindings.open_team_chat) {
1262 						SDL_EnableUNICODE(1);
1263 						SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1264 						m_text_manager->set_active_color(TEXT_COLOR);
1265 						m_text_manager->set_active_font(m_font);
1266 						m_team_chatting = true;
1267 						m_chat_input->reset();
1268 						m_text_manager->set_active_font(m_bold_font);
1269 						m_text_manager->set_active_color(TEXT_COLOR);
1270 						m_chat_input->set_prefix("[TEAM]> ");
1271 						m_chat_input->set_invisible(false);
1272 						m_focus = m_chat_input;
1273 					} else if (event.key.keysym.sym == m_key_bindings.show_menu || event.key.keysym.sym == m_alt_key_bindings.show_menu) {
1274 						if (!m_chat_log->is_invisible()) {
1275 							m_chat_log->set_visible(false);
1276 						} else if (m_game_state == SHOW_MENUS && !m_players.empty()) {
1277 							m_game_state = GAME_IN_PROGRESS;
1278 						} else {
1279 							reset_options();
1280 							m_game_state = SHOW_MENUS;
1281 						}
1282 					}
1283 				}
1284 				break;
1285 
1286 			case SDL_KEYUP:
1287 				break;
1288 
1289 			case SDL_MOUSEMOTION:
1290 				// Use: event.motion.xrel, event.motion.yrel (changes in position), event.motion.x, event.motion.y
1291 				m_mouse_x = event.motion.x;
1292 				m_mouse_y = event.motion.y;
1293 				m_crosshairs->set_x(m_mouse_x);
1294 				m_crosshairs->set_y(m_mouse_y);
1295 				if (!m_server_browser->is_invisible()) {
1296 					m_server_browser->scrollbar_motion_event(event.motion);
1297 				}
1298 				if (!m_overlay_scrollbar->is_invisible()) {
1299 					m_overlay_scrollbar->mouse_motion_event(event.motion);
1300 				}
1301 				if (!m_chat_log->is_invisible()) {
1302 					m_chat_log->scrollbar_motion_event(event.motion);
1303 				}
1304 				if (m_weapon_selector != NULL && m_weapons.size() > 0 /*&& !m_weapon_selector->get_graphic_group()->is_invisible()*/) {
1305 					m_weapon_selector->mouse_motion_event(event.motion);
1306 				}
1307 
1308 				if (m_game_state == SHOW_MENUS) {
1309 					m_main_menu.mouse_motion_event(event.motion);
1310 					// TODO: Move this. Why is it HERE?
1311 					if (!m_network.is_connected() || !m_join_sent_time == 0) {
1312 						m_item_resume->set_state(MenuItem::DISABLED);
1313 						m_item_disconnect->set_state(MenuItem::DISABLED);
1314 					}
1315 				} else if (m_game_state == SHOW_OPTIONS_MENU) {
1316 					m_options_menu.mouse_motion_event(event.motion);
1317 				}
1318 				break;
1319 
1320 			case SDL_MOUSEBUTTONDOWN:
1321 				// Firing code, use event.button.button, event.button.x, event.button.y
1322 				process_mouse_click(event);
1323 				if (!m_server_browser->is_invisible()) {
1324 					m_server_browser->scrollbar_button_event(event.button);
1325 				}
1326 				if (!m_overlay_scrollbar->is_invisible()) {
1327 					m_overlay_scrollbar->mouse_button_event(event.button);
1328 				}
1329 				if (!m_chat_log->is_invisible()) {
1330 					m_chat_log->scrollbar_button_event(event.button);
1331 				}
1332 				break;
1333 
1334 			case SDL_MOUSEBUTTONUP:
1335 				process_mouse_click(event);
1336 				if (!m_server_browser->is_invisible()) {
1337 					m_server_browser->scrollbar_button_event(event.button);
1338 				}
1339 				if (!m_overlay_scrollbar->is_invisible()) {
1340 					m_overlay_scrollbar->mouse_button_event(event.button);
1341 				}
1342 				if (!m_chat_log->is_invisible()) {
1343 					m_chat_log->scrollbar_button_event(event.button);
1344 				}
1345 				break;
1346 
1347 			case SDL_QUIT:
1348 				m_quit_game = true;
1349 				break;
1350 
1351 			default:
1352 				break;
1353 		}
1354 	}
1355 
1356 	// Check if left mouse button is held down, for weapon firing code.
1357 	int mouse_x = 0;
1358 	int mouse_y = 0;
1359 	Uint8 mouse_state = SDL_GetMouseState(&mouse_x, &mouse_y);
1360 	if (mouse_state&SDL_BUTTON(1)) {
1361 		if (m_game_state == GAME_IN_PROGRESS) {
1362 			if (m_players.empty() || m_players[m_player_id].is_frozen() || m_players[m_player_id].is_dead() || !m_current_weapon) {
1363 				// Do nothing. We don't have a current player or weapon.
1364 			} else if (!m_overlay_background->is_invisible()) {
1365 				// Do nothing. The overlay is up.
1366 			} else if (!m_chat_log->is_invisible()) {
1367 				// Do nothing. The chat log is up.
1368 			} else if (m_current_weapon->is_continuous()) {
1369 				// Fire the gun if it's ready.
1370 				double x_dist = (mouse_x + m_offset_x) - m_players[m_player_id].get_x();
1371 				double y_dist = (mouse_y + m_offset_y) - m_players[m_player_id].get_y();
1372 				double direction = atan2(y_dist, x_dist);
1373 				if (m_last_weapon_switch == 0 || (get_ticks() - m_last_weapon_switch) > m_params.weapon_switch_delay) {
1374 					m_current_weapon->fire(m_players[m_player_id], *this, m_players[m_player_id].get_position(), direction);
1375 				}
1376 			}
1377 		}
1378 	}
1379 
1380 	// Check if the right mouse button is held down, for weapon switching.
1381 	if (mouse_state&SDL_BUTTON(3)) {
1382 		if (GraphicalPlayer* player = get_player_by_id(m_player_id)) {
1383 			if (m_weapons.size() > 0 && m_game_state == GAME_IN_PROGRESS) {
1384 				m_weapon_selector->get_graphic_group()->set_invisible(false);
1385 			}
1386 		}
1387 	} else {
1388 		if (m_game_state == GAME_IN_PROGRESS) {
1389 			if (!m_weapon_selector->get_graphic_group()->is_invisible()) {
1390 				MenuItem* over = m_weapon_selector->item_at_position(mouse_x, mouse_y);
1391 				if (over != NULL) {
1392 					change_weapon(over->get_name().c_str());
1393 				}
1394 			}
1395 			m_weapon_selector->get_graphic_group()->set_invisible(true);
1396 		}
1397 	}
1398 
1399 	parse_key_input();
1400 }
1401 /*
1402  * Set which keys are used for which functions.
1403  */
initialize_key_bindings()1404 void GameController::initialize_key_bindings() {
1405 	// -1 = unused
1406 	m_key_bindings.quit = -1;
1407 	m_key_bindings.jump = SDLK_SPACE;
1408 	m_key_bindings.show_overlay = SDLK_TAB;
1409 	m_key_bindings.show_menu = SDLK_ESCAPE;
1410 	m_key_bindings.open_chat = SDLK_t;
1411 	m_key_bindings.open_team_chat = SDLK_y;
1412 	m_key_bindings.open_console = SDLK_BACKQUOTE;
1413 	m_key_bindings.send_chat = SDLK_RETURN;
1414 	m_key_bindings.weapon_1 = SDLK_1;
1415 	m_key_bindings.weapon_2 = SDLK_2;
1416 	m_key_bindings.weapon_3 = SDLK_3;
1417 	m_key_bindings.weapon_4 = SDLK_4;
1418 	m_key_bindings.weapon_5 = SDLK_5;
1419 	m_key_bindings.weapon_6 = SDLK_6;
1420 	m_key_bindings.weapon_7 = SDLK_7;
1421 	m_key_bindings.weapon_8 = SDLK_8;
1422 
1423 	m_alt_key_bindings.quit = -1;
1424 	m_alt_key_bindings.jump = -1;
1425 	m_alt_key_bindings.show_overlay = -1;
1426 	m_alt_key_bindings.show_menu = -1;
1427 	m_alt_key_bindings.open_chat = -1;
1428 	m_alt_key_bindings.open_team_chat = -1;
1429 	m_alt_key_bindings.open_console = -1;
1430 	m_alt_key_bindings.send_chat = SDLK_KP_ENTER;
1431 	m_alt_key_bindings.weapon_1 = SDLK_KP1;
1432 	m_alt_key_bindings.weapon_2 = SDLK_KP2;
1433 	m_alt_key_bindings.weapon_3 = SDLK_KP3;
1434 	m_alt_key_bindings.weapon_4 = SDLK_KP4;
1435 	m_alt_key_bindings.weapon_5 = SDLK_KP5;
1436 	m_alt_key_bindings.weapon_6 = SDLK_KP6;
1437 	m_alt_key_bindings.weapon_7 = SDLK_KP7;
1438 	m_alt_key_bindings.weapon_8 = SDLK_KP8;
1439 }
1440 
1441 /*
1442  * Used to process any keys that are to be held down rather than pressed once.
1443  */
parse_key_input()1444 void GameController::parse_key_input() {
1445 	// For keys that can be held down:
1446    	m_keys = SDL_GetKeyState(NULL);
1447 
1448    	if (m_game_state == GAME_IN_PROGRESS && m_focus == NULL) {
1449 		if ((m_key_bindings.jump != -1 && m_keys[m_key_bindings.jump]) || (m_alt_key_bindings.jump != -1 && m_keys[m_alt_key_bindings.jump])) {
1450 			attempt_jump();
1451 		}
1452 		if ((m_key_bindings.weapon_1 != -1 && m_keys[m_key_bindings.weapon_1]) || (m_alt_key_bindings.weapon_1 != -1 && m_keys[m_alt_key_bindings.weapon_1])) {
1453 			change_weapon(0U);
1454 		}
1455 		if ((m_key_bindings.weapon_2 != -1 && m_keys[m_key_bindings.weapon_2]) || (m_alt_key_bindings.weapon_2 != -1 && m_keys[m_alt_key_bindings.weapon_2])) {
1456 			change_weapon(1U);
1457 		}
1458 		if ((m_key_bindings.weapon_3 != -1 && m_keys[m_key_bindings.weapon_3]) || (m_alt_key_bindings.weapon_3 != -1 && m_keys[m_alt_key_bindings.weapon_3])) {
1459 			change_weapon(2U);
1460 		}
1461 		if ((m_key_bindings.weapon_4 != -1 && m_keys[m_key_bindings.weapon_4]) || (m_alt_key_bindings.weapon_4 != -1 && m_keys[m_alt_key_bindings.weapon_4])) {
1462 			change_weapon(3U);
1463 		}
1464 		if ((m_key_bindings.weapon_5 != -1 && m_keys[m_key_bindings.weapon_5]) || (m_alt_key_bindings.weapon_5 != -1 && m_keys[m_alt_key_bindings.weapon_5])) {
1465 			change_weapon(4U);
1466 		}
1467 		if ((m_key_bindings.weapon_6 != -1 && m_keys[m_key_bindings.weapon_6]) || (m_alt_key_bindings.weapon_6 != -1 && m_keys[m_alt_key_bindings.weapon_6])) {
1468 			change_weapon(5U);
1469 		}
1470 		if ((m_key_bindings.weapon_7 != -1 && m_keys[m_key_bindings.weapon_7]) || (m_alt_key_bindings.weapon_7 != -1 && m_keys[m_alt_key_bindings.weapon_7])) {
1471 			change_weapon(6U);
1472 		}
1473 		if ((m_key_bindings.weapon_8 != -1 && m_keys[m_key_bindings.weapon_8]) || (m_alt_key_bindings.weapon_8 != -1 && m_keys[m_alt_key_bindings.weapon_8])) {
1474 			change_weapon(7U);
1475 		}
1476 	}
1477 
1478 	if ((m_key_bindings.show_overlay != -1 && m_keys[m_key_bindings.show_overlay]) || (m_alt_key_bindings.show_overlay != -1 && m_keys[m_alt_key_bindings.show_overlay])) {
1479 		if (m_game_state == GAME_IN_PROGRESS) {
1480 			if (m_overlay_background->is_invisible() == true) {
1481 				toggle_score_overlay(true);
1482 			}
1483 		} else if (m_game_state != GAME_OVER) {
1484 			if (m_overlay_background->is_invisible() == false) {
1485 				toggle_score_overlay(false);
1486 			}
1487 		}
1488 	} else {
1489 		if (m_game_state != GAME_OVER && m_overlay_background->is_invisible() == false) {
1490 			toggle_score_overlay(false);
1491 		}
1492 	}
1493 }
1494 
1495 /*
1496  * Process a mouse click, depending on the game state.
1497  */
process_mouse_click(SDL_Event event)1498 void GameController::process_mouse_click(SDL_Event event) {
1499 	switch (m_game_state) {
1500 		case SHOW_MENUS: {
1501 			if (event.button.button != 1) {
1502 				return;
1503 			}
1504 			MenuItem* item = m_main_menu.mouse_button_event(event.button);
1505 			if (item && event.type == SDL_MOUSEBUTTONUP) {
1506 				item->set_state(MenuItem::NORMAL);
1507 				m_sound_controller->play_sound("click");
1508 				if (item->get_name() == "quit") {
1509 					m_quit_game = true;
1510 				} else if (item->get_name() == "resume") {
1511 					if (!m_players.empty()) {
1512 						m_game_state = GAME_IN_PROGRESS;
1513 					} else {
1514 						display_message("Not connected to server.");
1515 					}
1516 				} else if (item->get_name() == "submenu:options") {
1517 					m_game_state = SHOW_OPTIONS_MENU;
1518 				} else if (item->get_name() == "disconnect") {
1519 					if (!m_players.empty()) {
1520 						disconnect();
1521 					} else {
1522 						display_message("Not connected to server.");
1523 					}
1524 				} else if (item->get_name() == "connect") {
1525 					m_game_state = SHOW_SERVER_BROWSER;
1526 					scan_all();
1527 				}
1528 			}
1529 		}
1530 		break;
1531 		case SHOW_OPTIONS_MENU: {
1532 			if (event.button.button != 1) {
1533 				return;
1534 			}
1535 			MenuItem* item = m_options_menu.mouse_button_event(event.button);
1536 			if (item && event.type == SDL_MOUSEBUTTONUP) {
1537 				m_sound_controller->play_sound("click");
1538 				if(item->get_name() == "cancel") {
1539 					if (m_focus == m_name_input) {
1540 						// TODO generalize
1541 						m_name_bar_back->set_border_color(Color(0, 0, 0, 0));
1542 						m_focus = NULL;
1543 					}
1544 					reset_options();
1545 					m_game_state = SHOW_MENUS;
1546 				} else if(item->get_name() == "name") {
1547 					// Open the input bar and allow the name to be entered.
1548 					SDL_EnableUNICODE(1);
1549 					SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
1550 					if (m_focus != NULL && m_focus != m_name_input) {
1551 						m_focus->reset();
1552 						m_focus->set_invisible(true);
1553 					}
1554 					m_name_bar_back->set_border_color(TEXT_COLOR);
1555 					m_focus = m_name_input;
1556 				} else {
1557 					m_name_bar_back->set_border_color(Color(0, 0, 0, 0));
1558 					m_focus = NULL;
1559 					if(item->get_name() == "apply") {
1560 						string resolution = m_options_form.get_item("resolution")->get_value();
1561 						size_t xpos = resolution.find('x');
1562 						int width;
1563 						int height;
1564 						int multisample;
1565 						istringstream wstring(resolution.substr(0, xpos));
1566 						istringstream hstring(resolution.substr(xpos + 1));
1567 						istringstream mstring(m_options_form.get_item("multisample")->get_value());
1568 						wstring >> width;
1569 						hstring >> height;
1570 						mstring >> multisample;
1571 						m_sound_controller->set_sound_on(m_options_form.get_item("sound")->get_value() == "on");
1572 						bool fullscreen = m_options_form.get_item("fullscreen")->get_value() == "on";
1573 						bool text_shadow = m_options_form.get_item("text_shadow")->get_value() == "on";
1574 						bool text_sliding = m_options_form.get_item("text_sliding")->get_value() == "on";
1575 						bool text_background = m_options_form.get_item("text_background")->get_value() == "on";
1576 						bool vsync = m_options_form.get_item("vsync")->get_value() == "on";
1577 						if (width != m_configuration->get_int_value("screen_width") ||
1578 								height != m_configuration->get_int_value("screen_height") ||
1579 								fullscreen != m_configuration->get_bool_value("fullscreen") ||
1580 								vsync != m_configuration->get_bool_value("vsync") ||
1581 								multisample != m_configuration->get_int_value("multisample") ||
1582 								text_shadow != m_configuration->get_bool_value("text_shadow") ||
1583 								text_sliding != m_configuration->get_bool_value("text_sliding") ||
1584 								text_background != m_configuration->get_bool_value("text_background")) {
1585 							m_configuration->set_int_value("screen_width", width);
1586 							m_configuration->set_int_value("screen_height", height);
1587 							m_configuration->set_bool_value("fullscreen", fullscreen);
1588 							m_configuration->set_bool_value("vsync", vsync);
1589 							m_configuration->set_int_value("multisample", multisample);
1590 							m_configuration->set_bool_value("text_shadow", text_shadow);
1591 							m_configuration->set_bool_value("text_sliding", text_sliding);
1592 							m_configuration->set_bool_value("text_background", text_background);
1593 							m_restart = true;
1594 							m_quit_game = true;
1595 						} else {
1596 							m_game_state = SHOW_MENUS;
1597 						}
1598 
1599 						string new_name(m_name_input->get_value());
1600 						if (new_name != m_name) {
1601 							if (!m_network.is_connected()) {
1602 								ostringstream	msg;
1603 								msg << "You are now known as " << new_name;
1604 								display_message(msg.str());
1605 							}
1606 							set_player_name(new_name);
1607 						}
1608 					}
1609 				}
1610 			}
1611 		}
1612 		break;
1613 		case SHOW_SERVER_BROWSER: {
1614 			if (event.button.button != 1 || event.type != SDL_MOUSEBUTTONUP) {
1615 				return;
1616 			}
1617 
1618 			string button = m_server_browser->check_button_press(event.button.x, event.button.y);
1619 
1620 			if (button != "") {
1621 				m_sound_controller->play_sound("click");
1622 			}
1623 
1624 			if (button == "Back") {
1625 				// Back.
1626 				m_game_state = SHOW_MENUS;
1627 				m_server_browser->deselect();
1628 			} else if (button == "Refresh") {
1629 				// Refresh.
1630 				m_server_browser->clear();
1631 				m_server_browser->deselect();
1632 				scan_all();
1633 			} else if (button == "Connect") {
1634 				// Connect.
1635 				int selected_item = m_server_browser->get_selected_item();
1636 				if (selected_item < 0 || selected_item > m_server_browser->get_count()) {
1637 					return;
1638 				}
1639 				connect_to_server(selected_item);
1640 				m_server_browser->set_visible(false);
1641 				m_game_state = SHOW_MENUS;
1642 			}
1643 
1644 			if (m_server_browser->get_count() == 0) {
1645 				return;
1646 			}
1647 
1648 			int selected_item = m_server_browser->check_item_select(event.button.x, event.button.y);
1649 
1650 			if (selected_item < 0) {
1651 				return;
1652 			}
1653 
1654 			m_sound_controller->play_sound("click");
1655 
1656 			if (m_last_clicked > get_ticks() - DOUBLE_CLICK_TIME) {
1657 				connect_to_server(selected_item);
1658 				m_server_browser->set_visible(false);
1659 				m_server_browser->deselect();
1660 				m_game_state = SHOW_MENUS;
1661 			}
1662 		}
1663 		break;
1664 		case GAME_IN_PROGRESS: {
1665 			if (!m_overlay_background->is_invisible()) {
1666 				// Do nothing.
1667 			} else if (!m_chat_log->is_invisible()) {
1668 				// Do nothing.
1669 			} else if (event.type == SDL_MOUSEBUTTONUP || event.type == SDL_MOUSEBUTTONDOWN) {
1670 				if (event.button.button == SDL_BUTTON_LEFT) {
1671 					// Fire the gun if it's ready.
1672 					if (m_players.empty() || m_players[m_player_id].is_frozen() || m_players[m_player_id].is_dead() || !m_current_weapon) {
1673 						return;
1674 					}
1675 
1676 					m_current_weapon->mouse_button_event(event);
1677 
1678 					if (event.type != SDL_MOUSEBUTTONDOWN) {
1679 						return;
1680 					}
1681 
1682 					double x_dist = (event.button.x + m_offset_x) - m_players[m_player_id].get_x();
1683 					double y_dist = (event.button.y + m_offset_y) - m_players[m_player_id].get_y();
1684 					double direction = atan2(y_dist, x_dist);
1685 					if (m_last_weapon_switch == 0 || (get_ticks() - m_last_weapon_switch) > m_params.weapon_switch_delay) {
1686 						m_current_weapon->fire(m_players[m_player_id], *this, m_players[m_player_id].get_position(), direction);
1687 					}
1688 				} else if (event.type == SDL_MOUSEBUTTONUP) {
1689 					if (event.button.button == SDL_BUTTON_WHEELUP) {
1690 						next_weapon();
1691 					} else if (event.button.button == SDL_BUTTON_WHEELDOWN) {
1692 						previous_weapon();
1693 					}
1694 				}
1695 			}
1696 		}
1697 	}
1698 	m_last_clicked = get_ticks();
1699 }
1700 
1701 /*
1702  * Reset the options menu, without applying the changes.
1703  */
reset_options()1704 void GameController::reset_options() {
1705 	m_text_manager->set_active_font(m_menu_font);
1706 	m_text_manager->set_active_color(TEXT_COLOR);
1707 	m_options_form.reset();
1708 }
1709 
1710 /*
1711  * Do the movement of objects in a certain time scale.
1712  */
move_objects(float timescale)1713 void GameController::move_objects(float timescale) {
1714 	if (timescale > 1.0) {
1715 		while (timescale > 1.0) {
1716 			timescale -= 1.0;
1717 			move_objects(1.0);
1718 		}
1719 	}
1720 	if (m_players.empty()) {
1721 		return;
1722 	}
1723 
1724 	GraphicalPlayer&	player(m_players[m_player_id]);
1725 
1726 	Point	oldpos(player.get_x(), player.get_y());
1727 
1728 	player.update_position(timescale);
1729 
1730 	double	half_width = player.get_radius();
1731 	double	half_height = player.get_radius();
1732 
1733 	// Check if the player is hitting a map edge.
1734 	bool	is_frozen = player.is_frozen() || player.is_dead();
1735 	if (player.get_x() - half_width < 0) {
1736 		player.set_x(half_width);
1737 		player.set_y(oldpos.y);
1738 		if (is_frozen && !player.is_invisible()) {
1739 			player.bounce(0, 0.9);
1740 		} else {
1741 			player.stop();
1742 			player.set_is_grabbing_obstacle(true);
1743 		}
1744 	} else if (player.get_x() + half_width > m_map_width) {
1745 		player.set_x(m_map_width - half_width);
1746 		player.set_y(oldpos.y);
1747 		if (is_frozen && !player.is_invisible()) {
1748 			player.bounce(180, 0.9);
1749 		} else {
1750 			player.stop();
1751 			player.set_is_grabbing_obstacle(true);
1752 		}
1753 	}
1754 
1755 	if (player.get_y() - half_height < 0) {
1756 		player.set_x(oldpos.x);
1757 		player.set_y(half_height);
1758 		if (is_frozen && !player.is_invisible()) {
1759 			player.bounce(90, 0.9);
1760 		} else {
1761 			player.stop();
1762 			player.set_is_grabbing_obstacle(true);
1763 		}
1764 	} else if (player.get_y() + half_height > m_map_height) {
1765 		player.set_x(oldpos.x);
1766 		player.set_y(m_map_height - half_height);
1767 		if (is_frozen && !player.is_invisible()) {
1768 			player.bounce(270, 0.9);
1769 		} else {
1770 			player.stop();
1771 			player.set_is_grabbing_obstacle(true);
1772 		}
1773 	}
1774 
1775 	Point	newpos(player.get_x(), player.get_y());
1776 	double	radius = player.get_radius();
1777 	Circle	player_circle(newpos, radius);
1778 	Circle	old_player_circle(oldpos, radius);
1779 
1780 	// Check each object for collisions with the player.
1781 	const list<BaseMapObject*>& map_objects(m_map->get_objects());
1782 	for (list<BaseMapObject*>::const_iterator it(map_objects.begin()); it != map_objects.end(); it++) {
1783 		BaseMapObject*	thisobj = *it;
1784 
1785 		if (!thisobj->is_intersectable()) {
1786 			continue;
1787 		}
1788 
1789 		const Shape& shape(*thisobj->get_bounding_shape());
1790 		double angle_of_incidence = 0;
1791 		double newdist = -1;
1792 
1793 		if (thisobj->is_interactive()) {
1794 			newdist = shape.solid_intersects_circle(player_circle, &angle_of_incidence);
1795 			angle_of_incidence = get_normalized_angle(angle_of_incidence + 180);
1796 		} else if (thisobj->is_collidable()) {
1797 			newdist = shape.boundary_intersects_circle(player_circle, &angle_of_incidence);
1798 			angle_of_incidence = get_normalized_angle(angle_of_incidence + 180);
1799 		}
1800 
1801 		if (newdist != -1) {
1802 			// We are intersecting with the object
1803 
1804 			if (thisobj->is_collidable()) {
1805 				double olddist = shape.boundary_intersects_circle(old_player_circle, NULL);
1806 				if (newdist < olddist) {
1807 					// We're moving closer to the object... COLLISION!
1808 					thisobj->collide(*this, player, oldpos, angle_of_incidence);
1809 				}
1810 			}
1811 			if (thisobj->is_interactive()) {
1812 				thisobj->interact(*this, player);
1813 			}
1814 
1815 		} else if (thisobj->is_interactive() && thisobj->is_engaged()) {
1816 			// We are no longer engaging the object, but we were previously
1817 			thisobj->disengage(*this, player);
1818 		}
1819 	}
1820 
1821 	// Update player rotation
1822 	player.update_rotation(timescale);
1823 
1824 	// Set the player position and radar position.
1825 	m_radar->move_blip(m_player_id, player.get_x(), player.get_y());
1826 	m_radar->recenter(player.get_x(), player.get_y());
1827 
1828 	// Set name sprites visible/invisible. and move players.
1829 	map<uint32_t, GraphicalPlayer>::iterator it;
1830 	for ( it=m_players.begin() ; it != m_players.end(); it++ ) {
1831 		GraphicalPlayer&	currplayer(it->second);
1832 		if (currplayer.is_invisible()) {
1833 			currplayer.get_name_sprite()->set_invisible(true);
1834 		} else {
1835 			currplayer.get_name_sprite()->set_invisible(false);
1836 			m_text_manager->reposition_string(currplayer.get_name_sprite(), currplayer.get_x(), currplayer.get_y() - (currplayer.get_radius()+30), TextManager::CENTER);
1837 		}
1838 
1839 		if (currplayer.get_id() == m_player_id) {
1840 			continue;
1841 		}
1842 
1843 		currplayer.update_position(timescale);
1844 		m_radar->move_blip(currplayer.get_id(), currplayer.get_x(), currplayer.get_y());
1845 	}
1846 }
1847 
1848 /*
1849  * Try to jump off of an obstacle.
1850  */
attempt_jump()1851 void GameController::attempt_jump() {
1852 	if (m_players.empty()) {
1853 		return;
1854 	}
1855 
1856 	GraphicalPlayer& player = m_players[m_player_id];
1857 
1858 	//
1859 	// Make sure the player is able to jump right now
1860 	//
1861 	if (player.is_frozen() || player.is_dead() || player.is_invisible() || !player.is_grabbing_obstacle()) {
1862 		return;
1863 	}
1864 
1865 	double	jump_angle = get_crosshairs_angle();
1866 
1867 	//
1868 	// Calculate the new velocities
1869 	//
1870 	Vector	new_velocity(Vector::make_from_magnitude(m_params.jump_velocity, jump_angle));
1871 	double	new_rotation = (((double)rand() / ((double)(RAND_MAX)+1)) - 0.5) * RANDOM_ROTATION_SCALE;
1872 
1873 	//
1874 	// Find the nearest obstacle that we are jumping towards
1875 	//
1876 	Circle	player_circle(player.get_position(), player.get_radius()+5);
1877 	Point	start_pos(player.get_position());
1878 	Point	end_pos(start_pos + new_velocity.get_unit_vector() * (m_map_width + m_map_height));
1879 	double	angle_of_incidence = 0;
1880 	double	shortest_dist = numeric_limits<double>::infinity();
1881 
1882 	const list<BaseMapObject*>& map_objects(m_map->get_objects());
1883 	for (list<BaseMapObject*>::const_iterator it(map_objects.begin()); it != map_objects.end(); it++) {
1884 		BaseMapObject*	map_obj = *it;
1885 
1886 		if (!map_obj->is_jumpable() || !map_obj->is_intersectable()) {
1887 			continue;
1888 		}
1889 
1890 		// See if we're colliding with this obstacle right now
1891 		double		colliding_angle_of_incidence;
1892 		if (map_obj->get_bounding_shape()->boundary_intersects_circle(player_circle, &colliding_angle_of_incidence) != -1) {
1893 			double	angle_difference = get_normalized_angle(colliding_angle_of_incidence - to_degrees(jump_angle));
1894 			if (angle_difference < 90 || angle_difference > 270) {
1895 				// We are jumping RIGHT INTO the obstacle that we're already colliding with
1896 				// Abort the jump, since it can take us nowhere
1897 				// (If the jump weren't aborted, it would cause problems with active map obstacles)
1898 				return;
1899 			}
1900 		}
1901 
1902 		// See if our trajectory collides with this obstacle
1903 		double		new_angle = 0;
1904 		Point		new_point = map_obj->get_bounding_shape()->intersects_line(start_pos, end_pos, &new_angle);
1905 
1906 		if (new_point.x == -1 && new_point.y == -1) {
1907 			// Not in the trajectory - continue to next obstacle...
1908 			continue;
1909 		}
1910 
1911 		// How far away is the obstacle from us?
1912 		double		new_dist = Point::distance(start_pos, new_point);
1913 
1914 		if (new_dist != -1 && new_dist < shortest_dist) {
1915 			shortest_dist = new_dist;
1916 			angle_of_incidence = new_angle;
1917 		}
1918 	}
1919 
1920 	if (isinf(shortest_dist)) {
1921 		// Try the map edge
1922 		double	new_angle = 0;
1923 		Point	new_point = m_map_polygon.intersects_line(start_pos, end_pos, &new_angle);
1924 
1925 		if (new_point.x != -1 && new_point.y != -1) {
1926 			shortest_dist = Point::distance(start_pos, new_point);
1927 			angle_of_incidence = new_angle;
1928 		}
1929 	}
1930 
1931 	//
1932 	// Set the player in motion
1933 	//
1934 	player.set_velocity(new_velocity);
1935 	player.set_rotational_vel(new_rotation);
1936 
1937 	if (finite(shortest_dist)) {
1938 		//
1939 		// Set our rotational velocity so that we will land on this obstacle in a good orientation (i.e. head not hitting the wall)
1940 		//
1941 		angle_of_incidence = get_normalized_angle(angle_of_incidence + 180);
1942 
1943 		double	currangle = player.get_sprite()->get_rotation();
1944 		double	adjusted_angle = get_normalized_angle(currangle + 90 - angle_of_incidence);
1945 		double	newangle = currangle;
1946 		if (adjusted_angle < 110) {
1947 			newangle = currangle + (110 - adjusted_angle);
1948 		} else if (adjusted_angle > 250) {
1949 			newangle = currangle - (adjusted_angle - 250);
1950 		}
1951 
1952 		if (newangle != currangle) {
1953 			double	time_till_hit = shortest_dist / new_velocity.get_magnitude();
1954 			double	ideal_velocity = (newangle - currangle) / time_till_hit;
1955 
1956 			if (ideal_velocity < -2.0)
1957 				player.set_rotational_vel(-2.0);
1958 			else if (ideal_velocity > 2.0)
1959 				player.set_rotational_vel(2.0);
1960 			else
1961 				player.set_rotational_vel(ideal_velocity);
1962 		}
1963 	}
1964 }
1965 
show_muzzle_flash()1966 void GameController::show_muzzle_flash() {
1967 	// Switch to the gun with the muzzle flash.
1968 	Graphic*	frontarm = m_players[m_player_id].get_sprite()->get_graphic("frontarm");
1969 	frontarm->get_graphic("firing")->set_invisible(false);
1970 	send_animation_packet("frontarm/firing", "invisible", false);
1971 	frontarm->get_graphic("normal")->set_invisible(true);
1972 	send_animation_packet("frontarm/normal", "invisible", true);
1973 	m_muzzle_flash_start = get_ticks();
1974 }
1975 
show_bullet_impact(Point position,const char * sprite_name)1976 void GameController::show_bullet_impact(Point position, const char* sprite_name) {
1977 	if (!sprite_name[0]) {
1978 		return;
1979 	}
1980 	Sprite*	this_shot = m_graphics_cache.new_graphic<Sprite>(m_path_manager.data_path(sprite_name, "sprites"));
1981 	this_shot->set_x(position.x);
1982 	this_shot->set_y(position.y);
1983 	this_shot->set_scale_x(.1);
1984 	this_shot->set_scale_y(.1);
1985 	this_shot->set_invisible(false);
1986 	pair<Graphic*, unsigned int> new_shot(this_shot, get_ticks() + SHOT_DISPLAY_TIME);
1987 	m_shots.push_back(new_shot);
1988 	m_window->register_graphic(this_shot, GameWindow::LAYER_GAME);
1989 }
1990 
1991 /*
1992  * Set the player sprites visible or invisible.
1993  */
set_players_visible(bool visible)1994 void GameController::set_players_visible(bool visible) {
1995 	if (m_players.empty()) {
1996 		return;
1997 	}
1998 
1999 	map<uint32_t, GraphicalPlayer>::iterator it;
2000 	for ( it=m_players.begin() ; it != m_players.end(); it++ ) {
2001 		const GraphicalPlayer& currplayer = (*it).second;
2002 		if (currplayer.get_sprite() == NULL) {
2003 			continue;
2004 		}
2005 		if (visible) {
2006 			currplayer.get_sprite()->set_invisible(currplayer.is_invisible());
2007 			currplayer.get_name_sprite()->set_invisible(currplayer.is_invisible());
2008 			m_radar->set_blip_invisible(it->first,currplayer.is_invisible());
2009 		} else {
2010 			currplayer.get_sprite()->set_invisible(true);
2011 			currplayer.get_name_sprite()->set_invisible(true);
2012 			m_radar->set_blip_invisible(it->first,true);
2013 		}
2014 	}
2015 }
2016 
2017 /*
2018  * Show or hide the overlay.
2019  */
toggle_score_overlay(bool visible)2020 void GameController::toggle_score_overlay(bool visible) {
2021 	update_individual_scores();
2022 	m_overlay_background->set_invisible(!visible);
2023 	m_overlay_scrollbar->set_invisible(!visible);
2024 	m_overlay_scrollarea->set_invisible(!visible);
2025 	map<string, Text*>::iterator it;
2026 	for ( it=m_overlay_items.begin() ; it != m_overlay_items.end(); it++ ) {
2027 		Graphic* thisitem = (*it).second;
2028 		thisitem->set_invisible(!visible);
2029 	}
2030 }
2031 
2032 /*
2033  * Show or hide the main menu
2034  */
toggle_main_menu(bool visible)2035 void GameController::toggle_main_menu(bool visible) {
2036 	m_main_menu.get_graphic_group()->set_invisible(!visible);
2037 }
2038 
2039 /*
2040  * Show or hide the options menu
2041  */
toggle_options_menu(bool visible)2042 void GameController::toggle_options_menu(bool visible) {
2043 	m_options_menu.get_graphic_group()->set_invisible(!visible);
2044 	m_name_input->set_invisible(!visible);
2045 }
2046 
2047 /*
2048  * Change team scores.
2049  */
change_team_scores(int bluescore,int redscore)2050 void GameController::change_team_scores(int bluescore, int redscore) {
2051 	m_text_manager->set_active_font(m_menu_font);
2052 	m_text_manager->set_active_color(TEXT_COLOR);
2053 
2054 	if (redscore != -1) {
2055 		if (m_overlay_items.count("red score") != 0) {
2056 			m_text_manager->remove_string(m_overlay_items["red score"]);
2057 		}
2058 
2059 		stringstream redscoreprinter;
2060 		redscoreprinter << redscore;
2061 	 	m_overlay_items["red score"] = m_text_manager->place_string(redscoreprinter.str(), m_overlay_items["red label"]->get_x() + m_overlay_items["red label"]->get_image_width() + 10, 115, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
2062 	}
2063 
2064 	if (bluescore != -1) {
2065 		if (m_overlay_items.count("blue score") != 0) {
2066 			m_text_manager->remove_string(m_overlay_items["blue score"]);
2067 		}
2068 
2069 		stringstream bluescoreprinter;
2070 		bluescoreprinter << bluescore;
2071 		m_overlay_items["blue score"] = m_text_manager->place_string(bluescoreprinter.str(), m_overlay_items["blue label"]->get_x() + m_overlay_items["blue label"]->get_image_width() + 10, 115, TextManager::LEFT, GameWindow::LAYER_HUD, TEXT_LAYER);
2072 	}
2073 
2074 	m_text_manager->set_active_font(m_font);
2075 }
2076 
2077 /*
2078  * Update individual scores.
2079  */
update_individual_scores()2080 void GameController::update_individual_scores() {
2081 	// Place all the players into one of two lists based on their team
2082 	list<const GraphicalPlayer*>	blue_players;
2083 	list<const GraphicalPlayer*>	red_players;
2084 
2085 	if (m_players.empty()) {
2086 		return;
2087 	}
2088 
2089 	for (map<uint32_t, GraphicalPlayer>::iterator it = m_players.begin(); it != m_players.end(); ++it) {
2090 		if (it->second.get_sprite() != NULL && it->second.get_team() == 'A') {
2091 			blue_players.push_back(&it->second);
2092 		} else if (it->second.get_sprite() != NULL && it->second.get_team() == 'B') {
2093 			red_players.push_back(&it->second);
2094 		}
2095 	}
2096 	// Sort these lists by score
2097 	blue_players.sort(Player::compare_by_score());
2098 	red_players.sort(Player::compare_by_score());
2099 
2100 	m_text_manager->set_active_font(m_medium_font);
2101 	m_text_manager->set_active_color(TEXT_COLOR);
2102 
2103 	int count = 0;
2104 
2105 	for (list<const GraphicalPlayer*>::iterator it = blue_players.begin() ; it != blue_players.end(); ++it) {
2106 		update_individual_score_line(count++, **it);
2107 	}
2108 
2109 	// Skip two lines between the two teams
2110 	count += 2;
2111 
2112 	for (list<const GraphicalPlayer*>::iterator it = red_players.begin() ; it != red_players.end(); ++it) {
2113 		update_individual_score_line(count++, **it);
2114 	}
2115 
2116 	m_text_manager->set_active_font(m_font);
2117 }
2118 
update_individual_score_line(int count,const GraphicalPlayer & currplayer)2119 void GameController::update_individual_score_line(int count, const GraphicalPlayer& currplayer) {
2120 
2121 	string playername = currplayer.get_name();
2122 	stringstream idprinter;
2123 	idprinter << currplayer.get_id();
2124 	string playerid = idprinter.str();
2125 	string playernameforscore = currplayer.get_name();
2126 	string playerscore = playernameforscore.append("score");
2127 
2128 	m_text_manager->set_active_color(currplayer.get_team() == 'A' ? BLUE_COLOR : RED_COLOR);
2129 	m_text_manager->set_shadow_color(currplayer.get_team() == 'A' ? BLUE_SHADOW : RED_SHADOW);
2130 
2131 	stringstream scoreprinter;
2132 	scoreprinter << currplayer.get_score();
2133 	m_overlay_items[playerid] = m_text_manager->render_string(playername, 10, count*25, TextManager::LEFT);
2134 	m_overlay_items[playerscore] = m_text_manager->render_string(scoreprinter.str(), m_overlay_background->get_image_width()/2, count*25, TextManager::LEFT);
2135 
2136 	m_overlay_items[playerid]->set_priority(TEXT_LAYER);
2137 	m_overlay_scrollarea->get_group()->remove_graphic(playerid);
2138 	m_overlay_scrollarea->get_group()->add_graphic(m_overlay_items[playerid], playerid);
2139 
2140 	m_overlay_items[playerscore]->set_priority(TEXT_LAYER);
2141 	m_overlay_scrollarea->get_group()->remove_graphic(playerscore);
2142 	m_overlay_scrollarea->get_group()->add_graphic(m_overlay_items[playerscore], playerscore);
2143 
2144 	if (m_overlay_items[playerid]->get_y() + m_overlay_items[playerid]->get_image_height() + 2 > m_overlay_scrollarea->get_content_height()) {
2145 		m_overlay_scrollarea->set_content_height(m_overlay_items[playerid]->get_y() + m_overlay_items[playerid]->get_image_height() + 2);
2146 	}
2147 
2148 	m_text_manager->set_shadow_color(TEXT_SHADOW);
2149 }
2150 
delete_individual_score(const GraphicalPlayer & currplayer)2151 void GameController::delete_individual_score(const GraphicalPlayer& currplayer) {
2152 	stringstream idprinter;
2153 	idprinter << currplayer.get_id();
2154 	string playerid = idprinter.str();
2155 	string playernameforscore = currplayer.get_name();
2156 	string playerscore = playernameforscore.append("score");
2157 
2158 	m_overlay_scrollarea->get_group()->remove_graphic(playerid);
2159 	m_overlay_scrollarea->get_group()->remove_graphic(playerscore);
2160 	m_overlay_items.erase(playerscore);
2161 	m_overlay_items.erase(playerid);
2162 }
2163 
2164 /*
2165  * Try to connect to a server.
2166  */
connect_to_server(const IPAddress & server_address,char team)2167 void GameController::connect_to_server(const IPAddress& server_address, char team) {
2168 	if (!m_network.connect(server_address)) {
2169 		ostringstream	errmsg;
2170 		errmsg << "Error: Could not connect to server at " << server_address;
2171 		display_message(errmsg.str());
2172 		cerr << errmsg.str() << endl;
2173 		return;
2174 	}
2175 
2176 	update_energy_bar(0);
2177 
2178 	PacketWriter join_request(JOIN_PACKET);
2179 	join_request << m_protocol_number;
2180 	join_request << COMPAT_VERSION;
2181 	join_request << m_name;
2182 	if (is_valid_team(team)) {
2183 		join_request << team;
2184 	}
2185 
2186 	m_join_sent_time = get_ticks();
2187 
2188 	m_network.send_reliable_packet(join_request);
2189 }
2190 
2191 /*
2192  * Try to connect to a server from the server list by number.
2193  */
connect_to_server(int servernum)2194 void GameController::connect_to_server(int servernum) {
2195 	disconnect();
2196 	connect_to_server(m_server_browser->get_server_info(servernum));
2197 }
2198 
2199 /*
2200  * Send a disconnect packet.
2201  */
disconnect()2202 void GameController::disconnect() {
2203 	m_item_resume->set_state(MenuItem::DISABLED);
2204 	m_item_disconnect->set_state(MenuItem::DISABLED);
2205 	if (!m_players.empty()) {
2206 		PacketWriter leave_request(LEAVE_PACKET);
2207 		leave_request << m_player_id;
2208 		m_network.send_packet(leave_request);
2209 
2210 		clear_players();
2211 		m_game_state = SHOW_MENUS;
2212 		m_player_id = 0;
2213 
2214 		display_message("Disconnected.");
2215 	}
2216 	m_network.disconnect();
2217 	m_map->clear();
2218 }
2219 
2220 /*
2221  * When we receive a welcome packet.
2222  */
welcome(PacketReader & reader)2223 void GameController::welcome(PacketReader& reader) {
2224 	int server_proto_version;
2225 	int playerid;
2226 	string playername;
2227 	char team;
2228 
2229 	reader >> server_proto_version >> playerid >> playername >> team;
2230 
2231 	m_player_id = playerid;
2232 	m_name = playername;
2233 
2234 	cout << "Received welcome packet. Player ID: " << playerid << ", Name: " << playername << ", Team: " << team << endl;
2235 
2236 	if (server_proto_version != m_protocol_number) {
2237 		ostringstream serveraddress;
2238 		serveraddress << "Error: Server has a different protocol. Server: ";
2239 		serveraddress << server_proto_version;
2240 		serveraddress << " You: ";
2241 		serveraddress << m_protocol_number;
2242 		if (server_proto_version > m_protocol_number) {
2243 			serveraddress << ". Please upgrade your client.";
2244 		} else {
2245 			serveraddress << ". Your client is too new.";
2246 		}
2247 		display_message(serveraddress.str());
2248 
2249 		disconnect();
2250 	}
2251 
2252 	ostringstream serveraddress;
2253 	serveraddress << "Connected to server: ";
2254 	serveraddress << format_ip_address(m_network.get_server_address(), true);
2255 	display_message(serveraddress.str());
2256 
2257 	m_join_sent_time = 0;
2258 
2259 	m_item_resume->set_state(MenuItem::NORMAL);
2260 	m_item_disconnect->set_state(MenuItem::NORMAL);
2261 
2262 	// Reset gate packet sequence numbers.
2263 	m_last_gate_packet_seq_no[0] = 0;
2264 	m_last_gate_packet_seq_no[1] = 0;
2265 
2266 	clear_players();
2267 
2268 	// Insert different name colors and sprites depending on team.
2269 	if (team == 'A') {
2270 		m_players.insert(pair<int, GraphicalPlayer>(m_player_id,GraphicalPlayer(m_name.c_str(), m_player_id, team, new GraphicGroup(blue_player), blue_sprite->get_width()/2, blue_sprite->get_height()/2)));
2271 		m_text_manager->set_active_color(BLUE_COLOR);
2272 		m_text_manager->set_shadow_color(BLUE_SHADOW);
2273 	} else {
2274 		m_players.insert(pair<int, GraphicalPlayer>(m_player_id,GraphicalPlayer(m_name.c_str(), m_player_id, team, new GraphicGroup(red_player), red_sprite->get_width()/2, red_sprite->get_height()/2)));
2275 		m_text_manager->set_active_color(RED_COLOR);
2276 		m_text_manager->set_shadow_color(RED_SHADOW);
2277 	}
2278 	m_window->register_graphic(m_players[m_player_id].get_sprite(), GameWindow::LAYER_GAME);
2279 	m_radar->add_blip(m_player_id, team, 0, 0);
2280 	// Because our blip never gets "activated", and if the mode is aural, we won't get any alpha, so add some
2281 	m_radar->set_blip_alpha(m_player_id, 1.0);
2282 
2283 	m_players[m_player_id].set_radius(30);
2284 	m_players[m_player_id].set_name_sprite(m_text_manager->place_string(m_players[m_player_id].get_name(), m_screen_width/2, (m_screen_height/2)-(m_players[m_player_id].get_radius()+30), TextManager::CENTER, GameWindow::LAYER_GAME));
2285 	m_players[m_player_id].set_is_invisible(true);
2286 
2287 	send_my_player_update();
2288 
2289 	m_game_state = GAME_IN_PROGRESS;
2290 
2291 	m_text_manager->set_shadow_color(TEXT_SHADOW);
2292 }
2293 
2294 /*
2295  * Add a player when we get an announce packet.
2296  */
announce(PacketReader & reader)2297 void GameController::announce(PacketReader& reader) {
2298 	unsigned int playerid;
2299 	string playername;
2300 	char team;
2301 
2302 	if (m_players.empty()) {
2303 		// WELCOME packet not received yet
2304 		// do NOT send an ACK for this ANNOUNCE packet, so that the server will resend it, hopefully after the WELCOME has come in.
2305 		return;
2306 	}
2307 
2308 	reader >> playerid >> playername >> team;
2309 
2310 	string joinmsg = "";
2311 	joinmsg.append(playername);
2312 	joinmsg.append(" has joined the game!");
2313 	if (team == 'A') {
2314 		display_message(joinmsg, BLUE_COLOR, BLUE_SHADOW);
2315 	} else {
2316 		display_message(joinmsg, RED_COLOR, RED_SHADOW);
2317 	}
2318 
2319 	// Ignore announce packet for ourself, but still display join message (above)
2320 	if (playerid == m_player_id) {
2321 		return;
2322 	}
2323 
2324 	// Add a different sprite and name color depending on team.
2325 	if (team == 'A') {
2326 		m_players.insert(pair<int, GraphicalPlayer>(playerid,GraphicalPlayer((const char*)playername.c_str(), playerid, team, new GraphicGroup(blue_player))));
2327 		m_text_manager->set_active_color(BLUE_COLOR);
2328 		m_text_manager->set_shadow_color(BLUE_SHADOW);
2329 	} else {
2330 		m_players.insert(pair<int, GraphicalPlayer>(playerid,GraphicalPlayer((const char*)playername.c_str(), playerid, team, new GraphicGroup(red_player))));
2331 		m_text_manager->set_active_color(RED_COLOR);
2332 		m_text_manager->set_shadow_color(RED_SHADOW);
2333 	}
2334 	m_radar->add_blip(playerid,team,0,0);
2335 
2336 	// Register the player sprite with the window
2337 	m_window->register_graphic(m_players[playerid].get_sprite(), GameWindow::LAYER_GAME);
2338 	m_players[playerid].set_name_sprite(m_text_manager->place_string(m_players[playerid].get_name(), m_players[playerid].get_x(), m_players[playerid].get_y()-(m_players[playerid].get_radius()+30), TextManager::CENTER, GameWindow::LAYER_GAME));
2339 	m_players[playerid].set_radius(40);
2340 	m_text_manager->set_shadow_color(TEXT_SHADOW);
2341 }
2342 
2343 /*
2344  * When we receive a player update.
2345  */
player_update(PacketReader & reader)2346 void GameController::player_update(PacketReader& reader) {
2347 	if (m_players.empty()) {
2348 		return;
2349 	}
2350 
2351 	uint32_t player_id;
2352 	reader >> player_id;
2353 
2354 	if (player_id == m_player_id) {
2355 		// If the player update packet is for this player, send an ACK for it
2356 	}
2357 
2358 	GraphicalPlayer* currplayer = get_player_by_id(player_id);
2359 	if (currplayer == NULL) {
2360 		cerr << "Error: Received update packet for non-existent player " << player_id << endl;
2361 		return;
2362 	}
2363 
2364 	bool wasfrozen = currplayer->is_frozen();
2365 	string old_weapon_id = currplayer->get_current_weapon_id();
2366 
2367 	currplayer->read_update_packet(reader);
2368 
2369 	if (wasfrozen != currplayer->is_frozen()) {
2370 		recreate_name(currplayer);
2371 	}
2372 
2373 	if (player_id == m_player_id) {
2374 		update_energy_bar();
2375 	}
2376 
2377 	// Update the radar and name sprite
2378 	m_radar->move_blip(player_id, currplayer->get_x(), currplayer->get_y());
2379 	m_radar->set_blip_invisible(player_id, currplayer->is_invisible());
2380 	currplayer->get_name_sprite()->set_invisible(currplayer->is_invisible());
2381 
2382 	// If invisible or frozen, set these things appropriately and show/hide the sprite.
2383 	if (currplayer->is_invisible()) {
2384 		currplayer->set_velocity(0, 0);
2385 	} else {
2386 		// Reposition the name sprite to reflect the player's new position
2387 		m_text_manager->reposition_string(currplayer->get_name_sprite(), currplayer->get_x(), currplayer->get_y() - (currplayer->get_radius()+30), TextManager::CENTER);
2388 	}
2389 
2390 	if (old_weapon_id != currplayer->get_current_weapon_id()) {
2391 		// TODO: make this triggered by GraphicalPlayer::set_current_weapon_id()
2392 		if (Weapon* new_weapon = get_weapon(currplayer->get_current_weapon_id())) {
2393 			new_weapon->select(*currplayer, *this);
2394 		}
2395 	}
2396 
2397 	if (m_radar->get_mode() == RADAR_ON) {
2398 		m_radar->set_blip_alpha(player_id, currplayer->is_frozen() ? 0.5 : 1.0);
2399 	}
2400 }
2401 
2402 /*
2403  * Send a player update packet.
2404  */
send_my_player_update()2405 void GameController::send_my_player_update() {
2406 	if (m_players.empty()) {
2407 		return;
2408 	}
2409 
2410 	PacketWriter player_update(PLAYER_UPDATE_PACKET);
2411 	m_players[m_player_id].write_update_packet(player_update);
2412 	m_network.send_packet(player_update);
2413 }
2414 
2415 /*
2416  * Send a message packet.
2417  */
send_message(string message)2418 void GameController::send_message(string message) {
2419 	if (m_players.empty()) {
2420 		return;
2421 	}
2422 	strip_leading_trailing_spaces(message);
2423 	if (message.empty()) {
2424 		return;
2425 	}
2426 
2427 	if (message.find_first_of(':') != string::npos) {
2428 		// Message to individual player
2429 		string			recipient_name;
2430 		string			message_part;
2431 		StringTokenizer(message, ':', 2) >> recipient_name >> message_part;
2432 
2433 		const GraphicalPlayer*	player = get_player_by_name(recipient_name.c_str());
2434 		strip_leading_trailing_spaces(message_part);
2435 
2436 		if (player && !message_part.empty()) {
2437 			PacketWriter	message_writer(MESSAGE_PACKET);
2438 			message_writer << m_player_id << player->get_id() << message_part;
2439 			m_network.send_packet(message_writer);
2440 			return;
2441 		}
2442 	}
2443 
2444 	// Broadcast message to all players
2445 	PacketWriter	message_writer(MESSAGE_PACKET);
2446 	message_writer << m_player_id << "" << message;
2447 	m_network.send_packet(message_writer);
2448 }
2449 
2450 /*
2451  * Send a team message packet.
2452  */
send_team_message(string message)2453 void GameController::send_team_message(string message) {
2454 	if (m_players.empty()) {
2455 		return;
2456 	}
2457 	strip_leading_trailing_spaces(message);
2458 	if (message.empty()) {
2459 		return;
2460 	}
2461 
2462 	PacketWriter	message_writer(MESSAGE_PACKET);
2463 	message_writer << m_player_id << m_players[m_player_id].get_team() << message;
2464 	m_network.send_packet(message_writer);
2465 }
2466 
2467 
2468 /*
2469  * Deal with receiving a leave packet.
2470  */
leave(PacketReader & reader)2471 void GameController::leave(PacketReader& reader) {
2472 	uint32_t	playerid;
2473 	string		leave_message;
2474 	reader >> playerid >> leave_message;
2475 
2476 	if (GraphicalPlayer* player = get_player_by_id(playerid)) {
2477 		 // If it's for ourselves, we were kicked. Quit with a message.
2478 		if (playerid == m_player_id) {
2479 			string leavemsg = "You were kicked!  Reason: ";
2480 			leavemsg.append(leave_message);
2481 			display_message(leavemsg);
2482 			cout << "You were kicked!  Reason: " << leave_message << endl;
2483 			disconnect();
2484 			m_game_state = SHOW_MENUS;
2485 			return;
2486 		}
2487 
2488 		string leavemsg = "";
2489 		leavemsg.append(player->get_name());
2490 		leavemsg.append(" has left the game.");
2491 
2492 		// Display a message based on their team.
2493 		if (player->get_team() == 'A') {
2494 			display_message(leavemsg, BLUE_COLOR, BLUE_SHADOW);
2495 		} else {
2496 			display_message(leavemsg, RED_COLOR, RED_SHADOW);
2497 		}
2498 
2499 		m_text_manager->remove_string(player->get_name_sprite());
2500 		m_window->unregister_graphic(player->get_sprite(), GameWindow::LAYER_GAME);
2501 		m_radar->remove_blip(playerid);
2502 		delete_individual_score(*player);
2503 		delete player->get_sprite();
2504 		m_players.erase(playerid);
2505 	}
2506 }
2507 
2508 /*
2509  * Called when a gun fired packet is received.
2510  */
weapon_discharged(PacketReader & reader)2511 void GameController::weapon_discharged(PacketReader& reader) {
2512 	uint32_t	player_id;
2513 	string		weapon_id;
2514 
2515 	reader >> player_id >> weapon_id;
2516 
2517 	if (player_id == m_player_id) {
2518 		// Ignore discharge packets from ourself
2519 		return;
2520 	}
2521 
2522 	Weapon*		weapon = get_weapon(weapon_id);
2523 	Player*		player = get_player_by_id(player_id);
2524 
2525 	if (!weapon || !player) {
2526 		return;
2527 	}
2528 
2529 	weapon->discharged(*player, *this, reader);
2530 }
2531 
2532 /*
2533  * Called when a player shot packet is received.
2534  */
player_hit(PacketReader & reader)2535 void GameController::player_hit(PacketReader& reader) {
2536 	uint32_t	shooter_id;
2537 	string		weapon_id;
2538 	uint32_t	shot_player_id;
2539 	bool		has_effect;
2540 
2541 	reader >> shooter_id >> weapon_id >> shot_player_id >> has_effect;
2542 
2543 	GraphicalPlayer*	shooter = get_player_by_id(shooter_id);
2544 	GraphicalPlayer*	shot_player = get_player_by_id(shot_player_id);
2545 
2546 	if (!shooter || !shot_player) {
2547 		return;
2548 	}
2549 
2550 	if (shot_player_id == m_player_id) {
2551 		if (Weapon* weapon = get_weapon(weapon_id)) {
2552 			weapon->hit(*shot_player, *shooter, has_effect, *this, reader);
2553 		}
2554 	}
2555 }
2556 
player_died(PacketReader & reader)2557 void GameController::player_died(PacketReader& reader) {
2558 	uint32_t	dead_player_id;
2559 	uint32_t	killer_id;
2560 	uint64_t	time_to_unfreeze;
2561 
2562 	reader >> dead_player_id >> killer_id >> time_to_unfreeze;
2563 
2564 	GraphicalPlayer* dead_player = get_player_by_id(dead_player_id);
2565 	GraphicalPlayer* killer = killer_id ? get_player_by_id(killer_id) : NULL;
2566 
2567 	if (!dead_player) {
2568 		return;
2569 	}
2570 
2571 	if (time_to_unfreeze != 0) {
2572 		ostringstream message;
2573 		bool bold = false;
2574 		if (killer_id == m_player_id) {
2575 			message << "You";
2576 			bold = true;
2577 		} else if (killer) {
2578 			message << killer->get_name();
2579 		} else {
2580 			message << "A map hazard";
2581 		}
2582 
2583 		if (killer && killer->get_team() == dead_player->get_team()) { // TODO (when we finish game modes): only say "betrayed" if team-play is in effect.
2584 			message << " betrayed ";
2585 		} else {
2586 			message << " froze ";
2587 		}
2588 
2589 		if (dead_player_id == m_player_id) {
2590 			message << "you";
2591 			bold = true;
2592 		} else {
2593 			message << dead_player->get_name();
2594 		}
2595 
2596 		message << ".";
2597 
2598 		if (killer && killer->get_team() == 'A') {
2599 			display_message(message.str(), BLUE_COLOR, BLUE_SHADOW, bold);
2600 		} else if (killer && killer->get_team() == 'B') {
2601 			display_message(message.str(), RED_COLOR, RED_SHADOW, bold);
2602 		} else {
2603 			display_message(message.str());
2604 		}
2605 	}
2606 
2607 	// If we were killed, freeze
2608 	if (dead_player_id == m_player_id) {
2609 		if (time_to_unfreeze) {
2610 			freeze(time_to_unfreeze);
2611 		} else {
2612 			m_players[m_player_id].reset_energy();
2613 			update_energy_bar();
2614 		}
2615 	}
2616 }
2617 
2618 /*
2619  * Called when a message is receieved.
2620  */
message(PacketReader & reader)2621 void GameController::message(PacketReader& reader) {
2622 	uint32_t sender_id;
2623 	string recipient;
2624 	string message_text;
2625 
2626 	reader >> sender_id >> recipient >> message_text;
2627 
2628 	if (sender_id == 0) {
2629 		// sender_id = 0 ==> From the server
2630 		string message("[Server]: ");
2631 		message.append(message_text);
2632 
2633 		display_message(message);
2634 
2635 	} else if (const GraphicalPlayer* sender = get_player_by_id(sender_id)) {
2636 		string message(sender->get_name());
2637 		message.append(": ");
2638 		if (is_valid_team(recipient[0])) {
2639 			// Team chat
2640 			message.append("[TEAM]: ");
2641 		}
2642 		message.append(message_text);
2643 
2644 		// Show the message in a color depending on the sender's team.
2645 		if (sender->get_team() == 'A') {
2646 			display_message(message, BLUE_COLOR, BLUE_SHADOW, true);
2647 		} else {
2648 			display_message(message, RED_COLOR, RED_SHADOW, true);
2649 		}
2650 	}
2651 }
2652 
2653 /*
2654  * Called when a gate update packet is received.
2655  */
gate_update(PacketReader & reader)2656 void GameController::gate_update(PacketReader& reader) {
2657 	uint32_t	acting_player_id; 	// Who triggered the gate change?
2658 	char		team;			// Which team's gate is being updated
2659 	double		progress;		// How much has the gate opened? 0 == fully closed .. 1 == fully open
2660 	int		change_in_players;	// {-1,0,1} = the change in the # of players engaging the gate
2661 	size_t		new_nbr_players;	// How many players are now engaging the gate
2662 	uint64_t	sequence_no;		// This should be greater than the last received gate update.
2663 
2664 	reader >> acting_player_id >> team >> progress >> change_in_players >> new_nbr_players >> sequence_no;
2665 
2666 	GraphicalPlayer* myplayer = get_player_by_id(m_player_id);
2667 	GraphicalPlayer* actingplayer = get_player_by_id(acting_player_id);
2668 	if (myplayer == NULL) {
2669 		return;
2670 	}
2671 
2672 	// If the sequence is earlier than something we've already received, ignore it.
2673 	if (m_last_gate_packet_seq_no[team - 'A'] > sequence_no) {
2674 		return;
2675 	}
2676 
2677 	// Uncomment this code to return gate raise/lower messages.
2678 	/*ostringstream message;
2679 
2680 	if (actingplayer != NULL) {
2681 		message << actingplayer->get_name();
2682 		if (change_in_players == 1) {
2683 			message << " engaged the ";
2684 		} else if (change_in_players == -1) {
2685 			message << " disengaged from the ";
2686 		}
2687 	}
2688 
2689 	if (change_in_players != 0) {
2690 		if (team == 'A') {
2691 			message << "blue gate!";
2692 			display_message(message.str(), RED_COLOR, RED_SHADOW);
2693 		} else if (team == 'B') {
2694 			message << "red gate!";
2695 			display_message(message.str(), BLUE_COLOR, BLUE_SHADOW);
2696 		}
2697 	}*/
2698 
2699 	if (change_in_players == 1 && new_nbr_players == 1) {
2700 		// The gate just started opening.
2701 		// Play a sound and set the warning visible.
2702 		string soundname = "";
2703 		if (team == myplayer->get_team()) {
2704 			soundname = "gatelower";
2705 		} else {
2706 			soundname = "positivegatelower";
2707 		}
2708 
2709 		m_gate_lower_sounds[team - 'A'] = m_sound_controller->play_sound(soundname);
2710 		if (team == m_players[m_player_id].get_team()) {
2711 			m_gate_warning->set_invisible(false);
2712 			m_gate_warning_time = get_ticks();
2713 		}
2714 	} else if (change_in_players == -1 && new_nbr_players == 0) {
2715 		// The gate just started closing.
2716 		// Hide the warning and stop the sounds.
2717 	 	if (m_gate_lower_sounds[team - 'A'] != -1) {
2718 	 		if (team == myplayer->get_team()) {
2719 		 		m_gate_warning_time = 0;
2720 		 		m_gate_warning->set_invisible(true);
2721 		 	}
2722 	 		m_sound_controller->halt_sound(m_gate_lower_sounds[team - 'A']);
2723 	 	}
2724 	}
2725 
2726 	m_map->set_gate_progress(team, progress);
2727 
2728 	double width = ((1-progress) * (GATE_STATUS_RECT_WIDTH-2)) + 2;
2729 	if (team == 'A') {
2730 		m_blue_gate_status_rect->set_image_width(width);
2731 	} else if (team == 'B') {
2732 		m_red_gate_status_rect->set_image_width(width);
2733 	}
2734 }
2735 
2736 /*
2737  * When a new round packet is received.
2738  */
new_round(PacketReader & reader)2739 void GameController::new_round(PacketReader& reader) {
2740 	if (m_players.empty()) {
2741 		// WELCOME packet not received yet
2742 		// do NOT send an ACK for this NEW_ROUND packet, so that the server will resend it, hopefully after the WELCOME has come in.
2743 		return;
2744 	}
2745 
2746 	/*
2747 	 * Process the packet
2748 	 */
2749 	string 		map_name;
2750 	int 		map_revision;
2751 	int		map_width;
2752 	int		map_height;
2753 	bool		game_started;
2754 	uint64_t	time_until_start;
2755 	reader >> map_name >> map_revision >> map_width >> map_height >> game_started >> time_until_start;
2756 
2757 	/*
2758 	 * Tell the player what's going on
2759 	 */
2760 	ostringstream	message;
2761 	if (game_started) {
2762 		if (m_game_state == GAME_OVER) {
2763 			m_game_state = GAME_IN_PROGRESS;
2764 		}
2765 		if (time_until_start == numeric_limits<uint64_t>::max()) {
2766 			message << "Game in progress on map " << map_name << ". You will spawn when the next game starts.";
2767 		} else if (time_until_start/1000 > 0) {
2768 			message << "Game in progress on map " << map_name << ". " << time_until_start / 1000 << " seconds until spawn.";
2769 		}
2770 	} else if (time_until_start/1000 > 0) {
2771 		message << "Game starts in " << time_until_start/1000 << " seconds on map " << map_name << ".";
2772 	}
2773 
2774 	if (!message.str().empty()) {
2775 		display_message(message.str().c_str());
2776 	}
2777 
2778 	/*
2779 	 * Load the map, if it has changed
2780 	 */
2781 	if (m_map->is_loaded(map_name.c_str(), map_revision)) {
2782 		// Map already loaded - just reset it
2783 		m_map->reset();
2784 	} else {
2785 		// This map/revision is not already loaded - let's try to load it
2786 		if (!load_map(map_name.c_str(), map_revision)) {
2787 			init_map(map_width, map_height);
2788 			// Couldn't load - request it from the server
2789 			request_map();
2790 		}
2791 	}
2792 
2793 	/*
2794 	 * Reset the game state (TODO: add more stuff here)
2795 	 */
2796 	m_round_end_time = 0;
2797 	clear_weapons();
2798 	m_last_damage_time = 0;
2799 	m_last_recharge_time = 0;
2800 	m_last_weapon_switch = 0;
2801 }
2802 
2803 /*
2804  * Called when the game stop packet is received.
2805  */
round_over(PacketReader & reader)2806 void GameController::round_over(PacketReader& reader) {
2807 	char		winningteam;
2808 	int 		teamascore;
2809 	int		teambscore;
2810 
2811 	reader >> winningteam >> teamascore >> teambscore;
2812 
2813 	m_game_state = GAME_OVER;
2814 
2815 	if (winningteam == '-') {
2816 		display_message("DRAW");
2817 	} else if (winningteam == m_players[m_player_id].get_team()) {
2818 		display_message("VICTORY!");
2819 		m_sound_controller->play_sound("victory");
2820 	} else {
2821 		display_message("DEFEAT!");
2822 		m_sound_controller->play_sound("defeat");
2823 	}
2824 
2825 	change_team_scores(teamascore, teambscore);
2826 	toggle_score_overlay(true);
2827 
2828 	// Temporary score display
2829 	ostringstream	score_msg;
2830 	score_msg << "Blue: " << teamascore << " / Red: " << teambscore;
2831 	display_message(score_msg.str().c_str());
2832 	// End temporary score display
2833 
2834 	// Reset the gates and set yourself invisible and frozen until respawn.
2835 	m_map->reset_gates();
2836 	m_blue_gate_status_rect->set_image_width(GATE_STATUS_RECT_WIDTH);
2837 	m_red_gate_status_rect->set_image_width(GATE_STATUS_RECT_WIDTH);
2838 	m_players[m_player_id].set_is_invisible(true);
2839 	m_radar->set_blip_invisible(m_player_id,true);
2840 	m_players[m_player_id].set_is_frozen(true);
2841 	m_players[m_player_id].set_is_grabbing_obstacle(false);
2842 	m_time_to_unfreeze = 0;
2843 	m_total_time_frozen = 0;
2844 	update_energy_bar(0);
2845 	reset_weapons();
2846 }
2847 
round_start(PacketReader & reader)2848 void GameController::round_start(PacketReader& reader) {
2849 	// ACK TODO: Only accept this packet if we have gotten a related new_round packet
2850 
2851 	if (m_game_state == GAME_OVER) {
2852 		m_game_state = GAME_IN_PROGRESS;
2853 	}
2854 
2855 	uint64_t		time_left_in_game;
2856 	reader >> time_left_in_game;
2857 
2858 	if (time_left_in_game == numeric_limits<uint64_t>::max()) {
2859 		m_round_end_time = 0;
2860 	} else {
2861 		m_round_end_time = get_ticks() + time_left_in_game;
2862 	}
2863 
2864 	toggle_score_overlay(false);
2865 	m_sound_controller->play_sound("begin");
2866 	display_message("Game started!");
2867 }
2868 
2869 /*
2870  * Called when a score update packet is received.
2871  */
score_update(PacketReader & reader)2872 void GameController::score_update(PacketReader& reader) {
2873 	if (m_players.empty()) {
2874 		// WELCOME packet not received yet
2875 		// do NOT send an ACK for this SCORE_UPDATE packet, so that the server will resend it, hopefully after the WELCOME has come in.
2876 		return;
2877 	}
2878 
2879 	std::string	subject;
2880 	int		score;
2881 	reader >> subject >> score;
2882 
2883 	if (is_valid_team(subject[0])) {
2884 		char	team = subject[0];
2885 		if (team == 'A') {
2886 			change_team_scores(score, -1);
2887 		} else {
2888 			change_team_scores(-1, score);
2889 		}
2890 		toggle_score_overlay(!m_overlay_background->is_invisible());
2891 	} else if (GraphicalPlayer* player = get_player_by_id(atoi(subject.c_str()))) {
2892 		player->set_score(score);
2893 	}
2894 }
2895 
2896 /*
2897  * Called when an animation packet is received.
2898  */
animation_packet(PacketReader & reader)2899 void GameController::animation_packet(PacketReader& reader) {
2900 	uint32_t	player_id;
2901 	string		spritelist;
2902 	string		field;
2903 	int		value;
2904 
2905 	reader >> player_id >> spritelist >> field >> value;
2906 
2907 	StringTokenizer tokenizer(spritelist, '/');
2908 
2909 	if (m_players.count(player_id) == 0) {
2910 		return;
2911 	}
2912 
2913 	// Get the sprite to modify by going through the path separated by slashes.
2914 	Graphic* the_sprite = NULL;
2915 	while (tokenizer.has_more()) {
2916 		string spritename = tokenizer.get_next();
2917 
2918 		if (spritename == "all") {
2919 			if (the_sprite == NULL) {
2920 				the_sprite = m_players[player_id].get_sprite();
2921 			}
2922 		} else {
2923 			if (the_sprite == NULL) {
2924 				the_sprite = m_players[player_id].get_sprite()->get_graphic(spritename);
2925 			} else {
2926 				the_sprite = the_sprite->get_graphic(spritename);
2927 			}
2928 		}
2929 
2930 		if (the_sprite == NULL) {
2931 			return;
2932 		}
2933 	}
2934 
2935 	if (field == "rotation") {
2936 		the_sprite->set_rotation(value);
2937 	} else if (field == "scale_x") {
2938 		the_sprite->set_scale_x(value);
2939 	} else if (field == "scale_y") {
2940 		the_sprite->set_scale_y(value);
2941 	} else if (field == "x") {
2942 		the_sprite->set_x(value);
2943 	} else if (field == "y") {
2944 		the_sprite->set_y(value);
2945 	} else if (field == "center_x") {
2946 		the_sprite->set_center_x(value);
2947 	} else if (field == "center_y") {
2948 		the_sprite->set_center_y(value);
2949 	} else if (field == "invisible") {
2950 		the_sprite->set_invisible(value);
2951 		m_players[player_id].get_sprite()->set_invisible(m_players[player_id].get_sprite()->is_invisible());
2952 	}
2953 }
2954 
2955 /*
2956  * Called when a request denied packet is received.
2957  */
request_denied(PacketReader & reader)2958 void GameController::request_denied(PacketReader& reader) {
2959 	int		packet_type;
2960 	string		reason;
2961 	reader >> packet_type >> reason;
2962 
2963 	if (packet_type == JOIN_PACKET) {
2964 		string message = "Join denied! Reason: ";
2965 		message.append(reason);
2966 		display_message(message);
2967 		cout << "Join denied!  Reason: " << reason << endl;
2968 		disconnect();
2969 		m_game_state = SHOW_MENUS;
2970 	}
2971 }
2972 
2973 /*
2974  * Called when a player name change packet is received.
2975  */
name_change(PacketReader & reader)2976 void GameController::name_change(PacketReader& reader) {
2977 	uint32_t	player_id;
2978 	string		new_name;
2979 	reader >> player_id >> new_name;
2980 
2981 	if (GraphicalPlayer* player = get_player_by_id(player_id)) {
2982 		ostringstream	msg;
2983 		msg << player->get_name() << " is now known as " << new_name;
2984 
2985 		delete_individual_score(m_players[player_id]);
2986 
2987 		player->set_name(new_name.c_str());
2988 		if (player_id == m_player_id) {
2989 			m_configuration->set_string_value("name", new_name);
2990 			m_name = new_name;
2991 		}
2992 
2993 		if (player->get_team() == 'A') {
2994 			display_message(msg.str().c_str(), BLUE_COLOR, BLUE_SHADOW);
2995 		} else {
2996 			display_message(msg.str().c_str(), RED_COLOR, RED_SHADOW);
2997 		}
2998 
2999 		// Re-create the name sprite.
3000 		recreate_name(player);
3001 		update_individual_scores();
3002 	}
3003 }
3004 
3005 /*
3006  * Called when a team change packet is received.
3007  */
team_change(PacketReader & reader)3008 void GameController::team_change(PacketReader& reader) {
3009 	uint32_t	playerid;
3010 	char		team;
3011 	reader >> playerid >> team;
3012 
3013 	if (GraphicalPlayer* player = get_player_by_id(playerid)) {
3014 		delete_individual_score(m_players[playerid]);
3015 
3016 		player->set_team(team);
3017 
3018 		ostringstream	msg;
3019 		msg << player->get_name() << " has switched teams";
3020 
3021 		// Remove the name and sprite.
3022 		m_text_manager->remove_string(player->get_name_sprite());
3023 		m_window->unregister_graphic(player->get_sprite(), GameWindow::LAYER_GAME);
3024 		m_radar->remove_blip(playerid);
3025 		delete player->get_sprite();
3026 
3027 		// Generate new graphics for it.
3028 		if (team == 'A') {
3029 			player->set_sprite(new GraphicGroup(blue_player));
3030 			display_message(msg.str().c_str(), BLUE_COLOR, BLUE_SHADOW);
3031 			m_text_manager->set_shadow_color(BLUE_SHADOW);
3032 		} else {
3033 			player->set_sprite(new GraphicGroup(red_player));
3034 			display_message(msg.str().c_str(), RED_COLOR, RED_SHADOW);
3035 			m_text_manager->set_shadow_color(RED_SHADOW);
3036 		}
3037 		m_radar->add_blip(playerid,team,0,0);
3038 
3039 		if (Weapon* new_weapon = get_weapon(player->get_current_weapon_id())) {
3040 			new_weapon->select(*player, *this);
3041 		}
3042 
3043 		player->get_sprite()->set_invisible(false);
3044 		m_window->register_graphic(player->get_sprite(), GameWindow::LAYER_GAME);
3045 		player->set_name_sprite(m_text_manager->place_string(player->get_name(), player->get_x(), player->get_y()-(player->get_radius()+30), TextManager::CENTER, GameWindow::LAYER_GAME));
3046 		update_individual_scores();
3047 	}
3048 }
3049 
3050 /*
3051  * Send an animation packet.
3052  */
send_animation_packet(string sprite,string field,int value)3053 void GameController::send_animation_packet(string sprite, string field, int value) {
3054 	PacketWriter animation_packet(PLAYER_ANIMATION_PACKET);
3055 	animation_packet << m_player_id << sprite << field << value;
3056 
3057 	m_network.send_packet(animation_packet);
3058 }
3059 
3060 /*
3061  * Send a gate hold packet.
3062  */
send_gate_hold(bool holding)3063 void GameController::send_gate_hold(bool holding) {
3064 	PacketWriter gate_hold(GATE_UPDATE_PACKET);
3065 	if (holding) {
3066 		gate_hold << m_player_id << get_other_team(m_players[m_player_id].get_team()) << 1;
3067 	} else {
3068 		gate_hold << m_player_id << get_other_team(m_players[m_player_id].get_team()) << 0;
3069 	}
3070 	m_network.send_reliable_packet(gate_hold);
3071 }
3072 
set_gate_hold(bool holding_gate)3073 void GameController::set_gate_hold(bool holding_gate) {
3074 	if (m_holding_gate != holding_gate) {
3075 		m_holding_gate = holding_gate;
3076 		send_gate_hold(m_holding_gate);
3077 	}
3078 
3079 }
3080 
3081 /*
3082  * Send a name change packet.
3083  */
send_name_change_packet(const char * new_name)3084 void GameController::send_name_change_packet(const char* new_name) {
3085 	PacketWriter packet(NAME_CHANGE_PACKET);
3086 	packet << m_player_id << new_name;
3087 	m_network.send_reliable_packet(packet);
3088 }
3089 
3090 /*
3091  * Recreate the player name sprite.
3092  */
recreate_name(GraphicalPlayer * player)3093 void GameController::recreate_name(GraphicalPlayer* player) {
3094 	// Re-create the name sprite.
3095 	if (player->get_team() == 'A') {
3096 		m_text_manager->set_active_color(BLUE_COLOR);
3097 		m_text_manager->set_shadow_color(BLUE_SHADOW);
3098 	} else {
3099 		m_text_manager->set_active_color(RED_COLOR);
3100 		m_text_manager->set_shadow_color(RED_SHADOW);
3101 	}
3102 	m_text_manager->remove_string(player->get_name_sprite());
3103 	m_text_manager->set_active_font(m_font);
3104 	string name_string = player->get_name();
3105 	if (player->is_frozen()) {
3106 		m_font->set_font_style(false, true);
3107 		name_string = "[" + name_string + "]";
3108 	}
3109 	player->set_name_sprite(m_text_manager->place_string(name_string, player->get_x(), player->get_y()-(player->get_radius()+30), TextManager::CENTER, GameWindow::LAYER_GAME));
3110 	m_font->set_font_style(false, false);
3111 	m_text_manager->set_shadow_color(TEXT_SHADOW);
3112 }
3113 
3114 /*
3115  * Send a team change packet.
3116  */
send_team_change_packet(char new_team)3117 void GameController::send_team_change_packet(char new_team) {
3118 	PacketWriter packet(TEAM_CHANGE_PACKET);
3119 	packet << m_player_id << new_team;
3120 	m_network.send_reliable_packet(packet);
3121 }
3122 
3123 /*
3124  * Display a message on the screen.
3125  */
display_message(string message,Color color,Color shadow,bool bold)3126 void GameController::display_message(string message, Color color, Color shadow, bool bold) {
3127 	m_text_manager->set_active_color(color);
3128 	m_text_manager->set_shadow_color(shadow);
3129 	if (bold) {
3130 		m_text_manager->set_active_font(m_bold_font);
3131 	} else {
3132 		m_text_manager->set_active_font(m_font);
3133 	}
3134 	int y = 20 + (m_font->ascent() + m_font->descent() + 5) * m_messages.size();
3135 	Text* message_sprite = m_text_manager->place_string(message, 20, y, TextManager::LEFT, GameWindow::LAYER_SUPER);
3136 	if (!message_sprite) {
3137 		return;
3138 	}
3139 	uint64_t currframe = get_ticks();
3140 	Message new_message;
3141 	new_message.message = message_sprite;
3142 	new_message.timeout = currframe + MESSAGE_DISPLAY_TIME;
3143 	if (m_chat_window_transition_x != NULL) {
3144 		new_message.transition = new Transition(new_message.message, &Graphic::set_y, new LinearCurve(y, y), currframe, 1);
3145 		m_transition_manager.add_transition(new_message.transition, false, TransitionManager::KEEP);
3146 		m_chat_window_transition_y->change_curve(currframe, new LinearCurve(0, y + message_sprite->get_image_height() + 6 - m_chat_window_back->get_y()), 1);
3147 		double max_w = m_chat_window_transition_x->get_curve()->get_end();
3148 		if (max_w < message_sprite->get_image_width() + 6) {
3149 			m_chat_window_transition_x->change_curve(currframe, new LinearCurve(0, message_sprite->get_image_width() + 6), 1);
3150 		}
3151 	} else {
3152 		new_message.transition = NULL;
3153 		m_chat_window_back->set_row_height(0, y + message_sprite->get_image_height() + 6 - m_chat_window_back->get_y());
3154 		double max_w = m_chat_window_back->get_image_width();
3155 		if (max_w < message_sprite->get_image_width() + 6) {
3156 			m_chat_window_back->set_width(message_sprite->get_image_width() + 6);
3157 		}
3158 	}
3159 	m_messages.push_back(new_message);
3160 	m_chat_log->add_message(message, color, shadow);
3161 	if (m_configuration->get_bool_value("text_background")) {
3162 		m_chat_window_back->set_invisible(false);
3163 	}
3164 }
3165 
3166 /*
3167  * Get a player by their ID.
3168  */
get_player_by_id(uint32_t player_id)3169 GraphicalPlayer* GameController::get_player_by_id(uint32_t player_id) {
3170 	map<uint32_t, GraphicalPlayer>::iterator it(m_players.find(player_id));
3171 	return it == m_players.end() ? NULL : &it->second;
3172 }
get_player_by_id(uint32_t player_id) const3173 const GraphicalPlayer* GameController::get_player_by_id(uint32_t player_id) const {
3174 	map<uint32_t, GraphicalPlayer>::const_iterator it(m_players.find(player_id));
3175 	return it == m_players.end() ? NULL : &it->second;
3176 }
3177 
3178 /*
3179  * Get a player by name.
3180  */
get_player_by_name(const char * name)3181 GraphicalPlayer* GameController::get_player_by_name(const char* name) {
3182 	for (map<uint32_t, GraphicalPlayer>::iterator it(m_players.begin()); it != m_players.end(); ++it) {
3183 		if (it->second.compare_name(name)) {
3184 			return &it->second;
3185 		}
3186 	}
3187 	return NULL;
3188 }
3189 
3190 /*
3191  * Send an ack packet.
3192  */
server_info(const IPAddress & server_address,PacketReader & info_packet)3193 void	GameController::server_info(const IPAddress& server_address, PacketReader& info_packet) {
3194 	uint32_t	request_packet_id;
3195 	uint64_t	scan_start_time;
3196 	info_packet >> request_packet_id >> scan_start_time;
3197 
3198 	if (request_packet_id != m_current_scan_id && request_packet_id != m_current_ping_id) {
3199 		// From an old scan - ignore it
3200 		return;
3201 	}
3202 
3203 	if (server_address == m_metaserver_address) {
3204 		// A response from the meta server
3205 		// Now send an info packet to the server specified in this packet, to measure ping time and get the most up-to-date information
3206 		IPAddress	server_address;
3207 		info_packet >> server_address;
3208 		scan_server(server_address);
3209 	} else {
3210 		// A response from an actual server
3211 		// Get the info on the server, and present it to the user
3212 		int		server_protocol_version;
3213 		string		current_map_name;
3214 		int		team_count[2];
3215 		int		max_players;
3216 		uint64_t	uptime;
3217 		uint64_t	time_left_in_game;
3218 		string		server_name;
3219 		string		server_location;
3220 		Version		server_compat_version;
3221 		info_packet >> server_protocol_version >> server_compat_version >> current_map_name >> team_count[0] >> team_count[1] >> max_players >> uptime >> time_left_in_game >> server_name >> server_location;
3222 
3223 		m_ping = get_ticks() - scan_start_time;
3224 		if (request_packet_id == m_current_ping_id) {
3225 			//cerr << "Ping: " << m_ping << " Framerate: " << m_framerate << endl;
3226 			return;
3227 		}
3228 
3229 		//cerr << "Received INFO packet from " << format_ip_address(server_address, true) << ": Protocol=" << server_protocol_version << "; Map=" << current_map_name << "; Blue players=" << team_count[0] << "; Red players=" << team_count[1] << "; Ping time=" << get_ticks() - scan_start_time << "ms"  << "; Uptime=" << uptime << endl;
3230 
3231 		if (server_protocol_version != m_protocol_number || server_compat_version != COMPAT_VERSION) {
3232 			//cerr << "Server with different protocol found: " << format_ip_address(server_address, true) << ": Protocol=" << server_protocol_version << "; Map=" << current_map_name << "; Blue players=" << team_count[0] << "; Red players=" << team_count[1] << "; Ping time=" << get_ticks() - scan_start_time << "ms" << endl;
3233 			return;
3234 		}
3235 
3236 		if (m_server_browser->is_invisible()) {
3237 			return;
3238 		}
3239 
3240 		if (!m_server_browser->contains_ip(server_address)) {
3241 			m_server_browser->add_entry(server_address, current_map_name, team_count, max_players, uptime, m_ping, server_name, server_location);
3242 		}
3243 	}
3244 }
3245 
3246 
hole_punch_packet(const IPAddress & server_address,PacketReader & packet)3247 void	GameController::hole_punch_packet(const IPAddress& server_address, PacketReader& packet) {
3248 	uint32_t	scan_id;
3249 	packet >> scan_id;
3250 
3251 	if (scan_id != m_current_scan_id || m_server_browser->contains_ip(server_address)) {
3252 		return;
3253 	}
3254 
3255 	scan_server(server_address);
3256 }
3257 
3258 
upgrade_available(const IPAddress & server_address,PacketReader & packet)3259 void	GameController::upgrade_available(const IPAddress& server_address, PacketReader& packet) {
3260 	string		latest_version;
3261 	packet >> latest_version;
3262 	ostringstream message;
3263 	message << "Version: " << latest_version;
3264 
3265 	if (m_version_nag1 != NULL) {
3266 		m_main_menu.remove_item(m_version_nag1);
3267 		delete m_version_nag1;
3268 		m_version_nag1 = NULL;
3269 	}
3270 	if (m_version_nag2 != NULL) {
3271 		m_main_menu.remove_item(m_version_nag2);
3272 		delete m_version_nag2;
3273 		m_version_nag2 = NULL;
3274 	}
3275 	m_text_manager->set_active_font(m_menu_font);
3276 	m_text_manager->set_active_color(TEXT_COLOR);
3277 	Text *nag1 = m_text_manager->render_string("There is an upgrade available!", m_window->get_width() - 30, 200, TextManager::RIGHT);
3278 	Text *nag2 = m_text_manager->render_string(message.str(), m_window->get_width() - 30, 240, TextManager::RIGHT);
3279 	m_version_nag1 = new TextMenuItem(nag1, "", MenuItem::STATIC);
3280 	m_version_nag2 = new TextMenuItem(nag2, "", MenuItem::STATIC);
3281 	m_main_menu.add_item(m_version_nag1);
3282 	m_main_menu.add_item(m_version_nag2);
3283 }
3284 
scan_all()3285 void	GameController::scan_all() {
3286 	PacketWriter info_request_packet(INFO_PACKET);
3287 	m_current_scan_id = get_next_scan_id();
3288 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks() << m_client_version;
3289 	m_network.broadcast_packet(DEFAULT_PORTNO, info_request_packet);
3290 	IPAddress localhostip;
3291 	if (resolve_hostname(localhostip, "localhost", DEFAULT_PORTNO)) {
3292 		m_network.send_packet_to(localhostip, info_request_packet);
3293 	}
3294 	if (!m_offline_mode) {
3295 		m_network.send_packet_to(m_metaserver_address, info_request_packet);
3296 	}
3297 }
3298 
check_for_upgrade()3299 void    GameController::check_for_upgrade() {
3300 	PacketWriter packet(UPGRADE_AVAILABLE_PACKET);
3301 	packet << m_client_version;
3302 	m_network.send_packet_to(m_metaserver_address, packet);
3303 }
3304 
scan_local_network()3305 void	GameController::scan_local_network() {
3306 	PacketWriter info_request_packet(INFO_PACKET);
3307 	m_current_scan_id = get_next_scan_id();
3308 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks();
3309 	m_network.broadcast_packet(DEFAULT_PORTNO, info_request_packet);
3310 }
3311 
contact_metaserver()3312 void	GameController::contact_metaserver() {
3313 	PacketWriter info_request_packet(INFO_PACKET);
3314 	m_current_scan_id = get_next_scan_id();
3315 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks() << m_client_version;
3316 	m_network.send_packet_to(m_metaserver_address, info_request_packet);
3317 }
3318 
ping_server(const IPAddress & server_address)3319 void	GameController::ping_server(const IPAddress& server_address) {
3320 	PacketWriter info_request_packet(INFO_PACKET);
3321 	m_current_scan_id = get_next_scan_id();
3322 	info_request_packet << m_protocol_number << m_current_ping_id << get_ticks();
3323 	m_network.send_packet_to(server_address, info_request_packet);
3324 }
3325 
scan_server(const IPAddress & server_address)3326 void	GameController::scan_server(const IPAddress& server_address) {
3327 	PacketWriter info_request_packet(INFO_PACKET);
3328 	info_request_packet << m_protocol_number << m_current_scan_id << get_ticks();
3329 	m_network.send_packet_to(server_address, info_request_packet);
3330 }
3331 
set_player_name(string name)3332 void	GameController::set_player_name(string name) {
3333 	m_name = name;
3334 	m_configuration->set_string_value("name", name);
3335 	if (m_network.is_connected()) {
3336 		send_name_change_packet(m_name.c_str());
3337 	}
3338 	m_text_manager->set_active_font(m_menu_font);
3339 	m_name_input->set_default_value(name);
3340 	m_name_input->reset();
3341 }
3342 
clear_players()3343 void	GameController::clear_players() {
3344 	if (!m_players.empty()) {
3345 		map<uint32_t, GraphicalPlayer>::iterator it;
3346 		for ( it=m_players.begin() ; it != m_players.end(); it++ ) {
3347 			const GraphicalPlayer& currplayer = (*it).second;
3348 			m_text_manager->remove_string(m_players[currplayer.get_id()].get_name_sprite());
3349 			m_window->unregister_graphic(m_players[currplayer.get_id()].get_sprite(), GameWindow::LAYER_GAME);
3350 			m_radar->remove_blip(currplayer.get_id());
3351 			delete_individual_score(m_players[currplayer.get_id()]);
3352 			delete m_players[currplayer.get_id()].get_sprite();
3353 		}
3354 	}
3355 	m_players.clear();
3356 }
3357 
sound_finished(int channel)3358 void	GameController::sound_finished(int channel) {
3359 	for (unsigned int i = 0; i < (sizeof(m_gate_lower_sounds)/sizeof(m_gate_lower_sounds[0])); i++) {
3360 		if (m_gate_lower_sounds[i] == channel) {
3361 			m_gate_lower_sounds[i] = -1;
3362 		}
3363 	}
3364 }
3365 
wants_restart()3366 bool	GameController::wants_restart() {
3367 	return m_restart;
3368 }
3369 
get_server_address()3370 string	GameController::get_server_address() {
3371 	if (m_network.is_connected() && m_join_sent_time == 0) {
3372 		return format_ip_address(m_network.get_server_address(), true);
3373 	} else {
3374 		return "";
3375 	}
3376 }
3377 
format_time_from_millis(uint64_t milliseconds)3378 string	GameController::format_time_from_millis(uint64_t milliseconds) {
3379 	unsigned int uptimesecs = (milliseconds/1000);
3380 	unsigned int uptimedays = uptimesecs/86400;
3381 	uptimesecs -= uptimedays * 86400;
3382 	unsigned int uptimehours = uptimesecs/3600;
3383 	uptimesecs -= uptimehours * 3600;
3384 	unsigned int uptimeminutes = uptimesecs/60;
3385 	uptimesecs -= uptimeminutes * 60;
3386 	ostringstream uptimestr;
3387 	if (uptimedays != 0) {
3388 		uptimestr << uptimedays << "d ";
3389 	}
3390 	uptimestr << uptimehours << ":";
3391 	if (uptimeminutes < 10) {
3392 		uptimestr << "0";
3393 	}
3394 	uptimestr << uptimeminutes << ":";
3395 	if (uptimesecs < 10) {
3396 		uptimestr << "0";
3397 	}
3398 	uptimestr << uptimesecs;
3399 	return uptimestr.str();
3400 }
3401 
map_info_packet(PacketReader & reader)3402 void GameController::map_info_packet(PacketReader& reader) {
3403 	if (m_map_receiver.get() && m_map_receiver->map_info(reader)) {
3404 		init_map(m_map->get_width(), m_map->get_height());
3405 		if (m_map_receiver->is_done()) {
3406 			m_map_receiver.reset();
3407 		}
3408 	}
3409 }
3410 
map_object_packet(PacketReader & reader)3411 void GameController::map_object_packet(PacketReader& reader) {
3412 	if (m_map_receiver.get() && m_map_receiver->map_object(reader)) {
3413 		if (m_map_receiver->is_done()) {
3414 			m_map_receiver.reset();
3415 		}
3416 	}
3417 }
3418 
request_map()3419 void	GameController::request_map() {
3420 	m_map->clear();
3421 	m_map_receiver.reset(new MapReceiver(*m_map));
3422 
3423 	PacketWriter		packet(MAP_INFO_PACKET);
3424 	packet << m_player_id << m_map_receiver->transmission_id();
3425 	m_network.send_reliable_packet(packet);
3426 }
3427 
load_map(const char * map_name,int map_revision)3428 bool	GameController::load_map(const char* map_name, int map_revision) {
3429 	if (strpbrk(map_name, "/\\") != NULL) {
3430 		return false;
3431 	}
3432 
3433 	string		map_filename(map_name);
3434 	map_filename += ".map";
3435 
3436 	if (!m_map->load_file(m_path_manager.data_path(map_filename.c_str(), "maps"))) {
3437 		return false;
3438 	}
3439 
3440 	if (m_map->get_revision() != map_revision) {
3441 		return false;
3442 	}
3443 
3444 	init_map(m_map->get_width(), m_map->get_height());
3445 	return true;
3446 }
3447 
init_map(int map_width,int map_height)3448 void	GameController::init_map(int map_width, int map_height) {
3449 	m_map_width = map_width;
3450 	m_map_height = map_height;
3451 	m_map_polygon.make_rectangle(m_map_width, m_map_height);
3452 }
3453 
display_legalese()3454 void	GameController::display_legalese() {
3455 	const char*	legalese[] = {
3456 		"This is Leges Motus, a networked, 2D shooter set in zero gravity.",
3457 		" ",
3458 		"Copyright 2009-2010 Andrew Ayer, Nathan Partlan, Jeffrey Pfau",
3459 		" ",
3460 		"Leges Motus is free and open source software.  You may redistribute it and/or",
3461 		"modify it under the terms of version 2, or (at your option) version 3, of the",
3462 		"GNU General Public License (GPL), as published by the Free Software Foundation.",
3463 		" ",
3464 		"Leges Motus is distributed in the hope that it will be useful, but WITHOUT ANY",
3465 		"WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A",
3466 		"PARTICULAR PURPOSE.  See the full text of the GNU General Public License for",
3467 		"further detail.",
3468 		" ",
3469 		"For a full copy of the GNU General Public License, please see the COPYING file",
3470 		"in the root of the source code tree.  You may also retrieve a copy from",
3471 		"<http://www.gnu.org/licenses/gpl-2.0.txt>, or request a copy by writing to the",
3472 		"Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA",
3473 		"02111-1307  USA",
3474 		" "
3475 	};
3476 
3477 	for (size_t i = 0; i < sizeof(legalese) / sizeof(legalese[0]); ++i) {
3478 		display_message(legalese[i]);
3479 	}
3480 }
3481 
game_param_packet(PacketReader & packet)3482 void GameController::game_param_packet(PacketReader& packet) {
3483 	if (!m_params.process_param_packet(packet)) {
3484 		// Parameter not recognized - reject the packet
3485 		return;
3486 	}
3487 
3488 	set_radar_mode(m_params.radar_mode);
3489 	m_radar->set_scale(m_params.radar_scale);
3490 }
3491 
set_radar_mode(RadarMode mode)3492 void GameController::set_radar_mode(RadarMode mode) {
3493 	if (mode == m_radar->get_mode()) {
3494 		return;
3495 	}
3496 
3497 	m_radar->set_mode(mode);
3498 
3499 	if (mode == RADAR_AURAL) {
3500 		// Hide all the radar blips, because we're entering aural mode
3501 		for (std::map<uint32_t, GraphicalPlayer>::const_iterator it(m_players.begin()); it != m_players.end(); ++it) {
3502 			if (it->second.get_id() != m_player_id) {
3503 				m_radar->set_blip_alpha(it->second.get_id(), 0.0);
3504 			}
3505 		}
3506 	}
3507 }
3508 
next_weapon()3509 void GameController::next_weapon() {
3510 	// Change to the next weapon in the list.
3511 	map<string, Weapon*>::iterator it(m_weapons.begin());
3512 	for (int i = 0; it != m_weapons.end(); it++, i++) {
3513 		if (it->second == m_current_weapon) {
3514 			change_weapon(i+1);
3515 			break;
3516 		}
3517 	}
3518 }
3519 
previous_weapon()3520 void GameController::previous_weapon() {
3521 	// Change to the previous weapon in the list.
3522 	map<string, Weapon*>::iterator it(m_weapons.begin());
3523 	for (int i = 0; it != m_weapons.end(); it++, i++) {
3524 		if (it->second == m_current_weapon) {
3525 			if (i == 0) {
3526 				i = m_weapons.size();
3527 			}
3528 			i--;
3529 			change_weapon(i);
3530 			break;
3531 		}
3532 	}
3533 }
3534 
change_weapon(const char * weapon_id)3535 void GameController::change_weapon(const char* weapon_id) {
3536 	if (Weapon* weapon = get_weapon(weapon_id)) {
3537 		change_weapon(weapon);
3538 	}
3539 }
3540 
change_weapon(unsigned int n)3541 void GameController::change_weapon(unsigned int n) {
3542 	if (n >= m_weapons.size()) {
3543 		return;
3544 	}
3545 
3546 	map<string, Weapon*>::iterator it(m_weapons.begin());
3547 	advance(it, n);
3548 	change_weapon(it->second);
3549 }
3550 
change_weapon(Weapon * weapon)3551 void	GameController::change_weapon(Weapon* weapon) {
3552 	if (m_current_weapon != weapon) {
3553 		m_current_weapon = weapon;
3554 		m_last_weapon_switch = get_ticks();
3555 		update_curr_weapon_image();
3556 	}
3557 }
3558 
get_crosshairs_angle() const3559 double	GameController::get_crosshairs_angle() const {
3560 	if (m_players.empty()) {
3561 		return 0;
3562 	}
3563 
3564 	Point	crosshairs_location(m_crosshairs->get_x() + m_offset_x, m_crosshairs->get_y() + m_offset_y);
3565 	return Vector(crosshairs_location - get_player_by_id(m_player_id)->get_position()).get_angle();
3566 }
3567 
freeze(uint64_t time_to_unfreeze)3568 void	GameController::freeze(uint64_t time_to_unfreeze) {
3569 	if (GraphicalPlayer* player = get_player_by_id(m_player_id)) {
3570 		if (!player->is_frozen() && time_to_unfreeze != 0) {
3571 			m_frozen_status_rect->set_y(m_screen_height/2 + player->get_radius() + 15);
3572 			m_frozen_status_rect_back->set_y(m_frozen_status_rect->get_y());
3573 			m_frozen_status_text->set_y(m_frozen_status_rect->get_y());
3574 			m_sound_controller->play_sound("freeze");
3575 			player->set_is_frozen(true);
3576 			if (m_radar->get_mode() == RADAR_ON) {
3577 				m_radar->set_blip_alpha(m_player_id, 0.5);
3578 			}
3579 			recreate_name(player);
3580 			m_time_to_unfreeze = get_ticks() + time_to_unfreeze;
3581 			m_total_time_frozen = time_to_unfreeze;
3582 		}
3583 	}
3584 }
3585 
unfreeze()3586 void	GameController::unfreeze() {
3587 	if (GraphicalPlayer* player = get_player_by_id(m_player_id)) {
3588 		m_sound_controller->play_sound("unfreeze");
3589 		if (player->is_dead()) {
3590 			// If we were dead (i.e. at 0 energy), reset our energy
3591 			// Otherwise, we continue with the energy we had before getting killed
3592 			player->reset_energy();
3593 			update_energy_bar();
3594 			m_last_damage_time = 0;
3595 		}
3596 		player->set_is_frozen(false);
3597 		m_last_recharge_time = 0;
3598 		recreate_name(player);
3599 		if (m_radar->get_mode() == RADAR_ON) {
3600 			m_radar->set_blip_alpha(m_player_id, 1.0);
3601 		}
3602 		m_time_to_unfreeze = 0;
3603 		m_total_time_frozen = 0;
3604 	}
3605 }
3606 
reduce_freeze_time(uint64_t milliseconds)3607 void	GameController::reduce_freeze_time(uint64_t milliseconds) {
3608 	if (GraphicalPlayer* player = get_player_by_id(m_player_id)) {
3609 		if (player->is_frozen()) {
3610 			if (m_time_to_unfreeze <= milliseconds || m_total_time_frozen <= milliseconds) {
3611 				unfreeze();
3612 			}
3613 
3614 			m_time_to_unfreeze -= milliseconds;
3615 
3616 			if (get_ticks() > m_time_to_unfreeze) {
3617 				unfreeze();
3618 			}
3619 
3620 			m_total_time_frozen -= milliseconds;
3621 		}
3622 	}
3623 }
3624 
play_sound(const char * sound_name)3625 void	GameController::play_sound(const char* sound_name) {
3626 	m_sound_controller->play_sound(sound_name);
3627 }
3628 
get_weapon(const string & name)3629 Weapon*	GameController::get_weapon(const string& name) {
3630 	map<string, Weapon*>::iterator it(m_weapons.find(name));
3631 	return it == m_weapons.end() ? NULL : it->second;
3632 }
3633 
update_curr_weapon_image()3634 void	GameController::update_curr_weapon_image() {
3635 	if (m_current_weapon == NULL) {
3636 		if (m_curr_weapon_image != NULL) {
3637 			m_curr_weapon_image->set_invisible(true);
3638 		}
3639 		return;
3640 	}
3641 	m_window->unregister_graphic(m_curr_weapon_image, GameWindow::LAYER_HUD);
3642 	delete m_curr_weapon_image;
3643 	m_curr_weapon_image = m_graphics_cache.new_graphic<Sprite>(m_path_manager.data_path(m_current_weapon->hud_graphic(), "sprites"));
3644 	m_curr_weapon_image->set_x(m_cooldown_bar->get_x());
3645 	m_curr_weapon_image->set_y(m_cooldown_bar->get_y() - m_curr_weapon_image->get_image_height()/2 - 5);
3646 	m_curr_weapon_image->set_invisible(false);
3647 	m_window->register_graphic(m_curr_weapon_image, GameWindow::LAYER_HUD);
3648 
3649 	if (Player* curr_player = get_player_by_id(m_player_id)) {
3650 		curr_player->set_current_weapon_id(m_current_weapon->get_id());
3651 		m_current_weapon->select(*curr_player, *this); // TODO: make this triggered by GraphicalPlayer::set_current_weapon_id()
3652 	}
3653 }
3654 
reset_weapons()3655 void	GameController::reset_weapons() {
3656 	for (map<string, Weapon*>::iterator it(m_weapons.begin()); it != m_weapons.end(); ++it) {
3657 		it->second->reset();
3658 	}
3659 }
3660 
clear_weapons()3661 void	GameController::clear_weapons() {
3662 	for (map<string, Weapon*>::iterator it(m_weapons.begin()); it != m_weapons.end(); ++it) {
3663 		delete it->second;
3664 	}
3665 	m_weapons.clear();
3666 	m_current_weapon = NULL;
3667 }
3668 
activate_radar_blip(const Player & player)3669 void	GameController::activate_radar_blip(const Player& player) {
3670 	m_radar->activate_blip(player.get_id(), get_ticks(), m_params.radar_blip_duration);
3671 }
3672 
damage(int amount,const Player * aggressor)3673 bool	GameController::damage (int amount, const Player* aggressor) {
3674 	if (amount == 0 || m_players.empty() || m_players[m_player_id].is_frozen() || m_players[m_player_id].is_dead()) {
3675 		// No effect on already frozen players
3676 		return false;
3677 	}
3678 
3679 	m_players[m_player_id].change_energy(-amount);
3680 	m_last_damage_time = get_ticks();
3681 	update_energy_bar();
3682 	if (m_players[m_player_id].is_dead()) {
3683 		// Inform the server that we died
3684 		PacketWriter		died_packet(PLAYER_DIED_PACKET);
3685 		died_packet << m_player_id << (aggressor ? aggressor->get_id() : 0);
3686 		m_network.send_reliable_packet(died_packet);
3687 
3688 		return true;
3689 	}
3690 	return false;
3691 }
3692 
HitObject(double arg_distance,Point arg_point,BaseMapObject * arg_map_object)3693 GameController::HitObject::HitObject (double arg_distance, Point arg_point, BaseMapObject* arg_map_object) {
3694 	distance = arg_distance;
3695 	point = arg_point;
3696 	map_object = arg_map_object;
3697 	player = 0;
3698 }
3699 
HitObject(double arg_distance,Point arg_point,Player * arg_player)3700 GameController::HitObject::HitObject (double arg_distance, Point arg_point, Player* arg_player) {
3701 	distance = arg_distance;
3702 	point = arg_point;
3703 	map_object = 0;
3704 	player = arg_player;
3705 }
3706 
3707 // Starting at a given pivot point, find all players that are in a given region, populate
3708 // the given set with the players that are hit. Returns a list of hit points.
3709 // TODO: add and make use of bool penetrate_players, bool penetrate_obstacles
shoot_in_region(const Shape & shape,const Point & pivot,std::list<Player * > & hit_players)3710 list<Point>	GameController::shoot_in_region(const Shape& shape, const Point& pivot, std::list<Player*>& hit_players) {
3711 	list<Point> hit_points;
3712 
3713 	// Find the players in this region
3714 	for (map<uint32_t, GraphicalPlayer>::iterator it(m_players.begin()); it != m_players.end(); ++it) {
3715 		GraphicalPlayer& thisplayer = it->second;
3716 		if (thisplayer.get_id() == m_player_id) {
3717 			continue;
3718 		}
3719 
3720 		Point	playerpos = thisplayer.get_position();
3721 
3722 		Circle	player_circle(playerpos, thisplayer.get_radius());
3723 
3724 		double	angle = 0;
3725 		double	intersecting = shape.solid_intersects_circle(player_circle, &angle);
3726 		if (intersecting == -1) {
3727 			// Not intersecting
3728 			continue;
3729 		}
3730 
3731 		double	distance = Point::distance(pivot, playerpos);
3732 		if (distance != -1) {
3733 			HitObject thishit = HitObject(distance, playerpos, &thisplayer);
3734 
3735 			// Don't add the player if it's occluded.
3736 			Point actualhit = is_occluded(pivot, playerpos, thishit);
3737 			if (actualhit.x == -1 && actualhit.y == -1) {
3738 				// Not actually a hit
3739 				continue;
3740 			}
3741 
3742 			hit_players.push_back(&thisplayer);
3743 			hit_points.push_back(actualhit);
3744 		}
3745 	}
3746 	return hit_points;
3747 }
3748 
3749 // Check if any objects are in the way of this object starting at a given point
3750 // If occluded, return (-1, -1). If not, return the hit point.
is_occluded(const Point & startpos,Point & objectcenter,HitObject & object)3751 Point	GameController::is_occluded(const Point& startpos, Point& objectcenter, HitObject& object) {
3752 	double x_dist = objectcenter.x - startpos.x;
3753 	double y_dist = objectcenter.y - startpos.y;
3754 	double direction = atan2(y_dist, x_dist);
3755 
3756 	multiset<HitObject>	hit_objects;
3757 	shoot_in_line(startpos, direction, hit_objects);
3758 
3759 	// Find the nearest hit object, check if it's the same as the given object
3760 	const HitObject& nearest_hit(*hit_objects.begin());
3761 
3762 	if ((nearest_hit.map_object != NULL && object.map_object != NULL &&
3763 			 nearest_hit.map_object == object.map_object)
3764 			|| (nearest_hit.player != NULL && object.player != NULL &&
3765 			 nearest_hit.player->get_id() == object.player->get_id())) {
3766 		return nearest_hit.point;
3767 	} else {
3768 		return Point(-1, -1);
3769 	}
3770 }
3771 
3772 // Starting from the given point, shoot in a STRAIGHT LINE in the given direction,
3773 // and populate the given set with the objects that are hit.
shoot_in_line(Point startpos,double direction,multiset<HitObject> & hit_objects)3774 void	GameController::shoot_in_line(Point startpos, double direction, multiset<HitObject>& hit_objects)
3775 {
3776 	Point	endpos(startpos + Vector::make_from_magnitude(m_map_width + m_map_height, direction));
3777 
3778 	//
3779 	// First, find what map objects this line hits
3780 	//
3781 	const list<BaseMapObject*>& map_objects(m_map->get_objects());
3782 	for (list<BaseMapObject*>::const_iterator it(map_objects.begin()); it != map_objects.end(); ++it) {
3783 		BaseMapObject*	thisobj = *it;
3784 		if (!thisobj->is_shootable() || !thisobj->is_intersectable()) {
3785 			continue;
3786 		}
3787 
3788 		Point intersection = thisobj->get_bounding_shape()->intersects_line(startpos, endpos, NULL);
3789 		if (intersection.x == -1 && intersection.y == -1) {
3790 			// Not intersecting line
3791 			continue;
3792 		}
3793 
3794 		double distance = Point::distance(startpos, intersection);
3795 		if (distance != -1) {
3796 			hit_objects.insert(HitObject(distance, intersection, thisobj));
3797 		}
3798 	}
3799 
3800 	//
3801 	// Now, find what players this line hits
3802 	//
3803 	for (map<uint32_t, GraphicalPlayer>::iterator it(m_players.begin()); it != m_players.end(); ++it) {
3804 		GraphicalPlayer& thisplayer = it->second;
3805 		if (thisplayer.get_id() == m_player_id) {
3806 			continue;
3807 		}
3808 
3809 		Circle	player_circle(thisplayer.get_position(), thisplayer.get_radius());
3810 
3811 		Point	intersection = player_circle.intersects_line(startpos, endpos, NULL);
3812 		if (intersection.x == -1 && intersection.y == -1) {
3813 			// Not intersecting line
3814 			continue;
3815 		}
3816 
3817 		double	distance = Point::distance(startpos, intersection);
3818 		if (distance != -1) {
3819 			hit_objects.insert(HitObject(distance, intersection, &thisplayer));
3820 		}
3821 	}
3822 
3823 	//
3824 	// Finally, find the nearest map edge where the shot will hit.
3825 	//
3826 	Point	edge_intersection = m_map_polygon.intersects_line(startpos, endpos, NULL);
3827 
3828 	if (edge_intersection.x != -1 || edge_intersection.y != -1) {
3829 		double	distance = Point::distance(startpos, edge_intersection);
3830 		if (distance != -1) {
3831 			hit_objects.insert(HitObject(distance, edge_intersection));
3832 		}
3833 	}
3834 }
3835 
3836 // This is a COMPATIBILITY WRAPPER around the more general shoot_in_line() function above.
3837 // Code should be migrated to use the new function.
find_shootable_object(Point startpos,double direction,BaseMapObject * & hit_map_object,Player * & hit_player)3838 Point GameController::find_shootable_object(Point startpos, double direction, BaseMapObject*& hit_map_object, Player*& hit_player) {
3839 	multiset<HitObject>	hit_objects;
3840 	shoot_in_line(startpos, direction, hit_objects);
3841 
3842 	if (hit_objects.empty()) {
3843 		return Point(-1, -1);
3844 	}
3845 
3846 	const HitObject&	nearest_hit(*hit_objects.begin());
3847 	hit_map_object = nearest_hit.map_object;
3848 	hit_player = nearest_hit.player;
3849 	return nearest_hit.point;
3850 }
3851 
3852 
3853 
weapon_info_packet(PacketReader & reader)3854 void	GameController::weapon_info_packet(PacketReader& reader) {
3855 	size_t			weapon_index;
3856 	WeaponReader		weapon_data;
3857 	reader >> weapon_index;
3858 	reader >> weapon_data;
3859 
3860 	if (!m_weapons.count(weapon_data.get_id())) {
3861 		if (Weapon* weapon = Weapon::new_weapon(weapon_data)) {
3862 			m_weapons.insert(std::make_pair(weapon->get_id(), weapon));
3863 			if (weapon_index == 0) {
3864 				m_current_weapon = weapon;
3865 				update_curr_weapon_image();
3866 			}
3867 			init_weapon_selector();
3868 		}
3869 	}
3870 }
3871 
spawn_packet(PacketReader & reader)3872 void GameController::spawn_packet(PacketReader& reader) {
3873 	// ACK TODO: ignore packet if we have already spawned, or if round has not started yet
3874 	Point			position;
3875 	Vector			velocity;
3876 	bool			is_grabbing_obstacle;
3877 	bool			is_alive;
3878 	uint64_t		freeze_time;
3879 
3880 	reader >> position >> velocity >> is_grabbing_obstacle >> is_alive >> freeze_time;
3881 
3882 	GraphicalPlayer*	player = get_player_by_id(m_player_id);
3883 
3884 	if (!player) {
3885 		return;
3886 	}
3887 
3888 	player->set_position(position);
3889 	player->set_velocity(velocity);
3890 	player->set_is_grabbing_obstacle(is_grabbing_obstacle);
3891 
3892 	if (is_alive) {
3893 		player->reset_energy();
3894 		player->set_is_invisible(false);
3895 		player->set_is_frozen(false);
3896 		if (freeze_time) {
3897 			freeze(freeze_time);
3898 		}
3899 	} else {
3900 		player->set_energy(0);
3901 		player->set_is_invisible(true);
3902 		player->set_is_frozen(true);
3903 	}
3904 
3905 	update_energy_bar();
3906 
3907 	// TODO: Move all this code below into a helper function!
3908 
3909 	// Update the radar and name sprite
3910 	recreate_name(player);
3911 	m_radar->move_blip(player->get_id(), player->get_x(), player->get_y());
3912 	m_radar->set_blip_invisible(player->get_id(), player->is_invisible());
3913 	player->get_name_sprite()->set_invisible(player->is_invisible());
3914 
3915 	// If invisible or frozen, set these things appropriately and show/hide the sprite.
3916 	if (player->is_invisible()) {
3917 		player->set_velocity(0, 0);
3918 	} else {
3919 		// Reposition the name sprite to reflect the player's new position
3920 		m_text_manager->reposition_string(player->get_name_sprite(), player->get_x(), player->get_y() - (player->get_radius()+30), TextManager::CENTER);
3921 	}
3922 
3923 	if (m_radar->get_mode() == RADAR_ON) {
3924 		m_radar->set_blip_alpha(player->get_id(), player->is_frozen() ? 0.5 : 1.0);
3925 	}
3926 }
3927 
populate_graphic_group(GraphicGroup & group,const char * str)3928 void GameController::populate_graphic_group(GraphicGroup& group, const char* str) {
3929 	StringTokenizer	item_tokenizer(str, ';');
3930 	while (item_tokenizer) {
3931 		string	item_string;
3932 		item_tokenizer >> item_string;
3933 
3934 		Point	position;
3935 		double	rotation;
3936 		int	priority;
3937 		string	sprite_name;
3938 
3939 		StringTokenizer(item_string, ':') >> position >> rotation >> priority >> sprite_name;
3940 
3941 		Sprite*	sprite = m_graphics_cache.new_graphic<Sprite>(m_path_manager.data_path(sprite_name.c_str(), "sprites"));
3942 		sprite->set_x(position.x);
3943 		sprite->set_y(position.y);
3944 		sprite->set_rotation(rotation);
3945 		sprite->set_priority(priority);
3946 
3947 		group.add_graphic(sprite);
3948 	}
3949 }
3950 
add_front_arm(GraphicGroup & group,const char * sprite_name)3951 void GameController::add_front_arm(GraphicGroup& group, const char* sprite_name) {
3952 	Sprite*	sprite = m_graphics_cache.new_graphic<Sprite>(m_path_manager.data_path(sprite_name, "sprites"));
3953 	sprite->set_x(0);
3954 	sprite->set_y(0);
3955 	sprite->set_rotation(0);
3956 	sprite->set_priority(0);
3957 	group.add_graphic(sprite);
3958 }
3959 
register_front_arm_graphic(Player & base_player,const char * normal_str,const char * firing_str)3960 void GameController::register_front_arm_graphic(Player& base_player, const char* normal_str, const char* firing_str) {
3961 	if (GraphicalPlayer* player = get_player_by_id(base_player.get_id())) {
3962 		make_front_arm_graphic(*player->get_sprite(), player->get_team() == 'A' ? "blue_frontarm.png" : "red_frontarm.png", normal_str, firing_str);
3963 		player->set_is_frozen(player->is_frozen());
3964 	}
3965 }
3966 
make_front_arm_graphic(GraphicGroup & player_sprite,const char * arm_sprite,const char * normal_str,const char * firing_str)3967 void GameController::make_front_arm_graphic(GraphicGroup& player_sprite, const char* arm_sprite, const char* normal_str, const char* firing_str) {
3968 	GraphicGroup	normal_group;
3969 	GraphicGroup	firing_group;
3970 
3971 	if (normal_str) {
3972 		populate_graphic_group(normal_group, normal_str);
3973 	}
3974 	if (firing_str) {
3975 		populate_graphic_group(firing_group, firing_str);
3976 	}
3977 
3978 	if (arm_sprite) {
3979 		add_front_arm(normal_group, arm_sprite);
3980 		add_front_arm(firing_group, arm_sprite);
3981 	}
3982 
3983 	firing_group.set_invisible(true);
3984 
3985 	GraphicGroup	frontarm;
3986 	frontarm.add_graphic(&normal_group, "normal");
3987 	frontarm.add_graphic(&firing_group, "firing");
3988 	frontarm.set_x(13);
3989 	frontarm.set_y(-18);
3990 	frontarm.set_center_x(13);
3991 	frontarm.set_center_y(-18);
3992 	if (Graphic* oldarm = player_sprite.get_graphic("frontarm")) {
3993 		frontarm.set_rotation(oldarm->get_rotation());
3994 	}
3995 
3996 	player_sprite.remove_graphic("frontarm");
3997 	player_sprite.add_graphic(&frontarm, "frontarm");
3998 }
3999 
4000 
send_packet(const PacketWriter & packet)4001 void	GameController::send_packet(const PacketWriter& packet) {
4002 	m_network.send_packet(packet);
4003 }
4004 
send_reliable_packet(const PacketWriter & packet)4005 void	GameController::send_reliable_packet(const PacketWriter& packet) {
4006 	m_network.send_reliable_packet(packet);
4007 }
4008 
get_next_scan_id()4009 uint32_t	GameController::get_next_scan_id() {
4010 	static uint32_t		next_scan_id = 1L;
4011 	return next_scan_id++;
4012 }
4013 
excessive_packet_drop()4014 void		GameController::excessive_packet_drop() {
4015 	display_message("Too much packet loss to server.", RED_COLOR, RED_SHADOW);
4016 	disconnect();
4017 }
4018 
4019