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