1 /* ScummVM - Graphic Adventure Engine
2 *
3 * ScummVM is the legal property of its developers, whose names
4 * are too numerous to list here. Please refer to the COPYRIGHT
5 * file distributed with this source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20 *
21 */
22
23 #include "startrek/iwfile.h"
24 #include "startrek/resource.h"
25 #include "startrek/room.h"
26 #include "startrek/startrek.h"
27
28 namespace StarTrek {
29
initAwayMission()30 void StarTrekEngine::initAwayMission() {
31 _awayMission = AwayMission(); // Initialize members to 0
32
33 // memset(bitmapBuffer->pixels, 0, 0xfa00);
34
35 _resource->setTxtFileName("ground");
36
37 // sub_23a60(); // TODO
38 _sound->loadMusicFile("ground");
39
40 loadRoom(_missionToLoad, _roomIndexToLoad);
41 _roomIndexToLoad = -1;
42
43 // Load crew positions for beaming in
44 initAwayCrewPositions(4);
45 }
46
runAwayMission()47 void StarTrekEngine::runAwayMission() {
48 while (_gameMode == GAMEMODE_AWAYMISSION && !_resetGameMode) {
49 // Original game manipulates the stack when the room changes to return execution
50 // to this point. Instead of doing that, just check if a variable is set.
51 if (_roomIndexToLoad != -1 && _spawnIndexToLoad != -1) {
52 loadRoomIndex(_roomIndexToLoad, _spawnIndexToLoad);
53 _roomIndexToLoad = -1;
54 _spawnIndexToLoad = -1;
55 }
56
57 handleAwayMissionEvents();
58
59 Common::Point mousePos = _gfx->getMousePos();
60 _awayMission.mouseX = mousePos.x;
61 _awayMission.mouseY = mousePos.y;
62
63 assert(_actionQueue.size() <= 16);
64 while (!_actionQueue.empty()) {
65 // sub_200e7(); // TODO
66 // sub_20118();
67 handleAwayMissionAction();
68 }
69 }
70 }
71
cleanupAwayMission()72 void StarTrekEngine::cleanupAwayMission() {
73 // TODO
74 }
75
loadRoom(const Common::String & missionName,int roomIndex)76 void StarTrekEngine::loadRoom(const Common::String &missionName, int roomIndex) {
77 _keyboardControlsMouse = true;
78
79 _missionName = _missionToLoad;
80 _roomIndex = roomIndex;
81
82 _roomFrameCounter = 0;
83 _awayMission.disableInput = false;
84
85 _gfx->fadeoutScreen();
86 _sound->stopAllVocSounds();
87
88 _gfx->setBackgroundImage(getScreenName());
89 _gfx->loadPri(getScreenName());
90 _gfx->loadPalette("palette");
91 _gfx->copyBackgroundScreen();
92
93 _room = new Room(this, getScreenName());
94
95 // Original sets up bytes 0-3 of rdf file as "remote function caller"
96
97 bool isDemo = getFeatures() & GF_DEMO;
98 if (!isDemo)
99 _room->loadMapFile(getScreenName());
100
101 _awayMission.activeAction = ACTION_WALK;
102
103 removeDrawnActorsFromScreen();
104 initActors();
105
106 Fixed8 num = _room->getMaxScale() - _room->getMinScale();
107 int16 den = _room->getMaxY() - _room->getMinY() + 1;
108 _playerActorScale = Fixed16(num) / den;
109
110 _actionQueue.clear();
111
112 if (!isDemo) {
113 int16 addr = _room->getBanDataStart();
114 while (addr != _room->getBanDataEnd()) {
115 Common::String name((char *)&_room->_rdfData[addr]);
116 loadBanFile(name);
117 addr += strlen((char *)&_room->_rdfData[addr]) + 1;
118 }
119 }
120 }
121
initAwayCrewPositions(int warpEntryIndex)122 void StarTrekEngine::initAwayCrewPositions(int warpEntryIndex) {
123 _sound->stopAllVocSounds();
124
125 memset(_awayMission.crewDirectionsAfterWalk, 0xff, 4);
126
127 switch (warpEntryIndex) {
128 case 0: // 0-3: Crew spawns in a spot and walks to a spot.
129 case 1:
130 case 2:
131 case 3:
132 for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
133 Common::String anim = getCrewmanAnimFilename(i, "walk");
134
135 int16 rdfOffset = RDF_ROOM_ENTRY_POSITIONS + warpEntryIndex * 32 + i * 8;
136
137 int16 srcX = _room->readRdfWord(rdfOffset + 0); // Position to spawn at
138 int16 srcY = _room->readRdfWord(rdfOffset + 2);
139 int16 destX = _room->readRdfWord(rdfOffset + 4); // Position to walk to
140 int16 destY = _room->readRdfWord(rdfOffset + 6);
141
142 actorWalkToPosition(i, anim, srcX, srcY, destX, destY);
143 }
144
145 _kirkActor->triggerActionWhenAnimFinished = true;
146 _kirkActor->finishedAnimActionParam = 0xff;
147 _awayMission.disableInput = true;
148 _warpHotspotsActive = false;
149 break;
150 case 4: // Crew is beaming in.
151 warpEntryIndex -= 4;
152 for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
153 Common::String animFilename = getCrewmanAnimFilename(i, "tele");
154 Common::Point warpPos = _room->getBeamInPosition(i);
155 loadActorAnimWithRoomScaling(i, animFilename, warpPos.x, warpPos.y);
156 }
157 _kirkActor->triggerActionWhenAnimFinished = true;
158 _kirkActor->finishedAnimActionParam = 0xff;
159 _awayMission.disableInput = true;
160 _sound->playSoundEffectIndex(0x09);
161 _warpHotspotsActive = false;
162 break;
163 case 5: // Crew spawns in directly at a position.
164 for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
165 Common::String animFilename = getCrewmanAnimFilename(i, "stnds");
166 Common::Point warpPos = _room->getSpawnPosition(i);
167 loadActorAnimWithRoomScaling(i, animFilename, warpPos.x, warpPos.y);
168 }
169 _warpHotspotsActive = true;
170 break;
171 case 6:
172 loadBridgeActors();
173 break;
174 default:
175 warning("Invalid parameter (%d) to initAwayCrewPositions", warpEntryIndex);
176 break;
177 }
178 }
179
handleAwayMissionEvents()180 void StarTrekEngine::handleAwayMissionEvents() {
181 TrekEvent event;
182
183 if (popNextEvent(&event)) {
184 switch (event.type) {
185 case TREKEVENT_TICK:
186 updateActorAnimations();
187 updateCrewmanGetupTimers();
188
189 updateMouseBitmap();
190 renderBanBelowSprites();
191 _gfx->drawAllSprites(false);
192 renderBanAboveSprites();
193 _gfx->updateScreen();
194
195 _sound->checkLoopMusic();
196 updateAwayMissionTimers();
197 _frameIndex++;
198 _roomFrameCounter++;
199 addAction(ACTION_TICK, _roomFrameCounter & 0xff, (_roomFrameCounter >> 8) & 0xff, 0);
200 if (_roomFrameCounter >= 2)
201 _gfx->incPaletteFadeLevel();
202 break;
203
204 case TREKEVENT_LBUTTONDOWN:
205 awayMissionLeftClick();
206 break; // End of TREKEVENT_LBUTTONDOWN
207
208 case TREKEVENT_MOUSEMOVE:
209 break;
210
211 case TREKEVENT_RBUTTONDOWN:
212 awayMissionSelectAction(true);
213 break;
214
215 case TREKEVENT_KEYDOWN:
216 if (_awayMission.disableInput)
217 break;
218
219 switch (event.kbd.keycode) {
220 case Common::KEYCODE_ESCAPE:
221 case Common::KEYCODE_SPACE:
222 case Common::KEYCODE_F2:
223 awayMissionSelectAction(true);
224 break;
225
226 case Common::KEYCODE_t:
227 hideInventoryIcons();
228 _awayMission.activeAction = ACTION_TALK;
229 awayMissionSelectAction(false);
230 break;
231
232 case Common::KEYCODE_l:
233 hideInventoryIcons();
234 _awayMission.activeAction = ACTION_LOOK;
235 awayMissionSelectAction(false);
236 break;
237
238 case Common::KEYCODE_g:
239 hideInventoryIcons();
240 _awayMission.activeAction = ACTION_GET;
241 awayMissionSelectAction(false);
242 break;
243
244 case Common::KEYCODE_u:
245 hideInventoryIcons();
246 _awayMission.activeAction = ACTION_USE;
247 awayMissionSelectAction(false);
248 break;
249
250 case Common::KEYCODE_w:
251 hideInventoryIcons();
252 _awayMission.activeAction = ACTION_WALK;
253 break;
254
255 case Common::KEYCODE_i:
256 if (_awayMission.activeAction == ACTION_USE) {
257 hideInventoryIcons();
258 int clickedObject = showInventoryMenu(50, 50, true);
259 if (clickedObject == -1)
260 clickedObject = -2;
261 awayMissionUseObject(clickedObject);
262 } else if (_awayMission.activeAction == ACTION_LOOK) {
263 hideInventoryIcons();
264 int clickedObject = showInventoryMenu(50, 50, true);
265 if (clickedObject == -1)
266 clickedObject = -2;
267 awayMissionGetLookOrTalk(clickedObject);
268 }
269 break;
270
271 case Common::KEYCODE_RETURN:
272 case Common::KEYCODE_KP_ENTER:
273 case Common::KEYCODE_F1:
274 awayMissionLeftClick();
275 break;
276
277 case Common::KEYCODE_c:
278 // Bridge computer, where the player can ask about various topics.
279 // ENHANCEMENT: Normally, this is only available when in the bridge.
280 // We also show it in missions.
281 if (!(getFeatures() & GF_DEMO))
282 handleBridgeComputer();
283 break;
284
285 case Common::KEYCODE_p:
286 // Pause game
287 // TODO
288 break;
289
290 case Common::KEYCODE_e:
291 if (event.kbd.flags & Common::KBD_CTRL) {
292 _sound->toggleSfx();
293 }
294 break;
295
296 case Common::KEYCODE_m:
297 if (event.kbd.flags & Common::KBD_CTRL) {
298 _sound->toggleMusic();
299 }
300 break;
301
302 case Common::KEYCODE_q:
303 if (event.kbd.flags & Common::KBD_CTRL) {
304 showQuitGamePrompt(20, 20);
305 }
306 break;
307
308 default:
309 break;
310 }
311 break;
312
313 default:
314 break;
315 }
316 }
317 }
318
awayMissionLeftClick()319 void StarTrekEngine::awayMissionLeftClick() {
320 if (_awayMission.disableInput)
321 return;
322
323 switch (_awayMission.activeAction) {
324 case ACTION_WALK: {
325 if (_awayMission.disableWalking)
326 break;
327 _kirkActor->sprite.drawMode = 1; // Hide these objects for function call below?
328 _spockActor->sprite.drawMode = 1;
329 _mccoyActor->sprite.drawMode = 1;
330 _redshirtActor->sprite.drawMode = 1;
331
332 int16 clickedObject = findObjectAt(_gfx->getMousePos());
333
334 _kirkActor->sprite.drawMode = 0;
335 _spockActor->sprite.drawMode = 0;
336 _mccoyActor->sprite.drawMode = 0;
337 _redshirtActor->sprite.drawMode = 0;
338
339 if (walkActiveObjectToHotspot())
340 break;
341
342 if (clickedObject > OBJECT_KIRK && clickedObject < ITEMS_START)
343 addAction(ACTION_WALK, clickedObject, 0, 0);
344 else {
345 Common::String animFilename = getCrewmanAnimFilename(OBJECT_KIRK, "walk");
346 Common::Point mousePos = _gfx->getMousePos();
347 actorWalkToPosition(OBJECT_KIRK, animFilename, _kirkActor->pos.x, _kirkActor->pos.y, mousePos.x, mousePos.y);
348 }
349 break;
350 }
351
352 case ACTION_USE: {
353 if (_awayMission.activeObject == OBJECT_REDSHIRT && (_awayMission.redshirtDead || (_awayMission.crewDownBitset & (1 << OBJECT_REDSHIRT)))) {
354 hideInventoryIcons();
355 _awayMission.activeAction = ACTION_WALK;
356 break;
357 }
358
359 int16 clickedObject = findObjectAt(_gfx->getMousePos());
360 hideInventoryIcons();
361
362 if (clickedObject == OBJECT_INVENTORY_ICON) {
363 clickedObject = showInventoryMenu(50, 50, false);
364
365 // -1 means "clicked on something unknown"; -2 means "clicked on
366 // nothing". In the case of the inventory, either one clicks on an
367 // inventory item, or no action is performed.
368 if (clickedObject == -1)
369 clickedObject = -2;
370 }
371
372 awayMissionUseObject(clickedObject);
373 break;
374 }
375
376 case ACTION_GET:
377 case ACTION_LOOK:
378 case ACTION_TALK: {
379 int16 clickedObject = findObjectAt(_gfx->getMousePos());
380 if (!isObjectUnusable(clickedObject, _awayMission.activeAction)) {
381 hideInventoryIcons();
382
383 if (clickedObject == OBJECT_INVENTORY_ICON) {
384 clickedObject = showInventoryMenu(50, 50, false);
385 if (clickedObject == -1)
386 clickedObject = -2;
387 }
388
389 awayMissionGetLookOrTalk(clickedObject);
390 }
391 break;
392 }
393
394 default:
395 break;
396 }
397 }
398
awayMissionSelectAction(bool openActionMenu)399 void StarTrekEngine::awayMissionSelectAction(bool openActionMenu) {
400 if (openActionMenu) {
401 if (_awayMission.disableInput)
402 return;
403 hideInventoryIcons();
404 _sound->playSoundEffectIndex(kSfxButton);
405 _awayMission.activeAction = showActionMenu();
406 }
407
408 if (_awayMission.activeAction == ACTION_USE) {
409 int16 clickedObject = selectObjectForUseAction();
410 if (clickedObject == -1)
411 return;
412 else
413 _awayMission.activeObject = clickedObject;
414 }
415 if (_awayMission.activeAction == ACTION_USE
416 && _awayMission.activeObject == OBJECT_ICOMM && (_awayMission.crewDownBitset & (1 << OBJECT_KIRK)) == 0) {
417 if (!walkActiveObjectToHotspot()) {
418 addAction(_awayMission.activeAction, _awayMission.activeObject, 0, 0);
419 _sound->playVoc("communic");
420 _awayMission.activeAction = ACTION_WALK;
421 }
422 } else if (_awayMission.activeAction == ACTION_LOOK)
423 showInventoryIcons(false);
424 else if (_awayMission.activeAction == ACTION_USE && (_awayMission.crewDownBitset & (1 << OBJECT_KIRK)) == 0)
425 showInventoryIcons(true);
426 }
427
awayMissionUseObject(int16 clickedObject)428 void StarTrekEngine::awayMissionUseObject(int16 clickedObject) {
429 _awayMission.passiveObject = clickedObject;
430
431 bool activeIsCrewman = _awayMission.activeObject <= OBJECT_REDSHIRT;
432 bool activeIsItem = _awayMission.activeObject >= ITEMS_START && _awayMission.activeObject < ITEMS_END;
433 bool passiveIsCrewman = _awayMission.passiveObject <= OBJECT_REDSHIRT;
434 bool passiveIsItem = _awayMission.passiveObject >= ITEMS_START && _awayMission.passiveObject <= ITEMS_END; // FIXME: "<= ITEMS_END" doesn't make sense?
435
436 bool tryWalkToHotspot = false;
437 bool showInventory = false;
438
439 if (clickedObject == -2)
440 tryWalkToHotspot = true;
441 else if (_room->actionHasCode(ACTION_USE, _awayMission.activeObject, _awayMission.passiveObject, 0))
442 tryWalkToHotspot = true;
443 else if (_awayMission.activeObject == OBJECT_MCCOY && _room->actionHasCode(ACTION_USE, OBJECT_IMEDKIT, _awayMission.passiveObject, 0))
444 tryWalkToHotspot = true;
445 // CHECKME: Identical to the previous check, thus never used
446 //else if (_awayMission.activeObject == OBJECT_MCCOY && _room->actionHasCode(ACTION_USE, OBJECT_IMEDKIT, _awayMission.passiveObject, 0))
447 // tryWalkToHotspot = true;
448 else if (_awayMission.activeObject == OBJECT_SPOCK && _room->actionHasCode(ACTION_USE, OBJECT_ISTRICOR, _awayMission.passiveObject, 0))
449 tryWalkToHotspot = true;
450
451 if (!tryWalkToHotspot) {
452 if ((activeIsCrewman && passiveIsCrewman)
453 || (activeIsCrewman && passiveIsItem)
454 || (activeIsItem && passiveIsItem)) {
455 if (_awayMission.passiveObject == OBJECT_ICOMM) {
456 if (walkActiveObjectToHotspot())
457 return;
458 addAction(ACTION_USE, OBJECT_ICOMM, 0, 0);
459 _sound->playVoc("commun30");
460 if (_awayMission.activeObject <= OBJECT_REDSHIRT) {
461 showInventory = true;
462 } else {
463 _awayMission.activeAction = ACTION_WALK;
464 return;
465 }
466 }
467
468 _awayMission.activeObject = _awayMission.passiveObject;
469 showInventory = true;
470 } else
471 tryWalkToHotspot = true;
472 }
473
474 if (tryWalkToHotspot) {
475 if (!walkActiveObjectToHotspot()) {
476 if (clickedObject != -2)
477 addAction(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0);
478 showInventory = true;
479 }
480 }
481
482 if (showInventory && !(_awayMission.crewDownBitset & (1 << OBJECT_KIRK)))
483 showInventoryIcons(true);
484 }
485
awayMissionGetLookOrTalk(int16 clickedObject)486 void StarTrekEngine::awayMissionGetLookOrTalk(int16 clickedObject) {
487 _awayMission.activeObject = clickedObject;
488
489 if (walkActiveObjectToHotspot())
490 return;
491
492 if (clickedObject != -2)
493 addAction(_awayMission.activeAction, _awayMission.activeObject, 0, 0);
494
495 if (_awayMission.activeAction == ACTION_LOOK && !(_awayMission.crewDownBitset & (1 << OBJECT_KIRK)))
496 showInventoryIcons(false);
497 }
498
unloadRoom()499 void StarTrekEngine::unloadRoom() {
500 _gfx->fadeoutScreen();
501 // sub_2394b(); // TODO
502 removeDrawnActorsFromScreen();
503 delete _room;
504 _room = nullptr;
505 delete _mapFile;
506 _mapFile = nullptr;
507 delete _iwFile;
508 _iwFile = nullptr;
509 }
510
loadActorAnimWithRoomScaling(int actorIndex,const Common::String & animName,int16 x,int16 y)511 int StarTrekEngine::loadActorAnimWithRoomScaling(int actorIndex, const Common::String &animName, int16 x, int16 y) {
512 Fixed8 scale = getActorScaleAtPosition(y);
513 return loadActorAnim(actorIndex, animName, x, y, scale);
514 }
515
getActorScaleAtPosition(int16 y)516 Fixed8 StarTrekEngine::getActorScaleAtPosition(int16 y) {
517 int16 maxY = _room->getMaxY();
518 int16 minY = _room->getMinY();
519 Fixed8 minScale = _room->getMinScale();
520
521 if (y > maxY)
522 y = maxY;
523 if (y < minY)
524 y = minY;
525
526 return Fixed8(_playerActorScale * (y - minY)) + minScale;
527 }
528
getRoom()529 Room *StarTrekEngine::getRoom() {
530 return _room;
531 }
532
addAction(const Action & action)533 void StarTrekEngine::addAction(const Action &action) {
534 if (action.type != ACTION_TICK)
535 debugC(kDebugGeneral, 4, "Action %d: %x, %x, %x", action.type, action.b1, action.b2, action.b3);
536 _actionQueue.push(action);
537 }
538
addAction(int8 type,byte b1,byte b2,byte b3)539 void StarTrekEngine::addAction(int8 type, byte b1, byte b2, byte b3) {
540 const Action a = {type, b1, b2, b3};
541 addAction(a);
542 }
543
handleAwayMissionAction()544 void StarTrekEngine::handleAwayMissionAction() {
545 Action action = _actionQueue.pop();
546
547 if ((action.type == ACTION_FINISHED_ANIMATION || action.type == ACTION_FINISHED_WALKING) && action.b1 == 0xff) {
548 // Just finished walking or beaming into a room
549 if (_awayMission.disableInput == 1)
550 _awayMission.disableInput = false;
551 _warpHotspotsActive = true;
552 return;
553 } else if (action.type == ACTION_FINISHED_WALKING && action.b1 >= 0xe0) {
554 // Finished walking to a position; perform the action that was input back when
555 // they started walking over there.
556 int index = action.b1 - 0xe0;
557 addAction(_actionOnWalkCompletion[index]);
558 _actionOnWalkCompletionInUse[index] = false;
559 }
560
561 if (_room->handleAction(action))
562 return;
563
564 // Action not defined for the room, check for default behaviour
565
566 switch (action.type) {
567
568 case ACTION_WALK:
569 if (!_room->handleActionWithBitmask(action)) {
570 Common::String animFilename = getCrewmanAnimFilename(OBJECT_KIRK, "walk");
571 Common::Point mousePos = _gfx->getMousePos();
572 actorWalkToPosition(OBJECT_KIRK, animFilename, _kirkActor->pos.x, _kirkActor->pos.y, mousePos.x, mousePos.y);
573 }
574 break;
575
576 case ACTION_USE:
577 if (action.activeObject() != action.passiveObject()) {
578 switch (action.activeObject()) {
579 case OBJECT_KIRK:
580 // BUGFIX: Don't allow the "use" action to bypass the "disableWalking" variable
581 if (!(!_awayMission.disableWalking && _room->handleAction(ACTION_WALK, action.passiveObject(), 0, 0))
582 && !_room->handleAction(ACTION_GET, action.passiveObject(), 0, 0)) {
583 showTextbox("Capt. Kirk", _resource->getLoadedText(GROUNDTX_KIRK_USE), 20, 20, TEXTCOLOR_YELLOW, 0);
584 }
585 break;
586
587 case OBJECT_SPOCK:
588 if (!_room->handleAction(ACTION_USE, OBJECT_ISTRICOR, action.passiveObject(), 0)) {
589 // BUGFIX: Original game has just "Spock" instead of "Mr. Spock" as the
590 // speaker. That's inconsistent.
591 // Same applies to other parts of this function.
592 showTextbox("Mr. Spock", _resource->getLoadedText(GROUNDTX_SPOCK_USE), 20, 20, TEXTCOLOR_BLUE, 0);
593 }
594 break;
595
596 case OBJECT_MCCOY:
597 if (!_room->handleAction(ACTION_USE, OBJECT_IMEDKIT, action.passiveObject(), 0)
598 && !_room->handleAction(ACTION_USE, OBJECT_IMTRICOR, action.passiveObject(), 0)) {
599 // BUGFIX: Original game has just "McCoy" instead of "Dr. McCoy".
600 showTextbox("Dr. McCoy", _resource->getLoadedText(GROUNDTX_MCCOY_USE), 20, 20, TEXTCOLOR_BLUE, 0);
601 }
602 break;
603
604 case OBJECT_REDSHIRT:
605 showTextbox(NULL, _resource->getLoadedText(GROUNDTX_REDSHIRT_USE), 20, 20, TEXTCOLOR_YELLOW, 0);
606 break;
607
608 case OBJECT_IPHASERS:
609 case OBJECT_IPHASERK:
610 if (action.passiveObject() == OBJECT_SPOCK) {
611 int text = GROUNDTX_PHASER_ON_SPOCK + getRandomWord() % 8;
612 showTextbox("Mr. Spock", _resource->getLoadedText(text), 20, 20, TEXTCOLOR_BLUE, 0);
613 } else if (action.passiveObject() == OBJECT_MCCOY) {
614 int text = GROUNDTX_PHASER_ON_MCCOY + getRandomWord() % 8;
615 showTextbox("Dr. McCoy", _resource->getLoadedText(text), 20, 20, TEXTCOLOR_BLUE, 0);
616 } else if (action.passiveObject() == OBJECT_REDSHIRT) {
617 Common::String text = _resource->getLoadedText(GROUNDTX_PHASER_ON_REDSHIRT + getRandomWord() % 8);
618 // Replace audio filename with start of mission name (to load the
619 // audio for the crewman specific to the mission))
620 text.setChar(_missionName[0], 6);
621 text.setChar(_missionName[1], 7);
622 text.setChar(_missionName[2], 8);
623 showTextbox("Security Officer", text, 20, 20, TEXTCOLOR_RED, 0);
624 // TODO: replace "Security Officer" string with their actual name as
625 // an enhancement?
626 } else if (!_room->handleActionWithBitmask(action)) {
627 int index = getRandomWord() % 7;
628 if (index & 1)
629 showTextbox("Dr. McCoy", _resource->getLoadedText(GROUNDTX_PHASER_ANYWHERE + index), 20, 20, TEXTCOLOR_BLUE, 0);
630 else
631 showTextbox("Mr. Spock", _resource->getLoadedText(GROUNDTX_PHASER_ANYWHERE + index), 20, 20, TEXTCOLOR_BLUE, 0);
632 }
633 break;
634
635 case OBJECT_ISTRICOR:
636 showTextbox("Mr. Spock", _resource->getLoadedText(GROUNDTX_SPOCK_SCAN), 20, 20, TEXTCOLOR_BLUE, 0);
637 break;
638
639 case OBJECT_IMTRICOR:
640 showTextbox("Dr. McCoy", _resource->getLoadedText(GROUNDTX_MCCOY_SCAN), 20, 20, TEXTCOLOR_BLUE, 0);
641 break;
642
643 case OBJECT_ICOMM:
644 if (!_room->handleAction(ACTION_USE, OBJECT_ICOMM, 0xff, 0))
645 showTextbox("Lt. Uhura", _resource->getLoadedText(GROUNDTX_USE_COMMUNICATOR), 20, 20, TEXTCOLOR_RED, 0);
646 break;
647
648 case OBJECT_IMEDKIT:
649 showTextbox("Dr. McCoy", _resource->getLoadedText(GROUNDTX_USE_MEDKIT), 20, 20, TEXTCOLOR_BLUE, 0);
650 break;
651
652 default:
653 if (!_room->handleActionWithBitmask(action.type, action.b1, action.b2, action.b3))
654 showTextbox("", _resource->getLoadedText(GROUNDTX_NOTHING_HAPPENS), 20, 20, TEXTCOLOR_YELLOW, 0);
655 }
656 }
657 break;
658
659 case ACTION_GET:
660 if (!_room->handleActionWithBitmask(action.type, action.b1, action.b2, action.b3))
661 showTextbox("", _resource->getLoadedText(GROUNDTX_FAIL_TO_OBTAIN_ANYTHING), 20, 20, TEXTCOLOR_YELLOW, 0);
662 break;
663
664 case ACTION_LOOK:
665 if (action.activeObject() >= ITEMS_START && action.activeObject() < ITEMS_END) {
666 int i = action.activeObject() - ITEMS_START;
667 Common::String text = _resource->getLoadedText(_itemList[i].textIndex);
668 showTextbox("", text, 20, 20, TEXTCOLOR_YELLOW, 0);
669 } else if (action.activeObject() == OBJECT_KIRK)
670 showTextbox("", _resource->getLoadedText(GROUNDTX_LOOK_KIRK), 20, 20, TEXTCOLOR_YELLOW, 0);
671 else if (action.activeObject() == OBJECT_SPOCK)
672 showTextbox("", _resource->getLoadedText(GROUNDTX_LOOK_SPOCK), 20, 20, TEXTCOLOR_YELLOW, 0);
673 else if (action.activeObject() == OBJECT_MCCOY)
674 showTextbox("", _resource->getLoadedText(GROUNDTX_LOOK_MCCOY), 20, 20, TEXTCOLOR_YELLOW, 0);
675 else if (action.activeObject() == OBJECT_REDSHIRT)
676 showTextbox("", _resource->getLoadedText(GROUNDTX_LOOK_REDSHIRT), 20, 20, TEXTCOLOR_YELLOW, 0);
677 else
678 // Show generic "nothing of note" text.
679 // BUGFIX: originally this was shown after the redshirt's text as well.
680 // Though, the original game may not have used this default implementation
681 // anywhere...
682 showTextbox("", _resource->getLoadedText(GROUNDTX_LOOK_ANYWHERE), 20, 20, TEXTCOLOR_YELLOW, 0);
683 break;
684
685 case ACTION_TALK:
686 switch (action.activeObject()) {
687 case OBJECT_KIRK:
688 case OBJECT_SPOCK:
689 case OBJECT_MCCOY:
690 case OBJECT_REDSHIRT:
691 showTextbox("", _resource->getLoadedText(GROUNDTX_TALK_TO_CREWMAN), 20, 20, TEXTCOLOR_YELLOW, 0);
692 break;
693
694 default:
695 showTextbox("", _resource->getLoadedText(GROUNDTX_NO_RESPONSE), 20, 20, TEXTCOLOR_YELLOW, 0);
696 break;
697 }
698 break;
699
700 case ACTION_TOUCHED_WARP:
701 if (!_room->handleActionWithBitmask(action)) {
702 byte warpIndex = action.b1;
703 int16 roomIndex = _room->readRdfWord(RDF_WARP_ROOM_INDICES + warpIndex * 2);
704 unloadRoom();
705 _sound->loadMusicFile("ground");
706 loadRoom(_missionName, roomIndex);
707 initAwayCrewPositions(warpIndex ^ 1);
708 }
709 break;
710
711 default:
712 _room->handleActionWithBitmask(action);
713 break;
714 }
715 }
716
checkTouchedLoadingZone(int16 x,int16 y)717 void StarTrekEngine::checkTouchedLoadingZone(int16 x, int16 y) {
718 int16 offset = _room->getFirstDoorPolygonOffset();
719
720 while (offset != _room->getDoorPolygonEndOffset()) {
721 if (_room->isPointInPolygon(offset, x, y)) {
722 uint16 var = _room->readRdfWord(offset);
723 if (_activeDoorWarpHotspot != var) {
724 _activeDoorWarpHotspot = var;
725 addAction(ACTION_TOUCHED_HOTSPOT, var & 0xff, 0, 0);
726 }
727 return;
728 }
729
730 int16 numVertices = _room->readRdfWord(offset + 2);
731 offset += numVertices * 4 + 4;
732 }
733 _activeDoorWarpHotspot = -1;
734
735 if (_awayMission.crewDownBitset == 0 && _warpHotspotsActive) {
736 offset = _room->getFirstWarpPolygonOffset();
737
738 while (offset != _room->getWarpPolygonEndOffset()) {
739 if (_room->isPointInPolygon(offset, x, y)) {
740 uint16 var = _room->readRdfWord(offset);
741 if (_activeWarpHotspot != var) {
742 _activeWarpHotspot = var;
743 addAction(ACTION_TOUCHED_WARP, var & 0xff, 0, 0);
744 }
745 return;
746 }
747
748 int16 numVertices = _room->readRdfWord(offset + 2);
749 offset += numVertices * 4 + 4;
750 }
751 }
752 _activeWarpHotspot = -1;
753 }
754
updateAwayMissionTimers()755 void StarTrekEngine::updateAwayMissionTimers() {
756 for (int i = 0; i < 8; i++) {
757 if (_awayMission.timers[i] == 0)
758 continue;
759 _awayMission.timers[i]--;
760 if (_awayMission.timers[i] == 0)
761 addAction(ACTION_TIMER_EXPIRED, i, 0, 0);
762 }
763 }
764
isPositionSolid(int16 x,int16 y)765 bool StarTrekEngine::isPositionSolid(int16 x, int16 y) {
766 assert(x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT);
767
768 _mapFile->seek((y * SCREEN_WIDTH + x) / 8, SEEK_SET);
769 return _mapFile->readByte() & (0x80 >> (x % 8));
770 }
771
loadRoomIndex(int roomIndex,int spawnIndex)772 void StarTrekEngine::loadRoomIndex(int roomIndex, int spawnIndex) {
773 unloadRoom();
774 _sound->loadMusicFile("ground");
775
776 loadRoom(_missionName, roomIndex);
777 initAwayCrewPositions(spawnIndex % 6);
778
779 // WORKAROUND: original game calls "retrieveStackVars" to return execution directly to
780 // the top of "runAwayMission". That can't really be done here. But does it matter?
781 }
782
783 } // End of namespace StarTrek
784