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 #include "../colors.h"
21 #include "../setup.h"
22 #include "libs/graphics/gfx_common.h"
23 #include "libs/graphics/drawable.h"
24 #include "libs/mathlib.h"
25 #include "scan.h"
26 #include "options.h"
27 
28 #include <math.h>
29 
30 
31 // define USE_ADDITIVE_SCAN_BLIT to use additive blittting
32 // instead of transparency for the planet scans.
33 // It still doesn't look right though (it is too bright)
34 #define USE_ADDITIVE_SCAN_BLIT
35 
36 static int rotFrameIndex;
37 static int rotDirection;
38 static bool throbShield;
39 static int rotPointIndex;
40 
41 // Draw the planet sphere and any extra graphic (like a shield) if present
42 void
DrawPlanetSphere(int x,int y)43 DrawPlanetSphere (int x, int y)
44 {
45 	STAMP s;
46 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
47 
48 	s.origin.x = x;
49 	s.origin.y = y;
50 
51 	BatchGraphics ();
52 	s.frame = Orbit->SphereFrame;
53 	DrawStamp (&s);
54 	if (Orbit->ObjectFrame)
55 	{
56 		s.frame = Orbit->ObjectFrame;
57 		DrawStamp (&s);
58 	}
59 	UnbatchGraphics ();
60 }
61 
62 void
DrawDefaultPlanetSphere(void)63 DrawDefaultPlanetSphere (void)
64 {
65 	CONTEXT oldContext;
66 
67 	oldContext = SetContext (PlanetContext);
68 	DrawPlanetSphere (SIS_SCREEN_WIDTH / 2, PLANET_ORG_Y);
69 	SetContext (oldContext);
70 }
71 
72 void
InitSphereRotation(int direction,BOOLEAN shielded)73 InitSphereRotation (int direction, BOOLEAN shielded)
74 {
75 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
76 
77 	rotDirection = direction;
78 	rotPointIndex = 0;
79 	throbShield = shielded && optWhichShield == OPT_3DO;
80 
81 	if (throbShield)
82 	{
83 		// ObjectFrame must contain the shield graphic
84 		Orbit->WorkFrame = Orbit->ObjectFrame;
85 		// We need a scratch frame so that we can apply throbbing
86 		// to the shield, so create one
87 		Orbit->ObjectFrame = CaptureDrawable (CreateDrawable (
88 				WANT_PIXMAP | WANT_ALPHA,
89 				GetFrameWidth (Orbit->ObjectFrame),
90 				GetFrameHeight (Orbit->ObjectFrame), 2));
91 	}
92 
93 	// Render the first sphere/shield frame
94 	// Prepare will set the next one
95 	rotFrameIndex = 1;
96 	PrepareNextRotationFrame ();
97 }
98 
99 void
UninitSphereRotation(void)100 UninitSphereRotation (void)
101 {
102 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
103 
104 	if (Orbit->WorkFrame)
105 	{
106 		DestroyDrawable (ReleaseDrawable (Orbit->ObjectFrame));
107 		Orbit->ObjectFrame = Orbit->WorkFrame;
108 		Orbit->WorkFrame = NULL;
109 	}
110 }
111 
112 void
PrepareNextRotationFrame(void)113 PrepareNextRotationFrame (void)
114 {
115 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
116 
117 	// Generate the next rotation frame
118 	// We alternate between the frames because we do not call FlushGraphics()
119 	// The frame we just drew may not have made it to the screen yet
120 	rotFrameIndex ^= 1;
121 
122 	// Go to next point, taking care of wraparounds
123 	rotPointIndex += rotDirection;
124 	if (rotPointIndex < 0)
125 		rotPointIndex = MAP_WIDTH - 1;
126 	else if (rotPointIndex >= MAP_WIDTH)
127 		rotPointIndex = 0;
128 
129 	// prepare the next sphere frame
130 	Orbit->SphereFrame = SetAbsFrameIndex (Orbit->SphereFrame, rotFrameIndex);
131 	RenderPlanetSphere (Orbit->SphereFrame, rotPointIndex, throbShield);
132 
133 	if (throbShield)
134 	{	// prepare the next shield throb frame
135 		Orbit->ObjectFrame = SetAbsFrameIndex (Orbit->ObjectFrame,
136 				rotFrameIndex);
137 		SetShieldThrobEffect (Orbit->WorkFrame, rotPointIndex,
138 				Orbit->ObjectFrame);
139 	}
140 }
141 
142 #define ZOOM_RATE  24
143 #define ZOOM_TIME  (ONE_SECOND * 6 / 5)
144 
145 // This takes care of zooming the planet sphere into place
146 // when entering orbit
147 void
ZoomInPlanetSphere(void)148 ZoomInPlanetSphere (void)
149 {
150 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
151 	const int base = GSCALE_IDENTITY;
152 	int dx, dy;
153 	int oldScale;
154 	int oldMode;
155 	int i;
156 	int frameCount;
157 	int zoomCorner;
158 	RECT frameRect;
159 	RECT repairRect;
160 	TimeCount NextTime;
161 
162 	frameCount = ZOOM_TIME / (ONE_SECOND / ZOOM_RATE);
163 
164 	// Planet zoom in from a randomly chosen corner
165 	zoomCorner = TFB_Random ();
166 	dx = 1 - (zoomCorner & 1) * 2;
167 	dy = 1 - (zoomCorner & 2);
168 
169 	if (Orbit->ObjectFrame)
170 		GetFrameRect (Orbit->ObjectFrame, &frameRect);
171 	else
172 		GetFrameRect (Orbit->SphereFrame, &frameRect);
173 	repairRect = frameRect;
174 
175 	for (i = 0; i <= frameCount; ++i)
176 	{
177 		double scale;
178 		POINT pt;
179 
180 		NextTime = GetTimeCounter () + (ONE_SECOND / ZOOM_RATE);
181 
182 		// Use 1 + e^-2 - e^(-2x / frameCount)) function to get a decelerating
183 		// zoom like the one 3DO does (supposedly)
184 		if (i < frameCount)
185 			scale = 1.134 - exp (-2.0 * i / frameCount);
186 		else
187 			scale = 1.0; // final frame
188 
189 		// start from beyond the screen
190 		pt.x = SIS_SCREEN_WIDTH / 2 + (int) (dx * (1.0 - scale)
191 				* (SIS_SCREEN_WIDTH * 6 / 10) + 0.5);
192 		pt.y = PLANET_ORG_Y + (int) (dy * (1.0 - scale)
193 				* (SCAN_SCREEN_HEIGHT * 6 / 10) + 0.5);
194 
195 		SetContext (PlanetContext);
196 
197 		BatchGraphics ();
198 		if (i > 0)
199 			RepairBackRect (&repairRect);
200 
201 		oldMode = SetGraphicScaleMode (TFB_SCALE_BILINEAR);
202 		oldScale = SetGraphicScale ((int)(base * scale + 0.5));
203 		DrawPlanetSphere (pt.x, pt.y);
204 		SetGraphicScale (oldScale);
205 		SetGraphicScaleMode (oldMode);
206 
207 		UnbatchGraphics ();
208 
209 		repairRect.corner.x = pt.x + frameRect.corner.x;
210 		repairRect.corner.y = pt.y + frameRect.corner.y;
211 
212 		PrepareNextRotationFrame ();
213 
214 		SleepThreadUntil (NextTime);
215 	}
216 }
217 
218 void
RotatePlanetSphere(BOOLEAN keepRate)219 RotatePlanetSphere (BOOLEAN keepRate)
220 {
221 	static TimeCount NextTime;
222 	TimeCount Now = GetTimeCounter ();
223 
224 	if (keepRate && Now < NextTime)
225 		return; // not time yet
226 
227 	NextTime = Now + PLANET_ROTATION_RATE;
228 	DrawDefaultPlanetSphere ();
229 
230 	PrepareNextRotationFrame ();
231 }
232 
233 static void
renderTintFrame(Color tintColor)234 renderTintFrame (Color tintColor)
235 {
236 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
237 	CONTEXT oldContext;
238 	DrawMode mode, oldMode;
239 	STAMP s;
240 	RECT r;
241 
242 	oldContext = SetContext (OffScreenContext);
243 	SetContextFGFrame (Orbit->TintFrame);
244 	SetContextClipRect (NULL);
245 	// get the rect of the whole context (or our frame really)
246 	GetContextClipRect (&r);
247 
248 	// copy the topo frame to the tint frame
249 	s.origin.x = 0;
250 	s.origin.y = 0;
251 	s.frame = pSolarSysState->TopoFrame;
252 	DrawStamp (&s);
253 
254 	// apply the tint
255 #ifdef USE_ADDITIVE_SCAN_BLIT
256 	mode = MAKE_DRAW_MODE (DRAW_ADDITIVE, DRAW_FACTOR_1 / 2);
257 #else
258 	mode = MAKE_DRAW_MODE (DRAW_ALPHA, DRAW_FACTOR_1 / 2);
259 #endif
260 	oldMode = SetContextDrawMode (mode);
261 	SetContextForeGroundColor (tintColor);
262 	DrawFilledRectangle (&r);
263 	SetContextDrawMode (oldMode);
264 
265 	SetContext (oldContext);
266 }
267 
268 // tintColor.a is ignored
269 void
DrawPlanet(int tintY,Color tintColor)270 DrawPlanet (int tintY, Color tintColor)
271 {
272 	STAMP s;
273 	PLANET_ORBIT *Orbit = &pSolarSysState->Orbit;
274 
275 	s.origin.x = 0;
276 	s.origin.y = 0;
277 
278 	BatchGraphics ();
279 	if (sameColor (tintColor, BLACK_COLOR))
280 	{	// no tint -- just draw the surface
281 		s.frame = pSolarSysState->TopoFrame;
282 		DrawStamp (&s);
283 	}
284 	else
285 	{	// apply different scan type tints
286 		FRAME tintFrame = Orbit->TintFrame;
287 		int height = GetFrameHeight (tintFrame);
288 
289 		if (!sameColor (tintColor, Orbit->TintColor))
290 		{
291 			renderTintFrame (tintColor);
292 			Orbit->TintColor = tintColor;
293 		}
294 
295 		if (tintY < height - 1)
296 		{	// untinted piece showing, draw regular topo
297 			s.frame = pSolarSysState->TopoFrame;
298 			DrawStamp (&s);
299 		}
300 
301 		if (tintY >= 0)
302 		{	// tinted piece showing, draw tinted piece
303 			RECT oldClipRect;
304 			RECT clipRect;
305 
306 			// adjust cliprect to confine the tint
307 			GetContextClipRect (&oldClipRect);
308 			clipRect = oldClipRect;
309 			clipRect.extent.height = tintY + 1;
310 			SetContextClipRect (&clipRect);
311 			s.frame = tintFrame;
312 			DrawStamp (&s);
313 			SetContextClipRect (&oldClipRect);
314 		}
315 	}
316 	UnbatchGraphics ();
317 }
318 
319