1 /* ResidualVM - A 3D game interpreter
2  *
3  * ResidualVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the AUTHORS
5  * file distributed with this source distribution.
6  *
7  * Additional copyright for this file:
8  * Copyright (C) 1999-2000 Revolution Software Ltd.
9  * This code is based on source code created by Revolution Software,
10  * used with permission.
11  *
12  * This program is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU General Public License
14  * as published by the Free Software Foundation; either version 2
15  * of the License, or (at your option) any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
25  *
26  */
27 
28 #include "engines/icb/p4.h"
29 #include "engines/icb/remora.h"
30 #include "engines/icb/surface_manager.h"
31 #include "engines/icb/global_objects.h"
32 #include "engines/icb/sound.h"
33 #include "engines/icb/sound/music_manager.h"
34 #include "engines/icb/direct_input.h"
35 #include "engines/icb/res_man.h"
36 #include "engines/icb/floors.h"
37 #include "engines/icb/mission.h"
38 #include "engines/icb/icb.h"
39 
40 #include "common/util.h"
41 
42 namespace ICB {
43 
44 // This array defines alternate colour schemes for the Remora.
45 enum ColourIndex {
46 	CI_HEADING = 0,       // Colour of heading text.
47 	CI_WARNING,           // Colour of warning text.
48 	CI_OPTION,            // Colour of option text.
49 	CI_PARAGRAPH,         // Colour of paragraph text.
50 	CI_BACKGROUND,        // Colour of the backdrop to the scanner and behind the text.
51 	CI_GRID,              // Colour of grid
52 	CI_CLIP,              // Colour of the clip area above and below the text.
53 	CI_FLOORS,            // Colour of floor rectangles in the scanner.
54 	CI_BARRIERS,          // Colour of barriers in the scanner.
55 	CI_BEAM_LEADING,      // Colour of the leading edge of the beam in the scanner.
56 	CI_BEAM_TRAILING,     // Colour of the trailing edge of the beam in the scanner.
57 	CI_ROBOT,             // Colour of alive robots in the scanner.
58 	CI_DEAD_ROBOT,        // Colour of dead robots in the scanner.
59 	CI_HUMAN,             // Colour of alive humans in the scanner.
60 	CI_DEAD_HUMAN,        // Colour of dead humans in the scanner.
61 	CI_RECHARGE,          // Colour of the recharge symbols in the scanner.
62 	CI_RECHARGE_ARMED,    // Colour of the armed recharge symbols in the scanner.
63 	CI_PLAYER,            // Colour of the player symbol in the scanner.
64 	CI_DOOR_OPEN,         // Colour of an open door.
65 	CI_DOOR_CLOSED,       // Colour of a closed door.
66 	CI_PROGRESS_HEAD,     // Colour of progress bar's leading edge.
67 	CI_PROGRESS_TAIL,     // Colour of progress bar's trailing edge.
68 	CI_M08_LOCKED_DOOR,   // Colour of the armed recharge symbols in the scanner.
69 	CI_M08_UNLOCKED_DOOR, // Colour of the armed recharge symbols in the scanner.
70 	CI_M08_BARRIERS       // Colour of barriers in M08 lock control interface.
71 };                            // Allows the cardinality of the enum to be got.
72 
73 // This macro shortens the syntax for accessing the paletts.
74 #define REMPAL(c, v) (pnRemoraColour[m_nCurrentPalette][c][v])
75 
76 const uint8 pnRemoraColour[REMORA_NUM_COLOUR_SCHEMES][REMORA_COLOURS_PER_SCHEME][3] = {
77 	// Pallete 0.
78 	{{0, 180, 241},   // CI_HEADING
79 	 {255, 255, 255}, // CI_WARNING
80 	 {0, 221, 160},   // CI_OPTION
81 	 {0, 140, 200},   // CI_PARAGRAPH
82 	 {0, 3, 35},      // CI_BACKGROUND
83 	 {30, 60, 120},   // CI_GRID
84 	 {0, 3, 35},      // CI_CLIP
85 	 {2, 55, 128},    // CI_FLOORS
86 	 {50, 80, 255},   // CI_BARRIERS
87 	 {0, 20, 180},    // CI_BEAM_LEADING
88 	 {0, 0, 0},       // CI_BEAM_TRAILING
89 	 {225, 34, 5},    // CI_ROBOT
90 	 {160, 10, 0},    // CI_DEAD_ROBOT
91 	 {2, 24, 200},    // CI_HUMAN
92 	 {1, 10, 140},    // CI_DEAD_HUMAN
93 	 {38, 255, 0},    // CI_RECHARGE
94 	 {255, 20, 20},   // CI_RECHARGE_ARMED
95 	 {255, 255, 255}, // CI_PLAYER
96 	 {38, 255, 0},    // CI_DOOR_OPEN
97 	 {255, 125, 0},   // CI_DOOR_CLOSED
98 	 {255, 255, 255}, // CI_PROGRESS_HEAD
99 	 {15, 50, 200},   // CI_PROGRESS_TAIL
100 	 {255, 30, 30},   // CI_M08_LOCKED_DOOR
101 	 {7, 141, 23},    // CI_M08_UNLOCKED_DOOR
102 	 {0, 90, 220}},   // CI_M08_BARRIERS
103 
104 	// Pallete 1.
105 	{{0, 241, 180},   // CI_HEADING
106 	 {255, 255, 255}, // CI_WARNING
107 	 {0, 160, 221},   // CI_OPTION
108 	 {0, 200, 140},   // CI_PARAGRAPH
109 	 {0, 35, 10},     // CI_BACKGROUND
110 	 {0, 220, 80},    // CI_GRID
111 	 {0, 35, 10},     // CI_CLIP
112 	 {0, 75, 15},     // CI_FLOORS
113 	 {0, 220, 80},    // CI_BARRIERS
114 	 {40, 255, 100},  // CI_BEAM_LEADING
115 	 {0, 20, 0},      // CI_BEAM_TRAILING
116 	 {255, 34, 5},    // CI_ROBOT
117 	 {170, 10, 0},    // CI_DEAD_ROBOT
118 	 {5, 225, 34},    // CI_HUMAN
119 	 {5, 160, 10},    // CI_DEAD_HUMAN
120 	 {238, 156, 0},   // CI_RECHARGE
121 	 {255, 20, 20},   // CI_RECHARGE_ARMED
122 	 {255, 255, 255}, // CI_PLAYER
123 	 {238, 156, 0},   // CI_DOOR_OPEN
124 	 {238, 4, 0},     // CI_DOOR_CLOSED
125 	 {255, 255, 255}, // CI_PROGRESS_HEAD
126 	 {5, 200, 50},    // CI_PROGRESS_TAIL
127 	 {255, 30, 30},   // CI_M08_LOCKED_DOOR
128 	 {7, 235, 23},    // CI_M08_UNLOCKED_DOOR
129 	 {0, 230, 90}},   // CI_M08_BARRIERS
130 
131 	// Pallete 2.
132 	{{247, 236, 23},  // CI_HEADING
133 	 {252, 21, 10},   // CI_WARNING
134 	 {235, 165, 33},  // CI_OPTION
135 	 {233, 238, 30},  // CI_PARAGRAPH
136 	 {85, 45, 0},     // CI_BACKGROUND
137 	 {216, 151, 1},   // CI_GRID
138 	 {85, 45, 0},     // CI_CLIP
139 	 {133, 84, 1},    // CI_FLOORS
140 	 {216, 151, 1},   // CI_BARRIERS
141 	 {224, 200, 123}, // CI_BEAM_LEADING
142 	 {35, 20, 0},     // CI_BEAM_TRAILING
143 	 {250, 140, 15},  // CI_ROBOT
144 	 {176, 94, 2},    // CI_DEAD_ROBOT
145 	 {24, 175, 3},    // CI_HUMAN
146 	 {15, 104, 2},    // CI_DEAD_HUMAN
147 	 {247, 236, 23},  // CI_RECHARGE
148 	 {255, 20, 20},   // CI_RECHARGE_ARMED
149 	 {255, 255, 255}, // CI_PLAYER
150 	 {197, 47, 20},   // CI_DOOR_OPEN
151 	 {235, 47, 20},   // CI_DOOR_CLOSED
152 	 {255, 255, 255}, // CI_PROGRESS_HEAD
153 	 {216, 137, 8},   // CI_PROGRESS_TAIL
154 	 {255, 30, 30},   // CI_M08_LOCKED_DOOR
155 	 {247, 236, 23},  // CI_M08_UNLOCKED_DOOR
156 	 {247, 236, 23}}  // CI_M08_BARRIERS
157 };
158 
159 // struct Point2DColoured
160 // This is used in the Gouraud drawing routines.
161 typedef struct {
162 	int32 x, y;
163 	int32 u, v;
164 	uint8 r, g, b;
165 
166 } Point2DColoured;
167 
168 // struct Span
169 // This is used in the Gouraud drawing routines.
170 typedef struct {
171 	int32 x0, x1;
172 	uint8 r0, g0, b0;
173 	uint8 r1, g1, b1;
174 
175 } Span;
176 Span remora_spans[REMORA_MAX_YS];
177 
178 void DrawGouraudQuad(uint32 nX0, uint32 nY0, uint32 nX1, uint32 nY1, uint32 nX2, uint32 nY2, uint32 nX3, uint32 nY3, uint8 nRed0, uint8 nGreen0, uint8 nBlue0, uint8 nRed1,
179 					 uint8 nGreen1, uint8 nBlue1, uint8 nRed2, uint8 nGreen2, uint8 nBlue2, uint8 nRed3, uint8 nGreen3, uint8 nBlue3, int32 nOpacity);
180 
181 void DrawGouraudTriangle(uint32 x0, uint32 y0, uint32 x1, uint32 y1, uint32 x2, uint32 y2, uint8 r0, uint8 g0, uint8 b0, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2,
182 						 int32 nOpacity);
183 
184 // These values are used for mapping game-world points to the Remora's screen.
185 float fRotateCos, fRotateSin;     // Used to store cosine and sine for rotating, so we don't have to keep working it out.
186 float fXDrawScale, fZDrawScale;   // Scaling factors for the display.
187 float fXDrawOrigin, fZDrawOrigin; // The origin for displaying points (in the game world).
188 
InitialiseGameToRemora(float fPan,uint32 nCurrentXScale,uint32 nCurrentZScale,int32 nCurrentXOrigin,int32 nCurrentZOrigin)189 inline void InitialiseGameToRemora(float fPan, uint32 nCurrentXScale, uint32 nCurrentZScale, int32 nCurrentXOrigin, int32 nCurrentZOrigin) {
190 	// These terms are needed to take account of the player's direction of looking.
191 	fRotateCos = (float)cos(fPan * TWO_PI + M_PI);
192 	fRotateSin = (float)sin(fPan * TWO_PI + M_PI);
193 
194 	// The scale is a number between 1 and 256 (probably clamped to a smaller range).  The formulat for
195 	// scaling this to the Remora's screen is P = (W * (scale/512) ) / 4, where P is a point on the Remora
196 	// screen and W is a point in the game world.
197 	fXDrawScale = (float)nCurrentXScale / 2048.0f;
198 	fZDrawScale = (float)nCurrentZScale / 2048.0f;
199 	fXDrawOrigin = (float)nCurrentXOrigin;
200 	fZDrawOrigin = (float)nCurrentZOrigin;
201 }
202 
GameToRemora(float & fX,float & fZ)203 inline void GameToRemora(float &fX, float &fZ) {
204 	float fTempX, fTempZ;
205 
206 	// First calculate the point relative to the origin for the display.
207 	fTempX = fX - fXDrawOrigin;
208 	fTempZ = fZ - fZDrawOrigin;
209 
210 	// Now rotate to take account of the viewing direction.
211 	fX = fTempX * fRotateCos + fTempZ * -fRotateSin;
212 	fZ = fTempX * fRotateSin + fTempZ * fRotateCos;
213 
214 	// Now scale the point to fit on the screen.
215 	fX *= fXDrawScale;
216 	fZ *= fZDrawScale;
217 
218 	// Now work out the position of the point relative to the centre of the drawn screen.
219 	fX += REMORA_SCREEN_CENTRE_X;
220 	fZ += REMORA_SCREEN_CENTRE_Z;
221 }
222 
ActivateRemora(RemoraMode eMode)223 void _remora::ActivateRemora(RemoraMode eMode) {
224 	if (g_theMusicManager)
225 		g_theMusicManager->StopMusic();
226 
227 	// Play the Remora-activate sound.
228 	RegisterSoundSpecial(activateRemoraSfx, activateRemoraDesc, 127, 0);
229 
230 	// Make a rectangle for the drawable screen area (used to come from a sprite).
231 	m_sScreenRectangle = MakeRECTFromSpriteSizes(REMORA_DRAWABLE_SCREEN_LEFT, REMORA_DRAWABLE_SCREEN_TOP, REMORA_DRAWABLE_SCREEN_WIDTH, REMORA_DRAWABLE_SCREEN_HEIGHT);
232 
233 	// Call the common PC/PSX set-up code.  (Note that this code relies on m_sScreenRectangle being set.)
234 	SetCommonActivateInfo(eMode);
235 
236 	// Grab the memory for text formatting.
237 	GrabTextFormattingMemory();
238 
239 	// Only the PC has this pulse counter.
240 	m_nPulseHighlight = 0;
241 
242 	// Here we set some text formatting parameters depending on whether the PC is simulating PSX
243 	// text formatting.
244 	if (m_bFormatForPSX) {
245 		m_nDisplayedTextRows = REMORA_PSXFORMAT_DISPLAYED_ROWS;
246 		m_nCharacterSpacing = REMORA_PSXFORMAT_CHAR_SPACING;
247 		m_nRowSpacing = REMORA_PSXFORMAT_ROW_SPACING;
248 	} else {
249 		m_nDisplayedTextRows = REMORA_PC_DISPLAYED_TEXT_ROWS;
250 		m_nCharacterSpacing = 0;
251 		m_nRowSpacing = 0;
252 	}
253 
254 	Zdebug("Activated Remora (mode = %d)", eMode);
255 }
256 
DoPlatformSpecificInitialisation()257 void _remora::DoPlatformSpecificInitialisation() {
258 	// Allocate a text buffer if one hasn't been allocated already.
259 	if (!m_pDisplayBuffer)
260 		m_pDisplayBuffer = new _remora_line[REMORA_TEXT_BUFFER_ROWS];
261 }
262 
GrabTextFormattingMemory()263 void _remora::GrabTextFormattingMemory() {
264 	// Allocate a text buffer if one hasn't been allocated already.
265 	if (!m_pDisplayBuffer)
266 		m_pDisplayBuffer = new _remora_line[REMORA_TEXT_BUFFER_ROWS];
267 }
268 
ReleaseTextFormattingMemory()269 void _remora::ReleaseTextFormattingMemory() {
270 	if (m_pDisplayBuffer) {
271 		delete[] m_pDisplayBuffer;
272 		m_pDisplayBuffer = NULL;
273 	}
274 }
275 
SetUpRemora()276 void _remora::SetUpRemora() {
277 	Zdebug("Entered _remora::SetUpRemora()");
278 
279 	PXTRY
280 	// Create a surface for the remora to draw into
281 	m_nRemoraSurfaceID = surface_manager->Create_new_surface("RemoraBB", SCREEN_WIDTH, SCREEN_DEPTH, SYSTEM);
282 
283 	// Can make the background size now.
284 	m_sBackgroundRectangle = MakeRECTFromSpriteSizes(0, 0, REMORA_SCREEN_WIDTH, REMORA_SCREEN_HEIGHT);
285 
286 	// Create a surface for the whole screen and clear it.
287 	uint32 dwFillPixel = GetPen(m_nCurrentPalette, CI_BACKGROUND);
288 	surface_manager->Blit_fillfx(m_nRemoraSurfaceID, &m_sBackgroundRectangle, dwFillPixel);
289 
290 	// Prepare the Remora casing.
291 	SetUpSurfaceForBitmap(REMORA_BITMAP_REMORA, m_sCasingSourceRectangle, m_sCasingTargetRectangle, m_nCasingSurfaceID);
292 
293 	// Draw the the casing.
294 	surface_manager->Blit_surface_to_surface(m_nCasingSurfaceID, m_nRemoraSurfaceID, &m_sCasingSourceRectangle, &m_sCasingTargetRectangle);
295 
296 	// Set up the sprites that flash when text goes off the top or botttom of the screen.
297 	SetUpSurfaceForBitmap(REMORA_BITMAP_MORE_UP, m_sMoreUpSourceRectangle, m_sMoreUpTargetRectangle, m_nMoreUpSurfaceID);
298 	SetUpSurfaceForBitmap(REMORA_BITMAP_MORE_DOWN, m_sMoreDownSourceRectangle, m_sMoreDownTargetRectangle, m_nMoreDownSurfaceID);
299 
300 	PXCATCH
301 
302 	Tdebug(EXCEPTION_LOG, "Exception in _remora::SetUpRemora()");
303 	Fatal_error("Exception in _remora::SetUpRemora()");
304 
305 	PXENDCATCH
306 
307 	Zdebug("Leaving _remora::SetUpRemora()...");
308 }
309 
CloseDownRemora()310 void _remora::CloseDownRemora() {
311 	Zdebug("Closing down Remora ...");
312 
313 	// Dump the surfaces I've created.
314 	Zdebug("Killing casing surface - ID=%d", m_nCasingSurfaceID);
315 	surface_manager->Kill_surface(m_nCasingSurfaceID);
316 
317 	Zdebug("Killing casing surface - ID=%d", m_nMoreUpSurfaceID);
318 	surface_manager->Kill_surface(m_nMoreUpSurfaceID);
319 
320 	Zdebug("Killing casing surface - ID=%d", m_nMoreDownSurfaceID);
321 	surface_manager->Kill_surface(m_nMoreDownSurfaceID);
322 
323 	Zdebug("Killing Remora Back Buffer surface - ID=%d", m_nRemoraSurfaceID);
324 	surface_manager->Kill_surface(m_nRemoraSurfaceID);
325 
326 	Zdebug("Remora closed down");
327 }
328 
DrawRemora()329 void _remora::DrawRemora() {
330 	LRECT sBlankingRect;
331 	RemoraMode eModeToDraw;
332 	uint8 nRed, nGreen, nBlue;
333 
334 	Zdebug("_remora::DrawRemora()");
335 
336 	PXTRY
337 	uint32 oldTextSurface = MS->text_bloc->GetSurface();
338 	MS->text_bloc->SetSurface(m_nRemoraSurfaceID);
339 
340 	// Start by wiping the drawing area.
341 	sBlankingRect.left = REMORA_SCREEN_ORIGIN_X;
342 	sBlankingRect.right = REMORA_SCREEN_ORIGIN_X + REMORA_SCREEN_WIDTH;
343 	sBlankingRect.top = REMORA_SCREEN_ORIGIN_Y;
344 	sBlankingRect.bottom = REMORA_SCREEN_ORIGIN_Y + REMORA_SCREEN_HEIGHT;
345 
346 	uint32 dwFillPixel = GetPen(m_nCurrentPalette, CI_BACKGROUND);
347 
348 	surface_manager->Blit_fillfx(m_nRemoraSurfaceID, &sBlankingRect, dwFillPixel);
349 
350 	// If the mode changed flag is set, it means a new mode has been set but the logic has
351 	// not yet responded to it.  This means the old graphics won't have been closed down and
352 	// the new ones won't have been initialised.  In this case, we just continue to draw the
353 	// old mode until the logic catches up.
354 	if (m_bModeChanged) {
355 		eModeToDraw = m_eLastMode;
356 		m_nPulseHighlight = 0;
357 	} else {
358 		eModeToDraw = m_eCurrentMode;
359 	}
360 
361 	// See what mode we currently have to draw.
362 	switch (eModeToDraw) {
363 	case MOTION_SCAN:
364 		DrawGrid();
365 		DrawWideScan();
366 		DrawHeadingText();
367 		DrawHeaderAndFooterLines();
368 		DrawPulse();
369 		SetTextColour(voice_over_red, voice_over_green, voice_over_blue);
370 		DrawVoiceOverText();
371 		DrawEmailWaiting();
372 		break;
373 
374 	case INFRA_RED_LINK:
375 		DrawGrid();
376 		DrawScreenText();
377 		ClipTopAndBottom();
378 		DrawHeadingText();
379 		DrawMoreUpDownArrows();
380 		DrawHeaderAndFooterLines();
381 		DrawIRLinkPulse();
382 		DrawVoiceOverText();
383 		DrawEmailWaiting();
384 		DrawProgressBar();
385 		break;
386 
387 	case DATABASE:
388 		DrawGrid();
389 		DrawScreenText();
390 		ClipTopAndBottom();
391 		DrawHeadingText();
392 		DrawMoreUpDownArrows();
393 		DrawHeaderAndFooterLines();
394 		DrawPulse();
395 		DrawVoiceOverText();
396 		DrawEmailWaiting();
397 		DrawProgressBar();
398 		break;
399 
400 	case COMMUNICATIONS:
401 		DrawGrid();
402 		DrawScreenText();
403 		ClipTopAndBottom();
404 		DrawHeadingText();
405 		DrawMoreUpDownArrows();
406 		DrawHeaderAndFooterLines();
407 		DrawIRLinkPulse();
408 		DrawVoiceOverText();
409 		DrawEmailWaiting();
410 		DrawProgressBar();
411 		break;
412 
413 	case M08_LOCK_CONTROL:
414 		DrawGrid();
415 		DrawM08LockControl();
416 		DrawHeadingText();
417 		DrawPulse();
418 		DrawHeaderAndFooterLines();
419 		DrawVoiceOverText();
420 		DrawEmailWaiting();
421 		break;
422 
423 	default:
424 		// This should never happen.
425 		Fatal_error("Invalid Remora mode %d in _remora::DrawRemora()", eModeToDraw);
426 
427 	} // end switch
428 
429 	// Put the shadow on the edge of the screen.
430 	nRed = pnRemoraColour[m_nCurrentPalette][CI_BACKGROUND][CI_RED];
431 	nGreen = pnRemoraColour[m_nCurrentPalette][CI_BACKGROUND][CI_GREEN];
432 	nBlue = pnRemoraColour[m_nCurrentPalette][CI_BACKGROUND][CI_BLUE];
433 
434 	DrawGouraudQuad(75, 60, 110, 60, 75, REMORA_SCREEN_CENTRE_Z, 95, REMORA_SCREEN_CENTRE_Z, 0, 0, 0, 0, 0, 0, 0, 0, 0, nRed, nGreen, nBlue, 115);
435 
436 	DrawGouraudQuad(75, REMORA_SCREEN_CENTRE_Z + 1, 95, REMORA_SCREEN_CENTRE_Z + 1, 75, REMORA_SCREEN_HEIGHT - 70, 110, REMORA_SCREEN_HEIGHT - 70, 0, 0, 0, nRed, nGreen, nBlue,
437 	                0, 0, 0, nRed, nGreen, nBlue, 115);
438 
439 	DrawGouraudQuad(110, 60, REMORA_SCREEN_WIDTH - 60, 60, 107, 75, REMORA_SCREEN_WIDTH - 60, 75, 0, 0, 0, 0, 0, 0, nRed, nGreen, nBlue, nRed, nGreen, nBlue, 115);
440 
441 	// Here we draw the case on again, in case anything has gone over the screen edge.
442 	surface_manager->Blit_surface_to_surface(m_nCasingSurfaceID, m_nRemoraSurfaceID, &m_sCasingSourceRectangle, &m_sCasingTargetRectangle, DDBLT_KEYSRC);
443 
444 	// Ok, now blit the remora back buffer across.
445 	surface_manager->Blit_surface_to_surface(m_nRemoraSurfaceID, working_buffer_id, &full_rect, &full_rect, 0);
446 
447 	Zdebug("Leaving _remora::DrawRemora()");
448 
449 	MS->text_bloc->SetSurface(oldTextSurface);
450 
451 	PXCATCH
452 
453 	Fatal_error("Exception in _remora::DrawRemora()");
454 
455 	PXENDCATCH
456 }
457 
SetUpWideScan()458 void _remora::SetUpWideScan() {}
459 
DrawWideScan()460 void _remora::DrawWideScan() {
461 	_rgb oLineColour;
462 
463 	// Set a line colour.
464 	oLineColour.red = REMPAL(CI_BARRIERS, CI_RED);
465 	oLineColour.green = REMPAL(CI_BARRIERS, CI_GREEN);
466 	oLineColour.blue = REMPAL(CI_BARRIERS, CI_BLUE);
467 
468 	// Set up the function to map game points to the Remora's screen.
469 	InitialiseGameToRemora(m_fPlayerPan, m_nCurrentZoom, m_nCurrentZoom, m_nPlayerX, m_nPlayerZ);
470 
471 	// Draw the non-animating barriers.
472 	DrawStaticBarriers(oLineColour);
473 
474 	// Set a line colour.
475 	oLineColour.red = REMPAL(CI_DOOR_OPEN, CI_RED);
476 	oLineColour.green = REMPAL(CI_DOOR_OPEN, CI_GREEN);
477 	oLineColour.blue = REMPAL(CI_DOOR_OPEN, CI_BLUE);
478 
479 	// Draw the animating barriers.  At some point, we are going to have to provide some visual
480 	// indication of whether or not they are open or closed.
481 	DrawAnimatingBarriers(oLineColour);
482 
483 	// Draw the floor rectangles.
484 	DrawFloorRectangles();
485 
486 	// Draw other object symbols.
487 	DrawObjects();
488 
489 	// Draw on the scanning beam and add interference.
490 	DrawScanBeam();
491 
492 	// Put the crosshairs on.
493 	DrawCrosshairs();
494 
495 	// Add the flash effect if it is active.
496 	if (m_nScreenFlashCount > 0)
497 		DrawEMPEffect();
498 }
499 
DrawFloorRectangles() const500 void _remora::DrawFloorRectangles() const {
501 	uint32 i;
502 	uint32 nNumFloors;
503 	_floor *pFloor;
504 	_rect sRect;
505 	int32 nX0, nZ0, nX1, nZ1, nX2, nZ2, nX3, nZ3;
506 	float fX0, fZ0, fX1, fZ1, fX2, fZ2, fX3, fZ3;
507 	uint8 nRed, nGreen, nBlue;
508 
509 	PXTRY
510 
511 	// Get the colour to draw them.
512 	nRed = pnRemoraColour[m_nCurrentPalette][CI_FLOORS][CI_RED];
513 	nGreen = pnRemoraColour[m_nCurrentPalette][CI_FLOORS][CI_GREEN];
514 	nBlue = pnRemoraColour[m_nCurrentPalette][CI_FLOORS][CI_BLUE];
515 
516 	// Draw floor rectangles.
517 	nNumFloors = MS->floor_def->Fetch_number_of_floors();
518 
519 	for (i = 0; i < nNumFloors; ++i) {
520 		// Get the floor.
521 		pFloor = MS->floor_def->Fetch_floor_number(i);
522 
523 		// See if it is in the height range we are drawing.
524 		if (((PXfloat)pFloor->base_height >= m_nIncludedFloor) && ((PXfloat)pFloor->base_height <= m_nIncludedCeiling)) {
525 			// Get the actual rectangle.
526 			sRect = pFloor->rect;
527 
528 			// Map the points onto the Remora's screen.
529 			fX0 = (float)sRect.x1;
530 			fZ0 = (float)sRect.z1;
531 
532 			fX1 = (float)sRect.x2;
533 			fZ1 = (float)sRect.z1;
534 
535 			fX2 = (float)sRect.x1;
536 			fZ2 = (float)sRect.z2;
537 
538 			fX3 = (float)sRect.x2;
539 			fZ3 = (float)sRect.z2;
540 
541 			GameToRemora(fX0, fZ0);
542 			GameToRemora(fX1, fZ1);
543 			GameToRemora(fX2, fZ2);
544 			GameToRemora(fX3, fZ3);
545 
546 			nX0 = (int32)fX0;
547 			nZ0 = (int32)fZ0;
548 
549 			nX1 = (int32)fX1;
550 			nZ1 = (int32)fZ1;
551 
552 			nX2 = (int32)fX2;
553 			nZ2 = (int32)fZ2;
554 
555 			nX3 = (int32)fX3;
556 			nZ3 = (int32)fZ3;
557 
558 			DrawGouraudQuad(nX0, nZ0, nX1, nZ1, nX2, nZ2, nX3, nZ3, nRed, nGreen, nBlue, nRed, nGreen, nBlue, nRed, nGreen, nBlue, nRed, nGreen, nBlue,
559 			                REMORA_BRIGHT_MIN);
560 		}
561 	}
562 
563 	PXCATCH
564 
565 	Fatal_error("Exception in _remora::DrawFloorRectangles()");
566 
567 	PXENDCATCH
568 }
569 
DrawStaticBarriers(_rgb oLineColour) const570 void _remora::DrawStaticBarriers(_rgb oLineColour) const {
571 	uint32 i, j, k;
572 	float fX1, fZ1, fX2, fZ2;
573 	int32 nX1, nZ1, nX2, nZ2;
574 	_barrier_cube *pBarrierCube;
575 	_barrier_slice *pSlice;
576 	uint32 *pBarrierArray;
577 	uint32 nBarrierCubeOffset;
578 	uint32 nBarrierIndex;
579 	_route_barrier *pBarrier;
580 
581 	PXTRY
582 
583 	// Loop for all the slices we have to draw (mostly will be only one).
584 	for (i = 0; i < m_nNumCurrentFloorRanges; ++i) {
585 		// Get the pointer to the slice.
586 		pSlice = m_pSlices[i];
587 
588 		// Loop for all the cubes on the slice we are in.
589 		for (j = 0; j < pSlice->num_cubes; ++j) {
590 			// Get to the barriers for this cube.
591 			nBarrierCubeOffset = pSlice->offset_cubes[j];
592 			pBarrierCube = (_barrier_cube *)((uint8 *)pSlice + nBarrierCubeOffset);
593 			pBarrierArray = (uint32 *)((uint8 *)pSlice + pBarrierCube->barriers);
594 
595 			// Draw the barriers for this cube.
596 			for (k = 0; k < (uint32)pBarrierCube->num_barriers; ++k) {
597 				// Work out the barrier index.
598 				nBarrierIndex = pBarrierArray[k];
599 
600 				// Get the actual barrier.
601 				pBarrier = MS->session_barriers->Fetch_barrier(nBarrierIndex);
602 
603 				// Set up the vector, the player relative bit has been done in the translation_matrix setting up
604 				fX1 = pBarrier->x1();
605 				fZ1 = pBarrier->z1();
606 				fX2 = pBarrier->x2();
607 				fZ2 = pBarrier->z2();
608 
609 				GameToRemora(fX1, fZ1);
610 				GameToRemora(fX2, fZ2);
611 
612 				nX1 = (int32)fX1;
613 				nZ1 = (int32)fZ1;
614 				nX2 = (int32)fX2;
615 				nZ2 = (int32)fZ2;
616 
617 				if (CohenSutherland(m_sScreenRectangle, nX1, nZ1, nX2, nZ2, TRUE8))
618 					RemoraLineDraw(nX1, nZ1, nX2, nZ2, oLineColour, oLineColour, REMORA_LINE_FUZZ);
619 			}
620 		}
621 	}
622 
623 	PXCATCH
624 
625 	Fatal_error("Exception in _remora::DrawStaticBarriers()");
626 
627 	PXENDCATCH
628 }
629 
DrawAnimatingBarriers(_rgb oLineColour) const630 void _remora::DrawAnimatingBarriers(_rgb oLineColour) const {
631 	uint32 i, j, k, m;
632 	float fX1, fZ1, fX2, fZ2;
633 	int32 nX1, nZ1, nX2, nZ2;
634 	uint32 nBarrierIndex;
635 	_route_barrier *pBarrier;
636 	uint32 nPropID;
637 	uint32 nPropState;
638 	uint32 nBarriersPerState;
639 	uint16 *pnBarriers;
640 	uint32 nSliceIndex;
641 
642 	PXTRY
643 
644 	// Loop for each slice we have to draw.
645 	for (i = 0; i < m_nNumCurrentFloorRanges; ++i) {
646 		// Get the slice index.
647 		nSliceIndex = m_pnSlices[i];
648 
649 		// Loop for all the parent boxes on the slice we are in.
650 		for (j = 0; j < MAX_parents_per_anim_slice; ++j) {
651 			// Check if the parent has any animating barriers.
652 			if (MS->session_barriers->anim_slices[nSliceIndex].anim_parents[j]) {
653 				// Yes it does.
654 				for (k = 0; k < MS->session_barriers->anim_slices[nSliceIndex].anim_parents[j]->num_props; ++k) {
655 					// Get the prop's ID.
656 					nPropID = MS->session_barriers->anim_slices[nSliceIndex].anim_parents[j]->prop_number[k];
657 
658 					// Get its state.
659 					nPropState = MS->prop_state_table[nPropID];
660 
661 					// Get number of barriers per state.
662 					nBarriersPerState = MS->session_barriers->anim_prop_info[nPropID].barriers_per_state;
663 
664 					// Get index into barrier list to first barrier for this state.
665 					nBarrierIndex = nPropState * nBarriersPerState;
666 
667 					// Position a pointer at the start of the barriers for the prop in this state.
668 					pnBarriers = (uint16 *)(&MS->session_barriers->anim_prop_info[nPropID].barrier_list[0]);
669 					pnBarriers += nBarrierIndex;
670 
671 					for (m = 0; m < nBarriersPerState; m++) {
672 						// Get the barrier.
673 						pBarrier = MS->session_barriers->Fetch_barrier(*(pnBarriers++));
674 
675 						// Set up the vector, the player relative bit has been done in the translation_matrix setting up
676 						fX1 = pBarrier->x1();
677 						fZ1 = pBarrier->z1();
678 						fX2 = pBarrier->x2();
679 						fZ2 = pBarrier->z2();
680 
681 						GameToRemora(fX1, fZ1);
682 						GameToRemora(fX2, fZ2);
683 
684 						nX1 = (int32)fX1;
685 						nZ1 = (int32)fZ1;
686 						nX2 = (int32)fX2;
687 						nZ2 = (int32)fZ2;
688 
689 						if (CohenSutherland(m_sScreenRectangle, nX1, nZ1, nX2, nZ2, TRUE8))
690 							RemoraLineDraw(nX1, nZ1, nX2, nZ2, oLineColour, oLineColour, REMORA_LINE_FUZZ);
691 					}
692 				}
693 			}
694 		}
695 	}
696 
697 	PXCATCH
698 
699 	Fatal_error("Exception in _remora::DrawAnimatingBarriers()");
700 
701 	PXENDCATCH
702 }
703 
DrawObjects()704 void _remora::DrawObjects() {
705 	uint32 i;
706 	float fX, fZ;
707 	int32 nX, nY, nZ;
708 	_logic *pGameObject;
709 	ScreenSymbol eSymbol;
710 	static bool8 bArmedRechargeFlash = FALSE8;
711 	char pcSymbolString[2] = {'&', '\0'};
712 
713 	PXTRY
714 
715 	// This is the loop that draws the blips for megas.
716 	for (i = 0; i < MS->total_objects; ++i) {
717 		// Ignore held objects.
718 		if (MS->logic_structs[i]->ob_status != OB_STATUS_HELD) {
719 			// Get the game object.
720 			pGameObject = MS->logic_structs[i];
721 
722 			// Get the x,y,z of the object.
723 			if (pGameObject->image_type == VOXEL) {
724 				fX = pGameObject->mega->actor_xyz.x;
725 				fZ = pGameObject->mega->actor_xyz.z;
726 				nY = (int32)pGameObject->mega->actor_xyz.y;
727 			} else {
728 				fX = pGameObject->prop_xyz.x;
729 				fZ = pGameObject->prop_xyz.z;
730 				nY = (int32)pGameObject->prop_xyz.y;
731 			}
732 
733 			// Ignore objects which are not in the height range we are drawing.
734 			if ((nY >= m_nIncludedFloor) && (nY <= m_nIncludedCeiling)) {
735 				// Map the point to the Remora's screen.
736 				GameToRemora(fX, fZ);
737 
738 				// We only need integers now.
739 				nX = (int32)fX;
740 				nZ = (int32)fZ;
741 
742 				// Here we get an ID back of which symbol to draw for the object
743 				eSymbol = GetSymbolToDrawObject(pGameObject, i);
744 
745 				// Make a string to display.
746 				switch (eSymbol) {
747 				case SS_REMORA:
748 					DrawTriangleSymbol(nX, nZ, CI_PLAYER, REMORA_SYMBOL_SIZE_BIG);
749 					break;
750 
751 				case ALIVE_ROBOT:
752 					DrawSquareSymbol(nX, nZ, CI_ROBOT, REMORA_SYMBOL_SIZE_NORMAL);
753 					break;
754 
755 				case DEAD_ROBOT:
756 					DrawSquareSymbol(nX, nZ, CI_DEAD_ROBOT, REMORA_SYMBOL_SIZE_NORMAL);
757 					break;
758 
759 				case ALIVE_HUMAN:
760 					DrawOctagonSymbol(nX, nZ, CI_HUMAN, REMORA_SYMBOL_SIZE_NORMAL);
761 					break;
762 
763 				case DEAD_HUMAN:
764 					DrawOctagonSymbol(nX, nZ, CI_DEAD_HUMAN, REMORA_SYMBOL_SIZE_NORMAL);
765 					break;
766 
767 				case RECHARGE_UNARMED:
768 					DrawSquareSymbol(nX, nZ, CI_RECHARGE, REMORA_SYMBOL_SIZE_NORMAL);
769 					break;
770 
771 				case RECHARGE_ARMED:
772 					if (bArmedRechargeFlash) {
773 						DrawSquareSymbol(nX, nZ, CI_RECHARGE, REMORA_SYMBOL_SIZE_NORMAL);
774 						bArmedRechargeFlash = FALSE8;
775 					} else {
776 						DrawSquareSymbol(nX, nZ, CI_RECHARGE_ARMED, REMORA_SYMBOL_SIZE_NORMAL);
777 						bArmedRechargeFlash = TRUE8;
778 					}
779 					break;
780 
781 				case DOOR_OPEN:
782 					pcSymbolString[0] = 'O';
783 					SetTextColour(REMPAL(CI_DOOR_OPEN, CI_RED), REMPAL(CI_DOOR_OPEN, CI_GREEN), REMPAL(CI_DOOR_OPEN, CI_BLUE));
784 
785 					MS->Create_remora_text(nX, nZ, pcSymbolString, 0, PIN_AT_CENTRE, 0, 0, REMORA_DISPLAY_WIDTH);
786 					MS->Render_speech(MS->text_bloc);
787 					MS->Kill_remora_text();
788 					break;
789 
790 				case DOOR_CLOSED:
791 					pcSymbolString[0] = 'X';
792 					SetTextColour(REMPAL(CI_DOOR_CLOSED, CI_RED), REMPAL(CI_DOOR_CLOSED, CI_GREEN), REMPAL(CI_DOOR_CLOSED, CI_BLUE));
793 					MS->Create_remora_text(nX, nZ, pcSymbolString, 0, PIN_AT_CENTRE, 0, 0, REMORA_DISPLAY_WIDTH);
794 					MS->Render_speech(MS->text_bloc);
795 					MS->Kill_remora_text();
796 					break;
797 				default:
798 					break;
799 				}
800 			} // end if
801 		} // end if
802 	} // end for
803 
804 	PXCATCH
805 
806 	Fatal_error("Exception in _remora::DrawObjects()");
807 
808 	PXENDCATCH
809 }
810 
DrawScanBeam() const811 void _remora::DrawScanBeam() const {
812 	int32 nLeadingX, nLeadingY;
813 	int32 nTrailingX, nTrailingY;
814 	int32 nOrigin2X, nOrigin2Y;
815 	int32 nLeadingAngle;
816 	uint8 nLeadingR, nLeadingG, nLeadingB;
817 	uint8 nTrailingR, nTrailingG, nTrailingB;
818 
819 	// Work out the angular position of the endpoint of the leading edge.
820 	nLeadingAngle = (m_nScanPan + REMORA_SCAN_WIDTH) % 360;
821 
822 	// Now work out the actual drawing coordinates for the beam ends.
823 	nLeadingX = (int32)(cos(((float)nLeadingAngle / 360.0f) * TWO_PI) * REMORA_SCAN_LENGTH);
824 	nLeadingY = (int32)(sin(((float)nLeadingAngle / 360.0f) * TWO_PI) * REMORA_SCAN_LENGTH);
825 
826 	nLeadingX += REMORA_SCREEN_CENTRE_X;
827 	nLeadingY += REMORA_SCREEN_CENTRE_Z;
828 
829 	nTrailingX = (int32)(cos(((float)m_nScanPan / 360.0f) * TWO_PI) * REMORA_SCAN_LENGTH);
830 	nTrailingY = (int32)(sin(((float)m_nScanPan / 360.0f) * TWO_PI) * REMORA_SCAN_LENGTH);
831 
832 	nTrailingX += REMORA_SCREEN_CENTRE_X;
833 	nTrailingY += REMORA_SCREEN_CENTRE_Z;
834 
835 	nOrigin2X = (int32)(cos(((float)nLeadingAngle / 360.0f) * TWO_PI) * 3);
836 	nOrigin2Y = (int32)(sin(((float)nLeadingAngle / 360.0f) * TWO_PI) * 3);
837 
838 	nOrigin2X += REMORA_SCREEN_CENTRE_X;
839 	nOrigin2Y += REMORA_SCREEN_CENTRE_Z;
840 
841 	// Get the RGB values for the beam.
842 	nLeadingR = REMPAL(CI_BEAM_LEADING, CI_RED);
843 	nLeadingG = REMPAL(CI_BEAM_LEADING, CI_GREEN);
844 	nLeadingB = REMPAL(CI_BEAM_LEADING, CI_BLUE);
845 
846 	nTrailingR = REMPAL(CI_BEAM_TRAILING, CI_RED);
847 	nTrailingG = REMPAL(CI_BEAM_TRAILING, CI_GREEN);
848 	nTrailingB = REMPAL(CI_BEAM_TRAILING, CI_BLUE);
849 
850 	// Draw the beam.
851 	DrawGouraudQuad(REMORA_SCREEN_CENTRE_X, REMORA_SCREEN_CENTRE_Z, nLeadingX, nLeadingY, nOrigin2X, nOrigin2Y, nTrailingX, nTrailingY, nLeadingR, nLeadingG, nLeadingB,
852 	                nLeadingR, nLeadingG, nLeadingB, nTrailingR, nTrailingG, nTrailingB, nTrailingR, nTrailingG, nTrailingB, -255);
853 }
854 
AddInterference() const855 void _remora::AddInterference() const {
856 	uint32 i;
857 	int32 nStartX, nStartY, nLength;
858 
859 	// Loop for how many interference lines we are going to draw.
860 	for (i = 0; i < 50; ++i) {
861 		// Pick a random row.
862 		nStartY = g_icb->getRandomSource()->getRandomNumber(REMORA_SCREEN_HEIGHT - 1);
863 
864 		// Pick a random startpoint in the row.
865 		nStartX = g_icb->getRandomSource()->getRandomNumber(REMORA_SCREEN_WIDTH - 1);
866 
867 		// Pick a random length of interference.
868 		nLength = g_icb->getRandomSource()->getRandomNumber(40 - 1);
869 
870 		// Draw a line that int32.
871 		DrawGouraudTriangle(nStartX, nStartY, nStartX, nStartY, nStartX + nLength, nStartY, 30, 255, 30, 30, 255, 30, 0, 5, 0, REMORA_BRIGHT_MIN);
872 	}
873 }
874 
DrawCrosshairs() const875 void _remora::DrawCrosshairs() const {
876 	int32 i;
877 	_rgb sLineColour;
878 	int32 nStart, nEnd;
879 
880 	// Set a colour for the crosshair.
881 	sLineColour.red = REMPAL(CI_PLAYER, CI_RED);
882 	sLineColour.green = REMPAL(CI_PLAYER, CI_GREEN);
883 	sLineColour.blue = REMPAL(CI_PLAYER, CI_BLUE);
884 
885 	// Draw the vertical line.
886 	RemoraLineDraw(REMORA_SCREEN_CENTRE_X, 0, REMORA_SCREEN_CENTRE_X, REMORA_SCREEN_HEIGHT, sLineColour, sLineColour);
887 
888 	// Draw the horizontal line.
889 	RemoraLineDraw(0, REMORA_SCREEN_CENTRE_Z - 4, REMORA_SCREEN_WIDTH, REMORA_SCREEN_CENTRE_Z - 4, sLineColour, sLineColour);
890 
891 	// Draw vertical increments.
892 	nStart = (REMORA_SCREEN_CENTRE_Z - (5 * REMORA_GRID_SIZE)) - 4;
893 	nEnd = (REMORA_SCREEN_CENTRE_Z + (4 * REMORA_GRID_SIZE)) - 4;
894 	i = nStart;
895 	while (i <= nEnd) {
896 		RemoraLineDraw(REMORA_SCREEN_CENTRE_X, i, REMORA_SCREEN_CENTRE_X + 4, i, sLineColour, sLineColour);
897 		i += REMORA_GRID_SIZE;
898 	}
899 
900 	// Draw horizontal increments.
901 	nStart = (REMORA_SCREEN_CENTRE_Z - (6 * REMORA_GRID_SIZE)) - 1;
902 	nEnd = (REMORA_SCREEN_CENTRE_Z + (8 * REMORA_GRID_SIZE)) - 1;
903 	i = nStart;
904 	while (i <= nEnd) {
905 		RemoraLineDraw(i, REMORA_SCREEN_CENTRE_Z - 4, i, REMORA_SCREEN_CENTRE_Z, sLineColour, sLineColour);
906 		i += REMORA_GRID_SIZE;
907 	}
908 }
909 
DrawEMPEffect()910 void _remora::DrawEMPEffect() {
911 	uint32 i;
912 
913 	// For now, just add loads more interference.
914 	for (i = 0; i < 100; ++i)
915 		AddInterference();
916 }
917 
DrawHeadingText()918 void _remora::DrawHeadingText() {
919 	uint8 nRed, nGreen, nBlue;
920 
921 	if (!m_bMainHeadingSet)
922 		return;
923 
924 	ColourToRGB(m_pDisplayBuffer[0].s_nAttribute, nRed, nGreen, nBlue);
925 	SetTextColour(nRed, nGreen, nBlue);
926 
927 	// Casing curves in where the heading is, so we have to move it over a bit.
928 	MS->Create_remora_text(REMORA_TEXT_LEFT_MARGIN + 5, REMORA_TEXT_TITLE_Y, m_pDisplayBuffer[0].s_pcText, 0, PIN_AT_CENTRE_OF_LEFT, REMORA_ROW_SPACING,
929 	                       REMORA_CHARACTER_SPACING, REMORA_DISPLAY_WIDTH);
930 	MS->Render_speech(MS->text_bloc);
931 	MS->Kill_remora_text();
932 }
933 
DrawProgressBar()934 void _remora::DrawProgressBar() {
935 	uint32 nX;
936 	_rgb sTailColour, sHeadColour;
937 	_rgb sBoxColour;
938 
939 	// Check if bar is currently up.
940 	if (m_nProgressBarValue == -1)
941 		return;
942 
943 	// Work out how much of the bar to draw.
944 	nX = (REMORA_PROGRESS_BAR_WIDTH * m_nProgressBarValue) / m_nProgressBarTotal;
945 
946 	// Set a colour for the trailing edge.
947 	sTailColour.red = REMPAL(CI_PROGRESS_TAIL, CI_RED);
948 	sTailColour.green = REMPAL(CI_PROGRESS_TAIL, CI_GREEN);
949 	sTailColour.blue = REMPAL(CI_PROGRESS_TAIL, CI_BLUE);
950 
951 	// Work out a colour for the leading edge.
952 	sHeadColour.red = (uint8)(((REMPAL(CI_PROGRESS_HEAD, CI_RED) - sTailColour.red) * m_nProgressBarValue) / m_nProgressBarTotal);
953 	sHeadColour.green = (uint8)(((REMPAL(CI_PROGRESS_HEAD, CI_GREEN) - sTailColour.green) * m_nProgressBarValue) / m_nProgressBarTotal);
954 	sHeadColour.blue = (uint8)(((REMPAL(CI_PROGRESS_HEAD, CI_BLUE) - sTailColour.blue) * m_nProgressBarValue) / m_nProgressBarTotal);
955 
956 	sHeadColour.red = (uint8)(sHeadColour.red + sTailColour.red);
957 	sHeadColour.green = (uint8)(sHeadColour.green + sTailColour.green);
958 	sHeadColour.blue = (uint8)(sHeadColour.blue + sTailColour.blue);
959 
960 	// Draw it.
961 	DrawGouraudQuad(REMORA_PROGRESS_BAR_X, REMORA_PROGRESS_BAR_Y, REMORA_PROGRESS_BAR_X + nX, REMORA_PROGRESS_BAR_Y, REMORA_PROGRESS_BAR_X,
962 	                REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT, REMORA_PROGRESS_BAR_X + nX, REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT, sTailColour.red,
963 	                sTailColour.green, sTailColour.blue, sHeadColour.red, sHeadColour.green, sHeadColour.blue, sTailColour.red, sTailColour.green, sTailColour.blue,
964 	                sHeadColour.red, sHeadColour.green, sHeadColour.blue, REMORA_BRIGHT_FULL);
965 
966 	// Draw the box around it.
967 	sBoxColour.red = REMPAL(CI_HEADING, CI_RED);
968 	sBoxColour.green = REMPAL(CI_HEADING, CI_GREEN);
969 	sBoxColour.blue = REMPAL(CI_HEADING, CI_BLUE);
970 
971 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 6, REMORA_PROGRESS_BAR_Y - 6, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 6, REMORA_PROGRESS_BAR_Y - 6, sBoxColour,
972 	               sBoxColour);
973 	RemoraLineDraw(REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 6, REMORA_PROGRESS_BAR_Y - 6, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 6,
974 	               REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 6, sBoxColour, sBoxColour);
975 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 6, REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 6, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 6,
976 	               REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 6, sBoxColour, sBoxColour);
977 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 6, REMORA_PROGRESS_BAR_Y - 6, REMORA_PROGRESS_BAR_X - 6, REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 6, sBoxColour,
978 	               sBoxColour);
979 
980 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 1, REMORA_PROGRESS_BAR_Y - 1, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 1, REMORA_PROGRESS_BAR_Y - 1, sBoxColour,
981 	               sBoxColour);
982 	RemoraLineDraw(REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 1, REMORA_PROGRESS_BAR_Y - 1, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 1,
983 	               REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 1, sBoxColour, sBoxColour);
984 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 1, REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 1, REMORA_PROGRESS_BAR_X + REMORA_PROGRESS_BAR_WIDTH + 1,
985 	               REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 1, sBoxColour, sBoxColour);
986 	RemoraLineDraw(REMORA_PROGRESS_BAR_X - 1, REMORA_PROGRESS_BAR_Y - 1, REMORA_PROGRESS_BAR_X - 1, REMORA_PROGRESS_BAR_Y + REMORA_PROGRESS_BAR_HEIGHT + 1, sBoxColour,
987 	               sBoxColour);
988 }
989 
DrawHeaderAndFooterLines()990 void _remora::DrawHeaderAndFooterLines() {
991 	_rgb sLineColour;
992 
993 	// Set a line colour.
994 	sLineColour.red = REMPAL(CI_HEADING, CI_RED);
995 	sLineColour.green = REMPAL(CI_HEADING, CI_GREEN);
996 	sLineColour.blue = REMPAL(CI_HEADING, CI_BLUE);
997 
998 	// Draw the header lines.
999 	RemoraLineDraw(0, REMORA_TEXT_CLIP_TOP - 5, 402, REMORA_TEXT_CLIP_TOP - 5, sLineColour, sLineColour);
1000 	RemoraLineDraw(402, REMORA_TEXT_CLIP_TOP - 5, 452, REMORA_TEXT_CLIP_TOP - 35, sLineColour, sLineColour);
1001 	RemoraLineDraw(452, REMORA_TEXT_CLIP_TOP - 35, 630, REMORA_TEXT_CLIP_TOP - 35, sLineColour, sLineColour);
1002 
1003 	RemoraLineDraw(0, REMORA_TEXT_CLIP_TOP - 6, 402, REMORA_TEXT_CLIP_TOP - 6, sLineColour, sLineColour);
1004 	RemoraLineDraw(402, REMORA_TEXT_CLIP_TOP - 6, 452, REMORA_TEXT_CLIP_TOP - 36, sLineColour, sLineColour);
1005 	RemoraLineDraw(452, REMORA_TEXT_CLIP_TOP - 36, 630, REMORA_TEXT_CLIP_TOP - 36, sLineColour, sLineColour);
1006 
1007 	RemoraLineDraw(401, REMORA_TEXT_CLIP_TOP - 6, 451, REMORA_TEXT_CLIP_TOP - 36, sLineColour, sLineColour);
1008 	RemoraLineDraw(403, REMORA_TEXT_CLIP_TOP - 6, 451, REMORA_TEXT_CLIP_TOP - 36, sLineColour, sLineColour);
1009 	RemoraLineDraw(401, REMORA_TEXT_CLIP_TOP - 6, 450, REMORA_TEXT_CLIP_TOP - 36, sLineColour, sLineColour);
1010 	RemoraLineDraw(452, REMORA_TEXT_CLIP_TOP - 35, 401, REMORA_TEXT_CLIP_TOP - 6, sLineColour, sLineColour);
1011 }
1012 
ClipTopAndBottom()1013 void _remora::ClipTopAndBottom() {
1014 	uint32 i;
1015 	LRECT sBlankingRect;
1016 	_rgb oLineColour;
1017 
1018 	// Left and right coordinates are same for both rectangles we have to draw.
1019 	sBlankingRect.left = REMORA_SCREEN_ORIGIN_X;
1020 	sBlankingRect.right = REMORA_SCREEN_ORIGIN_X + REMORA_SCREEN_WIDTH;
1021 
1022 	// Set colour to be same as Remora background colour.
1023 	uint32 dwFillPixel = GetPen(m_nCurrentPalette, CI_CLIP);
1024 
1025 	// Setup the rectangle to clip the top.
1026 	sBlankingRect.top = 0;
1027 	sBlankingRect.bottom = REMORA_TEXT_CLIP_TOP;
1028 
1029 	// Draw the rectangle.
1030 	surface_manager->Blit_fillfx(m_nRemoraSurfaceID, &sBlankingRect, dwFillPixel);
1031 
1032 	// Setup the rectangle to clip the bottom.
1033 	sBlankingRect.top = REMORA_TEXT_CLIP_BOTTOM;
1034 	sBlankingRect.bottom = REMORA_SCREEN_HEIGHT;
1035 
1036 	// Draw the rectangle.
1037 	surface_manager->Blit_fillfx(m_nRemoraSurfaceID, &sBlankingRect, dwFillPixel);
1038 
1039 	// Set a line colour to redraw part of background grid.
1040 	oLineColour.red = REMPAL(CI_GRID, CI_RED);
1041 	oLineColour.green = REMPAL(CI_GRID, CI_GREEN);
1042 	oLineColour.blue = REMPAL(CI_GRID, CI_BLUE);
1043 
1044 	// Draw horizontal lines at top of screen.
1045 	for (i = REMORA_GRID_Y; i < REMORA_TEXT_CLIP_TOP; i += REMORA_GRID_SIZE)
1046 		RemoraLineDraw(0, i, REMORA_SCREEN_WIDTH, i, oLineColour, oLineColour);
1047 
1048 	// Draw horizontal lines at bottom of screen.
1049 	for (i = REMORA_TEXT_CLIP_BOTTOM - 1; i < REMORA_SCREEN_HEIGHT; i += REMORA_GRID_SIZE)
1050 		RemoraLineDraw(0, i, REMORA_SCREEN_WIDTH, i, oLineColour, oLineColour);
1051 
1052 	// Draw vertical lines at top.
1053 	for (i = REMORA_GRID_X; i < REMORA_SCREEN_WIDTH; i += REMORA_GRID_SIZE)
1054 		RemoraLineDraw(i, 0, i, REMORA_TEXT_CLIP_TOP, oLineColour, oLineColour);
1055 
1056 	// Draw vertical lines at bottom.
1057 	for (i = REMORA_GRID_X; i < REMORA_SCREEN_WIDTH; i += REMORA_GRID_SIZE)
1058 		RemoraLineDraw(i, REMORA_TEXT_CLIP_BOTTOM, i, REMORA_SCREEN_HEIGHT, oLineColour, oLineColour);
1059 }
1060 
DrawGrid()1061 void _remora::DrawGrid() {
1062 	uint32 i;
1063 	_rgb oLineColour;
1064 
1065 	// Set a line colour.
1066 	oLineColour.red = REMPAL(CI_GRID, CI_RED);
1067 	oLineColour.green = REMPAL(CI_GRID, CI_GREEN);
1068 	oLineColour.blue = REMPAL(CI_GRID, CI_BLUE);
1069 
1070 	// Draw horizontal lines.
1071 	for (i = REMORA_GRID_Y; i < REMORA_SCREEN_HEIGHT; i += REMORA_GRID_SIZE)
1072 		RemoraLineDraw(0, i, REMORA_SCREEN_WIDTH, i, oLineColour, oLineColour);
1073 
1074 	// Draw vertical lines.
1075 	for (i = REMORA_GRID_X; i < REMORA_SCREEN_WIDTH; i += REMORA_GRID_SIZE)
1076 		RemoraLineDraw(i, 0, i, REMORA_SCREEN_HEIGHT, oLineColour, oLineColour);
1077 }
1078 
DrawPulse()1079 void _remora::DrawPulse() {
1080 	uint32 i, j;
1081 	int32 nHighlightIndex;
1082 	int32 pnWholePulse[REMORA_PULSE_POINTS * REMORA_MAX_HEALTH][2];
1083 	float pfHighlightValues[REMORA_PULSE_POINTS * REMORA_MAX_HEALTH];
1084 	//int32 nBaseX, nBaseY;
1085 	int32 nX1, nY1, nX2, nY2;
1086 	_rgb oLineColour, oDrawColour;
1087 	uint32 nWholePulseIndex;
1088 	int32 nXCoord;
1089 	int32 nWhiteValue;
1090 	float fHighlightStep, fCurrentHighlight;
1091 	c_game_object *pPlayer;
1092 	uint32 nHits, nHealth;
1093 
1094 	// Calculate a base drawing point for the whole thing.
1095 	//nBaseX = REMORA_PULSE_X;
1096 	//nBaseY = REMORA_PULSE_Y;
1097 
1098 	// Work out player's health.
1099 	pPlayer = (c_game_object *)MS->objects->Fetch_item_by_name("player");
1100 	nHits = pPlayer->GetIntegerVariable(pPlayer->GetVariable("hits"));
1101 
1102 	// This counts from 10 down to zero (check player's script for this figure if it changes).
1103 	if (nHits > 6)
1104 		nHealth = 3;
1105 	else if (nHits > 3)
1106 		nHealth = 2;
1107 	else
1108 		nHealth = 1;
1109 
1110 	// Build an array of points for drawing the pulse, depending on how many
1111 	// pulse symbols are required for indicating the player's health.
1112 	nWholePulseIndex = 0;
1113 
1114 	for (i = 0; i < nHealth; ++i) {
1115 		for (j = 0; j < REMORA_PULSE_POINTS; ++j) {
1116 			// Copy the x,y point and add the offset to the x for which pulse we're drawing.
1117 			nXCoord = REMORA_PULSE_X + (i * REMORA_PULSE_WIDTH) + pnPulsePoints[j][0];
1118 			pnWholePulse[nWholePulseIndex][1] = REMORA_PULSE_Y + pnPulsePoints[j][1];
1119 
1120 			// Apply an offset to the x to centre it in the space available.
1121 			nXCoord += ((REMORA_MAX_HEALTH - nHealth) * REMORA_PULSE_WIDTH / 2);
1122 			pnWholePulse[nWholePulseIndex][0] = nXCoord;
1123 
1124 			++nWholePulseIndex;
1125 		}
1126 	}
1127 
1128 	// Work out the starting highlight colour.
1129 	nHighlightIndex = m_nPulseHighlight;
1130 	fCurrentHighlight = 0.7f;
1131 	fHighlightStep = fCurrentHighlight / (float)nWholePulseIndex;
1132 
1133 	// Go through and work out the real highlight value.
1134 	for (i = 0; i < nWholePulseIndex; ++i) {
1135 		// Assign a highlight.
1136 		pfHighlightValues[nHighlightIndex] = fCurrentHighlight;
1137 
1138 		// Work out next one.
1139 		fCurrentHighlight -= fHighlightStep;
1140 
1141 		// Move on to next segment of line.
1142 		nHighlightIndex = (nHighlightIndex == -1) ? nWholePulseIndex - 1 : nHighlightIndex - 1;
1143 	}
1144 
1145 	// Unhighlighted colour of line is same as floors.
1146 	oLineColour.red = REMPAL(CI_BACKGROUND, CI_RED);
1147 	oLineColour.green = REMPAL(CI_BACKGROUND, CI_GREEN);
1148 	oLineColour.blue = REMPAL(CI_BACKGROUND, CI_BLUE);
1149 
1150 	// Draw it.
1151 	for (i = 0; i < nWholePulseIndex - 2; ++i) {
1152 		// Get ends of line to draw.
1153 		nX1 = pnWholePulse[i][0];
1154 		nY1 = pnWholePulse[i][1];
1155 		nX2 = pnWholePulse[i + 1][0];
1156 		nY2 = pnWholePulse[i + 1][1];
1157 
1158 		// Draw a bigger blob right at the tip of the highlight.
1159 		if (i == m_nPulseHighlight) {
1160 			oDrawColour.red = 255;
1161 			oDrawColour.green = 255;
1162 			oDrawColour.blue = 255;
1163 
1164 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour, 5);
1165 		} else {
1166 			// Get amount of white to add to this line segment.
1167 			nWhiteValue = (int32)(255.0f * pfHighlightValues[i]);
1168 
1169 			// Work out a colour for this line segment by adding in white to create highlight
1170 			// where needed, capping at pure white (0xff).
1171 			oDrawColour.red = ((nWhiteValue + oLineColour.red) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.red);
1172 			oDrawColour.green = ((nWhiteValue + oLineColour.green) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.green);
1173 			oDrawColour.blue = ((nWhiteValue + oLineColour.blue) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.blue);
1174 
1175 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour);
1176 		}
1177 	}
1178 
1179 	// Move the highlight.
1180 	m_nPulseHighlight = (m_nPulseHighlight + 1) % nWholePulseIndex;
1181 }
1182 
DrawIRLinkPulse()1183 void _remora::DrawIRLinkPulse() {
1184 	uint32 i;
1185 	float fCurrentHighlight, fHighlightStep;
1186 	float pfForwardHighlight[REMORA_IR_LINK_POINTS];
1187 	float pfReverseHighlight[REMORA_IR_LINK_POINTS];
1188 	int32 nForwardHighlight, nReverseHighlight, nSaveReverseHighlight;
1189 	_rgb oLineColour, oDrawColour;
1190 	int32 nWhiteValue;
1191 	int32 nBaseX, nBaseY;
1192 	int32 nX1, nY1, nX2, nY2;
1193 
1194 	// Work out a base point for drawing the data lines.
1195 	nBaseX = REMORA_IRPULSE_X + 20;
1196 	nBaseY = REMORA_IRPULSE_Y + 3;
1197 
1198 	// Unhighlighted colour of line is same as floors.
1199 	oLineColour.red = REMPAL(CI_BACKGROUND, CI_RED);
1200 	oLineColour.green = REMPAL(CI_BACKGROUND, CI_GREEN);
1201 	oLineColour.blue = REMPAL(CI_BACKGROUND, CI_BLUE);
1202 
1203 	// Initialise.
1204 	nForwardHighlight = m_nPulseHighlight;
1205 	nReverseHighlight = (REMORA_IR_LINK_POINTS - m_nPulseHighlight) - 1;
1206 	nSaveReverseHighlight = nReverseHighlight;
1207 	fCurrentHighlight = 0.7f;
1208 	fHighlightStep = fCurrentHighlight / (float)REMORA_IR_LINK_POINTS;
1209 
1210 	// Loop to fill in highlight values for both lines.
1211 	for (i = 0; i < REMORA_IR_LINK_POINTS; ++i) {
1212 		pfForwardHighlight[nForwardHighlight] = fCurrentHighlight;
1213 		pfReverseHighlight[nReverseHighlight] = fCurrentHighlight;
1214 
1215 		fCurrentHighlight -= fHighlightStep;
1216 
1217 		nForwardHighlight = (nForwardHighlight > 0) ? nForwardHighlight - 1 : REMORA_IR_LINK_POINTS - 1;
1218 		nReverseHighlight = (nReverseHighlight + 1) % REMORA_IR_LINK_POINTS;
1219 	}
1220 
1221 	// Draw the lines.
1222 	for (i = 0; i < REMORA_IR_LINK_POINTS - 1; ++i) {
1223 		// Draw forward line.
1224 		nX1 = pnIRLinkPoints[i][0] + nBaseX;
1225 		nY1 = pnIRLinkPoints[i][1] + nBaseY;
1226 		nX2 = pnIRLinkPoints[i + 1][0] + nBaseX;
1227 		nY2 = pnIRLinkPoints[i + 1][1] + nBaseY;
1228 
1229 		if (i == m_nPulseHighlight) {
1230 			oDrawColour.red = REMPAL(CI_WARNING, CI_RED);
1231 			oDrawColour.green = REMPAL(CI_WARNING, CI_GREEN);
1232 			oDrawColour.blue = REMPAL(CI_WARNING, CI_BLUE);
1233 
1234 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour, 5);
1235 		} else {
1236 			// Get amount of white to add to this line segment.
1237 			nWhiteValue = (int32)(255.0f * pfForwardHighlight[i]);
1238 
1239 			// Work out a colour for this line segment by adding in white to create highlight
1240 			// where needed, capping at pure white (0xff).
1241 			oDrawColour.red = ((nWhiteValue + oLineColour.red) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.red);
1242 			oDrawColour.green = ((nWhiteValue + oLineColour.green) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.green);
1243 			oDrawColour.blue = ((nWhiteValue + oLineColour.blue) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.blue);
1244 
1245 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour);
1246 		}
1247 
1248 		// Draw reverse line.
1249 		nX1 = pnIRLinkPointsR[i][0] + nBaseX;
1250 		nY1 = pnIRLinkPointsR[i][1] + nBaseY;
1251 		nX2 = pnIRLinkPointsR[i + 1][0] + nBaseX;
1252 		nY2 = pnIRLinkPointsR[i + 1][1] + nBaseY;
1253 
1254 		if (i == (uint32)nSaveReverseHighlight) {
1255 			oDrawColour.red = REMPAL(CI_WARNING, CI_RED);
1256 			oDrawColour.green = REMPAL(CI_WARNING, CI_GREEN);
1257 			oDrawColour.blue = REMPAL(CI_WARNING, CI_BLUE);
1258 
1259 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour, 5);
1260 		} else {
1261 			// Get amount of white to add to this line segment.
1262 			nWhiteValue = (int32)(255.0f * pfReverseHighlight[i]);
1263 
1264 			// Work out a colour for this line segment by adding in white to create highlight
1265 			// where needed, capping at pure white (0xff).
1266 			oDrawColour.red = ((nWhiteValue + oLineColour.red) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.red);
1267 			oDrawColour.green = ((nWhiteValue + oLineColour.green) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.green);
1268 			oDrawColour.blue = ((nWhiteValue + oLineColour.blue) > 255) ? (uint8)255 : (uint8)(nWhiteValue + oLineColour.blue);
1269 
1270 			RemoraLineDraw(nX1, nY1, nX2, nY2, oDrawColour, oDrawColour);
1271 		}
1272 	}
1273 
1274 	// Move the highlight.
1275 	m_nPulseHighlight = (m_nPulseHighlight + 1) % REMORA_IR_LINK_POINTS;
1276 
1277 	// Set drawing colour for symbols.
1278 	oDrawColour.red = REMPAL(CI_WARNING, CI_RED);
1279 	oDrawColour.green = REMPAL(CI_WARNING, CI_GREEN);
1280 	oDrawColour.blue = REMPAL(CI_WARNING, CI_BLUE);
1281 
1282 	// Base point for drawing the Remora symbol.
1283 	nBaseX = REMORA_IRPULSE_X;
1284 	nBaseY = REMORA_IRPULSE_Y;
1285 
1286 	RemoraLineDraw(nBaseX, nBaseY, nBaseX + 20, nBaseY, oDrawColour, oDrawColour);
1287 	RemoraLineDraw(nBaseX + 20, nBaseY, nBaseX + 20, nBaseY + 15, oDrawColour, oDrawColour);
1288 	RemoraLineDraw(nBaseX, nBaseY + 15, nBaseX + 20, nBaseY + 15, oDrawColour, oDrawColour);
1289 	RemoraLineDraw(nBaseX, nBaseY + 15, nBaseX, nBaseY, oDrawColour, oDrawColour);
1290 
1291 	RemoraLineDraw(nBaseX + 5, nBaseY, nBaseX + 5, nBaseY - 2, oDrawColour, oDrawColour);
1292 	RemoraLineDraw(nBaseX + 5, nBaseY - 2, nBaseX + 15, nBaseY - 2, oDrawColour, oDrawColour);
1293 	RemoraLineDraw(nBaseX + 15, nBaseY - 2, nBaseX + 15, nBaseY, oDrawColour, oDrawColour);
1294 
1295 	RemoraLineDraw(nBaseX + 5, nBaseY + 15, nBaseX + 5, nBaseY + 17, oDrawColour, oDrawColour);
1296 	RemoraLineDraw(nBaseX + 5, nBaseY + 17, nBaseX + 15, nBaseY + 17, oDrawColour, oDrawColour);
1297 	RemoraLineDraw(nBaseX + 15, nBaseY + 17, nBaseX + 15, nBaseY + 15, oDrawColour, oDrawColour);
1298 
1299 	// Base point for drawing the Remora symbol.
1300 	nBaseX = REMORA_IRPULSE_X + 56;
1301 	nBaseY = REMORA_IRPULSE_Y + 11;
1302 
1303 	RemoraLineDraw(nBaseX, nBaseY, nBaseX + 40, nBaseY, oDrawColour, oDrawColour);
1304 	RemoraLineDraw(nBaseX + 40, nBaseY, nBaseX + 40, nBaseY + 15, oDrawColour, oDrawColour);
1305 	RemoraLineDraw(nBaseX, nBaseY + 15, nBaseX + 40, nBaseY + 15, oDrawColour, oDrawColour);
1306 	RemoraLineDraw(nBaseX, nBaseY + 15, nBaseX, nBaseY, oDrawColour, oDrawColour);
1307 
1308 	// Draw the data arrows.
1309 	if ((m_nPulseHighlight == 8) || (m_nPulseHighlight == 9)) {
1310 		RemoraLineDraw(nBaseX + 4, nBaseY + 4, nBaseX + 8, nBaseY + 8, oDrawColour, oDrawColour);
1311 		RemoraLineDraw(nBaseX + 4, nBaseY + 12, nBaseX + 8, nBaseY + 8, oDrawColour, oDrawColour);
1312 	} else if ((m_nPulseHighlight == 2) || (m_nPulseHighlight == 3)) {
1313 		RemoraLineDraw(nBaseX + 4, nBaseY + 8, nBaseX + 9, nBaseY + 4, oDrawColour, oDrawColour);
1314 		RemoraLineDraw(nBaseX + 4, nBaseY + 8, nBaseX + 9, nBaseY + 12, oDrawColour, oDrawColour);
1315 	}
1316 }
1317 
DrawMoreUpDownArrows()1318 void _remora::DrawMoreUpDownArrows() {
1319 	// Check the flash timer before drawing anything.
1320 	if (!m_bFlashingTextVisible)
1321 		return;
1322 
1323 	// Check if top arrow is required.
1324 	if (m_nFirstLineToDraw > REMORA_FIRST_SCROLLING_LINE) {
1325 		// Yes, there is some text off the top of the screen.
1326 		surface_manager->Blit_surface_to_surface(m_nMoreUpSurfaceID, m_nRemoraSurfaceID, &m_sMoreUpSourceRectangle, &m_sMoreUpTargetRectangle, DDBLT_KEYSRC);
1327 	}
1328 
1329 	// Check if the bottom arrow is required.
1330 	if (m_nNextAvailableRow > (m_nFirstLineToDraw + REMORA_DISPLAYED_TEXT_ROWS)) {
1331 		// Yes, there is some text off the bottom of the screen.
1332 		surface_manager->Blit_surface_to_surface(m_nMoreDownSurfaceID, m_nRemoraSurfaceID, &m_sMoreDownSourceRectangle, &m_sMoreDownTargetRectangle, DDBLT_KEYSRC);
1333 	}
1334 }
1335 
ColourToRGB(uint8 nAttributes,uint8 & nRed,uint8 & nGreen,uint8 & nBlue) const1336 void _remora::ColourToRGB(uint8 nAttributes, uint8 &nRed, uint8 &nGreen, uint8 &nBlue) const {
1337 	uint32 nColourAttribute;
1338 
1339 	// The colour is stored in the lower 4 bits.
1340 	nColourAttribute = (uint32)(nAttributes & 0x0f);
1341 
1342 	// Need to get the bit position and map to an integer.
1343 	switch (nColourAttribute) {
1344 	case 0x01:
1345 		nColourAttribute = 0;
1346 		break;
1347 	case 0x02:
1348 		nColourAttribute = 1;
1349 		break;
1350 	case 0x04:
1351 		nColourAttribute = 2;
1352 		break;
1353 	case 0x08:
1354 		nColourAttribute = 3;
1355 		break;
1356 	}
1357 
1358 	// Set the RGB for the colour.
1359 	nRed = pnRemoraColour[m_nCurrentPalette][nColourAttribute][CI_RED];
1360 	nGreen = pnRemoraColour[m_nCurrentPalette][nColourAttribute][CI_GREEN];
1361 	nBlue = pnRemoraColour[m_nCurrentPalette][nColourAttribute][CI_BLUE];
1362 }
1363 
SetUpM08LockControl()1364 void _remora::SetUpM08LockControl() {
1365 	// Here we build a list of the door names and what object ID they are.
1366 	BuildM08DoorList();
1367 }
1368 
DrawM08LockControl()1369 void _remora::DrawM08LockControl() {
1370 	_rgb oLineColour;
1371 
1372 	// Set up the function to map game points to the Remora's screen.  Doesn't actually need calling each
1373 	// cycle, but it keeps it consistent with the motion scan and it won't hurt.
1374 	InitialiseGameToRemora(0.0f, REMORA_M08_ZOOM_X, REMORA_M08_ZOOM_Z, REMORA_M08_X_ORIGIN, REMORA_M08_Z_ORIGIN);
1375 
1376 	// Set a line colour.
1377 	oLineColour.red = pnRemoraColour[m_nCurrentPalette][CI_M08_BARRIERS][CI_RED];
1378 	oLineColour.green = pnRemoraColour[m_nCurrentPalette][CI_M08_BARRIERS][CI_GREEN];
1379 	oLineColour.blue = pnRemoraColour[m_nCurrentPalette][CI_M08_BARRIERS][CI_BLUE];
1380 
1381 	// Draw the non-animating barriers.
1382 	DrawStaticBarriers(oLineColour);
1383 
1384 	// Draw the animating barriers.  At some point, we are going to have to provide some visual
1385 	// indication of whether or not they are open or closed.
1386 	DrawAnimatingBarriers(oLineColour);
1387 
1388 	// Draw the floor rectangles.
1389 	DrawFloorRectangles();
1390 
1391 	// Draw other object symbols.
1392 	DrawM08LockControlObjects();
1393 
1394 	// Draw the door-lock symbols.
1395 	DrawM08DoorLocks();
1396 }
1397 
DrawM08DoorLocks()1398 void _remora::DrawM08DoorLocks() {
1399 	uint32 i;
1400 	int32 nX, nZ;
1401 	float fX, fZ;
1402 	_logic *pGameObject;
1403 	uint32 nDoorID;
1404 	char pcDigitString[16];
1405 	_rs_params sSpriteParams;
1406 
1407 	PXTRY
1408 
1409 	// All sprites are going to be drawn the same, so we can fill
1410 	// in the parameter block here.
1411 	sSpriteParams.bCentre = TRUE8;
1412 	sSpriteParams.bUpdate = FALSE8;
1413 
1414 	// Loop for each door lock.
1415 	for (i = 0; i < REMORA_M08_NUM_LOCKS; ++i) {
1416 		// Get the ID of this door.
1417 		nDoorID = m_pnDoorIDs[i];
1418 
1419 		// Get the game object.
1420 		pGameObject = MS->logic_structs[nDoorID];
1421 
1422 		// Ignore held objects.
1423 		if ((pGameObject->ob_status != OB_STATUS_HELD) && pGameObject->prop_coords_set) {
1424 			// Get the object's x, z.
1425 			fX = pGameObject->prop_xyz.x;
1426 			fZ = pGameObject->prop_xyz.z;
1427 
1428 			// Map the point to the Remora's screen.
1429 			GameToRemora(fX, fZ);
1430 
1431 			// We only need integers now.
1432 			nX = (int32)fX;
1433 			nZ = (int32)fZ;
1434 
1435 			// Check the point is on the screen.  Allow a 20-pixel border.
1436 			if ((nX > 20) && (nX < REMORA_SCREEN_WIDTH - 20) && (nZ > 20) && (nZ < REMORA_SCREEN_HEIGHT - 20)) {
1437 				// Draw either a red or green symbol depending on the door's state.
1438 				if (MS->Fetch_object_integer_variable(pGameObject->GetName(), "locked_in_place") == 1) {
1439 					SetTextColour(REMPAL(CI_M08_LOCKED_DOOR, CI_RED), REMPAL(CI_M08_LOCKED_DOOR, CI_GREEN), REMPAL(CI_M08_LOCKED_DOOR, CI_BLUE));
1440 				} else {
1441 					SetTextColour(REMPAL(CI_M08_UNLOCKED_DOOR, CI_RED), REMPAL(CI_M08_UNLOCKED_DOOR, CI_GREEN), REMPAL(CI_M08_UNLOCKED_DOOR, CI_BLUE));
1442 				}
1443 
1444 				// Write a number on them so player knows which icon controls which lock.
1445 				snprintf(pcDigitString, 16, "%d", i + 1);
1446 
1447 				MS->Create_remora_text(nX, nZ - 7, pcDigitString, 0, PIN_AT_CENTRE, 0, 0, REMORA_DISPLAY_WIDTH);
1448 
1449 				MS->Render_speech(MS->text_bloc);
1450 				MS->Kill_remora_text();
1451 			}
1452 		}
1453 	}
1454 
1455 	PXCATCH
1456 
1457 	Fatal_error("Exception in _remora::DrawM08DoorLocks()");
1458 
1459 	PXENDCATCH
1460 }
1461 
DrawM08LockControlObjects()1462 void _remora::DrawM08LockControlObjects() {
1463 	uint32 i;
1464 	float fX, fZ;
1465 	int32 nX, nY, nZ;
1466 	_logic *pGameObject;
1467 	ScreenSymbol eSymbol;
1468 	static bool8 bArmedRechargeFlash;
1469 
1470 	PXTRY
1471 
1472 	// This is the loop that draws the blips for megas.
1473 	for (i = 0; i < MS->total_objects; ++i) {
1474 		// Ignore held objects.
1475 		if (MS->logic_structs[i]->ob_status != OB_STATUS_HELD) {
1476 			// Get the game object.
1477 			pGameObject = MS->logic_structs[i];
1478 
1479 			// Get the x,y,z of the object.
1480 			if (pGameObject->image_type == VOXEL) {
1481 				fX = pGameObject->mega->actor_xyz.x;
1482 				fZ = pGameObject->mega->actor_xyz.z;
1483 				nY = (int32)pGameObject->mega->actor_xyz.y;
1484 			} else {
1485 				fX = pGameObject->prop_xyz.x;
1486 				fZ = pGameObject->prop_xyz.z;
1487 				nY = (int32)pGameObject->prop_xyz.y;
1488 			}
1489 
1490 			// Ignore objects which are not on our height.
1491 			if (m_nPlayerY == nY) {
1492 				// Map the point to the Remora's screen.
1493 				GameToRemora(fX, fZ);
1494 
1495 				// We only need integers now.
1496 				nX = (int32)fX;
1497 				nZ = (int32)fZ;
1498 
1499 				// Here we get an ID back of which symbol to draw for the object
1500 				eSymbol = GetSymbolToDrawObject(pGameObject, i);
1501 
1502 				switch (eSymbol) {
1503 				case SS_REMORA:
1504 					// Fudge drawing player symbol at correct angle 'cos it's fixed.
1505 					DrawGouraudTriangle(nX + (REMORA_SYMBOL_SIZE_NORMAL >> 1), nZ - REMORA_SYMBOL_SIZE_NORMAL, nX + (REMORA_SYMBOL_SIZE_NORMAL << 1),
1506 					                    nZ + REMORA_SYMBOL_SIZE_NORMAL, nX - (REMORA_SYMBOL_SIZE_NORMAL >> 1), nZ + REMORA_SYMBOL_SIZE_NORMAL,
1507 					                    REMPAL(CI_PLAYER, CI_RED), REMPAL(CI_PLAYER, CI_GREEN), REMPAL(CI_PLAYER, CI_BLUE), REMPAL(CI_PLAYER, CI_RED),
1508 					                    REMPAL(CI_PLAYER, CI_GREEN), REMPAL(CI_PLAYER, CI_BLUE), REMPAL(CI_PLAYER, CI_RED), REMPAL(CI_PLAYER, CI_GREEN),
1509 					                    REMPAL(CI_PLAYER, CI_BLUE), 255);
1510 
1511 					break;
1512 
1513 				case ALIVE_ROBOT:
1514 					DrawSquareSymbol(nX, nZ, CI_ROBOT, REMORA_SYMBOL_SIZE_NORMAL);
1515 					break;
1516 
1517 				case DEAD_ROBOT:
1518 					DrawSquareSymbol(nX, nZ, CI_DEAD_ROBOT, REMORA_SYMBOL_SIZE_NORMAL);
1519 					break;
1520 
1521 				case ALIVE_HUMAN:
1522 					DrawOctagonSymbol(nX, nZ, CI_HUMAN, REMORA_SYMBOL_SIZE_NORMAL);
1523 					break;
1524 
1525 				case DEAD_HUMAN:
1526 					DrawOctagonSymbol(nX, nZ, CI_DEAD_HUMAN, REMORA_SYMBOL_SIZE_NORMAL);
1527 					break;
1528 
1529 				case RECHARGE_UNARMED:
1530 					DrawSquareSymbol(nX, nZ, CI_RECHARGE, REMORA_SYMBOL_SIZE_NORMAL);
1531 					break;
1532 
1533 				case RECHARGE_ARMED:
1534 					if (bArmedRechargeFlash) {
1535 						DrawSquareSymbol(nX, nZ, CI_RECHARGE, REMORA_SYMBOL_SIZE_NORMAL);
1536 						bArmedRechargeFlash = FALSE8;
1537 					} else {
1538 						DrawSquareSymbol(nX, nZ, CI_RECHARGE_ARMED, REMORA_SYMBOL_SIZE_NORMAL);
1539 						bArmedRechargeFlash = TRUE8;
1540 					}
1541 					break;
1542 				default:
1543 					break;
1544 				} // end switch
1545 			} // end if
1546 		} // end if
1547 	} // end for
1548 
1549 	PXCATCH
1550 
1551 	Fatal_error("Exception in _remora::DrawM08LockControlObjects()");
1552 
1553 	PXENDCATCH
1554 }
1555 
RemoraLineDraw(int32 nX1,int32 nZ1,int32 nX2,int32 nZ2,_rgb sColour,_rgb,uint32 nHalfThickness) const1556 void _remora::RemoraLineDraw(int32 nX1, int32 nZ1, int32 nX2, int32 nZ2, _rgb sColour, _rgb, uint32 nHalfThickness) const {
1557 	_rgb col = sColour;
1558 
1559 	BlendedLine(nX1, nZ1, nX2, nZ2, col, m_nRemoraSurfaceID);
1560 
1561 	for (uint32 i = 1; i < nHalfThickness; i++) {
1562 		col.blue >>= 1;
1563 		col.green >>= 1;
1564 		col.red >>= 1;
1565 		col.alpha >>= 1;
1566 		BlendedLine(nX1 - i, nZ1 - i, nX2 - i, nZ2 - i, col, m_nRemoraSurfaceID);
1567 		BlendedLine(nX1 + i, nZ1 + i, nX2 + i, nZ2 + i, col, m_nRemoraSurfaceID);
1568 	}
1569 }
1570 
SetUpSurfaceForBitmap(const char * pcBitmapName,DXrect & sSourceRect,DXrect & sTargetRect,uint32 & nSurfaceID)1571 void _remora::SetUpSurfaceForBitmap(const char *pcBitmapName, DXrect &sSourceRect, DXrect &sTargetRect, uint32 &nSurfaceID) {
1572 	_pxBitmap *pBitmap;
1573 	_pxSprite *pSprite;
1574 	uint32 nFullBitmapNameHash;
1575 	uint8 *pSurfaceBitmap;
1576 	uint32 nPitch;
1577 	const char *pcFullBitmapName;
1578 
1579 	// Now do the same with the screen inlay.
1580 	nFullBitmapNameHash = NULL_HASH;
1581 	pcFullBitmapName = MakeRemoraGraphicsPath(pcBitmapName);
1582 	pBitmap = (_pxBitmap *)rs_remora->Res_open(pcFullBitmapName, nFullBitmapNameHash, m_pcRemoraCluster, m_nRemoraClusterHash);
1583 
1584 	if (pBitmap->schema != PC_BITMAP_SCHEMA)
1585 		Fatal_error("Incorrect versions loading [%s] (engine has %d, data has %d", pcFullBitmapName, PC_BITMAP_SCHEMA, pBitmap->schema);
1586 
1587 	pSprite = pBitmap->Fetch_item_by_number(0);
1588 
1589 	// Prepare the source/target rectangles for blitting later.
1590 	sSourceRect = MakeRECTFromSpriteSizes(0, 0, pSprite->width, pSprite->height);
1591 	sTargetRect = MakeRECTFromSpriteSizes(pSprite->x, pSprite->y, pSprite->width, pSprite->height);
1592 
1593 	nSurfaceID = surface_manager->Create_new_surface(pcBitmapName, pSprite->width, pSprite->height, SYSTEM);
1594 	surface_manager->Set_transparent_colour_key(nSurfaceID, g_oIconMenu->GetTransparencyKey());
1595 
1596 	pSurfaceBitmap = surface_manager->Lock_surface(nSurfaceID);
1597 	nPitch = surface_manager->Get_pitch(nSurfaceID);
1598 	SpriteXYFrameDraw(pSurfaceBitmap, nPitch, pSprite->width, pSprite->height, pBitmap, 0, 0, 0, FALSE8, NULL, 255);
1599 	surface_manager->Unlock_surface(nSurfaceID);
1600 }
1601 
DrawSquareSymbol(int32 nX,int32 nY,uint32 nPal,uint32 nSize)1602 inline void _remora::DrawSquareSymbol(int32 nX, int32 nY, uint32 nPal, uint32 nSize) {
1603 	int32 nX1, nX2, nY1, nY2;
1604 
1605 	// Work out the coordinates for it
1606 	nX1 = nX - nSize;
1607 	nX2 = nX + nSize;
1608 	nY1 = nY - nSize;
1609 	nY2 = nY + nSize;
1610 
1611 	// Check the symbol is on the screen.
1612 	if (nX1 < REMORA_SYMBOL_BORDER)
1613 		return;
1614 
1615 	if (nX2 > (REMORA_SCREEN_WIDTH - REMORA_SYMBOL_BORDER))
1616 		return;
1617 
1618 	if (nY1 < REMORA_SYMBOL_BORDER)
1619 		return;
1620 
1621 	if (nY2 > (REMORA_SCREEN_HEIGHT - REMORA_SYMBOL_BORDER))
1622 		return;
1623 
1624 	// Okay, safe to draw.
1625 	DrawGouraudQuad(nX1, nY1, nX2, nY1, nX1, nY2, nX2, nY2, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1626 	                REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1627 	                REMPAL(nPal, CI_BLUE), 255);
1628 }
1629 
DrawTriangleSymbol(int32 nX,int32 nY,uint32 nPal,uint32 nSize)1630 inline void _remora::DrawTriangleSymbol(int32 nX, int32 nY, uint32 nPal, uint32 nSize) {
1631 	int32 nX1, nX2, nY1, nY2, nX3, nY3;
1632 
1633 	// Work out the coordinates for it.
1634 	nX1 = nX;
1635 	nY1 = nY - (nSize << 1);
1636 	nX2 = nX - nSize;
1637 	nY2 = nY + nSize;
1638 	nX3 = nX + nSize;
1639 	nY3 = nY + nSize;
1640 
1641 	// Check the symbol is on the screen.
1642 	if (nX1 < REMORA_SYMBOL_BORDER)
1643 		return;
1644 
1645 	if (nX2 > (REMORA_SCREEN_WIDTH - REMORA_SYMBOL_BORDER))
1646 		return;
1647 
1648 	if (nY1 < REMORA_SYMBOL_BORDER)
1649 		return;
1650 
1651 	if (nY2 > (REMORA_SCREEN_HEIGHT - REMORA_SYMBOL_BORDER))
1652 		return;
1653 
1654 	// Okay, safe to draw.
1655 	DrawGouraudTriangle(nX1, nY1, nX2, nY2, nX3, nY3, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1656 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1657 }
1658 
DrawOctagonSymbol(int32 nX,int32 nY,uint32 nPal,uint32 nSize)1659 inline void _remora::DrawOctagonSymbol(int32 nX, int32 nY, uint32 nPal, uint32 nSize) {
1660 	int32 nX1, nX2, nX3, nX4;
1661 	int32 nY1, nY2, nY3, nY4;
1662 
1663 	// Work out the coordinates for it.
1664 	nX1 = nX - nSize;
1665 	nX2 = nX - ((nSize >> 1));
1666 	nX3 = nX + ((nSize >> 1));
1667 	nX4 = nX + nSize;
1668 
1669 	nY1 = nY - nSize;
1670 	nY2 = nY - ((nSize >> 1));
1671 	nY3 = nY + ((nSize >> 1));
1672 	nY4 = nY + nSize;
1673 
1674 	// Check the symbol is on the screen.
1675 	if (nX1 < REMORA_SYMBOL_BORDER)
1676 		return;
1677 
1678 	if (nX4 > (REMORA_SCREEN_WIDTH - REMORA_SYMBOL_BORDER))
1679 		return;
1680 
1681 	if (nY1 < REMORA_SYMBOL_BORDER)
1682 		return;
1683 
1684 	if (nY4 > (REMORA_SCREEN_HEIGHT - REMORA_SYMBOL_BORDER))
1685 		return;
1686 
1687 	// Okay, safe to draw.
1688 	DrawGouraudTriangle(nX1, nY2, nX2, nY1, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1689 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1690 
1691 	DrawGouraudTriangle(nX2, nY1, nX3, nY1, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1692 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1693 
1694 	DrawGouraudTriangle(nX3, nY1, nX4, nY2, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1695 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1696 
1697 	DrawGouraudTriangle(nX4, nY2, nX4, nY3, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1698 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1699 
1700 	DrawGouraudTriangle(nX4, nY3, nX3, nY4, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1701 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1702 
1703 	DrawGouraudTriangle(nX3, nY4, nX2, nY4, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1704 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1705 
1706 	DrawGouraudTriangle(nX2, nY4, nX1, nY3, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1707 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1708 
1709 	DrawGouraudTriangle(nX1, nY3, nX1, nY2, nX, nY, REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN),
1710 	                    REMPAL(nPal, CI_BLUE), REMPAL(nPal, CI_RED), REMPAL(nPal, CI_GREEN), REMPAL(nPal, CI_BLUE), 255);
1711 }
1712 
DrawGouraudQuad(uint32 nX0,uint32 nY0,uint32 nX1,uint32 nY1,uint32 nX2,uint32 nY2,uint32 nX3,uint32 nY3,uint8 nRed0,uint8 nGreen0,uint8 nBlue0,uint8 nRed1,uint8 nGreen1,uint8 nBlue1,uint8 nRed2,uint8 nGreen2,uint8 nBlue2,uint8 nRed3,uint8 nGreen3,uint8 nBlue3,int32 nOpacity)1713 void DrawGouraudQuad(uint32 nX0, uint32 nY0, uint32 nX1, uint32 nY1, uint32 nX2, uint32 nY2, uint32 nX3, uint32 nY3, uint8 nRed0, uint8 nGreen0, uint8 nBlue0, uint8 nRed1,
1714 					 uint8 nGreen1, uint8 nBlue1, uint8 nRed2, uint8 nGreen2, uint8 nBlue2, uint8 nRed3, uint8 nGreen3, uint8 nBlue3, int32 nOpacity) {
1715 	DrawGouraudTriangle(nX0, nY0, nX1, nY1, nX2, nY2, nRed0, nGreen0, nBlue0, nRed1, nGreen1, nBlue1, nRed2, nGreen2, nBlue2, nOpacity);
1716 
1717 	DrawGouraudTriangle(nX1, nY1, nX2, nY2, nX3, nY3, nRed1, nGreen1, nBlue1, nRed2, nGreen2, nBlue2, nRed3, nGreen3, nBlue3, nOpacity);
1718 }
1719 
DrawGouraudTriangle(uint32 x0,uint32 y0,uint32 x1,uint32 y1,uint32 x2,uint32 y2,uint8 r0,uint8 g0,uint8 b0,uint8 r1,uint8 g1,uint8 b1,uint8 r2,uint8 g2,uint8 b2,int32 nOpacity)1720 void DrawGouraudTriangle(uint32 x0, uint32 y0, uint32 x1, uint32 y1, uint32 x2, uint32 y2, uint8 r0, uint8 g0, uint8 b0, uint8 r1, uint8 g1, uint8 b1, uint8 r2, uint8 g2, uint8 b2,
1721 						 int32 nOpacity) {
1722 	// Range-check the opacity.
1723 	if (nOpacity == 0)
1724 		return;
1725 
1726 	int32 div;
1727 
1728 	int32 minY;
1729 	int32 maxY;
1730 
1731 	Point2DColoured ps[3];
1732 	Point2DColoured *p0;
1733 	Point2DColoured *p1;
1734 
1735 	int32 i;
1736 
1737 	int32 dx, dy;
1738 	int32 x, y;
1739 	int32 ystep;
1740 	int32 xstep;
1741 
1742 	int32 rstep, gstep, bstep, r, g, b;
1743 
1744 	int32 count;
1745 	int32 swap;
1746 
1747 	uint8 *pSurfaceBitmap; // Pointer to the surface we are going to draw on.
1748 	uint32 nPitch;         // Pitch of the surface.
1749 
1750 	ps[0].x = x0;
1751 	ps[0].y = y0;
1752 	ps[0].r = r0;
1753 	ps[0].g = g0;
1754 	ps[0].b = b0;
1755 	ps[1].x = x1;
1756 	ps[1].y = y1;
1757 	ps[1].r = r1;
1758 	ps[1].g = g1;
1759 	ps[1].b = b1;
1760 	ps[2].x = x2;
1761 	ps[2].y = y2;
1762 	ps[2].r = r2;
1763 	ps[2].g = g2;
1764 	ps[2].b = b2;
1765 
1766 	// Rendering colour array
1767 	uint8 newCol[8] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
1768 
1769 	// GET MIN AND MAX
1770 	minY = ps[0].y;
1771 	maxY = ps[0].y;
1772 	for (i = 1; i < 3; i++) {
1773 		if ((ps[i].y) < minY)
1774 			minY = ps[i].y;
1775 		if ((ps[i].y) > maxY)
1776 			maxY = ps[i].y;
1777 	}
1778 
1779 	// clamp min y and max y
1780 	if (minY < 0)
1781 		minY = 0;
1782 	if (maxY >= REMORA_SCREEN_HEIGHT)
1783 		maxY = REMORA_SCREEN_HEIGHT - 1;
1784 
1785 	// RESET SPANS
1786 	for (i = minY; i <= maxY; i++) {
1787 		remora_spans[i].x0 = -99999;
1788 		remora_spans[i].x1 = -99999;
1789 	}
1790 
1791 	// scan line
1792 	for (i = 0; i < 3; i++) {
1793 		p0 = ps + i;
1794 
1795 		// work out next point along
1796 		if (i == 2)
1797 			p1 = ps;
1798 		else
1799 			p1 = ps + i + 1;
1800 
1801 		if (p0->y == p1->y) {
1802 			xstep = 0;
1803 			ystep = 0;
1804 			rstep = 0;
1805 			gstep = 0;
1806 			bstep = 0;
1807 		} else {
1808 			dx = (p1->x) - (p0->x);
1809 
1810 			if (p1->y > p0->y) {
1811 				dy = (p1->y) - (p0->y);
1812 				ystep = 1;
1813 			} else {
1814 				dy = (p0->y) - (p1->y);
1815 				ystep = -1;
1816 			}
1817 
1818 			div = (0xffff) / dy;
1819 
1820 			xstep = (div * dx) >> 8;
1821 
1822 			rstep = (div * (p1->r - p0->r)) >> 8;
1823 			gstep = (div * (p1->g - p0->g)) >> 8;
1824 			bstep = (div * (p1->b - p0->b)) >> 8;
1825 		}
1826 
1827 		x = p0->x << 8;
1828 		y = p0->y;
1829 
1830 		r = p0->r << 8;
1831 		g = p0->g << 8;
1832 		b = p0->b << 8;
1833 
1834 		do {
1835 			if (r < 0)
1836 				r = 0;
1837 			if (g < 0)
1838 				g = 0;
1839 			if (b < 0)
1840 				b = 0;
1841 			if ((y > 0) && (y < REMORA_SCREEN_HEIGHT)) {
1842 				int32 sx = x >> 8;
1843 				if (remora_spans[y].x0 == -99999) {
1844 					remora_spans[y].x0 = sx;
1845 					remora_spans[y].r0 = (uint8)(r >> 8);
1846 					remora_spans[y].g0 = (uint8)(g >> 8);
1847 					remora_spans[y].b0 = (uint8)(b >> 8);
1848 				} else if (sx > remora_spans[y].x0) {
1849 					remora_spans[y].x1 = sx;
1850 					remora_spans[y].r1 = (uint8)(r >> 8);
1851 					remora_spans[y].g1 = (uint8)(g >> 8);
1852 					remora_spans[y].b1 = (uint8)(b >> 8);
1853 				} else { // must swap
1854 					swap = remora_spans[y].x0;
1855 					remora_spans[y].x0 = sx;
1856 					remora_spans[y].x1 = swap;
1857 
1858 					swap = remora_spans[y].r0;
1859 					remora_spans[y].r0 = (uint8)(r >> 8);
1860 					remora_spans[y].r1 = (uint8)swap;
1861 
1862 					swap = remora_spans[y].g0;
1863 					remora_spans[y].g0 = (uint8)(g >> 8);
1864 					remora_spans[y].g1 = (uint8)swap;
1865 
1866 					swap = remora_spans[y].b0;
1867 					remora_spans[y].b0 = (uint8)(b >> 8);
1868 					remora_spans[y].b1 = (uint8)swap;
1869 				}
1870 			}
1871 			x += xstep;
1872 			y += ystep;
1873 
1874 			r += rstep;
1875 			g += gstep;
1876 			b += bstep;
1877 		} while (y != p1->y);
1878 	}
1879 
1880 	// draw remora_spans
1881 
1882 	// Get the surface to draw on.
1883 	pSurfaceBitmap = surface_manager->Lock_surface(g_oRemora->GetRemoraSurfaceId());
1884 
1885 	// Get its pitch.
1886 	nPitch = surface_manager->Get_pitch(g_oRemora->GetRemoraSurfaceId()) >> 2;
1887 
1888 	// Check to see if pixel-merging or the special brightening mode are required.  This check is done
1889 	// here to avoid repeating it in the inner loop.
1890 	if (nOpacity < 0) {
1891 		nOpacity = -nOpacity;
1892 
1893 		// The new pixel is used only to brighten what is already in the surface.
1894 		for (y = minY; y <= maxY; ++y) {
1895 			count = remora_spans[y].x1 - remora_spans[y].x0;
1896 			if (count > 0) {
1897 				div = 0xffff / count;
1898 
1899 				rstep = (div * (remora_spans[y].r1 - remora_spans[y].r0)) >> 8;
1900 				gstep = (div * (remora_spans[y].g1 - remora_spans[y].g0)) >> 8;
1901 				bstep = (div * (remora_spans[y].b1 - remora_spans[y].b0)) >> 8;
1902 
1903 				int32 ar = remora_spans[y].r0 << 8;
1904 				int32 ag = remora_spans[y].g0 << 8;
1905 				int32 ab = remora_spans[y].b0 << 8;
1906 
1907 				x = remora_spans[y].x0;
1908 
1909 				while (x < 0) {
1910 					ar += rstep;
1911 					ag += gstep;
1912 					ab += bstep;
1913 					++x;
1914 				}
1915 
1916 				int32 xLim = remora_spans[y].x1;
1917 				if (xLim > REMORA_SCREEN_WIDTH)
1918 					xLim = REMORA_SCREEN_WIDTH;
1919 
1920 				uint32 *left = (uint32 *)(pSurfaceBitmap) + (nPitch * y) + x;
1921 				while (x < xLim) {
1922 					if (ar < 0)
1923 						ar = 0;
1924 					if (ag < 0)
1925 						ag = 0;
1926 					if (ab < 0)
1927 						ab = 0;
1928 					newCol[0] = (uint8)(ab >> 8);
1929 					newCol[1] = (uint8)(ag >> 8);
1930 					newCol[2] = (uint8)(ar >> 8);
1931 
1932 #if 1
1933 					// 32-bit BGR pixel
1934 					uint8 *pixel = (uint8 *)&left;
1935 					uint8 *add = (uint8 *)&newCol;
1936 					// Add from RGB components
1937 					for (int32 p = 0; p < 3; p++) {
1938 						pixel[p] = MIN(255, pixel[p] + add[p]);
1939 					}
1940 #else
1941 					__asm {
1942 						lea  edi, newCol    ; // Get the adress of the colour table
1943 						mov  esi, left      ; // Load the address of the pixel pointer
1944 						movq mm0, [edi]     ; // Load the colour
1945 						pxor mm4, mm4       ; // Clear mm4 for packing/unpacking
1946 						movd mm5, [esi]     ; // Get a pixel
1947 						paddusb mm5, mm0    ; // Add the colour
1948 						movd  [esi], mm5    ; // Store the new colour
1949 						emms
1950 					}
1951 #endif
1952 
1953 					++left;
1954 					ar += rstep;
1955 					ag += gstep;
1956 					ab += bstep;
1957 					++x;
1958 				}
1959 			}
1960 		}
1961 	} else if (nOpacity == 255) {
1962 		// New pixel fully overwrites the existing pixel.
1963 		for (y = minY; y <= maxY; y++) {
1964 			count = remora_spans[y].x1 - remora_spans[y].x0;
1965 			if (count > 0) {
1966 				div = (0xffff) / count;
1967 
1968 				rstep = (div * (remora_spans[y].r1 - remora_spans[y].r0)) >> 8;
1969 				gstep = (div * (remora_spans[y].g1 - remora_spans[y].g0)) >> 8;
1970 				bstep = (div * (remora_spans[y].b1 - remora_spans[y].b0)) >> 8;
1971 
1972 				r = remora_spans[y].r0 << 8;
1973 				g = remora_spans[y].g0 << 8;
1974 				b = remora_spans[y].b0 << 8;
1975 
1976 				x = remora_spans[y].x0;
1977 
1978 				if (x < 0) {
1979 					r -= rstep * x;
1980 					g -= gstep * x;
1981 					b -= bstep * x;
1982 					count += x;
1983 					x = 0;
1984 				}
1985 
1986 				if (remora_spans[y].x1 >= REMORA_SCREEN_WIDTH) {
1987 					count -= (remora_spans[y].x1 - REMORA_SCREEN_WIDTH);
1988 				}
1989 
1990 				uint32 *left = (uint32 *)(pSurfaceBitmap) + (nPitch * y) + x;
1991 				do {
1992 					*left = ((b >> 8) & 0xff) + (g & 0xff00) + ((r << 8) & 0xff0000);
1993 
1994 					++left;
1995 
1996 					r += rstep;
1997 					g += gstep;
1998 					b += bstep;
1999 					++x;
2000 
2001 					count--;
2002 				} while (count > 0);
2003 			}
2004 		}
2005 	} else {
2006 		// New pixel RGB needs merging into the pixel already in the surface.
2007 		for (y = minY; y <= maxY; y++) {
2008 			// do this after working out go and tex bits
2009 			count = remora_spans[y].x1 - remora_spans[y].x0;
2010 			if ((count > 0) && (remora_spans[y].x1 > 0)) {
2011 				x = remora_spans[y].x0;
2012 
2013 				if (x < 0)
2014 					x = 0;
2015 
2016 				int32 xLim = remora_spans[y].x1;
2017 				if (xLim > REMORA_SCREEN_WIDTH)
2018 					xLim = REMORA_SCREEN_WIDTH;
2019 
2020 				xLim -= x;
2021 
2022 				if (xLim < 1)
2023 					continue;
2024 
2025 				uint32 *left = (uint32 *)(pSurfaceBitmap) + (nPitch * y) + x;
2026 
2027 				// colours
2028 				newCol[0] = (uint8)remora_spans[y].b0;
2029 				newCol[2] = (uint8)remora_spans[y].g0;
2030 				newCol[4] = (uint8)remora_spans[y].r0;
2031 
2032 #if 1
2033 				// 32-bit BGR pixel
2034 				uint8 *pixel = (uint8 *)&left;
2035 				uint8 *add = (uint8 *)&newCol;
2036 				// Add from RGB components
2037 				for (int32 p = 0; p < 3; p++) {
2038 					pixel[p] = (pixel[p] + add[p]) >> 1;
2039 				}
2040 #else
2041 				_asm {
2042 					lea  edi, newCol; // Get the adress of the colour table
2043 					mov  ecx, xLim  ; // Load the counter
2044 					movq mm0, [edi] ; // Load the colour
2045 					mov  esi, left  ; // Load the address of the pixel pointer
2046 					pxor mm4, mm4   ; // Clear mm4 for packing/unpacking
2047 					sub  esi, 4     ; // compensate for index
2048 
2049 					remora_blend_loop:
2050 					movd      mm5, [esi+ecx*4]; // Get a pixel
2051 					punpcklbw mm5, mm4  ; // Unpack the pixel into words
2052 					paddusw   mm5, mm0  ; // Add the colour
2053 					psrlw     mm5, 1    ; // Divide by 2
2054 					packuswb  mm5, mm4  ; // Repack into bytes
2055 					movd  [esi+ecx*4], mm5  ; // Store the new colour
2056 					dec ecx             ; // decrement the counter
2057 					jne remora_blend_loop   ; // On to the next
2058 					emms
2059 				}
2060 #endif
2061 			}
2062 		}
2063 	}
2064 
2065 	// Release the surface now we have done drawing.
2066 	surface_manager->Unlock_surface(g_oRemora->GetRemoraSurfaceId());
2067 }
2068 
CohenSutherland(DXrect oRect,int32 & nZ1,int32 & nY1,int32 & nX2,int32 & nY2,bool8 bClip) const2069 bool8 _remora::CohenSutherland(DXrect oRect, int32 &nZ1, int32 &nY1, int32 &nX2, int32 &nY2, bool8 bClip) const {
2070 	uint32 nOutcode1, nOutcode2;
2071 	uint32 nOutcodeOutside;
2072 
2073 	int32 nNewX = nZ1;
2074 	int32 nNewY = nY1;
2075 
2076 	// Compute outcode for line's endpoints.
2077 	nOutcode1 = ComputeOutcode(oRect, nZ1, nY1);
2078 	nOutcode2 = ComputeOutcode(oRect, nX2, nY2);
2079 
2080 	// Main loop.  In the full algorithm, this runs until clipping is complete, but since this function has to return
2081 	// whether or not the line intersects the rectangle, we might be able to quit early.
2082 	while (TRUE8) {
2083 		// If both outcodes are zero then the line lies wholly within the rectangle.
2084 		if ((nOutcode1 | nOutcode2) == 0x00000000)
2085 			return (TRUE8);
2086 
2087 		// If logical AND of codes is not zero then line cannot intersect the rectangle.
2088 		if ((nOutcode1 & nOutcode2) != 0x00000000)
2089 			return (FALSE8);
2090 
2091 		// Right the easy tests have failed to give the answer.  Now we pick the endpoint outside the clip rectangle
2092 		// and calculate the line's intersection with the infinitely-extended sides of the clip rectangle.
2093 		nOutcodeOutside = (nOutcode1 != 0x00000000) ? nOutcode1 : nOutcode2;
2094 
2095 		if (TestOutcode(nOutcodeOutside, OUTCODE_TOP)) {
2096 			// In here, I calculate the intersection.  If the function is not required to do clipping, I might be able to
2097 			// quit early if the intersection point with the INFINITE rectangle-side passes through the ACTUAL rectangle-side.
2098 			nNewX = nZ1 + (int32)((float)(nX2 - nZ1) * (float)(oRect.top - nY1) / (float)(nY2 - nY1));
2099 			if (!bClip) {
2100 				if ((nNewX >= oRect.left) && (nNewX <= oRect.right))
2101 					return (TRUE8);
2102 			}
2103 			nNewY = oRect.top;
2104 		} else if (TestOutcode(nOutcodeOutside, OUTCODE_BOTTOM)) {
2105 			// See notes on previous if().
2106 			nNewX = nZ1 + (int32)((float)(nX2 - nZ1) * (float)(oRect.bottom - nY1) / (float)(nY2 - nY1));
2107 			if (!bClip) {
2108 				if ((nNewX >= oRect.left) && (nNewX <= oRect.right))
2109 					return (TRUE8);
2110 			}
2111 			nNewY = oRect.bottom;
2112 		} else if (TestOutcode(nOutcodeOutside, OUTCODE_RIGHT)) {
2113 			// See notes on first if().
2114 			nNewY = nY1 + (int32)((float)(nY2 - nY1) * (float)(oRect.right - nZ1) / (float)(nX2 - nZ1));
2115 			if (!bClip) {
2116 				if ((nNewY <= oRect.bottom) && (nNewY >= oRect.top))
2117 					return (TRUE8);
2118 			}
2119 			nNewX = oRect.right;
2120 		} else if (TestOutcode(nOutcodeOutside, OUTCODE_LEFT)) {
2121 			// See notes on first if().
2122 			nNewY = nY1 + (int32)((float)(nY2 - nY1) * (float)(oRect.left - nZ1) / (float)(nX2 - nZ1));
2123 			if (!bClip) {
2124 				if ((nNewY <= oRect.bottom) && (nNewY >= oRect.top))
2125 					return (TRUE8);
2126 			}
2127 			nNewX = oRect.left;
2128 		}
2129 
2130 		// Now move the outside endpoint to the intersection point we've just calculated and iterate again.
2131 		if (nOutcodeOutside == nOutcode1) {
2132 			nZ1 = nNewX;
2133 			nY1 = nNewY;
2134 			nOutcode1 = ComputeOutcode(oRect, nZ1, nY1);
2135 		} else {
2136 			nX2 = nNewX;
2137 			nY2 = nNewY;
2138 			nOutcode2 = ComputeOutcode(oRect, nX2, nY2);
2139 		}
2140 	} // end while
2141 }
2142 
ComputeOutcode(DXrect oRect,int32 nX,int32 nY) const2143 uint32 _remora::ComputeOutcode(DXrect oRect, int32 nX, int32 nY) const {
2144 	uint32 nRetVal = 0x00000000;
2145 
2146 	if (nX < oRect.left)
2147 		nRetVal = OUTCODE_LEFT;
2148 	else if (nX > oRect.right)
2149 		nRetVal = OUTCODE_RIGHT;
2150 
2151 	if (nY < oRect.top)
2152 		nRetVal = nRetVal | OUTCODE_TOP;
2153 	else if (nY > oRect.bottom)
2154 		nRetVal = nRetVal | OUTCODE_BOTTOM;
2155 
2156 	return (nRetVal);
2157 }
2158 
2159 } // End of namespace ICB
2160