1 //---------------------------------------------------------------------------
2 #include "stdafx.h"
3
4 #include <cassert>
5 #include <cerrno> // n.b., needed on Linux at least
6
7 #include <stdexcept> // needed for Android at least
8
9 #include <sstream>
10
11 #include "gamestate.h"
12 #include "game.h"
13 #include "utils.h"
14 #include "sector.h"
15 #include "gui.h"
16 #include "player.h"
17 #include "tutorial.h"
18
19 #include "screen.h"
20 #include "image.h"
21 #include "sound.h"
22
23 //---------------------------------------------------------------------------
24
25 const int defenders_ticks_per_update_c = (int)(60.0 * ticks_per_frame_c * time_ratio_c); // consider a turn every this number of ticks
26 const int soldier_move_rate_c = (int)(1.8 * ticks_per_frame_c * time_ratio_c); // ticks per pixel - needs to be in sync with the animation!
27 const int cannon_move_rate_c = (int)(0.6 * ticks_per_frame_c * time_ratio_c); // ticks per pixel - needs to be in sync with the animation!
28 const int air_move_rate_c = (int)(0.2 * ticks_per_frame_c * time_ratio_c); // ticks per pixel
29 const int soldier_turn_rate_c = (int)(50.0 * ticks_per_frame_c * time_ratio_c); // mean ticks per turn
30
31 const int shield_step_y_c = 20;
32
33 class Soldier {
34 static int sort_soldier_pair(const void *v1,const void *v2);
35 public:
36 int player;
37 int epoch;
38 int xpos, ypos;
39 AmmoDirection dir;
Soldier(int player,int epoch,int xpos,int ypos)40 Soldier(int player,int epoch,int xpos,int ypos) {
41 ASSERT_S_EPOCH(epoch);
42 this->player = player;
43 this->epoch = epoch;
44 this->xpos = xpos;
45 this->ypos = ypos;
46 this->dir = (AmmoDirection)(rand() % 4);
47 }
sortSoldiers(Soldier ** soldiers,int n_soldiers)48 static void sortSoldiers(Soldier **soldiers,int n_soldiers) {
49 qsort(soldiers, n_soldiers, sizeof( Soldier *), sort_soldier_pair);
50 }
51 };
52
sort_soldier_pair(const void * v1,const void * v2)53 int Soldier::sort_soldier_pair(const void *v1,const void *v2) {
54 Soldier *s1 = *(Soldier **)v1;
55 Soldier *s2 = *(Soldier **)v2;
56 /*if( s1->epoch >= 6 )
57 return 1;
58 else if( s2->epoch >= 6 )
59 return -1;
60 else*/
61 return (s1->ypos - s2->ypos);
62 }
63
draw() const64 void Feature::draw() const {
65 const int ticks_per_frame_c = 110; // tree animation looks better if offset from main animation, and if slightly slower
66 int counter = ( game_g->getRealTime() * game_g->getTimeRate() ) / ticks_per_frame_c;
67 image[counter % n_frames]->draw(xpos, ypos);
68 }
69
TimedEffect()70 TimedEffect::TimedEffect() {
71 this->timeset = game_g->getRealTime();
72 this->func_finish = NULL;
73 }
74
TimedEffect(int delay,void (* func_finish)())75 TimedEffect::TimedEffect(int delay, void (*func_finish)()) {
76 this->timeset = game_g->getRealTime() + delay;
77 this->func_finish = func_finish;
78 }
79
80 const int ammo_time_c = 1000;
81 const float ammo_speed_c = 1.5f; // higher is faster
82
AmmoEffect(PlayingGameState * gamestate,int epoch,AmmoDirection dir,int xpos,int ypos)83 AmmoEffect::AmmoEffect(PlayingGameState *gamestate,int epoch, AmmoDirection dir, int xpos, int ypos) : TimedEffect(), gamestate(gamestate) {
84 ASSERT_EPOCH(epoch);
85 this->gametimeset = game_g->getGameTime();
86 this->epoch = epoch;
87 this->dir = dir;
88 this->xpos = xpos;
89 this->ypos = ypos;
90 }
91
render() const92 bool AmmoEffect::render() const {
93 int time = game_g->getRealTime() - this->timeset;
94 if( time < 0 )
95 return false;
96 int gametime = game_g->getGameTime() - this->gametimeset;
97 int x = xpos;
98 int y = ypos;
99 int dist = (int)(gametime * ammo_speed_c);
100 if( dir == ATTACKER_AMMO_BOMB )
101 dist /= 2;
102 if( dir == ATTACKER_AMMO_DOWN )
103 y += dist;
104 else if( dir == ATTACKER_AMMO_UP )
105 y -= dist;
106 else if( dir == ATTACKER_AMMO_LEFT )
107 x -= dist;
108 else if( dir == ATTACKER_AMMO_RIGHT )
109 x += dist;
110 else if( dir == ATTACKER_AMMO_BOMB )
111 y += dist;
112 else {
113 ASSERT(0);
114 }
115 Image *image = game_g->attackers_ammo[epoch][dir];
116 if( dir == ATTACKER_AMMO_BOMB && dist > 24 ) {
117 if( game_g->explosions[0] != NULL ) {
118 int w = image->getScaledWidth();
119 int w2 = game_g->explosions[0]->getScaledWidth();
120 gamestate->explosionEffect(offset_land_x_c + x + (w-w2)/2, offset_land_y_c + y);
121 }
122 return true;
123 }
124 if( x < 0 || y < 0 )
125 return true;
126 x += offset_land_x_c;
127 y += offset_land_y_c;
128 if( x + image->getScaledWidth() >= game_g->getScreen()->getWidth() || y + image->getScaledHeight() >= game_g->getScreen()->getHeight() )
129 return true;
130 image->draw(x, y);
131 if( time > ammo_time_c )
132 return true;
133 return false;
134 }
135
136 const int fade_time_c = 1000;
137 const int whitefade_time_c = 1000;
138
FadeEffect(bool white,bool out,int delay,void (* func_finish)())139 FadeEffect::FadeEffect(bool white,bool out,int delay, void (*func_finish)()) : TimedEffect(delay, func_finish) {
140 this->white = white;
141 this->out = out;
142 #if SDL_MAJOR_VERSION == 1
143 this->image = Image::createBlankImage(game_g->getScreen()->getWidth(), game_g->getScreen()->getHeight(), 24);
144 int r = 0, g = 0, b = 0;
145 if( white ) {
146 r = g = b = 255;
147 }
148 else {
149 r = g = b = 0;
150 }
151 image->fillRect(0, 0, game_g->getScreen()->getWidth(), game_g->getScreen()->getHeight(), r, g, b);
152 this->image->convertToDisplayFormat();
153 #else
154 image = NULL;
155 #endif
156 }
157
~FadeEffect()158 FadeEffect::~FadeEffect() {
159 #if SDL_MAJOR_VERSION == 1
160 delete image;
161 #endif
162 }
163
render() const164 bool FadeEffect::render() const {
165 int time = game_g->getRealTime() - this->timeset;
166 int length = white ? whitefade_time_c : fade_time_c;
167 if( time < 0 )
168 return false;
169 double alpha = 0.0;
170 if( white ) {
171 alpha = ((double)time) / (0.5 * (double)length);
172 if( alpha > 2.0 )
173 alpha = 2.0;
174 if( time > length/2.0 ) {
175 alpha = 2.0 - alpha;
176 }
177 }
178 else {
179 alpha = ((double)time) / (double)length;
180 if( alpha > 1.0 )
181 alpha = 1.0;
182 if( !out )
183 alpha = 1.0 - alpha;
184 }
185 #if SDL_MAJOR_VERSION == 1
186 image->drawWithAlpha(0, 0, (unsigned char)(alpha * 255));
187 #else
188 unsigned char value = white ? 255 : 0;
189 game_g->getScreen()->fillRectWithAlpha(0, 0, game_g->getScreen()->getWidth(), game_g->getScreen()->getHeight(), value, value, value, (unsigned char)(alpha * 255));
190 #endif
191 if( time > length ) // we still need to draw the fade, on the last time
192 return true;
193 return false;
194 }
195
196 const int flashingsquare_flash_time_c = 250;
197 const int flashing_square_n_flashes_c = 8;
198
render() const199 bool FlashingSquare::render() const {
200 int time = game_g->getRealTime() - this->timeset;
201 if( time < 0 )
202 return false;
203 if( time > flashingsquare_flash_time_c * flashing_square_n_flashes_c ) {
204 return true;
205 }
206
207 bool flash = ( time / flashingsquare_flash_time_c ) % 2 == 0;
208 if( flash ) {
209 int map_x = offset_map_x_c + 16 * this->xpos;
210 int map_y = offset_map_y_c + 16 * this->ypos;
211 game_g->flashingmapsquare->draw(map_x, map_y);
212 }
213 return false;
214 }
215
render() const216 bool AnimationEffect::render() const {
217 int time = game_g->getRealTime() - this->timeset;
218 if( time < 0 )
219 return false;
220 int frame = time / time_per_frame;
221 if( frame >= n_images )
222 return true;
223 //this->finished = true;
224 else {
225 if( !dir )
226 frame = n_images - 1 - frame;
227 images[frame]->draw(xpos, ypos);
228 }
229 return false;
230 }
231
render() const232 bool TextEffect::render() const {
233 int time = game_g->getRealTime() - this->timeset;
234 if( time < 0 )
235 return false;
236 else if( time > duration )
237 return true;
238 Image::write(xpos, ypos, game_g->letters_small, text.c_str(), Image::JUSTIFY_CENTRE);
239 return false;
240 }
241
GameState(int client_player)242 GameState::GameState(int client_player) : client_player(client_player) {
243 this->fade = NULL;
244 this->whitefade = NULL;
245 this->screen_page = new PanelPage(0, 0);
246 this->screen_page->setTolerance(0);
247 this->mobile_ui_display_mouse = false;
248 this->mouse_image = NULL;
249 this->mouse_off_x = 0;
250 this->mouse_off_y = 0;
251 this->confirm_type = CONFIRMTYPE_UNKNOWN;
252 this->confirm_window = NULL;
253 this->confirm_button_1 = NULL;
254 this->confirm_button_2 = NULL;
255 this->confirm_button_3 = NULL;
256 }
257
~GameState()258 GameState::~GameState() {
259 LOG("~GameState()\n");
260 if( fade != NULL )
261 delete fade;
262 if( whitefade != NULL )
263 delete whitefade;
264
265 if( this->screen_page )
266 delete screen_page;
267
268 LOG("~GameState() done\n");
269 }
270
reset()271 void GameState::reset() {
272 //LOG("GameState::reset()\n");
273 this->screen_page->free(true);
274 }
275
setDefaultMouseImage()276 void GameState::setDefaultMouseImage() {
277 if( game_g->isDemo() )
278 mouse_image = game_g->mouse_pointers[0];
279 else
280 mouse_image = game_g->mouse_pointers[client_player];
281 mobile_ui_display_mouse = false;
282 }
283
draw()284 void GameState::draw() {
285 if( mouse_image != NULL ) {
286 bool touch_mode = game_g->isMobileUI() || game_g->getApplication()->isBlankMouse();
287 if( touch_mode && mobile_ui_display_mouse ) {
288 mouse_image->draw(default_width_c - mouse_image->getScaledWidth(), 0);
289 }
290 else if( !touch_mode ) {
291 int m_x = 0, m_y = 0;
292 game_g->getScreen()->getMouseCoords(&m_x, &m_y);
293 m_x = (int)(m_x / game_g->getScaleWidth());
294 m_y = (int)(m_y / game_g->getScaleHeight());
295 m_x += mouse_off_x;
296 m_y += mouse_off_y;
297 mouse_image->draw(m_x, m_y);
298 }
299 }
300
301 if( fade ) {
302 if( fade->render() ) {
303 FadeEffect *copy = fade;
304 delete fade;
305 if( copy == fade )
306 fade = NULL;
307 }
308 }
309 if( whitefade ) {
310 if( whitefade->render() ) {
311 FadeEffect *copy = whitefade;
312 delete whitefade;
313 if( copy == whitefade )
314 whitefade = NULL;
315 }
316 }
317
318 if( game_g->isPaused() ) {
319 string str = game_g->isMobileUI() ? "touch screen\nto unpause game" : "press p or click\nmouse to unpause game";
320 // n.b., don't use 120 for y pos, need to avoid collision with quit game message
321 // and offset x pos slightly, to avoid overlapping with GUI
322 Image::write(120, 100, game_g->letters_large, str.c_str(), Image::JUSTIFY_LEFT);
323 }
324
325 if( game_g->getApplication()->hasFPS() ) {
326 float fps = game_g->getApplication()->getFPS();
327 if( fps > 0.0f ) {
328 stringstream str;
329 str << fps;
330 Image::writeMixedCase(4, default_height_c - 16, game_g->letters_large, game_g->letters_small, game_g->numbers_white, str.str().c_str(), Image::JUSTIFY_LEFT);
331 }
332 }
333
334 game_g->getScreen()->refresh();
335 }
336
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)337 void GameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
338 this->screen_page->input(m_x, m_y, m_left, m_middle, m_right, click);
339 }
340
requestQuit(bool force_quit)341 void GameState::requestQuit(bool force_quit) {
342 game_g->getApplication()->setQuit();
343 }
344
createQuitWindow()345 void GameState::createQuitWindow() {
346 if( confirm_window == NULL && !game_g->isStateChanged() ) {
347 confirm_type = CONFIRMTYPE_QUITGAME;
348 confirm_window = new PanelPage(0, 0, default_width_c, default_height_c);
349 confirm_window->setBackground(0, 0, 0, 200);
350 const int offset_x_c = 120, offset_y_c = 120;
351 Button *text_button = new Button(offset_x_c, offset_y_c, "REALLY QUIT?", game_g->letters_large);
352 confirm_window->add(text_button);
353 confirm_button_1 = new Button(offset_x_c, offset_y_c+16, "YES", game_g->letters_large);
354 confirm_window->add(confirm_button_1);
355 confirm_button_2 = new Button(offset_x_c+32, offset_y_c+16, "NO", game_g->letters_large);
356 confirm_window->add(confirm_button_2);
357 screen_page->add(confirm_window);
358 }
359 else if( confirm_window != NULL && !game_g->isStateChanged() ) {
360 closeConfirmWindow();
361 }
362 }
363
closeConfirmWindow()364 void GameState::closeConfirmWindow() {
365 //LOG("GameState::closeConfirmWindow()\n");
366 if( confirm_window != NULL ) {
367 delete confirm_window;
368 confirm_window = NULL;
369 confirm_button_1 = NULL;
370 confirm_button_2 = NULL;
371 confirm_button_3 = NULL;
372 }
373 //LOG("GameState::closeConfirmWindow() done\n");
374 }
375
ChooseGameTypeGameState(int client_player)376 ChooseGameTypeGameState::ChooseGameTypeGameState(int client_player) : GameState(client_player) {
377 this->choosegametypePanel = NULL;
378 T_ASSERT(game_g->getTutorial() == NULL);
379 }
380
~ChooseGameTypeGameState()381 ChooseGameTypeGameState::~ChooseGameTypeGameState() {
382 LOG("~ChooseGameTypeGameState()\n");
383 if( this->choosegametypePanel )
384 delete choosegametypePanel;
385 LOG("~ChooseGameTypeGameState() done\n");
386 }
387
getChooseGameTypePanel()388 ChooseGameTypePanel *ChooseGameTypeGameState::getChooseGameTypePanel() {
389 return this->choosegametypePanel;
390 }
391
reset()392 void ChooseGameTypeGameState::reset() {
393 //LOG("ChooseGameTypeGameState::reset()\n");
394 this->screen_page->free(true);
395
396 if( this->choosegametypePanel != NULL ) {
397 delete this->choosegametypePanel;
398 this->choosegametypePanel = NULL;
399 }
400 this->choosegametypePanel = new ChooseGameTypePanel();
401 }
402
draw()403 void ChooseGameTypeGameState::draw() {
404 #if defined(__ANDROID__)
405 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
406 #endif
407 game_g->background->draw(0, 0);
408
409 this->choosegametypePanel->draw();
410
411 this->screen_page->draw();
412 //this->screen_page->drawPopups();
413
414 GameState::setDefaultMouseImage();
415 GameState::draw();
416 }
417
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)418 void ChooseGameTypeGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
419 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
420
421 this->choosegametypePanel->input(m_x, m_y, m_left, m_middle, m_right, click);
422 }
423
ChooseDifficultyGameState(int client_player)424 ChooseDifficultyGameState::ChooseDifficultyGameState(int client_player) : GameState(client_player) {
425 this->choosedifficultyPanel = NULL;
426 }
427
~ChooseDifficultyGameState()428 ChooseDifficultyGameState::~ChooseDifficultyGameState() {
429 LOG("~ChooseDifficultyGameState()\n");
430 if( this->choosedifficultyPanel )
431 delete choosedifficultyPanel;
432 LOG("~ChooseDifficultyGameState() done\n");
433 }
434
getChooseDifficultyPanel()435 ChooseDifficultyPanel *ChooseDifficultyGameState::getChooseDifficultyPanel() {
436 return this->choosedifficultyPanel;
437 }
438
reset()439 void ChooseDifficultyGameState::reset() {
440 //LOG("ChooseDifficultyGameState::reset()\n");
441 this->screen_page->free(true);
442
443 if( this->choosedifficultyPanel != NULL ) {
444 delete this->choosedifficultyPanel;
445 this->choosedifficultyPanel = NULL;
446 }
447 this->choosedifficultyPanel = new ChooseDifficultyPanel();
448 }
449
draw()450 void ChooseDifficultyGameState::draw() {
451 #if defined(__ANDROID__)
452 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
453 #endif
454 game_g->background->draw(0, 0);
455
456 this->choosedifficultyPanel->draw();
457
458 this->screen_page->draw();
459
460 GameState::setDefaultMouseImage();
461 GameState::draw();
462 }
463
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)464 void ChooseDifficultyGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
465 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
466
467 this->choosedifficultyPanel->input(m_x, m_y, m_left, m_middle, m_right, click);
468 }
469
ChoosePlayerGameState(int client_player)470 ChoosePlayerGameState::ChoosePlayerGameState(int client_player) : GameState(client_player), button_red(NULL), button_yellow(NULL), button_green(NULL), button_blue(NULL) {
471 }
472
~ChoosePlayerGameState()473 ChoosePlayerGameState::~ChoosePlayerGameState() {
474 LOG("~ChoosePlayerGameState()\n");
475 LOG("~ChoosePlayerGameState() done\n");
476 }
477
reset()478 void ChoosePlayerGameState::reset() {
479 this->screen_page->free(true);
480
481 const int xpos = 64;
482 int ypos = 48;
483 const int ydiff = 48;
484 const int xindent = 8;
485 const int ylargediff = game_g->letters_large[0]->getScaledHeight() + 2;
486 const int ysmalldiff = game_g->letters_small[0]->getScaledHeight() + 2;
487 const int draw_offset_x = 32;
488
489 button_red = new Button(xpos-draw_offset_x, ypos, draw_offset_x, ylargediff + 2*ysmalldiff, "CONTROLLER OF THE RED PEOPLE", game_g->letters_large);
490 screen_page->add(new Button(xpos+xindent, ypos+ylargediff, "SPECIAL SKILL STRENGTH", game_g->letters_small));
491 screen_page->add(new Button(xpos+xindent, ypos+ylargediff+ysmalldiff, "UNARMED MEN ARE STRONGER IN COMBAT", game_g->letters_small));
492 ypos += ydiff;
493 screen_page->add(button_red);
494
495 button_green = new Button(xpos-draw_offset_x, ypos, draw_offset_x, ylargediff + 2*ysmalldiff, "CONTROLLER OF THE GREEN PEOPLE", game_g->letters_large);
496 screen_page->add(new Button(xpos+xindent, ypos+ylargediff, "SPECIAL SKILL CONSTRUCTION", game_g->letters_small));
497 screen_page->add(new Button(xpos+xindent, ypos+ylargediff+ysmalldiff, "FASTER AT BUILDING NEW TOWERS", game_g->letters_small));
498 ypos += ydiff;
499 screen_page->add(button_green);
500
501 button_yellow = new Button(xpos-draw_offset_x, ypos, draw_offset_x, ylargediff + 2*ysmalldiff, "CONTROLLER OF THE YELLOW PEOPLE", game_g->letters_large);
502 screen_page->add(new Button(xpos+xindent, ypos+ylargediff, "SPECIAL SKILL DIPLOMACY", game_g->letters_small));
503 screen_page->add(new Button(xpos+xindent, ypos+ylargediff+ysmalldiff, "EASIER TO FORM ALLIANCES", game_g->letters_small));
504 ypos += ydiff;
505 screen_page->add(button_yellow);
506
507 button_blue = new Button(xpos-draw_offset_x, ypos, draw_offset_x, ylargediff + 2*ysmalldiff, "CONTROLLER OF THE BLUE PEOPLE", game_g->letters_large);
508 screen_page->add(new Button(xpos+xindent, ypos+ylargediff, "SPECIAL SKILL DEFENCE", game_g->letters_small));
509 screen_page->add(new Button(xpos+xindent, ypos+ylargediff+ysmalldiff, "BUILDINGS STRONGER AGAINST ATTACK", game_g->letters_small));
510 ypos += ydiff;
511 screen_page->add(button_blue);
512 }
513
draw()514 void ChoosePlayerGameState::draw() {
515 #if defined(__ANDROID__)
516 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
517 #endif
518 //player_select->draw(0, 0, false);
519 game_g->background->draw(0, 0);
520 Image::writeMixedCase(160, 16, game_g->letters_large, game_g->letters_small, NULL, "Select a Player", Image::JUSTIFY_CENTRE);
521
522 const int y_offset = 2; // must be even, otherwise we have graphical problems when running at 1280x1024 mode
523 if( game_g->player_heads_select[0] != NULL )
524 game_g->player_heads_select[0]->draw(button_red->getLeft(), button_red->getTop()+y_offset);
525 if( game_g->player_heads_select[1] != NULL )
526 game_g->player_heads_select[1]->draw(button_green->getLeft(), button_green->getTop()+y_offset);
527 if( game_g->player_heads_select[2] != NULL )
528 game_g->player_heads_select[2]->draw(button_yellow->getLeft(), button_yellow->getTop()+y_offset);
529 if( game_g->player_heads_select[3] != NULL )
530 game_g->player_heads_select[3]->draw(button_blue->getLeft(), button_blue->getTop()+y_offset);
531
532 this->screen_page->draw();
533
534 GameState::setDefaultMouseImage();
535 GameState::draw();
536 }
537
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)538 void ChoosePlayerGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
539 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
540
541 int player = -1;
542 if( m_left && click && button_red->mouseOver(m_x, m_y) ) {
543 player = 0;
544 }
545 else if( m_left && click && button_yellow->mouseOver(m_x, m_y) ) {
546 player = 2;
547 }
548 else if( m_left && click && button_green->mouseOver(m_x, m_y) ) {
549 player = 1;
550 }
551 else if( m_left && click && button_blue->mouseOver(m_x, m_y) ) {
552 player = 3;
553 }
554
555 if( player != -1 ) {
556 game_g->setClientPlayer(player);
557 if( game_g->getGameType() == GAMETYPE_TUTORIAL ) {
558 game_g->setGameStateID(GAMESTATEID_CHOOSETUTORIAL);
559 }
560 else {
561 game_g->setGameStateID(GAMESTATEID_PLACEMEN);
562 game_g->newGame();
563 }
564 }
565 }
566
ChooseTutorialGameState(int client_player)567 ChooseTutorialGameState::ChooseTutorialGameState(int client_player) : GameState(client_player) {
568 }
569
reset()570 void ChooseTutorialGameState::reset() {
571 this->screen_page->free(true);
572
573 const int xpos = 96;
574 int ypos = 48;
575 const int ydiff = 16;
576
577 vector<TutorialInfo> infos = TutorialManager::getTutorialInfo();
578 for(vector<TutorialInfo>::const_iterator iter = infos.begin(); iter != infos.end(); ++iter) {
579 TutorialInfo info = *iter;
580 Button *button = new Button(xpos, ypos, info.text.c_str(), game_g->letters_large);
581 button->setId(info.id);
582 ypos += ydiff;
583 screen_page->add(button);
584 buttons.push_back(button);
585 }
586 }
587
draw()588 void ChooseTutorialGameState::draw() {
589 #if defined(__ANDROID__)
590 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
591 #endif
592 game_g->background->draw(0, 0);
593 Image::writeMixedCase(160, 16, game_g->letters_large, game_g->letters_small, NULL, "Select a Tutorial", Image::JUSTIFY_CENTRE);
594
595 this->screen_page->draw();
596
597 GameState::setDefaultMouseImage();
598 GameState::draw();
599 }
600
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)601 void ChooseTutorialGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
602 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
603
604 for(vector<Button *>::const_iterator iter = buttons.begin(); iter != buttons.end(); ++iter) {
605 const Button *button = *iter;
606 if( m_left && click && button->mouseOver(m_x, m_y) ) {
607 game_g->setupTutorial(button->getId());
608 game_g->setCurrentIsand(game_g->getTutorial()->getStartEpoch(), game_g->getTutorial()->getIsland());
609 game_g->setupPlayers();
610 game_g->setGameStateID(GAMESTATEID_PLAYING);
611 break;
612 }
613 }
614 }
615
PlaceMenGameState(int client_player)616 PlaceMenGameState::PlaceMenGameState(int client_player) : GameState(client_player), start_map_x(-1), start_map_y(-1) {
617 this->off_x = 220;
618 this->off_y = 32;
619 this->choosemenPanel = NULL;
620 for(int y=0;y<map_height_c;y++) {
621 for(int x=0;x<map_width_c;x++) {
622 map_panels[x][y] = NULL;
623 }
624 }
625 }
626
~PlaceMenGameState()627 PlaceMenGameState::~PlaceMenGameState() {
628 LOG("~PlaceMenGameState()\n");
629 if( this->choosemenPanel )
630 delete choosemenPanel;
631 LOG("~PlaceMenGameState() done\n");
632 }
633
getChooseMenPanel()634 ChooseMenPanel *PlaceMenGameState::getChooseMenPanel() {
635 return this->choosemenPanel;
636 }
637
getMapPanel(int x,int y) const638 const PanelPage *PlaceMenGameState::getMapPanel(int x, int y) const {
639 ASSERT( x >= 0 && x < map_width_c );
640 ASSERT( y >= 0 && y < map_height_c );
641 return this->map_panels[x][y];
642 }
643
getMapPanel(int x,int y)644 PanelPage *PlaceMenGameState::getMapPanel(int x, int y) {
645 ASSERT( x >= 0 && x < map_width_c );
646 ASSERT( y >= 0 && y < map_height_c );
647 return this->map_panels[x][y];
648 }
649
reset()650 void PlaceMenGameState::reset() {
651 //LOG("PlaceMenGameState::reset()\n");
652 /*if( !_CrtCheckMemory() ) {
653 throw "_CrtCheckMemory FAILED";
654 }*/
655 this->screen_page->free(true);
656
657 if( this->choosemenPanel != NULL ) {
658 delete this->choosemenPanel;
659 this->choosemenPanel = NULL;
660 }
661
662 this->choosemenPanel = new ChooseMenPanel(this);
663
664 // setup screen_page buttons
665 for(int y=0;y<map_height_c;y++) {
666 for(int x=0;x<map_width_c;x++) {
667 map_panels[x][y] = NULL;
668 if( game_g->getMap()->isSectorAt(x, y) ) {
669 //int map_x = offset_map_x_c + 16 * x;
670 int map_x = this->off_x - 8 * map_width_c + 16 * x;
671 int map_y = this->off_y + 16 * y;
672 PanelPage *panel = new PanelPage(map_x, map_y, 16, 16);
673 panel->setInfoLMB("place starting tower\nin this sector");
674 panel->setVisible(false);
675 screen_page->add(panel);
676 map_panels[x][y] = panel;
677 }
678 }
679 }
680 /*if( !_CrtCheckMemory() ) {
681 throw "_CrtCheckMemory FAILED";
682 }*/
683 }
684
draw()685 void PlaceMenGameState::draw() {
686 char buffer[256] = "";
687
688 #if defined(__ANDROID__)
689 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
690 #endif
691 game_g->background_islands->draw(0, 0);
692
693 if( !game_g->isUsingOldGfx() ) {
694 sprintf(buffer, "Gigalomania v%d.%d", majorVersion, minorVersion);
695 Image::writeMixedCase(160, 228, game_g->letters_large, game_g->letters_small, game_g->numbers_white, buffer, Image::JUSTIFY_CENTRE);
696 }
697
698 /*this->choosemenPanel->draw();
699 this->screen_page->draw();
700 GameState::draw();
701 return;*/
702
703 int l_h = game_g->letters_large[0]->getScaledHeight();
704 int s_h = game_g->letters_small[0]->getScaledHeight();
705 const int cx = this->off_x;
706 int cy = this->off_y + 104;
707 Image::writeMixedCase(cx, cy, game_g->letters_large, game_g->letters_small, NULL, game_g->getMap()->getName(), Image::JUSTIFY_CENTRE);
708 cy += s_h + 2;
709 Image::writeMixedCase(cx, cy, game_g->letters_large, game_g->letters_small, NULL, "of the", Image::JUSTIFY_CENTRE);
710 cy += l_h + 2;
711 sprintf(buffer, "%s AGE", epoch_names[game_g->getStartEpoch()]);
712 Image::writeMixedCase(cx, cy, game_g->letters_large, game_g->letters_small, NULL, buffer, Image::JUSTIFY_CENTRE);
713 cy += l_h + 2;
714
715 int year = epoch_dates[game_g->getStartEpoch()];
716 bool shiny = game_g->getStartEpoch() == n_epochs_c-1;
717 Image::writeNumbers(cx+8, cy, shiny ? game_g->numbers_largeshiny : game_g->numbers_largegrey, abs(year),Image::JUSTIFY_RIGHT);
718 Image *era = ( year < 0 ) ? game_g->icon_bc :
719 shiny ? game_g->icon_ad_shiny : game_g->icon_ad;
720 if( era != NULL )
721 era->draw(cx+8, cy);
722 else {
723 Image::write(cx+8, cy, game_g->letters_small, ( year < 0 ) ? "BC" : "AD", Image::JUSTIFY_LEFT);
724 }
725 cy += l_h + 2;
726
727 if( !game_g->isDemo() && game_g->getGameType() == GAMETYPE_ALLISLANDS ) {
728 int n_suspended = game_g->getNSuspended();
729 if( n_suspended > 0 )
730 {
731 sprintf(buffer, "Saved Men %d", n_suspended);
732 Image::writeMixedCase(cx, cy, game_g->letters_large, game_g->letters_small, game_g->numbers_white, buffer, Image::JUSTIFY_CENTRE);
733 }
734 }
735
736 /*cy += l_h + 2;
737 cy += l_h + 2;
738 if( choosemenPanel->getPage() == ChooseMenPanel::STATE_LOADGAME ) {
739 Image::write(cx, cy, game_g->letters_large, "LOAD", Image::JUSTIFY_CENTRE, true);
740 }
741 else if( choosemenPanel->getPage() == ChooseMenPanel::STATE_SAVEGAME ) {
742 Image::write(cx, cy, game_g->letters_large, "SAVE", Image::JUSTIFY_CENTRE, true);
743 }
744 cy += s_h + 2;*/
745
746 if( choosemenPanel->getPage() == ChooseMenPanel::STATE_CHOOSEMEN ) {
747 cy = 100;
748 const int xpos = 80;
749 Image::writeMixedCase(xpos, cy, game_g->letters_large, game_g->letters_small, NULL, "Click on the icon below", Image::JUSTIFY_CENTRE);
750 cy += l_h + 2;
751 Image::writeMixedCase(xpos, cy, game_g->letters_large, game_g->letters_small, NULL, "to choose how many men", Image::JUSTIFY_CENTRE);
752 cy += l_h + 2;
753 Image::writeMixedCase(xpos, cy, game_g->letters_large, game_g->letters_small, NULL, "to play with", Image::JUSTIFY_CENTRE);
754 cy += l_h + 2;
755 Image::writeMixedCase(xpos, cy, game_g->letters_large, game_g->letters_small, NULL, "then click on the map", Image::JUSTIFY_CENTRE);
756 cy += l_h + 2;
757 Image::writeMixedCase(xpos, cy, game_g->letters_large, game_g->letters_small, NULL, "to the right", Image::JUSTIFY_CENTRE);
758 cy += l_h + 2;
759 }
760
761 game_g->getMap()->draw(cx - 8*map_width_c, off_y);
762
763 this->choosemenPanel->draw();
764 //this->choosemenPanel->drawPopups();
765
766 this->screen_page->draw();
767 //this->screen_page->drawPopups();
768
769 GameState::setDefaultMouseImage();
770 GameState::draw();
771 }
772
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)773 void PlaceMenGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
774 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
775
776 bool done = false;
777 if( !done && m_left && click && confirm_button_1 != NULL && confirm_button_1->mouseOver(m_x, m_y) ) {
778 LOG("confirm yes clicked\n");
779 done = true;
780 registerClick();
781 ASSERT( confirm_window != NULL );
782 this->requestConfirm();
783 }
784 else if( !done && m_left && click && confirm_button_2 != NULL && confirm_button_2->mouseOver(m_x, m_y) ) {
785 LOG("confirm no clicked\n");
786 done = true;
787 registerClick();
788 ASSERT( confirm_window != NULL );
789 this->closeConfirmWindow();
790 }
791
792 if( !done && m_left && click && this->choosemenPanel->getPage() == ChooseMenPanel::STATE_CHOOSEMEN && this->choosemenPanel->getNMen() > 0 ) {
793 bool found = false;
794 int map_x = -1;
795 int map_y = -1;
796 for(int y=0;y<map_height_c && !found;y++) {
797 for(int x=0;x<map_width_c && !found;x++) {
798 if( game_g->getMap()->isSectorAt(x, y) ) {
799 ASSERT( this->map_panels[x][y] != NULL );
800 if( this->map_panels[x][y]->mouseOver(m_x, m_y) ) {
801 found = true;
802 map_x = x;
803 map_y = y;
804 }
805 }
806 }
807 }
808 if( found ) {
809 LOG("starting epoch %d island %s at %d, %d\n", game_g->getStartEpoch(), game_g->getMap()->getName(), map_x, map_y);
810 this->setStartMapPos(map_x, map_y);
811 return;
812 }
813 }
814
815 if( !done )
816 this->choosemenPanel->input(m_x, m_y, m_left, m_middle, m_right, click);
817 }
818
requestQuit(bool force_quit)819 void PlaceMenGameState::requestQuit(bool force_quit) {
820 if( force_quit ) {
821 game_g->saveState();
822 game_g->getApplication()->setQuit();
823 }
824 else if( choosemenPanel->getPage() == ChooseMenPanel::STATE_CHOOSEMEN && !game_g->isStateChanged() ) {
825 choosemenPanel->setPage(ChooseMenPanel::STATE_CHOOSEISLAND);
826 }
827 else {
828 this->createQuitWindow();
829 }
830 }
831
requestConfirm()832 void PlaceMenGameState::requestConfirm() {
833 if( confirm_window != NULL ) {
834 this->closeConfirmWindow();
835 if( confirm_type == CONFIRMTYPE_NEWGAME ) {
836 game_g->setGameStateID(GAMESTATEID_CHOOSEGAMETYPE);
837 }
838 else if( confirm_type == CONFIRMTYPE_QUITGAME ) {
839 game_g->getApplication()->setQuit();
840 }
841 else {
842 T_ASSERT(false);
843 }
844 }
845 }
846
setStartMapPos(int start_map_x,int start_map_y)847 void PlaceMenGameState::setStartMapPos(int start_map_x, int start_map_y ) {
848 this->start_map_x = start_map_x;
849 this->start_map_y = start_map_y;
850 if( !game_g->isDemo() ) {
851 game_g->players[client_player]->setNMenForThisIsland( this->choosemenPanel->getNMen() );
852 ASSERT( game_g->players[client_player]->getNMenForThisIsland() <= game_g->getMenAvailable() );
853 LOG("human is player %d, starting with %d men\n", client_player, game_g->players[client_player]->getNMenForThisIsland());
854 }
855 else {
856 LOG("DEMO mode\n");
857 //placeTower(map_x, map_y, 0);
858 }
859 game_g->placeTower();
860 }
861
requestNewGame()862 void PlaceMenGameState::requestNewGame() {
863 if( confirm_window != NULL ) {
864 this->closeConfirmWindow();
865 }
866 confirm_window = new PanelPage(0, 0, default_width_c, default_height_c);
867 confirm_type = CONFIRMTYPE_NEWGAME;
868 confirm_window->setBackground(0, 0, 0, 200);
869 const int offset_x_c = 120, offset_y_c = 120;
870 Button *text_button = new Button(offset_x_c, offset_y_c, "NEW GAME?", game_g->letters_large);
871 confirm_window->add(text_button);
872 confirm_button_1 = new Button(offset_x_c, offset_y_c+16, "YES", game_g->letters_large);
873 confirm_window->add(confirm_button_1);
874 confirm_button_2 = new Button(offset_x_c+32, offset_y_c+16, "NO", game_g->letters_large);
875 confirm_window->add(confirm_button_2);
876 this->screen_page->add(confirm_window);
877 }
878
879
PlayingGameState(int client_player)880 PlayingGameState::PlayingGameState(int client_player) : GameState(client_player) {
881 this->current_sector = NULL;
882 this->flag_frame_step = 0;
883 this->defenders_last_time_update = 0;
884 this->soldier_last_time_moved_x = -1;
885 this->soldier_last_time_moved_y = -1;
886 this->cannon_last_time_moved_x = -1;
887 this->cannon_last_time_moved_y = -1;
888 this->air_last_time_moved = -1;
889 this->soldiers_last_time_turned = 0;
890
891 this->text_effect = NULL;
892 this->speed_button = NULL;
893 for(int i=0;i<n_players_c;i++) {
894 this->shield_buttons[i] = NULL;
895 this->shield_number_panels[i] = NULL;
896 }
897 this->shield_blank_button = NULL;
898 this->land_panel = NULL;
899 this->pause_button = NULL;
900 this->quit_button = NULL;
901 this->tutorial_next_button = NULL;
902 this->gamePanel = NULL;
903 this->selected_army = NULL;
904 this->map_display = MAPDISPLAY_MAP;
905 //this->map_display = MAPDISPLAY_UNITS;
906 this->player_asking_alliance = -1;
907 /*for(i=0;i<n_players_c;i++)
908 this->n_soldiers[i] = 0;*/
909 for(int i=0;i<n_players_c;i++)
910 for(int j=0;j<=n_epochs_c;j++)
911 this->n_deaths[i][j] = 0;
912 //this->refreshSoldiers(false);
913 for(int y=0;y<map_height_c;y++) {
914 for(int x=0;x<map_width_c;x++) {
915 map_panels[x][y] = NULL;
916 }
917 }
918 alliance_yes = NULL;
919 alliance_no = NULL;
920
921 game_g->setTimeRate(client_player == PLAYER_DEMO ? 5 : 1);
922 }
923
~PlayingGameState()924 PlayingGameState::~PlayingGameState() {
925 LOG("~PlayingGameState()\n");
926 if( game_g->getMap() != NULL ) { // check needed if the current map failed to load, and we're resuming from saved state with that island
927 game_g->getMap()->freeSectors(); // needed to avoid crash for tests, and exiting to desktop
928 }
929 game_g->s_biplane->fadeOut(500);
930 game_g->s_jetplane->fadeOut(500);
931 game_g->s_spaceship->fadeOut(500);
932 for(size_t i=0;i<effects.size();i++) {
933 TimedEffect *effect = effects.at(i);
934 delete effect;
935 }
936 for(size_t i=0;i<ammo_effects.size();i++) {
937 TimedEffect *effect = ammo_effects.at(i);
938 delete effect;
939 }
940 if( text_effect != NULL ) {
941 delete text_effect;
942 }
943 if( this->gamePanel )
944 delete gamePanel;
945 LOG("~PlayingGameState() done\n");
946 }
947
createQuitWindow()948 void PlayingGameState::createQuitWindow() {
949 if( confirm_window == NULL && !game_g->isStateChanged() ) {
950 confirm_type = CONFIRMTYPE_QUITGAME;
951 confirm_window = new PanelPage(0, 0, default_width_c, default_height_c);
952 confirm_window->setBackground(0, 0, 0, 200);
953 const int offset_x_c = 80, offset_y_c = 100;
954 #if defined(__ANDROID__)
955 confirm_button_1 = NULL; // if user wants to exit to homescreen, they can just press the Home button
956 #else
957 confirm_button_1 = new Button(offset_x_c, offset_y_c, "SAVE GAME AND QUIT TO DESKTOP", game_g->letters_large);
958 confirm_window->add(confirm_button_1);
959 #endif
960 confirm_button_2 = new Button(offset_x_c, offset_y_c+16, "EXIT BATTLE", game_g->letters_large);
961 confirm_window->add(confirm_button_2);
962 confirm_button_3 = new Button(offset_x_c, offset_y_c+32, "CANCEL", game_g->letters_large);
963 confirm_window->add(confirm_button_3);
964 screen_page->add(confirm_window);
965 }
966 else if( confirm_window != NULL && !game_g->isStateChanged() ) {
967 closeConfirmWindow();
968 }
969 }
970
readSectorsProcessLine(Map * map,char * line,bool * done_header,int * sec_x,int * sec_y)971 bool PlayingGameState::readSectorsProcessLine(Map *map, char *line, bool *done_header, int *sec_x, int *sec_y) {
972 bool ok = true;
973 line[ strlen(line) - 1 ] = '\0'; // trim new line
974 line[ strlen(line) - 1 ] = '\0'; // trim carriage return
975 if( !(*done_header) ) {
976 if( line[0] != '#' ) {
977 LOG("expected first character to be '#'\n");
978 ok = false;
979 return ok;
980 }
981 // ignore rest of header
982 *done_header = true;
983 }
984 else {
985 char *line_ptr = line;
986 while( *line_ptr == ' ' || *line_ptr == '\t' ) // avoid initial whitespace
987 line_ptr++;
988 char *comment = strchr(line_ptr, '#');
989 if( comment != NULL ) { // trim comments
990 *comment = '\0';
991 }
992 char *ptr = strtok(line_ptr, " ");
993 if( ptr == NULL ) {
994 // this line may be a comment
995 }
996 else if( strcmp(ptr, "SECTOR") == 0 ) {
997 ptr = strtok(NULL, " ");
998 if( ptr == NULL ) {
999 LOG("can't find sec_x\n");
1000 ok = false;
1001 return ok;
1002 }
1003 *sec_x = atoi(ptr);
1004 if( *sec_x < 0 || *sec_x >= map_width_c ) {
1005 LOG("invalid map x %d\n", *sec_x);
1006 ok = false;
1007 return ok;
1008 }
1009
1010 ptr = strtok(NULL, " ");
1011 if( ptr == NULL ) {
1012 LOG("can't find sec_y\n");
1013 ok = false;
1014 return ok;
1015 }
1016 *sec_y = atoi(ptr);
1017 if( *sec_y < 0 || *sec_y >= map_height_c ) {
1018 LOG("invalid map y %d\n", *sec_y);
1019 ok = false;
1020 return ok;
1021 }
1022 }
1023 else if( strcmp(ptr, "ELEMENT") == 0 ) {
1024 if( *sec_x == -1 || *sec_y == -1 ) {
1025 LOG("sector not defined\n");
1026 ok = false;
1027 return ok;
1028 }
1029 ptr = strtok(NULL, " ");
1030 if( ptr == NULL ) {
1031 LOG("can't find element name\n");
1032 ok = false;
1033 return ok;
1034 }
1035 /*char elementname[MAX_LINE+1] = "";
1036 strcpy(elementname, ptr);*/
1037 string elementname = ptr;
1038
1039 ptr = strtok(NULL, " ");
1040 if( ptr == NULL ) {
1041 LOG("can't find n_elements\n");
1042 ok = false;
1043 return ok;
1044 }
1045 int n_elements = atoi(ptr);
1046
1047 Id element = UNDEFINED;
1048 for(int i=0;i<N_ID;i++) {
1049 if( strcmp( game_g->elements[i]->getName(), elementname.c_str() ) == 0 ) {
1050 element = (Id)i;
1051 }
1052 }
1053 if( element == UNDEFINED ) {
1054 LOG("unknown element: %s\n", elementname.c_str());
1055 ok = false;
1056 return ok;
1057 }
1058
1059 game_g->getMap()->getSector(*sec_x, *sec_y)->setElements(element, n_elements);
1060 }
1061 else {
1062 LOG("unknown word: %s\n", ptr);
1063 ok = false;
1064 return ok;
1065 }
1066 }
1067 return ok;
1068 }
1069
readSectors(Map * map)1070 bool PlayingGameState::readSectors(Map *map) {
1071 bool ok = true;
1072 const int MAX_LINE = 4096;
1073 char line[MAX_LINE+1] = "";
1074 int sec_x = -1, sec_y = -1;
1075 bool done_header = false;
1076
1077 char fullname[4096] = "";
1078 sprintf(fullname, "%s/%s", maps_dirname, game_g->getMap()->getFilename());
1079 // open in binary mode, so that we parse files in an OS-independent manner
1080 // (otherwise, Windows will parse "\r\n" as being "\n", but Linux will still read it as "\n")
1081 //FILE *file = fopen(fullname, "rb");
1082 SDL_RWops *file = SDL_RWFromFile(fullname, "rb");
1083 #if !defined(__ANDROID__) && defined(__DragonFly__)
1084 if( file == NULL ) {
1085 LOG("searching in /usr/local/share/gigalomania/ for islands folder\n");
1086 sprintf(fullname, "%s/%s", alt_maps_dirname, game_g->getMap()->getFilename());
1087 file = SDL_RWFromFile(fullname, "rb");
1088 }
1089 #endif
1090 if( file == NULL ) {
1091 LOG("failed to open file: %s\n", fullname);
1092 //perror("perror returns: ");
1093 return false;
1094 }
1095 char buffer[MAX_LINE+1] = "";
1096 int buffer_offset = 0;
1097 bool reached_end = false;
1098 int newline_index = 0;
1099 while( ok ) {
1100 bool done = game_g->readLineFromRWOps(ok, file, buffer, line, MAX_LINE, buffer_offset, newline_index, reached_end);
1101 if( !ok ) {
1102 LOG("failed to read line\n");
1103 }
1104 else if( done ) {
1105 break;
1106 }
1107 else {
1108 ok = readSectorsProcessLine(map, line, &done_header, &sec_x, &sec_y);
1109 }
1110 }
1111 file->close(file);
1112
1113 return ok;
1114 }
1115
createSectors(int x,int y,int n_men)1116 void PlayingGameState::createSectors(int x, int y, int n_men) {
1117 LOG("PlayingGameState::createSectors(%d, %d, %d)\n", x, y, n_men);
1118
1119 game_g->getMap()->createSectors(this, game_g->getStartEpoch());
1120 Sector *sector = game_g->getMap()->getSector(x, y);
1121 current_sector = sector;
1122 if( !game_g->isDemo() ) {
1123 sector->createTower(client_player, n_men);
1124 }
1125 //current_sector->createTower(human_player, 10);
1126
1127 //current_sector->getArmy(enemy_player)->soldiers[10] = 0;
1128 //game_g->getMap()->sectors[2][2]->getArmy(enemy_player)->soldiers[0] = 10;
1129
1130 //Sector *enemy_sector = game_g->getMap()->sectors[1][2];
1131
1132 for(int i=0;i<n_players_c;i++) {
1133 if( i == client_player || game_g->players[i] == NULL )
1134 continue;
1135 int ex = 0, ey = 0;
1136 while( true ) {
1137 game_g->getMap()->findRandomSector(&ex, &ey);
1138 ASSERT( game_g->getMap()->isSectorAt(ex, ey) );
1139 if( game_g->getMap()->getSector(ex, ey)->getPlayer() == -1 && !game_g->getMap()->isReserved(ex, ey) )
1140 break;
1141 }
1142 //Sector *enemy_sector = game_g->getMap()->sectors[ex][ey];
1143 Sector *enemy_sector = game_g->getMap()->getSector(ex, ey);
1144 //enemy_sector->getArmy(enemy_player)->soldiers[10] = 30;
1145 //enemy_sector->createTower(enemy_player, 12);
1146 if( game_g->getStartEpoch() == end_epoch_c ) {
1147 //game_g->players[i]->n_men_for_this_island = n_suspended[i];
1148 game_g->players[i]->setNMenForThisIsland(100);
1149 }
1150 else {
1151 game_g->players[i]->setNMenForThisIsland(20 + 5*game_g->getStartEpoch());
1152 // total: 360*3 + 65 = 1145 men
1153 }
1154 LOG("Enemy %d created at %d , %d\n", i, ex, ey);
1155 enemy_sector->createTower(i, game_g->players[i]->getNMenForThisIsland());
1156 //enemy_sector->createTower(enemy_player, 20);
1157 //enemy_sector->createTower(enemy_player, 200);
1158 }
1159
1160 if( !readSectors(game_g->getMap()) ) {
1161 LOG("failed to read map sector info!\n");
1162 ASSERT(false);
1163 }
1164 }
1165
getGamePanel()1166 GamePanel *PlayingGameState::getGamePanel() {
1167 return this->gamePanel;
1168 }
1169
1170 /*Sector *PlayingGameState::getCurrentSector() {
1171 return current_sector;
1172 }*/
1173
getCurrentSector() const1174 const Sector *PlayingGameState::getCurrentSector() const {
1175 return current_sector;
1176 }
1177
viewingActiveClientSector() const1178 bool PlayingGameState::viewingActiveClientSector() const {
1179 return this->getCurrentSector()->getActivePlayer() == client_player;
1180 }
1181
viewingAnyClientSector() const1182 bool PlayingGameState::viewingAnyClientSector() const {
1183 // includes shutdown sectors
1184 return this->getCurrentSector()->getPlayer() == client_player;
1185 }
1186
openPitMine()1187 bool PlayingGameState::openPitMine() {
1188 if( current_sector->getActivePlayer() != -1 && current_sector->getBuilding(BUILDING_MINE) == NULL && current_sector->getEpoch() >= 1 ) {
1189 for(int i=0;i<N_ID;i++) {
1190 if( current_sector->anyElements((Id)i) ) {
1191 Element *element = game_g->elements[i];
1192 if( element->getType() == Element::OPENPITMINE )
1193 return true;
1194 }
1195 }
1196 }
1197 return false;
1198 }
1199
setupMapGUI()1200 void PlayingGameState::setupMapGUI() {
1201 if( alliance_yes != NULL ) {
1202 delete alliance_yes;
1203 alliance_yes = NULL;
1204 }
1205 if( alliance_no != NULL ) {
1206 delete alliance_no;
1207 alliance_no = NULL;
1208 }
1209 for(int y=0;y<map_height_c;y++) {
1210 for(int x=0;x<map_width_c;x++) {
1211 if( map_panels[x][y] != NULL ) {
1212 delete map_panels[x][y];
1213 map_panels[x][y] = NULL;
1214 }
1215 }
1216 }
1217 if( this->player_asking_alliance != -1 ) {
1218 alliance_yes = new Button(24, 82, "YES", game_g->letters_large);
1219 alliance_yes->setInfoLMB("join the alliance");
1220 screen_page->add(alliance_yes);
1221 alliance_no = new Button(56, 82, "NO", game_g->letters_large);
1222 alliance_no->setInfoLMB("refuse the alliance");
1223 screen_page->add(alliance_no);
1224 }
1225 else if( this->map_display == MAPDISPLAY_MAP ) {
1226 for(int y=0;y<map_height_c;y++) {
1227 for(int x=0;x<map_width_c;x++) {
1228 if( game_g->getMap()->isSectorAt(x, y) ) {
1229 int map_x = offset_map_x_c + 16 * x;
1230 int map_y = offset_map_y_c + 16 * y;
1231 PanelPage *panel = new PanelPage(map_x, map_y, 16, 16);
1232 panel->setTolerance(0); // since map sectors are aligned, better for touchscreens not to use the "tolerance"
1233 panel->setInfoLMB("view this sector");
1234 screen_page->add(panel);
1235 //game_g->getMap()->panels[x][y] = panel;
1236 map_panels[x][y] = panel;
1237 char buffer[256] = "";
1238 sprintf(buffer, "map_%d_%d", x, y);
1239 map_panels[x][y]->setId(buffer);
1240 }
1241 }
1242 }
1243 }
1244 }
1245
reset()1246 void PlayingGameState::reset() {
1247 //LOG("PlayingGameState::reset()\n");
1248 if( current_sector == NULL )
1249 return;
1250
1251 this->screen_page->free(true);
1252 alliance_yes = NULL;
1253 alliance_no = NULL;
1254 tutorial_next_button = NULL;
1255 for(int y=0;y<map_height_c;y++) {
1256 for(int x=0;x<map_width_c;x++) {
1257 map_panels[x][y] = NULL;
1258 }
1259 }
1260 confirm_type = CONFIRMTYPE_UNKNOWN;
1261 confirm_window = NULL;
1262 confirm_button_1 = NULL;
1263 confirm_button_2 = NULL;
1264 confirm_button_3 = NULL;
1265
1266 if( this->gamePanel != NULL ) {
1267 delete this->gamePanel;
1268 this->gamePanel = NULL;
1269 }
1270
1271 this->land_panel = new PanelPage(offset_land_x_c, offset_land_y_c, default_width_c - offset_land_x_c, default_height_c - offset_land_y_c - quit_button_offset_c);
1272 screen_page->add(this->land_panel);
1273
1274 // setup screen_page buttons
1275 this->setupMapGUI();
1276
1277 if( !game_g->isDemo() ) {
1278 speed_button = new ImageButton(offset_map_x_c + 16 * map_width_c + 4, 4, game_g->icon_speeds[game_g->getTimeRate()-1]);
1279 speed_button->setId("speed_button");
1280 if( game_g->isOneMouseButton() ) {
1281 speed_button->setInfoLMB("cycle through different time rates");
1282 }
1283 else {
1284 speed_button->setInfoLMB("decrease the rate of time");
1285 speed_button->setInfoRMB("increase the rate of time");
1286 }
1287 screen_page->add(speed_button);
1288
1289 //if( mobile_ui )
1290 {
1291 pause_button = new Button(default_width_c - 80, default_height_c - quit_button_offset_c, "PAUSE", game_g->letters_large);
1292 pause_button->setId("pause_button");
1293 screen_page->add(pause_button);
1294 quit_button = new Button(default_width_c - 32, default_height_c - quit_button_offset_c, "QUIT", game_g->letters_large);
1295 quit_button->setId("quit_button");
1296 screen_page->add(quit_button);
1297 }
1298 }
1299
1300 for(int i=0;i<n_players_c;i++) {
1301 shield_buttons[i] = NULL;
1302 shield_number_panels[i] = NULL;
1303 }
1304 this->shield_blank_button = NULL;
1305
1306 resetShieldButtons();
1307
1308 for(int i=0;i<N_BUILDINGS;i++) {
1309 Building *building = current_sector->getBuilding((Type)i);
1310 if( building != NULL ) {
1311 addBuilding(building);
1312 }
1313 }
1314
1315 // must be done after creating shield_number_panels
1316 this->refreshSoldiers(false);
1317
1318 /*if( smokeParticleSystem != NULL ) {
1319 delete smokeParticleSystem;
1320 }
1321 Building *building_factory = current_sector->getBuilding(BUILDING_FACTORY);
1322 //Building *building_factory = current_sector->getBuilding(BUILDING_TOWER);
1323 if( building_factory != NULL ) {
1324 //smokeParticleSystem = new SmokeParticleSystem(smoke_image, offset_land_x_c + building_factory->getX(), offset_land_y_c + building_factory->getY());
1325 smokeParticleSystem = new SmokeParticleSystem(smoke_image, offset_land_x_c + building_factory->getX() + 20, offset_land_y_c + building_factory->getY());
1326 }*/
1327 /*if( smokeParticleSystem == NULL ) {
1328 smokeParticleSystem = new SmokeParticleSystem(smoke_image);
1329 }*/
1330
1331 this->gamePanel = new GamePanel(this, this->client_player);
1332 // must call setup last, in case it recalls member functions of PlayingGameState, that requires the buttons to have been initialised
1333 this->gamePanel->setup();
1334
1335 if( game_g->getTutorial() != NULL ) {
1336 const TutorialCard *card = game_g->getTutorial()->getCard();
1337 if( card != NULL ) {
1338 card->setGUI(this);
1339 }
1340 else {
1341 GUIHandler::resetGUI(this);
1342 }
1343 }
1344 if( LOGGING ) {
1345 current_sector->printDebugInfo();
1346 }
1347 }
1348
resetShieldButtons()1349 void PlayingGameState::resetShieldButtons() {
1350 bool done_shield[n_players_c];
1351 for(int i=0;i<n_players_c;i++)
1352 done_shield[i] = false;
1353 for(int i=0;i<n_players_c;i++) {
1354 if( shield_buttons[i] != NULL ) {
1355 screen_page->remove(shield_buttons[i]);
1356 delete shield_buttons[i];
1357 shield_buttons[i] = NULL;
1358 }
1359 if( shield_number_panels[i] != NULL ) {
1360 screen_page->remove(shield_number_panels[i]);
1361 delete shield_number_panels[i];
1362 shield_number_panels[i] = NULL;
1363 }
1364 }
1365 if( shield_blank_button != NULL ) {
1366 screen_page->remove(shield_blank_button);
1367 delete shield_blank_button;
1368 shield_blank_button = NULL;
1369 }
1370 int n_sides = 0;
1371 for(int i=0;i<n_players_c;i++) {
1372 if( !done_shield[i] && game_g->players[i] != NULL && !game_g->players[i]->isDead() ) {
1373 bool allied[n_players_c];
1374 for(int j=0;j<n_players_c;j++)
1375 allied[j] = false;
1376 allied[i] = true;
1377 done_shield[i] = true;
1378 for(int j=i+1;j<n_players_c;j++) {
1379 if( Player::isAlliance(i, j) ) {
1380 ASSERT( game_g->players[j] != NULL );
1381 ASSERT( !game_g->players[j]->isDead() );
1382 allied[j] = true;
1383 done_shield[j] = true;
1384 }
1385 }
1386 n_sides++;
1387 shield_buttons[i] = new ImageButton(offset_map_x_c + 16 * map_width_c + 4, offset_map_y_c + shield_step_y_c * i + 8, game_g->playershields[ Player::getShieldIndex(allied) ]);
1388 screen_page->add(shield_buttons[i]);
1389 //shield_number_panels[i] = new PanelPage(offset_map_x_c + 16 * map_width_c + 4 + 16, offset_map_y_c + shield_step_y_c * i + 8, 20, 10);
1390 shield_number_panels[i] = new PanelPage(offset_map_x_c + 16 * map_width_c + 4 + 16, offset_map_y_c + shield_step_y_c * i + 8, 20, shield_step_y_c);
1391 screen_page->add(shield_number_panels[i]);
1392 }
1393 }
1394 if( !game_g->isDemo() && n_sides > 2 ) {
1395 for(int i=0;i<n_players_c;i++) {
1396 if( shield_buttons[i] != NULL && i != client_player && !Player::isAlliance(i, client_player) ) {
1397 shield_buttons[i]->setInfoLMB("make an alliance");
1398 }
1399 }
1400 }
1401 bool any_alliances = false;
1402 for(int i=0;i<n_players_c && !any_alliances && !game_g->isDemo();i++) {
1403 if( i != client_player && Player::isAlliance(i, client_player) ) {
1404 any_alliances = true;
1405 ASSERT( game_g->players[i] != NULL );
1406 ASSERT( !game_g->players[i]->isDead() );
1407 }
1408 }
1409 if( any_alliances ) {
1410 shield_blank_button = new ImageButton(offset_map_x_c + 16 * map_width_c + 4, offset_map_y_c + 4*shield_step_y_c + 8, game_g->playershields[0]);
1411 shield_blank_button->setInfoLMB("break current alliance");
1412 screen_page->add(shield_blank_button);
1413 }
1414
1415 refreshShieldNumberPanels();
1416 }
1417
addBuilding(Building * building)1418 void PlayingGameState::addBuilding(Building *building) {
1419 for(int j=0;j<building->getNTurrets();j++) {
1420 screen_page->add(building->getTurretButton(j));
1421 }
1422 if( building->getBuildingButton() != NULL )
1423 screen_page->add(building->getBuildingButton());
1424 }
1425
setFlashingSquare(int xpos,int ypos)1426 void PlayingGameState::setFlashingSquare(int xpos,int ypos) {
1427 if( this->player_asking_alliance == -1 && map_display == MAPDISPLAY_MAP ) {
1428 FlashingSquare *square = new FlashingSquare(xpos, ypos);
1429 this->effects.push_back(square);
1430 }
1431 };
1432
fadeScreen(bool out,int delay,void (* func_finish)())1433 void GameState::fadeScreen(bool out, int delay, void (*func_finish)()) {
1434 if( fade != NULL )
1435 delete fade;
1436 if( game_g->isTesting() ) {
1437 if( func_finish != NULL ) {
1438 func_finish();
1439 }
1440 }
1441 else {
1442 fade = new FadeEffect(false, out, delay, func_finish);
1443 }
1444 }
1445
whiteFlash()1446 void GameState::whiteFlash() {
1447 //ASSERT( whitefade == NULL );
1448 if( whitefade != NULL )
1449 delete whitefade;
1450 if( !game_g->isTesting() ) {
1451 whitefade = new FadeEffect(true, false, 0, NULL);
1452 }
1453 }
1454
getFlagOffset(int * offset_x,int * offset_y,int epoch) const1455 void PlayingGameState::getFlagOffset(int *offset_x, int *offset_y, int epoch) const {
1456 if( game_g->isUsingOldGfx() ) {
1457 *offset_x = 22;
1458 *offset_y = 6;
1459 }
1460 else if( epoch == 6 || epoch ==7 ) {
1461 // building towers have flat roof
1462 *offset_x = 22;
1463 *offset_y = 6;
1464 }
1465 else {
1466 *offset_x = 21;
1467 *offset_y = 2;
1468 }
1469 }
1470
draw()1471 void PlayingGameState::draw() {
1472 #if defined(__ANDROID__)
1473 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
1474 #endif
1475
1476 game_g->background->draw(0, 0);
1477 //background->draw(0, 0, true);
1478
1479 bool no_armies = true;
1480 for(int i=0;i<n_players_c && no_armies;i++) {
1481 const Army *army = current_sector->getArmy(i);
1482 if( army->getTotal() > 0 ) {
1483 no_armies = false;
1484 }
1485 }
1486 if( no_armies && this->map_display == MAPDISPLAY_UNITS ) {
1487 this->map_display = MAPDISPLAY_MAP;
1488 this->reset();
1489 }
1490
1491 if( this->player_asking_alliance != -1 ) {
1492 // ask alliance
1493 if( game_g->player_heads_alliance[player_asking_alliance] != NULL ) {
1494 game_g->player_heads_alliance[player_asking_alliance]->draw(offset_map_x_c + 24, offset_map_y_c + 24);
1495 }
1496 stringstream str;
1497 str << PlayerType::getName((PlayerType::PlayerTypeID)player_asking_alliance);
1498 Image::write(offset_map_x_c + 8, offset_map_y_c + 0, game_g->letters_small, str.str().c_str(), Image::JUSTIFY_LEFT);
1499 str.str("asks for an");
1500 Image::write(offset_map_x_c + 8, offset_map_y_c + 8, game_g->letters_small, str.str().c_str(), Image::JUSTIFY_LEFT);
1501 str.str("alliance");
1502 Image::write(offset_map_x_c + 8, offset_map_y_c + 16, game_g->letters_small, str.str().c_str(), Image::JUSTIFY_LEFT);
1503 }
1504 else if( this->map_display == MAPDISPLAY_MAP ) {
1505 // map
1506
1507 game_g->getMap()->draw(offset_map_x_c, offset_map_y_c);
1508 for(int y=0;y<map_height_c;y++) {
1509 for(int x=0;x<map_width_c;x++) {
1510 if( game_g->getMap()->getSector(x, y) != NULL ) {
1511 int map_x = offset_map_x_c + 16 * x;
1512 int map_y = offset_map_y_c + 16 * y;
1513 //map_sq[15]->draw(map_x, map_y, true);
1514 if( game_g->getMap()->getSector(x, y)->getPlayer() != -1 ) {
1515 game_g->icon_towers[ game_g->getMap()->getSector(x, y)->getPlayer() ]->draw(map_x + 5, map_y + 5);
1516 }
1517 else if( game_g->getMap()->getSector(x, y)->isNuked() ) {
1518 game_g->icon_nuke_hole->draw(map_x + 4, map_y + 4);
1519 }
1520 for(int i=0;i<n_players_c;i++) {
1521 Army *army = game_g->getMap()->getSector(x, y)->getArmy(i);
1522 int n_army = army->getTotal();
1523 if( n_army > 0 ) {
1524 int off_step = 5;
1525 int off_step_x = ( i == 0 || i == 2 ) ? -off_step : off_step;
1526 int off_step_y = ( i == 0 || i == 1 ) ? -off_step : off_step;
1527 game_g->icon_armies[i]->draw(map_x + 6 + off_step_x, map_y + 6 + off_step_y);
1528 }
1529 }
1530 }
1531 }
1532 }
1533 int map_x = offset_map_x_c + 16 * current_sector->getXPos();
1534 int map_y = offset_map_y_c + 16 * current_sector->getYPos();
1535 game_g->mapsquare->draw(map_x, map_y);
1536 }
1537 else if( this->map_display == MAPDISPLAY_UNITS ) {
1538 // unit stats
1539 const int gap = 18;
1540 const int extra = 0;
1541 for(int i=0;i<=game_g->getNSubEpochs();i++) {
1542 Image *image = (i==0) ? game_g->unarmed_man : game_g->numbered_weapons[game_g->getStartEpoch() + i - 1];
1543 //Image *image = (i==0) ? men[game_g->getStartEpoch()] : numbered_weapons[game_g->getStartEpoch() + i - 1];
1544 image->draw(offset_map_x_c + gap * i + extra, offset_map_y_c + 2 - 16 + 8);
1545 }
1546 for(int i=0;i<n_players_c;i++) {
1547 if( shield_buttons[i] == NULL ) {
1548 continue;
1549 }
1550 int off = 0;
1551 for(int j=i;j<n_players_c;j++) {
1552 if( j == i || Player::isAlliance(i, j) ) {
1553 const Army *army = current_sector->getArmy(j);
1554 if( army->getTotal() > 0 ) {
1555 for(int k=0;k<=game_g->getNSubEpochs();k++) {
1556 int idx = (k==0) ? 10 : game_g->getStartEpoch() + k - 1;
1557 int n_men = army->getSoldiers(idx);
1558 if( n_men > 0 ) {
1559 //Image::writeNumbers(offset_map_x_c + 16 * k + 4, offset_map_y_c + 2 + 16 * i + 8 * off + 8, game_g->numbers_small[j], n_men, Image::JUSTIFY_LEFT, true);
1560 Image::writeNumbers(offset_map_x_c + gap * k + extra, offset_map_y_c + 2 + 16 * i + 8 * off + 8, game_g->numbers_small[j], n_men, Image::JUSTIFY_LEFT);
1561 }
1562 }
1563 off++;
1564 }
1565 }
1566 }
1567 }
1568 }
1569
1570 // land area
1571 game_g->land[game_g->getMap()->getColour()]->draw(offset_land_x_c, offset_land_y_c);
1572
1573 // trees etc (not at front)
1574 for(int i=0;i<current_sector->getNFeatures();i++) {
1575 const Feature *feature = current_sector->getFeature(i);
1576 if( !feature->isAtFront() ) {
1577 feature->draw();
1578 }
1579 }
1580
1581 if( current_sector->getActivePlayer() != -1 )
1582 {
1583 if( openPitMine() )
1584 game_g->icon_openpitmine->draw(offset_land_x_c + offset_openpitmine_x_c, offset_land_y_c + offset_openpitmine_y_c);
1585
1586 bool rotate_defenders = false;
1587 if( game_g->getGameTime() - defenders_last_time_update > defenders_ticks_per_update_c ) {
1588 rotate_defenders = true;
1589 defenders_last_time_update = game_g->getGameTime();
1590 }
1591
1592 for(int i=0;i<N_BUILDINGS;i++) {
1593 Building *building = current_sector->getBuilding((Type)i);
1594 if( building == NULL )
1595 continue;
1596
1597 // draw building
1598 Image **images = building->getImages();
1599 images[ current_sector->getBuildingEpoch() ]->draw(offset_land_x_c + building->getX(), offset_land_y_c + building->getY());
1600
1601 if( rotate_defenders )
1602 building->rotateDefenders();
1603
1604 // draw defenders
1605 for(int j=0;j<building->getNTurrets();j++) {
1606 // uncomment to draw turrent button regions:
1607 /*{
1608 PanelPage *button = building->getTurretButton(j);
1609 game_g->getScreen()->fillRectWithAlpha(game_g->getScaleWidth()*button->getLeft(), game_g->getScaleHeight()*button->getTop(), game_g->getScaleWidth()*button->getWidth(), game_g->getScaleHeight()*button->getHeight(), 255, 0, 0, 127);
1610 }*/
1611
1612 if( building->getTurretMan(j) != -1 ) {
1613 Image *image = NULL;
1614 int defender_epoch = building->getTurretMan(j);
1615 if( defender_epoch == nuclear_epoch_c ) {
1616 image = game_g->nuke_defences[current_sector->getPlayer()];
1617 }
1618 else {
1619 image = game_g->defenders[current_sector->getPlayer()][defender_epoch][ building->getTurretManFrame(j) % game_g->n_defender_frames[defender_epoch] ];
1620 }
1621 image->draw(building->getTurretButton(j)->getLeft(), building->getTurretButton(j)->getTop() - 4);
1622 }
1623 }
1624
1625 if( i == BUILDING_TOWER ) {
1626 int offset_x = 0, offset_y = 0;
1627 getFlagOffset(&offset_x, &offset_y, current_sector->getBuildingEpoch());
1628 game_g->flags[ current_sector->getPlayer() ][game_g->getFrameCounter() % n_flag_frames_c]->draw(offset_land_x_c + building->getX() + offset_x, offset_land_y_c + building->getY() + offset_y);
1629 }
1630
1631 int width = game_g->building_health->getScaledWidth();
1632 int health = building->getHealth();
1633 int max_health = building->getMaxHealth();
1634 int offx = offset_land_x_c + building->getX() + 4;
1635 int offy = offset_land_y_c + building->getY() + images[ current_sector->getBuildingEpoch() ]->getScaledHeight() + 2;
1636 game_g->building_health->draw(offx, offy, (int)((width*health)/(float)max_health), game_g->building_health->getScaledHeight());
1637 }
1638 }
1639 else if( current_sector->getPlayer() != -1 ) {
1640 ASSERT( current_sector->isShutdown() );
1641 Building *building = current_sector->getBuilding(BUILDING_TOWER);
1642 ASSERT( building != NULL );
1643 Image **images = building->getImages();
1644 images[ current_sector->getBuildingEpoch() ]->draw(offset_land_x_c + building->getX(), offset_land_y_c + building->getY());
1645 int offset_x = 0, offset_y = 0;
1646 getFlagOffset(&offset_x, &offset_y, current_sector->getBuildingEpoch());
1647 game_g->flags[ current_sector->getPlayer() ][game_g->getFrameCounter() % n_flag_frames_c]->draw(offset_land_x_c + building->getX() + offset_x, offset_land_y_c + building->getY() + offset_y);
1648 }
1649
1650 //Vector soldier_list(n_players_c * 250);
1651 int n_total_soldiers = 0;
1652 for(int i=0;i<n_players_c;i++) {
1653 n_total_soldiers += soldiers[i].size();
1654 }
1655 Soldier **soldier_list = new Soldier *[n_total_soldiers];
1656 for(int i=0,c=0;i<n_players_c;i++) {
1657 //for(int j=0;j<n_soldiers[i];j++) {
1658 for(size_t j=0;j<soldiers[i].size();j++) {
1659 //Soldier *soldier = soldiers[i][j];
1660 //Soldier *soldier = (Soldier *)soldiers[i]->get(j);
1661 Soldier *soldier = soldiers[i].at(j);
1662 //soldier_list.add(soldier);
1663 soldier_list[c++] = soldier;
1664 }
1665 }
1666 //Soldier::sortSoldiers((Soldier **)soldier_list.getData(), soldier_list.size());
1667 Soldier::sortSoldiers(soldier_list, n_total_soldiers);
1668 // draw land units
1669 /*for(int i=0;i<soldier_list.size();i++) {
1670 Soldier *soldier = (Soldier *)soldier_list.elementAt(i);*/
1671 for(int i=0;i<n_total_soldiers;i++) {
1672 Soldier *soldier = soldier_list[i];
1673 ASSERT(soldier->epoch != nuclear_epoch_c);
1674 if( !isAirUnit(soldier->epoch) ) {
1675 //int frame = soldier->dir * 4 + ( game_g->getFrameCounter() % 3 );
1676 //Image *image = attackers_walking[soldier->player][soldier->epoch][frame];
1677 int n_frames = game_g->n_attacker_frames[soldier->epoch][soldier->dir];
1678 Image *image = game_g->attackers_walking[soldier->player][soldier->epoch][soldier->dir][game_g->getFrameCounter() % n_frames];
1679 image->draw(offset_land_x_c + soldier->xpos, offset_land_y_c + soldier->ypos);
1680 }
1681 }
1682
1683 // trees etc (at front)
1684 for(int i=0;i<current_sector->getNFeatures();i++) {
1685 const Feature *feature = current_sector->getFeature(i);
1686 if( feature->isAtFront() ) {
1687 feature->draw();
1688 }
1689 }
1690
1691 for(int i=effects.size()-1;i>=0;i--) {
1692 TimedEffect *effect = effects.at(i);
1693 if( effect->render() ) {
1694 effects.erase(effects.begin() + i);
1695 delete effect;
1696 }
1697 }
1698 for(int i=ammo_effects.size()-1;i>=0;i--) {
1699 TimedEffect *effect = ammo_effects.at(i);
1700 if( effect->render() ) {
1701 ammo_effects.erase(ammo_effects.begin() + i);
1702 delete effect;
1703 }
1704 }
1705
1706 // draw air units
1707 /*for(int i=0;i<soldier_list.size();i++) {
1708 Soldier *soldier = (Soldier *)soldier_list.elementAt(i);*/
1709 for(int i=0;i<n_total_soldiers;i++) {
1710 Soldier *soldier = soldier_list[i];
1711 ASSERT(soldier->epoch != nuclear_epoch_c);
1712 if( isAirUnit(soldier->epoch) ) {
1713 Image *image = NULL;
1714 if( soldier->epoch == 6 || soldier->epoch == 7 ) {
1715 image = game_g->planes[soldier->player][soldier->epoch];
1716 }
1717 else if( soldier->epoch == 9 ) {
1718 int frame = game_g->getFrameCounter() % 3;
1719 image = game_g->saucers[soldier->player][frame];
1720 }
1721 ASSERT(image != NULL);
1722 image->draw(offset_land_x_c + soldier->xpos, offset_land_y_c + soldier->ypos);
1723 if( soldier->epoch == 7 ) {
1724 if( current_sector->getJetParticleSystem() != NULL ) {
1725 current_sector->getJetParticleSystem()->draw(offset_land_x_c + soldier->xpos + 17, offset_land_y_c + soldier->ypos + 17);
1726 }
1727 }
1728 }
1729 }
1730 delete [] soldier_list;
1731
1732 // nuke
1733 int nuke_time = -1;
1734 int nuke_by_player = current_sector->beingNuked(&nuke_time);
1735 if( nuke_by_player != -1 ) {
1736 int xpos = -1, ypos = -1;
1737 current_sector->getNukePos(&xpos, &ypos);
1738 game_g->nukes[nuke_by_player][1]->draw(xpos, ypos);
1739 if( current_sector->getNukeParticleSystem() != NULL ) {
1740 current_sector->getNukeParticleSystem()->draw(xpos + 23 - 4, ypos + 2 - 4);
1741 }
1742 }
1743 // nuke defence
1744 int nuke_defence_time = -1;
1745 int nuke_defence_x = 0;
1746 int nuke_defence_y = 0;
1747 if( current_sector->hasNuclearDefenceAnimation(&nuke_defence_time, &nuke_defence_x, &nuke_defence_y) ) {
1748 ASSERT( nuke_defence_time != -1 );
1749 float alpha = ((float)( game_g->getGameTime() - nuke_defence_time )) / (float)nuke_delay_c;
1750 ASSERT( alpha >= 0.0 );
1751 if( alpha > 1.0 )
1752 alpha = 1.0;
1753 int ey = nuke_defence_y - 200;
1754 int ypos = (int)(alpha * ey + (1.0 - alpha) * nuke_defence_y);
1755 game_g->nukes[current_sector->getPlayer()][0]->draw(nuke_defence_x, ypos);
1756 if( current_sector->getNukeDefenceParticleSystem() != NULL ) {
1757 current_sector->getNukeDefenceParticleSystem()->draw(nuke_defence_x + 4 - 4, ypos + 31 - 4);
1758 }
1759 }
1760
1761 // playershields etc
1762 for(int i=0;i<n_players_c;i++) {
1763 if( game_g->players[i] != NULL && !game_g->players[i]->isDead() ) {
1764 //ASSERT( shield_buttons[i] != NULL );
1765 if( shield_buttons[i] == NULL ) {
1766 continue;
1767 }
1768 shield_number_panels[i]->setVisible(false);
1769 /*int n_allied = 1;
1770 for(j=i+1;j<n_players_c;j++) {
1771 if( Player::isAlliance(i, j) ) {
1772 n_allied++;
1773 }
1774 }*/
1775 //playershields[ game_g->players[i]->getShieldIndex() ]->draw(offset_map_x_c + 16 * map_width_c + 4, offset_map_y_c + shield_step_y_c * i + 8, true);
1776 int off = 0;
1777 for(int j=i;j<n_players_c;j++) {
1778 if( j == i || Player::isAlliance(i, j) ) {
1779 const Army *army = current_sector->getArmy(j);
1780 int n_army = army->getTotal();
1781 if( n_army > 0 ) {
1782 shield_number_panels[i]->setVisible(true);
1783 //Image::writeNumbers(offset_map_x_c + 16 * map_width_c + 20, offset_map_y_c + 2 + shield_step_y_c * i + 8, game_g->numbers_small[i], n_army, Image::JUSTIFY_LEFT, true);
1784 Image::writeNumbers(offset_map_x_c + 16 * map_width_c + 20, offset_map_y_c + 2 + shield_step_y_c * i + 8 * off + 8, game_g->numbers_small[j], n_army, Image::JUSTIFY_LEFT);
1785 off++;
1786 }
1787 }
1788 }
1789 }
1790 }
1791
1792 // panel
1793 if( game_g->getTutorial() != NULL ) {
1794 const TutorialCard *card = game_g->getTutorial()->getCard();
1795 if( card != NULL ) {
1796 const unsigned char tutorial_alpha_c = 127;
1797 int n_lines = 0, max_wid = 0;
1798 int s_w = game_g->letters_small[0]->getScaledWidth();
1799 int l_w = game_g->letters_large[0]->getScaledWidth();
1800 int l_h = game_g->letters_large[0]->getScaledHeight();
1801 textLines(&n_lines, &max_wid, card->getText().c_str(), s_w, l_w);
1802
1803 Rect2D rect;
1804 rect.x = 100;
1805 rect.y = 130;
1806 rect.w = max_wid;
1807 rect.h = n_lines * (l_h + 2);
1808 const Image *player_image = game_g->player_heads_alliance[client_player];
1809 if( player_image != NULL ) {
1810 player_image->draw(rect.x, rect.y - player_image->getScaledHeight());
1811 }
1812 #if SDL_MAJOR_VERSION == 1
1813 Image *fill_rect = Image::createBlankImage(game_g->getScaleWidth()*rect.w, game_g->getScaleHeight()*rect.h, 24);
1814 fill_rect->fillRect(0, 0, game_g->getScaleWidth()*rect.w, game_g->getScaleHeight()*rect.h, 0, 0, 0);
1815 fill_rect->convertToDisplayFormat();
1816 fill_rect->drawWithAlpha(game_g->getScaleWidth()*rect.x, game_g->getScaleHeight()*rect.y, tutorial_alpha_c);
1817 delete fill_rect;
1818 #else
1819 game_g->getScreen()->fillRectWithAlpha((short)(game_g->getScaleWidth()*rect.x), (short)(game_g->getScaleHeight()*rect.y), (short)(game_g->getScaleWidth()*rect.w), (short)(game_g->getScaleHeight()*rect.h), 0, 0, 0, tutorial_alpha_c);
1820 #endif
1821 Image::writeMixedCase(rect.x, rect.y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, card->getText().c_str(), Image::JUSTIFY_LEFT);
1822 if( card->hasArrow(this) ) {
1823 int arrow_x = card->getArrowX();
1824 int arrow_y = card->getArrowY();
1825 int src_x = rect.x - 4;
1826 int src_y = (int)(rect.y + 0.5*rect.h);
1827 if( arrow_x >= rect.x + rect.w ) {
1828 src_x = rect.x + rect.w + 4;
1829 }
1830 else if( arrow_x >= rect.x && arrow_x < rect.x + rect.w ) {
1831 src_x = (int)(rect.x + 0.5*rect.w);
1832 if( arrow_y < src_y ) {
1833 src_y = rect.y - 4;
1834 }
1835 else {
1836 src_y = rect.y + rect.h + 4;
1837 }
1838 }
1839 game_g->getScreen()->drawLine((short)(game_g->getScaleWidth()*src_x), (short)(game_g->getScaleHeight()*src_y), (short)(game_g->getScaleWidth()*arrow_x), (short)(game_g->getScaleHeight()*arrow_y), 255, 255, 255);
1840 }
1841
1842 if( !card->autoProceed() && card->canProceed(this) ) {
1843 if( tutorial_next_button == NULL ) {
1844 textLines(&n_lines, &max_wid, card->getNextText().c_str(), l_w, l_w);
1845 tutorial_next_button = new Button(rect.x + rect.w - max_wid, rect.y + rect.h + 4, card->getNextText().c_str(), game_g->letters_large);
1846 tutorial_next_button->setBackground(0, 0, 0, tutorial_alpha_c);
1847 screen_page->add(tutorial_next_button);
1848 }
1849 }
1850 else {
1851 if( tutorial_next_button != NULL ) {
1852 delete tutorial_next_button;
1853 tutorial_next_button = NULL;
1854 }
1855 }
1856 if( card->autoProceed() && card->canProceed(this) ) {
1857 game_g->getTutorial()->proceed();
1858 const TutorialCard *new_card = game_g->getTutorial()->getCard();
1859 if( new_card != NULL ) {
1860 new_card->setGUI(this);
1861 }
1862 else {
1863 GUIHandler::resetGUI(this);
1864 }
1865 }
1866 }
1867 }
1868
1869 this->gamePanel->draw();
1870 //this->gamePanel->drawPopups();
1871
1872 this->screen_page->draw();
1873 //this->screen_page->drawPopups();
1874
1875 /*if( smokeParticleSystem != NULL ) {
1876 const Building *building_factory = current_sector->getBuilding(BUILDING_FACTORY);
1877 //const Building *building_factory = current_sector->getBuilding(BUILDING_TOWER);
1878 if( building_factory != NULL ) {
1879 const SmokeParticleSystem *ps = ( current_sector->getWorkers() > 0 ) ? smokeParticleSystem_busy : smokeParticleSystem;
1880 ps->draw(offset_land_x_c + building_factory->getX() + 17, offset_land_y_c + building_factory->getY() + 2);
1881 }
1882 }*/
1883 if( current_sector->getSmokeParticleSystem() != NULL ) {
1884 const Building *building_factory = current_sector->getBuilding(BUILDING_FACTORY);
1885 //const Building *building_factory = current_sector->getBuilding(BUILDING_TOWER);
1886 if( building_factory != NULL ) {
1887 current_sector->getSmokeParticleSystem()->draw(offset_land_x_c + building_factory->getX() + 17, offset_land_y_c + building_factory->getY() + 2);
1888 }
1889 }
1890
1891 if( text_effect != NULL && text_effect->render() ) {
1892 delete text_effect;
1893 text_effect = NULL;
1894 }
1895
1896 // mouse pointer
1897 GameState::setDefaultMouseImage();
1898 mouse_off_x = 0;
1899 mouse_off_y = 0;
1900 if( game_g->getGameStateID() == GAMESTATEID_PLAYING ) {
1901 GamePanel::MouseState mousestate = gamePanel->getMouseState();
1902 if( mousestate == GamePanel::MOUSESTATE_DEPLOY_WEAPON || selected_army != NULL ) {
1903 ASSERT( mousestate != GamePanel::MOUSESTATE_DEPLOY_WEAPON || selected_army == NULL );
1904 bool bloody = false;
1905 const Sector *this_sector = ( selected_army == NULL ) ? current_sector : selected_army->getSector();
1906 if( this_sector->getPlayer() != client_player ) {
1907 if( this_sector->getPlayer() != PLAYER_NONE && !Player::isAlliance(this_sector->getPlayer(), client_player) )
1908 bloody = true;
1909 for(int i=0;i<n_players_c && !bloody;i++) {
1910 if( i != client_player && !Player::isAlliance(i, client_player) ) {
1911 if( this_sector->getArmy(i)->any(true) )
1912 bloody = true;
1913 }
1914 }
1915 }
1916 if( bloody )
1917 mouse_image = game_g->panel_bloody_attack;
1918 else
1919 mouse_image = game_g->panel_attack;
1920 mobile_ui_display_mouse = true;
1921 }
1922 else if( mousestate == GamePanel::MOUSESTATE_DEPLOY_DEFENCE ) {
1923 mouse_image = game_g->panel_defence;
1924 //m_x -= mouse_image->getScaledWidth() / 2;
1925 //m_y -= mouse_image->getScaledHeight() / 2;
1926 mouse_off_x = - mouse_image->getScaledWidth() / 2;
1927 mouse_off_y = - mouse_image->getScaledHeight() / 2;
1928 mobile_ui_display_mouse = true;
1929 }
1930 else if( mousestate == GamePanel::MOUSESTATE_DEPLOY_SHIELD ) {
1931 mouse_image = game_g->panel_shield;
1932 mobile_ui_display_mouse = true;
1933 //m_x -= mouse_image->getScaledWidth() / 2;
1934 //m_y -= mouse_image->getScaledHeight() / 2;
1935 mouse_off_x = - mouse_image->getScaledWidth() / 2;
1936 mouse_off_y = - mouse_image->getScaledHeight() / 2;
1937 }
1938 else if( mousestate == GamePanel::MOUSESTATE_SHUTDOWN ) {
1939 mouse_image = game_g->men[n_epochs_c-1];
1940 mouse_off_x = - mouse_image->getScaledWidth() / 2;
1941 mouse_off_y = - mouse_image->getScaledHeight() / 2;
1942 mobile_ui_display_mouse = true;
1943 }
1944 }
1945
1946 GameState::draw();
1947 }
1948
update()1949 void PlayingGameState::update() {
1950 /*if( this->smokeParticleSystem != NULL ) {
1951 if( current_sector->getWorkers() > 0 ) {
1952 this->smokeParticleSystem->setBirthRate(0.008f);
1953 }
1954 else {
1955 this->smokeParticleSystem->setBirthRate(0.004f);
1956 }
1957 }*/
1958 int move_soldier_step_x = 0;
1959 int move_soldier_step_y = 0;
1960 int move_cannon_step_x = 0;
1961 int move_cannon_step_y = 0;
1962 int move_air_step = 0;
1963 if( soldier_last_time_moved_x == -1 )
1964 soldier_last_time_moved_x = game_g->getGameTime();
1965 if( soldier_last_time_moved_y == -1 )
1966 soldier_last_time_moved_y = game_g->getGameTime();
1967 if( cannon_last_time_moved_x == -1 )
1968 cannon_last_time_moved_x = game_g->getGameTime();
1969 if( cannon_last_time_moved_y == -1 )
1970 cannon_last_time_moved_y = game_g->getGameTime();
1971 if( air_last_time_moved == -1 )
1972 air_last_time_moved = game_g->getGameTime();
1973 // move twice as fast in x direction, to simulate 3D look
1974 while( game_g->getGameTime() - soldier_last_time_moved_x > soldier_move_rate_c ) {
1975 move_soldier_step_x++;
1976 soldier_last_time_moved_x += soldier_move_rate_c;
1977 }
1978 while( game_g->getGameTime() - soldier_last_time_moved_y > 2*soldier_move_rate_c ) {
1979 move_soldier_step_y++;
1980 soldier_last_time_moved_y += 2*soldier_move_rate_c;
1981 }
1982 while( game_g->getGameTime() - cannon_last_time_moved_x > cannon_move_rate_c ) {
1983 move_cannon_step_x++;
1984 cannon_last_time_moved_x += cannon_move_rate_c;
1985 }
1986 while( game_g->getGameTime() - cannon_last_time_moved_y > 2*cannon_move_rate_c ) {
1987 move_cannon_step_y++;
1988 cannon_last_time_moved_y += 2*cannon_move_rate_c;
1989 }
1990 while( game_g->getGameTime() - air_last_time_moved > air_move_rate_c ) {
1991 move_air_step++;
1992 air_last_time_moved += air_move_rate_c;
1993 }
1994 /*bool move_soldiers = ( game_g->getGameTime() - soldiers_last_time_moved > soldier_move_rate_c );
1995 bool move_air = ( game_g->getGameTime() - air_last_time_moved > air_move_rate_c );
1996 if( move_soldiers )
1997 soldiers_last_time_moved = game_g->getGameTime();
1998 if( move_air )
1999 air_last_time_moved = game_g->getGameTime();*/
2000 int time_interval = game_g->getLoopTime();
2001
2002 int n_armies = 0;
2003 for(int i=0;i<n_players_c;i++) {
2004 const Army *army = current_sector->getArmy(i);
2005 if( army->getTotal() > 0 )
2006 n_armies++;
2007 }
2008 bool combat = false;
2009 if( n_armies >= 2 || ( current_sector->getPlayer() != -1 && current_sector->enemiesPresent() ) ) {
2010 combat = true;
2011 }
2012
2013 int fire_prob = poisson(soldier_turn_rate_c, time_interval);
2014 for(int i=0;i<n_players_c;i++) {
2015 //for(int j=0;j<n_soldiers[i];j++) {
2016 for(size_t j=0;j<soldiers[i].size();j++) {
2017 //Soldier *soldier = soldiers[i][j];
2018 //Soldier *soldier = (Soldier *)soldiers[i]->get(j);
2019 Soldier *soldier = soldiers[i].at(j);
2020 //if( soldier->epoch == 6 || soldier->epoch == 7 || soldier->epoch == 9 ) {
2021 if( isAirUnit(soldier->epoch) ) {
2022 // air unit
2023 if( move_air_step > 0 ) {
2024 soldier->xpos -= move_air_step;
2025 soldier->ypos -= move_air_step;
2026 while( soldier->xpos < - offset_land_x_c - 32 )
2027 soldier->xpos += default_width_c + 64;
2028 while( soldier->ypos < - offset_land_y_c - 32 )
2029 soldier->ypos += default_height_c + 64;
2030 }
2031 if( combat ) {
2032 int fire_random = rand() % RAND_MAX;
2033 if( fire_random <= fire_prob ) {
2034 // fire!
2035 AmmoEffect *ammoeffect = new AmmoEffect( this, soldier->epoch, ATTACKER_AMMO_BOMB, soldier->xpos + 4, soldier->ypos + 8 );
2036 this->ammo_effects.push_back(ammoeffect);
2037 }
2038 }
2039 }
2040 else {
2041 if( !validSoldierLocation(soldier->epoch,soldier->xpos, soldier->ypos) ) {
2042 /* Soldier is already invalid location. This usually happens if the scenery suddenly
2043 * changes (eg, new building appearing). If this happens, find a new valid locaation.
2044 */
2045 bool found_loc = false;
2046 while(!found_loc) {
2047 soldier->xpos = rand() % land_width_c;
2048 soldier->ypos = rand() % land_height_c;
2049 found_loc = validSoldierLocation(soldier->epoch, soldier->xpos, soldier->ypos);
2050 }
2051 }
2052 /* Turns are modelled as a Poisson distribution - so soldier_turn_rate_c is the mean number of
2053 * ticks that elapse per turn. Therefore we are interested in the probability that at least one
2054 * turn occured within this time interval.
2055 */
2056 /*double prob = 1.0 - exp( - ((double)time_interval) / soldier_turn_rate_c );
2057 double random = ((double)( rand() % RAND_MAX )) / (double)RAND_MAX;*/
2058 //double prob = RAND_MAX * ( 1.0 - exp( - ((double)time_interval) / soldier_turn_rate_c ) );
2059 int prob = poisson(soldier_turn_rate_c, time_interval);
2060 int random = rand() % RAND_MAX;
2061 if( random <= prob ) {
2062 // turn!
2063 soldier->dir = (AmmoDirection)(rand() % 4);
2064 }
2065 int move_step = 0;
2066 if( soldier->epoch == cannon_epoch_c )
2067 move_step = (soldier->dir == 0 || soldier->dir == 1) ? move_cannon_step_y : move_cannon_step_x;
2068 else
2069 move_step = (soldier->dir == 0 || soldier->dir == 1) ? move_soldier_step_y : move_soldier_step_x;
2070 if( move_step > 0 ) {
2071 int step_x = 0;
2072 int step_y = 0;
2073 if( soldier->dir == 0 )
2074 step_y = move_step;
2075 else if( soldier->dir == 1 )
2076 step_y = - move_step;
2077 else if( soldier->dir == 2 )
2078 step_x = move_step;
2079 else if( soldier->dir == 3 )
2080 step_x = - move_step;
2081
2082 int new_xpos = soldier->xpos + step_x;
2083 int new_ypos = soldier->ypos + step_y;
2084 if( !validSoldierLocation(soldier->epoch,new_xpos, new_ypos) ) {
2085 // path blocked, so turn around
2086 new_xpos = soldier->xpos;
2087 new_ypos = soldier->ypos;
2088 if( soldier->dir == 0 )
2089 soldier->dir = (AmmoDirection)1;
2090 else if( soldier->dir == 1 )
2091 soldier->dir = (AmmoDirection)0;
2092 else if( soldier->dir == 2 )
2093 soldier->dir = (AmmoDirection)3;
2094 else if( soldier->dir == 3 )
2095 soldier->dir = (AmmoDirection)2;
2096 }
2097 soldier->xpos = new_xpos;
2098 soldier->ypos = new_ypos;
2099 }
2100
2101 if( combat && soldier->epoch != n_epochs_c ) {
2102 int fire_random = rand() % RAND_MAX;
2103 if( fire_random <= fire_prob ) {
2104 // fire!
2105 Image *image = game_g->attackers_walking[soldier->player][soldier->epoch][soldier->dir][0];
2106 int xpos = 0, ypos = 0;
2107 if( soldier->epoch == cannon_epoch_c ) {
2108 xpos = soldier->xpos;
2109 ypos = soldier->ypos;
2110 if( soldier->dir == ATTACKER_AMMO_LEFT ) {
2111 xpos = soldier->xpos;
2112 ypos = soldier->ypos;
2113 }
2114 else if( soldier->dir == ATTACKER_AMMO_RIGHT ) {
2115 xpos = soldier->xpos + image->getScaledWidth();
2116 ypos = soldier->ypos;
2117 }
2118 else if( soldier->dir == ATTACKER_AMMO_UP ) {
2119 xpos = soldier->xpos + image->getScaledWidth()/4;
2120 ypos = soldier->ypos;
2121 }
2122 else if( soldier->dir == ATTACKER_AMMO_DOWN ) {
2123 xpos = soldier->xpos + image->getScaledWidth()/4;
2124 ypos = soldier->ypos + image->getScaledHeight();
2125 }
2126 }
2127 else {
2128 xpos = soldier->xpos + image->getScaledWidth()/2;
2129 ypos = soldier->ypos;
2130 }
2131 AmmoEffect *ammoeffect = new AmmoEffect( this, soldier->epoch, soldier->dir, xpos, ypos );
2132 this->ammo_effects.push_back(ammoeffect);
2133 }
2134 }
2135 }
2136 }
2137 }
2138 }
2139
buildingMouseClick(int s_m_x,int s_m_y,bool m_left,bool m_right,Building * building)2140 bool PlayingGameState::buildingMouseClick(int s_m_x,int s_m_y,bool m_left,bool m_right,Building *building) {
2141 bool done = false;
2142 if( building == NULL )
2143 return done;
2144 if( !m_left && !m_right )
2145 return done;
2146
2147 //Image *base_image = building->getImages()[0];
2148 Image *base_image = building->getImages()[current_sector->getBuildingEpoch()];
2149 ASSERT( base_image != NULL );
2150 if( s_m_x < offset_land_x_c + building->getX() || s_m_x >= offset_land_x_c + building->getX() + base_image->getScaledWidth() ||
2151 s_m_y < offset_land_y_c + building->getY() || s_m_y >= offset_land_y_c + building->getY() + base_image->getScaledHeight() ) {
2152 return done;
2153 }
2154
2155 if( !done && this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_SHUTDOWN && building->getType() == BUILDING_TOWER ) {
2156 //current_sector->shutdown();
2157 this->shutdown(current_sector->getXPos(), current_sector->getYPos());
2158 done = true;
2159 }
2160
2161 for(int i=0;i<building->getNTurrets() && !done;i++) {
2162 if( m_left
2163 /*&& s_m_x >= offset_land_x_c + building->pos_x + building->turret_pos[i].x
2164 && s_m_x < offset_land_x_c + building->pos_x + building->turret_pos[i].getRight()
2165 && s_m_y >= offset_land_y_c + building->pos_y + building->turret_pos[i].y
2166 && s_m_y < offset_land_y_c + building->pos_y + building->turret_pos[i].getBottom()*/
2167 && s_m_x >= building->getTurretButton(i)->getLeft()
2168 && s_m_x < building->getTurretButton(i)->getRight()
2169 && s_m_y >= building->getTurretButton(i)->getTop()
2170 && s_m_y < building->getTurretButton(i)->getBottom()
2171 ) {
2172 done = true;
2173 //int n_population = current_sector->getPopulation();
2174 //int n_spare = current_sector->getSparePopulation();
2175 if( this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_DEFENCE ) {
2176 // set new defender
2177 int deploy_defence = this->getGamePanel()->getDeployDefence();
2178 ASSERT(deploy_defence != -1);
2179 //current_sector->deployDefender(building, i, deploy_defence);
2180 this->deployDefender(current_sector->getXPos(), current_sector->getYPos(), building->getType(), i, deploy_defence);
2181 }
2182 else if( building->getTurretMan(i) != -1 ) {
2183 // remove existing defender
2184 // return current defender to stocks
2185 //current_sector->returnDefender(building, i);
2186 this->returnDefender(current_sector->getXPos(), current_sector->getYPos(), building->getType(), i);
2187 }
2188 }
2189 }
2190
2191 if( !done && this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_SHIELD ) {
2192 if( building->getHealth() < building->getMaxHealth() ) {
2193 int deploy_shield = this->getGamePanel()->getDeployShield();
2194 ASSERT(deploy_shield != -1);
2195 this->useShield(current_sector->getXPos(), current_sector->getYPos(), building->getType(), deploy_shield);
2196 }
2197 }
2198
2199 return done;
2200 }
2201
moveTo(int map_x,int map_y)2202 void PlayingGameState::moveTo(int map_x,int map_y) {
2203 current_sector = game_g->getMap()->getSector(map_x, map_y);
2204 if( this->getGamePanel() != NULL )
2205 this->getGamePanel()->setPage( GamePanel::STATE_SECTORCONTROL );
2206 this->reset();
2207 for(size_t i=0;i<effects.size();i++) {
2208 TimedEffect *effect = effects.at(i);
2209 delete effect;
2210 }
2211 effects.clear();
2212 for(size_t i=0;i<ammo_effects.size();i++) {
2213 TimedEffect *effect = ammo_effects.at(i);
2214 delete effect;
2215 }
2216 ammo_effects.clear();
2217 }
2218
canRequestAlliance(int player,int i) const2219 bool PlayingGameState::canRequestAlliance(int player,int i) const {
2220 ASSERT(player != i);
2221 ASSERT(game_g->players[player] != NULL);
2222 ASSERT(!game_g->players[player]->isDead());
2223 bool ok = true;
2224 // check not already allied
2225 for(int j=0;j<n_players_c && ok;j++) {
2226 if( j == player || game_g->players[j] == NULL || game_g->players[j]->isDead() ) {
2227 }
2228 else if( j == i || Player::isAlliance(i, j) ) {
2229 if( Player::isAlliance(player, j) )
2230 ok = false;
2231 }
2232 }
2233
2234 // check still two sides
2235 bool allied_all_others = ok;
2236 for(int j=0;j<n_players_c && allied_all_others;j++) {
2237 if( j == player || game_g->players[j] == NULL || game_g->players[j]->isDead() ) {
2238 }
2239 else if( j == i || Player::isAlliance(i, j) ) {
2240 // player on the side that we are requesting an alliance with
2241 }
2242 else if( !Player::isAlliance(player, j) ) {
2243 allied_all_others = false;
2244 }
2245 }
2246 if( allied_all_others )
2247 ok = false;
2248 return ok;
2249 }
2250
requestAlliance(int player,int i,bool human)2251 void PlayingGameState::requestAlliance(int player,int i,bool human) {
2252 // 'player' requests alliance with 'i'
2253 /*if( !human ) {
2254 // AIs only supported in non-player mode
2255 ASSERT(gameMode == GAMEMODE_SINGLEPLAYER);
2256 }*/
2257 ASSERT(game_g->getGameMode() == GAMEMODE_SINGLEPLAYER); // blocked for now
2258 ASSERT(player != i);
2259 ASSERT(game_g->players[player] != NULL);
2260 ASSERT(!game_g->players[player]->isDead());
2261 //ASSERT(i != human_player); // todo: for requesting with human player
2262 bool ok = true;
2263 bool ask_human_player = false;
2264 int playing_asking_human = -1; // which player do we need to ask the human?
2265 int human_player = -1;
2266 // okay to request?
2267 // check i, and those who are allied with i
2268 for(int j=0;j<n_players_c && ok;j++) {
2269 if( j == player || game_g->players[j] == NULL || game_g->players[j]->isDead() ) {
2270 }
2271 else if( j == i || Player::isAlliance(i, j) ) {
2272 //if( j == human_player ) {
2273 if( game_g->players[j]->isHuman() ) {
2274 // request if human is part of alliance
2275 ask_human_player = true;
2276 playing_asking_human = player;
2277 human_player = j;
2278 }
2279 else if( !game_g->players[j]->requestAlliance(player) ) {
2280 ok = false;
2281 if( human )
2282 playSample(game_g->s_alliance_no[j]);
2283 }
2284 }
2285 }
2286 // check those who are allied with player
2287 for(int j=0;j<n_players_c && ok;j++) {
2288 if( j == player || game_g->players[j] == NULL || game_g->players[j]->isDead() ) {
2289 }
2290 else if( Player::isAlliance(player, j) ) {
2291 //if( j == human_player ) {
2292 if( game_g->players[j]->isHuman() ) {
2293 // request if human is part of alliance
2294 //ok = false;
2295 ask_human_player = true;
2296 playing_asking_human = i;
2297 human_player = j;
2298 }
2299 else if( !game_g->players[j]->requestAlliance(i) ) {
2300 ok = false;
2301 if( human )
2302 playSample(game_g->s_alliance_no[j]);
2303 }
2304 }
2305 }
2306
2307 if( ok && ask_human_player ) {
2308 if( this->player_asking_alliance != -1 ) {
2309 // someone else asking, so don't ask
2310 }
2311 else {
2312 // askHuman() is called to avoid the cpu player repeatedly asking
2313 if( game_g->players[playing_asking_human]->askHuman() && game_g->players[playing_asking_human]->requestAlliance(human_player) ) {
2314 playSample(game_g->s_alliance_ask[playing_asking_human]);
2315 this->player_asking_alliance = playing_asking_human;
2316 //this->reset();
2317 this->setupMapGUI(); // needed to change the map GUI to ask player; call this rather than reset(), to avoid resetting the entire GUI (which causes the GUI to return to main sector control)
2318 }
2319 }
2320 }
2321 else if( ok ) {
2322 if( human )
2323 playSample(game_g->s_alliance_yes[i]);
2324 makeAlliance(player, i);
2325 }
2326 }
2327
makeAlliance(int player,int i)2328 void PlayingGameState::makeAlliance(int player,int i) {
2329 for(int j=0;j<n_players_c;j++) {
2330 if( j == player || game_g->players[j] == NULL || game_g->players[j]->isDead() ) {
2331 }
2332 else if( j == i || Player::isAlliance(i, j) ) {
2333 // bring player j into the alliance
2334 for(int k=0;k<n_players_c;k++) {
2335 if( k != j && ( k == player || Player::isAlliance(k, player) ) ) {
2336 Player::setAlliance(k, j, true);
2337 }
2338 }
2339 }
2340 }
2341 //gamestate->reset(); // reset shield buttons
2342 //((PlayingGameState *)gamestate)->resetShieldButtons(); // needed to update player shield buttons
2343 this->resetShieldButtons(); // needed to update player shield buttons
2344 this->cancelPlayerAskingAlliance(); // need to do this even if AIs make an alliance between themselves, as it may mean the player-alliance is no longer possible!
2345 }
2346
cancelPlayerAskingAlliance()2347 void PlayingGameState::cancelPlayerAskingAlliance() {
2348 if( this->player_asking_alliance != -1 ) {
2349 this->player_asking_alliance = -1;
2350 //this->reset();
2351 this->setupMapGUI(); // call this rather than reset(), to avoid the GUI going back to main sector control!
2352 }
2353 }
2354
refreshTimeRate()2355 void PlayingGameState::refreshTimeRate() {
2356 speed_button->setImage( game_g->icon_speeds[ game_g->getTimeRate()-1 ] );
2357 }
2358
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)2359 void PlayingGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
2360 if( !game_g->isDemo() && game_g->players[client_player]->isDead() ) {
2361 return;
2362 }
2363 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
2364
2365 //bool m_left = mouse_left(m_b);
2366 //bool m_right = mouse_right(m_b);
2367 int s_m_x = (int)(m_x / game_g->getScaleWidth());
2368 int s_m_y = (int)(m_y / game_g->getScaleHeight());
2369
2370 bool done = false;
2371 bool clear_selected_army = true;
2372
2373 //int s_m_x = m_x / 1;
2374 //int s_m_y = m_y / 1;
2375 int map_x = ( s_m_x - offset_map_x_c ) / 16;
2376 int map_y = ( s_m_y - offset_map_y_c ) / 16;
2377 /*if( m_x >= offset_map_x_c && m_x < offset_map_x_c + map_width_c * map_sq->getScaledWidth() &&
2378 m_y >= offset_map_y_c && m_y < offset_map_y_c + map_height_c * map_sq->getScaledHeight() ) {*/
2379 if( !done && m_left && click && this->player_asking_alliance != -1 ) {
2380 ASSERT( this->alliance_yes != NULL );
2381 ASSERT( this->alliance_no != NULL );
2382 if( this->alliance_yes->mouseOver(m_x, m_y) ) {
2383 ASSERT( game_g->players[player_asking_alliance] != NULL );
2384 ASSERT( !game_g->players[player_asking_alliance]->isDead() );
2385 this->makeAlliance(player_asking_alliance, client_player);
2386 // makeAlliance also cancels
2387 done = true;
2388 }
2389 else if( this->alliance_no->mouseOver(m_x, m_y ) ) {
2390 this->cancelPlayerAskingAlliance();
2391 done = true;
2392 }
2393 if( done ) {
2394 registerClick();
2395 }
2396 }
2397 if( !done && click && map_x >= 0 && map_x < map_width_c && map_y >= 0 && map_y < map_height_c ) {
2398 if( this->player_asking_alliance == -1 && map_display == MAPDISPLAY_MAP && game_g->getMap()->isSectorAt(map_x, map_y) && this->map_panels[map_x][map_y]->mouseOver(m_x, m_y) ) {
2399 // although the mouse should always be over the map square, we call mouseOver so that the enabled flag is checked
2400 done = true;
2401 if( m_left && selected_army != NULL ) {
2402 if( selected_army->getSector() != game_g->getMap()->getSector(map_x, map_y) ) {
2403 int n_nukes = selected_army->getSoldiers(nuclear_epoch_c);
2404 ASSERT( n_nukes == 0 );
2405 // move selected army
2406 /*if( game_g->getMap()->getSector(map_x, map_y)->moveArmy(selected_army) ) {
2407 this->moveTo(map_x,map_y);
2408 }*/
2409 if( this->moveArmyTo(selected_army->getSector()->getXPos(), selected_army->getSector()->getYPos(), map_x, map_y) ) {
2410 this->moveTo(map_x, map_y);
2411 }
2412 else {
2413 // (some of) army too far - don't lose selection
2414 clear_selected_army = false;
2415 }
2416 }
2417 }
2418 else if( m_left && this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_WEAPON ) {
2419 // deploy assembled army
2420 ASSERT( current_sector->getAssembledArmy() != NULL );
2421 Sector *target_sector = game_g->getMap()->getSector(map_x, map_y);
2422 if( target_sector->isNuked() ) {
2423 //clear_selected_army = false;
2424 }
2425 else {
2426 int n_nukes = current_sector->getAssembledArmy()->getSoldiers(nuclear_epoch_c);
2427 if( n_nukes > 0 ) {
2428 // nuke!
2429 LOG("nuke sector %d, %d (%d)\n", map_x, map_y, n_nukes);
2430 ASSERT( n_nukes == 1 );
2431 if( target_sector->getActivePlayer() != -1 && target_sector->getPlayer() == current_sector->getPlayer() ) {
2432 // don't nuke own sector
2433 LOG("don't nuke own sector: %d\n", target_sector->getActivePlayer());
2434 }
2435 else if( target_sector->getActivePlayer() != -1 && Player::isAlliance(current_sector->getPlayer(), target_sector->getPlayer()) ) {
2436 // don't nuke allied sectors
2437 LOG("don't nuke allied sector\n");
2438 playSample(game_g->s_cant_nuke_ally);
2439 }
2440 else {
2441 if( this->nukeSector(current_sector->getXPos(), current_sector->getYPos(), map_x, map_y) ) {
2442 this->moveTo(map_x,map_y);
2443 }
2444 }
2445 }
2446 //else if( game_g->getMap()->getSector(map_x, map_y)->moveArmy(current_sector->getAssembledArmy() ) ) {
2447 else if( this->moveAssembledArmyTo(current_sector->getXPos(), current_sector->getYPos(), map_x, map_y) ) {
2448 this->getGamePanel()->setMouseState(GamePanel::MOUSESTATE_NORMAL);
2449 this->moveTo(map_x,map_y);
2450 }
2451 }
2452 }
2453 else if( m_left ) {
2454 //if( game_g->getMap()->sectors[map_x][map_y] != current_sector )
2455 {
2456 // move to viewing a different sector
2457 if( current_sector->getPlayer() == client_player ) {
2458 //current_sector->returnAssembledArmy();
2459 this->returnAssembledArmy(current_sector->getXPos(), current_sector->getYPos());
2460 }
2461 this->getGamePanel()->setMouseState(GamePanel::MOUSESTATE_NORMAL);
2462 this->moveTo(map_x,map_y);
2463 }
2464 }
2465 else if( m_right && !game_g->isDemo() ) {
2466 // select an army
2467 Army *army = game_g->getMap()->getSector(map_x, map_y)->getArmy(client_player);
2468 if( army->getTotal() > 0 ) {
2469 done = true;
2470 selected_army = army;
2471 clear_selected_army = false;
2472 }
2473 }
2474 }
2475 }
2476
2477 if( !done && ( m_left || m_right ) && click && speed_button != NULL && speed_button->mouseOver(m_x, m_y) ) {
2478 done = true;
2479 registerClick();
2480 if( game_g->oneMouseButtonMode() ) {
2481 // cycle through the speeds
2482 game_g->cycleTimeRate();
2483 }
2484 else {
2485 if( m_left ) {
2486 game_g->increaseTimeRate();
2487 }
2488 else if( m_right ) {
2489 game_g->decreaseTimeRate();
2490 }
2491 }
2492 LOG("set time_rate to %d\n", game_g->getTimeRate());
2493 refreshTimeRate();
2494 //processClick(buttonSpeedClick, this->screen_page, this, 0, speed_button, m_left, m_middle, m_right);
2495 }
2496 else if( !done && m_left && click && quit_button != NULL && quit_button->mouseOver(m_x, m_y) ) {
2497 done = true;
2498 registerClick();
2499 requestQuit(false);
2500 }
2501 else if( !done && m_left && click && confirm_button_1 != NULL && confirm_button_1->mouseOver(m_x, m_y) ) {
2502 // save game and quit to desktop
2503 done = true;
2504 registerClick();
2505 ASSERT( confirm_window != NULL );
2506 game_g->saveState();
2507 game_g->getApplication()->setQuit();
2508 }
2509 else if( !done && m_left && click && confirm_button_2 != NULL && confirm_button_2->mouseOver(m_x, m_y) ) {
2510 // exit battle
2511 done = true;
2512 registerClick();
2513 ASSERT( confirm_window != NULL );
2514 this->requestConfirm();
2515 }
2516 else if( !done && m_left && click && confirm_button_3 != NULL && confirm_button_3->mouseOver(m_x, m_y) ) {
2517 // cancel
2518 done = true;
2519 registerClick();
2520 ASSERT( confirm_window != NULL );
2521 this->closeConfirmWindow();
2522 }
2523 else if( !done && m_left && click && pause_button != NULL && pause_button->mouseOver(m_x, m_y) ) {
2524 // should always be non-paused if we are here!
2525 if( !game_g->isPaused() ) {
2526 done = true;
2527 registerClick();
2528 game_g->togglePause();
2529 }
2530 }
2531 else if( !done && m_left && click && tutorial_next_button != NULL && tutorial_next_button->mouseOver(m_x, m_y) ) {
2532 game_g->getTutorial()->proceed();
2533 const TutorialCard *new_card = game_g->getTutorial()->getCard();
2534 if( new_card != NULL ) {
2535 new_card->setGUI(this);
2536 }
2537 else {
2538 GUIHandler::resetGUI(this);
2539 }
2540 delete tutorial_next_button;
2541 tutorial_next_button = NULL;
2542 done = true;
2543 registerClick();
2544 // don't lose selection, important for tutorial:
2545 clear_selected_army = false;
2546 }
2547
2548 // switch map display
2549 for(int i=0;i<n_players_c && !done && m_left && click;i++) {
2550 if( shield_buttons[i] == NULL ) {
2551 continue;
2552 }
2553 ASSERT( shield_number_panels[i] != NULL );
2554 /*int bx = offset_map_x_c + 16 * map_width_c + 20;
2555 int by = offset_map_y_c + 2 + 16 * i + 8;
2556 if( s_m_x >= bx && s_m_x < bx + 16 && s_m_y >= by && s_m_y < by + 16 ) {*/
2557 if( shield_number_panels[i]->mouseOver(m_x, m_y) ) {
2558 bool ok = false;
2559 for(int j=i;j<n_players_c && !ok;j++) {
2560 if( j == i || Player::isAlliance(i, j) ) {
2561 const Army *army = current_sector->getArmy(j);
2562 if( army->getTotal() > 0 )
2563 ok = true;
2564 }
2565 }
2566
2567 if( ok ) {
2568 done = true;
2569 registerClick();
2570 if( this->map_display == MAPDISPLAY_MAP )
2571 this->map_display = MAPDISPLAY_UNITS;
2572 else if( this->map_display == MAPDISPLAY_UNITS )
2573 this->map_display = MAPDISPLAY_MAP;
2574 this->reset();
2575 }
2576 }
2577 }
2578
2579 // alliances
2580 for(int i=0;i<n_players_c && !done && m_left && click && !game_g->isDemo();i++) {
2581 if( i != client_player && game_g->players[i] != NULL && !game_g->players[i]->isDead() ) {
2582 if( shield_buttons[i] != NULL && shield_buttons[i]->mouseOver(m_x, m_y) ) {
2583 if( this->player_asking_alliance != -1 && this->player_asking_alliance == i ) {
2584 // automatically accept
2585 this->makeAlliance(client_player, i);
2586 }
2587 else {
2588 // request alliance
2589 if( canRequestAlliance(client_player, i) ) {
2590 requestAlliance(client_player, i, true);
2591 }
2592 }
2593 // automatically cancel any alliance being asked for
2594 if( this->player_asking_alliance != -1 ) {
2595 this->cancelPlayerAskingAlliance();
2596 }
2597 done = true;
2598 }
2599 }
2600 }
2601 if( !done && m_left && click && shield_blank_button != NULL && shield_blank_button->mouseOver(m_x, m_y) ) {
2602 // break alliance
2603 bool any = false;
2604 for(int i=0;i<n_players_c;i++) {
2605 if( i != client_player && Player::isAlliance(i, client_player) ) {
2606 Player::setAlliance(i, client_player, false);
2607 any = true;
2608 }
2609 }
2610 ASSERT( any );
2611 //gamestate->reset(); // reset shield buttons
2612 //((PlayingGameState *)gamestate)->resetShieldButtons(); // needed to update player shield buttons
2613 this->resetShieldButtons(); // needed to update player shield buttons
2614 done = true;
2615 }
2616
2617 //if( !done && s_m_x >= offset_land_x_c + 16 && s_m_y >= offset_land_y_c ) {
2618 if( !done && click && !game_g->isDemo() && this->land_panel->mouseOver(m_x, m_y) ) {
2619 const Army *army_in_sector = current_sector->getArmy(client_player);
2620 Building *building = current_sector->getBuilding(BUILDING_TOWER);
2621 bool clicked_fortress = building != NULL && s_m_x >= offset_land_x_c + building->getX() && s_m_x < offset_land_x_c + building->getX() + game_g->fortress[ current_sector->getBuildingEpoch() ]->getScaledWidth() &&
2622 s_m_y >= offset_land_y_c + building->getY() && s_m_y < offset_land_y_c + building->getY() + game_g->fortress[ current_sector->getBuildingEpoch() ]->getScaledHeight();
2623 if( m_left ) {
2624 if( this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_WEAPON ) {
2625 ASSERT( current_sector->getAssembledArmy() != NULL );
2626 int n_nukes = current_sector->getAssembledArmy()->getSoldiers(nuclear_epoch_c);
2627 if( n_nukes == 0 ) {
2628 // deploy assembled army
2629 done = true;
2630 registerClick();
2631 //army_in_sector->add( current_sector->getAssembledArmy() );
2632 this->moveAssembledArmyTo(current_sector->getXPos(), current_sector->getYPos(), current_sector->getXPos(), current_sector->getYPos());
2633 this->getGamePanel()->setMouseState(GamePanel::MOUSESTATE_NORMAL);
2634 }
2635 else {
2636 // can't nuke own sector!
2637 int n_selected = current_sector->getAssembledArmy()->getTotal();
2638 ASSERT( n_nukes == 1 );
2639 ASSERT( n_selected == n_nukes );
2640 }
2641 }
2642 else if( selected_army != NULL ) {
2643 done = true;
2644 registerClick();
2645 // move selected army
2646 if( clicked_fortress && current_sector->getPlayer() == selected_army->getPlayer() ) {
2647 this->returnArmy(current_sector->getXPos(), current_sector->getYPos(), selected_army->getSector()->getXPos(), selected_army->getSector()->getYPos());
2648 }
2649 else {
2650 if( selected_army->getSector() != current_sector ) {
2651 // move selected army
2652 this->moveArmyTo(selected_army->getSector()->getXPos(), selected_army->getSector()->getYPos(), current_sector->getXPos(), current_sector->getYPos());
2653 }
2654 }
2655 }
2656 }
2657 if( !done && ( game_g->oneMouseButtonMode() ? m_left : m_right ) && !clicked_fortress && army_in_sector->getTotal() > 0 ) {
2658 done = true;
2659 registerClick();
2660 //selected_army = army_in_sector;
2661 selected_army = game_g->getMap()->getSector(current_sector->getXPos(), current_sector->getYPos())->getArmy(client_player);
2662 clear_selected_army = false;
2663 if( current_sector->getPlayer() == client_player ) {
2664 //current_sector->returnAssembledArmy();
2665 this->returnAssembledArmy(current_sector->getXPos(), current_sector->getYPos());
2666 }
2667 this->getGamePanel()->setMouseState(GamePanel::MOUSESTATE_NORMAL);
2668 }
2669 }
2670
2671 if( current_sector->getPlayer() == client_player && click ) {
2672 for(int i=0;i<N_BUILDINGS && !done;i++) {
2673 done = buildingMouseClick(s_m_x, s_m_y, m_left, m_right, current_sector->getBuilding((Type)i));
2674 }
2675 }
2676
2677 if( !done )
2678 this->getGamePanel()->input(m_x, m_y, m_left, m_middle, m_right, click);
2679
2680 if( clear_selected_army && ( m_left || m_right ) && click ) {
2681 selected_army = NULL;
2682 refreshButtons();
2683 }
2684 else if( selected_army != NULL ) {
2685 refreshButtons();
2686 }
2687
2688 }
2689
requestQuit(bool force_quit)2690 void PlayingGameState::requestQuit(bool force_quit) {
2691 if( force_quit ) {
2692 game_g->saveState();
2693 game_g->getApplication()->setQuit();
2694 }
2695 else {
2696 this->createQuitWindow();
2697 }
2698 }
2699
requestConfirm()2700 void PlayingGameState::requestConfirm() {
2701 if( confirm_window != NULL ) {
2702 this->closeConfirmWindow();
2703 if( !game_g->isStateChanged() ) {
2704 game_g->setGameResult(GAMERESULT_QUIT);
2705 game_g->fadeMusic(1000);
2706 game_g->setStateChanged(true);
2707 this->fadeScreen(true, 0, endIsland_g);
2708 }
2709 }
2710 }
2711
validSoldierLocation(int epoch,int xpos,int ypos)2712 bool PlayingGameState::validSoldierLocation(int epoch,int xpos,int ypos) {
2713 ASSERT_S_EPOCH(epoch);
2714 bool okay = true;
2715 if( epoch == 6 || epoch == 7 || epoch == 9 )
2716 return true;
2717
2718 //int size_x = attackers_walking[0][ epoch ][0]->getScaledWidth();
2719 //int size_y = attackers_walking[0][ epoch ][0]->getScaledHeight();
2720 int size_x = game_g->attackers_walking[0][ epoch ][0][0]->getScaledWidth();
2721 int size_y = game_g->attackers_walking[0][ epoch ][0][0]->getScaledHeight();
2722 if( xpos < 0 || xpos + size_x >= land_width_c || ypos < 0 || ypos + size_y >= land_height_c )
2723 okay = false;
2724 else if( current_sector->getPlayer() != -1 ) {
2725 for(int i=0;i<N_BUILDINGS && okay;i++) {
2726 Building *building = current_sector->getBuilding((Type)i);
2727 if( building == NULL )
2728 continue;
2729 Image *image = building->getImages()[current_sector->getBuildingEpoch()];
2730 if( xpos + size_x >= building->getX() && xpos < building->getX() + image->getScaledWidth() &&
2731 ypos + size_y >= building->getY() && ypos < building->getY() + image->getScaledHeight() )
2732 okay = false;
2733 }
2734 if( okay && openPitMine() && xpos + size_x >= offset_openpitmine_x_c && xpos < offset_openpitmine_x_c + game_g->icon_openpitmine->getScaledWidth() &&
2735 ypos + size_y >= offset_openpitmine_y_c && ypos < offset_openpitmine_y_c + game_g->icon_openpitmine->getScaledHeight() )
2736 okay = false;
2737 /*Building *building = current_sector->getBuilding(BUILDING_TOWER);
2738 Building *building_mine = current_sector->getBuilding(BUILDING_MINE);
2739 if( xpos + size_x >= building->pos_x && xpos < building->pos_x + fortress[ current_sector->getBuildingEpoch() ]->getScaledWidth() &&
2740 ypos + size_y >= building->pos_y && ypos < building->pos_y + fortress[ current_sector->getBuildingEpoch() ]->getScaledHeight() )
2741 okay = false;
2742 else if( building_mine != NULL && xpos + size_x >= building_mine->pos_x && xpos < building_mine->pos_x + mine[ current_sector->getBuildingEpoch() ]->getScaledWidth() &&
2743 ypos + size_y >= building_mine->pos_y && ypos < building_mine->pos_y + mine[ current_sector->getBuildingEpoch() ]->getScaledHeight() )
2744 okay = false;
2745 else if( openPitMine() && xpos + size_x >= offset_openpitmine_x_c && xpos < offset_openpitmine_x_c + icon_openpitmine->getScaledWidth() &&
2746 ypos + size_y >= offset_openpitmine_y_c && ypos < offset_openpitmine_y_c + icon_openpitmine->getScaledHeight() )
2747 okay = false;*/
2748 }
2749 return okay;
2750 }
2751
refreshSoldiers(bool flash)2752 void PlayingGameState::refreshSoldiers(bool flash) {
2753 for(int i=0;i<n_players_c;i++) {
2754 int n_soldiers_type[n_epochs_c+1];
2755 for(int j=0;j<=n_epochs_c;j++)
2756 n_soldiers_type[j] = 0;
2757 for(size_t j=0;j<soldiers[i].size();j++) {
2758 Soldier *soldier = soldiers[i].at(j);
2759 n_soldiers_type[ soldier->epoch ]++;
2760 }
2761 const Army *army = current_sector->getArmy(i);
2762 for(int j=0;j<=n_epochs_c;j++) {
2763 int diff = army->getSoldiers(j) - n_soldiers_type[j];
2764 if( diff > 0 ) {
2765 // create some more
2766 for(int k=0;k<diff;k++) {
2767 int xpos = 0, ypos = 0;
2768 bool found_loc = false;
2769 while(!found_loc) {
2770 xpos = rand() % land_width_c;
2771 ypos = rand() % land_height_c;
2772 found_loc = validSoldierLocation(j, xpos, ypos);
2773 }
2774 Soldier *soldier = new Soldier(i, j, xpos, ypos);
2775 soldiers[i].push_back( soldier );
2776 if( flash && !isAirUnit( soldier->epoch ) ) {
2777 blueEffect(offset_land_x_c + soldier->xpos, offset_land_y_c + soldier->ypos, true);
2778 }
2779 }
2780 if( j == biplane_epoch_c ) {
2781 playSample(game_g->s_biplane, SOUND_CHANNEL_BIPLANE, -1); // n.b., doesn't matter if this restarts the currently playing sample
2782 }
2783 else if( j == jetplane_epoch_c ) {
2784 playSample(game_g->s_jetplane, SOUND_CHANNEL_BOMBER, -1); // n.b., doesn't matter if this restarts the currently playing sample
2785 }
2786 else if( j == spaceship_epoch_c ) {
2787 playSample(game_g->s_spaceship, SOUND_CHANNEL_SPACESHIP, -1); // n.b., doesn't matter if this restarts the currently playing sample
2788 }
2789 }
2790 else if( diff < 0 ) {
2791 // remove some
2792 for(size_t k=0;k<soldiers[i].size();) {
2793 Soldier *soldier = soldiers[i].at(k);
2794 if( soldier->epoch == j ) {
2795 if( n_deaths[i][j] > 0 ) {
2796 if( flash && !isAirUnit( soldier->epoch ) ) {
2797 deathEffect(offset_land_x_c + soldier->xpos, offset_land_y_c + soldier->ypos);
2798 if( !isPlaying(SOUND_CHANNEL_FX) ) {
2799 // only play if sound fx channel is free, to avoid too many death samples sounding
2800 playSample(game_g->s_scream, SOUND_CHANNEL_FX);
2801 }
2802 }
2803 n_deaths[i][j]--;
2804 }
2805 else if( flash && !isAirUnit( soldier->epoch ) ) {
2806 blueEffect(offset_land_x_c + soldier->xpos, offset_land_y_c + soldier->ypos, false);
2807 }
2808 soldiers[i].erase(soldiers[i].begin() + k);
2809 delete soldier;
2810 diff++;
2811 if( diff == 0 )
2812 break;
2813 }
2814 else
2815 k++;
2816 }
2817 if( army->getSoldiers(j) == 0 ) {
2818 if( j == biplane_epoch_c ) {
2819 game_g->s_biplane->fadeOut(500);
2820 }
2821 else if( j == jetplane_epoch_c ) {
2822 game_g->s_jetplane->fadeOut(500);
2823 }
2824 else if( j == spaceship_epoch_c ) {
2825 game_g->s_spaceship->fadeOut(500);
2826 }
2827 }
2828 }
2829 }
2830 }
2831
2832 //this->refreshShieldNumberPanels();
2833 }
2834
2835 /*void GameState::clearSoldiers() {
2836 for(int i=0;i<n_players_c;i++) {
2837 n_soldiers[i] = 0;
2838 }
2839 }*/
2840
deathEffect(int xpos,int ypos)2841 void PlayingGameState::deathEffect(int xpos,int ypos) {
2842 AnimationEffect *animationeffect = new AnimationEffect(xpos, ypos, game_g->death_flashes, n_death_flashes_c, 100, true);
2843 this->effects.push_back(animationeffect);
2844 }
2845
blueEffect(int xpos,int ypos,bool dir)2846 void PlayingGameState::blueEffect(int xpos,int ypos,bool dir) {
2847 AnimationEffect *animationeffect = new AnimationEffect(xpos, ypos, game_g->blue_flashes, n_blue_flashes_c, 50, dir);
2848 this->effects.push_back(animationeffect);
2849 }
2850
explosionEffect(int xpos,int ypos)2851 void PlayingGameState::explosionEffect(int xpos,int ypos) {
2852 if( game_g->explosions[0] != NULL ) { // not available with "old" graphics
2853 AnimationEffect *animationeffect = new AnimationEffect(xpos, ypos, game_g->explosions, n_explosions_c, 50, true);
2854 this->effects.push_back(animationeffect);
2855 }
2856 }
2857
refreshButtons()2858 void PlayingGameState::refreshButtons() {
2859 for(int i=0;i<N_BUILDINGS;i++) {
2860 Building *building = current_sector->getBuilding((Type)i);
2861 if( building != NULL ) {
2862 for(int j=0;j<building->getNTurrets();j++) {
2863 PanelPage *panel = building->getTurretButton(j);
2864 panel->setInfoLMB("");
2865 }
2866 if( building->getBuildingButton() != NULL ) {
2867 building->getBuildingButton()->setInfoLMB("");
2868 }
2869
2870 if( current_sector->getPlayer() != client_player ) {
2871 // no text
2872 }
2873 else if( this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_SHUTDOWN ) {
2874 if( building->getBuildingButton() != NULL ) {
2875 building->getBuildingButton()->setInfoLMB("shutdown the sector");
2876 }
2877 }
2878 else {
2879 for(int j=0;j<building->getNTurrets();j++) {
2880 PanelPage *panel = building->getTurretButton(j);
2881 if( this->getGamePanel()->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_DEFENCE ) {
2882 panel->setInfoLMB("place a defender here");
2883 }
2884 else if( building->getTurretMan(j) != -1 ) {
2885 panel->setInfoLMB("return defender to tower"); // todo: check text
2886 }
2887 }
2888 }
2889 }
2890 }
2891
2892 if( selected_army != NULL || this->gamePanel->getMouseState() == GamePanel::MOUSESTATE_DEPLOY_WEAPON ) {
2893 bool is_nukes = current_sector->getAssembledArmy() != NULL && current_sector->getAssembledArmy()->getSoldiers(nuclear_epoch_c) > 0;
2894 this->land_panel->setInfoLMB(is_nukes ? "" : "move army here");
2895 if( this->player_asking_alliance == -1 && this->map_display == MAPDISPLAY_MAP ) {
2896 for(int y=0;y<map_height_c;y++) {
2897 for(int x=0;x<map_width_c;x++) {
2898 if( game_g->getMap()->isSectorAt(x, y) ) {
2899 if( is_nukes ) {
2900 if( game_g->getMap()->getSector(x, y)->getPlayer() == current_sector->getPlayer() || game_g->getMap()->getSector(x, y)->isBeingNuked() || game_g->getMap()->getSector(x, y)->isNuked() ) {
2901 map_panels[x][y]->setInfoLMB("");
2902 }
2903 else {
2904 map_panels[x][y]->setInfoLMB("nuke sector");
2905 }
2906 }
2907 else {
2908 map_panels[x][y]->setInfoLMB("move army to this sector");
2909 }
2910 //map_panels[x][y]->setInfoLMB(!is_nukes ? "move army to this sector" : game_g->getMap()->getSector(x, y) == current_sector ? "" : "nuke sector");
2911 }
2912 }
2913 }
2914 }
2915 }
2916 else {
2917 this->land_panel->setInfoLMB("");
2918 if( this->player_asking_alliance == -1 && this->map_display == MAPDISPLAY_MAP ) {
2919 for(int y=0;y<map_height_c;y++) {
2920 for(int x=0;x<map_width_c;x++) {
2921 if( game_g->getMap()->isSectorAt(x, y) ) {
2922 map_panels[x][y]->setInfoLMB("view this sector");
2923 }
2924 }
2925 }
2926 }
2927 }
2928 }
2929
refreshShieldNumberPanels()2930 void PlayingGameState::refreshShieldNumberPanels() {
2931 if( this->map_display == MAPDISPLAY_MAP ) {
2932 for(int i=0;i<n_players_c;i++) {
2933 if( shield_number_panels[i] != NULL ) {
2934 //shield_number_panels[i]->setVisible(false);
2935 shield_number_panels[i]->setInfoLMB("display numbers in each army");
2936 }
2937 }
2938 }
2939 else {
2940 for(int i=0;i<n_players_c;i++) {
2941 if( shield_number_panels[i] != NULL ) {
2942 shield_number_panels[i]->setInfoLMB("display map");
2943 }
2944 }
2945 }
2946 }
2947
setNDesigners(int sector_x,int sector_y,int n_designers)2948 void PlayingGameState::setNDesigners(int sector_x, int sector_y, int n_designers) {
2949 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
2950 ASSERT(sector != NULL);
2951 if( sector->getActivePlayer() == client_player ) {
2952 if( sector->getCurrentDesign() != NULL ) {
2953 sector->setDesigners(n_designers);
2954 }
2955 }
2956 }
2957
setNWorkers(int sector_x,int sector_y,int n_workers)2958 void PlayingGameState::setNWorkers(int sector_x, int sector_y, int n_workers) {
2959 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
2960 ASSERT(sector != NULL);
2961 if( sector->getActivePlayer() == client_player ) {
2962 if( sector->getCurrentManufacture() != NULL ) {
2963 sector->setWorkers(n_workers);
2964 }
2965 }
2966 }
2967
setFAmount(int sector_x,int sector_y,int n_famount)2968 void PlayingGameState::setFAmount(int sector_x, int sector_y, int n_famount) {
2969 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
2970 ASSERT(sector != NULL);
2971 if( sector->getActivePlayer() == client_player ) {
2972 if( sector->getCurrentManufacture() != NULL ) {
2973 sector->setFAmount(n_famount);
2974 }
2975 }
2976 }
2977
setNMiners(int sector_x,int sector_y,Id element,int n_miners)2978 void PlayingGameState::setNMiners(int sector_x, int sector_y, Id element, int n_miners) {
2979 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
2980 ASSERT(sector != NULL);
2981 if( sector->getActivePlayer() == client_player ) {
2982 if( sector->canMine(element) ) {
2983 sector->setMiners(element, n_miners);
2984 }
2985 }
2986 }
2987
setNBuilders(int sector_x,int sector_y,Type type,int n_builders)2988 void PlayingGameState::setNBuilders(int sector_x, int sector_y, Type type, int n_builders) {
2989 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
2990 ASSERT(sector != NULL);
2991 if( sector->getActivePlayer() == client_player ) {
2992 if( sector->canBuild(type) ) {
2993 sector->setBuilders(type, n_builders);
2994 }
2995 }
2996 }
2997
setCurrentDesign(int sector_x,int sector_y,Design * design)2998 void PlayingGameState::setCurrentDesign(int sector_x, int sector_y, Design *design) {
2999 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3000 ASSERT(sector != NULL);
3001 if( sector->getActivePlayer() == client_player ) {
3002 sector->setCurrentDesign(design);
3003 }
3004 }
3005
setCurrentManufacture(int sector_x,int sector_y,Design * design)3006 void PlayingGameState::setCurrentManufacture(int sector_x, int sector_y, Design *design) {
3007 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3008 ASSERT(sector != NULL);
3009 if( sector->getActivePlayer() == client_player ) {
3010 sector->setCurrentManufacture(design);
3011 }
3012 }
3013
assembledArmyEmpty(int sector_x,int sector_y)3014 void PlayingGameState::assembledArmyEmpty(int sector_x, int sector_y) {
3015 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3016 ASSERT(sector != NULL);
3017 if( sector->getActivePlayer() == client_player ) {
3018 sector->getAssembledArmy()->empty();
3019 }
3020 }
3021
assembleArmyUnarmed(int sector_x,int sector_y,int n)3022 bool PlayingGameState::assembleArmyUnarmed(int sector_x, int sector_y, int n) {
3023 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3024 ASSERT(sector != NULL);
3025 if( sector->getActivePlayer() == client_player ) {
3026 int n_spare = sector->getAvailablePopulation();
3027 if( n_spare >= n ) {
3028 int n_population = sector->getPopulation();
3029 sector->getAssembledArmy()->add(n_epochs_c, n);
3030 sector->setPopulation(n_population - n);
3031 return true;
3032 }
3033 }
3034 return false;
3035 }
3036
assembleArmy(int sector_x,int sector_y,int epoch,int n)3037 bool PlayingGameState::assembleArmy(int sector_x, int sector_y, int epoch, int n) {
3038 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3039 ASSERT(sector != NULL);
3040 if( sector->getActivePlayer() == client_player ) {
3041 if( sector->assembleArmy(epoch, n) ) {
3042 return true;
3043 }
3044 }
3045 return false;
3046 }
3047
assembleAll(int sector_x,int sector_y,bool include_unarmed)3048 bool PlayingGameState::assembleAll(int sector_x, int sector_y, bool include_unarmed) {
3049 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3050 ASSERT(sector != NULL);
3051 if( sector->getActivePlayer() == client_player ) {
3052 sector->assembleAll(include_unarmed);
3053 }
3054 return false;
3055 }
3056
returnAssembledArmy(int sector_x,int sector_y)3057 void PlayingGameState::returnAssembledArmy(int sector_x, int sector_y) {
3058 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3059 ASSERT(sector != NULL);
3060 if( sector->getActivePlayer() == client_player ) {
3061 sector->returnAssembledArmy();
3062 }
3063 }
3064
returnArmy(int sector_x,int sector_y,int src_x,int src_y)3065 bool PlayingGameState::returnArmy(int sector_x, int sector_y, int src_x, int src_y) {
3066 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3067 ASSERT(sector != NULL);
3068 if( sector->getActivePlayer() == client_player ) {
3069 Sector *src = game_g->getMap()->getSector(src_x, src_y);
3070 Army *army = src->getArmy(client_player);
3071 return sector->returnArmy(army);
3072 }
3073 return false;
3074 }
3075
moveArmyTo(int src_x,int src_y,int target_x,int target_y)3076 bool PlayingGameState::moveArmyTo(int src_x, int src_y, int target_x, int target_y) {
3077 Sector *src = game_g->getMap()->getSector(src_x, src_y);
3078 Sector *target = game_g->getMap()->getSector(target_x, target_y);
3079 ASSERT(src != NULL);
3080 ASSERT(target != NULL);
3081 Army *army = src->getArmy(client_player);
3082 return target->moveArmy(army);
3083 }
3084
moveAssembledArmyTo(int src_x,int src_y,int target_x,int target_y)3085 bool PlayingGameState::moveAssembledArmyTo(int src_x, int src_y, int target_x, int target_y) {
3086 Sector *src = game_g->getMap()->getSector(src_x, src_y);
3087 ASSERT(src != NULL);
3088 if( src->getActivePlayer() == client_player ) {
3089 Army *army = src->getAssembledArmy();
3090 Sector *target = game_g->getMap()->getSector(target_x, target_y);
3091 ASSERT(target != NULL);
3092 return target->moveArmy(army);
3093 }
3094 return false;
3095 }
3096
nukeSector(int src_x,int src_y,int target_x,int target_y)3097 bool PlayingGameState::nukeSector(int src_x, int src_y, int target_x, int target_y) {
3098 Sector *src = game_g->getMap()->getSector(src_x, src_y);
3099 Sector *target = game_g->getMap()->getSector(target_x, target_y);
3100 ASSERT(src != NULL);
3101 ASSERT(target != NULL);
3102 if( src->getActivePlayer() == client_player ) {
3103 if( target->nukeSector(src) ) {
3104 this->assembledArmyEmpty(src_x, src_y);
3105 return true;
3106 }
3107 }
3108 return false;
3109 }
3110
deployDefender(int sector_x,int sector_y,Type type,int turret,int epoch)3111 void PlayingGameState::deployDefender(int sector_x, int sector_y, Type type, int turret, int epoch) {
3112 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3113 ASSERT(sector != NULL);
3114 if( sector->getActivePlayer() == client_player ) {
3115 Building *building = sector->getBuilding(type);
3116 if( building != NULL ) {
3117 sector->deployDefender(building, turret, epoch);
3118 }
3119 }
3120 }
3121
returnDefender(int sector_x,int sector_y,Type type,int turret)3122 void PlayingGameState::returnDefender(int sector_x, int sector_y, Type type, int turret) {
3123 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3124 ASSERT(sector != NULL);
3125 if( sector->getActivePlayer() == client_player ) {
3126 Building *building = sector->getBuilding(type);
3127 if( building != NULL ) {
3128 sector->returnDefender(building, turret);
3129 }
3130 }
3131 }
3132
useShield(int sector_x,int sector_y,Type type,int shield)3133 void PlayingGameState::useShield(int sector_x, int sector_y, Type type, int shield) {
3134 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3135 ASSERT(sector != NULL);
3136 if( sector->getActivePlayer() == client_player ) {
3137 Building *building = sector->getBuilding(type);
3138 if( building != NULL ) {
3139 sector->useShield(building, shield);
3140 }
3141 }
3142 }
3143
trashDesign(int sector_x,int sector_y,Invention * invention)3144 void PlayingGameState::trashDesign(int sector_x, int sector_y, Invention *invention) {
3145 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3146 ASSERT(sector != NULL);
3147 if( sector->getActivePlayer() == client_player ) {
3148 sector->trashDesign(invention);
3149 }
3150 }
3151
shutdown(int sector_x,int sector_y)3152 void PlayingGameState::shutdown(int sector_x, int sector_y) {
3153 Sector *sector = game_g->getMap()->getSector(sector_x, sector_y);
3154 ASSERT(sector != NULL);
3155 if( sector->getActivePlayer() == client_player ) {
3156 sector->shutdown(client_player);
3157 }
3158 }
3159
saveState(stringstream & stream) const3160 void PlayingGameState::saveState(stringstream &stream) const {
3161 stream << "<playing_gamestate>\n";
3162 if( game_g->getGameType() == GAMETYPE_TUTORIAL ) {
3163 stream << "<tutorial ";
3164 stream << "name=\"" << game_g->getTutorial()->getId().c_str() << "\" ";
3165 if( game_g->getTutorial()->getCard() != NULL ) {
3166 stream << "current_card_name=\"" << game_g->getTutorial()->getCard()->getId().c_str() << "\" ";
3167 }
3168 stream << "/>\n";
3169 }
3170 stream << "<current_sector x=\"" << current_sector->getXPos() << "\" y=\"" << current_sector->getYPos() << "\" />\n";
3171 stream << "<game_panel page=\"" << this->gamePanel->getPage() << "\" />\n";
3172 stream << "<player_asking_alliance player_id=\"" << player_asking_alliance << "\" />\n";
3173
3174 for(int i=0;i<n_players_c;i++) {
3175 if( game_g->players[i] != NULL ) {
3176 game_g->players[i]->saveState(stream);
3177 }
3178 }
3179 Player::saveStateAlliances(stream);
3180 for(int i=0;i<n_players_c;i++) {
3181 for(int j=0;j<n_epochs_c+1;j++) {
3182 stream << "<n_deaths player_id=\"" << i << "\" epoch=\"" << j << "\" n=\"" << n_deaths[i][j] << "\" />\n";
3183 }
3184 }
3185 game_g->getMap()->saveStateSectors(stream);
3186 stream << "</playing_gamestate>\n";
3187 }
3188
loadStateParseXMLMapXY(int * map_x,int * map_y,const TiXmlAttribute * attribute)3189 void PlayingGameState::loadStateParseXMLMapXY(int *map_x, int *map_y, const TiXmlAttribute *attribute) {
3190 *map_x = -1;
3191 *map_y = -1;
3192 while( attribute != NULL ) {
3193 const char *attribute_name = attribute->Name();
3194 if( strcmp(attribute_name, "x") == 0 ) {
3195 *map_x = atoi(attribute->Value());
3196 }
3197 else if( strcmp(attribute_name, "y") == 0 ) {
3198 *map_y = atoi(attribute->Value());
3199 }
3200 else {
3201 // skip the other sector attributes, only interested in x/y in this subfunction
3202 }
3203 attribute = attribute->Next();
3204 }
3205 if( *map_x < 0 || *map_x >= map_width_c || *map_y < 0 || *map_y >= map_height_c ) {
3206 throw std::runtime_error("current_sector invalid map reference");
3207 }
3208 else if( !game_g->getMap()->isSectorAt(*map_x, *map_y) ) {
3209 throw std::runtime_error("current_sector map reference doesn't exist");
3210 }
3211 }
3212
loadStateParseXMLNode(const TiXmlNode * parent)3213 void PlayingGameState::loadStateParseXMLNode(const TiXmlNode *parent) {
3214 if( parent == NULL ) {
3215 return;
3216 }
3217 bool read_children = true;
3218 //throw std::runtime_error("blah"); // test failing to load state
3219
3220 switch( parent->Type() ) {
3221 case TiXmlNode::TINYXML_DOCUMENT:
3222 break;
3223 case TiXmlNode::TINYXML_ELEMENT:
3224 {
3225 const char *element_name = parent->Value();
3226 const TiXmlElement *element = parent->ToElement();
3227 const TiXmlAttribute *attribute = element->FirstAttribute();
3228 if( strcmp(element_name, "playing_gamestate") == 0 ) {
3229 // handled entirely by caller
3230 }
3231 else if( strcmp(element_name, "tutorial") == 0 ) {
3232 if( game_g->getGameType() != GAMETYPE_TUTORIAL ) {
3233 throw std::runtime_error("wrong game type for tutorial");
3234 }
3235 bool has_card_name = false;
3236 string card_name;
3237 while( attribute != NULL ) {
3238 const char *attribute_name = attribute->Name();
3239 if( strcmp(attribute_name, "name") == 0 ) {
3240 string name = attribute->Value();
3241 game_g->setupTutorial(name);
3242 }
3243 else if( strcmp(attribute_name, "current_card_name") == 0 ) {
3244 has_card_name = true;
3245 card_name = attribute->Value();
3246 }
3247 else {
3248 // don't throw an error here, to help backwards compatibility, but should throw an error in debug mode in case this is a sign of not loading something that we've saved
3249 LOG("unknown playinggamestate/tutorial attribute: %s\n", attribute_name);
3250 ASSERT(false);
3251 }
3252 attribute = attribute->Next();
3253 }
3254 if( game_g->getTutorial() == NULL ) {
3255 throw std::runtime_error("unknown tutorial name");
3256 }
3257 game_g->getTutorial()->initCards();
3258 if( has_card_name ) {
3259 if( !game_g->getTutorial()->jumpTo(card_name) ) {
3260 throw std::runtime_error("unknown tutorial card name");
3261 }
3262 }
3263 else
3264 game_g->getTutorial()->jumpToEnd();
3265 }
3266 else if( strcmp(element_name, "player_asking_alliance") == 0 ) {
3267 while( attribute != NULL ) {
3268 const char *attribute_name = attribute->Name();
3269 if( strcmp(attribute_name, "player_id") == 0 ) {
3270 player_asking_alliance = atoi(attribute->Value());
3271 }
3272 else {
3273 // don't throw an error here, to help backwards compatibility, but should throw an error in debug mode in case this is a sign of not loading something that we've saved
3274 LOG("unknown playinggamestate/player_asking_alliance attribute: %s\n", attribute_name);
3275 ASSERT(false);
3276 }
3277 attribute = attribute->Next();
3278 }
3279 }
3280 else if( strcmp(element_name, "n_deaths") == 0 ) {
3281 int player_id = -1;
3282 int epoch = -1;
3283 int n = -1;
3284 while( attribute != NULL ) {
3285 const char *attribute_name = attribute->Name();
3286 if( strcmp(attribute_name, "player_id") == 0 ) {
3287 player_id = atoi(attribute->Value());
3288 }
3289 else if( strcmp(attribute_name, "epoch") == 0 ) {
3290 epoch = atoi(attribute->Value());
3291 }
3292 else if( strcmp(attribute_name, "n") == 0 ) {
3293 n = atoi(attribute->Value());
3294 }
3295 else {
3296 // don't throw an error here, to help backwards compatibility, but should throw an error in debug mode in case this is a sign of not loading something that we've saved
3297 LOG("unknown playinggamestate/n_deaths attribute: %s\n", attribute_name);
3298 ASSERT(false);
3299 }
3300 attribute = attribute->Next();
3301 }
3302 if( player_id == -1 || epoch == -1 || n == -1 ) {
3303 throw std::runtime_error("n_deaths missing attributes");
3304 }
3305 else if( player_id < 0 || player_id >= n_players_c ) {
3306 throw std::runtime_error("n_deaths invalid player_id");
3307 }
3308 else if( epoch < 0 || epoch >= n_epochs_c+1 ) {
3309 throw std::runtime_error("n_deaths invalid epoch");
3310 }
3311 n_deaths[player_id][epoch] = n;
3312 }
3313 else if( strcmp(element_name, "current_sector") == 0 ) {
3314 int map_x = -1, map_y = -1;
3315 loadStateParseXMLMapXY(&map_x, &map_y, attribute);
3316 this->moveTo(map_x, map_y);
3317 }
3318 else if( strcmp(element_name, "game_panel") == 0 ) {
3319 while( attribute != NULL ) {
3320 const char *attribute_name = attribute->Name();
3321 if( strcmp(attribute_name, "page") == 0 ) {
3322 int page = atoi(attribute->Value());
3323 if( page < 0 || page > GamePanel::N_STATES ) {
3324 throw std::runtime_error("game_panel invalid page");
3325 }
3326 this->gamePanel->setPage(page);
3327 }
3328 else {
3329 // don't throw an error here, to help backwards compatibility, but should throw an error in debug mode in case this is a sign of not loading something that we've saved
3330 LOG("unknown playinggamestate/game_panel attribute: %s\n", attribute_name);
3331 ASSERT(false);
3332 }
3333 attribute = attribute->Next();
3334 }
3335 }
3336 else if( strcmp(element_name, "sector") == 0 ) {
3337 int map_x = -1, map_y = -1;
3338 loadStateParseXMLMapXY(&map_x, &map_y, attribute);
3339 game_g->getMap()->getSector(map_x, map_y)->loadStateParseXMLNode(parent);
3340 read_children = false;
3341 }
3342 else if( strcmp(element_name, "player") == 0 ) {
3343 int player_id = -1;
3344 while( attribute != NULL ) {
3345 const char *attribute_name = attribute->Name();
3346 if( strcmp(attribute_name, "player_id") == 0 ) {
3347 player_id = atoi(attribute->Value());
3348 }
3349 else {
3350 // everything else parsed by Player::loadStateParseXMLNode()
3351 }
3352 attribute = attribute->Next();
3353 }
3354 if( player_id < 0 || player_id >= n_players_c ) {
3355 throw std::runtime_error("player invalid player_id");
3356 }
3357 game_g->players[player_id] = new Player(player_id == this->client_player, player_id);
3358 game_g->players[player_id]->loadStateParseXMLNode(parent);
3359 read_children = false;
3360 }
3361 else if( strcmp(element_name, "player_alliances") == 0 ) {
3362 Player::loadStateParseXMLNodeAlliances(parent);
3363 read_children = false;
3364 }
3365 else {
3366 // don't throw an error here, to help backwards compatibility, but should throw an error in debug mode in case this is a sign of not loading something that we've saved
3367 LOG("unknown playinggamestate tag at line %d col %d: %s\n", parent->Row(), parent->Column(), element_name);
3368 ASSERT(false);
3369 }
3370 }
3371 break;
3372 case TiXmlNode::TINYXML_COMMENT:
3373 break;
3374 case TiXmlNode::TINYXML_UNKNOWN:
3375 break;
3376 case TiXmlNode::TINYXML_TEXT:
3377 {
3378 /*
3379 // add to the existing node, not a child
3380 vi_tree->setData( parent->Value() );
3381 */
3382 /*VI_XMLTreeNode *vi_child = new VI_XMLTreeNode();
3383 vi_child->setData( parent->Value() );
3384 // add to the tree
3385 vi_tree->addChild(vi_child);
3386 vi_tree = vi_child;*/
3387 }
3388 break;
3389 case TiXmlNode::TINYXML_DECLARATION:
3390 break;
3391 }
3392
3393 for(const TiXmlNode *child=parent->FirstChild();child!=NULL && read_children;child=child->NextSibling()) {
3394 loadStateParseXMLNode(child);
3395 }
3396 }
3397
reset()3398 void EndIslandGameState::reset() {
3399 //LOG("EndIslandGameState::reset()\n");
3400 this->screen_page->free(true);
3401 }
3402
draw()3403 void EndIslandGameState::draw() {
3404 #if defined(__ANDROID__)
3405 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
3406 #endif
3407 game_g->background->draw(0, 0);
3408 game_g->getScreen()->fillRectWithAlpha((short)(game_g->getScaleWidth()*40), (short)(game_g->getScaleHeight()*120), (short)(game_g->getScaleWidth()*240), (short)(game_g->getScaleHeight()*70), 0, 0, 0, 127);
3409 char text[4096] = "";
3410 if( game_g->getGameResult() == GAMERESULT_QUIT )
3411 strcpy(text, "QUITTER!");
3412 else if( game_g->getGameResult() == GAMERESULT_LOST )
3413 strcpy(text, "LOSER!");
3414 else if( game_g->getGameResult() == GAMERESULT_WON )
3415 strcpy(text, "CONGRATULATIONS!");
3416 else {
3417 ASSERT(false);
3418 }
3419 Image::write(160, 122, game_g->letters_large, text, Image::JUSTIFY_CENTRE);
3420
3421 bool suspend = false;
3422 if( game_g->getStartEpoch() >= 6 && game_g->getGameResult() == GAMERESULT_WON )
3423 suspend = true;
3424
3425 if( !game_g->isDemo() ) {
3426 if( game_g->player_heads_select[client_player] != NULL ) {
3427 game_g->player_heads_select[client_player]->draw(40, 96);
3428 if( game_g->getGameResult() == GAMERESULT_LOST ) {
3429 game_g->grave->draw(42, 64);
3430 }
3431 }
3432 }
3433 const int xstep = 40;
3434 for(int i=0,xpos=96;i<n_players_c;i++) {
3435 if( i == client_player || game_g->players[i] == NULL )
3436 continue;
3437 if( game_g->player_heads_select[i] != NULL ) {
3438 game_g->player_heads_select[i]->draw(xpos, 96);
3439 if( game_g->getGameResult() == GAMERESULT_WON || game_g->players[i]->getFinalMen() == 0 ) {
3440 game_g->grave->draw(xpos+2, 64);
3441 }
3442 }
3443 xpos += xstep;
3444 }
3445
3446 Image::write(40, 140, game_g->letters_small, "PLAYER", Image::JUSTIFY_LEFT);
3447 Image::write(100, 140, game_g->letters_small, "START", Image::JUSTIFY_LEFT);
3448 Image::write(140, 140, game_g->letters_small, "BIRTHS", Image::JUSTIFY_LEFT);
3449 Image::write(180, 140, game_g->letters_small, "DEATHS", Image::JUSTIFY_LEFT);
3450 Image::write(220, 140, game_g->letters_small, "END", Image::JUSTIFY_LEFT);
3451 if( suspend )
3452 Image::write(260, 140, game_g->letters_small, "SAVED", Image::JUSTIFY_LEFT);
3453
3454 int ypos = 150;
3455 int rect_y_offset = 2;
3456 //int r = 0, g = 0, b = 0, col = 0;
3457 int r = 0, g = 0, b = 0;
3458 int rect_x = (int)(20 * game_g->getScaleWidth());
3459 int rect_y = (int)((ypos-rect_y_offset) * game_g->getScaleHeight());
3460 int rect_w = (int)(16 * game_g->getScaleWidth());
3461 int rect_h = (int)(8 * game_g->getScaleHeight());
3462
3463 if( !game_g->isDemo() ) {
3464 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)client_player);
3465 /*col = SDL_MapRGB(game_g->getScreen()->getSurface()->format, r, g, b);
3466 SDL_FillRect(game_g->getScreen()->getSurface(), &rect, col);*/
3467 game_g->getScreen()->fillRect(rect_x, rect_y, rect_w, rect_h, r, g, b);
3468
3469 //Image::write(40, ypos, game_g->letters_small, "HUMAN", Image::JUSTIFY_LEFT, true);
3470 Image::write(40, ypos, game_g->letters_small, PlayerType::getName((PlayerType::PlayerTypeID)client_player), Image::JUSTIFY_LEFT);
3471
3472 Image::writeNumbers(110, ypos, game_g->numbers_yellow, game_g->players[client_player]->getNMenForThisIsland(), Image::JUSTIFY_LEFT);
3473 Image::writeNumbers(150, ypos, game_g->numbers_yellow, game_g->players[client_player]->getNBirths(), Image::JUSTIFY_LEFT);
3474 Image::writeNumbers(190, ypos, game_g->numbers_yellow, game_g->players[client_player]->getNDeaths(), Image::JUSTIFY_LEFT);
3475 Image::writeNumbers(230, ypos, game_g->numbers_yellow, game_g->players[client_player]->getFinalMen(), Image::JUSTIFY_LEFT);
3476 if( suspend )
3477 Image::writeNumbers(270, ypos, game_g->numbers_yellow, game_g->players[client_player]->getNSuspended(), Image::JUSTIFY_LEFT);
3478 ypos += 10;
3479 }
3480
3481 for(int i=0;i<n_players_c;i++) {
3482 if( i == client_player || game_g->players[i] == NULL )
3483 continue;
3484 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
3485 /*col = SDL_MapRGB(game_g->getScreen()->getSurface()->format, r, g, b);
3486 rect.y = (Sint16)(ypos * game_g->getScaleHeight());
3487 SDL_FillRect(game_g->getScreen()->getSurface(), &rect, col);*/
3488 rect_y = (int)((ypos-rect_y_offset) * game_g->getScaleHeight());
3489 game_g->getScreen()->fillRect(rect_x, rect_y, rect_w, rect_h, r, g, b);
3490
3491 //Image::write(40, ypos, game_g->letters_small, "COMPUTER", Image::JUSTIFY_LEFT, true);
3492 Image::write(40, ypos, game_g->letters_small, PlayerType::getName((PlayerType::PlayerTypeID)i), Image::JUSTIFY_LEFT);
3493 Image::writeNumbers(110, ypos, game_g->numbers_yellow, game_g->players[i]->getNMenForThisIsland(), Image::JUSTIFY_LEFT);
3494 Image::writeNumbers(150, ypos, game_g->numbers_yellow, game_g->players[i]->getNBirths(), Image::JUSTIFY_LEFT);
3495 Image::writeNumbers(190, ypos, game_g->numbers_yellow, game_g->players[i]->getNDeaths(), Image::JUSTIFY_LEFT);
3496 Image::writeNumbers(230, ypos, game_g->numbers_yellow, game_g->players[i]->getFinalMen(), Image::JUSTIFY_LEFT);
3497 if( suspend )
3498 Image::writeNumbers(270, ypos, game_g->numbers_yellow, game_g->players[i]->getNSuspended(), Image::JUSTIFY_LEFT);
3499 ypos += 10;
3500 }
3501
3502 this->screen_page->draw();
3503 //this->screen_page->drawPopups();
3504
3505 GameState::setDefaultMouseImage();
3506 GameState::draw();
3507 }
3508
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)3509 void EndIslandGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
3510 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
3511
3512 //bool m_left = mouse_left(m_b);
3513 //bool m_right = mouse_right(m_b);
3514
3515 if( ( m_left || m_right ) && click && !game_g->isStateChanged() ) {
3516 this->requestQuit(false);
3517 }
3518 }
3519
requestQuit(bool force_quit)3520 void EndIslandGameState::requestQuit(bool force_quit) {
3521 if( force_quit ) {
3522 game_g->saveState();
3523 game_g->getApplication()->setQuit();
3524 }
3525 else {
3526 game_g->setStateChanged(true);
3527 this->fadeScreen(true, 0, returnToChooseIsland_g);
3528 }
3529 }
3530
reset()3531 void GameCompleteGameState::reset() {
3532 //LOG("GameCompleteGameState::reset()\n");
3533 this->screen_page->free(true);
3534 }
3535
draw()3536 void GameCompleteGameState::draw() {
3537 #if defined(__ANDROID__)
3538 game_g->getScreen()->clear(); // SDL on Android requires screen be cleared (otherwise we get corrupt regions outside of the main area)
3539 #endif
3540 game_g->background->draw(0, 0);
3541
3542 this->screen_page->draw();
3543 //this->screen_page->drawPopups();
3544
3545 if( !game_g->isDemo() ) {
3546 stringstream str;
3547 int l_h = game_g->letters_large[0]->getScaledHeight();
3548 int y = 80;
3549
3550 Image::writeMixedCase(160, y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, "GAME COMPLETE", Image::JUSTIFY_CENTRE);
3551 y += l_h + 2;
3552
3553 if( game_g->getDifficultyLevel() == DIFFICULTY_EASY )
3554 str.str("Easy");
3555 else if( game_g->getDifficultyLevel() == DIFFICULTY_MEDIUM )
3556 str.str("Medium");
3557 else if( game_g->getDifficultyLevel() == DIFFICULTY_HARD )
3558 str.str("Hard");
3559 else if( game_g->getDifficultyLevel() == DIFFICULTY_ULTRA )
3560 str.str("Ultra");
3561 else {
3562 ASSERT(false);
3563 }
3564 Image::writeMixedCase(160, y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, str.str().c_str(), Image::JUSTIFY_CENTRE);
3565 y += l_h + 2;
3566
3567 y += l_h + 2;
3568
3569 str << "Men Remaining " << game_g->getMenAvailable();
3570 Image::writeMixedCase(160, y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, str.str().c_str(), Image::JUSTIFY_CENTRE);
3571 y += l_h + 2;
3572
3573 str.str("");
3574 str << "Men Saved " << game_g->getNSuspended();
3575 Image::writeMixedCase(160, y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, str.str().c_str(), Image::JUSTIFY_CENTRE);
3576 y += l_h + 2;
3577
3578 int score = game_g->getMenAvailable() + game_g->getNSuspended();
3579 str.str("");
3580 str << "Total Score " << score;
3581 Image::writeMixedCase(160, y, game_g->letters_large, game_g->letters_small, game_g->numbers_white, str.str().c_str(), Image::JUSTIFY_CENTRE);
3582 y += l_h + 2;
3583 }
3584
3585 GameState::setDefaultMouseImage();
3586 GameState::draw();
3587 }
3588
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)3589 void GameCompleteGameState::mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click) {
3590 GameState::mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
3591
3592 //bool m_left = mouse_left(m_b);
3593 //bool m_right = mouse_right(m_b);
3594
3595 if( ( m_left || m_right ) && click && !game_g->isStateChanged() ) {
3596 this->requestQuit(false);
3597 }
3598 }
3599
requestQuit(bool force_quit)3600 void GameCompleteGameState::requestQuit(bool force_quit) {
3601 if( force_quit ) {
3602 game_g->saveState();
3603 game_g->getApplication()->setQuit();
3604 }
3605 else {
3606 game_g->setStateChanged(true);
3607 this->fadeScreen(true, 0, startNewGame_g);
3608 }
3609 }
3610