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