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 "planets.h"
20
21 #include "scan.h"
22 #include "lander.h"
23 #include "../colors.h"
24 #include "../element.h"
25 #include "../settings.h"
26 #include "../controls.h"
27 #include "../sounds.h"
28 #include "../gameopt.h"
29 #include "../shipcont.h"
30 #include "../setup.h"
31 #include "../uqmdebug.h"
32 #include "../resinst.h"
33 #include "../nameref.h"
34 #include "options.h"
35 #include "libs/graphics/gfx_common.h"
36
37
38 // PlanetOrbitMenu() items
39 enum PlanetMenuItems
40 {
41 // XXX: Must match the enum in menustat.h
42 SCAN = 0,
43 STARMAP,
44 EQUIP_DEVICE,
45 CARGO,
46 ROSTER,
47 GAME_MENU,
48 NAVIGATION,
49 };
50
51 CONTEXT PlanetContext;
52 // Context for rotating planet view and lander surface view
53
54 static void
CreatePlanetContext(void)55 CreatePlanetContext (void)
56 {
57 CONTEXT oldContext;
58 RECT r;
59
60 assert (PlanetContext == NULL);
61
62 // PlanetContext rect is relative to SpaceContext
63 oldContext = SetContext (SpaceContext);
64 GetContextClipRect (&r);
65
66 PlanetContext = CreateContext ("PlanetContext");
67 SetContext (PlanetContext);
68 SetContextFGFrame (Screen);
69 r.extent.height -= MAP_HEIGHT + MAP_BORDER_HEIGHT;
70 SetContextClipRect (&r);
71
72 SetContext (oldContext);
73 }
74
75 static void
DestroyPlanetContext(void)76 DestroyPlanetContext (void)
77 {
78 if (PlanetContext)
79 {
80 DestroyContext (PlanetContext);
81 PlanetContext = NULL;
82 }
83 }
84
85 void
DrawScannedObjects(BOOLEAN Reversed)86 DrawScannedObjects (BOOLEAN Reversed)
87 {
88 HELEMENT hElement, hNextElement;
89
90 for (hElement = Reversed ? GetTailElement () : GetHeadElement ();
91 hElement; hElement = hNextElement)
92 {
93 ELEMENT *ElementPtr;
94
95 LockElement (hElement, &ElementPtr);
96 hNextElement = Reversed ?
97 GetPredElement (ElementPtr) :
98 GetSuccElement (ElementPtr);
99
100 if (ElementPtr->state_flags & APPEARING)
101 {
102 STAMP s;
103
104 s.origin = ElementPtr->current.location;
105 s.frame = ElementPtr->next.image.frame;
106 DrawStamp (&s);
107 }
108
109 UnlockElement (hElement);
110 }
111 }
112
113 void
DrawPlanetSurfaceBorder(void)114 DrawPlanetSurfaceBorder (void)
115 {
116 CONTEXT oldContext;
117 RECT oldClipRect;
118 RECT clipRect;
119 RECT r;
120
121 oldContext = SetContext (SpaceContext);
122 GetContextClipRect (&oldClipRect);
123
124 // Expand the context clip-rect so that we can tweak the existing border
125 clipRect = oldClipRect;
126 clipRect.corner.x -= 1;
127 clipRect.extent.width += 2;
128 clipRect.extent.height += 1;
129 SetContextClipRect (&clipRect);
130
131 BatchGraphics ();
132
133 // Border bulk
134 SetContextForeGroundColor (
135 BUILD_COLOR (MAKE_RGB15 (0x0A, 0x0A, 0x0A), 0x08));
136 r.corner.x = 0;
137 r.corner.y = clipRect.extent.height - MAP_HEIGHT - MAP_BORDER_HEIGHT;
138 r.extent.width = clipRect.extent.width;
139 r.extent.height = MAP_BORDER_HEIGHT - 2;
140 DrawFilledRectangle (&r);
141
142 SetContextForeGroundColor (SIS_BOTTOM_RIGHT_BORDER_COLOR);
143
144 // Border top shadow line
145 r.extent.width -= 1;
146 r.extent.height = 1;
147 r.corner.x = 1;
148 r.corner.y -= 1;
149 DrawFilledRectangle (&r);
150
151 // XXX: We will need bulk left and right rects here if MAP_WIDTH changes
152
153 // Right shadow line
154 r.extent.width = 1;
155 r.extent.height = MAP_HEIGHT + 2;
156 r.corner.y += MAP_BORDER_HEIGHT - 1;
157 r.corner.x = clipRect.extent.width - 1;
158 DrawFilledRectangle (&r);
159
160 SetContextForeGroundColor (SIS_LEFT_BORDER_COLOR);
161
162 // Left shadow line
163 r.corner.x -= MAP_WIDTH + 1;
164 DrawFilledRectangle (&r);
165
166 // Border bottom shadow line
167 r.extent.width = MAP_WIDTH + 2;
168 r.extent.height = 1;
169 DrawFilledRectangle (&r);
170
171 UnbatchGraphics ();
172
173 SetContextClipRect (&oldClipRect);
174 SetContext (oldContext);
175 }
176
177 typedef enum
178 {
179 DRAW_ORBITAL_FULL,
180 DRAW_ORBITAL_WAIT,
181 DRAW_ORBITAL_UPDATE,
182
183 } DRAW_ORBITAL_MODE;
184
185 static void
DrawOrbitalDisplay(DRAW_ORBITAL_MODE Mode)186 DrawOrbitalDisplay (DRAW_ORBITAL_MODE Mode)
187 {
188 RECT r;
189
190 SetContext (SpaceContext);
191 GetContextClipRect (&r);
192
193 BatchGraphics ();
194
195 if (Mode != DRAW_ORBITAL_UPDATE)
196 {
197 SetTransitionSource (NULL);
198
199 DrawSISFrame ();
200 DrawSISMessage (NULL);
201 DrawSISTitle (GLOBAL_SIS (PlanetName));
202 DrawStarBackGround ();
203 DrawPlanetSurfaceBorder ();
204 }
205
206 if (Mode == DRAW_ORBITAL_WAIT)
207 {
208 STAMP s;
209
210 SetContext (GetScanContext (NULL));
211 s.frame = CaptureDrawable (LoadGraphic (ORBENTER_PMAP_ANIM));
212 s.origin.x = -SAFE_X;
213 s.origin.y = 0;
214 DrawStamp (&s);
215 DestroyDrawable (ReleaseDrawable (s.frame));
216 }
217 else if (Mode == DRAW_ORBITAL_FULL)
218 {
219 DrawDefaultPlanetSphere ();
220 }
221
222 if (Mode != DRAW_ORBITAL_WAIT)
223 {
224 SetContext (GetScanContext (NULL));
225 DrawPlanet (0, BLACK_COLOR);
226 }
227
228 if (Mode != DRAW_ORBITAL_UPDATE)
229 {
230 ScreenTransition (3, &r);
231 }
232
233 UnbatchGraphics ();
234
235 // for later RepairBackRect()
236 LoadIntoExtraScreen (&r);
237 }
238
239 // Initialise the surface graphics, and start the planet music.
240 // Called from the GenerateFunctions.generateOribital() function
241 // (when orbit is entered; either from IP, or from loading a saved game)
242 // and when "starmap" is selected from orbit and then cancelled;
243 // also after in-orbit comm and after defeating planet guards in combat.
244 // SurfDefFrame contains surface definition images when a planet comes
245 // with its own bitmap (currently only for Earth)
246 void
LoadPlanet(FRAME SurfDefFrame)247 LoadPlanet (FRAME SurfDefFrame)
248 {
249 bool WaitMode = !(LastActivity & CHECK_LOAD);
250 PLANET_DESC *pPlanetDesc;
251
252 #ifdef DEBUG
253 if (disableInteractivity)
254 return;
255 #endif
256
257 assert (pSolarSysState->InOrbit && !pSolarSysState->TopoFrame);
258
259 CreatePlanetContext ();
260
261 if (WaitMode)
262 {
263 DrawOrbitalDisplay (DRAW_ORBITAL_WAIT);
264 }
265
266 StopMusic ();
267
268 pPlanetDesc = pSolarSysState->pOrbitalDesc;
269 GeneratePlanetSurface (pPlanetDesc, SurfDefFrame);
270 SetPlanetMusic (pPlanetDesc->data_index & ~PLANET_SHIELDED);
271 GeneratePlanetSide ();
272
273 if (!PLRPlaying ((MUSIC_REF)~0))
274 PlayMusic (LanderMusic, TRUE, 1);
275
276 if (WaitMode)
277 {
278 ZoomInPlanetSphere ();
279 DrawOrbitalDisplay (DRAW_ORBITAL_UPDATE);
280 }
281 else
282 {
283 DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
284 }
285 }
286
287 void
FreePlanet(void)288 FreePlanet (void)
289 {
290 COUNT i;
291 PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
292
293 UninitSphereRotation ();
294
295 StopMusic ();
296
297 for (i = 0; i < sizeof (pSolarSysState->PlanetSideFrame)
298 / sizeof (pSolarSysState->PlanetSideFrame[0]); ++i)
299 {
300 DestroyDrawable (ReleaseDrawable (pSolarSysState->PlanetSideFrame[i]));
301 pSolarSysState->PlanetSideFrame[i] = 0;
302 }
303
304 // FreeLanderData ();
305
306 DestroyStringTable (ReleaseStringTable (pSolarSysState->XlatRef));
307 pSolarSysState->XlatRef = 0;
308 DestroyDrawable (ReleaseDrawable (pSolarSysState->TopoFrame));
309 pSolarSysState->TopoFrame = 0;
310 DestroyColorMap (ReleaseColorMap (pSolarSysState->OrbitalCMap));
311 pSolarSysState->OrbitalCMap = 0;
312
313 HFree (Orbit->lpTopoData);
314 Orbit->lpTopoData = 0;
315 DestroyDrawable (ReleaseDrawable (Orbit->TopoZoomFrame));
316 Orbit->TopoZoomFrame = 0;
317 DestroyDrawable (ReleaseDrawable (Orbit->SphereFrame));
318 Orbit->SphereFrame = NULL;
319
320 DestroyDrawable (ReleaseDrawable (Orbit->TintFrame));
321 Orbit->TintFrame = 0;
322 Orbit->TintColor = BLACK_COLOR;
323
324 DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
325 Orbit->ObjectFrame = 0;
326 DestroyDrawable (ReleaseDrawable (Orbit->WorkFrame));
327 Orbit->WorkFrame = 0;
328
329 HFree (Orbit->TopoColors);
330 Orbit->TopoColors = NULL;
331 HFree (Orbit->ScratchArray);
332 Orbit->ScratchArray = NULL;
333
334 DestroyStringTable (ReleaseStringTable (
335 pSolarSysState->SysInfo.PlanetInfo.DiscoveryString
336 ));
337 pSolarSysState->SysInfo.PlanetInfo.DiscoveryString = 0;
338 FreeLanderFont (&pSolarSysState->SysInfo.PlanetInfo);
339
340 // Need to make sure our own CONTEXTs are not active because
341 // we will destroy them now
342 SetContext (SpaceContext);
343 DestroyPlanetContext ();
344 DestroyScanContext ();
345
346 }
347
348 void
LoadStdLanderFont(PLANET_INFO * info)349 LoadStdLanderFont (PLANET_INFO *info)
350 {
351 info->LanderFont = LoadFont (LANDER_FONT);
352 info->LanderFontEff = CaptureDrawable (
353 LoadGraphic (LANDER_FONTEFF_PMAP_ANIM));
354 }
355
356 void
FreeLanderFont(PLANET_INFO * info)357 FreeLanderFont (PLANET_INFO *info)
358 {
359 DestroyFont (info->LanderFont);
360 info->LanderFont = NULL;
361 DestroyDrawable (ReleaseDrawable (info->LanderFontEff));
362 info->LanderFontEff = NULL;
363 }
364
365 static BOOLEAN
DoPlanetOrbit(MENU_STATE * pMS)366 DoPlanetOrbit (MENU_STATE *pMS)
367 {
368 BOOLEAN select = PulsedInputState.menu[KEY_MENU_SELECT];
369 BOOLEAN handled;
370
371 if ((GLOBAL (CurrentActivity) & (CHECK_ABORT | CHECK_LOAD))
372 || GLOBAL_SIS (CrewEnlisted) == (COUNT)~0)
373 return FALSE;
374
375 // XXX: pMS actually refers to pSolarSysState->MenuState
376 handled = DoMenuChooser (pMS, PM_SCAN);
377 if (handled)
378 return TRUE;
379
380 if (!select)
381 return TRUE;
382
383 SetFlashRect (NULL);
384
385 switch (pMS->CurState)
386 {
387 case SCAN:
388 ScanSystem ();
389 if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
390 { // Found Fwiffo on Pluto
391 return FALSE;
392 }
393 break;
394 case EQUIP_DEVICE:
395 select = DevicesMenu ();
396 if (GLOBAL (CurrentActivity) & START_ENCOUNTER)
397 { // Invoked Talking Pet, a Caster or Sun Device over Chmmr,
398 // or a Caster for Ilwrath
399 // Going into conversation
400 return FALSE;
401 }
402 break;
403 case CARGO:
404 CargoMenu ();
405 break;
406 case ROSTER:
407 select = RosterMenu ();
408 break;
409 case GAME_MENU:
410 if (!GameOptions ())
411 return FALSE; // abort or load
412 break;
413 case STARMAP:
414 {
415 BOOLEAN AutoPilotSet;
416 InputFrameCallback *oldCallback;
417
418 // Deactivate planet rotation
419 oldCallback = SetInputCallback (NULL);
420
421 RepairSISBorder ();
422
423 AutoPilotSet = StarMap ();
424 if (GLOBAL (CurrentActivity) & CHECK_ABORT)
425 return FALSE;
426
427 // Reactivate planet rotation
428 SetInputCallback (oldCallback);
429
430 if (!AutoPilotSet)
431 { // Redraw the orbital display
432 DrawOrbitalDisplay (DRAW_ORBITAL_FULL);
433 break;
434 }
435 // Fall through !!!
436 }
437 case NAVIGATION:
438 return FALSE;
439 }
440
441 if (!(GLOBAL (CurrentActivity) & CHECK_ABORT))
442 {
443 if (select)
444 { // 3DO menu jumps to NAVIGATE after a successful submenu run
445 if (optWhichMenu != OPT_PC)
446 pMS->CurState = NAVIGATION;
447 DrawMenuStateStrings (PM_SCAN, pMS->CurState);
448 }
449 SetFlashRect (SFR_MENU_3DO);
450 }
451
452 return TRUE;
453 }
454
455 static void
on_input_frame(void)456 on_input_frame (void)
457 {
458 RotatePlanetSphere (TRUE);
459 }
460
461 void
PlanetOrbitMenu(void)462 PlanetOrbitMenu (void)
463 {
464 MENU_STATE MenuState;
465 InputFrameCallback *oldCallback;
466
467 memset (&MenuState, 0, sizeof MenuState);
468
469 DrawMenuStateStrings (PM_SCAN, SCAN);
470 SetFlashRect (SFR_MENU_3DO);
471
472 MenuState.CurState = SCAN;
473 SetMenuSounds (MENU_SOUND_ARROWS, MENU_SOUND_SELECT);
474 oldCallback = SetInputCallback (on_input_frame);
475
476 MenuState.InputFunc = DoPlanetOrbit;
477 DoInput (&MenuState, TRUE);
478
479 SetInputCallback (oldCallback);
480
481 SetFlashRect (NULL);
482 DrawMenuStateStrings (PM_STARMAP, -NAVIGATION);
483 }
484