1 //---------------------------------------------------------------------------
2 #include "stdafx.h"
3
4 #include <cassert>
5 #include <ctime>
6 #include <cerrno> // n.b., needed on Linux at least
7
8 #include <stdexcept> // needed for Android at least
9
10 #ifdef _WIN32
11 #include <io.h> // for access
12 #define access _access
13 #elif __DragonFly__
14 #include <unistd.h> // for access
15 #endif
16
17 #include <sstream>
18 using std::stringstream;
19
20 #ifdef _WIN32
21 #include <windows.h>
22 #endif
23
24 #ifndef _WIN32
25 #include <dirent.h>
26 #include <string.h>
27 #endif
28
29 #ifdef AROS
30 #include <proto/dos.h>
31 #endif
32
33 #undef min
34 #undef max
35
36 #include "game.h"
37 #include "utils.h"
38 #include "sector.h"
39 #include "gamestate.h"
40 #include "gui.h"
41 #include "player.h"
42 #include "tutorial.h"
43
44 #include "screen.h"
45 #include "image.h"
46 #include "sound.h"
47
48 const bool default_pref_sound_on_c = true;
49 const bool default_pref_music_on_c = true;
50 const bool default_pref_disallow_nukes_c = false;
51
Game()52 Game::Game() {
53 TrackedObject::initialise();
54
55 scale_factor_w = 1.0f;
56 scale_factor_h = 1.0f;
57 scale_width = 0.0f;
58 scale_height = 0.0f;
59 // onemousebutton means UI can be used with one mouse button only
60 #if defined(__ANDROID__)
61 onemousebutton = true;
62 #else
63 onemousebutton = false;
64 #endif
65 // mobile_ui means no mouse pointer
66 #if defined(__ANDROID__)
67 mobile_ui = true;
68 #else
69 mobile_ui = false;
70 #endif
71 using_old_gfx = false;
72 is_testing = false;
73
74 application = NULL;
75 screen = NULL;
76 paused = false;
77 gamestate = NULL;
78 dispose_gamestate = NULL;
79 lastmousepress_time = 0;
80
81 frame_counter = 0;
82 time_rate = 1;
83 real_time = 0;
84 real_loop_time = 0;
85 game_time = 0;
86 loop_time = 0;
87 accumulated_time = 0.0f;
88 mouseTime = -1;
89
90 pref_sound_on = default_pref_sound_on_c;
91 pref_music_on = default_pref_music_on_c;
92 pref_disallow_nukes = default_pref_disallow_nukes_c;
93
94 gameMode = GAMEMODE_SINGLEPLAYER;
95 gameType = GAMETYPE_SINGLEISLAND;
96 difficulty_level = DIFFICULTY_EASY;
97 human_player = 0;
98 tutorial = NULL;
99
100 gameStateID = GAMESTATEID_UNDEFINED;
101 state_changed = false;
102 gameResult = GAMERESULT_UNDEFINED;
103
104 start_epoch = 0;
105 n_sub_epochs = 4;
106 selected_island = 0;
107 for(int i=0;i<max_islands_per_epoch_c;i++) {
108 completed_island[i] = false;
109 for(int j=0;j<n_epochs_c;j++) {
110 maps[j][i] = NULL;
111 }
112 }
113 map = NULL;
114 n_men_store = 0;
115 n_player_suspended = 0;
116
117 background = NULL;
118 for(int i=0;i<n_players_c;i++) {
119 player_heads_select[i] = NULL;
120 player_heads_alliance[i] = NULL;
121 }
122 grave = NULL;
123 for(int i=0;i<MAP_N_COLOURS;i++)
124 land[i] = NULL;
125 for(int i=0;i<n_epochs_c;i++) {
126 fortress[i] = NULL;
127 mine[i] = NULL;
128 factory[i] = NULL;
129 lab[i] = NULL;
130 men[i] = NULL;
131 }
132 unarmed_man = NULL;
133 for(int i=0;i<n_players_c;i++) {
134 for(int j=0;j<n_flag_frames_c;j++) {
135 flags[i][j] = NULL;
136 }
137 }
138 panel_design = NULL;
139 panel_lab = NULL;
140 panel_factory = NULL;
141 panel_shield = NULL;
142 panel_defence = NULL;
143 panel_attack = NULL;
144 panel_bloody_attack = NULL;
145 panel_twoattack = NULL;
146 for(int i=0;i<N_BUILDINGS;i++) {
147 panel_build[i] = NULL;
148 panel_building[i] = NULL;
149 }
150 panel_knowndesigns = NULL;
151 panel_bigdesign = NULL;
152 panel_biglab = NULL;
153 panel_bigfactory = NULL;
154 panel_bigshield = NULL;
155 panel_bigdefence = NULL;
156 panel_bigattack = NULL;
157 panel_bigbuild = NULL;
158 panel_bigknowndesigns = NULL;
159 for(int i=0;i<10;i++) {
160 numbers_blue[i] = NULL;
161 numbers_grey[i] = NULL;
162 numbers_white[i] = NULL;
163 numbers_orange[i] = NULL;
164 numbers_yellow[i] = NULL;
165 numbers_largegrey[i] = NULL;
166 numbers_largeshiny[i] = NULL;
167 for(int j=0;j<n_players_c;j++)
168 numbers_small[j][i] = NULL;
169 }
170 numbers_half = NULL;
171 for(int i=0;i<n_font_chars_c;i++) {
172 letters_large[i] = NULL;
173 letters_small[i] = NULL;
174 }
175 for(int i=0;i<n_players_c;i++)
176 mouse_pointers[i] = NULL;
177 for(int i=0;i<n_playershields_c;i++)
178 playershields[i] = NULL;
179 building_health = NULL;
180 dash_grey = NULL;
181 icon_shield = NULL;
182 icon_defence = NULL;
183 icon_weapon = NULL;
184 for(int i=0;i<n_shields_c;i++)
185 icon_shields[i] = NULL;
186 for(int i=0;i<n_epochs_c;i++) {
187 icon_defences[i] = NULL;
188 icon_weapons[i] = NULL;
189 numbered_defences[i] = NULL;
190 numbered_weapons[i] = NULL;
191 }
192 for(int i=0;i<N_ID;i++) {
193 icon_elements[i] = NULL;
194 }
195 for(int i=0;i<12;i++) {
196 icon_clocks[i] = NULL;
197 }
198 icon_infinity = NULL;
199 icon_bc = NULL;
200 icon_ad = NULL;
201 icon_ad_shiny = NULL;
202 for(int i=0;i<n_players_c;i++) {
203 icon_towers[i] = NULL;
204 icon_armies[i] = NULL;
205 }
206 icon_nuke_hole = NULL;
207 mine_gatherable_small = NULL;
208 mine_gatherable_large = NULL;
209 icon_ergo = NULL;
210 icon_trash = NULL;
211 for(int i=0;i<n_coast_c;i++)
212 coast_icons[i] = NULL;
213 map_sq_offset = 0;
214 map_sq_coast_offset = 0;
215 for(int i=0;i<MAP_N_COLOURS;i++) {
216 for(int j=0;j<n_map_sq_c;j++)
217 map_sq[i][j] = NULL;
218 }
219 for(int i=0;i<n_epochs_c;i++) {
220 n_defender_frames[i] = 0;
221 }
222 for(int i=0;i<n_players_c;i++) {
223 for(int j=0;j<n_epochs_c;j++) {
224 for(int k=0;k<max_defender_frames_c;k++) {
225 defenders[i][j][k] = NULL;
226 }
227 }
228 nuke_defences[i] = NULL;
229 }
230 for(int i=0;i<n_epochs_c+1;i++) {
231 for(int j=0;j<n_attacker_directions_c;j++) {
232 n_attacker_frames[i][j] = 0;
233 }
234 }
235 for(int i=0;i<n_players_c;i++) {
236 for(int j=0;j<n_epochs_c+1;j++) {
237 for(int k=0;k<n_attacker_directions_c;k++) {
238 for(int l=0;l<max_attacker_frames_c;l++) {
239 attackers_walking[i][j][k][l] = NULL;
240 }
241 }
242 }
243 for(int j=0;j<n_epochs_c;j++) {
244 planes[i][j] = NULL;
245 }
246 for(int j=0;j<n_nuke_frames_c;j++) {
247 nukes[i][j] = NULL;
248 saucers[i][j] = NULL;
249 }
250 }
251 for(int i=0;i<n_epochs_c;i++) {
252 for(int j=0;j<N_ATTACKER_AMMO_DIRS;j++)
253 attackers_ammo[i][j] = NULL;
254 }
255 icon_openpitmine = NULL;
256 for(int i=0;i<n_trees_c;i++) {
257 for(int j=0;j<n_tree_frames_c;j++)
258 icon_trees[i][j] = NULL;
259 }
260 flashingmapsquare = NULL;
261 mapsquare = NULL;
262 arrow_left = NULL;
263 arrow_right = NULL;
264 for(int i=0;i<n_death_flashes_c;i++)
265 death_flashes[i] = NULL;
266 for(int i=0;i<n_blue_flashes_c;i++)
267 blue_flashes[i] = NULL;
268 for(int i=0;i<n_explosions_c;i++)
269 explosions[i] = NULL;
270 for(int i=0;i<2;i++)
271 icon_mice[i] = NULL;
272 for(int i=0;i<3;i++)
273 icon_speeds[i] = NULL;
274 smoke_image = NULL;
275 background_islands = NULL;
276
277 // speech
278 s_design_is_ready = NULL;
279 s_ergo = NULL;
280 s_advanced_tech = NULL;
281 s_fcompleted = NULL;
282 s_on_hold = NULL;
283 s_running_out_of_elements = NULL;
284 s_tower_critical = NULL;
285 s_sector_destroyed = NULL;
286 s_mine_destroyed = NULL;
287 s_factory_destroyed = NULL;
288 s_lab_destroyed = NULL;
289 s_itis_all_over = NULL;
290 s_conquered = NULL;
291 s_won = NULL;
292 s_weve_nuked_them = NULL;
293 s_weve_been_nuked = NULL;
294 for(int i=0;i<n_players_c;i++) {
295 s_alliance_yes[i] = NULL;
296 s_alliance_no[i] = NULL;
297 s_alliance_ask[i] = NULL;
298 s_quit[i] = NULL;
299 }
300 s_cant_nuke_ally = NULL;
301
302 // effects
303 s_explosion = NULL;
304 s_scream = NULL;
305 s_buildingdestroyed = NULL;
306 s_guiclick = NULL;
307 s_biplane = NULL;
308 s_jetplane = NULL;
309 s_spaceship = NULL;
310
311 music = NULL;
312
313 for(int i=0;i<n_epochs_c;i++) {
314 invention_shields[i] = NULL;
315 invention_defences[i] = NULL;
316 invention_weapons[i] = NULL;
317 }
318 for(int i=0;i<N_ID;i++) {
319 elements[i] = NULL;
320 }
321 for(int i=0;i<n_players_c;i++) {
322 players[i] = NULL;
323 }
324 }
325
~Game()326 Game::~Game() {
327 if( gamestate != NULL ) {
328 LOG("delete gamestate %d\n", gamestate);
329 delete gamestate;
330 gamestate = NULL;
331 }
332 if( tutorial != NULL ) {
333 LOG("delete tutorial\n");
334 delete tutorial;
335 tutorial = NULL;
336 }
337 LOG("delete maps\n");
338 for(int i=0;i<n_epochs_c;i++) {
339 for(int j=0;j<max_islands_per_epoch_c;j++) {
340 if( maps[i][j] != NULL ) {
341 delete maps[i][j];
342 maps[i][j] = NULL;
343 }
344 }
345 }
346 map = NULL;
347 if( screen != NULL ) {
348 LOG("delete screen %d\n", screen);
349 delete screen;
350 screen = NULL;
351 }
352 LOG("clean up tracked objects\n");
353 TrackedObject::cleanup();
354 // no longer need to stop music, as it's deleted as a TrackedObject
355 //stopMusic();
356 LOG("free sound\n");
357 freeSound();
358 LOG("delete application %d\n", application);
359 delete application;
360 LOG("exiting...\n");
361 cleanupLogFile();
362 }
363
oneMouseButtonMode() const364 bool Game::oneMouseButtonMode() const {
365 return onemousebutton || application->isBlankMouse();
366 }
367
368 Game *game_g = NULL;
369
370 const char *maps_dirname = "islands";
371 #if !defined(__ANDROID__) && defined(__DragonFly__)
372 const char *alt_maps_dirname = "/usr/local/share/gigalomania/islands";
373 #endif
374
375 //bool use_amigadata = true;
376
377 const unsigned char shadow_alpha_c = (unsigned char)160;
378
379 const int epoch_dates[n_epochs_c] = {-10000, -2000, 1, 900, 1400, 1850, 1914, 1950, 1980, 2100};
380 const char *epoch_names[n_epochs_c] = { "FIRST", "SECOND", "THIRD", "FOURTH", "FIFTH", "SIXTH", "SEVENTH", "EIGHTH", "NINTH", "TENTH" };
381
isDemo() const382 bool Game::isDemo() const {
383 return human_player == PLAYER_DEMO;
384 }
385
386 const char autosave_filename[] = "autosave.sav";
387 const char autosave_bad_filename[] = "autosave_bad.sav";
388 const char autosave_old_filename[] = "autosave_old.sav";
389 const bool autosave_survive_uninstall = false; // important for autosave state to be deleted upon uninstall if possible, so that any problems can be fixed by a reinstall
390
validDifficulty(DifficultyLevel difficulty)391 bool validDifficulty(DifficultyLevel difficulty) {
392 return difficulty >= 0 && difficulty < DIFFICULTY_N_LEVELS;
393 }
394
getMenPerEpoch() const395 int Game::getMenPerEpoch() const {
396 ASSERT( gameType == GAMETYPE_ALLISLANDS );
397 if( difficulty_level == DIFFICULTY_EASY )
398 return 150;
399 else if( difficulty_level == DIFFICULTY_MEDIUM )
400 return 120;
401 else if( difficulty_level == DIFFICULTY_HARD )
402 return 100;
403 else if( difficulty_level == DIFFICULTY_ULTRA )
404 return 75;
405 ASSERT(false);
406 return 0;
407 }
408
getMenAvailable() const409 int Game::getMenAvailable() const {
410 if( start_epoch == end_epoch_c && gameType == GAMETYPE_ALLISLANDS )
411 return n_player_suspended;
412 return n_men_store;
413 }
414
getNSuspended() const415 int Game::getNSuspended() const {
416 return n_player_suspended;
417 }
418
getMap() const419 const Map *Game::getMap() const {
420 return map;
421 }
422
getMap()423 Map *Game::getMap() {
424 return map;
425 }
426
Map(MapColour colour,int n_opponents,const char * name)427 Map::Map(MapColour colour,int n_opponents,const char *name) {
428 //*this->filename = '\0';
429 this->colour = colour;
430 this->n_opponents = n_opponents;
431 for(int x=0;x<map_width_c;x++) {
432 for(int y=0;y<map_height_c;y++) {
433 sector_at[x][y] = false;
434 sectors[x][y] = NULL;
435 reserved[x][y] = false;
436 //panels[x][y] = NULL;
437 }
438 }
439 /*for(int i=0;i<N_ID;i++) {
440 this->elements[i] = 0;
441 }*/
442 //clearTemp();
443 /*strncpy(this->name,name,MAP_MAX_NAME);
444 this->name[MAP_MAX_NAME] = '\0';*/
445 this->name = name;
446 }
447
~Map()448 Map::~Map() {
449 freeSectors();
450 }
451
452 /*void Map::clearTemp() {
453 for(int x=0;x<map_width_c;x++) {
454 for(int y=0;y<map_height_c;y++) {
455 temp[x][y] = false;
456 }
457 }
458 }*/
459
getSector(int x,int y) const460 const Sector *Map::getSector(int x, int y) const {
461 ASSERT(x >= 0 && x < map_width_c && y >= 0 && y < map_height_c);
462 Sector *sector = this->sectors[x][y];
463 return sector;
464 }
465
getSector(int x,int y)466 Sector *Map::getSector(int x, int y) {
467 ASSERT(x >= 0 && x < map_width_c && y >= 0 && y < map_height_c);
468 Sector *sector = this->sectors[x][y];
469 return sector;
470 }
471
isSectorAt(int x,int y) const472 bool Map::isSectorAt(int x, int y) const {
473 ASSERT(x >= 0 && x < map_width_c && y >= 0 && y < map_height_c);
474 return this->sector_at[x][y];
475 }
476
newSquareAt(int x,int y)477 void Map::newSquareAt(int x,int y) {
478 ASSERT(x >= 0 && x < map_width_c && y >= 0 && y < map_height_c);
479 this->sector_at[x][y] = true;
480 }
481
createSectors(PlayingGameState * gamestate,int epoch)482 void Map::createSectors(PlayingGameState *gamestate, int epoch) {
483 ASSERT_EPOCH(epoch);
484 for(int x=0;x<map_width_c;x++) {
485 for(int y=0;y<map_height_c;y++) {
486 if( sector_at[x][y] ) {
487 this->sectors[x][y] = new Sector(gamestate, epoch, x, y, this->getColour());
488 /*for(int i=0;i<N_ID;i++) {
489 this->sectors[x][y]->setElements(i, this->elements[i]);
490 }*/
491 }
492 }
493 }
494 }
495
496 #if 0
497 void Map::checkSectors() const {
498 //LOG("Map::checkSectors()\n");
499 #ifdef _WIN32
500 for(int x=0;x<map_width_c;x++) {
501 for(int y=0;y<map_height_c;y++) {
502 if( sectors[x][y] != NULL ) {
503 //LOG("check sector at %d, %d : %d\n", x, y, sectors[x][y]);
504 if( !_CrtIsValidHeapPointer( sectors[x][y] ) ) {
505 LOG("invalid sector at %d, %d\n", x, y);
506 LOG(" ptr %d\n", sectors[x][y]);
507 ASSERT( false );
508 }
509 }
510 }
511 }
512 #endif
513 //LOG("Map::checkSectors exit\n");
514 }
515 #endif
516
freeSectors()517 void Map::freeSectors() {
518 //LOG("Map::freeSectors()\n");
519 for(int x=0;x<map_width_c;x++) {
520 for(int y=0;y<map_height_c;y++) {
521 if( sectors[x][y] != NULL ) {
522 //LOG("free sector at %d, %d\n", x, y);
523 //LOG(" ptr %d\n", sectors[x][y]);
524 delete sectors[x][y];
525 sectors[x][y] = NULL;
526 }
527 }
528 }
529 //current_sector = NULL;
530 //LOG("Map::freeSectors exit\n");
531 }
532
533 /*void Map::setElements(int id,int n_elements) {
534 ASSERT_ELEMENT_ID(id);
535 this->elements[id] = n_elements;
536 }*/
537
findRandomSector(int * rx,int * ry) const538 void Map::findRandomSector(int *rx,int *ry) const {
539 while(true) {
540 int x = rand() % map_width_c;
541 int y = rand() % map_height_c;
542 if( sector_at[x][y] ) {
543 *rx = x;
544 *ry = y;
545 break;
546 }
547 }
548 }
549
canMoveTo(bool temp[map_width_c][map_height_c],int sx,int sy,int player) const550 void Map::canMoveTo(bool temp[map_width_c][map_height_c], int sx,int sy,int player) const {
551 ASSERT(player != -1 );
552 ASSERT(this->sector_at[sx][sy]);
553 //clearTemp();
554 //bool temp[map_width_c][map_height_c];
555
556 for(int x=0;x<map_width_c;x++) {
557 for(int y=0;y<map_height_c;y++) {
558 temp[x][y] = false;
559 }
560 }
561
562 temp[sx][sy] = true;
563 bool changed = false;
564 do {
565 changed = false;
566
567 for(int x=0;x<map_width_c;x++) {
568 for(int y=0;y<map_height_c;y++) {
569 if( temp[x][y] ) {
570 // can we move through this square?
571 if( ( x == sx && y == sy ) ||
572 sectors[x][y]->getPlayer() == player ||
573 ( sectors[x][y]->getPlayer() == -1 &&
574 !sectors[x][y]->enemiesPresent(player) ) ) {
575 for(int c=0;c<4;c++) {
576 int cx = x, cy = y;
577 if( c == 0 )
578 cy--;
579 else if( c == 1 )
580 cx++;
581 else if( c == 2 )
582 cy++;
583 else if( c == 3 )
584 cx--;
585 if( cx >= 0 && cy >= 0 && cx < map_width_c && cy < map_height_c
586 && sector_at[cx][cy]
587 && !sectors[x][y]->isNuked()
588 && !temp[cx][cy] ) {
589 temp[cx][cy] = true;
590 changed = true;
591 }
592 }
593 }
594 }
595 }
596 }
597 } while( changed );
598 }
599
calculateStats() const600 void Map::calculateStats() const {
601 // deaths = start + births - remaining
602 for(int i=0;i<n_players_c;i++) {
603 if( game_g->players[i] != NULL ) {
604 game_g->players[i]->setNDeaths( game_g->players[i]->getNMenForThisIsland() + game_g->players[i]->getNBirths() );
605 game_g->players[i]->setNSuspended(0);
606 }
607 }
608 for(int x=0;x<map_width_c;x++) {
609 for(int y=0;y<map_height_c;y++) {
610 if( this->sector_at[x][y] ) {
611 Sector *sector = this->sectors[x][y];
612 if( sector->getPlayer() != -1 && game_g->players[sector->getPlayer()] != NULL ) {
613 // check for players[sector->getPlayer()] not being NULL should be redundant, but just to be safe
614 game_g->players[sector->getPlayer()]->addNDeaths( - sector->getPopulation() );
615 if( sector->isShutdown() && game_g->getGameResult() == GAMERESULT_WON ) {
616 game_g->players[sector->getPlayer()]->addNSuspended(sector->getPopulation());
617 }
618 }
619 for(int i=0;i<n_players_c;i++) {
620 if( game_g->players[i] != NULL ) {
621 game_g->players[i]->addNDeaths( - sector->getArmy(i)->getTotalMen() );
622 }
623 }
624 }
625 }
626 }
627 /*for(int i=0;i<n_players_c;i++) {
628 if( players[i] != NULL )
629 ASSERT( players[i]->n_deaths >= 0 );
630 }*/
631 }
632
saveStateSectors(stringstream & stream) const633 void Map::saveStateSectors(stringstream &stream) const {
634 for(int x=0;x<map_width_c;x++) {
635 for(int y=0;y<map_height_c;y++) {
636 if( this->sector_at[x][y] ) {
637 Sector *sector = this->sectors[x][y];
638 sector->saveState(stream);
639 }
640 }
641 }
642 }
643
644 /*bool Map::mapIs(char *that_name) {
645 //return strcmp( this->name, that_name ) == 0;
646 return this->name == that_name;
647 }*/
648
getNSquares() const649 int Map::getNSquares() const {
650 int n_squares = 0;
651 for(int y=0;y<map_height_c;y++) {
652 for(int x=0;x<map_width_c;x++) {
653 if( this->sector_at[x][y] ) {
654 n_squares++;
655 }
656 }
657 }
658 return n_squares;
659 }
660
draw(int offset_x,int offset_y) const661 void Map::draw(int offset_x, int offset_y) const {
662 for(int y=0;y<map_height_c;y++) {
663 for(int x=0;x<map_width_c;x++) {
664 if( this->sector_at[x][y] ) {
665 int icon = 0;
666 if( y > 0 && this->sector_at[x][y-1] )
667 icon += 1;
668 if( x < map_width_c-1 && this->sector_at[x+1][y] )
669 icon += 2;
670 if( y < map_height_c-1 && this->sector_at[x][y+1] )
671 icon += 4;
672 if( x > 0 && this->sector_at[x-1][y] )
673 icon += 8;
674 ASSERT( icon >= 0 && icon < 16 );
675 if( game_g->map_sq[colour][icon] == NULL ) {
676 LOG("map name: %s\n", this->getName());
677 LOG("ERROR map icon not available [%d,%d]: %d, %d\n", x, y, colour, icon);
678 ASSERT( game_g->map_sq[colour][icon] != NULL );
679 }
680 int map_x = offset_x - game_g->getMapSqOffset() + 16 * x;
681 int map_y = offset_y - game_g->getMapSqOffset() + 16 * y;
682 int coast_map_x = offset_x - game_g->getMapSqCoastOffset() + 16 * x;
683 int coast_map_y = offset_y - game_g->getMapSqCoastOffset() + 16 * y;
684 //LOG("draw at: %d, %d : %d, %d\n", x, y, map_sq[colour][icon]->getWidth(), map_sq[colour][icon]->getHeight());
685 game_g->map_sq[colour][icon]->draw(map_x, map_y);
686 //LOG(">>> %d %d %d\n", icon, icon & 1, 4 & 1);
687 if( (icon & 1) == 0 && game_g->coast_icons[0] != NULL )
688 game_g->coast_icons[0]->draw(coast_map_x, coast_map_y);
689 if( (icon & 2) == 0 && game_g->coast_icons[1] != NULL )
690 game_g->coast_icons[1]->draw(coast_map_x, coast_map_y);
691 if( (icon & 4) == 0 && game_g->coast_icons[2] != NULL )
692 game_g->coast_icons[2]->draw(coast_map_x, coast_map_y);
693 if( (icon & 8) == 0 && game_g->coast_icons[3] != NULL )
694 game_g->coast_icons[3]->draw(coast_map_x, coast_map_y);
695
696 // now do corners
697 if( x > 0 && y > 0 && this->sector_at[x-1][y] && this->sector_at[x][y-1] && !this->sector_at[x-1][y-1] && game_g->coast_icons[4] != NULL )
698 game_g->coast_icons[4]->draw(coast_map_x, coast_map_y);
699 if( x < map_width_c-1 && y > 0 && this->sector_at[x+1][y] && this->sector_at[x][y-1] && !this->sector_at[x+1][y-1] && game_g->coast_icons[5] != NULL )
700 game_g->coast_icons[5]->draw(coast_map_x, coast_map_y);
701 if( x > 0 && y < map_height_c-1 && this->sector_at[x-1][y] && this->sector_at[x][y+1] && !this->sector_at[x-1][y+1] && game_g->coast_icons[6] != NULL )
702 game_g->coast_icons[6]->draw(coast_map_x, coast_map_y);
703 if( x < map_width_c-1 && y < map_height_c-1 && this->sector_at[x+1][y] && this->sector_at[x][y+1] && !this->sector_at[x+1][y+1] && game_g->coast_icons[7] != NULL )
704 game_g->coast_icons[7]->draw(coast_map_x, coast_map_y);
705 }
706 }
707 }
708 }
709
updatedEpoch()710 void Game::updatedEpoch() {
711 ASSERT( start_epoch >= 0 && start_epoch < n_epochs_c );
712 n_sub_epochs = 4;
713 //if( start_epoch == n_epochs_c-1 )
714 if( start_epoch == end_epoch_c )
715 n_sub_epochs = 0;
716 else if( start_epoch + n_sub_epochs > n_epochs_c ) {
717 n_sub_epochs = n_epochs_c - start_epoch;
718 }
719 }
720
setCurrentIsand(int start_epoch,int selected_island)721 void Game::setCurrentIsand(int start_epoch, int selected_island) {
722 this->start_epoch = start_epoch;
723 this->selected_island = selected_island;
724 this->updatedEpoch();
725 this->setCurrentMap();
726 }
727
setEpoch(int epoch)728 void Game::setEpoch(int epoch) {
729 LOG("set epoch %d\n", epoch);
730 ASSERT( epoch >= 0 && epoch < n_epochs_c );
731 start_epoch = epoch;
732 updatedEpoch();
733
734 selected_island = 0;
735
736 if( gameType == GAMETYPE_ALLISLANDS ) {
737 // skip islands we've completed
738 while( completed_island[selected_island] ) {
739 selected_island++;
740 if( selected_island == max_islands_per_epoch_c || maps[start_epoch][selected_island] == NULL ) {
741 LOG("error, should be at least one island on this epoch that isn't completed\n");
742 ASSERT( false );
743 }
744 }
745 }
746
747 map = maps[start_epoch][selected_island];
748 ASSERT( map != NULL );
749 gamestate->reset();
750 }
751
drawProgress(int percentage) const752 void Game::drawProgress(int percentage) const {
753 const int width = (int)(screen->getWidth() * 0.25f);
754 const int height = (int)(screen->getHeight()/15.0f);
755 const int xpos = (int)(screen->getWidth()*0.5f - width*0.5f);
756 const int ypos = (int)(screen->getHeight()*0.5f - height*0.5f);
757
758 screen->clear(); // n.b., needed for Qt/Symbian, where background defaults to white
759 screen->fillRect(xpos, ypos, width+1, height+1, 255, 255, 255);
760 int progress_width = (int)(((width-1) * percentage) / 100.0f);
761 screen->fillRect(xpos+1, ypos+1, progress_width, height-1, 183, 28, 28);
762
763 screen->refresh();
764 }
765
newGame()766 void Game::newGame() {
767 gamestate->fadeScreen(false, 0, NULL);
768 LOG("newGame()\n");
769 ASSERT( gameStateID == GAMESTATEID_PLACEMEN );
770 if( gameType == GAMETYPE_SINGLEISLAND )
771 n_men_store = 1000;
772 else
773 n_men_store = getMenPerEpoch();
774
775 for(int i=0;i<max_islands_per_epoch_c;i++)
776 completed_island[i] = false;
777 //completed_island[0] = true;
778
779 n_player_suspended = 0;
780
781 setEpoch(0);
782
783 //n_men_store = 1000;
784 //setEpoch(9);
785 }
786
setClientPlayer(int set_client_player)787 void Game::setClientPlayer(int set_client_player) {
788 this->human_player = set_client_player;
789 if( gamestate != NULL ) {
790 gamestate->setClientPlayer(set_client_player);
791 }
792 }
793
nextEpoch()794 void Game::nextEpoch() {
795 LOG("nextEpoch()\n");
796 start_epoch++;
797 if( start_epoch == n_epochs_c ) {
798 ASSERT( gameType == GAMETYPE_SINGLEISLAND );
799 start_epoch = 0;
800 }
801 for(int i=0;i<max_islands_per_epoch_c;i++)
802 completed_island[i] = false;
803 setEpoch(start_epoch);
804 }
805
nextIsland()806 void Game::nextIsland() {
807 if( gameType == GAMETYPE_SINGLEISLAND ) {
808 selected_island++;
809 if( selected_island == max_islands_per_epoch_c || maps[start_epoch][selected_island] == NULL )
810 selected_island = 0;
811 }
812 else {
813 // skip islands we've completed
814 do {
815 selected_island++;
816 if( selected_island == max_islands_per_epoch_c || maps[start_epoch][selected_island] == NULL )
817 selected_island = 0;
818 } while( completed_island[selected_island] );
819 }
820 LOG("next island: %d\n", selected_island);
821
822 map = maps[start_epoch][selected_island];
823 LOG("map name: %s\n", map==NULL ? "NULL?!" : map->getName());
824 gamestate->reset();
825 LOG("done reset\n");
826 }
827
loadGameInfo(DifficultyLevel * difficulty,int * player,int * n_men,int suspended[n_players_c],int * epoch,bool completed[max_islands_per_epoch_c],const char * filename) const828 bool Game::loadGameInfo(DifficultyLevel *difficulty, int *player, int *n_men, int suspended[n_players_c], int *epoch, bool completed[max_islands_per_epoch_c], const char *filename) const {
829 //LOG("loadGameInfo(%d)\n",slot);
830 ASSERT( gameStateID == GAMESTATEID_PLACEMEN );
831
832 //LOG("loading: %s\n", filename);
833 FILE *file = fopen(filename, "rb+");
834 if( file == NULL ) {
835 //LOG("FAILED to open file\n");
836 return false;
837 }
838
839 const int bufsize = 1024;
840 char buffer[bufsize+1] = "";
841 if( fgets(buffer, bufsize, file) == NULL ) { // header line
842 return false;
843 }
844
845 // data line
846 /*for(int i=0;i<8;i++) {
847 buffer[i] = fgetc(file);
848 }*/
849 for(int i=0;;) {
850 ASSERT( i <= bufsize );
851 //buffer[i] = fgetc(file);
852 int c = fgetc(file);
853 if( c == EOF ) {
854 buffer[i] = '\0';
855 break;
856 }
857 buffer[i] = (char)c;
858 i++;
859 }
860
861 char *ptr = buffer;
862
863 *difficulty = (DifficultyLevel)*((int *)ptr);
864 ptr += sizeof(int);
865 if( !validDifficulty( *difficulty ) )
866 return false;
867
868 *player = *((int *)ptr);
869 ptr += sizeof(int);
870 if( !validPlayer( *player ) )
871 return false;
872
873 *n_men = *((int *)ptr);
874 ptr += sizeof(int);
875 if( *n_men < 0 )
876 return false;
877
878 for(int i=0;i<n_players_c;i++) {
879 suspended[i] = *((int *)ptr);
880 ptr += sizeof(int);
881 if( suspended[i] < 0 )
882 return false;
883 }
884
885 *epoch = *((int *)ptr);
886 ptr += sizeof(int);
887 if( *epoch < 0 || *epoch >= n_epochs_c )
888 return false;
889
890 for(int i=0;i<max_islands_per_epoch_c;i++) {
891 int val = *((int *)ptr);
892 if( val != 0 && val != 1 )
893 return false;
894 completed[i] = (val==1);
895 ptr += sizeof(int);
896 }
897
898 fclose(file);
899 return true;
900 }
901
loadGame(const char * filename)902 bool Game::loadGame(const char *filename) {
903 LOG("loadGame(%s)\n",filename);
904 ASSERT( gameType == GAMETYPE_ALLISLANDS );
905 ASSERT( gameStateID == GAMESTATEID_PLACEMEN );
906
907 DifficultyLevel temp_difficulty = DIFFICULTY_EASY;
908 int temp_player = 0;
909 int temp_n_men_store = 0;
910 int temp_start_epoch = 0;
911 int temp_suspended[n_players_c];
912 bool temp_completed[max_islands_per_epoch_c];
913 if( loadGameInfo(&temp_difficulty, &temp_player, &temp_n_men_store, temp_suspended, &temp_start_epoch, temp_completed, filename) ) {
914 difficulty_level = temp_difficulty;
915 setClientPlayer(temp_player);
916 n_men_store = temp_n_men_store;
917 n_player_suspended = temp_suspended[temp_player];
918 for(int i=0;i<max_islands_per_epoch_c;i++)
919 completed_island[i] = temp_completed[i];
920 setEpoch(temp_start_epoch);
921 return true;
922 }
923 return false;
924 }
925
getFilename(int slot) const926 const char *Game::getFilename(int slot) const {
927 char name[300] = "";
928 sprintf(name, "game_%d.SAV", slot);
929 const char *filename = getApplicationFilename(name, true); // important for save game files to survive an uninstall
930 //LOG("filename: %s\n", filename);
931 return filename;
932 }
933
loadGameInfo(DifficultyLevel * difficulty,int * player,int * n_men,int suspended[n_players_c],int * epoch,bool completed[max_islands_per_epoch_c],int slot)934 bool Game::loadGameInfo(DifficultyLevel *difficulty, int *player, int *n_men, int suspended[n_players_c], int *epoch, bool completed[max_islands_per_epoch_c], int slot) {
935 ASSERT( slot >= 0 && slot < n_slots_c );
936 const char *filename = getFilename(slot);
937 bool ok = loadGameInfo(difficulty, player, n_men, suspended, epoch, completed, filename);
938 delete [] filename;
939 return ok;
940 }
941
loadGame(int slot)942 bool Game::loadGame(int slot) {
943 LOG("loadGame(%d)\n",slot);
944 ASSERT( slot >= 0 && slot < n_slots_c );
945 const char *filename = getFilename(slot);
946 bool ok = loadGame(filename);
947 delete [] filename;
948 return ok;
949 }
950
saveGame(int slot) const951 void Game::saveGame(int slot) const {
952 LOG("saveGame(%d)\n",slot);
953 ASSERT( gameType == GAMETYPE_ALLISLANDS );
954 ASSERT( gameStateID == GAMESTATEID_PLACEMEN );
955 ASSERT( slot >= 0 && slot < n_slots_c );
956
957 const char *filename = getFilename(slot);
958 FILE *file = fopen(filename, "wb+");
959 delete [] filename;
960 if( file == NULL ) {
961 LOG("FAILED to open file\n");
962 return;
963 }
964
965 const int savVersion = 1;
966 fprintf(file, "GLMSAV%d.%d.%d\n", majorVersion, minorVersion, savVersion);
967
968 char buffer[256] = "";
969 char *ptr = buffer;
970
971 *((int *)ptr) = difficulty_level;
972 ptr += sizeof(int);
973
974 *((int *)ptr) = human_player;
975 ptr += sizeof(int);
976
977 *((int *)ptr) = n_men_store;
978 ptr += sizeof(int);
979
980 int n_suspended[n_players_c]; // no longer use n_suspended for all players, but still need to keep save game files compatible
981 for(int i=0;i<n_players_c;i++) {
982 n_suspended[i] = 0;
983 }
984 n_suspended[human_player] = n_player_suspended;
985 for(int i=0;i<n_players_c;i++) {
986 *((int *)ptr) = n_suspended[i];
987 ptr += sizeof(int);
988 }
989
990 *((int *)ptr) = start_epoch;
991 ptr += sizeof(int);
992
993 for(int i=0;i<max_islands_per_epoch_c;i++) {
994 int val = completed_island[i] ? 1 : 0;
995 *((int *)ptr) = val;
996 ptr += sizeof(int);
997 }
998
999 ptrdiff_t n_bytes = ptr - buffer;
1000 int sum = 0;
1001 for(int i=0;i<n_bytes;i++) {
1002 sum += buffer[i];
1003 }
1004
1005 LOG("checksum %d\n", sum);
1006
1007 *((int *)ptr) = sum;
1008 ptr += sizeof(int);
1009
1010 *ptr = '\0';
1011
1012 //fprintf(file, "%s\n", buffer);
1013 //fwrite(buffer, (int)ptr - (int)buffer, 1, file);
1014 ptrdiff_t diff = ptr - buffer;
1015 fwrite(buffer, diff, 1, file);
1016
1017 fclose(file);
1018 }
1019
validPlayer(int player) const1020 bool Game::validPlayer(int player) const {
1021 bool valid = player >= 0 && player < n_players_c;
1022 if( !valid ) {
1023 LOG("ERROR invalid player %d\n", player);
1024 }
1025 return valid;
1026 }
1027
addTextEffect(TextEffect * effect)1028 void Game::addTextEffect(TextEffect *effect) {
1029 gamestate->addTextEffect(effect);
1030 }
1031
stopMusic()1032 void Game::stopMusic() {
1033 if( music != NULL ) {
1034 delete music;
1035 music = NULL;
1036 }
1037 }
1038
fadeMusic(int duration_ms) const1039 void Game::fadeMusic(int duration_ms) const {
1040 if( music != NULL ) {
1041 music->fadeOut(duration_ms);
1042 }
1043 }
1044
playMusic()1045 void Game::playMusic() {
1046 stopMusic();
1047
1048 if( !pref_music_on ) {
1049 // don't play any music
1050 }
1051 else if( gameStateID == GAMESTATEID_CHOOSEPLAYER ) {
1052 }
1053 else if( gameStateID == GAMESTATEID_PLACEMEN ) {
1054 music = Sample::loadMusic("music/mainscreen.ogg");
1055 // n.b., a music structure is always created, even if we fail to load, so no need to check for NULL pointers here (though we do elsewhere, as music not created if pref_music_on is false)
1056 music->play(SOUND_CHANNEL_MUSIC, -1);
1057 }
1058 else if( gameStateID == GAMESTATEID_PLAYING ) {
1059 music = Sample::loadMusic("music/gamemusic.ogg");
1060 // n.b., a music structure is always created, even if we fail to load, so no need to check for NULL pointers here (though we do elsewhere, as music not created if pref_music_on is false)
1061 music->play(SOUND_CHANNEL_MUSIC, -1);
1062 }
1063 else if( gameStateID == GAMESTATEID_ENDISLAND ) {
1064 if( gameResult == GAMERESULT_WON )
1065 music = Sample::loadMusic("music/victory.ogg");
1066 else
1067 music = Sample::loadMusic("music/defeat.ogg");
1068 // n.b., a music structure is always created, even if we fail to load, so no need to check for NULL pointers here (though we do elsewhere, as music not created if pref_music_on is false)
1069 music->play(SOUND_CHANNEL_MUSIC, 0); // don't loop
1070 }
1071 }
1072
loadSamples()1073 bool Game::loadSamples() {
1074 string sound_dir = "sound/";
1075
1076 s_design_is_ready = Sample::loadSample(sound_dir + "the_design_is_finished.wav");
1077 s_design_is_ready->setText("the design is finished");
1078 s_ergo = Sample::loadSample(sound_dir + "ergonomically_terrific.wav");
1079 s_ergo->setText("ergonomically terrific!");
1080 s_fcompleted = Sample::loadSample(sound_dir + "the_production_run_s_completed.wav");
1081 s_fcompleted->setText("the production run's completed");
1082 s_advanced_tech = Sample::loadSample(sound_dir + "we_ve_advanced_a_tech_level.wav");
1083 s_advanced_tech->setText("we've advanced a tech level!");
1084 s_running_out_of_elements = Sample::loadSample(sound_dir + "we_re_running_out_of_elements.wav");
1085 s_running_out_of_elements->setText("we're running out of elements");
1086 s_tower_critical = Sample::loadSample(sound_dir + "tower_critical.wav");
1087 s_tower_critical->setText("tower critical!");
1088 s_sector_destroyed = Sample::loadSample(sound_dir + "the_sector_s_been_destroyed.wav");
1089 s_sector_destroyed->setText("the sector's been destroyed!");
1090 s_mine_destroyed = Sample::loadSample(sound_dir + "the_mine_is_destroyed.wav");
1091 s_mine_destroyed->setText("the mine is destroyed");
1092 s_factory_destroyed = Sample::loadSample(sound_dir + "the_factory_s_been_destroyed.wav");
1093 s_factory_destroyed->setText("the factory's been destroyed");
1094 s_lab_destroyed = Sample::loadSample(sound_dir + "the_lab_s_been_destroyed.wav");
1095 s_lab_destroyed->setText("the lab's been destroyed");
1096 s_itis_all_over = Sample::loadSample(sound_dir + "it_s_all_over.wav");
1097 s_itis_all_over->setText("game over");
1098 s_conquered = Sample::loadSample(sound_dir + "we_ve_conquered_the_sector.wav");
1099 s_conquered->setText("we've conquered the sector");
1100 s_won = Sample::loadSample(sound_dir + "we_ve_won.wav");
1101 s_won->setText("we've won!");
1102 s_weve_nuked_them = Sample::loadSample(sound_dir + "we_ve_nuked_them.wav");
1103 s_weve_nuked_them->setText("we've nuked them!");
1104 s_weve_been_nuked = Sample::loadSample(sound_dir + "we_ve_been_nuked.wav");
1105 s_weve_been_nuked->setText("we've been nuked!");
1106 s_on_hold = Sample::loadSample(sound_dir + "putting_you_on_hold.wav");
1107 // no text for s_on_hold
1108 s_alliance_yes[0] = new Sample();
1109 s_alliance_yes[0]->setText("red team says okay");
1110 s_alliance_yes[1] = new Sample();
1111 s_alliance_yes[1]->setText("green team says okay");
1112 s_alliance_yes[2] = new Sample();
1113 s_alliance_yes[2]->setText("yellow team says okay");
1114 s_alliance_yes[3] = new Sample();
1115 s_alliance_yes[3]->setText("blue team says okay");
1116 s_alliance_no[0] = new Sample();
1117 s_alliance_no[0]->setText("red team says no");
1118 s_alliance_no[1] = new Sample();
1119 s_alliance_no[1]->setText("green team says no");
1120 s_alliance_no[2] = new Sample();
1121 s_alliance_no[2]->setText("yellow team says no");
1122 s_alliance_no[3] = new Sample();
1123 s_alliance_no[3]->setText("blue team says no");
1124 s_alliance_ask[0] = new Sample();
1125 s_alliance_ask[0]->setText("red team requests alliance");
1126 s_alliance_ask[1] = new Sample();
1127 s_alliance_ask[1]->setText("green team requests alliance");
1128 s_alliance_ask[2] = new Sample();
1129 s_alliance_ask[2]->setText("yellow team requests alliance");
1130 s_alliance_ask[3] = new Sample();
1131 s_alliance_ask[3]->setText("blue team requests alliance");
1132
1133 // text messages without corresponding samples
1134 //s_cant_nuke_ally = new Sample(NULL);
1135 s_cant_nuke_ally = new Sample();
1136 s_cant_nuke_ally->setText("cannot nuke our ally");
1137
1138 // no samples or text messages, but keep supported in case we re-add samples later
1139 s_quit[0] = new Sample();
1140 s_quit[1] = new Sample();
1141 s_quit[2] = new Sample();
1142 s_quit[3] = new Sample();
1143
1144 // sound effects
1145 s_explosion = Sample::loadSample(sound_dir + "bomb.wav");
1146 #if !defined(__ANDROID__) && defined(__DragonFly__)
1147 if( s_explosion == NULL || errorSound() ) {
1148 if( s_explosion != NULL ) {
1149 delete s_explosion;
1150 s_explosion = NULL;
1151 }
1152 resetErrorSound();
1153 sound_dir = "/usr/local/share/gigalomania/" + sound_dir;
1154 LOG("look in %s for sound\n", sound_dir.c_str());
1155 s_explosion = Sample::loadSample(sound_dir + "bomb.wav");
1156 }
1157 #endif
1158
1159 s_scream = Sample::loadSample(sound_dir + "pain1.wav");
1160 s_buildingdestroyed = Sample::loadSample(sound_dir + "woodbrk.wav");
1161 s_guiclick = Sample::loadSample(sound_dir + "misc_menu_3.wav");
1162 s_biplane = Sample::loadSample(sound_dir + "biplane.ogg");
1163 s_jetplane = Sample::loadSample(sound_dir + "jetplane.ogg");
1164 s_spaceship = Sample::loadSample(sound_dir + "spaceship.ogg");
1165
1166 bool ok = !errorSound();
1167 return ok;
1168 }
1169
remapLand(Image * image,MapColour colour)1170 bool remapLand(Image *image,MapColour colour) {
1171 for(int y=0;y<image->getHeight();y++) {
1172 for(int x=0;x<image->getWidth();x++) {
1173 unsigned char c = image->getPixelIndex(x,y);
1174 if( c == 0 ) {
1175 // leave as 0
1176 }
1177 else if( c == 1 ) {
1178 // light
1179 int c2 = 0;
1180 if( colour == MAP_ORANGE )
1181 c2 = 15;
1182 else if( colour == MAP_GREEN )
1183 c2 = 1;
1184 else if( colour == MAP_BROWN )
1185 c2 = 14;
1186 else if( colour == MAP_WHITE )
1187 c2 = 2;
1188 else if( colour == MAP_DBROWN )
1189 c2 = 6;
1190 else if( colour == MAP_DGREEN )
1191 c2 = 12;
1192 else if( colour == MAP_GREY )
1193 c2 = 1;
1194 else {
1195 ASSERT(false);
1196 }
1197 image->setPixelIndex(x, y, c2);
1198 }
1199 else if( c == 2 ) {
1200 // medium
1201 int c2 = 0;
1202 if( colour == MAP_ORANGE )
1203 c2 = 14;
1204 else if( colour == MAP_GREEN )
1205 c2 = 12;
1206 else if( colour == MAP_BROWN )
1207 c2 = 6;
1208 else if( colour == MAP_WHITE )
1209 c2 = 1;
1210 else if( colour == MAP_DBROWN )
1211 c2 = 8;
1212 else if( colour == MAP_DGREEN )
1213 c2 = 7;
1214 else if( colour == MAP_GREY )
1215 c2 = 5;
1216 else {
1217 ASSERT(false);
1218 }
1219 image->setPixelIndex(x, y, c2);
1220 }
1221 else if( c == 3 ) {
1222 // dark
1223 int c2 = 0;
1224 if( colour == MAP_ORANGE )
1225 c2 = 6;
1226 else if( colour == MAP_GREEN )
1227 c2 = 7;
1228 else if( colour == MAP_BROWN )
1229 c2 = 8;
1230 else if( colour == MAP_WHITE )
1231 c2 = 5;
1232 else if( colour == MAP_DBROWN )
1233 c2 = 3;
1234 else if( colour == MAP_DGREEN )
1235 c2 = 8;
1236 else if( colour == MAP_GREY )
1237 c2 = 4;
1238 else {
1239 ASSERT(false);
1240 }
1241 image->setPixelIndex(x, y, c2);
1242 }
1243 else {
1244 //LOG("land slabs should only have colour indices 0-3\n");
1245 //return false;
1246 }
1247 }
1248 }
1249 return true;
1250 }
1251
convertToHiColor(Image * image) const1252 void Game::convertToHiColor(Image *image) const {
1253 if( using_old_gfx ) {
1254 // create alpha from color keys
1255 image->createAlphaForColor(true, 255, 0, 255, 127, 0, 127, shadow_alpha_c);
1256 }
1257 image->convertToHiColor(false);
1258 }
1259
processImage(Image * image,bool old_smooth) const1260 void Game::processImage(Image *image, bool old_smooth) const {
1261 //LOG(" convert to hi color\n");
1262 convertToHiColor(image);
1263 //LOG(" scale\n");
1264 image->scale(scale_factor_w, scale_factor_h);
1265 //LOG(" set scale\n");
1266 image->setScale(scale_width, scale_height);
1267 #if SDL_MAJOR_VERSION == 1
1268 // with SDL 2, we let SDL do smoothing when scaling the graphics on the GPU
1269 if( using_old_gfx && old_smooth ) {
1270 image->smooth();
1271 }
1272 #endif
1273 //LOG(" done\n");
1274 }
1275
loadAttackersWalkingImages(const string & gfx_dir,int epoch)1276 bool Game::loadAttackersWalkingImages(const string &gfx_dir, int epoch) {
1277 char filename[300] = "";
1278 sprintf(filename, "attacker_walking_%d.png", epoch);
1279 Image *gfx_image = Image::loadImage(gfx_dir + filename);
1280 // if NULL, look for direction specific graphics
1281 if( gfx_image != NULL ) {
1282 gfx_image->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
1283 }
1284 for(int dir=0;dir<n_attacker_directions_c;dir++) {
1285 bool direction_specific = false;
1286 if( gfx_image == NULL ) {
1287 // load direction specific image
1288 //LOG("try loading direction specific images for epoch %d dir %d\n", epoch, dir);
1289 direction_specific = true;
1290 sprintf(filename, "attacker_walking_%d_%d.png", epoch, dir);
1291 gfx_image = Image::loadImage(gfx_dir + filename);
1292 if( gfx_image == NULL ) {
1293 LOG("failed to load attacker walking image for epoch %d dir %d\n", epoch, dir);
1294 return false;
1295 }
1296 gfx_image->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
1297 }
1298 int height_per_frame = gfx_image->getScaledHeight();
1299 int width_per_frame = height_per_frame;
1300 n_attacker_frames[epoch][dir] = gfx_image->getScaledWidth()/width_per_frame;
1301 //LOG("epoch %d, direction %d has %d frames\n", epoch, dir, n_attacker_frames[epoch][dir]);
1302 // need to update max_attacker_frames_c if we ever want to allow more frames!
1303 ASSERT( n_attacker_frames[epoch][dir] <= max_attacker_frames_c );
1304 for(int player=0;player<n_players_c;player++) {
1305 int n_frames = n_attacker_frames[epoch][dir];
1306 for(int frame=0;frame<n_frames;frame++) {
1307 attackers_walking[player][epoch][dir][frame] = gfx_image->copy(width_per_frame*frame, 0, width_per_frame, height_per_frame);
1308 int r = 0, g = 0, b = 0;
1309 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)player);
1310 attackers_walking[player][epoch][dir][frame]->remap(240, 0, 0, r, g, b);
1311 processImage(attackers_walking[player][epoch][dir][frame]);
1312 }
1313 }
1314 if( direction_specific ) {
1315 delete gfx_image;
1316 gfx_image = NULL;
1317 }
1318 }
1319 if( gfx_image != NULL ) {
1320 delete gfx_image;
1321 }
1322 return true;
1323 }
1324
calculateScale(const Image * image)1325 void Game::calculateScale(const Image *image) {
1326 #if SDL_MAJOR_VERSION == 1
1327 scale_factor_w = ((float)(scale_width*default_width_c))/(float)image->getWidth();
1328 scale_factor_h = ((float)(scale_height*default_height_c))/(float)image->getHeight();
1329 LOG("scale factor for images = %f X %f\n", scale_factor_w, scale_factor_h);
1330 #else
1331 // with SDL 2, we don't scale the graphics, and instead set the logical size to match the graphics
1332 scale_factor_w = 1.0f;
1333 scale_factor_h = 1.0f;
1334 scale_width = ((float)(image->getWidth()))/(float)default_width_c;
1335 scale_height = ((float)(image->getHeight()))/(float)default_height_c;
1336 LOG("scale width/height of logical resolution = %f X %f\n", scale_width, scale_height);
1337 screen->setLogicalSize((int)(scale_width*default_width_c), (int)(scale_height*default_height_c), true);
1338 #endif
1339 }
1340
loadOldImages()1341 bool Game::loadOldImages() {
1342 // progress should go from 0 to 80%
1343 LOG("try using old graphics\n");
1344 using_old_gfx = true;
1345
1346 background = Image::loadImage("data/mlm_sunrise");
1347
1348 if( background == NULL )
1349 return false;
1350 drawProgress(20);
1351 // do equivalent for calculateScale(), but for a 320x240 image (n.b., not 320x256)
1352 #if SDL_MAJOR_VERSION == 1
1353 scale_factor_w = scale_width;
1354 scale_factor_h = scale_height;
1355 LOG("scale factor for images = %f X %f\n", scale_factor_w, scale_factor_h);
1356 #else
1357 // with SDL 2, we don't scale the graphics, and instead set the logical size to match the graphics
1358 scale_factor_w = 1.0f;
1359 scale_factor_h = 1.0f;
1360 scale_width = 1.0f;
1361 scale_height = 1.0f;
1362 LOG("scale width/height of logical resolution = %f X %f\n", scale_width, scale_height);
1363 screen->setLogicalSize((int)(scale_width*default_width_c), (int)(scale_height*default_height_c), false); // don't smooth, as doesn't look too good with old graphics
1364 #endif
1365
1366 // nb, still scale if scale_factor==1, as this is a way of converting to 8bit
1367 processImage(background);
1368
1369 for(int i=0;i<n_players_c;i++) {
1370 player_heads_select[i] = NULL;
1371 player_heads_alliance[i] = NULL;
1372 }
1373 grave = NULL;
1374
1375 Image *image_slabs = Image::loadImage("data/mlm_slabs");
1376 if( image_slabs == NULL )
1377 return false;
1378 drawProgress(30);
1379 for(int i=0;i<MAP_N_COLOURS;i++) {
1380 land[i] = image_slabs->copy();
1381 //land[i]->scale(scale_factor, scale_factor); // also forces to 8bit (since should be paletted)
1382 land[i]->scale(scale_factor_w, scale_factor_h); // also forces to 8bit (since should be paletted)
1383 land[i]->setScale(scale_width, scale_height);
1384 if( !land[i]->isPaletted() ) {
1385 LOG("land slabs should be paletted\n");
1386 return false;
1387 }
1388 else if( land[i]->getNColors() < 16 ) { // should be 256 by now anyway, due to scaling
1389 LOG("land slabs should have at least 16 colours\n");
1390 return false;
1391 }
1392 else {
1393 // 1, 2, 3, 4, 5, 6, 7, 8, 12, 14, 15
1394 land[i]->setColor(0, 255, 0, 255);
1395 land[i]->setColor(1, 176, 176, 176);
1396 land[i]->setColor(2, 240, 240, 240);
1397 land[i]->setColor(3, 0, 0, 0);
1398 land[i]->setColor(4, 64, 64, 64);
1399 land[i]->setColor(5, 112, 112, 112);
1400 land[i]->setColor(6, 160, 64, 0);
1401 land[i]->setColor(7, 0, 96, 0);
1402 land[i]->setColor(8, 112, 36, 16);
1403 land[i]->setColor(12, 0, 192, 0);
1404 land[i]->setColor(14, 240, 112, 16);
1405 land[i]->setColor(15, 240, 240, 0);
1406 }
1407 if( !remapLand(land[i], (MapColour)i) ) {
1408 LOG("failed to remap land\n");
1409 return false;
1410 }
1411 convertToHiColor(land[i]);
1412 #if SDL_MAJOR_VERSION == 1
1413 // with SDL 2, we let SDL do smoothing when scaling the graphics on the GPU
1414 land[i]->smooth();
1415 #endif
1416 }
1417 delete image_slabs;
1418 image_slabs = NULL;
1419
1420 for(int i=0;i<n_epochs_c;i++) {
1421 fortress[i] = NULL;
1422 mine[i] = NULL;
1423 factory[i] = NULL;
1424 }
1425
1426 Image *buildings = Image::loadImage("data/mlm_buildings");
1427 if( buildings == NULL )
1428 return false;
1429 buildings->setColor(0, 255, 0, 255);
1430 processImage(buildings);
1431
1432 for(int i=0;i<n_epochs_c;i++) {
1433 fortress[i] = buildings->copy( 0, 60*i, 58, 60);
1434 mine[i] = buildings->copy( 64, 60*i, 58, 60);
1435 factory[i] = buildings->copy( 192, 60*i, 58, 60);
1436 lab[i] = buildings->copy( 128, 60*i, 58, 60);
1437 }
1438 delete buildings;
1439 buildings = NULL;
1440 drawProgress(40);
1441
1442 Image *icons = Image::loadImage("data/mlm_icons");
1443 if( icons == NULL )
1444 return false;
1445 if( !icons->scaleTo((int)(scale_width*default_width_c)) ) // must use this method, as still using alongside the new gfx images
1446 return false;
1447 //icons->scale(scale_factor, scale_factor);
1448 icons->setScale(scale_width, scale_height);
1449 icons->setColor(0, 255, 0, 255);
1450 convertToHiColor(icons);
1451 #if SDL_MAJOR_VERSION == 1
1452 // with SDL 2, we let SDL do smoothing when scaling the graphics on the GPU
1453 icons->smooth();
1454 #endif
1455
1456 for(int i=0;i<n_epochs_c;i++)
1457 men[i] = icons->copy(16*i, 0, 16, 16);
1458
1459 unarmed_man = icons->copy(80, 32, 16, 16);
1460
1461 panel_design = icons->copy(304, 0, 16, 16);
1462 //panel_design_dark = panel_design->copy();
1463 panel_lab = icons->copy(16, 33, 16, 16);
1464 panel_shield = icons->copy(240, 0, 16, 16);
1465 panel_defence = icons->copy(256, 0, 16, 16);
1466 panel_attack = icons->copy(272, 0, 16, 16);
1467 panel_knowndesigns = icons->copy(240, 16, 16, 16);
1468 panel_factory = icons->copy(48, 33, 16, 16);
1469 panel_bloody_attack = icons->copy(240, 32, 16, 16);
1470
1471 mine_gatherable_small = icons->copy(160, 0, 16, 16);
1472
1473 //panel_build[BUILDING_TOWER] = icons->copy(224, 63, 19, 16); // not yet used
1474 panel_build[BUILDING_MINE] = icons->copy(256, 63, 19, 16);
1475 panel_build[BUILDING_FACTORY] = icons->copy(288, 63, 19, 16);
1476 panel_build[BUILDING_LAB] = icons->copy(192, 63, 19, 16);
1477
1478 panel_building[BUILDING_TOWER] = icons->copy(0, 33, 16, 14);
1479 panel_building[BUILDING_MINE] = icons->copy(32, 33, 16, 14);
1480 //panel_building[BUILDING_FACTORY] = icons->copy(48, 33, 16, 14);
1481 //panel_building[BUILDING_LAB] = icons->copy(16, 33, 16, 14);
1482 panel_building[BUILDING_FACTORY] = panel_factory;
1483 panel_building[BUILDING_LAB] = panel_lab;
1484
1485 mine_gatherable_large = icons->copy(160, 64, 32, 16);
1486
1487 panel_bigdesign = icons->copy(256, 48, 32, 15);
1488 panel_biglab = icons->copy(96, 48, 32, 16);
1489 panel_bigfactory = icons->copy(128, 48, 32, 16);
1490 panel_bigshield = icons->copy(160, 48, 32, 15);
1491 panel_bigdefence = icons->copy(192, 48, 32, 15);
1492 panel_bigattack = icons->copy(224, 48, 32, 15);
1493 panel_bigbuild = icons->copy(32, 49, 32, 15);
1494 panel_bigknowndesigns = icons->copy(288, 48, 32, 15);
1495 panel_twoattack = icons->copy(64, 33, 15, 15);
1496
1497 for(int i=0;i<10;i++)
1498 numbers_blue[i] = icons->copy(16*i, 64, 6, 8);
1499 for(int i=0;i<10;i++)
1500 numbers_grey[i] = icons->copy(16*i, 72, 6, 8);
1501 for(int i=0;i<10;i++)
1502 numbers_white[i] = icons->copy(16*i, 80, 6, 8);
1503 for(int i=0;i<10;i++)
1504 numbers_orange[i] = icons->copy(16*i, 88, 6, 8);
1505 for(int i=0;i<10;i++)
1506 numbers_yellow[i] = icons->copy(16*i, 119, 6, 8);
1507 for(int i=0;i<3;i++)
1508 numbers_largeshiny[i] = icons->copy(16*i, 269, 6, 15);
1509 for(int i=3;i<10;i++)
1510 numbers_largeshiny[i] = NULL; // not used
1511 for(int i=0;i<10;i++)
1512 numbers_largegrey[i] = icons->copy(64 + 16*i, 269, 6, 15);
1513 for(int i=0;i<10;i++)
1514 numbers_small[0][i] = icons->copy(16*i, 126, 5, 7);
1515 for(int i=0;i<10;i++)
1516 numbers_small[1][i] = icons->copy(16*i, 134, 5, 7);
1517 for(int i=0;i<10;i++)
1518 numbers_small[2][i] = icons->copy(160 + 16*i, 126, 5, 7);
1519 for(int i=0;i<10;i++)
1520 numbers_small[3][i] = icons->copy(160 + 16*i, 134, 5, 7);
1521 numbers_half = icons->copy(160, 119, 6, 8);
1522
1523 for(int i=0;i<n_font_chars_c;i++) {
1524 letters_large[i] = NULL;
1525 letters_small[i] = NULL;
1526 }
1527 for(int i=0;i<4;i++)
1528 letters_large[i] = icons->copy(256 + 16*i, 269, 6, 15);
1529 for(int i=0;i<20;i++)
1530 letters_large[4+i] = icons->copy(16*i, 284, 6, 15);
1531 for(int i=0;i<2;i++)
1532 letters_large[24+i] = icons->copy(16*i, 299, 6, 15);
1533
1534 for(int i=0;i<15;i++)
1535 letters_small[i] = icons->copy(80 + 16*i, 299, 6, 8);
1536 for(int i=0;i<11;i++)
1537 letters_small[15+i] = icons->copy(80 + 16*i, 307, 6, 8);
1538
1539 for(int i=0;i<n_players_c;i++)
1540 mouse_pointers[i] = icons->copy(176 + 16*i, 0, 16, 16);
1541
1542 for(int i=0;i<13;i++)
1543 icon_clocks[i] = icons->copy(16*i, 16, 16, 16);
1544
1545 /*for(int i=0;i<3;i++)
1546 icon_mice[i] = icons->copy(256 + 16*i, 16, 16, 19);*/
1547 for(int i=0;i<2;i++)
1548 icon_mice[i] = icons->copy(256 + 16*i, 16, 16, 19);
1549
1550 for(int i=0;i<n_death_flashes_c;i++)
1551 death_flashes[i] = icons->copy(272 + 16*i, 346, 16, 19);
1552
1553 for(int i=0;i<5;i++)
1554 blue_flashes[i] = icons->copy(225 + 16*i, 173, 16, 16);
1555 blue_flashes[5] = icons->copy(305, 165, 15, 19);
1556 blue_flashes[6] = icons->copy(305, 184, 15, 17);
1557
1558 for(int i=0;i<n_explosions_c;i++)
1559 explosions[i] = NULL;
1560
1561 for(int i=0;i<n_players_c;i++) {
1562 icon_towers[i] = icons->copy(160 + 16*i, 81, 6, 6);
1563 icon_armies[i] = icons->copy(160 + 16*i, 87, 4, 4);
1564 }
1565
1566 for(int i=0;i<N_ID;i++)
1567 icon_elements[i] = icons->copy(16*i, 141, 16, 16);
1568
1569 for(int i=0;i<n_players_c;i++)
1570 flags[i][0] = icons->copy(160 + 16*i, 157, 16, 15);
1571 for(int i=0;i<n_players_c;i++)
1572 flags[i][1] = icons->copy(224 + 16*i, 157, 16, 15);
1573 for(int i=0;i<n_players_c;i++)
1574 flags[i][2] = icons->copy(160 + 16*i, 172, 16, 15);
1575 for(int i=0;i<n_players_c;i++)
1576 flags[i][3] = flags[i][1];
1577
1578 for(int i=0;i<n_playershields_c;i++)
1579 playershields[i] = icons->copy(16*i, 189, 16, 14);
1580
1581 for(int i=0;i<n_shields_c;i++)
1582 icon_shields[i] = icons->copy(112 + 16*i, 32, 16, 16);
1583 for(int i=0;i<n_epochs_c;i++)
1584 icon_defences[i] = icons->copy(16*i, 253, 16, 16);
1585 for(int i=0;i<n_epochs_c;i++)
1586 icon_weapons[i] = icons->copy(16*i, 237, 16, 16);
1587 for(int i=0;i<n_epochs_c;i++)
1588 numbered_defences[i] = icons->copy(16*i, 173, 16, 16);
1589 for(int i=0;i<n_epochs_c;i++)
1590 numbered_weapons[i] = icons->copy(16*i, 157, 16, 16);
1591
1592 for(int i=0;i<3;i++)
1593 icon_speeds[i] = icons->copy(272 + 16*i, 204, 16, 18);
1594
1595 building_health = icons->copy(0, 203, 41, 5);
1596 dash_grey = icons->copy(144, 114, 5, 2);
1597 icon_shield = icons->copy(272, 315, 16, 16);
1598 icon_defence = icons->copy(288, 315, 16, 16);
1599 icon_weapon = icons->copy(304, 315, 16, 16);
1600
1601 icon_infinity = icons->copy(112, 104, 12, 8);
1602
1603 icon_bc = icons->copy(240, 276, 12, 8);
1604 icon_ad = icons->copy(224, 276, 12, 8);
1605 icon_ad_shiny = icons->copy(48, 276, 12, 8);
1606
1607 icon_nuke_hole = icons->copy(288, 99, 9, 9);
1608
1609 icon_ergo = icons->copy(176, 111, 16, 16);
1610 icon_trash = icons->copy(192, 111, 16, 16);
1611
1612 mapsquare = icons->copy(288, 81, 17, 17);
1613 flashingmapsquare = icons->copy(192, 92, 17, 17);
1614
1615 delete icons;
1616 icons = NULL;
1617 drawProgress(50);
1618
1619 for(int i=0;i<n_coast_c;i++)
1620 coast_icons[i] = NULL;
1621
1622 Image *smallmap = Image::loadImage("data/mlm_smallmap");
1623 if( smallmap == NULL )
1624 return false;
1625 smallmap->setColor(0, 255, 0, 255);
1626 processImage(smallmap);
1627 map_sq_offset = 3;
1628 map_sq_coast_offset = 3;
1629
1630 for(int i=0;i<MAP_N_COLOURS;i++) {
1631 for(int j=0;j<n_map_sq_c;j++)
1632 map_sq[i][j] = NULL;
1633 }
1634
1635 map_sq[MAP_ORANGE][14] = smallmap->copy(0, 0, 22, 22);
1636 map_sq[MAP_ORANGE][7] = smallmap->copy(32, 0, 22, 22);
1637 map_sq[MAP_ORANGE][11] = smallmap->copy(64, 0, 22, 22);
1638 map_sq[MAP_ORANGE][4] = smallmap->copy(96, 0, 22, 22);
1639 map_sq[MAP_ORANGE][5] = smallmap->copy(128, 0, 22, 22);
1640 map_sq[MAP_ORANGE][1] = smallmap->copy(160, 0, 22, 22);
1641 map_sq[MAP_ORANGE][2] = smallmap->copy(192, 0, 22, 22);
1642 map_sq[MAP_ORANGE][8] = smallmap->copy(224, 0, 22, 22);
1643
1644 map_sq[MAP_GREEN][6] = smallmap->copy(256, 0, 22, 22);
1645 map_sq[MAP_GREEN][12] = smallmap->copy(288, 0, 22, 22);
1646 map_sq[MAP_GREEN][3] = smallmap->copy(0, 22, 22, 22);
1647 map_sq[MAP_GREEN][9] = smallmap->copy(32, 22, 22, 22);
1648 map_sq[MAP_GREEN][5] = smallmap->copy(64, 22, 22, 22);
1649 map_sq[MAP_GREEN][10] = smallmap->copy(96, 22, 22, 22);
1650
1651 map_sq[MAP_BROWN][6] = smallmap->copy(128, 22, 22, 22);
1652 map_sq[MAP_BROWN][4] = smallmap->copy(160, 22, 22, 22);
1653 map_sq[MAP_BROWN][1] = smallmap->copy(192, 22, 22, 22);
1654 map_sq[MAP_BROWN][8] = smallmap->copy(224, 22, 22, 22);
1655 map_sq[MAP_BROWN][0] = smallmap->copy(256, 22, 22, 22);
1656
1657 map_sq[MAP_DBROWN][6] = smallmap->copy(32, 66, 22, 22);
1658 map_sq[MAP_DBROWN][12] = smallmap->copy(64, 66, 22, 22);
1659 map_sq[MAP_DBROWN][7] = smallmap->copy(96, 66, 22, 22);
1660 map_sq[MAP_DBROWN][13] = smallmap->copy(128, 66, 22, 22);
1661 map_sq[MAP_DBROWN][3] = smallmap->copy(160, 66, 22, 22);
1662 map_sq[MAP_DBROWN][11] = smallmap->copy(192, 66, 22, 22);
1663 map_sq[MAP_DBROWN][9] = smallmap->copy(224, 66, 22, 22);
1664 map_sq[MAP_DBROWN][4] = smallmap->copy(256, 66, 22, 22);
1665 map_sq[MAP_DBROWN][5] = smallmap->copy(288, 66, 22, 22);
1666 map_sq[MAP_DBROWN][1] = smallmap->copy(0, 88, 22, 22);
1667 map_sq[MAP_DBROWN][2] = smallmap->copy(32, 88, 22, 22);
1668 map_sq[MAP_DBROWN][10] = smallmap->copy(64, 88, 22, 22);
1669
1670 map_sq[MAP_WHITE][1] = smallmap->copy(224, 44, 22, 22);
1671 map_sq[MAP_WHITE][2] = smallmap->copy(256, 44, 22, 22);
1672 map_sq[MAP_WHITE][3] = smallmap->copy(96, 44, 22, 22);
1673 map_sq[MAP_WHITE][4] = smallmap->copy(192, 44, 22, 22);
1674 map_sq[MAP_WHITE][6] = smallmap->copy(288, 22, 22, 22);
1675 map_sq[MAP_WHITE][8] = smallmap->copy(0, 66, 22, 22);
1676 map_sq[MAP_WHITE][9] = smallmap->copy(160, 44, 22, 22);
1677 map_sq[MAP_WHITE][10] = smallmap->copy(288, 44, 22, 22);
1678 map_sq[MAP_WHITE][11] = smallmap->copy(128, 44, 22, 22);
1679 map_sq[MAP_WHITE][12] = smallmap->copy(32, 44, 22, 22);
1680 map_sq[MAP_WHITE][14] = smallmap->copy(0, 44, 22, 22);
1681 map_sq[MAP_WHITE][15] = smallmap->copy(64, 44, 22, 22);
1682
1683 map_sq[MAP_DGREEN][6] = smallmap->copy(96, 88, 22, 22);
1684 map_sq[MAP_DGREEN][14] = smallmap->copy(128, 88, 22, 22);
1685 map_sq[MAP_DGREEN][12] = smallmap->copy(160, 88, 22, 22);
1686 map_sq[MAP_DGREEN][7] = smallmap->copy(192, 88, 22, 22);
1687 map_sq[MAP_DGREEN][15] = smallmap->copy(224, 88, 22, 22);
1688 map_sq[MAP_DGREEN][13] = smallmap->copy(256, 88, 22, 22);
1689 map_sq[MAP_DGREEN][3] = smallmap->copy(288, 88, 22, 22);
1690 map_sq[MAP_DGREEN][11] = smallmap->copy(0, 110, 22, 22);
1691 map_sq[MAP_DGREEN][9] = smallmap->copy(32, 110, 22, 22);
1692
1693 map_sq[MAP_GREY][0] = smallmap->copy(0, 132, 22, 22);
1694 map_sq[MAP_GREY][1] = smallmap->copy(192, 110, 22, 22);
1695 map_sq[MAP_GREY][2] = smallmap->copy(224, 110, 22, 22);
1696 map_sq[MAP_GREY][4] = smallmap->copy(128, 110, 22, 22);
1697 map_sq[MAP_GREY][5] = smallmap->copy(160, 110, 22, 22);
1698 map_sq[MAP_GREY][8] = smallmap->copy(288, 110, 22, 22);
1699 map_sq[MAP_GREY][10] = smallmap->copy(256, 110, 22, 22);
1700 map_sq[MAP_GREY][12] = smallmap->copy(64, 110, 22, 22);
1701 map_sq[MAP_GREY][15] = smallmap->copy(96, 110, 22, 22);
1702
1703 delete smallmap;
1704 smallmap = NULL;
1705 drawProgress(60);
1706
1707 for(int i=0;i<n_players_c;i++) {
1708 for(int j=0;j<=n_epochs_c;j++) {
1709 for(int k=0;k<n_attacker_directions_c;k++) {
1710 for(int l=0;l<max_attacker_frames_c;l++) {
1711 attackers_walking[i][j][k][l] = NULL;
1712 }
1713 }
1714 }
1715 }
1716 for(int j=0;j<=n_epochs_c;j++) {
1717 for(int k=0;k<n_attacker_directions_c;k++) {
1718 n_attacker_frames[j][k] = 3;
1719 }
1720 }
1721 for(int i=0;i<n_epochs_c;i++)
1722 for(int j=0;j<N_ATTACKER_AMMO_DIRS;j++)
1723 attackers_ammo[i][j] = NULL;
1724
1725 Image *armies = Image::loadImage("data/mlm_armies");
1726 if( armies == NULL ) {
1727 return false;
1728 }
1729 armies->setColor(0, 255, 0, 255);
1730 processImage(armies, false); // don't smooth, as it messes up the colour remapping!
1731
1732 const int n_defender_frames_c = 8;
1733 ASSERT( n_defender_frames_c <= max_defender_frames_c );
1734 for(int i=0;i<n_epochs_c;i++) {
1735 n_defender_frames[i] = n_defender_frames_c;
1736 }
1737 for(int i=0;i<=5;i++) {
1738 for(int j=0;j<n_defender_frames_c;j++) {
1739 for(int k=0;k<n_players_c;k++)
1740 defenders[k][i][j] = armies->copy(16*j, 16 + 32*i, 16, 16);
1741 }
1742 }
1743 for(int j=0;j<n_defender_frames_c;j++) {
1744 for(int k=0;k<n_players_c;k++)
1745 defenders[k][6][j] = armies->copy(128 + 16*j, 192, 16, 16);
1746 }
1747 for(int j=0;j<2;j++) {
1748 for(int k=0;k<n_players_c;k++)
1749 defenders[k][7][j] = armies->copy(224 + 16*j, 240, 16, 16);
1750 }
1751 for(int j=2;j<4;j++) {
1752 for(int k=0;k<n_players_c;k++)
1753 defenders[k][7][j] = armies->copy(288 + 16*(j-2), 224, 16, 16);
1754 }
1755 for(int j=4;j<6;j++) {
1756 for(int k=0;k<n_players_c;k++)
1757 defenders[k][7][j] = armies->copy(256 + 16*(j-4), 240, 16, 16);
1758 }
1759 for(int j=6;j<8;j++) {
1760 for(int k=0;k<n_players_c;k++)
1761 defenders[k][7][j] = armies->copy(288 + 16*(j-6), 240, 16, 16);
1762 }
1763 for(int j=0;j<n_defender_frames_c;j++) {
1764 defenders[0][8][j] = armies->copy(192, 256, 16, 16);
1765 defenders[1][8][j] = armies->copy(192, 272, 16, 16);
1766 defenders[2][8][j] = armies->copy(208, 256, 16, 16);
1767 defenders[3][8][j] = armies->copy(208, 272, 16, 16);
1768 }
1769 for(int k=0;k<n_players_c;k++) {
1770 int kx = k / 2;
1771 int ky = k % 2;
1772 for(int j=0;j<n_defender_frames_c;j++) {
1773 int j2 = j % 4;
1774 if( j2 == 0 )
1775 j2 = 1;
1776 else if( j2 == 1 )
1777 j2 = 0;
1778 defenders[k][9][j] = armies->copy(192 + kx * 64 + j2 * 16, 288 + ky * 13, 16, 13);
1779 }
1780 }
1781
1782 for(int i=0;i<n_players_c;i++) {
1783 for(int j=0;j<=5;j++) {
1784 for(int k=0;k<n_attacker_directions_c;k++) {
1785 int n_frames = n_attacker_frames[j][k];
1786 for(int l=0;l<n_frames;l++) {
1787 attackers_walking[i][j][k][l] = armies->copy(16*l + 64*k, 32*j, 16, 16);
1788 int r = 0, g = 0, b = 0;
1789 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
1790 attackers_walking[i][j][k][l]->remap(240, 0, 0, r, g, b);
1791 }
1792 }
1793 }
1794 }
1795
1796 for(int i=0;i<n_players_c;i++) {
1797 for(int k=0;k<n_attacker_directions_c;k++) {
1798 int n_frames = n_attacker_frames[10][k];
1799 for(int l=0;l<n_frames;l++) {
1800 attackers_walking[i][10][k][l] = armies->copy(16*l + 64*k, 320, 16, 16);
1801 int r = 0, g = 0, b = 0;
1802 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
1803 attackers_walking[i][10][k][l]->remap(240, 0, 0, r, g, b);
1804 }
1805 }
1806 }
1807
1808 for(int i=0;i<n_players_c;i++) {
1809 for(int j=0;j<n_epochs_c;j++)
1810 planes[i][j] = NULL;
1811 planes[i][6] = armies->copy(32*i, 192, 32, 32);
1812 planes[i][7] = armies->copy(32*i, 232, 32, 24);
1813 }
1814
1815 for(int i=0;i<n_players_c;i++) {
1816 nukes[i][0] = armies->copy(48*i, 256, 16, 32);
1817 nukes[i][1] = armies->copy(48*i+16, 256, 32, 32);
1818 }
1819 for(int i=0;i<n_players_c;i++) {
1820 for(int j=0;j<n_saucer_frames_c;j++) {
1821 saucers[i][j] = armies->copy(32*j, 288, 32, 21);
1822 }
1823 }
1824
1825 // ammo:
1826 // rock
1827 attackers_ammo[0][ATTACKER_AMMO_RIGHT] = armies->copy(272, 24, 16, 8);
1828 attackers_ammo[0][ATTACKER_AMMO_LEFT] = armies->copy(288, 24, 16, 8);
1829 attackers_ammo[0][ATTACKER_AMMO_UP] = armies->copy(288, 16, 16, 8);
1830 attackers_ammo[0][ATTACKER_AMMO_DOWN] = armies->copy(272, 16, 16, 8);
1831 // catapult/sword
1832 attackers_ammo[1][ATTACKER_AMMO_RIGHT] = armies->copy(272, 24, 16, 8);
1833 attackers_ammo[1][ATTACKER_AMMO_LEFT] = armies->copy(288, 24, 16, 8);
1834 attackers_ammo[1][ATTACKER_AMMO_UP] = armies->copy(288, 16, 16, 8);
1835 attackers_ammo[1][ATTACKER_AMMO_DOWN] = armies->copy(272, 16, 16, 8);
1836 /* spear
1837 attackers_ammo[1][ATTACKER_AMMO_RIGHT] = armies->copy(272, 32, 16, 8);
1838 attackers_ammo[1][ATTACKER_AMMO_LEFT] = armies->copy(272, 40, 16, 8);
1839 attackers_ammo[1][ATTACKER_AMMO_UP] = armies->copy(288, 32, 16, 16);
1840 attackers_ammo[1][ATTACKER_AMMO_DOWN] = armies->copy(304, 32, 16, 16);*/
1841 // pike
1842 attackers_ammo[2][ATTACKER_AMMO_RIGHT] = armies->copy(272, 64, 16, 8);
1843 attackers_ammo[2][ATTACKER_AMMO_LEFT] = armies->copy(272, 72, 16, 8);
1844 attackers_ammo[2][ATTACKER_AMMO_UP] = armies->copy(288, 64, 16, 16);
1845 attackers_ammo[2][ATTACKER_AMMO_DOWN] = armies->copy(304, 64, 16, 16);
1846 /* bow and arrow
1847 attackers_ammo[2][ATTACKER_AMMO_RIGHT] = armies->copy(272, 80, 16, 8);
1848 attackers_ammo[2][ATTACKER_AMMO_LEFT] = armies->copy(272, 88, 16, 8);
1849 attackers_ammo[2][ATTACKER_AMMO_UP] = armies->copy(288, 80, 16, 16);
1850 attackers_ammo[2][ATTACKER_AMMO_DOWN] = armies->copy(304, 80, 16, 16);*/
1851 // longbow
1852 attackers_ammo[3][ATTACKER_AMMO_RIGHT] = armies->copy(272, 96, 16, 8);
1853 attackers_ammo[3][ATTACKER_AMMO_LEFT] = armies->copy(272, 104, 16, 8);
1854 attackers_ammo[3][ATTACKER_AMMO_UP] = armies->copy(288, 96, 16, 16);
1855 attackers_ammo[3][ATTACKER_AMMO_DOWN] = armies->copy(304, 96, 16, 16);
1856 // trebuchet
1857 attackers_ammo[4][ATTACKER_AMMO_RIGHT] = armies->copy(256, 144, 16, 8);
1858 attackers_ammo[4][ATTACKER_AMMO_LEFT] = armies->copy(256, 144, 16, 8);
1859 attackers_ammo[4][ATTACKER_AMMO_UP] = armies->copy(256, 144, 16, 16);
1860 attackers_ammo[4][ATTACKER_AMMO_DOWN] = armies->copy(256, 144, 16, 16);
1861 // cannon
1862 attackers_ammo[5][ATTACKER_AMMO_RIGHT] = armies->copy(272, 160, 10, 9);
1863 attackers_ammo[5][ATTACKER_AMMO_LEFT] = armies->copy(272, 160, 10, 9);
1864 attackers_ammo[5][ATTACKER_AMMO_UP] = armies->copy(272, 160, 10, 9);
1865 attackers_ammo[5][ATTACKER_AMMO_DOWN] = armies->copy(272, 160, 10, 9);
1866 // bombs
1867 //attackers_ammo[6][ATTACKER_AMMO_BOMB] = armies->copy(304, 208, 16, 16);
1868 attackers_ammo[6][ATTACKER_AMMO_BOMB] = armies->copy(288, 206, 12, 12);
1869
1870 delete armies;
1871 armies = NULL;
1872
1873 for(int k=0;k<n_players_c;k++) {
1874 nuke_defences[k] = defenders[k][nuclear_epoch_c][0];
1875 }
1876
1877 attackers_ammo[7][ATTACKER_AMMO_BOMB] = attackers_ammo[6][ATTACKER_AMMO_BOMB];
1878 attackers_ammo[9][ATTACKER_AMMO_BOMB] = attackers_ammo[6][ATTACKER_AMMO_BOMB];
1879
1880 for(int i=0;i<n_saucer_frames_c;i++) {
1881 for(int k=0;k<n_players_c;k++) {
1882 int r = 0, g = 0, b = 0;
1883 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
1884 saucers[k][i]->remap(240, 0, 0, r, g, b);
1885 }
1886 }
1887 for(int i=0;i<n_epochs_c;i++) {
1888 if( defenders[0][i][0] == NULL )
1889 continue;
1890 for(int j=0;j<n_defender_frames_c;j++) {
1891 for(int k=0;k<n_players_c;k++) {
1892 int r = 0, g = 0, b = 0;
1893 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
1894 defenders[k][i][j]->remap(240, 0, 0, r, g, b);
1895 }
1896 }
1897 }
1898 drawProgress(65);
1899
1900 Image *features = Image::loadImage("data/mlm_features");
1901 if( features == NULL )
1902 return false;
1903 features->setColor(0, 255, 0, 255);
1904 processImage(features);
1905
1906 icon_openpitmine = features->copy(256, 118, 47, 24);
1907
1908 for(int i=0;i<4;i++) {
1909 icon_trees[i][0] = features->copy(96 + 32*i, 114, 24, 28);
1910 for(int j=1;j<n_tree_frames_c;j++) {
1911 icon_trees[i][j] = icon_trees[i][0]; // no animation for old data available
1912 }
1913 }
1914
1915 delete features;
1916 features = NULL;
1917
1918 drawProgress(70);
1919
1920 background_islands = Image::loadImage("data/mlm_sunrise");
1921 if( background_islands == NULL )
1922 return false;
1923 processImage(background_islands);
1924
1925 drawProgress(80);
1926
1927 return true;
1928 }
1929
loadImages()1930 bool Game::loadImages() {
1931 //int time_s = clock();
1932 // progress should go from 0 to 80%
1933 string gfx_dir = "gfx/";
1934
1935 background = Image::loadImage(gfx_dir + "starfield.jpg");
1936 #if !defined(__ANDROID__) && defined(__DragonFly__)
1937 if( background == NULL ) {
1938 gfx_dir = "/usr/local/share/gigalomania/" + gfx_dir;
1939 LOG("look in %s for gfx\n", gfx_dir.c_str());
1940 background = Image::loadImage(gfx_dir + "starfield.jpg");
1941 }
1942 #endif
1943 if( background == NULL ) {
1944 //return false;
1945 return loadOldImages();
1946 }
1947 drawProgress(20);
1948 //scale_factor = ((float)(scale_width*default_width_c))/(float)player_select->getWidth();
1949 //LOG("scale factor for images = %f\n", scale_factor);
1950
1951 calculateScale(background);
1952 // nb, still scale if scale_factor==1, as this is a way of converting to 8bit
1953 processImage(background);
1954 drawProgress(25);
1955
1956 Image *image_slabs = NULL;
1957 image_slabs = Image::loadImage(gfx_dir + "slabs.png");
1958 if( image_slabs == NULL )
1959 return false;
1960 drawProgress(30);
1961 for(int i=0;i<MAP_N_COLOURS;i++) {
1962 land[i] = image_slabs->copy();
1963 processImage(land[i]);
1964 //land[i]->setMaskColor(255, 0, 255); // need to set the mask colour now, to stop it being multiplied!
1965 }
1966 drawProgress(32);
1967 land[MAP_ORANGE]->brighten(187.5f/255.0f, 96.0f/255.0f, 42.0f/255.0f);
1968 land[MAP_GREEN]->brighten(52.0f/255.0f, 163.5f/255.0f, 52.0f/255.0f);
1969 land[MAP_BROWN]->brighten(116.0f/255.0f, 72.0f/255.0f, 36.0f/255.0f);
1970 land[MAP_WHITE]->brighten(163.5f/255.0f, 163.5f/255.0f, 163.5f/255.0f);
1971 land[MAP_DBROWN]->brighten(120.0f/255.0f, 76.0f/255.0f, 58.0f/255.0f);
1972 land[MAP_DGREEN]->brighten(26/255.0f, 120.0f/255.0f, 26.0f/255.0f);
1973 land[MAP_GREY]->brighten(94.0f/255.0f, 94.0f/255.0f, 94.0f/255.0f);
1974 delete image_slabs;
1975 image_slabs = NULL;
1976
1977 Image *player_heads_select_all = Image::loadImage(gfx_dir + "player_heads_select.png");
1978 if( player_heads_select_all == NULL )
1979 return false;
1980 processImage(player_heads_select_all);
1981 for(int i=0;i<n_players_c;i++) {
1982 player_heads_select[i] = player_heads_select_all->copy(32*i, 0, 32, 25);
1983 }
1984 delete player_heads_select_all;
1985
1986 Image *player_heads_alliance_all = Image::loadImage(gfx_dir + "player_heads_alliance.png");
1987 processImage(player_heads_alliance_all);
1988 if( player_heads_alliance_all == NULL )
1989 return false;
1990 for(int i=0;i<n_players_c;i++) {
1991 player_heads_alliance[i] = player_heads_alliance_all->copy(32*i, 0, 32, 41);
1992 }
1993 delete player_heads_alliance_all;
1994
1995 grave = Image::loadImage(gfx_dir + "grave1.png");
1996 if( grave == NULL )
1997 return false;
1998 processImage(grave);
1999
2000 /*Image *buildings = Image::loadImage(gfx_dir + "buildings.png");
2001 if( buildings != NULL ) {
2002 buildings_shadow = false; // done using alpha channel
2003 buildings->scale(scale_factor, scale_factor);
2004 buildings->setScale(scale_width, scale_height);
2005 for(int i=0;i<n_epochs_c;i++) {
2006 fortress[i] = buildings->copy( 0, 60*i, 58, 60);
2007 mine[i] = buildings->copy( 58, 60*i, 58, 60);
2008 factory[i] = buildings->copy( 174, 60*i, 58, 60);
2009 lab[i] = buildings->copy( 116, 60*i, 58, 60);
2010 }
2011 }*/
2012 for(int i=0;i<n_epochs_c;i++) {
2013 fortress[i] = NULL;
2014 mine[i] = NULL;
2015 factory[i] = NULL;
2016 }
2017 for(int i=0;i<n_epochs_c;i++) {
2018 stringstream filename;
2019 filename << gfx_dir << "building_tower_" << i << ".png";
2020 Image *temp = Image::loadImage(filename.str().c_str());
2021 if( temp == NULL ) {
2022 return false;
2023 }
2024 processImage(temp);
2025 //delete fortress[i];
2026 //fortress[i] = temp->copy(29, 9, 62, 51);
2027 fortress[i] = temp->copy(27, 9, 64, 51);
2028 delete temp;
2029 }
2030 drawProgress(34);
2031 for(int i=mine_epoch_c;i<n_epochs_c-1;i++) {
2032 stringstream filename;
2033 filename << gfx_dir << "building_mine_" << i << ".png";
2034 Image *temp = Image::loadImage(filename.str().c_str());
2035 if( temp == NULL ) {
2036 return false;
2037 }
2038 processImage(temp);
2039 mine[i] = temp->copy(28, 12, 66, 51);
2040 delete temp;
2041 }
2042 drawProgress(36);
2043 for(int i=factory_epoch_c;i<n_epochs_c-1;i++) {
2044 stringstream filename;
2045 filename << gfx_dir << "building_factory_" << i << ".png";
2046 Image *temp = Image::loadImage(filename.str().c_str());
2047 if( temp == NULL ) {
2048 return false;
2049 }
2050 processImage(temp);
2051 //factory[i] = temp->copy(24, 1, 70, 62);
2052 factory[i] = temp->copy(25, 1, 68, 62);
2053 delete temp;
2054 }
2055 drawProgress(38);
2056 for(int i=lab_epoch_c;i<n_epochs_c-1;i++) {
2057 stringstream filename;
2058 filename << gfx_dir << "building_lab_" << i << ".png";
2059 Image *temp = Image::loadImage(filename.str().c_str());
2060 if( temp == NULL ) {
2061 return false;
2062 }
2063 processImage(temp);
2064 //lab[i] = temp->copy(28, 12, 66, 51);
2065 lab[i] = temp->copy(31, 12, 52, 51);
2066 delete temp;
2067 }
2068
2069 drawProgress(40);
2070
2071 Image *icons = Image::loadImage(gfx_dir + "icons.png");
2072 if( icons == NULL )
2073 return false;
2074 /*if( !icons->scaleTo(scale_width*default_width_c) )
2075 return false;*/
2076
2077 // need to do flags beforehand, due to colour remapping
2078 icons->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
2079 for(int i=0;i<n_players_c;i++) { // different locations
2080 /*for(int j=0;j<3;j++)
2081 flags[i][j] = icons->copy(160 + 16*j, 144, 16, 16);
2082 flags[i][3] = icons->copy(160 + 16*1, 144, 16, 16);*/
2083 for(int j=0;j<4;j++)
2084 flags[i][j] = icons->copy(144 + 16*j, 144, 16, 16);
2085 for(int j=0;j<n_flag_frames_c;j++) {
2086 int r = 0, g = 0, b = 0;
2087 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
2088 flags[i][j]->remap(240, 0, 0, r, g, b);
2089 processImage(flags[i][j]);
2090 }
2091 }
2092
2093 processImage(icons);
2094
2095 for(int i=0;i<n_epochs_c;i++)
2096 men[i] = icons->copy(16*i, 0, 16, 16);
2097
2098 unarmed_man = icons->copy(80, 32, 16, 16);
2099
2100 panel_design = icons->copy(304, 0, 16, 16);
2101 //panel_design_dark = panel_design->copy();
2102 panel_lab = icons->copy(16, 33, 16, 16);
2103 panel_shield = icons->copy(240, 0, 16, 16);
2104 panel_defence = icons->copy(256, 0, 16, 16);
2105 panel_attack = icons->copy(272, 0, 16, 16);
2106 panel_knowndesigns = icons->copy(240, 16, 16, 16);
2107 panel_factory = icons->copy(48, 32, 16, 16);
2108 panel_bloody_attack = icons->copy(240, 32, 16, 16);
2109
2110 mine_gatherable_small = icons->copy(160, 0, 16, 16);
2111
2112 //panel_build[BUILDING_TOWER] = icons->copy(224, 63, 16, 16); // different size // not yet used
2113 //panel_build[BUILDING_MINE] = icons->copy(256, 63, 16, 16); // different size
2114 panel_build[BUILDING_MINE] = mine_gatherable_small;
2115 panel_build[BUILDING_FACTORY] = icons->copy(288, 64, 16, 16); // different size
2116 panel_build[BUILDING_LAB] = icons->copy(192, 64, 16, 16); // different size
2117 //panel_build[BUILDING_LAB] = panel_lab;
2118
2119 panel_building[BUILDING_TOWER] = icons->copy(0, 33, 16, 16); // different size
2120 //panel_building[BUILDING_MINE] = icons->copy(32, 33, 16, 16); // different size
2121 panel_building[BUILDING_MINE] = mine_gatherable_small;
2122 panel_building[BUILDING_FACTORY] = panel_factory;
2123 panel_building[BUILDING_LAB] = panel_lab;
2124
2125 mine_gatherable_large = mine_gatherable_small;
2126 panel_bigdesign = panel_design;
2127 panel_biglab = panel_lab;
2128 panel_bigfactory = panel_factory;
2129 panel_bigshield = panel_shield;
2130 panel_bigdefence = panel_defence;
2131 panel_bigattack = panel_attack;
2132 panel_bigbuild = icons->copy(48, 48, 16, 16);
2133 panel_bigknowndesigns = panel_knowndesigns;
2134 panel_twoattack = panel_attack;
2135
2136 for(int i=0;i<n_players_c;i++)
2137 mouse_pointers[i] = icons->copy(176 + 16*i, 0, 16, 16);
2138
2139 for(int i=0;i<13;i++)
2140 icon_clocks[i] = icons->copy(16*i, 16, 16, 16);
2141
2142 /*for(int i=0;i<3;i++)
2143 icon_mice[i] = icons->copy(256 + 16*i, 16, 16, 16); // smaller size*/
2144 for(int i=0;i<2;i++)
2145 icon_mice[i] = icons->copy(256 + 16*i, 16, 16, 16); // smaller size
2146
2147 for(int i=0;i<n_death_flashes_c;i++)
2148 death_flashes[i] = icons->copy(256 + 16*i, 32, 16, 16); // different location and smaller size
2149
2150 for(int i=0;i<7;i++)
2151 blue_flashes[i] = icons->copy(208 + 16*i, 144, 16, 16); // different location (and size for i = 5, 6)
2152
2153 for(int i=0;i<n_players_c;i++) {
2154 icon_towers[i] = icons->copy(160 + 16*i, 81, 6, 6);
2155 icon_armies[i] = icons->copy(160 + 16*i, 87, 4, 4);
2156 }
2157
2158 for(int i=0;i<N_ID;i++)
2159 icon_elements[i] = icons->copy(16*i, 128, 16, 16); // different location
2160
2161 /*for(int i=0;i<n_players_c;i++) { // different locations
2162 for(int j=0;j<3;j++)
2163 flags[i][j] = icons->copy(160 + 16*j, 144, 16, 16);
2164 flags[i][3] = flags[i][1];
2165 for(int j=0;j<n_flag_frames_c;j++) {
2166 int r = 0, g = 0, b = 0;
2167 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
2168 flags[i][j]->remap(240, 0, 0, r, g, b);
2169 }
2170 }*/
2171
2172 for(int i=0;i<n_playershields_c;i++) {
2173 //playershields[i] = icons->copy(16*i, 176, 16, 14); // different location // original version for my gfx
2174 playershields[i] = icons->copy(16*i, 176, 16, 16); // different location
2175 //playershields[i] = icons->copy(16*i, 177, 16, 14); // different location
2176 }
2177
2178 for(int i=0;i<n_shields_c;i++)
2179 icon_shields[i] = icons->copy(112 + 16*i, 32, 16, 16);
2180 for(int i=0;i<n_epochs_c;i++)
2181 icon_defences[i] = icons->copy(16*i, 240, 16, 16); // different location
2182 for(int i=0;i<n_epochs_c;i++)
2183 icon_weapons[i] = icons->copy(16*i, 224, 16, 16); // different location
2184 for(int i=0;i<n_epochs_c;i++)
2185 //numbered_defences[i] = icons->copy(16*i, 173, 16, 16);
2186 numbered_defences[i] = icon_defences[i]; // todo:
2187 for(int i=0;i<n_epochs_c;i++)
2188 //numbered_weapons[i] = icons->copy(16*i, 157, 16, 16);
2189 numbered_weapons[i] = icon_weapons[i]; // todo:
2190
2191 for(int i=0;i<3;i++)
2192 icon_speeds[i] = icons->copy(272 + 16*i, 192, 16, 16); // different location and size
2193
2194 building_health = icons->copy(0, 192, 38, 16);
2195
2196 icon_shield = icons->copy(0, 64, 16, 16);
2197 icon_defence = icons->copy(16, 64, 16, 16);
2198 icon_weapon = icons->copy(32, 64, 16, 16);
2199
2200 /*icon_infinity = icons->copy(128, 112, 12, 8); // original versions for my gfx
2201 icon_bc = icons->copy(224, 15, 12, 8);
2202 icon_ad = icons->copy(208, 16, 12, 8);
2203 icon_ad_shiny = icons->copy(208, 16, 12, 8);*/
2204 icon_infinity = icons->copy(128, 112, 16, 16);
2205 /*icon_bc = icons->copy(224, 15, 16, 16);
2206 icon_ad = icons->copy(208, 16, 16, 16);
2207 icon_ad_shiny = icons->copy(208, 16, 16, 16);*/
2208
2209 numbers_half = icons->copy(112, 112, 7, 4);
2210
2211 icon_nuke_hole = icons->copy(288, 96, 16, 16);
2212
2213 icon_ergo = icons->copy(176, 112, 16, 16);
2214 icon_trash = icons->copy(192, 112, 16, 16);
2215
2216 icons = Image::loadImage(gfx_dir + "explosions_test4.png");
2217 if( icons == NULL )
2218 return false;
2219 drawProgress(42);
2220 processImage(icons);
2221 for(int i=0;i<n_explosions_c;i++) {
2222 int x = (i % 10);
2223 int y = i/10;
2224 int w = 25, h = 25;
2225 explosions[i] = icons->copy(x*w, y*h, w, h);
2226 }
2227
2228 icons = Image::loadImage(gfx_dir + "icons64.png");
2229 if( icons == NULL )
2230 return false;
2231 drawProgress(45);
2232 // replace with new large icons
2233 /*if( !icons->scaleTo(scale_width*128) ) // may need to update width as more icons added!
2234 return false;*/
2235 processImage(icons);
2236
2237 mapsquare = icons->copy(0, 0, 17, 17);
2238 flashingmapsquare = icons->copy(32, 0, 17, 17);
2239 arrow_left = icons->copy(64, 0, 32, 32);
2240 arrow_left->scaleAlpha(0.625f);
2241 arrow_right = icons->copy(96, 0, 32, 32);
2242 arrow_right->scaleAlpha(0.625f);
2243
2244 icons = Image::loadImage(gfx_dir + "font.png");
2245 if( icons == NULL )
2246 return false;
2247 drawProgress(48);
2248 //processImage(icons);
2249 //const int font_w = 6;
2250 //const int font_h = 10;
2251 //const int font_w = 8;
2252 //const int font_h = 16;
2253 int font_w = 12;
2254 int font_h = 16;
2255 for(int i=0;i<10;i++) {
2256 //int xpos = 8*i;
2257 //int ypos = 0;
2258 //int xpos = 136+9*i;
2259 //int ypos = 1;
2260 int xpos = 196+13*i;
2261 int ypos = 1;
2262 //int xpos = 512+32*i+4;
2263 //int ypos = 0;
2264 numbers_blue[i] = icons->copy(xpos, ypos, font_w, font_h);
2265 numbers_grey[i] = icons->copy(xpos, ypos, font_w, font_h);
2266 numbers_white[i] = icons->copy(xpos, ypos, font_w, font_h);
2267 numbers_orange[i] = icons->copy(xpos, ypos, font_w, font_h);
2268 numbers_yellow[i] = icons->copy(xpos, ypos, font_w, font_h);
2269 for(int j=0;j<4;j++) {
2270 numbers_small[j][i] = icons->copy(xpos, ypos, font_w, font_h);
2271 }
2272 numbers_small[0][i]->brighten(1.0f, 0.0f, 0.0f);
2273 numbers_small[1][i]->brighten(0.0f, 1.0f, 0.0f);
2274 numbers_small[2][i]->brighten(1.0f, 1.0f, 0.0f);
2275 numbers_small[3][i]->brighten(0.0f, 0.0f, 1.0f);
2276
2277 processImage(numbers_blue[i]);
2278 processImage(numbers_grey[i]);
2279 processImage(numbers_white[i]);
2280 processImage(numbers_orange[i]);
2281 processImage(numbers_yellow[i]);
2282 for(int j=0;j<4;j++) {
2283 processImage(numbers_small[j][i]);
2284 }
2285 }
2286 for(int i=0;i<n_font_chars_c;i++) {
2287 letters_small[i] = NULL;
2288 }
2289 for(int i=0;i<26;i++) {
2290 //int xpos = 80+8*i;
2291 //int ypos = 16;
2292 //int xpos = 289+9*i;
2293 //int ypos = 1;
2294 int xpos = 417+13*i;
2295 int ypos = 1;
2296 //int xpos = 1056+32*i+4;
2297 //int ypos = 0;
2298 letters_small[i] = icons->copy(xpos, ypos, font_w, font_h);
2299 }
2300 //numbers_half = icons->copy(288, 16, font_w, font_h);
2301 /*letters_small[font_index_period_c] = icons->copy(296, 16, font_w, font_h);
2302 letters_small[font_index_apostrophe_c] = icons->copy(304, 16, font_w, font_h);
2303 letters_small[font_index_exclamation_c] = icons->copy(312, 16, font_w, font_h);
2304 letters_small[font_index_question_c] = icons->copy(320, 16, font_w, font_h);*/
2305 letters_small[font_index_period_c] = icons->copy(170, 1, font_w, font_h);
2306 letters_small[font_index_apostrophe_c] = icons->copy(66, 1, font_w, font_h);
2307 letters_small[font_index_exclamation_c] = icons->copy(1, 1, font_w, font_h);
2308 letters_small[font_index_question_c] = icons->copy(391, 1, font_w, font_h);
2309 letters_small[font_index_comma_c] = icons->copy(144, 1, font_w, font_h);
2310 letters_small[font_index_dash_c] = icons->copy(157, 1, font_w, font_h);
2311 for(int i=0;i<n_font_chars_c;i++) {
2312 if( letters_small[i] != NULL )
2313 processImage(letters_small[i]);
2314 }
2315 dash_grey = letters_small[font_index_dash_c];
2316
2317 delete icons;
2318 drawProgress(50);
2319
2320 icons = Image::loadImage(gfx_dir + "font_large.png");
2321 if( icons == NULL )
2322 return false;
2323 drawProgress(48);
2324 font_w = 24;
2325 font_h = 32;
2326 for(int i=0;i<10;i++) {
2327 int xpos = 196+13*i;
2328 int ypos = 1;
2329 xpos *= 2;
2330 ypos *= 2;
2331 numbers_largegrey[i] = icons->copy(xpos, ypos, font_w, font_h);
2332 numbers_largeshiny[i] = icons->copy(xpos, ypos, font_w, font_h);
2333
2334 processImage(numbers_largegrey[i]);
2335 processImage(numbers_largeshiny[i]);
2336 }
2337 for(int i=0;i<n_font_chars_c;i++) {
2338 letters_large[i] = NULL;
2339 }
2340 for(int i=0;i<26;i++) {
2341 int xpos = 417+13*i;
2342 int ypos = 1;
2343 xpos *= 2;
2344 ypos *= 2;
2345 letters_large[i] = icons->copy(xpos, ypos, font_w, font_h);
2346 }
2347 letters_large[font_index_period_c] = icons->copy(170*2, 1*2, font_w, font_h);
2348 letters_large[font_index_apostrophe_c] = icons->copy(66*2, 1*2, font_w, font_h);
2349 letters_large[font_index_exclamation_c] = icons->copy(1*2, 1*2, font_w, font_h);
2350 letters_large[font_index_question_c] = icons->copy(391*2, 1*2, font_w, font_h);
2351 letters_large[font_index_comma_c] = icons->copy(144*2, 1*2, font_w, font_h);
2352 letters_large[font_index_dash_c] = icons->copy(157*2, 1*2, font_w, font_h);
2353 for(int i=0;i<n_font_chars_c;i++) {
2354 if( letters_large[i] != NULL )
2355 processImage(letters_large[i]);
2356 }
2357
2358 smoke_image = Image::createRadial((int)(scale_width * 16), (int)(scale_height * 16), 0.5f);
2359 processImage(smoke_image);
2360
2361 for(int i=0;i<n_coast_c;i++)
2362 coast_icons[i] = NULL;
2363 map_sq_offset = 0;
2364 int map_width = (int)(scale_width * 16);
2365 int map_height = (int)(scale_height * 16);
2366 {
2367 unsigned char filter_max[3] = {255, 192, 84};
2368 unsigned char filter_min[3] = {120, 0, 0};
2369 map_sq[MAP_ORANGE][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2370 }
2371 {
2372 unsigned char filter_max[3] = {104, 255, 104};
2373 unsigned char filter_min[3] = {0, 72, 0};
2374 map_sq[MAP_GREEN][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2375 }
2376 {
2377 unsigned char filter_max[3] = {216, 144, 72};
2378 unsigned char filter_min[3] = {16, 0, 0};
2379 map_sq[MAP_BROWN][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2380 }
2381 {
2382 unsigned char filter_max[3] = {255, 255, 255};
2383 unsigned char filter_min[3] = {72, 72, 72};
2384 map_sq[MAP_WHITE][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2385 }
2386 {
2387 unsigned char filter_max[3] = {224, 152, 116};
2388 unsigned char filter_min[3] = {16, 0, 0};
2389 map_sq[MAP_DBROWN][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2390 }
2391 {
2392 unsigned char filter_max[3] = {52, 232, 52};
2393 unsigned char filter_min[3] = {0, 8, 0};
2394 map_sq[MAP_DGREEN][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2395 }
2396 {
2397 unsigned char filter_max[3] = {188, 188, 188};
2398 unsigned char filter_min[3] = {0, 0, 0};
2399 map_sq[MAP_GREY][0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max, filter_min, Image::NOISEMODE_PERLIN, 4);
2400 }
2401 for(int i=0;i<MAP_N_COLOURS;i++) {
2402 map_sq[i][0]->setScale(scale_width, scale_height);
2403 for(int j=1;j<n_map_sq_c;j++) {
2404 map_sq[i][j] = map_sq[i][0]->copy(0, 0, 16, 16);
2405 }
2406 }
2407 drawProgress(53);
2408
2409 map_sq_coast_offset = 0;
2410 unsigned char filter_max_ocean[3] = {180, 215, 240};
2411 unsigned char filter_min_ocean[3] = {60, 95, 115};
2412 coast_icons[0] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2413 coast_icons[0]->fadeAlpha(false, false);
2414 coast_icons[1] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2415 coast_icons[1]->fadeAlpha(true, true);
2416 coast_icons[2] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2417 coast_icons[2]->fadeAlpha(false, true);
2418 coast_icons[3] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2419 coast_icons[3]->fadeAlpha(true, false);
2420
2421 coast_icons[4] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2422 coast_icons[4]->fadeAlpha(true, false);
2423 coast_icons[4]->fadeAlpha(false, false);
2424 coast_icons[5] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2425 coast_icons[5]->fadeAlpha(true, true);
2426 coast_icons[5]->fadeAlpha(false, false);
2427 coast_icons[6] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2428 coast_icons[6]->fadeAlpha(true, false);
2429 coast_icons[6]->fadeAlpha(false, true);
2430 coast_icons[7] = Image::createNoise(map_width, map_height, 4.0f, 4.0f, filter_max_ocean, filter_min_ocean, Image::NOISEMODE_PERLIN, 4);
2431 coast_icons[7]->fadeAlpha(true, true);
2432 coast_icons[7]->fadeAlpha(false, true);
2433
2434 for(int i=0;i<8;i++) {
2435 coast_icons[i]->setScale(scale_width, scale_height);
2436 }
2437
2438 drawProgress(55);
2439
2440 {
2441 // initialise
2442 for(int i=0;i<n_players_c;i++) {
2443 for(int j=0;j<=n_epochs_c;j++) {
2444 for(int k=0;k<n_attacker_directions_c;k++) {
2445 for(int l=0;l<max_attacker_frames_c;l++) {
2446 attackers_walking[i][j][k][l] = NULL;
2447 }
2448 }
2449 }
2450 }
2451 for(int j=0;j<=n_epochs_c;j++) {
2452 for(int k=0;k<n_attacker_directions_c;k++) {
2453 n_attacker_frames[j][k] = 0;
2454 }
2455 }
2456 for(int i=0;i<n_epochs_c;i++)
2457 for(int j=0;j<N_ATTACKER_AMMO_DIRS;j++)
2458 attackers_ammo[i][j] = NULL;
2459
2460 Image *gfx_def_image = Image::loadImage(gfx_dir + "defenders.png");
2461 if( gfx_def_image == NULL )
2462 return false;
2463 drawProgress(58);
2464 gfx_def_image->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
2465 for(int k=0;k<n_players_c;k++) {
2466 int r = 0, g = 0, b = 0;
2467 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
2468 for(int i=0;i<9;i++) {
2469 n_defender_frames[i] = 8;
2470 ASSERT( n_defender_frames[i] <= max_defender_frames_c );
2471 for(int j=0;j<n_defender_frames[i];j++) {
2472 defenders[k][i][j] = gfx_def_image->copy(16*i, 0, 16, 16);
2473 defenders[k][i][j]->remap(240, 0, 0, r, g, b);
2474 processImage(defenders[k][i][j]);
2475 if( i == 8 ) {
2476 defenders[k][i][j]->setOffset(-1, 0);
2477 }
2478 }
2479 }
2480 }
2481 delete gfx_def_image;
2482
2483 gfx_def_image = Image::loadImage(gfx_dir + "defender_9.png");
2484 if( gfx_def_image == NULL )
2485 return false;
2486 gfx_def_image->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
2487 for(int k=0;k<n_players_c;k++) {
2488 int r = 0, g = 0, b = 0;
2489 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
2490 n_defender_frames[9] = 11;
2491 ASSERT( n_defender_frames[9] <= max_defender_frames_c );
2492 for(int j=0;j<n_defender_frames[9];j++) {
2493 //defenders[k][9][j] = gfx_def_image->copy(16*j, 0, 16, 16);
2494 defenders[k][9][j] = gfx_def_image->copy(32*j+8, 9, 16, 18);
2495 defenders[k][9][j]->remap(240, 0, 0, r, g, b);
2496 processImage(defenders[k][9][j]);
2497 defenders[k][9][j]->setOffset(0, -4);
2498 }
2499 }
2500 delete gfx_def_image;
2501
2502 for(int i=0;i<=5;i++) {
2503 if( !loadAttackersWalkingImages(gfx_dir, i) ) {
2504 return false;
2505 }
2506 }
2507
2508 if( !loadAttackersWalkingImages(gfx_dir, n_epochs_c) ) {
2509 return false;
2510 }
2511 drawProgress(60);
2512
2513 Image *gfx_planes = Image::loadImage(gfx_dir + "attacker_flying.png");
2514 if( gfx_planes == NULL )
2515 return false;
2516 drawProgress(62);
2517 /*if( !gfx_planes->scaleTo(scale_width*default_width_c) )
2518 return false;*/
2519 gfx_planes->setScale(scale_width/scale_factor_w, scale_height/scale_factor_h); // so the copying will work at the right scale for the input image
2520 // do remapping before scaling
2521 for(int i=0;i<n_players_c;i++) {
2522 int r = 0, g = 0, b = 0;
2523 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
2524 for(int j=0;j<n_saucer_frames_c;j++) {
2525 saucers[i][j] = gfx_planes->copy(32*j, 64, 32, 32);
2526 saucers[i][j]->remap(240, 0, 0, r, g, b);
2527 processImage(saucers[i][j]);
2528 }
2529 }
2530 // do remapping before scaling
2531 for(int i=0;i<n_players_c;i++) {
2532 //nukes[i][0] = gfx_planes->copy(64*i, 32, 32, 32);
2533 //nukes[i][1] = gfx_planes->copy(64*i+32, 32, 32, 32);
2534 nukes[i][0] = gfx_planes->copy(0, 32, 32, 32);
2535 nukes[i][1] = gfx_planes->copy(32, 32, 32, 32);
2536 int r = 0, g = 0, b = 0;
2537 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)i);
2538 for(int j=0;j<2;j++) {
2539 nukes[i][j]->remap(240, 0, 0, r, g, b);
2540 processImage(nukes[i][j]);
2541 }
2542 }
2543 // now remap
2544 processImage(gfx_planes);
2545 for(int i=0;i<n_players_c;i++) {
2546 for(int j=0;j<n_epochs_c;j++)
2547 planes[i][j] = NULL;
2548 planes[i][6] = gfx_planes->copy(32*i, 0, 32, 32);
2549 planes[i][7] = gfx_planes->copy(128+32*i, 0, 32, 32);
2550 }
2551 delete gfx_planes;
2552
2553 Image *gfx_ammo = Image::loadImage(gfx_dir + "attacker_ammo.png");
2554 if( gfx_ammo == NULL )
2555 return false;
2556 drawProgress(65);
2557 /*if( !gfx_ammo->scaleTo(scale_width*default_width_c) )
2558 return false;*/
2559 processImage(gfx_ammo);
2560 for(int i=0;i<6;i++) {
2561 attackers_ammo[i][ATTACKER_AMMO_RIGHT] = gfx_ammo->copy(0, 16*i, 16, 16);
2562 attackers_ammo[i][ATTACKER_AMMO_LEFT] = gfx_ammo->copy(16, 16*i, 16, 16);
2563 attackers_ammo[i][ATTACKER_AMMO_UP] = gfx_ammo->copy(32, 16*i, 16, 16);
2564 attackers_ammo[i][ATTACKER_AMMO_DOWN] = gfx_ammo->copy(48, 16*i, 16, 16);
2565 }
2566 // bombs
2567 attackers_ammo[6][ATTACKER_AMMO_BOMB] = gfx_ammo->copy(0, 96, 8, 16);
2568 delete gfx_ammo;
2569
2570 /*nuke_defences[0] = armies->copy(192, 256, 16, 16);
2571 nuke_defences[1] = armies->copy(208, 256, 16, 16);
2572 nuke_defences[2] = armies->copy(192, 272, 16, 16);
2573 nuke_defences[3] = armies->copy(208, 272, 16, 16);*/
2574 for(int k=0;k<n_players_c;k++) {
2575 nuke_defences[k] = defenders[k][nuclear_epoch_c][0];
2576 }
2577
2578 attackers_ammo[7][ATTACKER_AMMO_BOMB] = attackers_ammo[6][ATTACKER_AMMO_BOMB];
2579 attackers_ammo[9][ATTACKER_AMMO_BOMB] = Image::createRadial((int)(scale_width * 16), (int)(scale_height * 16), 1.0f, 0, 255, 255);
2580 processImage(attackers_ammo[9][ATTACKER_AMMO_BOMB]);
2581
2582 for(int i=0;i<=n_epochs_c;i++) {
2583 if( i == 6 || i == 7 || i == 8 || i == 9 )
2584 continue;
2585 for(int player=0;player<n_players_c;player++) {
2586 for(int dir=0;dir<n_attacker_directions_c;dir++) {
2587 int n_frames = n_attacker_frames[i][dir];
2588 for(int frame=0;frame<n_frames;frame++) {
2589 int r = 0, g = 0, b = 0;
2590 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)player);
2591 ASSERT(attackers_walking[player][i][dir][frame] != NULL);
2592 attackers_walking[player][i][dir][frame]->remap(240, 0, 0, r, g, b);
2593 }
2594 }
2595 }
2596 }
2597 for(int i=0;i<n_saucer_frames_c;i++) {
2598 for(int k=0;k<n_players_c;k++) {
2599 int r = 0, g = 0, b = 0;
2600 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
2601 saucers[k][i]->remap(240, 0, 0, r, g, b);
2602 }
2603 }
2604 for(int i=0;i<n_epochs_c;i++) {
2605 if( defenders[0][i][0] == NULL )
2606 continue;
2607 for(int j=0;j<n_defender_frames[i];j++) {
2608 for(int k=0;k<n_players_c;k++) {
2609 int r = 0, g = 0, b = 0;
2610 PlayerType::getColour(&r, &g, &b, (PlayerType::PlayerTypeID)k);
2611 defenders[k][i][j]->remap(240, 0, 0, r, g, b);
2612 }
2613 }
2614 }
2615 }
2616
2617 // features
2618 Image *gfx_features = Image::loadImage(gfx_dir + "features.png");
2619 if( gfx_features == NULL )
2620 return false;
2621 /*if( !gfx_features->scaleTo(scale_width*default_width_c) )
2622 return false;*/
2623 processImage(gfx_features);
2624 icon_openpitmine = gfx_features->copy(0, 0, 47, 24);
2625
2626 icon_trees[0][0] = Image::loadImage(gfx_dir + "tree2_00.png");
2627 icon_trees[0][1] = Image::loadImage(gfx_dir + "tree2_01.png");
2628 icon_trees[0][2] = Image::loadImage(gfx_dir + "tree2_02.png");
2629 icon_trees[0][3] = Image::loadImage(gfx_dir + "tree2_03.png");
2630
2631 icon_trees[1][0] = Image::loadImage(gfx_dir + "tree3_00.png");
2632 icon_trees[1][1] = Image::loadImage(gfx_dir + "tree3_01.png");
2633 icon_trees[1][2] = Image::loadImage(gfx_dir + "tree3_02.png");
2634 icon_trees[1][3] = Image::loadImage(gfx_dir + "tree3_03.png");
2635
2636 // [2][] is the nuked tree image
2637 icon_trees[2][0] = Image::loadImage(gfx_dir + "deadtree1_00.png");
2638 for(int j=1;j<n_tree_frames_c;j++) {
2639 icon_trees[2][j] = icon_trees[2][0]->copy(); // no animation for nuked tree
2640 }
2641
2642 icon_trees[3][0] = Image::loadImage(gfx_dir + "tree5_00.png");
2643 icon_trees[3][1] = Image::loadImage(gfx_dir + "tree5_01.png");
2644 icon_trees[3][2] = Image::loadImage(gfx_dir + "tree5_02.png");
2645 icon_trees[3][3] = Image::loadImage(gfx_dir + "tree5_03.png");
2646
2647 for(int i=0;i<n_trees_c;i++) {
2648 for(int j=0;j<n_tree_frames_c;j++) {
2649 if( icon_trees[i][j] == NULL )
2650 return false;
2651 processImage(icon_trees[i][j]);
2652 }
2653 }
2654
2655 icon_clutter.push_back(Image::loadImage(gfx_dir + "boulders.png"));
2656 icon_clutter.push_back(Image::loadImage(gfx_dir + "boulders2.png"));
2657 icon_clutter.push_back(Image::loadImage(gfx_dir + "bigboulder.png"));
2658 icon_clutter.push_back(Image::loadImage(gfx_dir + "rocks.png"));
2659 icon_clutter.push_back(Image::loadImage(gfx_dir + "plant.png"));
2660 icon_clutter.push_back(Image::loadImage(gfx_dir + "grass.png"));
2661 icon_clutter.push_back(Image::loadImage(gfx_dir + "grasses01.png"));
2662 icon_clutter.push_back(Image::loadImage(gfx_dir + "grasses02.png"));
2663 icon_clutter.push_back(Image::loadImage(gfx_dir + "grasses04.png"));
2664 icon_clutter.push_back(Image::loadImage(gfx_dir + "grasses05.png"));
2665 icon_clutter.push_back(Image::loadImage(gfx_dir + "shrub2-01.png"));
2666 icon_clutter.push_back(Image::loadImage(gfx_dir + "swirl01.png"));
2667 icon_clutter.push_back(Image::loadImage(gfx_dir + "weed01.png"));
2668 icon_clutter.push_back(Image::loadImage(gfx_dir + "weed02.png"));
2669 icon_clutter.push_back(Image::loadImage(gfx_dir + "weed03.png"));
2670 icon_clutter.push_back(Image::loadImage(gfx_dir + "weed04.png"));
2671 for(size_t i=0;i<icon_clutter.size();i++) {
2672 if( icon_clutter[i] == NULL )
2673 return false;
2674 processImage(icon_clutter[i]);
2675 }
2676 icon_clutter_nuked.push_back(Image::loadImage(gfx_dir + "bones.png"));
2677 icon_clutter_nuked.push_back(Image::loadImage(gfx_dir + "skulls.png"));
2678 for(size_t i=0;i<icon_clutter_nuked.size();i++) {
2679 if( icon_clutter_nuked[i] == NULL )
2680 return false;
2681 processImage(icon_clutter_nuked[i]);
2682 }
2683 drawProgress(70);
2684
2685 background_islands = background;
2686
2687 // finished loading/extracting images
2688
2689 drawProgress(80);
2690 /*int time_taken = clock() - time_s;
2691 LOG("time taken to load images: %d (%d=1sec)\n", time_taken, CLOCKS_PER_SEC);*/
2692 return true;
2693 }
2694
setupInventions()2695 void Game::setupInventions() {
2696 /*for(int i=0;i<n_shields_c;i++)
2697 invention_shields[i] = new Invention("SHIELD", Invention::SHIELD, start_epoch + i);*/
2698 for(int i=0;i<n_epochs_c;i++)
2699 invention_shields[i] = new Invention("SHIELD", Invention::SHIELD, i);
2700
2701 invention_defences[0] = new Invention("STICK", Invention::DEFENCE, 0);
2702 invention_defences[1] = new Invention("SPEAR", Invention::DEFENCE, 1);
2703 invention_defences[2] = new Invention("SHORTBOW", Invention::DEFENCE, 2);
2704 invention_defences[3] = new Invention("CAULDRON OF OIL", Invention::DEFENCE, 3);
2705 invention_defences[4] = new Invention("CROSSBOW", Invention::DEFENCE, 4);
2706 invention_defences[5] = new Invention("RIFLE", Invention::DEFENCE, 5);
2707 invention_defences[6] = new Invention("MACHINE GUN", Invention::DEFENCE, 6);
2708 invention_defences[7] = new Invention("ROCKET", Invention::DEFENCE, 7);
2709 invention_defences[8] = new Invention("NUCLEAR DEFENCE", Invention::DEFENCE, 8);
2710 invention_defences[9] = new Invention("SDI LASER", Invention::DEFENCE, 9);
2711
2712 invention_weapons[0] = new Weapon("ROCK WEAPON", 0, 1);
2713 //invention_weapons[1] = new Weapon("CATAPULT", 1, 1);
2714 invention_weapons[1] = new Weapon("SWORD", 1, 1);
2715 invention_weapons[2] = new Weapon("PIKE", 2, 1);
2716 invention_weapons[3] = new Weapon("LONGBOW", 3, 1);
2717 invention_weapons[4] = new Weapon("TREBUCHET", 4, 2);
2718 invention_weapons[5] = new Weapon("CANNON", 5, 3);
2719 invention_weapons[6] = new Weapon("BIPLANE", 6, 2);
2720 invention_weapons[7] = new Weapon("BOMBER", 7, 3);
2721 invention_weapons[8] = new Weapon("NUCLEAR MISSILE", 8, 0);
2722 invention_weapons[9] = new Weapon("SPACESHIP", 9, 8);
2723 }
2724
setupElements()2725 void Game::setupElements() {
2726 elements[WOOD] = new Element("WOOD", WOOD, Element::GATHERABLE);
2727 elements[ROCK] = new Element("ROCK", ROCK, Element::GATHERABLE);
2728 elements[BONE] = new Element("BONE", BONE, Element::GATHERABLE);
2729 elements[SLATE] = new Element("SLATE", SLATE, Element::GATHERABLE);
2730 elements[MOONLITE] = new Element("MOONLITE", MOONLITE, Element::OPENPITMINE);
2731 elements[PLANETARIUM] = new Element("PLANETARIUM", PLANETARIUM, Element::OPENPITMINE);
2732 elements[BETHLIUM] = new Element("BETHLIUM", BETHLIUM, Element::OPENPITMINE);
2733 elements[SOLARIUM] = new Element("SOLARIUM", SOLARIUM, Element::OPENPITMINE);
2734 elements[ARULDITE] = new Element("ARULDITE", ARULDITE, Element::DEEPMINE);
2735 elements[HERBIRITE] = new Element("HERBIRITE", HERBIRITE, Element::DEEPMINE);
2736 elements[YERIDIUM] = new Element("YERIDIUM", YERIDIUM, Element::DEEPMINE);
2737 elements[VALIUM] = new Element("VALIUM", VALIUM, Element::DEEPMINE);
2738 elements[PARASITE] = new Element("PARASITE", PARASITE, Element::DEEPMINE);
2739 elements[AQUARIUM] = new Element("AQUARIUM", AQUARIUM, Element::DEEPMINE);
2740 elements[PALADIUM] = new Element("PALADIUM", PALADIUM, Element::DEEPMINE);
2741 elements[ONION] = new Element("ONION", ONION, Element::DEEPMINE);
2742 elements[TEDIUM] = new Element("TEDIUM", TEDIUM, Element::DEEPMINE);
2743 elements[MORON] = new Element("MORON", MORON, Element::DEEPMINE);
2744 elements[MARMITE] = new Element("MAAMITE", MARMITE, Element::DEEPMINE);
2745 elements[ALIEN] = new Element("ALIEN", ALIEN, Element::DEEPMINE);
2746 }
2747
cleanupPlayers()2748 void Game::cleanupPlayers() {
2749 for(int i=0;i<n_players_c;i++) {
2750 if( players[i] )
2751 delete players[i];
2752 players[i] = NULL;
2753 }
2754
2755 // need to reset alliance flags!
2756 Player::resetAllAlliances();
2757 }
2758
setupPlayers()2759 void Game::setupPlayers() {
2760 LOG("Game::setupPlayers()");
2761 cleanupPlayers();
2762
2763 // 0 - Red team
2764 // 1 - Green team
2765 // 2 - Yellow team
2766 // 3 - Blue team
2767
2768 int n_opponents = maps[start_epoch][selected_island]->getNOpponents();
2769 ASSERT( n_opponents+1 <= maps[start_epoch][selected_island]->getNSquares() );
2770 int n_free = 4;
2771 if( human_player != PLAYER_DEMO ) {
2772 players[human_player] = new Player(true, human_player);
2773 n_free--;
2774 }
2775 else {
2776 n_opponents++;
2777 }
2778
2779 if( gameMode == GAMEMODE_SINGLEPLAYER ) {
2780 for(int i=0;i<n_opponents && n_free > 0;i++) {
2781 int indx = rand() % n_free;
2782 for(int j=0;j<4;j++) {
2783 if( players[j] == NULL ) {
2784 if( indx == 0 ) {
2785 players[j] = new Player(false, j);
2786 n_free--;
2787 break;
2788 }
2789 indx--;
2790 }
2791 }
2792 }
2793 }
2794
2795 }
2796
2797 // get the full desktop resolution
getDesktopResolution(int * user_width,int * user_height) const2798 void Game::getDesktopResolution(int *user_width, int *user_height) const {
2799 #if AROS
2800 // AROS doesn't have latest SDL version with SDL_GetVideoInfo, so use native code!
2801 getAROSScreenSize(user_width, user_height);
2802 #elif defined(__MORPHOS__)
2803 // MorphOS doesn't have latest SDL version with SDL_GetVideoInfo, so use native code!
2804 getAROSScreenSize(user_width, user_height);
2805 #else
2806
2807 #if SDL_MAJOR_VERSION == 1
2808 const SDL_VideoInfo *videoInfo = SDL_GetVideoInfo();
2809 *user_width = videoInfo->current_w;
2810 *user_height = videoInfo->current_h;
2811 LOG("desktop is %d x %d\n", *user_width, *user_height);
2812 #else
2813 SDL_DisplayMode displayMode;
2814 if( SDL_GetDesktopDisplayMode(0, &displayMode) != 0 ) {
2815 LOG("SDL_GetDesktopDisplayMode failed!");
2816 *user_width = 640;
2817 *user_height = 480;
2818 }
2819 else {
2820 *user_width = displayMode.w;
2821 *user_height = displayMode.h;
2822 LOG("desktop is %d x %d\n", *user_width, *user_height);
2823 }
2824 #endif
2825
2826 #endif
2827 }
2828
openScreen(bool fullscreen)2829 bool Game::openScreen(bool fullscreen) {
2830 if( !fullscreen ) {
2831 int user_width = 0, user_height = 0;
2832 #if defined(_WIN32)
2833 //#if 0
2834 // we do it using system calls instead of getDesktopResolution(), to ignore the start bar (if showing)
2835 RECT rect;
2836 SystemParametersInfo(SPI_GETWORKAREA, 0, &rect, 0);
2837 user_width = rect.right - rect.left;
2838 user_height = rect.bottom - rect.top;
2839 LOG("desktop is %d x %d\n", user_width, user_height);
2840 user_height -= GetSystemMetrics(SM_CYCAPTION); // also ignore the window height
2841 LOG("available height is %d\n", user_height);
2842 #else
2843 getDesktopResolution(&user_width, &user_height);
2844 #endif
2845
2846 #if SDL_MAJOR_VERSION == 1
2847 // Only allow integer scaling, otherwise have poor quality scaling (especially for the font)
2848 //user_width = 1184;
2849 //user_height = 720;
2850
2851 if( user_width >= 4*default_width_c ) {
2852 scale_width = 4.0f;
2853 LOG("scale width 4x\n");
2854 }
2855 else if( user_width >= 3*default_width_c ) {
2856 scale_width = 3.0f;
2857 LOG("scale width 3x\n");
2858 }
2859 else if( user_width >= 2*default_width_c ) {
2860 scale_width = 2.0f;
2861 LOG("scale width 2x\n");
2862 }
2863 else if( user_width >= default_width_c ) {
2864 scale_width = 1.0f;
2865 LOG("scale width 1x\n");
2866 }
2867 else {
2868 LOG("desktop resolution too small even for 1x scale width\n");
2869 return false;
2870 }
2871
2872 if( user_height >= 4*default_height_c ) {
2873 scale_height = 4.0f;
2874 LOG("scale height 4x\n");
2875 }
2876 else if( user_height >= 3*default_height_c ) {
2877 scale_height = 3.0f;
2878 LOG("scale height 3x\n");
2879 }
2880 else if( user_height >= 2*default_height_c ) {
2881 scale_height = 2.0f;
2882 LOG("scale height 2x\n");
2883 }
2884 else if( user_height >= default_height_c ) {
2885 scale_height = 1.0f;
2886 LOG("scale height 1x\n");
2887 }
2888 else {
2889 LOG("desktop resolution too small even for 1x scale height\n");
2890 return false;
2891 }
2892
2893 // better to have uniform scaling, so we have 1:1 aspect ratio
2894 scale_width = std::min(scale_width, scale_height);
2895 scale_height = scale_width;
2896
2897 //scale_width = 2.0f; scale_height = 1.5f;
2898 //scale_width = scale_height = 1.0f; // test
2899 //scale_width = scale_height = 2.0f; // test
2900 //scale_width = 1.0f;
2901 //scale_height = 1.0f;
2902
2903 int screen_width = (int)(scale_width * default_width_c);
2904 int screen_height = (int)(scale_height * default_height_c);
2905 #else
2906 // with SDL2, we let SDL do the scaling via SDL_RenderSetLogicalSize, so we don't have to do the scaling ourselves, and can set the screen width/height to whatever we like
2907 // for windowed mode, we pick a suitable size based on the available desktop space
2908 int screen_width = default_width_c;
2909 int screen_height = default_height_c;
2910
2911 while( 2*screen_width <= user_width && 2*screen_height <= user_height ) {
2912 screen_width *= 2;
2913 screen_height *= 2;
2914 }
2915 //screen_width = 480;
2916 //screen_height = 320;
2917 //screen_width = 640;
2918 //screen_height = 480;
2919 #endif
2920
2921 screen = new Screen();
2922 if( !screen->open(screen_width, screen_height, fullscreen) )
2923 return false;
2924
2925 }
2926 else {
2927 // fullscreen
2928 screen = new Screen();
2929
2930 #if SDL_MAJOR_VERSION == 1
2931 if( screen->open(4*default_width_c, 4*default_height_c, fullscreen) ) {
2932 scale_width = scale_height = 4.0f;
2933 LOG("scale 4x\n");
2934 }
2935 else if( screen->open(3*default_width_c, 3*default_height_c, fullscreen) ) {
2936 scale_width = scale_height = 3.0f;
2937 LOG("scale 3x\n");
2938 }
2939 else if( screen->open(2*default_width_c, 2*default_height_c, fullscreen) ) {
2940 scale_width = scale_height = 2.0f;
2941 LOG("scale 2x\n");
2942 }
2943 else if( screen->open(default_width_c, default_height_c, fullscreen) ) {
2944 scale_width = scale_height = 1.0f;
2945 LOG("scale 1x\n");
2946 }
2947 else {
2948 LOG("can't even open screen at 1x scale\n");
2949 return false;
2950 }
2951 #else
2952 // With SDL2, we let SDL do the scaling via SDL_RenderSetLogicalSize, so we don't have to do the scaling ourselves
2953 // for fullscreen, the supplied width/height is ignored, as we always run at the native resolution.
2954 int user_width = 0, user_height = 0;
2955 #ifdef _WIN32
2956 // However, on Windows 10, there is a bug in the task view that Gigalomania shows as a tiny window, unless we've opened the screen with the desktop resolution
2957 getDesktopResolution(&user_width, &user_height);
2958 #endif
2959 if( !screen->open(user_width, user_height, fullscreen) ) {
2960 LOG("can't open screen\n");
2961 return false;
2962 }
2963 #endif
2964
2965 }
2966
2967 char buffer[256] = "";
2968 sprintf(buffer, "Gigalomania, version %d.%d - Loading...", majorVersion, minorVersion);
2969 screen->setTitle(buffer);
2970 return true;
2971 }
2972
readMapProcessLine(int * epoch,int * index,Map ** l_map,char * line,const int MAX_LINE,const char * filename)2973 bool Game::readMapProcessLine(int *epoch, int *index, Map **l_map, char *line, const int MAX_LINE, const char *filename) {
2974 bool ok = true;
2975 line[ strlen(line) - 1 ] = '\0'; // trim new line
2976 line[ strlen(line) - 1 ] = '\0'; // trim carriage return
2977 //LOG("line: %s\n", line);
2978 if( *l_map == NULL ) {
2979 if( line[0] != '#' ) {
2980 LOG("expected first character to be '#'\n");
2981 ok = false;
2982 return ok;
2983 }
2984
2985 //char name[MAX_LINE+1] = "";
2986 int n_opponents = -1;
2987 //char colname[MAX_LINE+1] = "";
2988
2989 char *ptr = strtok(&line[1], " ");
2990 if( ptr == NULL ) {
2991 LOG("can't find map name\n");
2992 ok = false;
2993 return ok;
2994 }
2995 //strcpy(name, ptr);
2996 string name = ptr;
2997
2998 ptr = strtok(NULL, " ");
2999 if( ptr == NULL ) {
3000 LOG("can't find epoch\n");
3001 ok = false;
3002 return ok;
3003 }
3004 *epoch = atoi(ptr);
3005
3006 ptr = strtok(NULL, " ");
3007 if( ptr == NULL ) {
3008 LOG("can't find n_opponents\n");
3009 ok = false;
3010 return ok;
3011 }
3012 n_opponents = atoi(ptr);
3013
3014 ptr = strtok(NULL, " ");
3015 if( ptr == NULL ) {
3016 LOG("can't find colour name\n");
3017 ok = false;
3018 return ok;
3019 }
3020 //strcpy(colname, ptr);
3021 string colname = ptr;
3022
3023 MapColour map_colour = MAP_UNDEFINED_COL;
3024 if( strcmp(colname.c_str(), "ORANGE") == 0 ) {
3025 map_colour = MAP_ORANGE;
3026 }
3027 else if( strcmp(colname.c_str(), "GREEN") == 0 ) {
3028 map_colour = MAP_GREEN;
3029 }
3030 else if( strcmp(colname.c_str(), "BROWN") == 0 ) {
3031 map_colour = MAP_BROWN;
3032 }
3033 else if( strcmp(colname.c_str(), "WHITE") == 0 ) {
3034 map_colour = MAP_WHITE;
3035 }
3036 else if( strcmp(colname.c_str(), "DBROWN") == 0 ) {
3037 map_colour = MAP_DBROWN;
3038 }
3039 else if( strcmp(colname.c_str(), "DGREEN") == 0 ) {
3040 map_colour = MAP_DGREEN;
3041 }
3042 else if( strcmp(colname.c_str(), "GREY") == 0 ) {
3043 map_colour = MAP_GREY;
3044 }
3045 else {
3046 LOG("unknown map colour: %s\n", colname.c_str());
3047 ok = false;
3048 return ok;
3049 }
3050
3051 *index = 0;
3052 while( *index < max_islands_per_epoch_c && maps[*epoch][*index] != NULL )
3053 *index = *index + 1;
3054 if( *index == max_islands_per_epoch_c ) {
3055 LOG("too many islands for this epoch\n");
3056 ok = false;
3057 return ok;
3058 }
3059 *l_map = maps[*epoch][*index] = new Map(map_colour, n_opponents, name.c_str());
3060 (*l_map)->setFilename(filename);
3061 }
3062 else {
3063 char *line_ptr = line;
3064 while( *line_ptr == ' ' || *line_ptr == '\t' )
3065 line_ptr++;
3066 char *ptr = strtok(line_ptr, " ");
3067 if( ptr == NULL ) {
3068 LOG("can't find first word\n");
3069 ok = false;
3070 return ok;
3071 }
3072 else if( strcmp(ptr, "SECTOR") == 0 ) {
3073 ptr = strtok(NULL, " ");
3074 if( ptr == NULL ) {
3075 LOG("can't find sec_x\n");
3076 ok = false;
3077 return ok;
3078 }
3079 int sec_x = atoi(ptr);
3080 if( sec_x < 0 || sec_x >= map_width_c ) {
3081 LOG("invalid map x %d\n", sec_x);
3082 ok = false;
3083 return ok;
3084 }
3085
3086 ptr = strtok(NULL, " ");
3087 if( ptr == NULL ) {
3088 LOG("can't find sec_y\n");
3089 ok = false;
3090 return ok;
3091 }
3092 int sec_y = atoi(ptr);
3093 if( sec_y < 0 || sec_y >= map_height_c ) {
3094 LOG("invalid map y %d\n", sec_y);
3095 ok = false;
3096 return ok;
3097 }
3098 (*l_map)->newSquareAt(sec_x, sec_y);
3099 }
3100 else if( strcmp(ptr, "ELEMENT") == 0 ) {
3101 // ignore for now
3102 }
3103 else if( ptr[0] == '#' ) {
3104 // this line is a comment
3105 }
3106 else {
3107 LOG("unknown word: %s\n", ptr);
3108 ok = false;
3109 return ok;
3110 }
3111 }
3112 return ok;
3113 }
3114
readLineFromRWOps(bool & ok,SDL_RWops * file,char * buffer,char * line,int MAX_LINE,int & buffer_offset,int & newline_index,bool & reached_end)3115 bool Game::readLineFromRWOps(bool &ok, SDL_RWops *file, char *buffer, char *line, int MAX_LINE, int &buffer_offset, int &newline_index, bool &reached_end) {
3116 if( newline_index > 1 ) {
3117 // not safe to use strcpy on overlapping strings (undefined behaviour)
3118 int len = strlen(&buffer[newline_index-1]);
3119 memmove(buffer, &buffer[newline_index-1], len);
3120 buffer[len] = '\0';
3121 if( reached_end && buffer[0] == '\0' ) {
3122 return true;
3123 }
3124 buffer_offset = MAX_LINE - (newline_index-1);
3125 }
3126 if( !reached_end ) {
3127 // fill up buffer
3128 for(;;) {
3129 int n_read = file->read(file, &buffer[buffer_offset], 1, MAX_LINE-buffer_offset);
3130 //LOG("buffer offset %d , read %d\n", buffer_offset, n_read);
3131 if( n_read == 0 ) {
3132 // error or eof - don't quit yet, still need to finish reading buffer
3133 //LOG("read all of file\n");
3134 reached_end = true;
3135 break;
3136 }
3137 else {
3138 buffer[buffer_offset+n_read] = '\0';
3139 if( n_read < MAX_LINE-buffer_offset ) {
3140 // we didn't read all of the available buffer, and haven't reached end of file yet
3141 buffer_offset += n_read;
3142 }
3143 else {
3144 break;
3145 }
3146 }
3147 }
3148 }
3149 //LOG("buffer: %s\n", buffer);
3150 newline_index = 0;
3151 while( buffer[newline_index] != '\n' && buffer[newline_index] != '\0' ) {
3152 line[newline_index] = buffer[newline_index];
3153 newline_index++;
3154 }
3155 if( buffer[newline_index] == '\0' ) {
3156 LOG("file has too long line\n");
3157 ok = false;
3158 return true;
3159 }
3160 line[newline_index++] = '\n';
3161 line[newline_index++] = '\0';
3162 return false;
3163 }
3164
readMap(const char * filename)3165 bool Game::readMap(const char *filename) {
3166 //LOG("readMap: %s\n", filename); // disabled logging to improve performance on startup
3167 bool ok = true;
3168 const int MAX_LINE = 4096;
3169 //const int MAX_LINE = 64;
3170 char line[MAX_LINE+1] = "";
3171 Map *l_map = NULL;
3172 int epoch = -1;
3173 int index = -1;
3174
3175 char fullname[4096] = "";
3176 sprintf(fullname, "%s/%s", maps_dirname, filename);
3177 // open in binary mode, so that we parse files in an OS-independent manner
3178 // (otherwise, Windows will parse "\r\n" as being "\n", but Linux will still read it as "\n")
3179 //FILE *file = fopen(fullname, "rb");
3180 SDL_RWops *file = SDL_RWFromFile(fullname, "rb");
3181 #if !defined(__ANDROID__) && defined(__DragonFly__)
3182 if( file == NULL ) {
3183 LOG("searching in /usr/local/share/gigalomania/ for islands folder\n");
3184 sprintf(fullname, "%s/%s", alt_maps_dirname, filename);
3185 file = SDL_RWFromFile(fullname, "rb");
3186 }
3187 #endif
3188 if( file == NULL ) {
3189 LOG("failed to open file: %s\n", fullname);
3190 return false;
3191 }
3192 char buffer[MAX_LINE+1] = "";
3193 int buffer_offset = 0;
3194 bool reached_end = false;
3195 int newline_index = 0;
3196 while( ok ) {
3197 bool done = readLineFromRWOps(ok, file, buffer, line, MAX_LINE, buffer_offset, newline_index, reached_end);
3198 if( !ok ) {
3199 LOG("failed to read line for: %s\n", filename);
3200 }
3201 else if( done ) {
3202 break;
3203 }
3204 else {
3205 ok = readMapProcessLine(&epoch, &index, &l_map, line, MAX_LINE, filename);
3206 }
3207 }
3208 file->close(file);
3209
3210 if( !ok && l_map != NULL ) {
3211 LOG("delete map that was created\n");
3212 delete l_map;
3213 ASSERT(epoch != -1); // should have been set, if l_map!=NULL
3214 ASSERT(index != -1); // should have been set, if l_map!=NULL
3215 l_map = maps[epoch][index] = NULL;
3216 }
3217 return ok;
3218 }
3219
sortMapsFunc(const void * a,const void * b)3220 int sortMapsFunc(const void *a, const void *b) {
3221 Map *map_a = *(Map **)a;
3222 Map *map_b = *(Map **)b;
3223 // handling for 'Ohm'...
3224 if( map_a->getName()[0] == '0' )
3225 return 1;
3226 else if( map_b->getName()[0] == '0' )
3227 return -1;
3228 return strcmp(map_a->getName(), map_b->getName());
3229 }
3230
createMaps()3231 bool Game::createMaps() {
3232 LOG("createMaps()...\n");
3233 #if defined(_WIN32)
3234 WIN32_FIND_DATAA findFileData;
3235 char maps_dirname_w[256];
3236 sprintf(maps_dirname_w, "%s\\*", maps_dirname);
3237 HANDLE handle = FindFirstFileA(maps_dirname_w, &findFileData);
3238 if( handle == INVALID_HANDLE_VALUE ) {
3239 LOG("Invalid File Handle. GetLastError reports %d\n", GetLastError());
3240 return false;
3241 }
3242 for(;;) {
3243 if( !readMap(findFileData.cFileName) ) {
3244 LOG("failed reading map: %s\n", findFileData.cFileName);
3245 // don't fail altogether, just ignore
3246 }
3247 if( FindNextFileA(handle, &findFileData) == 0 ) {
3248 FindClose(handle);
3249 DWORD error = GetLastError();
3250 if( error != ERROR_NO_MORE_FILES ) {
3251 LOG("error reading directory: %d\n", error);
3252 return false;
3253 }
3254 break;
3255 }
3256 }
3257 #elif defined(__ANDROID__)
3258 // unclear how to read contents of a folder in assets, so we just hardcode the islands (not like the user can easily drop new files there)
3259 readMap("0mega.map");
3260 readMap("alpha.map");
3261 readMap("binary.map");
3262 readMap("castle.map");
3263 readMap("devil.map");
3264 readMap("eep.map");
3265 readMap("final.map");
3266 readMap("font.map");
3267 readMap("globe.map");
3268 readMap("home.map");
3269 readMap("infinity.map");
3270 readMap("just.map");
3271 readMap("koala.map");
3272 readMap("loop.map");
3273 readMap("moon.map");
3274 readMap("ninth.map");
3275 readMap("oxygen.map");
3276 readMap("polar.map");
3277 readMap("quart.map");
3278 readMap("rare.map");
3279 readMap("semi.map");
3280 readMap("toxic.map");
3281 readMap("universal.map");
3282 readMap("vine.map");
3283 readMap("wreath.map");
3284 readMap("x.map");
3285 readMap("yen.map");
3286 readMap("zinc.map");
3287 #else
3288
3289 DIR *dir = opendir(maps_dirname);
3290
3291 #if !defined(__ANDROID__) && defined(__DragonFly__)
3292 if( dir == NULL ) {
3293 LOG("searching in /usr/local/share/gigalomania/ for islands folder\n");
3294 dir = opendir(alt_maps_dirname);
3295 }
3296 #endif
3297 if( dir == NULL ) {
3298 LOG("failed to open directory: %s\n", maps_dirname);
3299 LOG("error: %d\n", errno);
3300 return false;
3301 }
3302 for(;;) {
3303 errno = 0;
3304 dirent *ent = readdir(dir);
3305 if( ent == NULL ) {
3306 closedir(dir);
3307 if( errno ) {
3308 LOG("error reading directory: %d\n", errno);
3309 return false;
3310 }
3311 break;
3312 }
3313 if( !readMap(ent->d_name) ) {
3314 LOG("failed reading map: %s\n", ent->d_name);
3315 // don't fail altogether, just ignore
3316 }
3317 }
3318 #endif
3319 LOG("done reading directory\n");
3320
3321 for(int i=0;i<n_epochs_c;i++) {
3322 int n_islands = 0;
3323 while(n_islands < max_islands_per_epoch_c && maps[i][n_islands] != NULL)
3324 n_islands++;
3325 if( n_islands == 0 ) {
3326 LOG("can't find any islands for epoch %d\n", i);
3327 return false;
3328 }
3329 qsort((void *)&maps[i], n_islands, sizeof(maps[i][0]), sortMapsFunc);
3330 }
3331 return true;
3332 }
3333
disposeGameState()3334 void Game::disposeGameState() {
3335 ASSERT( dispose_gamestate == NULL );
3336 LOG("disposeGameState: %d\n", gamestate);
3337 dispose_gamestate = gamestate;
3338 gamestate = NULL;
3339 }
3340
setGameStateID(GameStateID state,GameState * new_gamestate)3341 void Game::setGameStateID(GameStateID state, GameState *new_gamestate) {
3342 LOG("setGameStateID(%d, %d)\n", state, new_gamestate);
3343 LOG("old gameStateID was %d\n", gameStateID);
3344 gameStateID = state;
3345 playMusic();
3346
3347 GameState *old_gamestate = gamestate;
3348 if( gamestate != NULL ) {
3349 disposeGameState();
3350 }
3351
3352 if( new_gamestate != NULL ) {
3353 gamestate = new_gamestate;
3354 }
3355 else if( gameStateID == GAMESTATEID_CHOOSEGAMETYPE )
3356 gamestate = new ChooseGameTypeGameState(human_player);
3357 else if( gameStateID == GAMESTATEID_CHOOSEDIFFICULTY )
3358 gamestate = new ChooseDifficultyGameState(human_player);
3359 else if( gameStateID == GAMESTATEID_CHOOSEPLAYER )
3360 gamestate = new ChoosePlayerGameState(human_player);
3361 else if( gameStateID == GAMESTATEID_CHOOSETUTORIAL )
3362 gamestate = new ChooseTutorialGameState(human_player);
3363 else if( gameStateID == GAMESTATEID_PLACEMEN )
3364 gamestate = new PlaceMenGameState(human_player);
3365 else if( gameStateID == GAMESTATEID_PLAYING ) {
3366 gamestate = new PlayingGameState(human_player);
3367 int map_x = 0, map_y = 0, n_men = 0;
3368 if( gameType == GAMETYPE_TUTORIAL ) {
3369 map_x = tutorial->getStartMapX();
3370 map_y = tutorial->getStartMapY();
3371 n_men = tutorial->getNMen();
3372 game_g->players[human_player]->setNMenForThisIsland( n_men ); // need to set this here - for non-tutorial, this is done in PlaceMenGameState::setStartMapPos()
3373 }
3374 else {
3375 map_x = static_cast<PlaceMenGameState *>(old_gamestate)->getStartMapX();
3376 map_y = static_cast<PlaceMenGameState *>(old_gamestate)->getStartMapY();
3377 n_men = human_player == PLAYER_DEMO ? 0 : players[human_player]->getNMenForThisIsland();
3378 }
3379 static_cast<PlayingGameState *>(gamestate)->createSectors(map_x, map_y, n_men);
3380 if( gameType == GAMETYPE_TUTORIAL ) {
3381 tutorial->initCards();
3382 }
3383 }
3384 else if( gameStateID == GAMESTATEID_ENDISLAND )
3385 gamestate = new EndIslandGameState(human_player);
3386 else if( gameStateID == GAMESTATEID_GAMECOMPLETE )
3387 gamestate = new GameCompleteGameState(human_player);
3388 else {
3389 ASSERT(false);
3390 }
3391
3392 gamestate->reset();
3393 state_changed = false;
3394 //gameWon = GAME_PLAYING;
3395 /*if( state == GAMESTATE_PLACEMEN ) {
3396 placeMenInfo.init();
3397 }*/
3398 // cheat/test:
3399 /*if( gameStateID == GAMESTATEID_PLAYING && new_gamestate == NULL && gameType != GAMETYPE_TUTORIAL ) {
3400 // n.b., if we ever want this to run in tutorial mode, the old_gamestate won't be of type PlaceMenGameState
3401 int map_x = static_cast<PlaceMenGameState *>(old_gamestate)->getStartMapX();
3402 int map_y = static_cast<PlaceMenGameState *>(old_gamestate)->getStartMapY();
3403 Sector *start_sector = map->getSector(map_x, map_y);
3404 start_sector->cheat(human_player);
3405 }*/
3406 }
3407
setupTutorial(const string & id)3408 void Game::setupTutorial(const string &id) {
3409 T_ASSERT(tutorial == NULL);
3410 tutorial = TutorialManager::setupTutorial(id);
3411 }
3412
startIsland()3413 void Game::startIsland() {
3414 ASSERT(gameStateID == GAMESTATEID_PLACEMEN);
3415 /*int map_x = static_cast<PlaceMenGameState *>(gamestate)->getStartMapX();
3416 int map_y = static_cast<PlaceMenGameState *>(gamestate)->getStartMapY();*/
3417
3418 //setupPlayers();
3419 setGameStateID(GAMESTATEID_PLAYING);
3420 gamestate->fadeScreen(false, 0, NULL);
3421 gameResult = GAMERESULT_UNDEFINED;
3422 }
3423
startIsland_g()3424 void startIsland_g() {
3425 // wrapper so we can pass as a function pointer
3426 game_g->startIsland();
3427 }
3428
endIsland()3429 void Game::endIsland() {
3430 ASSERT(gameStateID == GAMESTATEID_PLAYING);
3431 map->calculateStats();
3432 map->freeSectors(); // must do this here rather than waiting until deleting PlayingGameState, as by that time the "map" variable may have switched to a different island
3433 n_player_suspended += players[human_player]->getNSuspended();
3434 setGameStateID(GAMESTATEID_ENDISLAND);
3435 gamestate->fadeScreen(false, 0, NULL);
3436
3437 if( gameResult == GAMERESULT_QUIT ) {
3438 // pick a random non-human player
3439 int n_cpu = 0;
3440 for(int i=0;i<n_players_c;i++) {
3441 if( players[i] != NULL && !players[i]->isDead() && i != human_player ) {
3442 n_cpu++;
3443 }
3444 }
3445 if( n_cpu > 0 ) {
3446 int index = rand() % n_cpu;
3447 n_cpu = 0;
3448 for(int i=0;i<n_players_c;i++) {
3449 if( players[i] != NULL && !players[i]->isDead() && i != human_player ) {
3450 if( n_cpu == index ) {
3451 playSample( s_quit[i] );
3452 break;
3453 }
3454 n_cpu++;
3455 }
3456 }
3457 }
3458 }
3459
3460 if( gameResult == GAMERESULT_WON && gameType == GAMETYPE_ALLISLANDS ) {
3461 //n_men_store -= n_men_for_this_island;
3462 n_men_store -= players[human_player]->getNMenForThisIsland();
3463 if( start_epoch == n_epochs_c-1 ) {
3464 }
3465 else {
3466 ASSERT( !completed_island[selected_island] );
3467 completed_island[selected_island] = true;
3468 // check advance to next epoch
3469 bool completed_epoch = true;
3470 for(int i=0;i<max_islands_per_epoch_c && maps[start_epoch][i] != NULL && completed_epoch;i++) {
3471 if( !completed_island[i] )
3472 completed_epoch = false;
3473 }
3474 LOG("completed epoch? %d\n", completed_epoch);
3475 if( completed_epoch ) {
3476 n_men_store += getMenPerEpoch();
3477 nextEpoch();
3478 }
3479 else {
3480 nextIsland();
3481 }
3482 }
3483 }
3484 }
3485
endIsland_g()3486 void endIsland_g() {
3487 // wrapper so we can pass as a function pointer
3488 game_g->endIsland();
3489 }
3490
returnToChooseIsland()3491 void Game::returnToChooseIsland() {
3492 ASSERT(gameStateID == GAMESTATEID_ENDISLAND);
3493 if( gameType == GAMETYPE_TUTORIAL ) {
3494 LOG("delete tutorial %d\n", tutorial);
3495 delete tutorial;
3496 tutorial = NULL;
3497 setGameStateID(GAMESTATEID_CHOOSEGAMETYPE);
3498 }
3499 else if( gameResult == GAMERESULT_WON && gameType == GAMETYPE_ALLISLANDS ) {
3500 if( start_epoch == n_epochs_c-1 ) {
3501 setGameStateID(GAMESTATEID_GAMECOMPLETE);
3502 }
3503 else {
3504 setGameStateID(GAMESTATEID_PLACEMEN);
3505 }
3506 }
3507 else {
3508 setGameStateID(GAMESTATEID_PLACEMEN);
3509 // test
3510 //setGameStateID(GAMESTATEID_GAMECOMPLETE);
3511 }
3512
3513 gamestate->fadeScreen(false, 0, NULL);
3514
3515 if( human_player != PLAYER_DEMO )
3516 players[human_player]->setNMenForThisIsland(0);
3517 }
3518
returnToChooseIsland_g()3519 void returnToChooseIsland_g() {
3520 // wrapper so we can pass as a function pointer
3521 game_g->returnToChooseIsland();
3522 }
3523
startNewGame()3524 void Game::startNewGame() {
3525 ASSERT(gameStateID == GAMESTATEID_GAMECOMPLETE);
3526 setGameStateID(GAMESTATEID_PLACEMEN);
3527 //gamestate->fadeScreen(false, 0, NULL);
3528 newGame();
3529 }
3530
startNewGame_g()3531 void startNewGame_g() {
3532 // wrapper so we can pass as a function pointer
3533 game_g->startNewGame();
3534 }
3535
placeTower()3536 void Game::placeTower() {
3537 ASSERT( gameStateID == GAMESTATEID_PLACEMEN );
3538 if( !state_changed ) {
3539 state_changed = true;
3540 gamestate->fadeScreen(true, 0, startIsland_g);
3541 }
3542 }
3543
3544 //bool quit = false;
3545 bool debugwindow = false;
3546
requestQuit(bool force_quit)3547 void Game::requestQuit(bool force_quit) {
3548 if( !state_changed ) {
3549 gamestate->requestQuit(force_quit);
3550 }
3551 }
3552
keypressReturn()3553 void Game::keypressReturn() {
3554 if( !state_changed ) {
3555 gamestate->requestConfirm();
3556 }
3557 }
3558
togglePause()3559 void Game::togglePause() {
3560 if( gameStateID == GAMESTATEID_PLAYING ) {
3561 paused = !paused;
3562 if( paused ) {
3563 playSample(s_on_hold);
3564
3565 // n.b., pausing music/looped-sounds absolutely important on Android, so music stops when game goes into background; but useful for other platforms too
3566 // note also that we don't pause all sounds, as it would immediately pause the "putting you on hold" sample!
3567 Sample::pauseMusic();
3568 Sample::pauseChannel(SOUND_CHANNEL_BIPLANE);
3569 Sample::pauseChannel(SOUND_CHANNEL_BOMBER);
3570 Sample::pauseChannel(SOUND_CHANNEL_SPACESHIP);
3571 }
3572 else {
3573 Sample::unpauseMusic();
3574 Sample::unpauseChannel(SOUND_CHANNEL_BIPLANE);
3575 Sample::unpauseChannel(SOUND_CHANNEL_BOMBER);
3576 Sample::unpauseChannel(SOUND_CHANNEL_SPACESHIP);
3577 }
3578 }
3579 }
3580
activate()3581 void Game::activate() {
3582 this->deleteState();
3583 if( gameStateID != GAMESTATEID_PLAYING ) {
3584 // when state is GAMESTATEID_PLAYING, we don't restart the music until the game is unpaused
3585 Sample::unpauseMusic();
3586 }
3587 }
3588
deactivate()3589 void Game::deactivate() {
3590 if( gameStateID != GAMESTATEID_PLAYING ) {
3591 Sample::pauseMusic();
3592 }
3593 else {
3594 if( !this->isPaused() ) {
3595 this->togglePause(); // automatically pause when application goes inactive
3596 }
3597 }
3598 this->saveState();
3599 }
3600
isPaused() const3601 bool Game::isPaused() const {
3602 return paused;
3603 }
3604
setTimeRate(int time_rate)3605 void Game::setTimeRate(int time_rate) {
3606 this->time_rate = time_rate;
3607 LOG("time_rate = %d\n", time_rate);
3608 }
3609
setRealTime(int real_time)3610 void Game::setRealTime(int real_time) {
3611 this->real_time = real_time;
3612 }
3613
getRealTime() const3614 int Game::getRealTime() const {
3615 return real_time;
3616 }
3617
getRealLoopTime() const3618 int Game::getRealLoopTime() const {
3619 return real_loop_time;
3620 }
3621
setGameTime(int game_time)3622 void Game::setGameTime(int game_time) {
3623 this->game_time = game_time;
3624 }
3625
getGameTime() const3626 int Game::getGameTime() const {
3627 return game_time;
3628 }
3629
getLoopTime() const3630 int Game::getLoopTime() const {
3631 return loop_time;
3632 }
3633
updateTime(int time)3634 void Game::updateTime(int time) {
3635 // prevent instability on slow machines
3636 const int max_interval_c = 200;
3637 if( time > max_interval_c )
3638 time = max_interval_c;
3639
3640 real_loop_time = time;
3641 real_time += time;
3642
3643 // Ideally we'd have always had time_rate being an integer, and have all usages of loop_time cope with that, but this would now be a significant change.
3644 // So we add this fix so that we don't have inaccuracy due to rounding. To test this code, disable wait() in Application::runMainLoop(), which means
3645 // we'll test this function with very small values of time.
3646 loop_time = (int)(time * time_ratio_c * time_rate + accumulated_time);
3647 accumulated_time = (time * time_ratio_c * time_rate + accumulated_time) - loop_time;
3648 //LOG("time %d loop time %d accumulated %f\n", time, loop_time, accumulated_time);
3649
3650 game_time += loop_time;
3651 frame_counter = (getRealTime() * time_rate) / ticks_per_frame_c;
3652 }
3653
resetMouseClick()3654 void Game::resetMouseClick() {
3655 mouseTime = -1;
3656 }
3657
getNClicks()3658 int Game::getNClicks() {
3659 if( mouseTime == -1 )
3660 mouseTime = getRealTime();
3661 int time = getRealTime() - mouseTime;
3662 if( time < 2000 )
3663 return 1;
3664 else if( time < 5000 )
3665 return 2;
3666 return 3;
3667 }
3668
deleteState() const3669 void Game::deleteState() const {
3670 const char *save_fullfilename = getApplicationFilename(autosave_filename, autosave_survive_uninstall);
3671 remove(save_fullfilename);
3672 delete [] save_fullfilename;
3673 }
3674
saveState() const3675 void Game::saveState() const {
3676 if( gameStateID == GAMESTATEID_UNDEFINED || gameStateID == GAMESTATEID_CHOOSEGAMETYPE || gameStateID == GAMESTATEID_CHOOSEDIFFICULTY || gameStateID == GAMESTATEID_CHOOSEPLAYER || gameStateID == GAMESTATEID_CHOOSETUTORIAL || gameStateID == GAMESTATEID_GAMECOMPLETE ) {
3677 // no need to save state
3678 }
3679 else if( gameType == GAMETYPE_TUTORIAL && gameStateID == GAMESTATEID_ENDISLAND ) {
3680 // no need to save state (and don't want to, otherwise this will resume to the islands screen instead the main menu)
3681 }
3682 else {
3683 stringstream stream;
3684 const int savegame_version_c = 1;
3685 stream << "<?xml version=\"1.0\" ?>\n";
3686 stream << "<savegame major=\"" << majorVersion << "\" minor=\"" << minorVersion << "\" savegame_version=\"" << savegame_version_c << "\">\n";
3687 stream << "<global ";
3688 stream << "game_type=\"" << gameType << "\" ";
3689 stream << "difficulty_level=\"" << difficulty_level << "\" ";
3690 stream << "human_player=\"" << human_player << "\" ";
3691 stream << "n_men_store=\"" << n_men_store << "\" ";
3692 stream << "n_player_suspended=\"" << n_player_suspended << "\" ";
3693 stream << "start_epoch=\"" << start_epoch << "\" ";
3694 stream << "selected_island=\"" << selected_island << "\" ";
3695 stream << "/>\n";
3696
3697 stream << "<time real_time=\"" << getRealTime() << "\" game_time=\"" << getGameTime() << "\" />\n";
3698
3699 for(int i=0;i<max_islands_per_epoch_c;i++) {
3700 stream << "<completed_island island_id=\"" << i << "\" complete=\"" << (completed_island[i] ? 1 : 0) << "\"/>\n";
3701 }
3702
3703 gamestate->saveState(stream);
3704
3705 stream << "</savegame>\n";
3706
3707 const char *save_fullfilename = getApplicationFilename(autosave_filename, autosave_survive_uninstall);
3708 SDL_RWops *file = SDL_RWFromFile(save_fullfilename, "w+");
3709 if( file == NULL ) {
3710 LOG("failed to open: %s\n", save_fullfilename);
3711 LOG("error: %s\n", SDL_GetError());
3712 }
3713 else {
3714 size_t length = stream.str().length();
3715 char *ptr = new char[length];
3716 stream.read(ptr, length);
3717 file->write(file, ptr, length, 1);
3718 file->close(file);
3719 }
3720 delete [] save_fullfilename;
3721 }
3722 }
3723
loadStateParseXMLNode(const TiXmlNode * parent)3724 GameState *Game::loadStateParseXMLNode(const TiXmlNode *parent) {
3725 if( parent == NULL ) {
3726 return NULL;
3727 }
3728 bool read_children = true;
3729 GameState *new_gamestate = NULL;
3730
3731 switch( parent->Type() ) {
3732 case TiXmlNode::TINYXML_DOCUMENT:
3733 break;
3734 case TiXmlNode::TINYXML_ELEMENT:
3735 {
3736 const char *element_name = parent->Value();
3737 const TiXmlElement *element = parent->ToElement();
3738 const TiXmlAttribute *attribute = element->FirstAttribute();
3739 if( strcmp(element_name, "savegame") == 0 ) {
3740 int save_major = -1, save_minor = -1;
3741 while( attribute != NULL ) {
3742 const char *attribute_name = attribute->Name();
3743 if( strcmp(attribute_name, "major") == 0 ) {
3744 save_major = static_cast<DifficultyLevel>(atoi(attribute->Value()));
3745 }
3746 else if( strcmp(attribute_name, "minor") == 0 ) {
3747 save_minor = static_cast<DifficultyLevel>(atoi(attribute->Value()));
3748 }
3749 else if( strcmp(attribute_name, "savegame_version") == 0 ) {
3750 int savegame_version = static_cast<DifficultyLevel>(atoi(attribute->Value()));
3751 LOG("save game version %d\n", savegame_version);
3752 }
3753 else {
3754 // 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
3755 LOG("unknown game/savegame attribute: %s\n", attribute_name);
3756 ASSERT(false);
3757 }
3758 attribute = attribute->Next();
3759 }
3760 LOG("saved game version %d.%d\n", save_major, save_minor);
3761 LOG("current game version %d.%d\n", majorVersion, minorVersion);
3762 }
3763 else if( strcmp(element_name, "global") == 0 ) {
3764 bool set_start_epoch = false;
3765 bool set_start_island = false;
3766 while( attribute != NULL ) {
3767 const char *attribute_name = attribute->Name();
3768 if( strcmp(attribute_name, "game_type") == 0 ) {
3769 gameType = static_cast<GameType>(atoi(attribute->Value()));
3770 if( gameType != GAMETYPE_SINGLEISLAND && gameType != GAMETYPE_ALLISLANDS && gameType != GAMETYPE_TUTORIAL ) {
3771 throw std::runtime_error("unknown game_type");
3772 }
3773 }
3774 else if( strcmp(attribute_name, "difficulty_level") == 0 ) {
3775 difficulty_level = static_cast<DifficultyLevel>(atoi(attribute->Value()));
3776 if( difficulty_level < 0 || difficulty_level >= DIFFICULTY_N_LEVELS ) {
3777 throw std::runtime_error("invalid difficulty_level");
3778 }
3779 }
3780 else if( strcmp(attribute_name, "human_player") == 0 ) {
3781 human_player = atoi(attribute->Value());
3782 if( human_player < 0 || selected_island >= n_players_c ) {
3783 throw std::runtime_error("invalid human_player");
3784 }
3785 }
3786 else if( strcmp(attribute_name, "n_men_store") == 0 ) {
3787 n_men_store = atoi(attribute->Value());
3788 if( n_men_store < 0 ) {
3789 throw std::runtime_error("invalid n_men_store");
3790 }
3791 }
3792 else if( strcmp(attribute_name, "n_player_suspended") == 0 ) {
3793 n_player_suspended = atoi(attribute->Value());
3794 if( n_player_suspended < 0 ) {
3795 throw std::runtime_error("invalid n_player_suspended");
3796 }
3797 }
3798 else if( strcmp(attribute_name, "start_epoch") == 0 ) {
3799 start_epoch = atoi(attribute->Value());
3800 if( start_epoch < 0 || start_epoch >= n_epochs_c ) {
3801 throw std::runtime_error("invalid start_epoch");
3802 }
3803 updatedEpoch();
3804 set_start_epoch = true;
3805 }
3806 else if( strcmp(attribute_name, "selected_island") == 0 ) {
3807 selected_island = atoi(attribute->Value());
3808 if( selected_island < 0 || selected_island >= max_islands_per_epoch_c ) {
3809 throw std::runtime_error("invalid selected_island");
3810 }
3811 set_start_island = true;
3812 }
3813 else {
3814 // 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
3815 LOG("unknown game/global attribute: %s\n", attribute_name);
3816 ASSERT(false);
3817 }
3818 attribute = attribute->Next();
3819 }
3820 if( set_start_epoch && set_start_island ) {
3821 map = maps[start_epoch][selected_island];
3822 }
3823 else {
3824 throw std::runtime_error("map not set");
3825 }
3826 }
3827 else if( strcmp(element_name, "completed_island") == 0 ) {
3828 int island_id = -1;
3829 bool complete = false;
3830 while( attribute != NULL ) {
3831 const char *attribute_name = attribute->Name();
3832 if( strcmp(attribute_name, "island_id") == 0 ) {
3833 island_id = atoi(attribute->Value());
3834 if( island_id < 0 || island_id >= max_islands_per_epoch_c ) {
3835 throw std::runtime_error("completed_island invalid island_id");
3836 }
3837 }
3838 else if( strcmp(attribute_name, "complete") == 0 ) {
3839 complete = atoi(attribute->Value())==1;
3840 }
3841 else {
3842 // 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
3843 LOG("unknown game/completed_island attribute: %s\n", attribute_name);
3844 ASSERT(false);
3845 }
3846 attribute = attribute->Next();
3847 }
3848 if( island_id == -1 ) {
3849 throw std::runtime_error("completed_island missing island_id");
3850 }
3851 completed_island[island_id] = complete;
3852 }
3853 else if( strcmp(element_name, "time") == 0 ) {
3854 // we need to set this at the Game level rather than the PlayingGamestate, so that the times are set before creating the map (otherwise messes up the saved times for the particle systems)
3855 while( attribute != NULL ) {
3856 const char *attribute_name = attribute->Name();
3857 if( strcmp(attribute_name, "real_time") == 0 ) {
3858 int real_time = atoi(attribute->Value());
3859 setRealTime(real_time);
3860 }
3861 else if( strcmp(attribute_name, "game_time") == 0 ) {
3862 int game_time = atoi(attribute->Value());
3863 setGameTime(game_time);
3864 }
3865 else {
3866 // 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
3867 LOG("unknown game/time attribute: %s\n", attribute_name);
3868 ASSERT(false);
3869 }
3870 attribute = attribute->Next();
3871 }
3872 }
3873 else if( strcmp(element_name, "playing_gamestate") == 0 ) {
3874 PlayingGameState *playing_gamestate = new PlayingGameState(human_player);
3875 try {
3876 if( map == NULL ) {
3877 throw std::runtime_error("playing_gamestate map not yet set");
3878 }
3879 map->createSectors(playing_gamestate, start_epoch);
3880 playing_gamestate->loadStateParseXMLNode(parent);
3881 if( gameType == GAMETYPE_TUTORIAL ) {
3882 if( tutorial == NULL ) {
3883 throw std::runtime_error("didn't set tutorial");
3884 }
3885 }
3886 //throw std::runtime_error("blah"); // test failing to load state
3887 new_gamestate = playing_gamestate;
3888 read_children = false;
3889 }
3890 catch(const std::runtime_error &error) {
3891 LOG("cleanup due to error loading state: %s\n", error.what());
3892 delete playing_gamestate;
3893 throw error;
3894 }
3895 }
3896 else {
3897 // 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
3898 LOG("unknown game tag: %s\n", element_name);
3899 ASSERT(false);
3900 }
3901 }
3902 break;
3903 case TiXmlNode::TINYXML_COMMENT:
3904 break;
3905 case TiXmlNode::TINYXML_UNKNOWN:
3906 break;
3907 case TiXmlNode::TINYXML_TEXT:
3908 break;
3909 case TiXmlNode::TINYXML_DECLARATION:
3910 break;
3911 }
3912
3913 for(const TiXmlNode *child=parent->FirstChild();child!=NULL && read_children;child=child->NextSibling()) {
3914 GameState *sub_gamestate = loadStateParseXMLNode(child);
3915 if( sub_gamestate != NULL ) {
3916 if( new_gamestate != NULL ) {
3917 throw std::runtime_error("more than one gamestate defined");
3918 }
3919 new_gamestate = sub_gamestate;
3920 }
3921 }
3922 return new_gamestate;
3923 }
3924
loadState()3925 bool Game::loadState() {
3926 bool ok = false;
3927 stringstream stream;
3928 const char *save_fullfilename = getApplicationFilename(autosave_filename, autosave_survive_uninstall);
3929 SDL_RWops *file = SDL_RWFromFile(save_fullfilename, "r");
3930 if( file == NULL ) {
3931 LOG("couldn't find or open saved state file: %s\n", save_fullfilename);
3932 }
3933 else {
3934 LOG("found a saved state file: %s\n", save_fullfilename);
3935 #if SDL_MAJOR_VERSION == 1
3936 // SDL 1 doesn't have a size parameter
3937 SDL_RWseek(file, 0, RW_SEEK_END);
3938 size_t size = (size_t)SDL_RWtell(file);
3939 SDL_RWseek(file, 0, RW_SEEK_SET);
3940 #else
3941 size_t size = (size_t)file->size(file);
3942 #endif
3943 char *buffer = new char[size+1];
3944 if( file->read(file, buffer, 1, size) > 0 ) {
3945 file->close(file);
3946 // rename immediately so that if there's a crash while loading the saved state, the game doesn't repeatedly crash
3947 const char *save_old_fullfilename = getApplicationFilename(autosave_old_filename, autosave_survive_uninstall);
3948 remove(save_old_fullfilename);
3949 rename(save_fullfilename, save_old_fullfilename);
3950
3951 buffer[size] = '\0';
3952
3953 TiXmlDocument doc;
3954 if( doc.Parse(buffer) == NULL ) {
3955 LOG("failed to parse XML file, error row %d col %d\n", doc.ErrorRow(), doc.ErrorCol());
3956 LOG("error: %s\n", doc.ErrorDesc());
3957 }
3958 else {
3959 try {
3960 GameState *new_gamestate = loadStateParseXMLNode(&doc);
3961 // we create a new gamestate if playing a game
3962 if( new_gamestate != NULL ) {
3963 LOG("loaded PlayingGameState\n");
3964 int c_page = static_cast<PlayingGameState *>(new_gamestate)->getGamePanel()->getPage();
3965 setGameStateID(GAMESTATEID_PLAYING, new_gamestate);
3966 static_cast<PlayingGameState *>(new_gamestate)->getGamePanel()->setPage(c_page);
3967 }
3968 else {
3969 LOG("loaded PlaceMenGameState\n");
3970 setGameStateID(GAMESTATEID_PLACEMEN);
3971 }
3972 ok = true;
3973 }
3974 catch(const std::runtime_error &error) {
3975 LOG("caught error loading state: %s\n", error.what());
3976 }
3977 }
3978 if( !ok ) {
3979 LOG("rename bad save file\n");
3980 const char *save_bad_fullfilename = getApplicationFilename(autosave_bad_filename, autosave_survive_uninstall);
3981 remove(save_bad_fullfilename);
3982 rename(save_old_fullfilename, save_bad_fullfilename);
3983
3984 if( gamestate != NULL ) {
3985 LOG("delete gamestate\n");
3986 delete gamestate;
3987 gamestate = NULL;
3988 }
3989 if( tutorial != NULL ) {
3990 LOG("delete tutorial\n");
3991 delete tutorial;
3992 tutorial = NULL;
3993 }
3994 }
3995 }
3996 else {
3997 file->close(file);
3998 const char *save_bad_fullfilename = getApplicationFilename(autosave_bad_filename, autosave_survive_uninstall);
3999 remove(save_bad_fullfilename);
4000 rename(save_fullfilename, save_bad_fullfilename);
4001 }
4002 delete [] buffer;
4003 }
4004 delete [] save_fullfilename;
4005 return ok;
4006 }
4007
mouseClick(int m_x,int m_y,bool m_left,bool m_middle,bool m_right,bool click)4008 void Game::mouseClick(int m_x, int m_y, bool m_left, bool m_middle, bool m_right, bool click) {
4009 const int mousepress_delay = 100;
4010 T_ASSERT( m_left || m_middle || m_right );
4011 if( click ) {
4012 //LOG("mouse click\n");
4013 gamestate->mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
4014 }
4015 else {
4016 // use getTicks() not getRealTime(), as this function may be called at any occasion (due to mouse click events), so getRealTime() might not yet be updated!
4017 unsigned int ticks = game_g->getApplication()->getTicks();
4018 if( ticks - lastmousepress_time >= mousepress_delay ) {
4019 //LOG("mouse press: %d, %d\n", lastmousepress_time, ticks);
4020 lastmousepress_time = ticks;
4021 gamestate->mouseClick(m_x, m_y, m_left, m_middle, m_right, click);
4022 }
4023 else {
4024 //LOG("ignore mouse press\n");
4025 }
4026 }
4027 }
4028
updateGame()4029 void Game::updateGame() {
4030 if( !paused ) {
4031 int m_x = 0, m_y = 0;
4032 bool m_left = false, m_middle = false, m_right = false;
4033 bool m_res = screen->getMouseState(&m_x, &m_y, &m_left, &m_middle, &m_right);
4034 /*screen->getMouseCoords(&m_x, &m_y);
4035 game_g->getApplication()->getMousePressed(&m_left, &m_middle, &m_right);*/
4036 if( m_res ) {
4037 mouseClick(m_x, m_y, m_left, m_middle, m_right, false);
4038 }
4039 else {
4040 resetMouseClick();
4041 }
4042 }
4043
4044 // update
4045 if( !paused ) {
4046 if( gameStateID == GAMESTATEID_PLAYING ) {
4047 for(int i=0;i<n_players_c;i++) {
4048 if( i != human_player && players[i] != NULL )
4049 players[i]->doAIUpdate(human_player, static_cast<PlayingGameState *>(gamestate));
4050 }
4051 //players[ enemy_player ]->doAIUpdate();
4052 gamestate->update();
4053 for(int y=0;y<map_height_c;y++) {
4054 for(int x=0;x<map_width_c;x++) {
4055 /*if( map->sectors[x][y] != NULL )
4056 map->sectors[x][y]->update();*/
4057 Sector *sector = map->getSector(x, y);
4058 if( sector != NULL ) {
4059 sector->update(human_player);
4060 }
4061 }
4062 }
4063 }
4064 }
4065
4066 if( gameStateID == GAMESTATEID_PLAYING && !state_changed && gameMode != GAMEMODE_MULTIPLAYER_CLIENT ) {
4067 if( human_player != PLAYER_DEMO && !playerAlive(human_player) ) {
4068 playSample(s_itis_all_over);
4069 state_changed = true;
4070 gameResult = GAMERESULT_LOST;
4071 fadeMusic(SHORT_DELAY + 1000);
4072 gamestate->fadeScreen(true, SHORT_DELAY, endIsland_g);
4073 }
4074 else {
4075 bool all_dead = true;
4076 /*for(int i=0;i<n_players_c && all_dead;i++) {
4077 if( i != human_player && players[i] != NULL && playerAlive(i) )
4078 all_dead = false;
4079 }*/
4080 for(int i=0;i<n_players_c;i++) {
4081 if( i != human_player && players[i] != NULL && !players[i]->isDead() ) {
4082 if( playerAlive(i) )
4083 all_dead = false;
4084 else {
4085 players[i]->kill(static_cast<PlayingGameState *>(gamestate));
4086 }
4087 }
4088 }
4089 //if( !playerAlive(enemy_player) ) {
4090 if( all_dead || (tutorial != NULL && tutorial->getCard() == NULL && tutorial->autoEnd() ) ) {
4091 playSample(s_won);
4092 state_changed = true;
4093 gameResult = GAMERESULT_WON;
4094 fadeMusic(SHORT_DELAY + 1000);
4095 gamestate->fadeScreen(true, SHORT_DELAY, endIsland_g);
4096 }
4097 }
4098 }
4099
4100 if( dispose_gamestate != NULL ) {
4101 LOG("delete dispose_gamestate %d (current gamestate is %d)\n", dispose_gamestate, gamestate);
4102 delete dispose_gamestate;
4103 LOG("done delete\n");
4104 dispose_gamestate = NULL;
4105 }
4106 }
4107
drawGame() const4108 void Game::drawGame() const {
4109 // we now redraw even when paused, to display paused message
4110 gamestate->draw();
4111 }
4112
4113 const char prefs_filename[] = "prefs";
4114 const bool prefs_survive_uninstall = false; // no need for prefs file to save uninstall, and seems better to allow resetting to initial condition (especially on Android where this is typical behaviour)
4115 const char onemousebutton_key[] = "onemousebutton";
4116 const char sound_on_key[] = "sound_on";
4117 const char music_on_key[] = "music_on";
4118 const char disallow_nukes_key[] = "disallow_nukes";
4119
createApplication()4120 bool Game::createApplication() {
4121 application = new Application();
4122 if( !application->init() ) {
4123 return false;
4124 }
4125 return true;
4126 }
4127
loadPrefs()4128 void Game::loadPrefs() {
4129 const char *prefs_fullfilename = getApplicationFilename(prefs_filename, prefs_survive_uninstall);
4130 SDL_RWops *prefs_file = SDL_RWFromFile(prefs_fullfilename, "rb");
4131 if( prefs_file != NULL ) {
4132 // reset
4133 pref_sound_on = false;
4134 pref_music_on = false;
4135 pref_disallow_nukes = false;
4136 onemousebutton = false;
4137
4138 const int MAX_LINE = 4096;
4139 char line[MAX_LINE+1] = "";
4140 char buffer[MAX_LINE+1] = "";
4141 int buffer_offset = 0;
4142 bool reached_end = false;
4143 int newline_index = 0;
4144 bool ok = true;
4145 while( ok ) {
4146 bool done = readLineFromRWOps(ok, prefs_file, buffer, line, MAX_LINE, buffer_offset, newline_index, reached_end);
4147 if( !ok ) {
4148 LOG("failed to read line for: %s\n", prefs_fullfilename);
4149 }
4150 else if( done ) {
4151 break;
4152 }
4153 else {
4154 LOG("read prefs line: %s", line);
4155 if( strncmp(line, onemousebutton_key, strlen(onemousebutton_key)) == 0 ) {
4156 LOG("enable onemousebutton from prefs\n");
4157 onemousebutton = true;
4158 }
4159 else if( strncmp(line, sound_on_key, strlen(sound_on_key)) == 0 ) {
4160 LOG("enable pref_sound_on from prefs\n");
4161 pref_sound_on = true;
4162 }
4163 else if( strncmp(line, music_on_key, strlen(music_on_key)) == 0 ) {
4164 LOG("enable pref_music_on from prefs\n");
4165 pref_music_on = true;
4166 }
4167 else if( strncmp(line, disallow_nukes_key, strlen(disallow_nukes_key)) == 0 ) {
4168 LOG("enable pref_disallow_nukes from prefs\n");
4169 pref_disallow_nukes = true;
4170 }
4171 }
4172 }
4173 prefs_file->close(prefs_file);
4174
4175 #if defined(__ANDROID__)
4176 // force onemousebutton mode, just to be safe
4177 onemousebutton = true;
4178 #endif
4179 }
4180 delete [] prefs_fullfilename;
4181 }
4182
savePrefs() const4183 void Game::savePrefs() const {
4184 const char *prefs_fullfilename = getApplicationFilename(prefs_filename, prefs_survive_uninstall);
4185 SDL_RWops *prefs_file = SDL_RWFromFile(prefs_fullfilename, "wb+");
4186 if( prefs_file == NULL ) {
4187 LOG("failed to open: %s\n", prefs_fullfilename);
4188 }
4189 else {
4190 if( onemousebutton ) {
4191 prefs_file->write(prefs_file, onemousebutton_key, sizeof(char), strlen(onemousebutton_key));
4192 prefs_file->write(prefs_file, "\n", sizeof(char), 1);
4193 }
4194 if( pref_sound_on ) {
4195 prefs_file->write(prefs_file, sound_on_key, sizeof(char), strlen(sound_on_key));
4196 prefs_file->write(prefs_file, "\n", sizeof(char), 1);
4197 }
4198 if( pref_music_on ) {
4199 prefs_file->write(prefs_file, music_on_key, sizeof(char), strlen(music_on_key));
4200 prefs_file->write(prefs_file, "\n", sizeof(char), 1);
4201 }
4202 if( pref_disallow_nukes ) {
4203 prefs_file->write(prefs_file, disallow_nukes_key, sizeof(char), strlen(disallow_nukes_key));
4204 prefs_file->write(prefs_file, "\n", sizeof(char), 1);
4205 }
4206 prefs_file->close(prefs_file);
4207 }
4208 delete [] prefs_fullfilename;
4209 }
4210
testFindSoldiersBuildingNewTower(const Sector * sector,int * total,int * squares) const4211 bool Game::testFindSoldiersBuildingNewTower(const Sector *sector, int *total, int *squares) const {
4212 bool found = false;
4213 *total = 0;
4214 *squares = 0;
4215 for(int cx=0;cx<map_width_c && !found;cx++) {
4216 for(int cy=0;cy<map_height_c && !found;cy++) {
4217 Sector *c_sector = game_g->getMap()->getSector(cx, cy);
4218 if( sector != c_sector ) {
4219 if( c_sector->getArmy(sector->getPlayer())->any(true) ) {
4220 found = true;
4221 *total = *total + c_sector->getArmy(sector->getPlayer())->getTotal();
4222 *squares = *squares + 1;
4223 if( c_sector->getPlayer() != -1 ) {
4224 throw string("shouldn't have sent soldiers to another tower");
4225 }
4226 for(int i=0;i<n_players_c;i++) {
4227 if( i != sector->getPlayer() && c_sector->getArmy(i)->any(true) ) {
4228 throw string("shouldn't have sent soldiers to a sector with other soldiers");
4229 }
4230 }
4231 }
4232 }
4233 }
4234 }
4235 return found;
4236 }
4237
runTests()4238 void Game::runTests() {
4239 game_g->setTesting(true);
4240
4241 human_player = rand() % 4;
4242 //human_player = 0;
4243 //human_player = 1;
4244 map = maps[start_epoch][selected_island];
4245 setGameStateID(GAMESTATEID_PLACEMEN);
4246 newGame();
4247 // check all maps are loaded
4248 for(int i=0;i<n_epochs_c;i++) {
4249 int expected_n_islands = i==n_epochs_c-1 ? 1 : max_islands_per_epoch_c;
4250 for(int j=0;j<expected_n_islands;j++) {
4251 if( maps[i][j] == NULL ) {
4252 LOG("failed to load island: %d , %d\n", i, j);
4253 throw string("failed to load island");
4254 }
4255 }
4256 for(int j=expected_n_islands;j<max_islands_per_epoch_c;j++) {
4257 if( maps[i][j] != NULL ) {
4258 LOG("unexpected island: %d , %d\n", i, j);
4259 throw string("unexpected island");
4260 }
4261 }
4262 }
4263
4264 while(true) {
4265 LOG("###TEST: epoch %d island %d\n", start_epoch, selected_island);
4266 PlaceMenGameState *placeMenGameState = static_cast<PlaceMenGameState *>(gamestate);
4267 setupPlayers();
4268 int sx = 0, sy = 0;
4269 if( start_epoch == 0 && selected_island == 0 ) {
4270 sx = 1;
4271 sy = 2;
4272 placeMenGameState->getChooseMenPanel()->setNMen(15);
4273 }
4274 else if( start_epoch == 0 && selected_island == 1 ) {
4275 sx = 1;
4276 sy = 2;
4277 placeMenGameState->getChooseMenPanel()->setNMen(15);
4278 }
4279 else if( start_epoch == 0 && selected_island == 2 ) {
4280 sx = 1;
4281 sy = 2;
4282 placeMenGameState->getChooseMenPanel()->setNMen(15);
4283 }
4284 else if( start_epoch == 1 && selected_island == 0 ) {
4285 sx = 1;
4286 sy = 2;
4287 placeMenGameState->getChooseMenPanel()->setNMen(15);
4288 }
4289 else if( start_epoch == 4 && selected_island == 1 ) {
4290 sx = 3;
4291 sy = 4;
4292 placeMenGameState->getChooseMenPanel()->setNMen(15);
4293 map->setReserved(sx-3, sy, true);
4294 }
4295 else if( start_epoch == 5 && selected_island == 0 ) {
4296 sx = 1;
4297 sy = 4;
4298 placeMenGameState->getChooseMenPanel()->setNMen(15);
4299 }
4300 else {
4301 map->findRandomSector(&sx, &sy);
4302 placeMenGameState->getChooseMenPanel()->setNMen(10);
4303 }
4304 placeMenGameState->setStartMapPos(sx, sy); // will automatically switch to playing gamestate
4305 updateGame(); // needed to dispose the gamestate
4306
4307 int ex = -1, ey = -1;
4308 for(int y=0;y<map_height_c && ex==-1;y++) {
4309 for(int x=0;x<map_width_c && ex==-1;x++) {
4310 Sector *sector = map->getSector(x, y);
4311 if( sector != NULL ) {
4312 if( sector->getPlayer() != -1 && sector->getPlayer() != human_player ) {
4313 ex = x;
4314 ey = y;
4315 }
4316 }
4317 }
4318 }
4319 if( ex == -1 || ey == -1 ) {
4320 throw string("couldn't find ai player");
4321 }
4322
4323 // test saving state
4324 saveState();
4325 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
4326 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
4327 if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) != 0 ) {
4328 throw string("save state file not created");
4329 }
4330 #endif
4331
4332 // test loading state
4333 delete gamestate;
4334 gamestate = NULL;
4335 if( !loadState() ) {
4336 throw string("failed to load state");
4337 }
4338 else if( gamestate == NULL ) {
4339 throw string("failed to create new gamestate when loading state");
4340 }
4341 else if( gameStateID != GAMESTATEID_PLAYING ) {
4342 throw string("expected playinggamestate when loading state");
4343 }
4344 else if( tutorial != NULL ) {
4345 throw string("didn't expect tutorial when loading state");
4346 }
4347 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
4348 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
4349 else if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) == 0 ) {
4350 throw string("save state file should have been deleted");
4351 }
4352 #endif
4353
4354 PlayingGameState *playingGameState = static_cast<PlayingGameState *>(gamestate);
4355 // island specific testing
4356 if( start_epoch == 0 && selected_island == 0 ) {
4357 Sector *start_sector = map->getSector(sx, sy);
4358 if( start_sector->bestDesign(Invention::WEAPON, 0) != NULL ) {
4359 throw string("shouldn't be able to build a rock weapon yet");
4360 }
4361 while( start_sector->anyElements(ROCK) ) {
4362 start_sector->mineElement(human_player, ROCK);
4363 }
4364 Design *design = start_sector->canResearch(Invention::WEAPON, 0);
4365 if( design == NULL ) {
4366 throw string("can't design rock weapon");
4367 }
4368 else if( !design->isErgonomicallyTerrific() ) {
4369 throw string("rock weapon isn't ergonomically terrific");
4370 }
4371 playingGameState->setCurrentDesign(sx, sy, design);
4372 start_sector->invent(human_player);
4373 if( !start_sector->canBuildDesign(design) ) {
4374 throw string("can't build rock weapon");
4375 }
4376 for(int i=0;i<10;i++) {
4377 if( !playingGameState->assembleArmy(sx, sy, 0, 1) ) {
4378 throw string("can't assemble rock weapon");
4379 }
4380 }
4381 for(int i=0;i<4;i++) {
4382 if( !playingGameState->assembleArmyUnarmed(sx, sy, 1) ) {
4383 throw string("can't assemble unarmed");
4384 }
4385 }
4386 if( !playingGameState->moveAssembledArmyTo(sx, sy, sx+1, sy) ) {
4387 throw string("can't move assembled army");
4388 }
4389 Sector *target_sector = map->getSector(sx+1, sy);
4390 Army *army = target_sector->getArmy(human_player);
4391 if( army->getTotal() != 14 ) {
4392 throw string("unexpected army total (1)");
4393 }
4394 if( army->getTotalMen() != 14 ) {
4395 throw string("unexpected army total men");
4396 }
4397 if( army->getSoldiers(0) != 10 ) {
4398 throw string("unexpected army number of rock weapons");
4399 }
4400 if( !playingGameState->moveArmyTo(sx+1, sy, sx, sy) ) {
4401 throw string("can't return army");
4402 }
4403 army = start_sector->getArmy(human_player);
4404 if( army->getTotal() >= 14 ) {
4405 throw string("no men were lost from retreating");
4406 }
4407 int n_men = army->getTotal();
4408 int n_rocks = army->getSoldiers(0);
4409 int n_population = start_sector->getPopulation();
4410 if( !playingGameState->moveArmyTo(sx, sy, sx+1, sy) ) {
4411 throw string("can't move army out again");
4412 }
4413 army = target_sector->getArmy(human_player);
4414 if( army->getTotal() != n_men ) {
4415 throw string("unexpected army total (2)");
4416 }
4417 if( !playingGameState->returnArmy(sx, sy, sx+1, sy) ) {
4418 throw string("can't return army to tower");
4419 }
4420 if( army->getTotal() != 0 ) {
4421 throw string("shouldn't have any men in the target sector");
4422 }
4423 army = start_sector->getArmy(human_player);
4424 if( army->getTotal() != 0 ) {
4425 throw string("shouldn't have any men in the start sector");
4426 }
4427 if( start_sector->getPopulation() <= n_population ) {
4428 throw string("didn't return any men");
4429 }
4430 else if( start_sector->getPopulation() >= n_population+n_men ) {
4431 throw string("no men were lost from retreating to tower");
4432 }
4433 else if( start_sector->getStoredArmy()->getSoldiers(0) <= 0 ) {
4434 throw string("didn't return any rock weapons");
4435 }
4436 else if( start_sector->getStoredArmy()->getSoldiers(0) >= n_rocks ) {
4437 throw string("no rock weapons were lost from retreating to tower");
4438 }
4439 }
4440 else if( start_epoch == 0 && selected_island == 1 ) {
4441 Sector *start_sector = map->getSector(sx, sy);
4442 if( start_sector->bestDesign(Invention::WEAPON, 0) != NULL ) {
4443 throw string("shouldn't be able to build a rock weapon yet");
4444 }
4445 // test time with and without sleep for mining:
4446 for(int type=0;type<2;type++) {
4447 unsigned int time_s = game_g->getApplication()->getTicks();
4448 unsigned int elapsed_time = time_s;
4449 int i_mined = 0, i_mined_fraction = 0;
4450 start_sector->getElementStocks(&i_mined, &i_mined_fraction, ROCK);
4451 int i_total = i_mined*element_multiplier_c + i_mined_fraction;
4452 const int n_steps_c = 22; // must not be greater than element_multiplier_c*max_gatherables_stored_c/2
4453 for(;;) {
4454 if( type == 0 ) {
4455 game_g->getApplication()->wait();
4456 }
4457 unsigned int new_time = game_g->getApplication()->getTicks();
4458 updateTime(new_time - elapsed_time);
4459 elapsed_time = new_time;
4460 updateGame();
4461 int mined = 0, mined_fraction = 0;
4462 start_sector->getElementStocks(&mined, &mined_fraction, ROCK);
4463 int total = mined*element_multiplier_c + mined_fraction;
4464 if( total >= i_total + n_steps_c ) {
4465 break;
4466 }
4467 }
4468 int time = game_g->getApplication()->getTicks() - time_s;
4469 int expected_time = (int)(( mine_rate_c * gameticks_per_hour_c * n_steps_c ) / ( element_multiplier_c * n_gatherable_rate_c * time_ratio_c ));
4470 int allowed_error = 20;
4471 if( abs(time - expected_time) > allowed_error ) {
4472 LOG("mining took %d , expected %d\n", time, expected_time);
4473 throw string("unexpected time for designing");
4474 }
4475 }
4476 // now mine the rest
4477 while( start_sector->anyElements(ROCK) ) {
4478 start_sector->mineElement(human_player, ROCK);
4479 }
4480 Design *design = start_sector->canResearch(Invention::WEAPON, 0);
4481 if( design == NULL ) {
4482 throw string("can't design rock weapon");
4483 }
4484 else if( !design->isErgonomicallyTerrific() ) {
4485 throw string("rock weapon isn't ergonomically terrific");
4486 }
4487 playingGameState->setCurrentDesign(sx, sy, design);
4488 playingGameState->setNDesigners(sx, sy, 1);
4489 // test time with and without sleep for designing:
4490 for(int type=0;type<2;type++) {
4491 unsigned int time_s = game_g->getApplication()->getTicks();
4492 unsigned int elapsed_time = time_s;
4493 int i_halfdays = 0, i_hours = 0;
4494 start_sector->inventionTimeLeft(&i_halfdays, &i_hours);
4495 int i_total = i_halfdays*12 + i_hours;
4496 const int n_steps_c = 3;
4497 for(;;) {
4498 if( type == 0 ) {
4499 game_g->getApplication()->wait();
4500 }
4501 unsigned int new_time = game_g->getApplication()->getTicks();
4502 updateTime(new_time - elapsed_time);
4503 elapsed_time = new_time;
4504 updateGame();
4505 int halfdays = 0, hours = 0;
4506 start_sector->inventionTimeLeft(&halfdays, &hours);
4507 int total = halfdays*12 + hours;
4508 if( total <= i_total - n_steps_c ) {
4509 break;
4510 }
4511 }
4512 int time = game_g->getApplication()->getTicks() - time_s;
4513 int expected_time = (int)(( gameticks_per_hour_c * n_steps_c ) / time_ratio_c);
4514 int allowed_error = 100;
4515 if( abs(time - expected_time) > allowed_error ) {
4516 LOG("halfday took %d , expected %d\n", time, expected_time);
4517 throw string("unexpected time for designing");
4518 }
4519 }
4520 }
4521 else if( start_epoch == 0 && selected_island == 2 ) {
4522 Sector *start_sector = map->getSector(sx, sy);
4523 if( start_sector->bestDesign(Invention::DEFENCE, 0) != NULL ) {
4524 throw string("shouldn't be able to build a stick weapon yet");
4525 }
4526 while( start_sector->anyElements(WOOD) ) {
4527 start_sector->mineElement(human_player, WOOD);
4528 }
4529 Design *design_stick = start_sector->canResearch(Invention::DEFENCE, 0);
4530 if( design_stick == NULL ) {
4531 throw string("can't design stick weapon");
4532 }
4533 else if( design_stick->isErgonomicallyTerrific() ) {
4534 throw string("stick weapon shouldn't be ergonomically terrific");
4535 }
4536 playingGameState->setCurrentDesign(sx, sy, design_stick);
4537 start_sector->invent(human_player);
4538 if( start_sector->getEpoch() != start_epoch ) {
4539 throw string("not on expected epoch");
4540 }
4541 if( !start_sector->canBuildDesign(design_stick) ) {
4542 throw string("can't build stick weapon");
4543 }
4544 playingGameState->deployDefender(sx, sy, BUILDING_TOWER, 1, 0);
4545 if( start_sector->getBuilding(BUILDING_TOWER)->getTurretMan(1) != 0 ) {
4546 throw string("didn't deploy defender");
4547 }
4548
4549 Design *design_shield = start_sector->canResearch(Invention::SHIELD, 1);
4550 if( design_shield == NULL ) {
4551 throw string("can't design shield");
4552 }
4553 else if( design_shield->isErgonomicallyTerrific() ) {
4554 throw string("shield shouldn't be ergonomically terrific");
4555 }
4556 playingGameState->setCurrentDesign(sx, sy, design_shield);
4557 start_sector->invent(human_player);
4558 if( start_sector->getEpoch() != start_epoch+1 ) {
4559 throw string("didn't advance a tech level");
4560 }
4561 if( !start_sector->canBuildDesign(design_shield) ) {
4562 throw string("can't build shield");
4563 }
4564 int max_health = start_sector->getBuilding(BUILDING_TOWER)->getMaxHealth();
4565 if( start_sector->getBuilding(BUILDING_TOWER)->getHealth() != max_health ) {
4566 throw string("building not at max health");
4567 }
4568 start_sector->getBuilding(BUILDING_TOWER)->addHealth(-20);
4569 if( start_sector->getBuilding(BUILDING_TOWER)->getHealth() != max_health-20 ) {
4570 throw string("building not at reduced health");
4571 }
4572 playingGameState->useShield(sx, sy, BUILDING_TOWER, 1);
4573 if( start_sector->getBuilding(BUILDING_TOWER)->getHealth() != max_health-10 ) {
4574 throw string("shield didn't work as expected");
4575 }
4576 }
4577 else if( start_epoch == 1 && selected_island == 0 ) {
4578 Sector *sector = map->getSector(ex, ey);
4579 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4580 if( sector->getCurrentDesign() != NULL ) {
4581 throw string("ai shouldn't be able to design anything yet");
4582 }
4583 for(int i=0;i<N_ID;i++) {
4584 while( sector->anyElements((Id)i) ) {
4585 sector->mineElement(human_player, (Id)i);
4586 }
4587 }
4588 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4589 if( sector->getCurrentDesign() == NULL ) {
4590 throw string("ai didn't design anything");
4591 }
4592 // now invent everything
4593 for(int i=0;i<3;i++) {
4594 for(int j=0;j<n_epochs_c;j++) {
4595 Design *design = sector->canResearch((Invention::Type)i, j);
4596 if( design != NULL ) {
4597 sector->setCurrentDesign(design);
4598 sector->invent(human_player);
4599 }
4600 }
4601 }
4602 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4603 if( sector->canBuild(BUILDING_MINE) ) {
4604 if( sector->getBuilders(BUILDING_MINE) <= 0 ) {
4605 throw string("didn't start building mine");
4606 }
4607 }
4608 if( sector->canBuild(BUILDING_FACTORY) ) {
4609 if( sector->getBuilders(BUILDING_FACTORY) <= 0 ) {
4610 throw string("didn't start building factory");
4611 }
4612 }
4613 if( sector->canBuild(BUILDING_LAB) ) {
4614 throw string("shouldn't be able to build a lab");
4615 }
4616 }
4617 else if( start_epoch == 4 && selected_island == 1 ) {
4618 Sector *start_sector = map->getSector(sx, sy);
4619 if( !start_sector->canBuild(BUILDING_MINE) ) {
4620 throw string("can't build mine");
4621 }
4622 start_sector->buildBuilding(BUILDING_MINE);
4623 if( !start_sector->canBuild(BUILDING_FACTORY) ) {
4624 throw string("can't build factory");
4625 }
4626 start_sector->buildBuilding(BUILDING_FACTORY);
4627 if( start_sector->canBuild(BUILDING_LAB) ) {
4628 throw string("shouldn't be able to build lab yet");
4629 }
4630 for(int i=0;i<N_ID;i++) {
4631 while( start_sector->canMine((Id)i) && start_sector->anyElements((Id)i) ) {
4632 start_sector->mineElement(human_player, (Id)i);
4633 }
4634 }
4635 Design *design_shield0 = start_sector->canResearch(Invention::SHIELD, 4);
4636 if( design_shield0 == NULL ) {
4637 throw string("can't design shield0");
4638 }
4639 Design *design_crossbow = start_sector->canResearch(Invention::DEFENCE, 4);
4640 if( design_crossbow == NULL ) {
4641 throw string("can't design crossbow");
4642 }
4643 Design *design_catapult = start_sector->canResearch(Invention::WEAPON, 4);
4644 if( design_catapult == NULL ) {
4645 throw string("can't design catapult");
4646 }
4647 Design *design_biplane = start_sector->canResearch(Invention::WEAPON, 6);
4648 if( design_biplane != NULL ) {
4649 throw string("shouldn't be able to design biplane yet (no lab)");
4650 }
4651 playingGameState->setCurrentDesign(sx, sy, design_shield0);
4652 start_sector->invent(human_player);
4653 playingGameState->setCurrentDesign(sx, sy, design_crossbow);
4654 start_sector->invent(human_player);
4655 if( start_sector->getEpoch() != start_epoch ) {
4656 throw string("not on expected epoch");
4657 }
4658 playingGameState->setCurrentDesign(sx, sy, design_catapult);
4659 start_sector->invent(human_player);
4660 if( start_sector->getEpoch() != start_epoch+1 ) {
4661 throw string("didn't advance a tech level");
4662 }
4663 design_biplane = start_sector->canResearch(Invention::WEAPON, 6);
4664 if( design_biplane != NULL ) {
4665 throw string("still shouldn't be able to design biplane yet (no lab)");
4666 }
4667 if( !start_sector->canBuild(BUILDING_LAB) ) {
4668 throw string("can't build lab");
4669 }
4670 start_sector->buildBuilding(BUILDING_LAB);
4671 design_biplane = start_sector->canResearch(Invention::WEAPON, 6);
4672 if( design_biplane == NULL ) {
4673 throw string("can't design biplane");
4674 }
4675 playingGameState->setCurrentDesign(sx, sy, design_biplane);
4676 start_sector->invent(human_player);
4677 if( !start_sector->canBuildDesign(design_biplane) ) {
4678 throw string("can't build biplane");
4679 }
4680 playingGameState->setCurrentManufacture(sx, sy, design_biplane);
4681 start_sector->buildDesign();
4682 int n_men = start_sector->getPopulation();
4683 if( !playingGameState->assembleArmy(sx, sy, 6, 1) ) {
4684 throw string("can't assemble biplane");
4685 }
4686 if( playingGameState->assembleArmy(sx, sy, 6, 1) ) {
4687 throw string("shouldn't be able to assemble another biplane");
4688 }
4689 if( !playingGameState->assembleArmyUnarmed(sx, sy, 1) ) {
4690 throw string("can't assemble unarmed");
4691 }
4692 if( start_sector->getPopulation() != n_men - 3 ) {
4693 throw string("unexpected remaining population");
4694 }
4695 if( start_sector->getAssembledArmy()->getTotal() != 2 ) {
4696 throw string("unexpected assembled army");
4697 }
4698 if( playingGameState->moveAssembledArmyTo(sx, sy, sx-3, sy) ) {
4699 throw string("shouldn't have been able to move all the assembled army");
4700 }
4701 if( start_sector->getAssembledArmy()->getTotal() != 1 ) {
4702 throw string("unexpected assembled army after moving biplanes");
4703 }
4704 Army *storedArmy = start_sector->getStoredArmy();
4705 if( storedArmy->getTotal() != 0 ) {
4706 throw string("didn't expect a stored army");
4707 }
4708 playingGameState->returnAssembledArmy(sx, sy);
4709 if( storedArmy->getTotal() != 0 ) {
4710 // unarmed men shouldn't be kept in stored_army
4711 throw string("didn't expect a stored army after returning assembled army");
4712 }
4713 if( start_sector->getPopulation() != n_men - 2 ) {
4714 throw string("unexpected remaining population after returning unarmed man");
4715 }
4716 Sector *targ_sector = map->getSector(sx-3, sy);
4717 Army *army = targ_sector->getArmy(human_player);
4718 if( army->getTotal() != 1 ) {
4719 throw string("unexpected army total");
4720 }
4721 if( army->getTotalMen() != 2 ) {
4722 throw string("unexpected army total men");
4723 }
4724 if( army->getSoldiers(6) != 1 ) {
4725 throw string("unexpected army number of biplanes");
4726 }
4727 // now put some unarmed men to destination
4728 army->add(n_epochs_c, 10);
4729 if( army->getTotal() != 11 ) {
4730 throw string("unexpected army total after adding unarmed men");
4731 }
4732 if( playingGameState->moveArmyTo(sx-3, sy, sx, sy) ) {
4733 throw string("shouldn't have been able to move all the army back");
4734 }
4735 if( army->getTotal() != 10 ) {
4736 throw string("unexpected army total after returning biplanes");
4737 }
4738 if( !playingGameState->moveArmyTo(sx, sy, sx-3, sy) ) {
4739 throw string("should have been able to move all the biplanes back again");
4740 }
4741 if( army->getTotal() != 11 ) {
4742 throw string("unexpected army total after moving biplanes back again");
4743 }
4744 // now put a catapult to destination
4745 army->add(4, 1);
4746 if( army->getTotal() != 12 ) {
4747 throw string("unexpected army total after adding a catapult");
4748 }
4749 if( army->getTotalMen() != 14 ) {
4750 throw string("unexpected army total men after adding a catapult");
4751 }
4752 if( storedArmy->getTotal() != 0 ) {
4753 throw string("still didn't expect a stored army");
4754 }
4755 if( playingGameState->returnArmy(sx, sy, sx-3, sy) ) {
4756 throw string("shouldn't have been able to move all the army back to tower");
4757 }
4758 if( army->getTotal() != 11 ) {
4759 throw string("unexpected army total after returning biplanes to tower");
4760 }
4761 if( army->getSoldiers(4) != 1 ) {
4762 throw string("unexpected army number of catapults after returning biplanes to tower");
4763 }
4764 if( storedArmy->getTotal() != 1 ) {
4765 throw string("unexpected stored army after returning biplanes");
4766 }
4767 if( storedArmy->getSoldiers(6) != 1 ) {
4768 throw string("unexpected stored army number of biplanes after returning biplanes");
4769 }
4770 }
4771 else if( start_epoch == 5 && selected_island == 0 ) {
4772 Sector *start_sector = map->getSector(sx, sy);
4773 if( !start_sector->canBuild(BUILDING_MINE) ) {
4774 throw string("can't build mine");
4775 }
4776 start_sector->buildBuilding(BUILDING_MINE);
4777 if( !start_sector->canBuild(BUILDING_FACTORY) ) {
4778 throw string("can't build factory");
4779 }
4780 start_sector->buildBuilding(BUILDING_FACTORY);
4781 if( !start_sector->canBuild(BUILDING_LAB) ) {
4782 throw string("can't build lab");
4783 }
4784 start_sector->buildBuilding(BUILDING_LAB);
4785 for(int i=0;i<N_ID;i++) {
4786 while( start_sector->canMine((Id)i) && start_sector->anyElements((Id)i) ) {
4787 start_sector->mineElement(human_player, (Id)i);
4788 }
4789 }
4790 // first test with disallow nukes pref
4791 pref_disallow_nukes = true;
4792 Design *design = start_sector->canResearch(Invention::WEAPON, 8);
4793 if( design != NULL ) {
4794 throw string("didn't expect to design nuke when disallow nukes is on");
4795 }
4796 pref_disallow_nukes = false;
4797 design = start_sector->canResearch(Invention::WEAPON, 8);
4798 if( design == NULL ) {
4799 throw string("can't design nuke");
4800 }
4801 playingGameState->setCurrentDesign(sx, sy, design);
4802 start_sector->invent(human_player);
4803 if( !start_sector->canBuildDesign(design) ) {
4804 throw string("can't build nuke");
4805 }
4806 playingGameState->setCurrentManufacture(sx, sy, design);
4807 start_sector->buildDesign();
4808 if( !playingGameState->assembleArmy(sx, sy, 8, 1) ) {
4809 throw string("can't assemble nuke");
4810 }
4811 if( !playingGameState->nukeSector(sx, sy, sx+1, sy) ) {
4812 throw string("can't nuke sector");
4813 }
4814 // build another one
4815 if( !start_sector->canBuildDesign(design) ) {
4816 throw string("can't build nuke");
4817 }
4818 playingGameState->setCurrentManufacture(sx, sy, design);
4819 start_sector->buildDesign();
4820 if( !playingGameState->assembleArmy(sx, sy, 8, 1) ) {
4821 throw string("can't assemble nuke");
4822 }
4823 // shouldn't be able to nuke again
4824 if( playingGameState->nukeSector(sx, sy, sx+1, sy) ) {
4825 throw string("didn't expect to nuke sector again");
4826 }
4827 }
4828 else if( start_epoch == 9 && selected_island == 0 ) {
4829 // test AI behaviour
4830 Sector *sector = map->getSector(ex, ey);
4831 sector->cheat(human_player);
4832 // make sure we don't mine when no unmined elements remaining
4833 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4834 Id element = MOONLITE; // should be a non-gatherable, that on its own can't be used to invent something on this island
4835 for(int i=0;i<N_ID;i++) {
4836 if( sector->getMiners((Id)i) > 0 ) {
4837 throw string("didn't expect miners when sector is used up");
4838 }
4839 }
4840 // now check we mine if there is some element present
4841 sector->setElements(element, 5*element_multiplier_c);
4842 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4843 if( sector->getMiners(element) == 0 ) {
4844 throw string("didn't assign miners");
4845 }
4846 // now use up the element stocks
4847 for(int i=0;i<N_ID;i++) {
4848 int n = 0, fraction = 0;
4849 sector->getElementStocks(&n, &fraction, (Id)i);
4850 sector->reduceElementStocks((Id)i, n*element_multiplier_c+fraction);
4851 sector->getElementStocks(&n, &fraction, (Id)i);
4852 if( n > 0 || fraction > 0 ) {
4853 throw string("didn't get rid of all elements");
4854 }
4855 }
4856 // need to reset any design or manufacturer
4857 sector->setCurrentDesign(NULL);
4858 sector->setCurrentManufacture(NULL);
4859 // now check we trash the designs, and check we can't research them again
4860 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4861 for(int i=0;i<n_epochs_c;i++) {
4862 if( sector->inventionKnown(Invention::WEAPON, i) )
4863 throw string("didn't expect invention to still be known");
4864 else if( sector->canResearch(Invention::WEAPON, i) )
4865 throw string("didn't expect to be able to research invention again");
4866 if( sector->inventionKnown(Invention::DEFENCE, i) )
4867 throw string("didn't expect invention to still be known");
4868 else if( sector->canResearch(Invention::DEFENCE, i) )
4869 throw string("didn't expect to be able to research invention again");
4870 if( sector->inventionKnown(Invention::SHIELD, i) )
4871 throw string("didn't expect invention to still be known");
4872 else if( sector->canResearch(Invention::SHIELD, i) )
4873 throw string("didn't expect to be able to research invention again");
4874 }
4875 // should have set miners, in case we can research something again
4876 if( sector->usedUp() ) {
4877 throw string("sector is deemed used up even though elements remain");
4878 }
4879 else if( sector->getMiners(element) == 0 ) {
4880 throw string("didn't assign miners after trashing designs");
4881 }
4882 // but if we have 6 or more stocks, then no need to mine after all
4883 sector->reduceElementStocks(element, -6*element_multiplier_c);
4884 int saved_population = sector->getPopulation();
4885 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4886 if( !sector->usedUp() ) {
4887 throw string("sector isn't deemed used up");
4888 }
4889 else if( sector->getMiners(element) > 0 ) {
4890 throw string("shouldn't assign miners even though some remaining, as we already have stocks and the sector is deemed used up");
4891 }
4892 // check that the AI sent soldiers to a new empty sector
4893 int total = 0, squares = 0;
4894 bool found = testFindSoldiersBuildingNewTower(sector, &total, &squares);
4895 if( !found ) {
4896 throw string("didn't send soldiers to build another tower");
4897 }
4898 else if( squares != 1 ) {
4899 throw string("didn't send soldiers to expected number of squares");
4900 }
4901 else if( total != saved_population-1 ) {
4902 throw string("didn't send expected number of soldiers");
4903 }
4904 else if( sector->getPopulation() != 1 ) {
4905 throw string("remaining population not as expected");
4906 }
4907 // now add some more men, but make sure we don't evacuate if it means leaving weapons behind
4908 sector->setPopulation(40);
4909 sector->getStoredArmy()->add(9, 6); // 6*8 needs 48 men
4910 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4911 found = testFindSoldiersBuildingNewTower(sector, &total, &squares);
4912 // should be unchanged from above
4913 if( !found ) {
4914 throw string("didn't send soldiers to build another tower");
4915 }
4916 else if( squares != 1 ) {
4917 throw string("didn't send soldiers to expected number of squares");
4918 }
4919 else if( total != saved_population-1 ) {
4920 throw string("didn't send expected number of soldiers");
4921 }
4922 else if( sector->getPopulation() != 40 ) {
4923 throw string("remaining population not as expected");
4924 }
4925 else if( sector->getStoredArmy()->getSoldiers(9) != 6 ) {
4926 throw string("unexpected number of remaining soldiers");
4927 }
4928 // now set enough
4929 sector->setPopulation(50);
4930 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4931 found = testFindSoldiersBuildingNewTower(sector, &total, &squares);
4932 // should be sent to the same square
4933 if( !found ) {
4934 throw string("didn't send soldiers to build another tower");
4935 }
4936 else if( squares != 1 ) {
4937 throw string("didn't send soldiers to expected number of squares");
4938 }
4939 else if( total != saved_population-1 + 6+1 ) {
4940 throw string("didn't send expected number of soldiers");
4941 }
4942 else if( sector->getPopulation() != 1 ) {
4943 throw string("remaining population not as expected");
4944 }
4945 else if( sector->getStoredArmy()->getSoldiers(9) != 0 ) {
4946 throw string("unexpected number of remaining soldiers");
4947 }
4948 // now if we have more than 375, should still send men even if it means leaving weapons behind
4949 sector->setPopulation(376);
4950 sector->getStoredArmy()->add(9, 50); // 50*8 needs 400 men
4951 players[sector->getPlayer()]->doAIUpdate(human_player, playingGameState);
4952 found = testFindSoldiersBuildingNewTower(sector, &total, &squares);
4953 // should be sent to the same square
4954 if( !found ) {
4955 throw string("didn't send soldiers to build another tower");
4956 }
4957 else if( squares != 1 ) {
4958 throw string("didn't send soldiers to expected number of squares");
4959 }
4960 else if( total != saved_population-1 + 6+1 + 46+7 ) {
4961 throw string("didn't send expected number of soldiers");
4962 }
4963 else if( sector->getPopulation() != 1 ) {
4964 throw string("remaining population not as expected");
4965 }
4966 else if( sector->getStoredArmy()->getSoldiers(9) != 4 ) {
4967 throw string("unexpected number of remaining soldiers");
4968 }
4969
4970 // test player shutting down the sector
4971 Sector *start_sector = map->getSector(sx, sy);
4972 if( start_sector->canShutdown() ) {
4973 throw string("shouldn't be able to shutdown the sector yet");
4974 }
4975 if( !playingGameState->assembleArmyUnarmed(sx, sy, 1) ) {
4976 throw string("can't assemble unarmed");
4977 }
4978 if( !playingGameState->moveAssembledArmyTo(sx, sy, sx, sy) ) {
4979 throw string("can't move assembled army");
4980 }
4981 if( !start_sector->canShutdown() ) {
4982 throw string("can't shutdown the sector");
4983 }
4984 playingGameState->shutdown(sx, sy);
4985 }
4986
4987 endIsland();
4988 updateGame(); // needed to dispose the gamestate
4989
4990 setGameStateID(GAMESTATEID_PLACEMEN);
4991 updateGame(); // needed to dispose the gamestate
4992 nextIsland();
4993 if( selected_island == 0 ) {
4994 nextEpoch();
4995 if( start_epoch == 0 )
4996 break;
4997 }
4998 }
4999
5000 // now test loading
5001 gameType = GAMETYPE_ALLISLANDS;
5002 newGame();
5003 if( n_men_store != getMenPerEpoch() ) {
5004 throw string("unexpected number of men");
5005 }
5006 if( !loadGame("_test_savegames/game_0.SAV") ) {
5007 throw string("failed to load game");
5008 }
5009 if( human_player != gamestate->getClientPlayer() ) {
5010 throw string("human_player doesn't match gamestate client_player");
5011 }
5012 if( human_player != 2 ) {
5013 throw string("didn't set human_player from saved game");
5014 }
5015
5016 // now test loading saved states
5017 delete gamestate;
5018 gamestate = NULL;
5019 copyFile("_test_savegames/autosave_bad.sav", getApplicationFilename(autosave_filename, autosave_survive_uninstall));
5020 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5021 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5022 if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) != 0 ) {
5023 throw string("failed to copy save state file");
5024 }
5025 #endif
5026 if( loadState() ) {
5027 throw string("didn't expect to load bad state");
5028 }
5029 else if( gamestate != NULL ) {
5030 throw string("didn't expect to create a gamestate when loading bad state");
5031 }
5032 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5033 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5034 else if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) == 0 ) {
5035 throw string("save state file should have been deleted");
5036 }
5037 #endif
5038
5039 copyFile("_test_savegames/autosave_tutorial.sav", getApplicationFilename(autosave_filename, autosave_survive_uninstall));
5040 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5041 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5042 if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) != 0 ) {
5043 throw string("failed to copy save state file");
5044 }
5045 #endif
5046 if( !loadState() ) {
5047 throw string("failed to load state");
5048 }
5049 else if( gamestate == NULL ) {
5050 throw string("failed to create new gamestate when loading state");
5051 }
5052 else if( gameStateID != GAMESTATEID_PLAYING ) {
5053 throw string("expected playinggamestate when loading state");
5054 }
5055 else if( tutorial == NULL ) {
5056 throw string("didn't create tutorial when loading state");
5057 }
5058 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5059 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5060 else if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) == 0 ) {
5061 throw string("save state file should have been deleted");
5062 }
5063 #endif
5064
5065 delete gamestate;
5066 gamestate = NULL;
5067 delete tutorial;
5068 tutorial = NULL;
5069 copyFile("_test_savegames/autosave_biplanes.sav", getApplicationFilename(autosave_filename, autosave_survive_uninstall));
5070 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5071 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5072 if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) != 0 ) {
5073 throw string("failed to copy save state file");
5074 }
5075 #endif
5076 if( !loadState() ) {
5077 throw string("failed to load state");
5078 }
5079 else if( gamestate == NULL ) {
5080 throw string("failed to create new gamestate when loading state");
5081 }
5082 else if( gameStateID != GAMESTATEID_PLAYING ) {
5083 throw string("expected playinggamestate when loading state");
5084 }
5085 else if( tutorial != NULL ) {
5086 throw string("didn't expect tutorial when loading state");
5087 }
5088 #if defined(_WIN32) || defined(__DragonFly__) || (defined(__APPLE__) && defined(__MACH__))
5089 // ensure on a platform where access() is defined (it isn't available on AROS etc - we could write platform specific code, but not really worth it for now)
5090 else if( access(getApplicationFilename(autosave_filename, autosave_survive_uninstall), 0) == 0 ) {
5091 throw string("save state file should have been deleted");
5092 }
5093 #endif
5094 {
5095 if( !map->isSectorAt(1, 3) ) {
5096 throw string("no sector");
5097 }
5098 const Sector *sector = map->getSector(1, 3);
5099 if( sector->getArmy(0)->getSoldiers(6) != 4 ) {
5100 throw string("unexpected number of biplanes");
5101 }
5102 else if( sector->getArmy(0)->getTotal() != 4 ) {
5103 throw string("unexpected number of soldiers");
5104 }
5105 for(int i=1;i<4;i++) {
5106 if( sector->getArmy(i)->getTotal() != 0 ) {
5107 throw string("unexpected number of enemy soldiers");
5108 }
5109 }
5110 }
5111 }
5112
copyFile(const char * src,const char * dst) const5113 void Game::copyFile(const char *src, const char *dst) const {
5114 SDL_RWops *read_file = SDL_RWFromFile(src, "r");
5115 if( read_file == NULL ) {
5116 throw string("couldn't open test save state file");
5117 }
5118 SDL_RWops *save_file = SDL_RWFromFile(dst, "w");
5119 if( save_file == NULL ) {
5120 throw string("couldn't copy save state file");
5121 }
5122 #if SDL_MAJOR_VERSION == 1
5123 // SDL 1 doesn't have a size parameter
5124 SDL_RWseek(read_file, 0, RW_SEEK_END);
5125 size_t size = (size_t)SDL_RWtell(read_file);
5126 SDL_RWseek(read_file, 0, RW_SEEK_SET);
5127 #else
5128 size_t size = (size_t)read_file->size(read_file);
5129 #endif
5130 char *buffer = new char[size+1];
5131 if( read_file->read(read_file, buffer, 1, size) == 0 ) {
5132 read_file->close(read_file);
5133 throw string("failed to read from test save state file");
5134 }
5135 save_file->write(save_file, buffer, 1, size);
5136 save_file->close(save_file);
5137 read_file->close(read_file);
5138 delete [] buffer;
5139 }
5140
playGame(int n_args,char * args[])5141 void playGame(int n_args, char *args[]) {
5142 LOG("playGame()\n");
5143
5144 Player::resetAllAlliances(); // need to reset for Android, where variables aren't reinitialised if the native app restarts!
5145 game_g = new Game();
5146
5147 #ifdef _DEBUG
5148 //_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF | _CRTDBG_CHECK_ALWAYS_DF ); // TEST!
5149 debugwindow = true;
5150 #endif
5151
5152 bool fullscreen = true;
5153 #if defined(__amigaos4__) || defined(AROS) || defined(__MORPHOS__)
5154 fullscreen = false; // run in windowed mode due to reported performance problems in fullscreen mode on AmigaOS 4; also randomly hangs on AROS in fullscreen mode; also included MorphOS just to be safe
5155 #endif
5156 #ifdef _DEBUG
5157 fullscreen = false;
5158 #endif
5159 //debugwindow = true;
5160 //fullscreen = false;
5161
5162 #if !defined(__ANDROID__)
5163 // n.b., crashes when run on Galaxy Nexus (even though fine in the emulator)
5164 for(int i=0;i<n_args;i++) {
5165 if( strcmp(args[i], "fullscreen") == 0 )
5166 fullscreen = true;
5167 else if( strcmp(args[i], "windowed") == 0 )
5168 fullscreen = false;
5169 else if( strcmp(args[i], "debugwindow") == 0 )
5170 debugwindow = true;
5171 else if( strcmp(args[i], "onemousebutton") == 0 )
5172 game_g->setOneMouseButton(true);
5173 else if( strcmp(args[i], "mobile_ui") == 0 )
5174 game_g->setMobileUI(true);
5175 else if( strcmp(args[i], "server") == 0 )
5176 game_g->setGameMode(GAMEMODE_MULTIPLAYER_SERVER);
5177 else if( strcmp(args[i], "client") == 0 )
5178 game_g->setGameMode(GAMEMODE_MULTIPLAYER_CLIENT);
5179 }
5180 #endif
5181
5182 #ifdef _WIN32
5183 if( debugwindow ) {
5184 AllocConsole();
5185 /*FILE *dummy = NULL;
5186 freopen_s(&dummy, "con", "w", stdout);*/
5187 FILE *dummy = freopen("con", "w", stdout);
5188 SetConsoleTitleA("DEBUG:");
5189 }
5190 #endif
5191
5192 initFolderPaths();
5193 initLogFile();
5194
5195 // set random seed - recommended way to do it from http://stackoverflow.com/questions/322938/recommended-way-to-initialize-srand
5196 unsigned int seed = (unsigned int)time(NULL);
5197 //seed = 72638; // test
5198 LOG("set random seed to %d\n", seed);
5199 srand( seed );
5200
5201 //bool run_tests = true;
5202 bool run_tests = false;
5203
5204 /*if( access("data", 0)==0 ) {
5205 use_amigadata = true;
5206 }
5207 else if( access("Mega Lo Mania", 0)==0 ) {
5208 use_amigadata = false;
5209 }
5210 else {
5211 LOG("can't find data folder\n");
5212 return;
5213 }*/
5214 LOG("onemousebutton?: %d\n", game_g->isOneMouseButton());
5215 LOG("mobile_ui?: %d\n", game_g->isMobileUI());
5216
5217 if( !run_tests ) {
5218 game_g->loadPrefs();
5219 }
5220
5221 // init application
5222 if( !game_g->createApplication() ) {
5223 LOG("failed to init application\n");
5224 }
5225 // init sound
5226 else if( !initSound() ) {
5227 // don't fail, just warn
5228 LOG("Failed to initialise sound system\n");
5229 }
5230
5231 LOG("successfully opened libraries\n");
5232
5233 bool ok = true;
5234 if( !game_g->openScreen(fullscreen) ) {
5235 LOG("failed to open screen\n");
5236 ok = false;
5237 #ifdef _WIN32
5238 MessageBoxA(NULL, "Failed to open screen", "Error", MB_OK|MB_ICONEXCLAMATION);
5239 #endif
5240 }
5241
5242 int time_s = clock();
5243 game_g->drawProgress(0);
5244
5245 if( ok && !game_g->loadImages() ) {
5246 LOG("failed to load images\n");
5247 ok = false;
5248 #ifdef _WIN32
5249 MessageBoxA(NULL, "Failed to load images", "Error", MB_OK|MB_ICONEXCLAMATION);
5250 #endif
5251 }
5252 LOG("time taken to load images: %d\n", clock() - time_s);
5253 // loadImages takes progress up to 80%
5254 if( !ok ) {
5255 LOG("delete game %d\n", game_g);
5256 delete game_g;
5257 game_g = NULL;
5258 return;
5259 }
5260
5261 // n.b., still need to load samples even if sound failed to initialise, as we want the Sample objects for the textual display
5262 if( !game_g->loadSamples() ) {
5263 // don't fail, just warn
5264 LOG("warning - failed to load samples\n");
5265 // no longer show message - no longer an error, as the default install won't have any samples!
5266 /*#ifdef _WIN32
5267 MessageBoxA(NULL, "Failed to load all samples", "Warning", MB_OK|MB_ICONEXCLAMATION);
5268 #endif*/
5269 }
5270 game_g->drawProgress(85);
5271
5272 game_g->setupInventions();
5273 game_g->drawProgress(87);
5274 game_g->setupElements();
5275 game_g->drawProgress(89);
5276 Design::setupDesigns();
5277 game_g->drawProgress(90);
5278 if( !game_g->createMaps() ) {
5279 LOG("failed to create maps\n");
5280 LOG("delete game %d\n", game_g);
5281 delete game_g;
5282 game_g = NULL;
5283 #ifdef _WIN32
5284 MessageBoxA(NULL, "Failed to create maps", "Error", MB_OK|MB_ICONEXCLAMATION);
5285 #endif
5286 return;
5287 }
5288 game_g->drawProgress(95);
5289
5290 for(size_t i=0;i<TrackedObject::getNumTags();i++) {
5291 TrackedObject *to = TrackedObject::getTag(i);
5292 if( to != NULL && strcmp( to->getClass(), "CLASS_IMAGE" ) == 0 ) {
5293 Image *image = (Image *)to;
5294 if( !image->convertToDisplayFormat() ) {
5295 LOG("failed to convertToDisplayFormat\n");
5296 LOG("delete game %d\n", game_g);
5297 delete game_g;
5298 game_g = NULL;
5299 #ifdef _WIN32
5300 MessageBoxA(NULL, "Failed to create texture images", "Error", MB_OK|MB_ICONEXCLAMATION);
5301 #endif
5302 return;
5303 }
5304 }
5305 }
5306
5307 game_g->drawProgress(100);
5308 int time_taken = clock() - time_s;
5309 LOG("time taken to load data: %d\n", time_taken);
5310
5311 char buffer[256] = "";
5312 sprintf(buffer, "Gigalomania, version %d.%d", majorVersion, minorVersion);
5313 game_g->getScreen()->setTitle(buffer);
5314
5315 LOG("all done!\n");
5316
5317 if( run_tests ) {
5318 game_g->runTests();
5319 }
5320 else {
5321 if( !game_g->loadState() ) {
5322 game_g->setCurrentMap();
5323 game_g->setGameStateID(GAMESTATEID_CHOOSEGAMETYPE);
5324 //setGameStateID(GAMESTATEID_CHOOSEPLAYER);
5325 }
5326
5327 game_g->getApplication()->runMainLoop();
5328 }
5329
5330 LOG("delete game %d\n", game_g);
5331 delete game_g;
5332 game_g = NULL;
5333 }
5334
playerAlive(int player) const5335 bool Game::playerAlive(int player) const {
5336 /*int n_player_sectors = 0;
5337 int n_army = 0;
5338 for(int x=0;x<map_width_c;x++) {
5339 for(int y=0;y<map_height_c;y++) {
5340 Sector *sector = map->sectors[x][y];
5341 if( sector != NULL ) {
5342 if( player == sector->getActivePlayer() )
5343 n_player_sectors++;
5344 n_army += sector->getArmy(player)->getTotal();
5345 }
5346 }
5347 }
5348 return ( n_player_sectors > 0 || n_army > 0 );*/
5349 for(int x=0;x<map_width_c;x++) {
5350 for(int y=0;y<map_height_c;y++) {
5351 //Sector *sector = map->sectors[x][y];
5352 Sector *sector = map->getSector(x, y);
5353 if( sector != NULL ) {
5354 if( player == sector->getActivePlayer() )
5355 return true;
5356 else if( sector->getArmy(player)->any(true) )
5357 return true;
5358 }
5359 }
5360 }
5361 return false;
5362 }
5363
5364 #if defined(__ANDROID__)
5365
5366 // JNI for Android
5367
5368 #include <jni.h>
5369 #include <android/log.h>
5370
5371 // see http://wiki.libsdl.org/SDL_AndroidGetActivity
5372
launchUrl(string url)5373 void launchUrl(string url) {
5374 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: launch url: %s", url.c_str());
5375 // retrieve the JNI environment.
5376 JNIEnv* env = (JNIEnv*)SDL_AndroidGetJNIEnv();
5377 if (!env)
5378 {
5379 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: can't find env");
5380 return;
5381 }
5382 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: obtained env");
5383
5384 // retrieve the Java instance of the SDLActivity
5385 jobject activity = (jobject)SDL_AndroidGetActivity();
5386 if (!activity)
5387 {
5388 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: can't find activity");
5389 return;
5390 }
5391 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: obtained activity");
5392
5393 // find the Java class of the activity. It should be SDLActivity or a subclass of it.
5394 jclass clazz( env->GetObjectClass(activity) );
5395 if (!clazz)
5396 {
5397 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: can't find class");
5398 return;
5399 }
5400 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: obtained class");
5401
5402 // find the identifier of the method to call
5403 jmethodID method_id = env->GetMethodID(clazz, "launchUrl", "(Ljava/lang/String;)V");
5404 if (!method_id)
5405 {
5406 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: can't find launchUrl method");
5407 return;
5408 }
5409 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: obtained method");
5410
5411 // effectively call the Java method
5412 jstring str = env->NewStringUTF(url.c_str());
5413 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: about to call static method");
5414 env->CallVoidMethod( activity, method_id, str );
5415 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: called method");
5416
5417 // clean up the local references.
5418 env->DeleteLocalRef(str);
5419 env->DeleteLocalRef(activity);
5420 __android_log_print(ANDROID_LOG_INFO, "Gigalomania", "JNI: done");
5421 }
5422 #endif
5423