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