1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; either version 2 of the License, or
5  *  (at your option) any later version.
6  *
7  *  This program is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  *  GNU General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program; if not, write to the Free Software
14  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
15  */
16 
17 #if defined(DEBUG) || defined(USE_DEBUG_KEY)
18 
19 #include "uqmdebug.h"
20 
21 #include "build.h"
22 #include "colors.h"
23 #include "controls.h"
24 #include "clock.h"
25 #include "starmap.h"
26 #include "element.h"
27 #include "sis.h"
28 #include "status.h"
29 #include "gamestr.h"
30 #include "gameev.h"
31 #include "gendef.h"
32 #include "globdata.h"
33 #include "planets/lifeform.h"
34 #include "planets/scan.h"
35 #include "races.h"
36 #include "setup.h"
37 #include "state.h"
38 #include "libs/mathlib.h"
39 
40 #include <stdio.h>
41 #include <errno.h>
42 
43 
44 static void dumpEventCallback (const EVENT *eventPtr, void *arg);
45 
46 static void starRecurse (STAR_DESC *star, void *arg);
47 static void planetRecurse (STAR_DESC *star, SOLARSYS_STATE *system,
48 		PLANET_DESC *planet, void *arg);
49 static void moonRecurse (STAR_DESC *star, SOLARSYS_STATE *system,
50 		PLANET_DESC *planet, PLANET_DESC *moon, void *arg);
51 
52 static void dumpSystemCallback (const STAR_DESC *star,
53 		const SOLARSYS_STATE *system, void *arg);
54 static void dumpPlanetCallback (const PLANET_DESC *planet, void *arg);
55 static void dumpMoonCallback (const PLANET_DESC *moon, void *arg);
56 static void dumpWorld (FILE *out, const PLANET_DESC *world);
57 
58 typedef struct TallyResourcesArg TallyResourcesArg;
59 static void tallySystemPreCallback (const STAR_DESC *star, const
60 		SOLARSYS_STATE *system, void *arg);
61 static void tallySystemPostCallback (const STAR_DESC *star, const
62 		SOLARSYS_STATE *system, void *arg);
63 static void tallyPlanetCallback (const PLANET_DESC *planet, void *arg);
64 static void tallyMoonCallback (const PLANET_DESC *moon, void *arg);
65 static void tallyResourcesWorld (TallyResourcesArg *arg,
66 		const PLANET_DESC *world);
67 
68 static void dumpPlanetTypeCallback (int index, const PlanetFrame *planet,
69 		void *arg);
70 
71 
72 BOOLEAN instantMove = FALSE;
73 BOOLEAN disableInteractivity = FALSE;
74 void (* volatile debugHook) (void) = NULL;
75 
76 
77 // Must be called on the Starcon2Main thread.
78 // This function is called synchronously wrt the game logic thread.
79 void
debugKeyPressedSynchronous(void)80 debugKeyPressedSynchronous (void)
81 {
82 	// State modifying:
83 	equipShip ();
84 	giveDevices ();
85 
86 	// Give the player the ships you can't ally with under normal
87 	// conditions.
88 	clearEscorts ();
89 	AddEscortShips (ARILOU_SHIP, 1);
90 	AddEscortShips (PKUNK_SHIP, 1);
91 	AddEscortShips (VUX_SHIP, 1);
92 	AddEscortShips (YEHAT_SHIP, 1);
93 	AddEscortShips (MELNORME_SHIP, 1);
94 	AddEscortShips (DRUUGE_SHIP, 1);
95 	AddEscortShips (ILWRATH_SHIP, 1);
96 	AddEscortShips (MYCON_SHIP, 1);
97 	AddEscortShips (SLYLANDRO_SHIP, 1);
98 	AddEscortShips (UMGAH_SHIP, 1);
99 	AddEscortShips (URQUAN_SHIP, 1);
100 	AddEscortShips (BLACK_URQUAN_SHIP, 1);
101 
102 	resetCrewBattle ();
103 	resetEnergyBattle ();
104 	instantMove = !instantMove;
105 	showSpheres ();
106 	activateAllShips ();
107 //	forwardToNextEvent (TRUE);
108 //	SET_GAME_STATE (MELNORME_CREDIT1, 100);
109 //	GLOBAL_SIS (ResUnits) = 100000;
110 
111 	// Informational:
112 //	dumpEvents (stderr);
113 
114 	// Graphical and textual:
115 //	debugContexts();
116 }
117 
118 // Can be called on any thread, but usually on main()
119 // This function is called asynchronously wrt the game logic thread,
120 // which means locking applies. Use carefully.
121 // TODO: Once game logic thread is purged of graphics and clock locks,
122 //   this function may not call graphics and game clock functions at all.
123 void
debugKeyPressed(void)124 debugKeyPressed (void)
125 {
126 	// Tests
127 //	Scale_PerfTest ();
128 
129 	// Informational:
130 //	dumpStrings (stdout);
131 //	dumpPlanetTypes(stderr);
132 //	debugHook = dumpUniverseToFile;
133 			// This will cause dumpUniverseToFile to be called from the
134 			// Starcon2Main loop. Calling it from here would give threading
135 			// problems.
136 //	debugHook = tallyResourcesToFile;
137 			// This will cause tallyResourcesToFile to be called from the
138 			// Starcon2Main loop. Calling it from here would give threading
139 			// problems.
140 
141 	// Interactive:
142 //	uio_debugInteractive(stdin, stdout, stderr);
143 }
144 
145 ////////////////////////////////////////////////////////////////////////////
146 
147 // Fast forwards to the next event.
148 // If skipHEE is set, HYPERSPACE_ENCOUNTER_EVENTs are skipped.
149 // Must be called from the Starcon2Main thread.
150 // TODO: LockGameClock may be removed since it is only
151 //   supposed to be called synchronously wrt the game logic thread.
152 void
forwardToNextEvent(BOOLEAN skipHEE)153 forwardToNextEvent (BOOLEAN skipHEE)
154 {
155 	HEVENT hEvent;
156 	EVENT *EventPtr;
157 	COUNT year, month, day;
158 			// time of next event
159 	BOOLEAN done;
160 
161 	if (!GameClockRunning ())
162 		return;
163 
164 	LockGameClock ();
165 
166 	done = !skipHEE;
167 	do {
168 		hEvent = GetHeadEvent ();
169 		if (hEvent == 0)
170 			return;
171 		LockEvent (hEvent, &EventPtr);
172 		if (EventPtr->func_index != HYPERSPACE_ENCOUNTER_EVENT)
173 			done = TRUE;
174 		year = EventPtr->year_index;
175 		month = EventPtr->month_index;
176 		day = EventPtr->day_index;
177 		UnlockEvent (hEvent);
178 
179 		for (;;) {
180 			if (GLOBAL (GameClock.year_index) > year ||
181 					(GLOBAL (GameClock.year_index) == year &&
182 					(GLOBAL (GameClock.month_index) > month ||
183 					(GLOBAL (GameClock.month_index) == month &&
184 					GLOBAL (GameClock.day_index) >= day))))
185 				break;
186 
187 			MoveGameClockDays (1);
188 		}
189 	} while (!done);
190 
191 	UnlockGameClock ();
192 }
193 
194 const char *
eventName(BYTE func_index)195 eventName (BYTE func_index)
196 {
197 	switch (func_index) {
198 	case ARILOU_ENTRANCE_EVENT:
199 		return "ARILOU_ENTRANCE_EVENT";
200 	case ARILOU_EXIT_EVENT:
201 		return "ARILOU_EXIT_EVENT";
202 	case HYPERSPACE_ENCOUNTER_EVENT:
203 		return "HYPERSPACE_ENCOUNTER_EVENT";
204 	case KOHR_AH_VICTORIOUS_EVENT:
205 		return "KOHR_AH_VICTORIOUS_EVENT";
206 	case ADVANCE_PKUNK_MISSION:
207 		return "ADVANCE_PKUNK_MISSION";
208 	case ADVANCE_THRADD_MISSION:
209 		return "ADVANCE_THRADD_MISSION";
210 	case ZOQFOT_DISTRESS_EVENT:
211 		return "ZOQFOT_DISTRESS";
212 	case ZOQFOT_DEATH_EVENT:
213 		return "ZOQFOT_DEATH_EVENT";
214 	case SHOFIXTI_RETURN_EVENT:
215 		return "SHOFIXTI_RETURN_EVENT";
216 	case ADVANCE_UTWIG_SUPOX_MISSION:
217 		return "ADVANCE_UTWIG_SUPOX_MISSION";
218 	case KOHR_AH_GENOCIDE_EVENT:
219 		return "KOHR_AH_GENOCIDE_EVENT";
220 	case SPATHI_SHIELD_EVENT:
221 		return "SPATHI_SHIELD_EVENT";
222 	case ADVANCE_ILWRATH_MISSION:
223 		return "ADVANCE_ILWRATH_MISSION";
224 	case ADVANCE_MYCON_MISSION:
225 		return "ADVANCE_MYCON_MISSION";
226 	case ARILOU_UMGAH_CHECK:
227 		return "ARILOU_UMGAH_CHECK";
228 	case YEHAT_REBEL_EVENT:
229 		return "YEHAT_REBEL_EVENT";
230 	case SLYLANDRO_RAMP_UP:
231 		return "SLYLANDRO_RAMP_UP";
232 	case SLYLANDRO_RAMP_DOWN:
233 		return "SLYLANDRO_RAMP_DOWN";
234 	default:
235 		// Should not happen
236 		return "???";
237 	}
238 }
239 
240 static void
dumpEventCallback(const EVENT * eventPtr,void * arg)241 dumpEventCallback (const EVENT *eventPtr, void *arg)
242 {
243 	FILE *out = (FILE *) arg;
244 	dumpEvent (out, eventPtr);
245 }
246 
247 void
dumpEvent(FILE * out,const EVENT * eventPtr)248 dumpEvent (FILE *out, const EVENT *eventPtr)
249 {
250 	fprintf (out, "%4u/%02u/%02u: %s\n",
251 			eventPtr->year_index,
252 			eventPtr->month_index,
253 			eventPtr->day_index,
254 			eventName (eventPtr->func_index));
255 }
256 
257 void
dumpEvents(FILE * out)258 dumpEvents (FILE *out)
259 {
260 	LockGameClock ();
261 	ForAllEvents (dumpEventCallback, out);
262 	UnlockGameClock ();
263 }
264 
265 ////////////////////////////////////////////////////////////////////////////
266 
267 // NB: Ship maximum speed and turning rate aren't updated in
268 // HyperSpace/QuasiSpace or in melee.
269 void
equipShip(void)270 equipShip (void)
271 {
272 	int i;
273 
274 	// Don't do anything unless in the full game.
275 	if (LOBYTE (GLOBAL (CurrentActivity)) == SUPER_MELEE)
276 		return;
277 
278 	// Thrusters:
279 	for (i = 0; i < NUM_DRIVE_SLOTS; i++)
280 		GLOBAL_SIS (DriveSlots[i]) = FUSION_THRUSTER;
281 
282 	// Turning jets:
283 	for (i = 0; i < NUM_JET_SLOTS; i++)
284 		GLOBAL_SIS (JetSlots[i]) = TURNING_JETS;
285 
286 	// Shields:
287 	SET_GAME_STATE (LANDER_SHIELDS,
288 			(1 << EARTHQUAKE_DISASTER) |
289 			(1 << BIOLOGICAL_DISASTER) |
290 			(1 << LIGHTNING_DISASTER) |
291 			(1 << LAVASPOT_DISASTER));
292 	// Lander upgrades:
293 	SET_GAME_STATE (IMPROVED_LANDER_SPEED, 1);
294 	SET_GAME_STATE (IMPROVED_LANDER_CARGO, 1);
295 	SET_GAME_STATE (IMPROVED_LANDER_SHOT, 1);
296 
297 	// Modules:
298 	if (GET_GAME_STATE (CHMMR_BOMB_STATE) < 2)
299 	{
300 		// The Precursor bomb has not been installed.
301 		// This is the original TFB testing layout.
302 		i = 0;
303 		GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
304 		GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
305 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
306 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
307 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
308 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
309 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
310 		GLOBAL_SIS (ModuleSlots[i++]) = STORAGE_BAY;
311 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
312 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
313 		GLOBAL_SIS (ModuleSlots[i++]) = DYNAMO_UNIT;
314 		GLOBAL_SIS (ModuleSlots[i++]) = TRACKING_SYSTEM;
315 		GLOBAL_SIS (ModuleSlots[i++]) = TRACKING_SYSTEM;
316 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
317 		GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
318 		GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
319 
320 		// Landers:
321 		GLOBAL_SIS (NumLanders) = MAX_LANDERS;
322 	}
323 	else
324 	{
325 		// The Precursor bomb has been installed.
326 		i = NUM_BOMB_MODULES;
327 		GLOBAL_SIS (ModuleSlots[i++]) = HIGHEFF_FUELSYS;
328 		GLOBAL_SIS (ModuleSlots[i++]) = CREW_POD;
329 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
330 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
331 		GLOBAL_SIS (ModuleSlots[i++]) = CANNON_WEAPON;
332 		GLOBAL_SIS (ModuleSlots[i++]) = SHIVA_FURNACE;
333 	}
334 
335 	assert (i <= NUM_MODULE_SLOTS);
336 
337 	// Fill the fuel and crew compartments to the maximum.
338 	GLOBAL_SIS (FuelOnBoard) = FUEL_RESERVE;
339 	GLOBAL_SIS (CrewEnlisted) = 0;
340 	for (i = 0; i < NUM_MODULE_SLOTS; i++)
341 	{
342 		switch (GLOBAL_SIS (ModuleSlots[i])) {
343 			case CREW_POD:
344 				GLOBAL_SIS (CrewEnlisted) += CREW_POD_CAPACITY;
345 				break;
346 			case FUEL_TANK:
347 				GLOBAL_SIS (FuelOnBoard) += FUEL_TANK_CAPACITY;
348 				break;
349 			case HIGHEFF_FUELSYS:
350 				GLOBAL_SIS (FuelOnBoard) += HEFUEL_TANK_CAPACITY;
351 				break;
352 		}
353 	}
354 
355 	// Update the maximum speed and turning rate when in interplanetary.
356 	if (pSolarSysState != NULL)
357 	{
358 		// Thrusters:
359 		pSolarSysState->max_ship_speed = 5 * IP_SHIP_THRUST_INCREMENT;
360 		for (i = 0; i < NUM_DRIVE_SLOTS; i++)
361 			if (GLOBAL_SIS (DriveSlots[i] == FUSION_THRUSTER))
362 				pSolarSysState->max_ship_speed += IP_SHIP_THRUST_INCREMENT;
363 
364 		// Turning jets:
365 		pSolarSysState->turn_wait = IP_SHIP_TURN_WAIT;
366 		for (i = 0; i < NUM_JET_SLOTS; i++)
367 			if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
368 				pSolarSysState->turn_wait -= IP_SHIP_TURN_DECREMENT;
369 	}
370 
371 	// Make sure everything is redrawn:
372 	if (inHQSpace () ||
373 			LOBYTE (GLOBAL (CurrentActivity)) == IN_INTERPLANETARY)
374 	{
375 		DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
376 	}
377 }
378 
379 ////////////////////////////////////////////////////////////////////////////
380 
381 void
giveDevices(void)382 giveDevices (void) {
383 	SET_GAME_STATE (ROSY_SPHERE_ON_SHIP, 1);
384 	SET_GAME_STATE (ARTIFACT_2_ON_SHIP, 1);
385 	SET_GAME_STATE (ARTIFACT_3_ON_SHIP, 1);
386 	SET_GAME_STATE (SUN_DEVICE_ON_SHIP, 1);
387 	SET_GAME_STATE (UTWIG_BOMB_ON_SHIP, 1);
388 	SET_GAME_STATE (ULTRON_CONDITION, 1);
389 	//SET_GAME_STATE (ULTRON_CONDITION, 2);
390 	//SET_GAME_STATE (ULTRON_CONDITION, 3);
391 	//SET_GAME_STATE (ULTRON_CONDITION, 4);
392 	SET_GAME_STATE (MAIDENS_ON_SHIP, 1);
393 	SET_GAME_STATE (TALKING_PET_ON_SHIP, 1);
394 	SET_GAME_STATE (AQUA_HELIX_ON_SHIP, 1);
395 	SET_GAME_STATE (CLEAR_SPINDLE_ON_SHIP, 1);
396 	SET_GAME_STATE (UMGAH_BROADCASTERS_ON_SHIP, 1);
397 	SET_GAME_STATE (TAALO_PROTECTOR_ON_SHIP, 1);
398 	SET_GAME_STATE (EGG_CASE0_ON_SHIP, 1);
399 	SET_GAME_STATE (EGG_CASE1_ON_SHIP, 1);
400 	SET_GAME_STATE (EGG_CASE2_ON_SHIP, 1);
401 	SET_GAME_STATE (SYREEN_SHUTTLE_ON_SHIP, 1);
402 	SET_GAME_STATE (VUX_BEAST_ON_SHIP, 1);
403 	SET_GAME_STATE (PORTAL_SPAWNER_ON_SHIP, 1);
404 	SET_GAME_STATE (PORTAL_KEY_ON_SHIP, 1);
405 	SET_GAME_STATE (BURV_BROADCASTERS_ON_SHIP, 1);
406 	SET_GAME_STATE (MOONBASE_ON_SHIP, 1);
407 
408 	// Not strictly a device (although it originally was one).
409 	SET_GAME_STATE (DESTRUCT_CODE_ON_SHIP, 1);
410 }
411 
412 ////////////////////////////////////////////////////////////////////////////
413 
414 void
clearEscorts(void)415 clearEscorts (void)
416 {
417 	HSHIPFRAG hStarShip, hNextShip;
418 
419 	for (hStarShip = GetHeadLink (&GLOBAL (built_ship_q));
420 			hStarShip; hStarShip = hNextShip)
421 	{
422 		SHIP_FRAGMENT *StarShipPtr;
423 
424 		StarShipPtr = LockShipFrag (&GLOBAL (built_ship_q), hStarShip);
425 		hNextShip = _GetSuccLink (StarShipPtr);
426 		UnlockShipFrag (&GLOBAL (built_ship_q), hStarShip);
427 
428 		RemoveQueue (&GLOBAL (built_ship_q), hStarShip);
429 		FreeShipFrag (&GLOBAL (built_ship_q), hStarShip);
430 	}
431 
432 	DeltaSISGauges (UNDEFINED_DELTA, UNDEFINED_DELTA, UNDEFINED_DELTA);
433 }
434 
435 ////////////////////////////////////////////////////////////////////////////
436 
437 #if 0
438 // Not needed anymore, but could be useful in the future.
439 
440 // Find the HELEMENT belonging to the Flagship.
441 static HELEMENT
442 findFlagshipElement (void)
443 {
444 	HELEMENT hElement, hNextElement;
445 	ELEMENT *ElementPtr;
446 
447 	// Find the ship element.
448 	for (hElement = GetTailElement (); hElement != 0;
449 			hElement = hNextElement)
450 	{
451 		LockElement (hElement, &ElementPtr);
452 
453 		if ((ElementPtr->state_flags & PLAYER_SHIP) != 0)
454 		{
455 			UnlockElement (hElement);
456 			return hElement;
457 		}
458 
459 		hNextElement = GetPredElement (ElementPtr);
460 		UnlockElement (hElement);
461 	}
462 	return 0;
463 }
464 #endif
465 
466 // Move the Flagship to the destination of the autopilot.
467 // Should only be called from HyperSpace/QuasiSpace.
468 // It can be called from debugHook directly after entering HS/QS though.
469 void
doInstantMove(void)470 doInstantMove (void)
471 {
472 	// Move to the new location:
473 	if ((GLOBAL (autopilot)).x == ~0 || (GLOBAL (autopilot)).y == ~0)
474 	{
475 		// If no destination has been selected, use the current location
476 		// as the destination.
477 		(GLOBAL (autopilot)).x = LOGX_TO_UNIVERSE(GLOBAL_SIS (log_x));
478 		(GLOBAL (autopilot)).y = LOGY_TO_UNIVERSE(GLOBAL_SIS (log_y));
479 	}
480 	else
481 	{
482 		// A new destination has been selected.
483 		GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX((GLOBAL (autopilot)).x);
484 		GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY((GLOBAL (autopilot)).y);
485 	}
486 
487 	// Check for a solar systems at the destination.
488 	if (GET_GAME_STATE (ARILOU_SPACE_SIDE) <= 1)
489 	{
490 		// If there's a solar system at the destination, enter it.
491 		CurStarDescPtr = FindStar (0, &(GLOBAL (autopilot)), 0, 0);
492 		if (CurStarDescPtr)
493 		{
494 			// Leave HyperSpace/QuasiSpace if we're there:
495 			SET_GAME_STATE (USED_BROADCASTER, 0);
496 			GLOBAL (CurrentActivity) &= ~IN_BATTLE;
497 
498 			// Enter IP:
499 			GLOBAL (ShipFacing) = 0;
500 			GLOBAL (ip_planet) = 0;
501 			GLOBAL (in_orbit) = 0;
502 					// This causes the ship position in IP to be reset.
503 			GLOBAL (CurrentActivity) |= START_INTERPLANETARY;
504 		}
505 	}
506 
507 	// Turn off the autopilot:
508 	(GLOBAL (autopilot)).x = ~0;
509 	(GLOBAL (autopilot)).y = ~0;
510 }
511 
512 ////////////////////////////////////////////////////////////////////////////
513 
514 void
showSpheres(void)515 showSpheres (void)
516 {
517 	HFLEETINFO hStarShip, hNextShip;
518 
519 	for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
520 			hStarShip != NULL; hStarShip = hNextShip)
521 	{
522 		FLEET_INFO *FleetPtr;
523 
524 		FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
525 		hNextShip = _GetSuccLink (FleetPtr);
526 
527 		if ((FleetPtr->actual_strength != INFINITE_RADIUS) &&
528 				(FleetPtr->known_strength != FleetPtr->actual_strength))
529 		{
530 			FleetPtr->known_strength = FleetPtr->actual_strength;
531 			FleetPtr->known_loc = FleetPtr->loc;
532 		}
533 
534 		UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
535 	}
536 }
537 
538 ////////////////////////////////////////////////////////////////////////////
539 
540 void
activateAllShips(void)541 activateAllShips (void)
542 {
543 	HFLEETINFO hStarShip, hNextShip;
544 
545 	for (hStarShip = GetHeadLink (&GLOBAL (avail_race_q));
546 			hStarShip != NULL; hStarShip = hNextShip)
547 	{
548 		FLEET_INFO *FleetPtr;
549 
550 		FleetPtr = LockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
551 		hNextShip = _GetSuccLink (FleetPtr);
552 
553 		if (FleetPtr->icons != NULL)
554 				// Skip the Ur-Quan probe.
555 		{
556 			FleetPtr->allied_state = GOOD_GUY;
557 		}
558 
559 		UnlockFleetInfo (&GLOBAL (avail_race_q), hStarShip);
560 	}
561 }
562 
563 ////////////////////////////////////////////////////////////////////////////
564 
565 void
forAllStars(void (* callback)(STAR_DESC *,void *),void * arg)566 forAllStars (void (*callback) (STAR_DESC *, void *), void *arg)
567 {
568 	int i;
569 	extern STAR_DESC starmap_array[];
570 
571 	for (i = 0; i < NUM_SOLAR_SYSTEMS; i++)
572 		callback (&starmap_array[i], arg);
573 }
574 
575 void
forAllPlanets(STAR_DESC * star,SOLARSYS_STATE * system,void (* callback)(STAR_DESC *,SOLARSYS_STATE *,PLANET_DESC *,void *),void * arg)576 forAllPlanets (STAR_DESC *star, SOLARSYS_STATE *system, void (*callback) (
577 		STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *, void *), void *arg)
578 {
579 	COUNT i;
580 
581 	assert(CurStarDescPtr == star);
582 	assert(pSolarSysState == system);
583 
584 	for (i = 0; i < system->SunDesc[0].NumPlanets; i++)
585 		callback (star, system, &system->PlanetDesc[i], arg);
586 }
587 
588 void
forAllMoons(STAR_DESC * star,SOLARSYS_STATE * system,PLANET_DESC * planet,void (* callback)(STAR_DESC *,SOLARSYS_STATE *,PLANET_DESC *,PLANET_DESC *,void *),void * arg)589 forAllMoons (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
590 		void (*callback) (STAR_DESC *, SOLARSYS_STATE *, PLANET_DESC *,
591 		PLANET_DESC *, void *), void *arg)
592 {
593 	COUNT i;
594 
595 	assert(pSolarSysState == system);
596 
597 	for (i = 0; i < planet->NumPlanets; i++)
598 		callback (star, system, planet, &system->MoonDesc[i], arg);
599 }
600 
601 ////////////////////////////////////////////////////////////////////////////
602 
603 // Must be called from the Starcon2Main thread.
604 // TODO: LockGameClock may be removed
605 void
UniverseRecurse(UniverseRecurseArg * universeRecurseArg)606 UniverseRecurse (UniverseRecurseArg *universeRecurseArg)
607 {
608 	ACTIVITY savedActivity;
609 
610 	if (universeRecurseArg->systemFuncPre == NULL
611 			&& universeRecurseArg->systemFuncPost == NULL
612 			&& universeRecurseArg->planetFuncPre == NULL
613 			&& universeRecurseArg->planetFuncPost == NULL
614 			&& universeRecurseArg->moonFunc == NULL)
615 		return;
616 
617 	LockGameClock ();
618 	//TFB_DEBUG_HALT = 1;
619 	savedActivity = GLOBAL (CurrentActivity);
620 	disableInteractivity = TRUE;
621 
622 	forAllStars (starRecurse, (void *) universeRecurseArg);
623 
624 	disableInteractivity = FALSE;
625 	GLOBAL (CurrentActivity) = savedActivity;
626 	UnlockGameClock ();
627 }
628 
629 static void
starRecurse(STAR_DESC * star,void * arg)630 starRecurse (STAR_DESC *star, void *arg)
631 {
632 	UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
633 
634 	SOLARSYS_STATE SolarSysState;
635 	SOLARSYS_STATE *oldPSolarSysState = pSolarSysState;
636 	STAR_DESC *oldStarDescPtr = CurStarDescPtr;
637 	CurStarDescPtr = star;
638 
639 	RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (star));
640 
641 	memset (&SolarSysState, 0, sizeof (SolarSysState));
642 	SolarSysState.SunDesc[0].pPrevDesc = 0;
643 	SolarSysState.SunDesc[0].rand_seed = RandomContext_Random (SysGenRNG);
644 	SolarSysState.SunDesc[0].data_index = STAR_TYPE (star->Type);
645 	SolarSysState.SunDesc[0].location.x = 0;
646 	SolarSysState.SunDesc[0].location.y = 0;
647 	//SolarSysState.SunDesc[0].radius = MIN_ZOOM_RADIUS;
648 	SolarSysState.genFuncs = getGenerateFunctions (star->Index);
649 
650 	pSolarSysState = &SolarSysState;
651 	(*SolarSysState.genFuncs->generatePlanets) (&SolarSysState);
652 
653 	if (universeRecurseArg->systemFuncPre != NULL)
654 	{
655 		(*universeRecurseArg->systemFuncPre) (
656 				star, &SolarSysState, universeRecurseArg->arg);
657 	}
658 
659 	if (universeRecurseArg->planetFuncPre != NULL
660 			|| universeRecurseArg->planetFuncPost != NULL
661 			|| universeRecurseArg->moonFunc != NULL)
662 	{
663 		forAllPlanets (star, &SolarSysState, planetRecurse,
664 				(void *) universeRecurseArg);
665 	}
666 
667 	if (universeRecurseArg->systemFuncPost != NULL)
668 	{
669 		(*universeRecurseArg->systemFuncPost) (
670 				star, &SolarSysState, universeRecurseArg->arg);
671 	}
672 
673 	pSolarSysState = oldPSolarSysState;
674 	CurStarDescPtr = oldStarDescPtr;
675 }
676 
677 static void
planetRecurse(STAR_DESC * star,SOLARSYS_STATE * system,PLANET_DESC * planet,void * arg)678 planetRecurse (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
679 		void *arg)
680 {
681 	UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
682 
683 	assert(CurStarDescPtr == star);
684 	assert(pSolarSysState == system);
685 
686 	planet->pPrevDesc = &system->SunDesc[0];
687 
688 	if (universeRecurseArg->planetFuncPre != NULL)
689 	{
690 		system->pOrbitalDesc = planet;
691 		DoPlanetaryAnalysis (&system->SysInfo, planet);
692 				// When GenerateDefaultFunctions is used as genFuncs,
693 				// generateOrbital will also call DoPlanetaryAnalysis,
694 				// but with other GenerateFunctions this is not guaranteed.
695 		(*system->genFuncs->generateOrbital) (system, planet);
696 		(*universeRecurseArg->planetFuncPre) (
697 				planet, universeRecurseArg->arg);
698 	}
699 
700 	if (universeRecurseArg->moonFunc != NULL)
701 	{
702 		RandomContext_SeedRandom (SysGenRNG, planet->rand_seed);
703 
704 		(*system->genFuncs->generateMoons) (system, planet);
705 
706 		forAllMoons (star, system, planet, moonRecurse,
707 				(void *) universeRecurseArg);
708 	}
709 
710 	if (universeRecurseArg->planetFuncPost != NULL)
711 	{
712 		system->pOrbitalDesc = planet;
713 		DoPlanetaryAnalysis (&system->SysInfo, planet);
714 				// When GenerateDefaultFunctions is used as genFuncs,
715 				// generateOrbital will also call DoPlanetaryAnalysis,
716 				// but with other GenerateFunctions this is not guaranteed.
717 		(*system->genFuncs->generateOrbital) (system, planet);
718 		(*universeRecurseArg->planetFuncPost) (
719 				planet, universeRecurseArg->arg);
720 	}
721 }
722 
723 static void
moonRecurse(STAR_DESC * star,SOLARSYS_STATE * system,PLANET_DESC * planet,PLANET_DESC * moon,void * arg)724 moonRecurse (STAR_DESC *star, SOLARSYS_STATE *system, PLANET_DESC *planet,
725 		PLANET_DESC *moon, void *arg)
726 {
727 	UniverseRecurseArg *universeRecurseArg = (UniverseRecurseArg *) arg;
728 
729 	assert(CurStarDescPtr == star);
730 	assert(pSolarSysState == system);
731 
732 	moon->pPrevDesc = planet;
733 
734 	if (universeRecurseArg->moonFunc != NULL)
735 	{
736 		system->pOrbitalDesc = moon;
737 		if (moon->data_index != HIERARCHY_STARBASE && moon->data_index != SA_MATRA)
738 		{
739 			DoPlanetaryAnalysis (&system->SysInfo, moon);
740 				// When GenerateDefaultFunctions is used as genFuncs,
741 				// generateOrbital will also call DoPlanetaryAnalysis,
742 				// but with other GenerateFunctions this is not guaranteed.
743 		}
744 		(*system->genFuncs->generateOrbital) (system, moon);
745 		(*universeRecurseArg->moonFunc) (
746 				moon, universeRecurseArg->arg);
747 	}
748 }
749 
750 ////////////////////////////////////////////////////////////////////////////
751 
752 typedef struct
753 {
754 	FILE *out;
755 } DumpUniverseArg;
756 
757 // Must be called from the Starcon2Main thread.
758 void
dumpUniverse(FILE * out)759 dumpUniverse (FILE *out)
760 {
761 	DumpUniverseArg dumpUniverseArg;
762 	UniverseRecurseArg universeRecurseArg;
763 
764 	dumpUniverseArg.out = out;
765 
766 	universeRecurseArg.systemFuncPre = dumpSystemCallback;
767 	universeRecurseArg.systemFuncPost = NULL;
768 	universeRecurseArg.planetFuncPre = dumpPlanetCallback;
769 	universeRecurseArg.planetFuncPost = NULL;
770 	universeRecurseArg.moonFunc = dumpMoonCallback;
771 	universeRecurseArg.arg = (void *) &dumpUniverseArg;
772 
773 	UniverseRecurse (&universeRecurseArg);
774 }
775 
776 // Must be called from the Starcon2Main thread.
777 void
dumpUniverseToFile(void)778 dumpUniverseToFile (void)
779 {
780 	FILE *out;
781 
782 #	define UNIVERSE_DUMP_FILE "PlanetInfo"
783 	out = fopen(UNIVERSE_DUMP_FILE, "w");
784 	if (out == NULL)
785 	{
786 		fprintf(stderr, "Error: Could not open file '%s' for "
787 				"writing: %s\n", UNIVERSE_DUMP_FILE, strerror(errno));
788 		return;
789 	}
790 
791 	dumpUniverse (out);
792 
793 	fclose(out);
794 
795 	fprintf(stdout, "*** Star dump complete. The game may be in an "
796 			"undefined state.\n");
797 			// Data generation may have changed the game state,
798 			// in particular for special planet generation.
799 }
800 
801 static void
dumpSystemCallback(const STAR_DESC * star,const SOLARSYS_STATE * system,void * arg)802 dumpSystemCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
803 		void *arg)
804 {
805 	FILE *out = ((DumpUniverseArg *) arg)->out;
806 	dumpSystem (out, star, system);
807 }
808 
809 void
dumpSystem(FILE * out,const STAR_DESC * star,const SOLARSYS_STATE * system)810 dumpSystem (FILE *out, const STAR_DESC *star, const SOLARSYS_STATE *system)
811 {
812 	UNICODE name[256];
813 	UNICODE buf[40];
814 
815 	GetClusterName (star, name);
816 	snprintf (buf, sizeof buf, "%s %s",
817 			bodyColorString (STAR_COLOR(star->Type)),
818 			starTypeString (STAR_TYPE(star->Type)));
819 	fprintf (out, "%-22s  (%3d.%1d, %3d.%1d) %-19s  %s\n",
820 			name,
821 			star->star_pt.x / 10, star->star_pt.x % 10,
822 			star->star_pt.y / 10, star->star_pt.y % 10,
823 			buf,
824 			starPresenceString (star->Index));
825 
826 	(void) system;  /* satisfy compiler */
827 }
828 
829 const char *
bodyColorString(BYTE col)830 bodyColorString (BYTE col)
831 {
832 	switch (col) {
833 		case BLUE_BODY:
834 			return "blue";
835 		case GREEN_BODY:
836 			return "green";
837 		case ORANGE_BODY:
838 			return "orange";
839 		case RED_BODY:
840 			return "red";
841 		case WHITE_BODY:
842 			return "white";
843 		case YELLOW_BODY:
844 			return "yellow";
845 		case CYAN_BODY:
846 			return "cyan";
847 		case PURPLE_BODY:
848 			return "purple";
849 		case VIOLET_BODY:
850 			return "violet";
851 		default:
852 			// Should not happen
853 			return "???";
854 	}
855 }
856 
857 const char *
starTypeString(BYTE type)858 starTypeString (BYTE type)
859 {
860 	switch (type) {
861 		case DWARF_STAR:
862 			return "dwarf";
863 		case GIANT_STAR:
864 			return "giant";
865 		case SUPER_GIANT_STAR:
866 			return "super giant";
867 		default:
868 			// Should not happen
869 			return "???";
870 	}
871 }
872 
873 const char *
starPresenceString(BYTE index)874 starPresenceString (BYTE index)
875 {
876 	switch (index) {
877 		case 0:
878 			// nothing
879 			return "";
880 		case SOL_DEFINED:
881 			return "Sol";
882 		case SHOFIXTI_DEFINED:
883 			return "Shofixti male";
884 		case MAIDENS_DEFINED:
885 			return "Shofixti maidens";
886 		case START_COLONY_DEFINED:
887 			return "Starting colony";
888 		case SPATHI_DEFINED:
889 			return "Spathi home";
890 		case ZOQFOT_DEFINED:
891 			return "Zoq-Fot-Pik home";
892 		case MELNORME0_DEFINED:
893 			return "Melnorme trader #0";
894 		case MELNORME1_DEFINED:
895 			return "Melnorme trader #1";
896 		case MELNORME2_DEFINED:
897 			return "Melnorme trader #2";
898 		case MELNORME3_DEFINED:
899 			return "Melnorme trader #3";
900 		case MELNORME4_DEFINED:
901 			return "Melnorme trader #4";
902 		case MELNORME5_DEFINED:
903 			return "Melnorme trader #5";
904 		case MELNORME6_DEFINED:
905 			return "Melnorme trader #6";
906 		case MELNORME7_DEFINED:
907 			return "Melnorme trader #7";
908 		case MELNORME8_DEFINED:
909 			return "Melnorme trader #8";
910 		case TALKING_PET_DEFINED:
911 			return "Talking Pet";
912 		case CHMMR_DEFINED:
913 			return "Chmmr home";
914 		case SYREEN_DEFINED:
915 			return "Syreen home";
916 		case BURVIXESE_DEFINED:
917 			return "Burvixese ruins";
918 		case SLYLANDRO_DEFINED:
919 			return "Slylandro home";
920 		case DRUUGE_DEFINED:
921 			return "Druuge home";
922 		case BOMB_DEFINED:
923 			return "Precursor bomb";
924 		case AQUA_HELIX_DEFINED:
925 			return "Aqua Helix";
926 		case SUN_DEVICE_DEFINED:
927 			return "Sun Device";
928 		case TAALO_PROTECTOR_DEFINED:
929 			return "Taalo Shield";
930 		case SHIP_VAULT_DEFINED:
931 			return "Syreen ship vault";
932 		case URQUAN_WRECK_DEFINED:
933 			return "Ur-Quan ship wreck";
934 		case VUX_BEAST_DEFINED:
935 			return "Zex' beauty";
936 		case SAMATRA_DEFINED:
937 			return "Sa-Matra";
938 		case ZOQ_SCOUT_DEFINED:
939 			return "Zoq-Fot-Pik scout";
940 		case MYCON_DEFINED:
941 			return "Mycon home";
942 		case EGG_CASE0_DEFINED:
943 			return "Mycon egg shell #0";
944 		case EGG_CASE1_DEFINED:
945 			return "Mycon egg shell #1";
946 		case EGG_CASE2_DEFINED:
947 			return "Mycon egg shell #2";
948 		case PKUNK_DEFINED:
949 			return "Pkunk home";
950 		case UTWIG_DEFINED:
951 			return "Utwig home";
952 		case SUPOX_DEFINED:
953 			return "Supox home";
954 		case YEHAT_DEFINED:
955 			return "Yehat home";
956 		case VUX_DEFINED:
957 			return "Vux home";
958 		case ORZ_DEFINED:
959 			return "Orz home";
960 		case THRADD_DEFINED:
961 			return "Thraddash home";
962 		case RAINBOW_DEFINED:
963 			return "Rainbow world";
964 		case ILWRATH_DEFINED:
965 			return "Ilwrath home";
966 		case ANDROSYNTH_DEFINED:
967 			return "Androsynth ruins";
968 		case MYCON_TRAP_DEFINED:
969 			return "Mycon trap";
970 		default:
971 			// Should not happen
972 			return "???";
973 	}
974 }
975 
976 static void
dumpPlanetCallback(const PLANET_DESC * planet,void * arg)977 dumpPlanetCallback (const PLANET_DESC *planet, void *arg)
978 {
979 	FILE *out = ((DumpUniverseArg *) arg)->out;
980 	dumpPlanet (out, planet);
981 }
982 
983 void
dumpPlanet(FILE * out,const PLANET_DESC * planet)984 dumpPlanet (FILE *out, const PLANET_DESC *planet)
985 {
986 	(*pSolarSysState->genFuncs->generateName) (pSolarSysState, planet);
987 	fprintf (out, "- %-37s  %s\n", GLOBAL_SIS (PlanetName),
988 			planetTypeString (planet->data_index & ~PLANET_SHIELDED));
989 	dumpWorld (out, planet);
990 }
991 
992 static void
dumpMoonCallback(const PLANET_DESC * moon,void * arg)993 dumpMoonCallback (const PLANET_DESC *moon, void *arg)
994 {
995 	FILE *out = ((DumpUniverseArg *) arg)->out;
996 	dumpMoon (out, moon);
997 }
998 
999 void
dumpMoon(FILE * out,const PLANET_DESC * moon)1000 dumpMoon (FILE *out, const PLANET_DESC *moon)
1001 {
1002 	const char *typeStr;
1003 
1004 	if (moon->data_index == HIERARCHY_STARBASE)
1005 	{
1006 		typeStr = "StarBase";
1007 	}
1008 	else if (moon->data_index == SA_MATRA)
1009 	{
1010 		typeStr = "Sa-Matra";
1011 	}
1012 	else
1013 	{
1014 		typeStr = planetTypeString (moon->data_index & ~PLANET_SHIELDED);
1015 	}
1016 	fprintf (out, "  - Moon %-30c  %s\n",
1017 			'a' + (moon - &pSolarSysState->MoonDesc[0]), typeStr);
1018 
1019 	dumpWorld (out, moon);
1020 }
1021 
1022 static void
dumpWorld(FILE * out,const PLANET_DESC * world)1023 dumpWorld (FILE *out, const PLANET_DESC *world)
1024 {
1025 	PLANET_INFO *info;
1026 
1027 	if (world->data_index == HIERARCHY_STARBASE) {
1028 		return;
1029 	}
1030 
1031 	if (world->data_index == SA_MATRA) {
1032 		return;
1033 	}
1034 
1035 	info = &pSolarSysState->SysInfo.PlanetInfo;
1036 	fprintf(out, "          AxialTilt:  %d\n", info->AxialTilt);
1037 	fprintf(out, "          Tectonics:  %d\n", info->Tectonics);
1038 	fprintf(out, "          Weather:    %d\n", info->Weather);
1039 	fprintf(out, "          Density:    %d\n", info->PlanetDensity);
1040 	fprintf(out, "          Radius:     %d\n", info->PlanetRadius);
1041 	fprintf(out, "          Gravity:    %d\n", info->SurfaceGravity);
1042 	fprintf(out, "          Temp:       %d\n", info->SurfaceTemperature);
1043 	fprintf(out, "          Day:        %d\n", info->RotationPeriod);
1044 	fprintf(out, "          Atmosphere: %d\n", info->AtmoDensity);
1045 	fprintf(out, "          LifeChance: %d\n", info->LifeChance);
1046 	fprintf(out, "          DistToSun:  %d\n", info->PlanetToSunDist);
1047 
1048 	if (world->data_index & PLANET_SHIELDED) {
1049 		// Slave-shielded planet
1050 		return;
1051 	}
1052 
1053 	fprintf (out, "          Bio: %4d    Min: %4d\n",
1054 			calculateBioValue (pSolarSysState, world),
1055 			calculateMineralValue (pSolarSysState, world));
1056 }
1057 
1058 COUNT
calculateBioValue(const SOLARSYS_STATE * system,const PLANET_DESC * world)1059 calculateBioValue (const SOLARSYS_STATE *system, const PLANET_DESC *world)
1060 {
1061 	COUNT result;
1062 	COUNT numBio;
1063 	COUNT i;
1064 
1065 	assert (system->pOrbitalDesc == world);
1066 
1067 	numBio = callGenerateForScanType (system, world, GENERATE_ALL,
1068 			BIOLOGICAL_SCAN, NULL);
1069 
1070 	result = 0;
1071 	for (i = 0; i < numBio; i++)
1072 	{
1073 		NODE_INFO info;
1074 		callGenerateForScanType (system, world, i, BIOLOGICAL_SCAN, &info);
1075 		result += BIO_CREDIT_VALUE *
1076 				LONIBBLE (CreatureData[info.type].ValueAndHitPoints);
1077 	}
1078 	return result;
1079 }
1080 
1081 void
generateBioIndex(const SOLARSYS_STATE * system,const PLANET_DESC * world,COUNT bio[])1082 generateBioIndex(const SOLARSYS_STATE *system, const PLANET_DESC *world,
1083 		COUNT bio[])
1084 {
1085 	COUNT numBio;
1086 	COUNT i;
1087 
1088 	assert (system->pOrbitalDesc == world);
1089 
1090 	numBio = callGenerateForScanType (system, world, GENERATE_ALL,
1091 			BIOLOGICAL_SCAN, NULL);
1092 
1093 	for (i = 0; i < NUM_CREATURE_TYPES + NUM_SPECIAL_CREATURE_TYPES; i++)
1094 		bio[i] = 0;
1095 
1096 	for (i = 0; i < numBio; i++)
1097 	{
1098 		NODE_INFO info;
1099 		callGenerateForScanType (system, world, i, BIOLOGICAL_SCAN, &info);
1100 		bio[info.type]++;
1101 	}
1102 }
1103 
1104 COUNT
calculateMineralValue(const SOLARSYS_STATE * system,const PLANET_DESC * world)1105 calculateMineralValue (const SOLARSYS_STATE *system, const PLANET_DESC *world)
1106 {
1107 	COUNT result;
1108 	COUNT numDeposits;
1109 	COUNT i;
1110 
1111 	assert (system->pOrbitalDesc == world);
1112 
1113 	numDeposits = callGenerateForScanType (system, world, GENERATE_ALL,
1114 			MINERAL_SCAN, NULL);
1115 
1116 	result = 0;
1117 	for (i = 0; i < numDeposits; i++)
1118 	{
1119 		NODE_INFO info;
1120 		callGenerateForScanType (system, world, i, MINERAL_SCAN, &info);
1121 		result += HIBYTE (info.density) *
1122 				GLOBAL (ElementWorth[ElementCategory (info.type)]);
1123 	}
1124 	return result;
1125 }
1126 
1127 void
generateMineralIndex(const SOLARSYS_STATE * system,const PLANET_DESC * world,COUNT minerals[])1128 generateMineralIndex(const SOLARSYS_STATE *system, const PLANET_DESC *world,
1129 		COUNT minerals[])
1130 {
1131 	COUNT numDeposits;
1132 	COUNT i;
1133 
1134 	assert (system->pOrbitalDesc == world);
1135 
1136 	numDeposits = callGenerateForScanType (system, world, GENERATE_ALL,
1137 			MINERAL_SCAN, NULL);
1138 
1139 	for (i = 0; i < NUM_ELEMENT_CATEGORIES; i++)
1140 		minerals[i] = 0;
1141 
1142 	for (i = 0; i < numDeposits; i++)
1143 	{
1144 		NODE_INFO info;
1145 		callGenerateForScanType (system, world, i, MINERAL_SCAN, &info);
1146 		minerals[ElementCategory (info.type)] += HIBYTE (info.density);
1147 	}
1148 }
1149 
1150 ////////////////////////////////////////////////////////////////////////////
1151 
1152 struct TallyResourcesArg
1153 {
1154 	FILE *out;
1155 	COUNT mineralCount;
1156 	COUNT bioCount;
1157 };
1158 
1159 // Must be called from the Starcon2Main thread.
1160 void
tallyResources(FILE * out)1161 tallyResources (FILE *out)
1162 {
1163 	TallyResourcesArg tallyResourcesArg;
1164 	UniverseRecurseArg universeRecurseArg;
1165 
1166 	tallyResourcesArg.out = out;
1167 
1168 	universeRecurseArg.systemFuncPre = tallySystemPreCallback;
1169 	universeRecurseArg.systemFuncPost = tallySystemPostCallback;
1170 	universeRecurseArg.planetFuncPre = tallyPlanetCallback;
1171 	universeRecurseArg.planetFuncPost = NULL;
1172 	universeRecurseArg.moonFunc = tallyMoonCallback;
1173 	universeRecurseArg.arg = (void *) &tallyResourcesArg;
1174 
1175 	UniverseRecurse (&universeRecurseArg);
1176 }
1177 
1178 // Must be called from the Starcon2Main thread.
1179 void
tallyResourcesToFile(void)1180 tallyResourcesToFile (void)
1181 {
1182 	FILE *out;
1183 
1184 #	define RESOURCE_TALLY_FILE "ResourceTally"
1185 	out = fopen(RESOURCE_TALLY_FILE, "w");
1186 	if (out == NULL)
1187 	{
1188 		fprintf(stderr, "Error: Could not open file '%s' for "
1189 				"writing: %s\n", RESOURCE_TALLY_FILE, strerror(errno));
1190 		return;
1191 	}
1192 
1193 	tallyResources (out);
1194 
1195 	fclose(out);
1196 
1197 	fprintf(stdout, "*** Resource tally complete. The game may be in an "
1198 			"undefined state.\n");
1199 			// Data generation may have changed the game state,
1200 			// in particular for special planet generation.
1201 }
1202 
1203 static void
tallySystemPreCallback(const STAR_DESC * star,const SOLARSYS_STATE * system,void * arg)1204 tallySystemPreCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
1205 		void *arg)
1206 {
1207 	TallyResourcesArg *tallyResourcesArg = (TallyResourcesArg *) arg;
1208 	tallyResourcesArg->mineralCount = 0;
1209 	tallyResourcesArg->bioCount = 0;
1210 
1211 	(void) star;  /* satisfy compiler */
1212 	(void) system;  /* satisfy compiler */
1213 }
1214 
1215 static void
tallySystemPostCallback(const STAR_DESC * star,const SOLARSYS_STATE * system,void * arg)1216 tallySystemPostCallback (const STAR_DESC *star, const SOLARSYS_STATE *system,
1217 		void *arg)
1218 {
1219 	UNICODE name[256];
1220 	TallyResourcesArg *tallyResourcesArg = (TallyResourcesArg *) arg;
1221 	FILE *out = tallyResourcesArg->out;
1222 
1223 	GetClusterName (star, name);
1224 	fprintf (out, "%s\t%d\t%d\n", name, tallyResourcesArg->mineralCount,
1225 			tallyResourcesArg->bioCount);
1226 
1227 	(void) star;  /* satisfy compiler */
1228 	(void) system;  /* satisfy compiler */
1229 }
1230 
1231 static void
tallyPlanetCallback(const PLANET_DESC * planet,void * arg)1232 tallyPlanetCallback (const PLANET_DESC *planet, void *arg)
1233 {
1234 	tallyResourcesWorld ((TallyResourcesArg *) arg, planet);
1235 }
1236 
1237 static void
tallyMoonCallback(const PLANET_DESC * moon,void * arg)1238 tallyMoonCallback (const PLANET_DESC *moon, void *arg)
1239 {
1240 	tallyResourcesWorld ((TallyResourcesArg *) arg, moon);
1241 }
1242 
1243 static void
tallyResourcesWorld(TallyResourcesArg * arg,const PLANET_DESC * world)1244 tallyResourcesWorld (TallyResourcesArg *arg, const PLANET_DESC *world)
1245 {
1246 	if (world->data_index == HIERARCHY_STARBASE) {
1247 		return;
1248 	}
1249 
1250 	if (world->data_index == SA_MATRA) {
1251 		return;
1252 	}
1253 
1254 	arg->bioCount += calculateBioValue (pSolarSysState, world),
1255 	arg->mineralCount += calculateMineralValue (pSolarSysState, world);
1256 }
1257 
1258 ////////////////////////////////////////////////////////////////////////////
1259 
1260 void
forAllPlanetTypes(void (* callback)(int,const PlanetFrame *,void *),void * arg)1261 forAllPlanetTypes (void (*callback) (int, const PlanetFrame *, void *),
1262 		void *arg)
1263 {
1264 	int i;
1265 	extern const PlanetFrame planet_array[];
1266 
1267 	for (i = 0; i < NUMBER_OF_PLANET_TYPES; i++)
1268 		callback (i, &planet_array[i], arg);
1269 }
1270 
1271 typedef struct
1272 {
1273 	FILE *out;
1274 } DumpPlanetTypesArg;
1275 
1276 void
dumpPlanetTypes(FILE * out)1277 dumpPlanetTypes (FILE *out)
1278 {
1279 	DumpPlanetTypesArg dumpPlanetTypesArg;
1280 	dumpPlanetTypesArg.out = out;
1281 
1282 	forAllPlanetTypes (dumpPlanetTypeCallback, (void *) &dumpPlanetTypesArg);
1283 }
1284 
1285 static void
dumpPlanetTypeCallback(int index,const PlanetFrame * planetType,void * arg)1286 dumpPlanetTypeCallback (int index, const PlanetFrame *planetType, void *arg)
1287 {
1288 	DumpPlanetTypesArg *dumpPlanetTypesArg = (DumpPlanetTypesArg *) arg;
1289 
1290 	dumpPlanetType(dumpPlanetTypesArg->out, index, planetType);
1291 }
1292 
1293 void
dumpPlanetType(FILE * out,int index,const PlanetFrame * planetType)1294 dumpPlanetType (FILE *out, int index, const PlanetFrame *planetType)
1295 {
1296 	int i;
1297 	fprintf (out,
1298 			"%s\n"
1299 			"\tType: %s\n"
1300 			"\tColor: %s\n"
1301 			"\tSurface generation algoritm: %s\n"
1302 			"\tTectonics: %s\n"
1303 			"\tAtmosphere: %s\n"
1304 			"\tDensity: %s\n"
1305 			"\tElements:\n",
1306 			planetTypeString (index),
1307 			worldSizeString (PLANSIZE (planetType->Type)),
1308 			bodyColorString (PLANCOLOR (planetType->Type)),
1309 			worldGenAlgoString (PLANALGO (planetType->Type)),
1310 			tectonicsString (planetType->BaseTectonics),
1311 			atmosphereString (HINIBBLE (planetType->AtmoAndDensity)),
1312 			densityString (LONIBBLE (planetType->AtmoAndDensity))
1313 			);
1314 	for (i = 0; i < NUM_USEFUL_ELEMENTS; i++)
1315 	{
1316 		const ELEMENT_ENTRY *entry;
1317 		entry = &planetType->UsefulElements[i];
1318 		if (entry->Density == 0)
1319 			continue;
1320 		fprintf(out, "\t\t0 to %d %s-quality (+%d) deposits of %s (%s)\n",
1321 				DEPOSIT_QUANTITY (entry->Density),
1322 				depositQualityString (DEPOSIT_QUALITY (entry->Density)),
1323 				DEPOSIT_QUALITY (entry->Density) * 5,
1324 				GAME_STRING (ELEMENTS_STRING_BASE + entry->ElementType),
1325 				GAME_STRING (CARGO_STRING_BASE + 2 + ElementCategory (
1326 				entry->ElementType))
1327 			);
1328 	}
1329 	fprintf (out, "\n");
1330 }
1331 
1332 const char *
planetTypeString(int typeIndex)1333 planetTypeString (int typeIndex)
1334 {
1335 	static UNICODE typeStr[40];
1336 
1337 	if (typeIndex >= FIRST_GAS_GIANT)
1338 	{
1339 		// "Gas Giant"
1340 		snprintf(typeStr, sizeof typeStr, "%s",
1341 				GAME_STRING (SCAN_STRING_BASE + 4 + 51));
1342 	}
1343 	else
1344 	{
1345 		// "<type> World" (eg. "Water World")
1346 		snprintf(typeStr, sizeof typeStr, "%s %s",
1347 				GAME_STRING (SCAN_STRING_BASE + 4 + typeIndex),
1348 				GAME_STRING (SCAN_STRING_BASE + 4 + 50));
1349 	}
1350 	return typeStr;
1351 }
1352 
1353 // size is what you get from PLANSIZE (planetFrame.Type)
1354 const char *
worldSizeString(BYTE size)1355 worldSizeString (BYTE size)
1356 {
1357 	switch (size)
1358 	{
1359 		case SMALL_ROCKY_WORLD:
1360 			return "small rocky world";
1361 		case LARGE_ROCKY_WORLD:
1362 			return "large rocky world";
1363 		case GAS_GIANT:
1364 			return "gas giant";
1365 		default:
1366 			// Should not happen
1367 			return "???";
1368 	}
1369 }
1370 
1371 // algo is what you get from PLANALGO (planetFrame.Type)
1372 const char *
worldGenAlgoString(BYTE algo)1373 worldGenAlgoString (BYTE algo)
1374 {
1375 	switch (algo)
1376 	{
1377 		case TOPO_ALGO:
1378 			return "TOPO_ALGO";
1379 		case CRATERED_ALGO:
1380 			return "CRATERED_ALGO";
1381 		case GAS_GIANT_ALGO:
1382 			return "GAS_GIANT_ALGO";
1383 		default:
1384 			// Should not happen
1385 			return "???";
1386 	}
1387 }
1388 
1389 // tectonics is what you get from planetFrame.BaseTechtonics
1390 // not reentrant
1391 const char *
tectonicsString(BYTE tectonics)1392 tectonicsString (BYTE tectonics)
1393 {
1394 	static char buf[sizeof "-127"];
1395 	switch (tectonics)
1396 	{
1397 		case NO_TECTONICS:
1398 			return "none";
1399 		case LOW_TECTONICS:
1400 			return "low";
1401 		case MED_TECTONICS:
1402 			return "medium";
1403 		case HIGH_TECTONICS:
1404 			return "high";
1405 		case SUPER_TECTONICS:
1406 			return "super";
1407 		default:
1408 			snprintf (buf, sizeof buf, "%d", tectonics);
1409 			return buf;
1410 	}
1411 }
1412 
1413 // atmosphere is what you get from HINIBBLE (planetFrame.AtmoAndDensity)
1414 const char *
atmosphereString(BYTE atmosphere)1415 atmosphereString (BYTE atmosphere)
1416 {
1417 	switch (atmosphere)
1418 	{
1419 		case LIGHT:
1420 			return "thin";
1421 		case MEDIUM:
1422 			return "normal";
1423 		case HEAVY:
1424 			return "thick";
1425 		default:
1426 			return "super thick";
1427 	}
1428 }
1429 
1430 // density is what you get from LONIBBLE (planetFrame.AtmoAndDensity)
1431 const char *
densityString(BYTE density)1432 densityString (BYTE density)
1433 {
1434 	switch (density)
1435 	{
1436 		case GAS_DENSITY:
1437 			return "gaseous";
1438 		case LIGHT_DENSITY:
1439 			return "light";
1440 		case LOW_DENSITY:
1441 			return "low";
1442 		case NORMAL_DENSITY:
1443 			return "normal";
1444 		case HIGH_DENSITY:
1445 			return "high";
1446 		case SUPER_DENSITY:
1447 			return "super high";
1448 		default:
1449 			// Should not happen
1450 			return "???";
1451 	}
1452 }
1453 
1454 // quality is what you get from DEPOSIT_QUALITY (elementEntry.Density)
1455 const char *
depositQualityString(BYTE quality)1456 depositQualityString (BYTE quality)
1457 {
1458 	switch (quality)
1459 	{
1460 		case LIGHT:
1461 			return "low";
1462 		case MEDIUM:
1463 			return "medium";
1464 		case HEAVY:
1465 			return "high";
1466 		default:
1467 			// Should not happen
1468 			return "???";
1469 	}
1470 }
1471 
1472 ////////////////////////////////////////////////////////////////////////////
1473 
1474 // playerNr should be 0 or 1
1475 STARSHIP*
findPlayerShip(SIZE playerNr)1476 findPlayerShip (SIZE playerNr)
1477 {
1478 	HELEMENT hElement, hNextElement;
1479 
1480 	for (hElement = GetHeadElement (); hElement; hElement = hNextElement)
1481 	{
1482 		ELEMENT *ElementPtr;
1483 
1484 		LockElement (hElement, &ElementPtr);
1485 		hNextElement = GetSuccElement (ElementPtr);
1486 
1487 		if ((ElementPtr->state_flags & PLAYER_SHIP)	&&
1488 				ElementPtr->playerNr == playerNr)
1489 		{
1490 			STARSHIP *StarShipPtr;
1491 			GetElementStarShip (ElementPtr, &StarShipPtr);
1492 			UnlockElement (hElement);
1493 			return StarShipPtr;
1494 		}
1495 
1496 		UnlockElement (hElement);
1497 	}
1498 	return NULL;
1499 }
1500 
1501 ////////////////////////////////////////////////////////////////////////////
1502 
1503 void
resetCrewBattle(void)1504 resetCrewBattle (void)
1505 {
1506 	STARSHIP *StarShipPtr;
1507 	COUNT delta;
1508 	CONTEXT OldContext;
1509 
1510 	if (!(GLOBAL (CurrentActivity) & IN_BATTLE) ||
1511 			(inHQSpace ()))
1512 		return;
1513 
1514 	StarShipPtr = findPlayerShip (RPG_PLAYER_NUM);
1515 	if (StarShipPtr == NULL || StarShipPtr->RaceDescPtr == NULL)
1516 		return;
1517 
1518 	delta = StarShipPtr->RaceDescPtr->ship_info.max_crew -
1519 			StarShipPtr->RaceDescPtr->ship_info.crew_level;
1520 
1521 	OldContext = SetContext (StatusContext);
1522 	DeltaCrew (StarShipPtr->hShip, delta);
1523 	SetContext (OldContext);
1524 }
1525 
1526 void
resetEnergyBattle(void)1527 resetEnergyBattle (void)
1528 {
1529 	STARSHIP *StarShipPtr;
1530 	COUNT delta;
1531 	CONTEXT OldContext;
1532 
1533 	if (!(GLOBAL (CurrentActivity) & IN_BATTLE) ||
1534 			(inHQSpace ()))
1535 		return;
1536 
1537 	StarShipPtr = findPlayerShip (RPG_PLAYER_NUM);
1538 	if (StarShipPtr == NULL || StarShipPtr->RaceDescPtr == NULL)
1539 		return;
1540 
1541 	delta = StarShipPtr->RaceDescPtr->ship_info.max_energy -
1542 			StarShipPtr->RaceDescPtr->ship_info.energy_level;
1543 
1544 	OldContext = SetContext (StatusContext);
1545 	DeltaEnergy (StarShipPtr->hShip, delta);
1546 	SetContext (OldContext);
1547 }
1548 
1549 ////////////////////////////////////////////////////////////////////////////
1550 
1551 // This function should help in making sure that gamestr.h matches
1552 // gamestrings.txt.
1553 void
dumpStrings(FILE * out)1554 dumpStrings (FILE *out)
1555 {
1556 #define STRINGIZE(a) #a
1557 #define MAKE_STRING_CATEGORY(prefix) \
1558 		{ \
1559 			/* .name  = */ STRINGIZE(prefix ## _BASE), \
1560 			/* .base  = */ prefix ## _BASE, \
1561 			/* .count = */ prefix ## _COUNT \
1562 		}
1563 	struct {
1564 		const char *name;
1565 		size_t base;
1566 		size_t count;
1567 	} categories[] = {
1568 		{ "0", 0, 0 },
1569 		MAKE_STRING_CATEGORY(STAR_STRING),
1570 		MAKE_STRING_CATEGORY(DEVICE_STRING),
1571 		MAKE_STRING_CATEGORY(CARGO_STRING),
1572 		MAKE_STRING_CATEGORY(ELEMENTS_STRING),
1573 		MAKE_STRING_CATEGORY(SCAN_STRING),
1574 		MAKE_STRING_CATEGORY(STAR_NUMBER),
1575 		MAKE_STRING_CATEGORY(PLANET_NUMBER),
1576 		MAKE_STRING_CATEGORY(MONTHS_STRING),
1577 		MAKE_STRING_CATEGORY(FEEDBACK_STRING),
1578 		MAKE_STRING_CATEGORY(STARBASE_STRING),
1579 		MAKE_STRING_CATEGORY(ENCOUNTER_STRING),
1580 		MAKE_STRING_CATEGORY(NAVIGATION_STRING),
1581 		MAKE_STRING_CATEGORY(NAMING_STRING),
1582 		MAKE_STRING_CATEGORY(MELEE_STRING),
1583 		MAKE_STRING_CATEGORY(SAVEGAME_STRING),
1584 		MAKE_STRING_CATEGORY(OPTION_STRING),
1585 		MAKE_STRING_CATEGORY(QUITMENU_STRING),
1586 		MAKE_STRING_CATEGORY(STATUS_STRING),
1587 		MAKE_STRING_CATEGORY(FLAGSHIP_STRING),
1588 		MAKE_STRING_CATEGORY(ORBITSCAN_STRING),
1589 		MAKE_STRING_CATEGORY(MAINMENU_STRING),
1590 		MAKE_STRING_CATEGORY(NETMELEE_STRING),
1591 		{ "GAMESTR_COUNT", GAMESTR_COUNT, (size_t) -1 }
1592 	};
1593 	size_t numCategories = sizeof categories / sizeof categories[0];
1594 	size_t numStrings = GetStringTableCount (GameStrings);
1595 	size_t stringI;
1596 	size_t categoryI = 0;
1597 
1598 	// Start with a sanity check to see if gamestr.h has been changed but
1599 	// not this file.
1600 	for (categoryI = 0; categoryI < numCategories - 1; categoryI++) {
1601 		if (categories[categoryI].base + categories[categoryI].count !=
1602 				categories[categoryI + 1].base) {
1603 			fprintf(stderr, "Error: String category list in dumpStrings() is "
1604 					"not up to date.\n");
1605 			return;
1606 		}
1607 	}
1608 
1609 	if (GAMESTR_COUNT != numStrings) {
1610 		fprintf(stderr, "Warning: GAMESTR_COUNT is %d, but GameStrings "
1611 				"contains %d strings.\n", GAMESTR_COUNT, numStrings);
1612 	}
1613 
1614 	categoryI = 0;
1615 	for (stringI = 0; stringI < numStrings; stringI++) {
1616 		while (categoryI < numCategories &&
1617 				stringI >= categories[categoryI + 1].base)
1618 			categoryI++;
1619 		fprintf(out, "[ %s + %d ]  %s\n", categories[categoryI].name,
1620 				stringI - categories[categoryI].base, GAME_STRING(stringI));
1621 	}
1622 }
1623 
1624 ////////////////////////////////////////////////////////////////////////////
1625 
1626 
1627 static Color
hsvaToRgba(double hue,double sat,double val,BYTE alpha)1628 hsvaToRgba (double hue, double sat, double val, BYTE alpha)
1629 {
1630 	unsigned int hi = (int) (hue / 60.0);
1631 	double f = (hue / 60.0) - ((int) (hue / 60.0));
1632 	double p = val * (1.0 - sat);
1633 	double q = val * (1.0 - f * sat);
1634 	double t = val * (1.0 - (1.0 - f * sat));
1635 
1636 	// Convert p, q, t, and v from [0..1] to [0..255]
1637 	BYTE pb = (BYTE) (p * 255.0 + 0.5);
1638 	BYTE qb = (BYTE) (q * 255.0 + 0.5);
1639 	BYTE tb = (BYTE) (t * 255.0 + 0.5);
1640 	BYTE vb = (BYTE) (val * 255.0 + 0.5);
1641 
1642 	assert (hue >= 0.0 && hue < 360.0);
1643 	assert (sat >= 0 && sat <= 1.0);
1644 	assert (val >= 0 && val <= 1.0);
1645 	/*fprintf(stderr, "hsva = (%.1f, %.2f, %.2f, %.2d)\n",
1646 			hue, sat, val, alpha);*/
1647 
1648 	assert (hi < 6);
1649 	switch (hi) {
1650 		case 0: return BUILD_COLOR_RGBA (vb, tb, pb, alpha);
1651 		case 1: return BUILD_COLOR_RGBA (qb, vb, pb, alpha);
1652 		case 2: return BUILD_COLOR_RGBA (pb, vb, tb, alpha);
1653 		case 3: return BUILD_COLOR_RGBA (pb, qb, vb, alpha);
1654 		case 4: return BUILD_COLOR_RGBA (tb, pb, vb, alpha);
1655 		case 5: return BUILD_COLOR_RGBA (vb, pb, qb, alpha);
1656 	}
1657 
1658 	// Should not happen.
1659 	return BUILD_COLOR_RGBA (0, 0, 0, alpha);
1660 }
1661 
1662 // Returns true iff this context has a visible FRAME.
1663 static bool
isContextVisible(CONTEXT context)1664 isContextVisible (CONTEXT context)
1665 {
1666 	FRAME contextFrame;
1667 
1668 	// Save the original context.
1669 	CONTEXT oldContext = SetContext (context);
1670 
1671 	// Get the frame of the specified context.
1672 	contextFrame = GetContextFGFrame ();
1673 
1674 	// Restore the original context.
1675 	SetContext (oldContext);
1676 
1677 	return contextFrame == Screen;
1678 }
1679 
1680 static size_t
countVisibleContexts(void)1681 countVisibleContexts (void)
1682 {
1683 	size_t contextCount;
1684 	CONTEXT context;
1685 
1686 	contextCount = 0;
1687 	for (context = GetFirstContext (); context != NULL;
1688 			context = GetNextContext (context))
1689 	{
1690 		if (!isContextVisible (context))
1691 			continue;
1692 
1693 		contextCount++;
1694 	}
1695 
1696 	return contextCount;
1697 }
1698 
1699 static void
drawContext(CONTEXT context,double hue)1700 drawContext (CONTEXT context, double hue /* no pun intended */)
1701 {
1702 	FRAME drawFrame;
1703 	CONTEXT oldContext;
1704 	FONT oldFont;
1705 	DrawMode oldMode;
1706 	Color oldFgCol;
1707 	Color rectCol;
1708 	Color lineCol;
1709 	Color textCol;
1710 	bool haveClippingRect;
1711 	RECT rect;
1712 	LINE line;
1713 	TEXT text;
1714 	POINT p1, p2, p3, p4;
1715 
1716 	drawFrame = GetContextFGFrame ();
1717 	rectCol = hsvaToRgba (hue, 1.0, 0.5, 100);
1718 	lineCol = hsvaToRgba (hue, 1.0, 1.0, 90);
1719 	textCol = lineCol;
1720 
1721 	// Save the original context.
1722 	oldContext = SetContext (context);
1723 
1724 	// Get the clipping rectangle of the specified context.
1725 	haveClippingRect = GetContextClipRect (&rect);
1726 
1727 	// Switch back the old context; we're going to draw in it.
1728 	(void) SetContext (oldContext);
1729 
1730 	p1 = rect.corner;
1731 	p2.x = rect.corner.x + rect.extent.width - 1;
1732 	p2.y = rect.corner.y;
1733 	p3.x = rect.corner.x;
1734 	p3.y = rect.corner.y + rect.extent.height - 1;
1735 	p4.x = rect.corner.x + rect.extent.width - 1;
1736 	p4.y = rect.corner.y + rect.extent.height - 1;
1737 
1738 	oldFgCol = SetContextForeGroundColor (rectCol);
1739 	DrawFilledRectangle (&rect);
1740 
1741 	SetContextForeGroundColor (lineCol);
1742 	line.first = p1; line.second = p2; DrawLine (&line);
1743 	line.first = p2; line.second = p4; DrawLine (&line);
1744 	line.first = p1; line.second = p3; DrawLine (&line);
1745 	line.first = p3; line.second = p4; DrawLine (&line);
1746 	line.first = p1; line.second = p4; DrawLine (&line);
1747 	line.first = p2; line.second = p3; DrawLine (&line);
1748 	// Gimme C'99! So I can do:
1749 	//     DrawLine ((LINE) { .first = p1, .second = p2 })
1750 
1751 	oldFont = SetContextFont (TinyFont);
1752 	SetContextForeGroundColor (textCol);
1753 	// Text prim does not yet support alpha via Color.a
1754 	oldMode = SetContextDrawMode (MAKE_DRAW_MODE (DRAW_ALPHA, textCol.a));
1755 	text.baseline.x = (p1.x + (p2.x + 1)) / 2;
1756 	text.baseline.y = p1.y + 8;
1757 	text.pStr = GetContextName (context);
1758 	text.align = ALIGN_CENTER;
1759 	text.CharCount = (COUNT) ~0;
1760 	font_DrawText (&text);
1761 	(void) SetContextDrawMode (oldMode);
1762 
1763 	(void) SetContextForeGroundColor (oldFgCol);
1764 	(void) SetContextFont (oldFont);
1765 }
1766 
1767 static void
describeContext(FILE * out,const CONTEXT context)1768 describeContext (FILE *out, const CONTEXT context) {
1769 	RECT rect;
1770 	CONTEXT oldContext = SetContext (context);
1771 
1772 	GetContextClipRect (&rect);
1773 	fprintf(out, "Context '%s':\n"
1774 			"\tClipRect = (%d, %d)-(%d, %d)  (%d x %d)\n",
1775 		   GetContextName (context),
1776 		   rect.corner.x, rect.corner.y,
1777 		   rect.corner.x + rect.extent.width,
1778 		   rect.corner.y + rect.extent.height,
1779 		   rect.extent.width, rect.extent.height);
1780 
1781 	SetContext (oldContext);
1782 }
1783 
1784 
1785 typedef struct wait_state
1786 {
1787 	// standard state required by DoInput
1788 	BOOLEAN (*InputFunc) (struct wait_state *self);
1789 } WAIT_STATE;
1790 
1791 
1792 // Maybe move to elsewhere, where it can be reused?
1793 static BOOLEAN
waitForKey(struct wait_state * self)1794 waitForKey (struct wait_state *self) {
1795 	if (PulsedInputState.menu[KEY_MENU_SELECT] ||
1796 			PulsedInputState.menu[KEY_MENU_CANCEL])
1797 		return FALSE;
1798 
1799 	SleepThread (ONE_SECOND / 20);
1800 
1801 	(void) self;
1802 	return TRUE;
1803 }
1804 
1805 // Maybe move to elsewhere, where it can be reused?
1806 static FRAME
getScreen(void)1807 getScreen (void)
1808 {
1809 	CONTEXT oldContext = SetContext (ScreenContext);
1810 	FRAME savedFrame;
1811 	RECT screenRect;
1812 
1813 	screenRect.corner.x = 0;
1814 	screenRect.corner.y = 0;
1815 	screenRect.extent.width = ScreenWidth;
1816 	screenRect.extent.height = ScreenHeight;
1817 	savedFrame = CaptureDrawable (LoadDisplayPixmap (&screenRect, (FRAME) 0));
1818 
1819 	(void) SetContext (oldContext);
1820 	return savedFrame;
1821 }
1822 
1823 static void
putScreen(FRAME savedFrame)1824 putScreen (FRAME savedFrame) {
1825 	STAMP stamp;
1826 
1827 	CONTEXT oldContext = SetContext (ScreenContext);
1828 
1829 	stamp.origin.x = 0;
1830 	stamp.origin.y = 0;
1831 	stamp.frame = savedFrame;
1832 	DrawStamp (&stamp);
1833 
1834 	(void) SetContext (oldContext);
1835 }
1836 
1837 // Show the contexts on the screen.
1838 // Must be called from the main thread.
1839 void
debugContexts(void)1840 debugContexts (void)
1841 {
1842 	static volatile bool inDebugContexts = false;
1843 			// Prevent this function from being called from within itself.
1844 
1845 	CONTEXT orgContext;
1846 	CONTEXT debugDrawContext;
1847 			// We're going to use this context to draw in.
1848 	FRAME debugDrawFrame;
1849 	double hueIncrement;
1850 	size_t visibleContextI;
1851 	CONTEXT context;
1852 	size_t contextCount;
1853 	FRAME savedScreen;
1854 
1855 	// Prevent this function from being called from within itself.
1856 	if (inDebugContexts)
1857 		return;
1858 	inDebugContexts = true;
1859 
1860 	contextCount = countVisibleContexts ();
1861 	if (contextCount == 0)
1862 	{
1863 		goto out;
1864 	}
1865 
1866 	savedScreen = getScreen ();
1867 	FlushGraphics ();
1868 			// Make sure that the screen has actually been captured,
1869 			// before we use the frame.
1870 
1871 	// Create a new frame to draw on.
1872 	debugDrawContext = CreateContext ("debugDrawContext");
1873 	// New work frame is a copy of the original.
1874 	debugDrawFrame = CaptureDrawable (CloneFrame (savedScreen));
1875 	orgContext = SetContext (debugDrawContext);
1876 	SetContextFGFrame (debugDrawFrame);
1877 
1878 	hueIncrement = 360.0 / contextCount;
1879 
1880 	visibleContextI = 0;
1881 	for (context = GetFirstContext (); context != NULL;
1882 			context = GetNextContext (context))
1883 	{
1884 		if (context == debugDrawContext) {
1885 			// Skip our own context.
1886 			continue;
1887 		}
1888 
1889 		if (isContextVisible (context))
1890 		{
1891 			// Only draw the visible contexts.
1892 			drawContext (context, visibleContextI * hueIncrement);
1893 			visibleContextI++;
1894 		}
1895 
1896 		describeContext (stderr, context);
1897 	}
1898 
1899 	// Blit the final debugging frame to the screen.
1900 	putScreen (debugDrawFrame);
1901 
1902 	// Wait for a key:
1903 	{
1904 		WAIT_STATE state;
1905 		state.InputFunc = waitForKey;
1906 		DoInput(&state, TRUE);
1907 	}
1908 
1909 	SetContext (orgContext);
1910 
1911 	// Destroy the debugging frame and context.
1912 	DestroyContext (debugDrawContext);
1913 			// This does nothing with the drawable set with
1914 			// SetContextFGFrame().
1915 	DestroyDrawable (ReleaseDrawable (debugDrawFrame));
1916 
1917 	putScreen (savedScreen);
1918 
1919 	DestroyDrawable (ReleaseDrawable (savedScreen));
1920 
1921 out:
1922 	inDebugContexts = false;
1923 }
1924 
1925 #endif  /* DEBUG */
1926 
1927