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/room.h"
25 #include "startrek/startrek.h"
26 
27 #include "rooms/function_map.h"
28 
29 // TODO: Delete this macro, replacing it with the next one.
30 // New "[roomName]NumActions" variables need to be made before that.
31 #define ADD_ROOM_OLD(ROOM) {\
32 		if (name.equalsIgnoreCase(#ROOM)) {\
33 			_roomActionList = ROOM##ActionList;\
34 			_numRoomActions = ARRAYSIZE(ROOM##ActionList);\
35 		}\
36 	}
37 
38 #define ADD_ROOM(ROOM) {\
39 		if (name.equalsIgnoreCase(#ROOM)) {\
40 			_roomActionList = ROOM##ActionList;\
41 			_numRoomActions = ROOM##NumActions;\
42 		}\
43 	}
44 
45 namespace StarTrek {
46 
Room(StarTrekEngine * vm,const Common::String & name)47 Room::Room(StarTrekEngine *vm, const Common::String &name) : _vm(vm), _awayMission(&vm->_awayMission) {
48 	Common::MemoryReadStreamEndian *rdfFile = _vm->loadFile(name + ".RDF");
49 
50 	int size = rdfFile->size();
51 	_rdfData = new byte[size];
52 	rdfFile->read(_rdfData, size);
53 	delete rdfFile;
54 
55 	_roomActionList = nullptr;
56 
57 	ADD_ROOM_OLD(demon0);
58 	ADD_ROOM_OLD(demon1);
59 	ADD_ROOM_OLD(demon2);
60 	ADD_ROOM_OLD(demon3);
61 	ADD_ROOM_OLD(demon4);
62 	ADD_ROOM_OLD(demon5);
63 	ADD_ROOM_OLD(demon6);
64 	ADD_ROOM_OLD(tug0);
65 	ADD_ROOM_OLD(tug1);
66 	ADD_ROOM_OLD(tug2);
67 	ADD_ROOM_OLD(tug3);
68 	ADD_ROOM_OLD(love0);
69 	ADD_ROOM_OLD(love1);
70 	ADD_ROOM_OLD(love2);
71 	ADD_ROOM_OLD(love3);
72 	ADD_ROOM_OLD(love4);
73 	ADD_ROOM_OLD(love5);
74 	ADD_ROOM_OLD(mudd0);
75 	ADD_ROOM_OLD(mudd1);
76 	ADD_ROOM_OLD(mudd2);
77 	ADD_ROOM_OLD(mudd3);
78 	ADD_ROOM_OLD(mudd4);
79 	ADD_ROOM_OLD(mudd5);
80 	ADD_ROOM_OLD(feather0);
81 	ADD_ROOM(feather1);
82 	ADD_ROOM(feather2);
83 	ADD_ROOM(feather3);
84 	ADD_ROOM(feather4);
85 	ADD_ROOM(feather5);
86 	ADD_ROOM(feather6);
87 	ADD_ROOM(feather7);
88 	ADD_ROOM(trial0);
89 	ADD_ROOM(trial1);
90 	ADD_ROOM(trial2);
91 	ADD_ROOM(trial3);
92 	ADD_ROOM(trial4);
93 	ADD_ROOM(trial5);
94 	ADD_ROOM(sins0);
95 	ADD_ROOM(sins1);
96 	ADD_ROOM(sins2);
97 	ADD_ROOM(sins3);
98 	ADD_ROOM(sins4);
99 	ADD_ROOM(sins5);
100 	ADD_ROOM(veng0);
101 	ADD_ROOM(veng1);
102 	ADD_ROOM(veng2);
103 	ADD_ROOM(veng3);
104 	ADD_ROOM(veng4);
105 	ADD_ROOM(veng5);
106 	ADD_ROOM(veng6);
107 	ADD_ROOM(veng7);
108 	ADD_ROOM(veng8);
109 
110 	if (_roomActionList == nullptr) {
111 		warning("Room \"%s\" unimplemented", name.c_str());
112 		_numRoomActions = 0;
113 	}
114 
115 	loadRoomMessages();
116 	loadOtherRoomMessages();
117 	memset(&_roomVar, 0, sizeof(_roomVar));
118 }
119 
~Room()120 Room::~Room() {
121 	_lookMessages.clear();
122 	_lookWithTalkerMessages.clear();
123 	_talkMessages.clear();
124 	delete[] _rdfData;
125 }
126 
loadRoomMessages()127 void Room::loadRoomMessages() {
128 	// TODO: There are some more messages which are not stored in that offset
129 	uint16 messagesOffset = readRdfWord(32);
130 	const char *text = (const char *)_rdfData + messagesOffset;
131 	const char roomIndexChar = '0' + _vm->_roomIndex;
132 
133 	do {
134 		while (text[0] != '#' || (text[1] != _vm->_missionName[0] && text[4] != roomIndexChar))
135 			text++;
136 
137 		if (text[5] == '\\')
138 			loadRoomMessage(text);
139 
140 		while (*text != '\0')
141 			text++;
142 
143 		// Peek the next byte, in case there's a filler text
144 		if (Common::isAlpha(*(text + 1))) {
145 			while (*text != '\0')
146 				text++;
147 		}
148 	} while (*(text + 1) == '#');
149 }
150 
loadRoomMessage(const char * text)151 void Room::loadRoomMessage(const char *text) {
152 	int messageNum;
153 	bool isTalkMessage;
154 	bool isLookWithTalkerMessage;
155 	char textType = text[10];	// _ and U: talk message, N: look message, L: look with talker message
156 
157 	if (text[5] != '\\')
158 		error("loadRoomMessage: Invalid message");
159 
160 	isTalkMessage = (textType == '_' || textType == 'U');	// U = Uhura
161 	isLookWithTalkerMessage = (textType == 'L');
162 
163 	sscanf((const char *)(text + 11), "%3d", &messageNum);
164 	if (text[14] != '#')
165 		error("loadRoomMessage: Invalid message");
166 
167 	if (isTalkMessage)
168 		_talkMessages[messageNum] = Common::String((const char *)text);
169 	else if (isLookWithTalkerMessage)
170 		_lookWithTalkerMessages[messageNum] = Common::String((const char *)text);
171 	else
172 		_lookMessages[messageNum] = Common::String((const char *)text);
173 
174 }
175 
loadOtherRoomMessages()176 void Room::loadOtherRoomMessages() {
177 	uint16 startOffset = readRdfWord(14);
178 	uint16 endOffset = readRdfWord(16);
179 	uint16 offset = startOffset;
180 
181 	while (offset < endOffset) {
182 		uint16 nextOffset = readRdfWord(offset + 4);
183 		if (nextOffset >= endOffset)
184 			break;
185 
186 		while (offset < nextOffset) {
187 			const char *text = (const char *)_rdfData + offset;
188 
189 			if (text[0] == '#' && text[1] == _vm->_missionName[0] && text[5] == '\\')
190 				loadRoomMessage(text);
191 
192 			offset++;
193 		}
194 	}
195 }
196 
readRdfWord(int offset)197 uint16 Room::readRdfWord(int offset) {
198 	return READ_LE_UINT16((_rdfData + offset));
199 }
200 
actionHasCode(const Action & action)201 bool Room::actionHasCode(const Action &action) {
202 	const RoomAction *roomActionPtr = _roomActionList;
203 	int n = _numRoomActions;
204 
205 	while (n-- > 0) {
206 		if (action == roomActionPtr->action)
207 			return true;
208 		roomActionPtr++;
209 	}
210 	return false;
211 }
212 
actionHasCode(byte type,byte b1,byte b2,byte b3)213 bool Room::actionHasCode(byte type, byte b1, byte b2, byte b3) {
214 	const Action a = {type, b1, b2, b3};
215 	return actionHasCode(a);
216 }
217 
handleAction(const Action & action)218 bool Room::handleAction(const Action &action) {
219 	const RoomAction *roomActionPtr = _roomActionList;
220 	int n = _numRoomActions;
221 
222 	while (n-- > 0) {
223 		if (action == roomActionPtr->action) {
224 			_vm->_awayMission.rdfStillDoDefaultAction = false;
225 			(this->*(roomActionPtr->funcPtr))();
226 			if (!_vm->_awayMission.rdfStillDoDefaultAction)
227 				return true;
228 		}
229 		roomActionPtr++;
230 	}
231 	return false;
232 }
233 
handleAction(byte type,byte b1,byte b2,byte b3)234 bool Room::handleAction(byte type, byte b1, byte b2, byte b3) {
235 	const Action a = {type, b1, b2, b3};
236 	return handleAction(a);
237 }
238 
handleActionWithBitmask(const Action & action)239 bool Room::handleActionWithBitmask(const Action &action) {
240 	const RoomAction *roomActionPtr = _roomActionList;
241 	int n = _numRoomActions;
242 
243 	while (n-- > 0) {
244 		uint32 bitmask = roomActionPtr->action.getBitmask();
245 		if ((action.toUint32() & bitmask) == (roomActionPtr->action.toUint32() & bitmask)) {
246 			_vm->_awayMission.rdfStillDoDefaultAction = false;
247 			(this->*(roomActionPtr->funcPtr))();
248 			if (!_vm->_awayMission.rdfStillDoDefaultAction)
249 				return true;
250 		}
251 		roomActionPtr++;
252 	}
253 	return false;
254 }
255 
handleActionWithBitmask(byte type,byte b1,byte b2,byte b3)256 bool Room::handleActionWithBitmask(byte type, byte b1, byte b2, byte b3) {
257 	Action a = {type, b1, b2, b3};
258 	return handleActionWithBitmask(a);
259 }
260 
getBeamInPosition(int crewmanIndex)261 Common::Point Room::getBeamInPosition(int crewmanIndex) {
262 	int base = RDF_BEAM_IN_POSITIONS + crewmanIndex * 4;
263 	return Common::Point(readRdfWord(base), readRdfWord(base + 2));
264 }
265 
getSpawnPosition(int crewmanIndex)266 Common::Point Room::getSpawnPosition(int crewmanIndex) {
267 	int base = RDF_SPAWN_POSITIONS + crewmanIndex * 4;
268 	return Common::Point(readRdfWord(base), readRdfWord(base + 2));
269 }
270 
271 // For actions of type ACTION_FINISHED_ANIMATION or ACTION_FINISHED_WALKING, this takes
272 // a function pointer and returns the index corresponding to that callback.
273 // Creates a fatal error on failure.
findFunctionPointer(int action,void (Room::* funcPtr)())274 int Room::findFunctionPointer(int action, void (Room::*funcPtr)()) {
275 	assert(action == ACTION_FINISHED_ANIMATION || action == ACTION_FINISHED_WALKING);
276 
277 	for (int i = 0; i < _numRoomActions; i++) {
278 		if (_roomActionList[i].action.type == action && _roomActionList[i].funcPtr == funcPtr)
279 			return _roomActionList[i].action.b1;
280 	}
281 
282 	if (action == ACTION_FINISHED_ANIMATION)
283 		error("Couldn't find FINISHED_ANIMATION function pointer");
284 	else
285 		error("Couldn't find FINISHED_WALKING function pointer");
286 }
287 
288 // Interface for room-specific code
289 
loadActorAnim(int actorIndex,Common::String anim,int16 x,int16 y,uint16 finishedAnimActionParam)290 void Room::loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
291 	Actor *actor = &_vm->_actorList[actorIndex];
292 
293 	if (x == -1 || y == -1) {
294 		x = actor->sprite.pos.x;
295 		y = actor->sprite.pos.y;
296 	}
297 
298 	if (actorIndex >= 0 && actorIndex < SCALED_ACTORS_END)
299 		_vm->loadActorAnimWithRoomScaling(actorIndex, anim, x, y);
300 	else
301 		_vm->loadActorAnim(actorIndex, anim, x, y, 1.0);
302 
303 	if (finishedAnimActionParam != 0) {
304 		actor->triggerActionWhenAnimFinished = true;
305 		actor->finishedAnimActionParam = finishedAnimActionParam;
306 	}
307 }
308 
309 // Same as above, but accepts a callback for when the animation finished (instead of an
310 // integer for an action)
loadActorAnimC(int actorIndex,Common::String anim,int16 x,int16 y,void (Room::* funcPtr)())311 void Room::loadActorAnimC(int actorIndex, Common::String anim, int16 x, int16 y, void (Room::*funcPtr)()) {
312 	Actor *actor = &_vm->_actorList[actorIndex];
313 
314 	if (x == -1 || y == -1) {
315 		x = actor->sprite.pos.x;
316 		y = actor->sprite.pos.y;
317 	}
318 
319 	if (actorIndex >= 0 && actorIndex < SCALED_ACTORS_END)
320 		_vm->loadActorAnimWithRoomScaling(actorIndex, anim, x, y);
321 	else
322 		_vm->loadActorAnim(actorIndex, anim, x, y, 1.0);
323 
324 	if (funcPtr != nullptr) {
325 		actor->triggerActionWhenAnimFinished = true;
326 		actor->finishedAnimActionParam = findFunctionPointer(ACTION_FINISHED_ANIMATION, funcPtr);
327 	}
328 }
329 
loadActorStandAnim(int actorIndex)330 void Room::loadActorStandAnim(int actorIndex) {
331 	if (_vm->_awayMission.redshirtDead && actorIndex == OBJECT_REDSHIRT)
332 		_vm->removeActorFromScreen(actorIndex);
333 	else {
334 		Actor *actor = &_vm->_actorList[actorIndex];
335 		if (actor->animationString.empty())
336 			_vm->removeActorFromScreen(actorIndex);
337 		else
338 			_vm->initStandAnim(actorIndex);
339 	}
340 }
341 
loadActorAnim2(int actorIndex,Common::String anim,int16 x,int16 y,uint16 finishedAnimActionParam)342 void Room::loadActorAnim2(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
343 	loadActorAnim(actorIndex, anim, x, y, finishedAnimActionParam);
344 }
345 
showRoomSpecificText(const char ** array)346 int Room::showRoomSpecificText(const char **array) {
347 	Common::String speaker;
348 	byte textColor;
349 
350 	if (array[0] != nullptr && array[0][0] != '\0') {
351 		speaker = Common::String(array[0]);
352 		if (speaker.equalsIgnoreCase("Capt. Kirk"))
353 			textColor = TEXTCOLOR_YELLOW;
354 		else if (speaker.equalsIgnoreCase("Mr. Spock"))
355 			textColor = TEXTCOLOR_BLUE;
356 		else if (speaker.equalsIgnoreCase("Dr. McCoy"))
357 			textColor = TEXTCOLOR_BLUE;
358 		else if (speaker.equalsIgnoreCase("Mr. Chekov"))
359 			textColor = TEXTCOLOR_YELLOW;
360 		else if (speaker.equalsIgnoreCase("Mr. Scott"))
361 			textColor = TEXTCOLOR_RED;
362 		else if (speaker.hasPrefixIgnoreCase("Lt"))
363 			textColor = TEXTCOLOR_RED;
364 		else if (speaker.hasPrefixIgnoreCase("Ensign"))
365 			textColor = TEXTCOLOR_RED;
366 		else
367 			textColor = TEXTCOLOR_GREY;
368 	} else
369 		textColor = TEXTCOLOR_YELLOW;
370 
371 	return _vm->showText(&StarTrekEngine::readTextFromArrayWithChoices, (uintptr)array, 20, 20, textColor, true, false, false);
372 }
373 
showMultipleTexts(const TextRef * textIDs,bool fromRDF,bool lookWithTalker)374 int Room::showMultipleTexts(const TextRef *textIDs, bool fromRDF, bool lookWithTalker) {
375 	int numIDs = 0;
376 	int retval;
377 	while (textIDs[numIDs] != TX_BLANK)
378 		numIDs++;
379 
380 	const char **text = (const char **)malloc(sizeof(const char *) * (numIDs + 1));
381 
382 	for (int i = 0; i <= numIDs; i++) {
383 		// TODO: This isn't nice, but it's temporary till we migrate to reading text from RDF files
384 		if (i > 0 && fromRDF) {
385 			if (textIDs[0] == TX_NULL)
386 				text[i] = _lookMessages[textIDs[i]].c_str();
387 			else if (lookWithTalker)
388 				text[i] = _lookWithTalkerMessages[textIDs[i]].c_str();
389 			else
390 				text[i] = _talkMessages[textIDs[i]].c_str();
391 		} else
392 			text[i] = g_gameStrings[textIDs[i]];
393 	}
394 
395 	retval = showRoomSpecificText(text);
396 	free(text);
397 
398 	return retval;
399 }
400 
showText(TextRef speaker,TextRef text,bool fromRDF,bool lookWithTalker)401 int Room::showText(TextRef speaker, TextRef text, bool fromRDF, bool lookWithTalker) {
402 	TextRef textIDs[3];
403 	textIDs[0] = speaker;
404 	textIDs[1] = text;
405 	textIDs[2] = TX_BLANK;
406 	return showMultipleTexts(textIDs, fromRDF, lookWithTalker);
407 }
408 
showDescription(TextRef text,bool fromRDF,bool lookWithTalker)409 int Room::showDescription(TextRef text, bool fromRDF, bool lookWithTalker) {
410 	return showText(TX_NULL, text, fromRDF, lookWithTalker);
411 }
412 
giveItem(int item)413 void Room::giveItem(int item) {
414 	assert(item >= ITEMS_START && item < ITEMS_END);
415 	_vm->_itemList[item - ITEMS_START].have = true;
416 }
417 
loadRoomIndex(int roomIndex,int spawnIndex)418 void Room::loadRoomIndex(int roomIndex, int spawnIndex) {
419 	if (_vm->_awayMission.crewDownBitset != 0)
420 		return;
421 
422 	_vm->_missionToLoad = _vm->_missionName;
423 	_vm->_roomIndexToLoad = roomIndex;
424 	_vm->_spawnIndexToLoad = spawnIndex;
425 
426 	// WORKAROUND: original game manipulates the stack to return directly to the start of
427 	// "runAwayMission". Instead, we set some variables and the room will be changed
428 	// later. (We wouldn't want to delete the room we're currently in...)
429 }
430 
loseItem(int item)431 void Room::loseItem(int item) {
432 	assert(item >= ITEMS_START && item < ITEMS_END);
433 	_vm->_itemList[item - ITEMS_START].have = false;
434 
435 	if (_vm->_awayMission.activeAction == ACTION_USE && _vm->_awayMission.activeObject == item) {
436 		_vm->_awayMission.activeAction = ACTION_WALK;
437 		_vm->chooseMouseBitmapForAction(ACTION_WALK, false);
438 		_vm->hideInventoryIcons();
439 	}
440 }
441 
walkCrewman(int actorIndex,int16 destX,int16 destY,uint16 finishedAnimActionParam)442 void Room::walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 finishedAnimActionParam) {
443 	if (!(actorIndex >= OBJECT_KIRK && actorIndex <= OBJECT_REDSHIRT))
444 		error("Tried to walk a non PC");
445 
446 	Actor *actor = &_vm->_actorList[actorIndex];
447 	Common::String anim = _vm->getCrewmanAnimFilename(actorIndex, "walk");
448 	bool success = _vm->actorWalkToPosition(actorIndex, anim, actor->pos.x, actor->pos.y, destX, destY);
449 
450 	if (success && finishedAnimActionParam != 0) {
451 		actor->triggerActionWhenAnimFinished = true;
452 		actor->finishedAnimActionParam = finishedAnimActionParam;
453 	}
454 }
455 
456 // Same as above, but with a function callback instead of an integer value to generate an
457 // action
walkCrewmanC(int actorIndex,int16 destX,int16 destY,void (Room::* funcPtr)())458 void Room::walkCrewmanC(int actorIndex, int16 destX, int16 destY, void (Room::*funcPtr)()) {
459 	if (!(actorIndex >= OBJECT_KIRK && actorIndex <= OBJECT_REDSHIRT))
460 		error("Tried to walk a non PC");
461 
462 	Actor *actor = &_vm->_actorList[actorIndex];
463 	Common::String anim = _vm->getCrewmanAnimFilename(actorIndex, "walk");
464 	bool success = _vm->actorWalkToPosition(actorIndex, anim, actor->pos.x, actor->pos.y, destX, destY);
465 
466 	if (success && funcPtr != nullptr) {
467 		actor->triggerActionWhenAnimFinished = true;
468 		actor->finishedAnimActionParam = 0;
469 		actor->finishedAnimActionParam = findFunctionPointer(ACTION_FINISHED_WALKING, funcPtr);
470 	}
471 }
472 
loadMapFile(const Common::String & name)473 void Room::loadMapFile(const Common::String &name) {
474 	delete _vm->_mapFile;
475 	_vm->_mapFile = _vm->loadFile(name + ".map");
476 
477 	_vm->_iwFile.reset();
478 	_vm->_iwFile = SharedPtr<IWFile>(new IWFile(_vm, name + ".iw"));
479 }
480 
showBitmapFor5Ticks(const Common::String & bmpName,int priority)481 void Room::showBitmapFor5Ticks(const Common::String &bmpName, int priority) {
482 	if (priority < 0 || priority > 15)
483 		priority = 5;
484 
485 	Sprite sprite;
486 	_vm->_gfx->addSprite(&sprite);
487 	sprite.setXYAndPriority(0, 0, priority);
488 	sprite.setBitmap(_vm->_gfx->loadBitmap(bmpName));
489 
490 	_vm->_gfx->drawAllSprites();
491 
492 	TrekEvent event;
493 	int ticks = 0;
494 
495 	while (ticks < 5) {
496 		while (!_vm->popNextEvent(&event));
497 
498 		if (event.type == TREKEVENT_TICK)
499 			ticks++;
500 	}
501 
502 	sprite.dontDrawNextFrame();
503 	_vm->_gfx->drawAllSprites();
504 	_vm->_gfx->delSprite(&sprite);
505 }
506 
haveItem(int item)507 bool Room::haveItem(int item) {
508 	return _vm->_itemList[item - 0x40].have;
509 }
510 
getActorPos(int actorIndex)511 Common::Point Room::getActorPos(int actorIndex) {
512 	return _vm->_actorList[actorIndex].pos;
513 }
514 
getRandomWordInRange(int start,int end)515 int16 Room::getRandomWordInRange(int start, int end) {
516 	return _vm->getRandomWord() % (end - start + 1) + start;
517 }
518 
playSoundEffectIndex(int soundEffect)519 void Room::playSoundEffectIndex(int soundEffect) {
520 	_vm->playSoundEffectIndex(soundEffect);
521 }
522 
playMidiMusicTracks(int startTrack,int loopTrack)523 void Room::playMidiMusicTracks(int startTrack, int loopTrack) {
524 	_vm->playMidiMusicTracks(startTrack, loopTrack);
525 }
526 
endMission(int16 score,int16 arg1,int16 arg2)527 void Room::endMission(int16 score, int16 arg1, int16 arg2) {
528 	_vm->_awayMission.disableInput = true;
529 
530 	for (int i = 0; i < (_vm->_awayMission.redshirtDead ? 3 : 4); i++) {
531 		Actor *actor = &_vm->_actorList[i];
532 		Common::String anim = _vm->getCrewmanAnimFilename(i, "teled");
533 		_vm->loadActorAnimWithRoomScaling(i, anim, actor->sprite.pos.x, actor->sprite.pos.y);
534 	}
535 
536 	_vm->_kirkActor->animationString.clear();
537 	_vm->_spockActor->animationString.clear();
538 	_vm->_mccoyActor->animationString.clear();
539 	_vm->_redshirtActor->animationString.clear();
540 
541 	playSoundEffectIndex(8);
542 
543 	while (_vm->_kirkActor->spriteDrawn)
544 		_vm->handleAwayMissionEvents();
545 
546 	_vm->_awayMission.disableInput = false;
547 
548 	// TODO: This is a stopgap measure (loading the next away mission immediately).
549 	// Replace this with the proper code later.
550 	_vm->_gameMode = GAMEMODE_BEAMDOWN;
551 
552 	const char *missionNames[] = {
553 		"DEMON",
554 		"TUG",
555 		"LOVE",
556 		"MUDD",
557 		"FEATHER",
558 		"TRIAL",
559 		"SINS",
560 		"VENG"
561 	};
562 
563 	for (int i = 0; i < ARRAYSIZE(missionNames)-1; i++) {
564 		if (_vm->_missionName == missionNames[i]) {
565 			_vm->_missionToLoad = missionNames[i + 1];
566 			break;
567 		}
568 	}
569 
570 	_vm->_roomIndexToLoad = 0;
571 }
572 
showGameOverMenu()573 void Room::showGameOverMenu() { // TODO: takes an optional parameter?
574 	_vm->showGameOverMenu();
575 	// TODO: finish. Shouldn't do this within a room due to deletion of current room?
576 }
577 
showCodeInputBox(const char * const * codes)578 int Room::showCodeInputBox(const char * const *codes) {
579 	Common::String inputString = _vm->showCodeInputBox();
580 
581 	// ENHANCEMENT: Extra condition for "nothing entered"
582 	if (inputString.empty())
583 		return -1;
584 
585 	int retval = 0;
586 	int code = 0;
587 
588 	while (codes[code] != nullptr) {
589 		if (strcmp(codes[code], inputString.c_str()) == 0)
590 			retval = code + 1;
591 		code++;
592 	}
593 
594 	return retval;
595 }
596 
showRepublicMap(int16 arg0,int16 arg2)597 void Room::showRepublicMap(int16 arg0, int16 arg2) {
598 	_vm->showRepublicMap(arg0, arg2);
599 }
600 
playVoc(Common::String filename)601 void Room::playVoc(Common::String filename) {
602 	_vm->_sound->playVoc(filename);
603 }
604 
stopAllVocSounds()605 void Room::stopAllVocSounds() {
606 	_vm->_sound->stopAllVocSounds();
607 }
608 
getCrewmanAnimFilename(int object,const Common::String & str)609 Common::String Room::getCrewmanAnimFilename(int object, const Common::String &str) {
610 	return _vm->getCrewmanAnimFilename(object, str);
611 }
612 
spockScan(int direction,TextRef text,bool changeDirection,bool fromRDF)613 void Room::spockScan(int direction, TextRef text, bool changeDirection, bool fromRDF) {
614 	const char *dirs = "nsew";
615 	Common::String anim = "sscan_";
616 	anim.setChar(dirs[direction], 5);
617 
618 	if (changeDirection) // Check whether he should turn back to original direction after scanning
619 		_vm->_awayMission.crewDirectionsAfterWalk[OBJECT_SPOCK] = direction;
620 
621 	loadActorAnim2(OBJECT_SPOCK, anim, -1, -1, 0);
622 	playSoundEffectIndex(SND_TRICORDER);
623 
624 	if (text != -1)
625 		showText(TX_SPEAKER_SPOCK, text, fromRDF);
626 }
627 
mccoyScan(int direction,TextRef text,bool changeDirection,bool fromRDF)628 void Room::mccoyScan(int direction, TextRef text, bool changeDirection, bool fromRDF) {
629 	const char *dirs = "nsew";
630 	Common::String anim = "mscan_";
631 	anim.setChar(dirs[direction], 5);
632 
633 	if (changeDirection)
634 		_vm->_awayMission.crewDirectionsAfterWalk[OBJECT_MCCOY] = direction;
635 
636 	loadActorAnim2(OBJECT_MCCOY, anim, -1, -1, 0);
637 	playSoundEffectIndex(SND_TRICORDER);
638 
639 	if (text != -1)
640 		showText(TX_SPEAKER_MCCOY, text, fromRDF);
641 }
642 
643 } // End of namespace StarTrek
644