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