1 //Copyright Paul Reiche, Fred Ford. 1992-2002
2
3 /*
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17 */
18
19 #include "solarsys.h"
20 #include "lander.h"
21 #include "../colors.h"
22 #include "../controls.h"
23 #include "../menustat.h"
24 // for DrawMenuStateStrings()
25 #include "../starmap.h"
26 #include "../races.h"
27 #include "../gamestr.h"
28 #include "../gendef.h"
29 #include "../globdata.h"
30 #include "../sis.h"
31 #include "../init.h"
32 #include "../shipcont.h"
33 #include "../gameopt.h"
34 #include "../nameref.h"
35 #include "../resinst.h"
36 #include "../settings.h"
37 #include "../ipdisp.h"
38 #include "../grpinfo.h"
39 #include "../process.h"
40 #include "../setup.h"
41 #include "../sounds.h"
42 #include "../state.h"
43 #include "../uqmdebug.h"
44 #include "../save.h"
45 #include "options.h"
46 #include "libs/graphics/gfx_common.h"
47 #include "libs/mathlib.h"
48 #include "libs/log.h"
49 #include "libs/misc.h"
50
51
52 //#define DEBUG_SOLARSYS
53 //#define SMOOTH_SYSTEM_ZOOM 1
54
55 #define IP_FRAME_RATE (ONE_SECOND / 30)
56
57 static BOOLEAN DoIpFlight (SOLARSYS_STATE *pSS);
58 static void DrawSystem (SIZE radius, BOOLEAN IsInnerSystem);
59 static FRAME CreateStarBackGround (void);
60 static void DrawInnerSystem (void);
61 static void DrawOuterSystem (void);
62 static void ValidateOrbits (void);
63
64 // SolarSysMenu() items
65 enum SolarSysMenuMenuItems
66 {
67 // XXX: Must match the enum in menustat.h
68 STARMAP = 1,
69 EQUIP_DEVICE,
70 CARGO,
71 ROSTER,
72 GAME_MENU,
73 NAVIGATION,
74 };
75
76
77 SOLARSYS_STATE *pSolarSysState;
78 FRAME SISIPFrame;
79 FRAME SunFrame;
80 FRAME OrbitalFrame;
81 FRAME SpaceJunkFrame;
82 COLORMAP OrbitalCMap;
83 COLORMAP SunCMap;
84 MUSIC_REF SpaceMusic;
85
86 SIZE EncounterRace;
87 BYTE EncounterGroup;
88 // last encountered group info
89
90 static FRAME StarsFrame;
91 // prepared star-field graphic
92 static FRAME SolarSysFrame;
93 // saved solar system view graphic
94
95 static RECT scaleRect;
96 // system zooms in when the flagship enters this rect
97
98 RandomContext *SysGenRNG;
99
100 #define DISPLAY_TO_LOC (DISPLAY_FACTOR >> 1)
101
102 POINT
locationToDisplay(POINT pt,SIZE scaleRadius)103 locationToDisplay (POINT pt, SIZE scaleRadius)
104 {
105 POINT out;
106
107 out.x = (SIS_SCREEN_WIDTH >> 1)
108 + (long)pt.x * DISPLAY_TO_LOC / scaleRadius;
109 out.y = (SIS_SCREEN_HEIGHT >> 1)
110 + (long)pt.y * DISPLAY_TO_LOC / scaleRadius;
111
112 return out;
113 }
114
115 POINT
displayToLocation(POINT pt,SIZE scaleRadius)116 displayToLocation (POINT pt, SIZE scaleRadius)
117 {
118 POINT out;
119
120 out.x = ((long)pt.x - (SIS_SCREEN_WIDTH >> 1))
121 * scaleRadius / DISPLAY_TO_LOC;
122 out.y = ((long)pt.y - (SIS_SCREEN_HEIGHT >> 1))
123 * scaleRadius / DISPLAY_TO_LOC;
124
125 return out;
126 }
127
128 POINT
planetOuterLocation(COUNT planetI)129 planetOuterLocation (COUNT planetI)
130 {
131 SIZE scaleRadius = pSolarSysState->SunDesc[0].radius;
132 return displayToLocation (pSolarSysState->PlanetDesc[planetI].image.origin,
133 scaleRadius);
134 }
135
136 bool
worldIsPlanet(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world)137 worldIsPlanet (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
138 {
139 return world->pPrevDesc == solarSys->SunDesc;
140 }
141
142 bool
worldIsMoon(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world)143 worldIsMoon (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
144 {
145 return world->pPrevDesc != solarSys->SunDesc;
146 }
147
148 // Returns the planet index of the world. If the world is a moon, then
149 // this is the index of the planet it is orbiting.
150 COUNT
planetIndex(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world)151 planetIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world)
152 {
153 const PLANET_DESC *planet = worldIsPlanet (solarSys, world) ?
154 world : world->pPrevDesc;
155 return planet - solarSys->PlanetDesc;
156 }
157
158 COUNT
moonIndex(const SOLARSYS_STATE * solarSys,const PLANET_DESC * moon)159 moonIndex (const SOLARSYS_STATE *solarSys, const PLANET_DESC *moon)
160 {
161 assert (!worldIsPlanet (solarSys, moon));
162 return moon - solarSys->MoonDesc;
163 }
164
165 // Test whether 'world' is the planetI-th planet, and if moonI is not
166 // set to MATCH_PLANET, also whether 'world' is the moonI-th moon.
167 bool
matchWorld(const SOLARSYS_STATE * solarSys,const PLANET_DESC * world,BYTE planetI,BYTE moonI)168 matchWorld (const SOLARSYS_STATE *solarSys, const PLANET_DESC *world,
169 BYTE planetI, BYTE moonI)
170 {
171 // Check whether we have the right planet.
172 if (planetIndex (solarSys, world) != planetI)
173 return false;
174
175 if (moonI == MATCH_PLANET)
176 {
177 // Only test whether we are at the planet.
178 if (!worldIsPlanet (solarSys, world))
179 return false;
180 }
181 else
182 {
183 // Test whether the moon matches too
184 if (!worldIsMoon (solarSys, world))
185 return false;
186
187 if (moonIndex (solarSys, world) != moonI)
188 return false;
189 }
190
191 return true;
192 }
193
194 bool
playerInSolarSystem(void)195 playerInSolarSystem (void)
196 {
197 return pSolarSysState != NULL;
198 }
199
200 bool
playerInPlanetOrbit(void)201 playerInPlanetOrbit (void)
202 {
203 return playerInSolarSystem () && pSolarSysState->InOrbit;
204 }
205
206 bool
playerInInnerSystem(void)207 playerInInnerSystem (void)
208 {
209 assert (playerInSolarSystem ());
210 assert (pSolarSysState->pBaseDesc == pSolarSysState->PlanetDesc
211 || pSolarSysState->pBaseDesc == pSolarSysState->MoonDesc);
212 return pSolarSysState->pBaseDesc != pSolarSysState->PlanetDesc;
213 }
214
215 // Sets the SysGenRNG to the required state first.
216 static void
GenerateMoons(SOLARSYS_STATE * system,PLANET_DESC * planet)217 GenerateMoons (SOLARSYS_STATE *system, PLANET_DESC *planet)
218 {
219 COUNT i;
220 COUNT facing;
221 PLANET_DESC *pMoonDesc;
222
223 RandomContext_SeedRandom (SysGenRNG, planet->rand_seed);
224
225 (*system->genFuncs->generateName) (system, planet);
226 (*system->genFuncs->generateMoons) (system, planet);
227
228 facing = NORMALIZE_FACING (ANGLE_TO_FACING (
229 ARCTAN (planet->location.x, planet->location.y)));
230 for (i = 0, pMoonDesc = &system->MoonDesc[0];
231 i < MAX_MOONS; ++i, ++pMoonDesc)
232 {
233 pMoonDesc->pPrevDesc = planet;
234 if (i >= planet->NumPlanets)
235 continue;
236
237 pMoonDesc->temp_color = planet->temp_color;
238 }
239 }
240
241 void
FreeIPData(void)242 FreeIPData (void)
243 {
244 DestroyDrawable (ReleaseDrawable (SISIPFrame));
245 SISIPFrame = 0;
246 DestroyDrawable (ReleaseDrawable (SunFrame));
247 SunFrame = 0;
248 DestroyColorMap (ReleaseColorMap (SunCMap));
249 SunCMap = 0;
250 DestroyColorMap (ReleaseColorMap (OrbitalCMap));
251 OrbitalCMap = 0;
252 DestroyDrawable (ReleaseDrawable (OrbitalFrame));
253 OrbitalFrame = 0;
254 DestroyDrawable (ReleaseDrawable (SpaceJunkFrame));
255 SpaceJunkFrame = 0;
256 DestroyMusic (SpaceMusic);
257 SpaceMusic = 0;
258
259 RandomContext_Delete (SysGenRNG);
260 SysGenRNG = NULL;
261 }
262
263 void
LoadIPData(void)264 LoadIPData (void)
265 {
266 if (SpaceJunkFrame == 0)
267 {
268 SpaceJunkFrame = CaptureDrawable (
269 LoadGraphic (IPBKGND_MASK_PMAP_ANIM));
270 SISIPFrame = CaptureDrawable (LoadGraphic (SISIP_MASK_PMAP_ANIM));
271
272 OrbitalCMap = CaptureColorMap (LoadColorMap (ORBPLAN_COLOR_MAP));
273 OrbitalFrame = CaptureDrawable (
274 LoadGraphic (ORBPLAN_MASK_PMAP_ANIM));
275 SunCMap = CaptureColorMap (LoadColorMap (IPSUN_COLOR_MAP));
276 SunFrame = CaptureDrawable (LoadGraphic (SUN_MASK_PMAP_ANIM));
277
278 SpaceMusic = LoadMusic (IP_MUSIC);
279 }
280
281 if (!SysGenRNG)
282 {
283 SysGenRNG = RandomContext_New ();
284 }
285 }
286
287
288 static void
sortPlanetPositions(void)289 sortPlanetPositions (void)
290 {
291 COUNT i;
292 SIZE sort_array[MAX_PLANETS + 1];
293
294 // When this part is done, sort_array will contain the indices to
295 // all planets, sorted on their y position.
296 // The sun itself, which has its data located at
297 // pSolarSysState->PlanetDesc[-1], is included in this array.
298 // Very ugly stuff, but it's correct.
299
300 // Initialise sort_array.
301 for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
302 sort_array[i] = i - 1;
303
304 // Sort sort_array, based on the positions of the planets/sun.
305 for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i)
306 {
307 COUNT j;
308
309 for (j = pSolarSysState->SunDesc[0].NumPlanets; j > i; --j)
310 {
311 SIZE real_i, real_j;
312
313 real_i = sort_array[i];
314 real_j = sort_array[j];
315 if (pSolarSysState->PlanetDesc[real_i].image.origin.y >
316 pSolarSysState->PlanetDesc[real_j].image.origin.y)
317 {
318 SIZE temp;
319
320 temp = sort_array[i];
321 sort_array[i] = sort_array[j];
322 sort_array[j] = temp;
323 }
324 }
325 }
326
327 // Put the results of the sorting in the solar system structure.
328 pSolarSysState->FirstPlanetIndex = sort_array[0];
329 pSolarSysState->LastPlanetIndex =
330 sort_array[pSolarSysState->SunDesc[0].NumPlanets];
331 for (i = 0; i <= pSolarSysState->SunDesc[0].NumPlanets; ++i) {
332 PLANET_DESC *planet = &pSolarSysState->PlanetDesc[sort_array[i]];
333 planet->NextIndex = sort_array[i + 1];
334 }
335 }
336
337 static void
initSolarSysSISCharacteristics(void)338 initSolarSysSISCharacteristics (void)
339 {
340 BYTE i;
341 BYTE num_thrusters;
342
343 num_thrusters = 0;
344 for (i = 0; i < NUM_DRIVE_SLOTS; ++i)
345 {
346 if (GLOBAL_SIS (DriveSlots[i]) == FUSION_THRUSTER)
347 ++num_thrusters;
348 }
349 pSolarSysState->max_ship_speed = (BYTE)(
350 (num_thrusters + 5) * IP_SHIP_THRUST_INCREMENT);
351
352 pSolarSysState->turn_wait = IP_SHIP_TURN_WAIT;
353 for (i = 0; i < NUM_JET_SLOTS; ++i)
354 {
355 if (GLOBAL_SIS (JetSlots[i]) == TURNING_JETS)
356 pSolarSysState->turn_wait -= IP_SHIP_TURN_DECREMENT;
357 }
358 }
359
360 DWORD
GetRandomSeedForStar(const STAR_DESC * star)361 GetRandomSeedForStar (const STAR_DESC *star)
362 {
363 return MAKE_DWORD (star->star_pt.x, star->star_pt.y);
364 }
365
366 // Returns an orbital PLANET_DESC when player is in orbit
367 static PLANET_DESC *
LoadSolarSys(void)368 LoadSolarSys (void)
369 {
370 COUNT i;
371 PLANET_DESC *orbital = NULL;
372 PLANET_DESC *pCurDesc;
373 #define NUM_TEMP_RANGES 5
374 static const Color temp_color_array[NUM_TEMP_RANGES] =
375 {
376 BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x00, 0x0E), 0x54),
377 BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x06, 0x08), 0x62),
378 BUILD_COLOR (MAKE_RGB15_INIT (0x00, 0x0B, 0x00), 0x6D),
379 BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x00, 0x00), 0x2D),
380 BUILD_COLOR (MAKE_RGB15_INIT (0x0F, 0x08, 0x00), 0x75),
381 };
382
383 RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));
384
385 SunFrame = SetAbsFrameIndex (SunFrame, STAR_TYPE (CurStarDescPtr->Type));
386
387 pCurDesc = &pSolarSysState->SunDesc[0];
388 pCurDesc->pPrevDesc = 0;
389 pCurDesc->rand_seed = RandomContext_Random (SysGenRNG);
390
391 pCurDesc->data_index = STAR_TYPE (CurStarDescPtr->Type);
392 pCurDesc->location.x = 0;
393 pCurDesc->location.y = 0;
394 pCurDesc->image.origin = pCurDesc->location;
395 pCurDesc->image.frame = SunFrame;
396
397 (*pSolarSysState->genFuncs->generatePlanets) (pSolarSysState);
398 if (GET_GAME_STATE (PLANETARY_CHANGE))
399 {
400 PutPlanetInfo ();
401 SET_GAME_STATE (PLANETARY_CHANGE, 0);
402 }
403
404 for (i = 0, pCurDesc = pSolarSysState->PlanetDesc;
405 i < MAX_PLANETS; ++i, ++pCurDesc)
406 {
407 pCurDesc->pPrevDesc = &pSolarSysState->SunDesc[0];
408 pCurDesc->image.origin = pCurDesc->location;
409 if (i >= pSolarSysState->SunDesc[0].NumPlanets)
410 {
411 pCurDesc->image.frame = 0;
412 }
413 else
414 {
415 COUNT index;
416 SYSTEM_INFO SysInfo;
417
418 DoPlanetaryAnalysis (&SysInfo, pCurDesc);
419 index = (SysInfo.PlanetInfo.SurfaceTemperature + 250) / 100;
420 if (index >= NUM_TEMP_RANGES)
421 index = NUM_TEMP_RANGES - 1;
422 pCurDesc->temp_color = temp_color_array[index];
423 }
424 }
425
426 sortPlanetPositions ();
427
428 if (!GLOBAL (ip_planet))
429 { // Outer system
430 pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
431 pSolarSysState->pOrbitalDesc = NULL;
432 }
433 else
434 { // Inner system
435 pSolarSysState->SunDesc[0].location = GLOBAL (ip_location);
436 GLOBAL (ip_location) = displayToLocation (
437 GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);
438
439 i = GLOBAL (ip_planet) - 1;
440 pSolarSysState->pOrbitalDesc = &pSolarSysState->PlanetDesc[i];
441 GenerateMoons (pSolarSysState, pSolarSysState->pOrbitalDesc);
442 pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;
443
444 SET_GAME_STATE (PLANETARY_LANDING, 0);
445 }
446
447 initSolarSysSISCharacteristics ();
448
449 if (GLOBAL (in_orbit))
450 { // Only when loading a game into orbital
451 i = GLOBAL (in_orbit) - 1;
452 if (i == 0)
453 { // Orbiting the planet itself
454 orbital = pSolarSysState->pBaseDesc->pPrevDesc;
455 }
456 else
457 { // Orbiting a moon
458 // -1 because planet itself is 1, and moons have to be 1-based
459 i -= 1;
460 orbital = &pSolarSysState->MoonDesc[i];
461 }
462 GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
463 GLOBAL (in_orbit) = 0;
464 }
465 else
466 {
467 i = GLOBAL (ShipFacing);
468 // XXX: Solar system reentry test depends on ShipFacing != 0
469 if (i == 0)
470 ++i;
471
472 GLOBAL (ShipStamp.frame) = SetAbsFrameIndex (SISIPFrame, i - 1);
473 }
474
475 return orbital;
476 }
477
478 static void
saveNonOrbitalLocation(void)479 saveNonOrbitalLocation (void)
480 {
481 // XXX: Solar system reentry test depends on ShipFacing != 0
482 GLOBAL (ShipFacing) = GetFrameIndex (GLOBAL (ShipStamp.frame)) + 1;
483 GLOBAL (in_orbit) = 0;
484 if (!playerInInnerSystem ())
485 {
486 GLOBAL (ip_planet) = 0;
487 }
488 else
489 {
490 // ip_planet is 1-based because code tests for ip_planet!=0
491 GLOBAL (ip_planet) = 1 + planetIndex (pSolarSysState,
492 pSolarSysState->pOrbitalDesc);
493 GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
494 }
495 }
496
497 static void
FreeSolarSys(void)498 FreeSolarSys (void)
499 {
500 if (pSolarSysState->InIpFlight)
501 {
502 pSolarSysState->InIpFlight = FALSE;
503
504 if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
505 saveNonOrbitalLocation ();
506 }
507
508 DestroyDrawable (ReleaseDrawable (SolarSysFrame));
509 SolarSysFrame = NULL;
510
511 StopMusic ();
512
513 // FreeIPData ();
514 }
515
516 static FRAME
getCollisionFrame(PLANET_DESC * planet,COUNT WaitPlanet)517 getCollisionFrame (PLANET_DESC *planet, COUNT WaitPlanet)
518 {
519 if (pSolarSysState->WaitIntersect != (COUNT)~0
520 && pSolarSysState->WaitIntersect != WaitPlanet)
521 { // New collisions are with a single point (center of planet)
522 return DecFrameIndex (stars_in_space);
523 }
524 else
525 { // Existing collisions are cleared only once the ship does not
526 // intersect anymore with a full planet image
527 return planet->image.frame;
528 }
529 }
530
531 // Returns the planet with which the flagship is colliding
532 static PLANET_DESC *
CheckIntersect(void)533 CheckIntersect (void)
534 {
535 COUNT i;
536 PLANET_DESC *pCurDesc;
537 INTERSECT_CONTROL ShipIntersect, PlanetIntersect;
538 COUNT NewWaitPlanet;
539 BYTE PlanetOffset, MoonOffset;
540
541 // Check collisions with the system center object
542 // This may be the planet in inner view, or the sun
543 pCurDesc = pSolarSysState->pBaseDesc->pPrevDesc;
544 PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc + 1;
545 MoonOffset = 1; // the planet itself
546
547 ShipIntersect.IntersectStamp.origin = GLOBAL (ShipStamp.origin);
548 ShipIntersect.EndPoint = ShipIntersect.IntersectStamp.origin;
549 ShipIntersect.IntersectStamp.frame = GLOBAL (ShipStamp.frame);
550
551 PlanetIntersect.IntersectStamp.origin.x = SIS_SCREEN_WIDTH >> 1;
552 PlanetIntersect.IntersectStamp.origin.y = SIS_SCREEN_HEIGHT >> 1;
553 PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;
554
555 PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
556 MAKE_WORD (PlanetOffset, MoonOffset));
557
558 // Start with no collisions
559 NewWaitPlanet = 0;
560
561 if (pCurDesc != pSolarSysState->SunDesc /* can't intersect with sun */
562 && DrawablesIntersect (&ShipIntersect,
563 &PlanetIntersect, MAX_TIME_VALUE))
564 {
565 #ifdef DEBUG_SOLARSYS
566 log_add (log_Debug, "0: Planet %d, Moon %d", PlanetOffset,
567 MoonOffset);
568 #endif /* DEBUG_SOLARSYS */
569 NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
570 if (pSolarSysState->WaitIntersect != (COUNT)~0
571 && pSolarSysState->WaitIntersect != NewWaitPlanet)
572 {
573 pSolarSysState->WaitIntersect = NewWaitPlanet;
574 #ifdef DEBUG_SOLARSYS
575 log_add (log_Debug, "Star index = %d, Planet index = %d, <%d, %d>",
576 CurStarDescPtr - star_array,
577 pCurDesc - pSolarSysState->PlanetDesc,
578 pSolarSysState->SunDesc[0].location.x,
579 pSolarSysState->SunDesc[0].location.y);
580 #endif /* DEBUG_SOLARSYS */
581 return pCurDesc;
582 }
583 }
584
585 for (i = pCurDesc->NumPlanets,
586 pCurDesc = pSolarSysState->pBaseDesc; i; --i, ++pCurDesc)
587 {
588 PlanetIntersect.IntersectStamp.origin = pCurDesc->image.origin;
589 PlanetIntersect.EndPoint = PlanetIntersect.IntersectStamp.origin;
590 if (playerInInnerSystem ())
591 {
592 PlanetOffset = pCurDesc->pPrevDesc -
593 pSolarSysState->PlanetDesc;
594 MoonOffset = pCurDesc - pSolarSysState->MoonDesc + 2;
595 }
596 else
597 {
598 PlanetOffset = pCurDesc - pSolarSysState->PlanetDesc;
599 MoonOffset = 0;
600 }
601 ++PlanetOffset;
602 PlanetIntersect.IntersectStamp.frame = getCollisionFrame (pCurDesc,
603 MAKE_WORD (PlanetOffset, MoonOffset));
604
605 if (DrawablesIntersect (&ShipIntersect,
606 &PlanetIntersect, MAX_TIME_VALUE))
607 {
608 #ifdef DEBUG_SOLARSYS
609 log_add (log_Debug, "1: Planet %d, Moon %d", PlanetOffset,
610 MoonOffset);
611 #endif /* DEBUG_SOLARSYS */
612 NewWaitPlanet = MAKE_WORD (PlanetOffset, MoonOffset);
613
614 if (pSolarSysState->WaitIntersect == (COUNT)~0)
615 { // All collisions disallowed, but the ship is still colliding
616 // with something. Collisions will remain disabled.
617 break;
618 }
619 else if (pSolarSysState->WaitIntersect == NewWaitPlanet)
620 { // Existing and continued collision -- ignore
621 continue;
622 }
623
624 // Collision with a new planet/moon. This may cause a transition
625 // to an inner system or start an orbital view.
626 pSolarSysState->WaitIntersect = NewWaitPlanet;
627 return pCurDesc;
628 }
629 }
630
631 // This records the planet/moon with which the ship just collided
632 // It may be a previously existing collision also (the value won't change)
633 // If all collisions were disabled, this will reenable then once the ship
634 // stops colliding with any planets
635 if (pSolarSysState->WaitIntersect != (COUNT)~0 || NewWaitPlanet == 0)
636 pSolarSysState->WaitIntersect = NewWaitPlanet;
637
638 return NULL;
639 }
640
641 static void
GetOrbitRect(RECT * pRect,COORD dx,COORD dy,SIZE radius,int xnumer,int ynumer,int denom)642 GetOrbitRect (RECT *pRect, COORD dx, COORD dy, SIZE radius,
643 int xnumer, int ynumer, int denom)
644 {
645 pRect->corner.x = (SIS_SCREEN_WIDTH >> 1) + (long)-dx * xnumer / denom;
646 pRect->corner.y = (SIS_SCREEN_HEIGHT >> 1) + (long)-dy * ynumer / denom;
647 pRect->extent.width = (long)radius * (xnumer << 1) / denom;
648 pRect->extent.height = pRect->extent.width >> 1;
649 }
650
651 static void
GetPlanetOrbitRect(RECT * r,PLANET_DESC * planet,int sizeNumer,int dyNumer,int denom)652 GetPlanetOrbitRect (RECT *r, PLANET_DESC *planet, int sizeNumer,
653 int dyNumer, int denom)
654 {
655 COORD dx, dy;
656
657 dx = planet->radius;
658 dy = planet->radius;
659 if (sizeNumer > DISPLAY_FACTOR)
660 {
661 dx = dx + planet->location.x;
662 dy = (dy + planet->location.y) << 1;
663 }
664 GetOrbitRect (r, dx, dy, planet->radius, sizeNumer, dyNumer, denom);
665 }
666
667 static void
ValidateOrbit(PLANET_DESC * planet,int sizeNumer,int dyNumer,int denom)668 ValidateOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
669 {
670 COUNT index;
671
672 if (sizeNumer <= DISPLAY_FACTOR)
673 { // All planets in outer view, and moons in inner
674 RECT r;
675
676 GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);
677
678 // Calculate the location of the planet's image
679 r.corner.x += (r.extent.width >> 1);
680 r.corner.y += (r.extent.height >> 1);
681 r.corner.x += (long)planet->location.x * sizeNumer / denom;
682 // Ellipse function always has coefficients a^2 = 2 * b^2
683 r.corner.y += (long)planet->location.y * (sizeNumer / 2) / denom;
684
685 planet->image.origin = r.corner;
686 }
687
688 // Calculate the size and lighting angle of planet's image and
689 // set the image that will be drawn
690 index = planet->data_index & ~WORLD_TYPE_SPECIAL;
691 if (index < NUMBER_OF_PLANET_TYPES)
692 { // The world is a normal planetary body (planet or moon)
693 BYTE Type;
694 COUNT Size;
695 COUNT angle;
696
697 Type = PlanData[index].Type;
698 Size = PLANSIZE (Type);
699 if (sizeNumer > DISPLAY_FACTOR)
700 {
701 Size += 3;
702 }
703 else if (worldIsMoon (pSolarSysState, planet))
704 {
705 Size += 2;
706 }
707 else if (denom <= (MAX_ZOOM_RADIUS >> 2))
708 {
709 ++Size;
710 if (denom == MIN_ZOOM_RADIUS)
711 ++Size;
712 }
713
714 if (worldIsPlanet (pSolarSysState, planet))
715 { // Planet
716 angle = ARCTAN (planet->location.x, planet->location.y);
717 }
718 else
719 { // Moon
720 angle = ARCTAN (planet->pPrevDesc->location.x,
721 planet->pPrevDesc->location.y);
722 }
723 planet->image.frame = SetAbsFrameIndex (OrbitalFrame,
724 (Size << FACING_SHIFT) + NORMALIZE_FACING (
725 ANGLE_TO_FACING (angle)));
726 }
727 else if (planet->data_index == HIERARCHY_STARBASE)
728 {
729 planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 16);
730 }
731 else if (planet->data_index == SA_MATRA)
732 {
733 planet->image.frame = SetAbsFrameIndex (SpaceJunkFrame, 19);
734 }
735 }
736
737 static void
DrawOrbit(PLANET_DESC * planet,int sizeNumer,int dyNumer,int denom)738 DrawOrbit (PLANET_DESC *planet, int sizeNumer, int dyNumer, int denom)
739 {
740 RECT r;
741
742 GetPlanetOrbitRect (&r, planet, sizeNumer, dyNumer, denom);
743
744 SetContextForeGroundColor (planet->temp_color);
745 DrawOval (&r, 1);
746 }
747
748 static SIZE
FindRadius(POINT shipLoc,SIZE fromRadius)749 FindRadius (POINT shipLoc, SIZE fromRadius)
750 {
751 SIZE nextRadius;
752 POINT displayLoc;
753
754 do
755 {
756 fromRadius >>= 1;
757 if (fromRadius > MIN_ZOOM_RADIUS)
758 nextRadius = fromRadius >> 1;
759 else
760 nextRadius = 0; // scaleRect will be nul
761
762 GetOrbitRect (&scaleRect, nextRadius, nextRadius, nextRadius,
763 DISPLAY_FACTOR, DISPLAY_FACTOR >> 2, fromRadius);
764 displayLoc = locationToDisplay (shipLoc, fromRadius);
765
766 } while (pointWithinRect (scaleRect, displayLoc));
767
768 return fromRadius;
769 }
770
771 static UWORD
flagship_inertial_thrust(COUNT CurrentAngle)772 flagship_inertial_thrust (COUNT CurrentAngle)
773 {
774 BYTE max_speed;
775 SIZE cur_delta_x, cur_delta_y;
776 COUNT TravelAngle;
777 VELOCITY_DESC *VelocityPtr;
778
779 max_speed = pSolarSysState->max_ship_speed;
780 VelocityPtr = &GLOBAL (velocity);
781 GetCurrentVelocityComponents (VelocityPtr, &cur_delta_x, &cur_delta_y);
782 TravelAngle = GetVelocityTravelAngle (VelocityPtr);
783 if (TravelAngle == CurrentAngle
784 && cur_delta_x == COSINE (CurrentAngle, max_speed)
785 && cur_delta_y == SINE (CurrentAngle, max_speed))
786 return (SHIP_AT_MAX_SPEED);
787 else
788 {
789 SIZE delta_x, delta_y;
790 DWORD desired_speed;
791
792 delta_x = cur_delta_x
793 + COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
794 delta_y = cur_delta_y
795 + SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT);
796 desired_speed = (DWORD) ((long) delta_x * delta_x)
797 + (DWORD) ((long) delta_y * delta_y);
798 if (desired_speed <= (DWORD) ((UWORD) max_speed * max_speed))
799 SetVelocityComponents (VelocityPtr, delta_x, delta_y);
800 else if (TravelAngle == CurrentAngle)
801 {
802 SetVelocityComponents (VelocityPtr,
803 COSINE (CurrentAngle, max_speed),
804 SINE (CurrentAngle, max_speed));
805 return (SHIP_AT_MAX_SPEED);
806 }
807 else
808 {
809 VELOCITY_DESC v;
810
811 v = *VelocityPtr;
812
813 DeltaVelocityComponents (&v,
814 COSINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
815 - COSINE (TravelAngle, IP_SHIP_THRUST_INCREMENT),
816 SINE (CurrentAngle, IP_SHIP_THRUST_INCREMENT >> 1)
817 - SINE (TravelAngle, IP_SHIP_THRUST_INCREMENT));
818 GetCurrentVelocityComponents (&v, &cur_delta_x, &cur_delta_y);
819 desired_speed =
820 (DWORD) ((long) cur_delta_x * cur_delta_x)
821 + (DWORD) ((long) cur_delta_y * cur_delta_y);
822 if (desired_speed > (DWORD) ((UWORD) max_speed * max_speed))
823 {
824 SetVelocityComponents (VelocityPtr,
825 COSINE (CurrentAngle, max_speed),
826 SINE (CurrentAngle, max_speed));
827 return (SHIP_AT_MAX_SPEED);
828 }
829
830 *VelocityPtr = v;
831 }
832
833 return 0;
834 }
835 }
836
837 static void
ProcessShipControls(void)838 ProcessShipControls (void)
839 {
840 COUNT index;
841 SIZE delta_x, delta_y;
842
843 if (CurrentInputState.key[PlayerControls[0]][KEY_UP])
844 delta_y = -1;
845 else
846 delta_y = 0;
847
848 delta_x = 0;
849 if (CurrentInputState.key[PlayerControls[0]][KEY_LEFT])
850 delta_x -= 1;
851 if (CurrentInputState.key[PlayerControls[0]][KEY_RIGHT])
852 delta_x += 1;
853
854 if (delta_x || delta_y < 0)
855 {
856 GLOBAL (autopilot.x) = ~0;
857 GLOBAL (autopilot.y) = ~0;
858 }
859 else if (GLOBAL (autopilot.x) != ~0 && GLOBAL (autopilot.y) != ~0)
860 delta_y = -1;
861 else
862 delta_y = 0;
863
864 index = GetFrameIndex (GLOBAL (ShipStamp.frame));
865 if (pSolarSysState->turn_counter)
866 --pSolarSysState->turn_counter;
867 else if (delta_x)
868 {
869 if (delta_x < 0)
870 index = NORMALIZE_FACING (index - 1);
871 else
872 index = NORMALIZE_FACING (index + 1);
873
874 GLOBAL (ShipStamp.frame) =
875 SetAbsFrameIndex (GLOBAL (ShipStamp.frame), index);
876
877 pSolarSysState->turn_counter = pSolarSysState->turn_wait;
878 }
879 if (pSolarSysState->thrust_counter)
880 --pSolarSysState->thrust_counter;
881 else if (delta_y < 0)
882 {
883 #define THRUST_WAIT 1
884 flagship_inertial_thrust (FACING_TO_ANGLE (index));
885
886 pSolarSysState->thrust_counter = THRUST_WAIT;
887 }
888 }
889
890 static void
enterInnerSystem(PLANET_DESC * planet)891 enterInnerSystem (PLANET_DESC *planet)
892 {
893 #define INNER_ENTRY_DISTANCE (MIN_MOON_RADIUS + ((MAX_MOONS - 1) \
894 * MOON_DELTA) + (MOON_DELTA / 4))
895 COUNT angle;
896
897 // Calculate the inner system entry location and facing
898 angle = FACING_TO_ANGLE (GetFrameIndex (GLOBAL (ShipStamp.frame)))
899 + HALF_CIRCLE;
900 GLOBAL (ShipStamp.origin.x) = (SIS_SCREEN_WIDTH >> 1)
901 + COSINE (angle, INNER_ENTRY_DISTANCE);
902 GLOBAL (ShipStamp.origin.y) = (SIS_SCREEN_HEIGHT >> 1)
903 + SINE (angle, INNER_ENTRY_DISTANCE);
904 if (GLOBAL (ShipStamp.origin.y) < 0)
905 GLOBAL (ShipStamp.origin.y) = 1;
906 else if (GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
907 GLOBAL (ShipStamp.origin.y) =
908 (SIS_SCREEN_HEIGHT - 1) - 1;
909
910 GLOBAL (ip_location) = displayToLocation (
911 GLOBAL (ShipStamp.origin), MAX_ZOOM_RADIUS);
912
913 pSolarSysState->SunDesc[0].location =
914 planetOuterLocation (planetIndex (pSolarSysState, planet));
915 ZeroVelocityComponents (&GLOBAL (velocity));
916
917 GenerateMoons (pSolarSysState, planet);
918 pSolarSysState->pBaseDesc = pSolarSysState->MoonDesc;
919 pSolarSysState->pOrbitalDesc = planet;
920 }
921
922 static void
leaveInnerSystem(PLANET_DESC * planet)923 leaveInnerSystem (PLANET_DESC *planet)
924 {
925 COUNT outerPlanetWait;
926
927 pSolarSysState->pBaseDesc = pSolarSysState->PlanetDesc;
928 pSolarSysState->pOrbitalDesc = NULL;
929
930 outerPlanetWait = MAKE_WORD (planet - pSolarSysState->PlanetDesc + 1, 0);
931 GLOBAL (ip_location) = pSolarSysState->SunDesc[0].location;
932 XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
933 ZeroVelocityComponents (&GLOBAL (velocity));
934
935 // Now the ship is in outer system (as per game logic)
936
937 pSolarSysState->WaitIntersect = outerPlanetWait;
938 // See if we also intersect with another planet, and if we do,
939 // disable collisions comletely until we stop intersecting
940 // with any planet at all.
941 CheckIntersect ();
942 if (pSolarSysState->WaitIntersect != outerPlanetWait)
943 pSolarSysState->WaitIntersect = (COUNT)~0;
944 }
945
946 static void
enterOrbital(PLANET_DESC * planet)947 enterOrbital (PLANET_DESC *planet)
948 {
949 ZeroVelocityComponents (&GLOBAL (velocity));
950 pSolarSysState->pOrbitalDesc = planet;
951 pSolarSysState->InOrbit = TRUE;
952 }
953
954 static BOOLEAN
CheckShipLocation(SIZE * newRadius)955 CheckShipLocation (SIZE *newRadius)
956 {
957 SIZE radius;
958
959 radius = pSolarSysState->SunDesc[0].radius;
960 *newRadius = pSolarSysState->SunDesc[0].radius;
961
962 if (GLOBAL (ShipStamp.origin.x) < 0
963 || GLOBAL (ShipStamp.origin.x) >= SIS_SCREEN_WIDTH
964 || GLOBAL (ShipStamp.origin.y) < 0
965 || GLOBAL (ShipStamp.origin.y) >= SIS_SCREEN_HEIGHT)
966 {
967 // The ship leaves the screen.
968 if (!playerInInnerSystem ())
969 { // Outer zoom-out transition
970 if (radius == MAX_ZOOM_RADIUS)
971 {
972 // The ship leaves IP.
973 GLOBAL (CurrentActivity) |= END_INTERPLANETARY;
974 return FALSE; // no location change
975 }
976
977 *newRadius = FindRadius (GLOBAL (ip_location),
978 MAX_ZOOM_RADIUS << 1);
979 }
980 else
981 {
982 leaveInnerSystem (pSolarSysState->pOrbitalDesc);
983 }
984
985 return TRUE;
986 }
987
988 if (!playerInInnerSystem ()
989 && pointWithinRect (scaleRect, GLOBAL (ShipStamp.origin)))
990 { // Outer zoom-in transition
991 *newRadius = FindRadius (GLOBAL (ip_location), radius);
992 return TRUE;
993 }
994
995 if (GLOBAL (autopilot.x) == ~0 && GLOBAL (autopilot.y) == ~0)
996 { // Not on autopilot -- may collide with a planet
997 PLANET_DESC *planet = CheckIntersect ();
998 if (planet)
999 { // Collision with a planet
1000 if (playerInInnerSystem ())
1001 { // Entering planet orbit (scans, etc.)
1002 enterOrbital (planet);
1003 return FALSE; // no location change
1004 }
1005 else
1006 { // Transition to inner system
1007 enterInnerSystem (planet);
1008 return TRUE;
1009 }
1010 }
1011 }
1012
1013 return FALSE; // no location change
1014 }
1015
1016 static void
DrawSystemTransition(BOOLEAN inner)1017 DrawSystemTransition (BOOLEAN inner)
1018 {
1019 SetTransitionSource (NULL);
1020 BatchGraphics ();
1021 if (inner)
1022 DrawInnerSystem ();
1023 else
1024 DrawOuterSystem ();
1025 RedrawQueue (FALSE);
1026 ScreenTransition (3, NULL);
1027 UnbatchGraphics ();
1028 }
1029
1030 static void
TransitionSystemIn(void)1031 TransitionSystemIn (void)
1032 {
1033 SetContext (SpaceContext);
1034 DrawSystemTransition (playerInInnerSystem ());
1035 }
1036
1037 static void
ScaleSystem(SIZE new_radius)1038 ScaleSystem (SIZE new_radius)
1039 {
1040 #ifdef SMOOTH_SYSTEM_ZOOM
1041 // XXX: This appears to have been an attempt to zoom the system view
1042 // in a different way. This code zooms gradually instead of
1043 // doing a crossfade from one zoom level to the other.
1044 // TODO: Do not loop here, and instead increment the zoom level
1045 // in IP_frame() with a function drawing the new zoom. The ship
1046 // controls are not handled in the loop, and the flagship
1047 // can collide with a group while zooming, and that is not handled
1048 // 100% correctly.
1049 #define NUM_STEPS 10
1050 COUNT i;
1051 SIZE old_radius;
1052 SIZE d, step;
1053
1054 old_radius = pSolarSysState->SunDesc[0].radius;
1055
1056 assert (old_radius != 0);
1057 assert (old_radius != new_radius);
1058
1059 d = new_radius - old_radius;
1060 step = d / NUM_STEPS;
1061
1062 for (i = 0; i < NUM_STEPS - 1; ++i)
1063 {
1064 pSolarSysState->SunDesc[0].radius += step;
1065 XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
1066
1067 BatchGraphics ();
1068 DrawOuterSystem ();
1069 RedrawQueue (FALSE);
1070 UnbatchGraphics ();
1071
1072 SleepThread (ONE_SECOND / 30);
1073 }
1074
1075 // Final zoom step
1076 pSolarSysState->SunDesc[0].radius = new_radius;
1077 XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
1078
1079 BatchGraphics ();
1080 DrawOuterSystem ();
1081 RedrawQueue (FALSE);
1082 UnbatchGraphics ();
1083
1084 #else // !SMOOTH_SYSTEM_ZOOM
1085 RECT r;
1086
1087 pSolarSysState->SunDesc[0].radius = new_radius;
1088 XFormIPLoc (&GLOBAL (ip_location), &GLOBAL (ShipStamp.origin), TRUE);
1089
1090 GetContextClipRect (&r);
1091 SetTransitionSource (&r);
1092 BatchGraphics ();
1093 DrawOuterSystem ();
1094 RedrawQueue (FALSE);
1095 ScreenTransition (3, &r);
1096 UnbatchGraphics ();
1097 #endif // SMOOTH_SYSTEM_ZOOM
1098 }
1099
1100 static void
RestoreSystemView(void)1101 RestoreSystemView (void)
1102 {
1103 STAMP s;
1104
1105 s.origin.x = 0;
1106 s.origin.y = 0;
1107 s.frame = SolarSysFrame;
1108 DrawStamp (&s);
1109 }
1110
1111 // Normally called by DoIpFlight() to process a frame
1112 static void
IP_frame(void)1113 IP_frame (void)
1114 {
1115 BOOLEAN locChange;
1116 SIZE newRadius;
1117
1118 SetContext (SpaceContext);
1119
1120 GameClockTick ();
1121 ProcessShipControls ();
1122
1123 locChange = CheckShipLocation (&newRadius);
1124 if (locChange)
1125 {
1126 if (playerInInnerSystem ())
1127 { // Entering inner system
1128 DrawSystemTransition (TRUE);
1129 }
1130 else if (pSolarSysState->SunDesc[0].radius == newRadius)
1131 { // Leaving inner system to outer
1132 DrawSystemTransition (FALSE);
1133 }
1134 else
1135 { // Zooming outer system
1136 ScaleSystem (newRadius);
1137 }
1138 }
1139 else
1140 { // Just flying around, minding own business..
1141 BatchGraphics ();
1142 RestoreSystemView ();
1143 RedrawQueue (FALSE);
1144 DrawAutoPilotMessage (FALSE);
1145 UnbatchGraphics ();
1146 }
1147
1148 }
1149
1150 static BOOLEAN
CheckZoomLevel(void)1151 CheckZoomLevel (void)
1152 {
1153 BOOLEAN InnerSystem;
1154 POINT shipLoc;
1155
1156 InnerSystem = playerInInnerSystem ();
1157 if (InnerSystem)
1158 shipLoc = pSolarSysState->SunDesc[0].location;
1159 else
1160 shipLoc = GLOBAL (ip_location);
1161
1162 pSolarSysState->SunDesc[0].radius = FindRadius (shipLoc,
1163 MAX_ZOOM_RADIUS << 1);
1164 if (!InnerSystem)
1165 { // Update ship stamp since the radius probably changed
1166 XFormIPLoc (&shipLoc, &GLOBAL (ShipStamp.origin), TRUE);
1167 }
1168
1169 return InnerSystem;
1170 }
1171
1172 static void
ValidateOrbits(void)1173 ValidateOrbits (void)
1174 {
1175 COUNT i;
1176 PLANET_DESC *planet;
1177
1178 for (i = pSolarSysState->SunDesc[0].NumPlanets,
1179 planet = &pSolarSysState->PlanetDesc[0]; i; --i, ++planet)
1180 {
1181 ValidateOrbit (planet, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
1182 pSolarSysState->SunDesc[0].radius);
1183 }
1184 }
1185
1186 static void
ValidateInnerOrbits(void)1187 ValidateInnerOrbits (void)
1188 {
1189 COUNT i;
1190 PLANET_DESC *planet;
1191
1192 assert (playerInInnerSystem ());
1193
1194 planet = pSolarSysState->pOrbitalDesc;
1195 ValidateOrbit (planet, DISPLAY_FACTOR * 4, DISPLAY_FACTOR,
1196 planet->radius);
1197
1198 for (i = 0; i < planet->NumPlanets; ++i)
1199 {
1200 PLANET_DESC *moon = &pSolarSysState->MoonDesc[i];
1201 ValidateOrbit (moon, 2, 1, 2);
1202 }
1203 }
1204
1205 static void
DrawInnerSystem(void)1206 DrawInnerSystem (void)
1207 {
1208 ValidateInnerOrbits ();
1209 DrawSystem (pSolarSysState->pOrbitalDesc->radius, TRUE);
1210 DrawSISTitle (GLOBAL_SIS (PlanetName));
1211 }
1212
1213 static void
DrawOuterSystem(void)1214 DrawOuterSystem (void)
1215 {
1216 ValidateOrbits ();
1217 DrawSystem (pSolarSysState->SunDesc[0].radius, FALSE);
1218 DrawHyperCoords (CurStarDescPtr->star_pt);
1219 }
1220
1221 static void
ResetSolarSys(void)1222 ResetSolarSys (void)
1223 {
1224 // Originally there was a flash_task test here, however, I found no cases
1225 // where flash_task could be set at the time of call. The test was
1226 // probably needed on 3DO when IP_frame() was a task.
1227 assert (!pSolarSysState->InIpFlight);
1228
1229 DrawMenuStateStrings (PM_STARMAP, -(PM_NAVIGATE - PM_SCAN));
1230
1231 InitDisplayList ();
1232 // This also spawns the flagship element
1233 DoMissions ();
1234
1235 // Figure out and note which planet/moon we just left, if any
1236 // This records any existing collision and prevents the ship
1237 // from entering planets until a new collision occurs.
1238 // TODO: this may need logic similar to one in leaveInnerSystem()
1239 // for when the ship collides with more than one planet at
1240 // the same time. While quite rare, it's still possible.
1241 CheckIntersect ();
1242
1243 pSolarSysState->InIpFlight = TRUE;
1244
1245 // Do not start playing the music if we entered the solarsys only
1246 // to load a game (load invoked from Main menu)
1247 // XXX: This is quite hacky
1248 if (!PLRPlaying ((MUSIC_REF)~0) &&
1249 (LastActivity != CHECK_LOAD || NextActivity))
1250 {
1251 PlayMusic (SpaceMusic, TRUE, 1);
1252 }
1253 }
1254
1255 static void
EnterPlanetOrbit(void)1256 EnterPlanetOrbit (void)
1257 {
1258 if (pSolarSysState->InIpFlight)
1259 { // This means we hit a planet in IP flight; not a Load into orbit
1260 FreeSolarSys ();
1261
1262 if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
1263 { // Moon -- use its origin
1264 // XXX: The conversion functions do not error-correct, so the
1265 // point we set here will change once flag_ship_preprocess()
1266 // in ipdisp.c starts over again.
1267 GLOBAL (ShipStamp.origin) =
1268 pSolarSysState->pOrbitalDesc->image.origin;
1269 }
1270 else
1271 { // Planet -- its origin is for the outer view, so use mid-screen
1272 GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
1273 GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT >> 1;
1274 }
1275 }
1276
1277 GetPlanetInfo ();
1278 (*pSolarSysState->genFuncs->generateOrbital) (pSolarSysState,
1279 pSolarSysState->pOrbitalDesc);
1280 LastActivity &= ~(CHECK_LOAD | CHECK_RESTART);
1281 if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD |
1282 START_ENCOUNTER)) || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0
1283 || GET_GAME_STATE (CHMMR_BOMB_STATE) == 2)
1284 return;
1285
1286 // Implement a to-do in generate.h for a better test
1287 if (pSolarSysState->TopoFrame)
1288 { // We've entered orbit; LoadPlanet() called planet surface-gen code
1289 PlanetOrbitMenu ();
1290 FreePlanet ();
1291 }
1292 // Otherwise, generateOrbital function started a homeworld conversation,
1293 // and we did not get to the planet no matter what.
1294
1295 // START_ENCOUNTER could be set by Devices menu a number of ways:
1296 // Talking Pet, Sun Device or a Caster over Chmmr, or
1297 // a Caster for Ilwrath
1298 // Could also have blown self up with Utwig Bomb
1299 if (!(GLOBAL (CurrentActivity) & (START_ENCOUNTER |
1300 CHECK_ABORT | CHECK_LOAD))
1301 && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0)
1302 { // Reload the system and return to the inner view
1303 PLANET_DESC *orbital = LoadSolarSys ();
1304 assert (!orbital);
1305 CheckZoomLevel ();
1306 ValidateOrbits ();
1307 ValidateInnerOrbits ();
1308 ResetSolarSys ();
1309
1310 RepairSISBorder ();
1311 TransitionSystemIn ();
1312 }
1313 }
1314
1315 static void
InitSolarSys(void)1316 InitSolarSys (void)
1317 {
1318 BOOLEAN InnerSystem;
1319 BOOLEAN Reentry;
1320 PLANET_DESC *orbital;
1321
1322
1323 LoadIPData ();
1324 LoadLanderData ();
1325
1326 Reentry = (GLOBAL (ShipFacing) != 0);
1327 if (!Reentry)
1328 {
1329 GLOBAL (autopilot.x) = ~0;
1330 GLOBAL (autopilot.y) = ~0;
1331
1332 GLOBAL (ShipStamp.origin.x) = SIS_SCREEN_WIDTH >> 1;
1333 GLOBAL (ShipStamp.origin.y) = SIS_SCREEN_HEIGHT - 2;
1334
1335 GLOBAL (ip_location) = displayToLocation (GLOBAL (ShipStamp.origin),
1336 MAX_ZOOM_RADIUS);
1337 }
1338
1339
1340 StarsFrame = CreateStarBackGround ();
1341
1342 SetContext (SpaceContext);
1343 SetContextFGFrame (Screen);
1344 SetContextBackGroundColor (BLACK_COLOR);
1345
1346
1347 orbital = LoadSolarSys ();
1348 InnerSystem = CheckZoomLevel ();
1349 ValidateOrbits ();
1350 if (InnerSystem)
1351 ValidateInnerOrbits ();
1352
1353 if (Reentry)
1354 {
1355 (*pSolarSysState->genFuncs->reinitNpcs) (pSolarSysState);
1356 }
1357 else
1358 {
1359 EncounterRace = -1;
1360 EncounterGroup = 0;
1361 GLOBAL (BattleGroupRef) = 0;
1362 ReinitQueue (&GLOBAL (ip_group_q));
1363 ReinitQueue (&GLOBAL (npc_built_ship_q));
1364 (*pSolarSysState->genFuncs->initNpcs) (pSolarSysState);
1365 }
1366
1367 if (orbital)
1368 {
1369 enterOrbital (orbital);
1370 }
1371 else
1372 { // Draw the borders, the system (inner or outer) and fade/transition
1373 SetContext (SpaceContext);
1374
1375 SetTransitionSource (NULL);
1376 BatchGraphics ();
1377
1378 DrawSISFrame ();
1379 DrawSISMessage (NULL);
1380
1381 ResetSolarSys ();
1382
1383 if (LastActivity == (CHECK_LOAD | CHECK_RESTART))
1384 { // Starting a new game, NOT from load!
1385 // We have to fade the screen in from intro or menu
1386 DrawOuterSystem ();
1387 RedrawQueue (FALSE);
1388 UnbatchGraphics ();
1389 FadeScreen (FadeAllToColor, ONE_SECOND / 2);
1390
1391 LastActivity = 0;
1392 }
1393 else if (LastActivity == CHECK_LOAD && !NextActivity)
1394 { // Called just to load a game; invoked from Main menu
1395 // No point in drawing anything
1396 UnbatchGraphics ();
1397 }
1398 else
1399 { // Entered a new system, or loaded into inner or outer
1400 if (InnerSystem)
1401 DrawInnerSystem ();
1402 else
1403 DrawOuterSystem ();
1404 RedrawQueue (FALSE);
1405 ScreenTransition (3, NULL);
1406 UnbatchGraphics ();
1407
1408 LastActivity &= ~CHECK_LOAD;
1409 }
1410 }
1411 }
1412
1413 static void
endInterPlanetary(void)1414 endInterPlanetary (void)
1415 {
1416 GLOBAL (CurrentActivity) &= ~END_INTERPLANETARY;
1417
1418 if (!(GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD)))
1419 {
1420 // These are game state changing ops and so cannot be
1421 // called once another game has been loaded!
1422 (*pSolarSysState->genFuncs->uninitNpcs) (pSolarSysState);
1423 SET_GAME_STATE (USED_BROADCASTER, 0);
1424 }
1425 }
1426
1427 // Find the closest planet to a point, in interplanetary.
1428 static PLANET_DESC *
closestPlanetInterPlanetary(const POINT * point)1429 closestPlanetInterPlanetary (const POINT *point)
1430 {
1431 BYTE i;
1432 BYTE numPlanets;
1433 DWORD bestDistSquared;
1434 PLANET_DESC *bestPlanet = NULL;
1435
1436 assert(pSolarSysState != NULL);
1437
1438 numPlanets = pSolarSysState->SunDesc[0].NumPlanets;
1439
1440 bestDistSquared = (DWORD) -1; // Maximum value of DWORD.
1441 for (i = 0; i < numPlanets; i++)
1442 {
1443 PLANET_DESC *planet = &pSolarSysState->PlanetDesc[i];
1444
1445 SIZE dx = point->x - planet->image.origin.x;
1446 SIZE dy = point->y - planet->image.origin.y;
1447
1448 DWORD distSquared = (DWORD) ((long) dx * dx + (long) dy * dy);
1449 if (distSquared < bestDistSquared)
1450 {
1451 bestDistSquared = distSquared;
1452 bestPlanet = planet;
1453 }
1454 }
1455
1456 return bestPlanet;
1457 }
1458
1459 static void
UninitSolarSys(void)1460 UninitSolarSys (void)
1461 {
1462 FreeSolarSys ();
1463
1464 //FreeLanderData ();
1465 //FreeIPData ();
1466
1467 DestroyDrawable (ReleaseDrawable (StarsFrame));
1468 StarsFrame = NULL;
1469
1470 if (GLOBAL (CurrentActivity) & END_INTERPLANETARY)
1471 {
1472 endInterPlanetary ();
1473 return;
1474 }
1475
1476 if ((GLOBAL (CurrentActivity) & START_ENCOUNTER) && EncounterGroup)
1477 {
1478 GetGroupInfo (GLOBAL (BattleGroupRef), EncounterGroup);
1479 // Generate the encounter location name based on the closest planet
1480
1481 if (GLOBAL (ip_planet) == 0)
1482 {
1483 PLANET_DESC *planet =
1484 closestPlanetInterPlanetary (&GLOBAL (ShipStamp.origin));
1485
1486 (*pSolarSysState->genFuncs->generateName) (
1487 pSolarSysState, planet);
1488 }
1489 }
1490 }
1491
1492 static void
CalcSunSize(PLANET_DESC * pSunDesc,SIZE radius)1493 CalcSunSize (PLANET_DESC *pSunDesc, SIZE radius)
1494 {
1495 SIZE index = 0;
1496
1497 if (radius <= (MAX_ZOOM_RADIUS >> 1))
1498 {
1499 ++index;
1500 if (radius <= (MAX_ZOOM_RADIUS >> 2))
1501 ++index;
1502 }
1503
1504 pSunDesc->image.origin.x = SIS_SCREEN_WIDTH >> 1;
1505 pSunDesc->image.origin.y = SIS_SCREEN_HEIGHT >> 1;
1506 pSunDesc->image.frame = SetRelFrameIndex (SunFrame, index);
1507 }
1508
1509 static void
SetPlanetColorMap(PLANET_DESC * planet)1510 SetPlanetColorMap (PLANET_DESC *planet)
1511 {
1512 COUNT index = planet->data_index & ~WORLD_TYPE_SPECIAL;
1513 assert (index < NUMBER_OF_PLANET_TYPES);
1514 SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (OrbitalCMap,
1515 PLANCOLOR (PlanData[index].Type))));
1516 }
1517
1518 static void
DrawInnerPlanets(PLANET_DESC * planet)1519 DrawInnerPlanets (PLANET_DESC *planet)
1520 {
1521 STAMP s;
1522 COUNT i;
1523 PLANET_DESC *moon;
1524
1525 // Draw the planet image
1526 SetPlanetColorMap (planet);
1527 s.origin.x = SIS_SCREEN_WIDTH >> 1;
1528 s.origin.y = SIS_SCREEN_HEIGHT >> 1;
1529 s.frame = planet->image.frame;
1530
1531 i = planet->data_index & ~WORLD_TYPE_SPECIAL;
1532 if (i < NUMBER_OF_PLANET_TYPES
1533 && (planet->data_index & PLANET_SHIELDED))
1534 { // Shielded world looks "shielded" in inner view
1535 s.frame = SetAbsFrameIndex (SpaceJunkFrame, 17);
1536 }
1537 DrawStamp (&s);
1538
1539 // Draw the moon images
1540 for (i = planet->NumPlanets, moon = pSolarSysState->MoonDesc;
1541 i; --i, ++moon)
1542 {
1543 if (!(moon->data_index & WORLD_TYPE_SPECIAL))
1544 SetPlanetColorMap (moon);
1545 DrawStamp (&moon->image);
1546 }
1547 }
1548
1549 static void
DrawSystem(SIZE radius,BOOLEAN IsInnerSystem)1550 DrawSystem (SIZE radius, BOOLEAN IsInnerSystem)
1551 {
1552 BYTE i;
1553 PLANET_DESC *pCurDesc;
1554 PLANET_DESC *pBaseDesc;
1555 CONTEXT oldContext;
1556 STAMP s;
1557
1558 if (!SolarSysFrame)
1559 { // Create the saved view graphic
1560 RECT clipRect;
1561
1562 GetContextClipRect (&clipRect);
1563 SolarSysFrame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
1564 clipRect.extent.width, clipRect.extent.height, 1));
1565 }
1566
1567 oldContext = SetContext (OffScreenContext);
1568 SetContextFGFrame (SolarSysFrame);
1569 SetContextClipRect (NULL);
1570
1571 DrawStarBackGround ();
1572
1573 pBaseDesc = pSolarSysState->pBaseDesc;
1574 if (IsInnerSystem)
1575 { // Draw the inner system view *planet's* orbit segment
1576 pCurDesc = pSolarSysState->pOrbitalDesc;
1577 DrawOrbit (pCurDesc, DISPLAY_FACTOR * 4, DISPLAY_FACTOR, radius);
1578 }
1579
1580 // Draw the planet orbits or moon orbits
1581 for (i = pBaseDesc->pPrevDesc->NumPlanets, pCurDesc = pBaseDesc;
1582 i; --i, ++pCurDesc)
1583 {
1584 if (IsInnerSystem)
1585 DrawOrbit (pCurDesc, 2, 1, 2);
1586 else
1587 DrawOrbit (pCurDesc, DISPLAY_FACTOR, DISPLAY_FACTOR / 4,
1588 radius);
1589 }
1590
1591 if (IsInnerSystem)
1592 { // Draw the inner system view
1593 DrawInnerPlanets (pSolarSysState->pOrbitalDesc);
1594 }
1595 else
1596 { // Draw the outer system view
1597 SIZE index;
1598
1599 CalcSunSize (&pSolarSysState->SunDesc[0], radius);
1600
1601 index = pSolarSysState->FirstPlanetIndex;
1602 for (;;)
1603 {
1604 pCurDesc = &pSolarSysState->PlanetDesc[index];
1605 if (pCurDesc == &pSolarSysState->SunDesc[0])
1606 { // It's a sun
1607 SetColorMap (GetColorMapAddress (SetAbsColorMapIndex (
1608 SunCMap, STAR_COLOR (CurStarDescPtr->Type))));
1609 }
1610 else
1611 { // It's a planet
1612 SetPlanetColorMap (pCurDesc);
1613 }
1614 DrawStamp (&pCurDesc->image);
1615
1616 if (index == pSolarSysState->LastPlanetIndex)
1617 break;
1618 index = pCurDesc->NextIndex;
1619 }
1620 }
1621
1622 SetContext (oldContext);
1623
1624 // Draw the now-saved view graphic
1625 s.origin.x = 0;
1626 s.origin.y = 0;
1627 s.frame = SolarSysFrame;
1628 DrawStamp (&s);
1629 }
1630
1631 void
DrawStarBackGround(void)1632 DrawStarBackGround (void)
1633 {
1634 STAMP s;
1635
1636 s.origin.x = 0;
1637 s.origin.y = 0;
1638 s.frame = StarsFrame;
1639 DrawStamp (&s);
1640 }
1641
1642 static FRAME
CreateStarBackGround(void)1643 CreateStarBackGround (void)
1644 {
1645 COUNT i, j;
1646 DWORD rand_val;
1647 STAMP s;
1648 CONTEXT oldContext;
1649 RECT clipRect;
1650 FRAME frame;
1651
1652 // Use SpaceContext to find out the dimensions of the background
1653 oldContext = SetContext (SpaceContext);
1654 GetContextClipRect (&clipRect);
1655
1656 // Prepare a pre-drawn stars frame for this system
1657 frame = CaptureDrawable (CreateDrawable (WANT_PIXMAP,
1658 clipRect.extent.width, clipRect.extent.height, 1));
1659 SetContext (OffScreenContext);
1660 SetContextFGFrame (frame);
1661 SetContextClipRect (NULL);
1662 SetContextBackGroundColor (BLACK_COLOR);
1663
1664 ClearDrawable ();
1665
1666 RandomContext_SeedRandom (SysGenRNG, GetRandomSeedForStar (CurStarDescPtr));
1667
1668 #define NUM_DIM_PIECES 8
1669 s.frame = SpaceJunkFrame;
1670 for (i = 0; i < NUM_DIM_PIECES; ++i)
1671 {
1672 #define NUM_DIM_DRAWN 5
1673 for (j = 0; j < NUM_DIM_DRAWN; ++j)
1674 {
1675 rand_val = RandomContext_Random (SysGenRNG);
1676 s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
1677 s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;
1678
1679 DrawStamp (&s);
1680 }
1681 s.frame = IncFrameIndex (s.frame);
1682 }
1683 #define NUM_BRT_PIECES 8
1684 for (i = 0; i < NUM_BRT_PIECES; ++i)
1685 {
1686 #define NUM_BRT_DRAWN 30
1687 for (j = 0; j < NUM_BRT_DRAWN; ++j)
1688 {
1689 rand_val = RandomContext_Random (SysGenRNG);
1690 s.origin.x = LOWORD (rand_val) % SIS_SCREEN_WIDTH;
1691 s.origin.y = HIWORD (rand_val) % SIS_SCREEN_HEIGHT;
1692
1693 DrawStamp (&s);
1694 }
1695 s.frame = IncFrameIndex (s.frame);
1696 }
1697
1698 SetContext (oldContext);
1699
1700 return frame;
1701 }
1702
1703 void
XFormIPLoc(POINT * pIn,POINT * pOut,BOOLEAN ToDisplay)1704 XFormIPLoc (POINT *pIn, POINT *pOut, BOOLEAN ToDisplay)
1705 {
1706 if (ToDisplay)
1707 *pOut = locationToDisplay (*pIn, pSolarSysState->SunDesc[0].radius);
1708 else
1709 *pOut = displayToLocation (*pIn, pSolarSysState->SunDesc[0].radius);
1710 }
1711
1712 void
ExploreSolarSys(void)1713 ExploreSolarSys (void)
1714 {
1715 SOLARSYS_STATE SolarSysState;
1716
1717 if (CurStarDescPtr == 0)
1718 {
1719 POINT universe;
1720
1721 universe.x = LOGX_TO_UNIVERSE (GLOBAL_SIS (log_x));
1722 universe.y = LOGY_TO_UNIVERSE (GLOBAL_SIS (log_y));
1723 CurStarDescPtr = FindStar (0, &universe, 1, 1);
1724 if (!CurStarDescPtr)
1725 {
1726 log_add (log_Fatal, "ExploreSolarSys(): do not know where you are!");
1727 explode ();
1728 }
1729 }
1730 GLOBAL_SIS (log_x) = UNIVERSE_TO_LOGX (CurStarDescPtr->star_pt.x);
1731 GLOBAL_SIS (log_y) = UNIVERSE_TO_LOGY (CurStarDescPtr->star_pt.y);
1732
1733 pSolarSysState = &SolarSysState;
1734
1735 memset (pSolarSysState, 0, sizeof (*pSolarSysState));
1736
1737 SolarSysState.genFuncs = getGenerateFunctions (CurStarDescPtr->Index);
1738
1739 InitSolarSys ();
1740 SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
1741 SolarSysState.InputFunc = DoIpFlight;
1742 DoInput (&SolarSysState, FALSE);
1743 UninitSolarSys ();
1744 pSolarSysState = 0;
1745 }
1746
1747 UNICODE *
GetNamedPlanetaryBody(void)1748 GetNamedPlanetaryBody (void)
1749 {
1750 if (!CurStarDescPtr || !playerInSolarSystem () || !playerInInnerSystem ())
1751 return NULL; // Not inside an inner system, so no name
1752
1753 assert (pSolarSysState->pOrbitalDesc != NULL);
1754
1755 if (CurStarDescPtr->Index == SOL_DEFINED)
1756 { // Planets and moons in Sol
1757 int planet;
1758 int moon;
1759
1760 planet = planetIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
1761
1762 if (worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
1763 { // A planet
1764 return GAME_STRING (PLANET_NUMBER_BASE + planet);
1765 }
1766
1767 // Moons
1768 moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
1769 switch (planet)
1770 {
1771 case 2: // Earth
1772 switch (moon)
1773 {
1774 case 0: // Starbase
1775 return GAME_STRING (STARBASE_STRING_BASE + 0);
1776 case 1: // Luna
1777 return GAME_STRING (PLANET_NUMBER_BASE + 9);
1778 }
1779 break;
1780 case 4: // Jupiter
1781 switch (moon)
1782 {
1783 case 0: // Io
1784 return GAME_STRING (PLANET_NUMBER_BASE + 10);
1785 case 1: // Europa
1786 return GAME_STRING (PLANET_NUMBER_BASE + 11);
1787 case 2: // Ganymede
1788 return GAME_STRING (PLANET_NUMBER_BASE + 12);
1789 case 3: // Callisto
1790 return GAME_STRING (PLANET_NUMBER_BASE + 13);
1791 }
1792 break;
1793 case 5: // Saturn
1794 if (moon == 0) // Titan
1795 return GAME_STRING (PLANET_NUMBER_BASE + 14);
1796 break;
1797 case 7: // Neptune
1798 if (moon == 0) // Triton
1799 return GAME_STRING (PLANET_NUMBER_BASE + 15);
1800 break;
1801 }
1802 }
1803 else if (CurStarDescPtr->Index == SPATHI_DEFINED)
1804 {
1805 if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc,
1806 0, MATCH_PLANET))
1807 {
1808 #ifdef NOTYET
1809 return "Spathiwa";
1810 #endif // NOTYET
1811 }
1812 }
1813 else if (CurStarDescPtr->Index == SAMATRA_DEFINED)
1814 {
1815 if (matchWorld (pSolarSysState, pSolarSysState->pOrbitalDesc, 4, 0))
1816 { // Sa-Matra
1817 return GAME_STRING (PLANET_NUMBER_BASE + 32);
1818 }
1819 }
1820
1821 return NULL;
1822 }
1823
1824 void
GetPlanetOrMoonName(UNICODE * buf,COUNT bufsize)1825 GetPlanetOrMoonName (UNICODE *buf, COUNT bufsize)
1826 {
1827 UNICODE *named;
1828 int moon;
1829 int i;
1830
1831 named = GetNamedPlanetaryBody ();
1832 if (named)
1833 {
1834 utf8StringCopy (buf, bufsize, named);
1835 return;
1836 }
1837
1838 // Either not named or we already have a name
1839 utf8StringCopy (buf, bufsize, GLOBAL_SIS (PlanetName));
1840
1841 if (!playerInSolarSystem () || !playerInInnerSystem () ||
1842 worldIsPlanet (pSolarSysState, pSolarSysState->pOrbitalDesc))
1843 { // Outer or inner system or orbiting a planet
1844 return;
1845 }
1846
1847 // Orbiting an unnamed moon
1848 i = strlen (buf);
1849 buf += i;
1850 bufsize -= i;
1851 moon = moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
1852 if (bufsize >= 3)
1853 {
1854 snprintf (buf, bufsize, "-%c", 'A' + moon);
1855 buf[bufsize - 1] = '\0';
1856 }
1857 }
1858
1859 void
SaveSolarSysLocation(void)1860 SaveSolarSysLocation (void)
1861 {
1862 assert (playerInSolarSystem ());
1863
1864 // This is a two-stage saving procedure
1865 // Stage 1: called when saving from inner/outer view
1866 // Stage 2: called when saving from orbital
1867
1868 if (!playerInPlanetOrbit ())
1869 {
1870 saveNonOrbitalLocation ();
1871 }
1872 else
1873 { // In orbit around a planet.
1874 BYTE moon;
1875
1876 // Update the starinfo.dat file if necessary.
1877 if (GET_GAME_STATE (PLANETARY_CHANGE))
1878 {
1879 PutPlanetInfo ();
1880 SET_GAME_STATE (PLANETARY_CHANGE, 0);
1881 }
1882
1883 // GLOBAL (ip_planet) is already set
1884 assert (GLOBAL (ip_planet) != 0);
1885
1886 // has to be at least 1 because code tests for in_orbit!=0
1887 moon = 1; /* the planet itself */
1888 if (worldIsMoon (pSolarSysState, pSolarSysState->pOrbitalDesc))
1889 {
1890 moon += moonIndex (pSolarSysState, pSolarSysState->pOrbitalDesc);
1891 // +1 because moons have to be 1-based
1892 moon += 1;
1893 }
1894 GLOBAL (in_orbit) = moon;
1895 }
1896 }
1897
1898 static BOOLEAN
DoSolarSysMenu(MENU_STATE * pMS)1899 DoSolarSysMenu (MENU_STATE *pMS)
1900 {
1901 BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
1902 BOOLEAN handled;
1903
1904 if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
1905 || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
1906 return FALSE;
1907
1908 handled = DoMenuChooser (pMS, PM_STARMAP);
1909 if (handled)
1910 return TRUE;
1911
1912 if (LastActivity == CHECK_LOAD)
1913 select = TRUE; // Selected LOAD from main menu
1914
1915 if (!select)
1916 return TRUE;
1917
1918 SetFlashRect (NULL);
1919
1920 switch (pMS->CurState)
1921 {
1922 case EQUIP_DEVICE:
1923 select = DevicesMenu ();
1924 if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
1925 { // Invoked Talking Pet or a Caster for Ilwrath
1926 // Going into conversation
1927 return FALSE;
1928 }
1929 break;
1930 case CARGO:
1931 CargoMenu ();
1932 break;
1933 case ROSTER:
1934 select = RosterMenu ();
1935 break;
1936 case GAME_MENU:
1937 if (!GameOptions ())
1938 return FALSE; // abort or load
1939 break;
1940 case STARMAP:
1941 StarMap ();
1942 if (GLOBAL (CurrentActivity) & CHECK_ABORT)
1943 return FALSE;
1944
1945 TransitionSystemIn ();
1946 // Fall through !!!
1947 case NAVIGATION:
1948 return FALSE;
1949 }
1950
1951 if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
1952 {
1953 if (select)
1954 { // 3DO menu jumps to NAVIGATE after a successful submenu run
1955 if (optWhichMenu != OPT_PC)
1956 pMS->CurState = NAVIGATION;
1957 DrawMenuStateStrings (PM_STARMAP, pMS->CurState);
1958 }
1959 SetFlashRect (SFR_MENU_3DO);
1960 }
1961
1962 return TRUE;
1963 }
1964
1965 static void
SolarSysMenu(void)1966 SolarSysMenu (void)
1967 {
1968 MENU_STATE MenuState;
1969
1970 memset (&MenuState, 0, sizeof MenuState);
1971
1972 if (LastActivity == CHECK_LOAD)
1973 { // Selected LOAD from main menu
1974 MenuState.CurState = GAME_MENU;
1975 }
1976 else
1977 {
1978 DrawMenuStateStrings (PM_STARMAP, STARMAP);
1979 MenuState.CurState = STARMAP;
1980 }
1981
1982 DrawStatusMessage (NULL);
1983 SetFlashRect (SFR_MENU_3DO);
1984
1985 SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
1986 MenuState.InputFunc = DoSolarSysMenu;
1987 DoInput (&MenuState, TRUE);
1988
1989 DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
1990 }
1991
1992 static BOOLEAN
DoIpFlight(SOLARSYS_STATE * pSS)1993 DoIpFlight (SOLARSYS_STATE *pSS)
1994 {
1995 static TimeCount NextTime;
1996 BOOLEAN cancel = PulsedInputState.menu[KEY_MENU_CANCEL];
1997
1998 if (pSS->InOrbit)
1999 { // CheckShipLocation() or InitSolarSys() sent us to orbital
2000 EnterPlanetOrbit ();
2001 SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
2002 pSS->InOrbit = FALSE;
2003 }
2004 else if (cancel || LastActivity == CHECK_LOAD)
2005 {
2006 SolarSysMenu ();
2007 SetMenuSounds (MENU_SOUND_NONE, MENU_SOUND_NONE);
2008 }
2009 else
2010 {
2011 assert (pSS->InIpFlight);
2012 IP_frame ();
2013 SleepThreadUntil (NextTime);
2014 NextTime = GetTimeCounter () + IP_FRAME_RATE;
2015 }
2016
2017 return (!(GLOBAL (CurrentActivity)
2018 & (START_ENCOUNTER | END_INTERPLANETARY
2019 | CHECK_ABORT | CHECK_LOAD))
2020 && GLOBAL_SIS (CrewEnlisted) != (COUNT)~0);
2021 }
2022