2018-03-10 22:47:52 -05:00
|
|
|
/* ScummVM - Graphic Adventure Engine
|
|
|
|
*
|
|
|
|
* ScummVM is the legal property of its developers, whose names
|
|
|
|
* are too numerous to list here. Please refer to the COPYRIGHT
|
|
|
|
* file distributed with this source distribution.
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
|
|
|
* as published by the Free Software Foundation; either version 2
|
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "startrek/filestream.h"
|
2018-05-25 21:53:18 -04:00
|
|
|
#include "startrek/iwfile.h"
|
2018-03-10 22:47:52 -05:00
|
|
|
#include "startrek/room.h"
|
|
|
|
#include "startrek/startrek.h"
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
#include "rooms/function_map.h"
|
2018-03-10 22:47:52 -05:00
|
|
|
|
|
|
|
namespace StarTrek {
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
Room::Room(StarTrekEngine *vm, const Common::String &name) : _vm(vm) {
|
2018-05-11 02:17:57 -04:00
|
|
|
SharedPtr<FileStream> rdfFile = _vm->loadFile(name + ".RDF");
|
2018-03-10 22:47:52 -05:00
|
|
|
|
|
|
|
int size = rdfFile->size();
|
|
|
|
_rdfData = new byte[size];
|
|
|
|
rdfFile->read(_rdfData, size);
|
2018-05-23 01:01:36 -04:00
|
|
|
|
|
|
|
// Find room-specific code table
|
|
|
|
if (name == "DEMON0") {
|
|
|
|
_roomActionList = demon0ActionList;
|
|
|
|
_numRoomActions = sizeof(demon0ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-24 22:08:56 -04:00
|
|
|
else if (name == "DEMON1") {
|
|
|
|
_roomActionList = demon1ActionList;
|
|
|
|
_numRoomActions = sizeof(demon1ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-25 21:53:18 -04:00
|
|
|
else if (name == "DEMON2") {
|
|
|
|
_roomActionList = demon2ActionList;
|
|
|
|
_numRoomActions = sizeof(demon2ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
|
|
|
else if (name == "DEMON3") {
|
|
|
|
_roomActionList = demon3ActionList;
|
|
|
|
_numRoomActions = sizeof(demon3ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-28 19:42:04 -04:00
|
|
|
else if (name == "DEMON4") {
|
|
|
|
_roomActionList = demon4ActionList;
|
|
|
|
_numRoomActions = sizeof(demon4ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-29 16:51:05 -04:00
|
|
|
else if (name == "DEMON5") {
|
|
|
|
_roomActionList = demon5ActionList;
|
|
|
|
_numRoomActions = sizeof(demon5ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-29 20:35:36 -04:00
|
|
|
else if (name == "DEMON6") {
|
|
|
|
_roomActionList = demon6ActionList;
|
|
|
|
_numRoomActions = sizeof(demon6ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-06-06 23:43:29 -04:00
|
|
|
else if (name == "TUG0") {
|
|
|
|
_roomActionList = tug0ActionList;
|
|
|
|
_numRoomActions = sizeof(tug0ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-06-09 21:08:25 -04:00
|
|
|
else if (name == "TUG1") {
|
|
|
|
_roomActionList = tug1ActionList;
|
|
|
|
_numRoomActions = sizeof(tug1ActionList) / sizeof(RoomAction);
|
|
|
|
}
|
2018-05-23 01:01:36 -04:00
|
|
|
else {
|
|
|
|
warning("Room \"%s\" unimplemented", name.c_str());
|
|
|
|
_numRoomActions = 0;
|
|
|
|
}
|
2018-05-24 22:08:56 -04:00
|
|
|
|
|
|
|
memset(&_roomVar, 0, sizeof(_roomVar));
|
2018-03-10 22:47:52 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Room::~Room() {
|
|
|
|
delete[] _rdfData;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint16 Room::readRdfWord(int offset) {
|
|
|
|
return _rdfData[offset] | (_rdfData[offset+1]<<8);
|
|
|
|
}
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
bool Room::actionHasCode(const Action &action) {
|
|
|
|
RoomAction *roomActionPtr = _roomActionList;
|
|
|
|
int n = _numRoomActions;
|
|
|
|
|
|
|
|
while (n-- > 0) {
|
|
|
|
if (action == roomActionPtr->action)
|
|
|
|
return true;
|
|
|
|
roomActionPtr++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Room::handleAction(const Action &action) {
|
|
|
|
RoomAction *roomActionPtr = _roomActionList;
|
|
|
|
int n = _numRoomActions;
|
|
|
|
|
|
|
|
while (n-- > 0) {
|
|
|
|
if (action == roomActionPtr->action) {
|
|
|
|
_vm->_awayMission.rdfStillDoDefaultAction = false;
|
|
|
|
(this->*(roomActionPtr->funcPtr))();
|
|
|
|
if (!_vm->_awayMission.rdfStillDoDefaultAction)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
roomActionPtr++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-23 19:56:15 -04:00
|
|
|
bool Room::handleActionWithBitmask(const Action &action) {
|
|
|
|
RoomAction *roomActionPtr = _roomActionList;
|
|
|
|
int n = _numRoomActions;
|
|
|
|
|
|
|
|
while (n-- > 0) {
|
|
|
|
uint32 bitmask = action.getBitmask();
|
|
|
|
if ((action.toUint32() & bitmask) == (roomActionPtr->action.toUint32() & bitmask)) {
|
|
|
|
_vm->_awayMission.rdfStillDoDefaultAction = false;
|
|
|
|
(this->*(roomActionPtr->funcPtr))();
|
|
|
|
if (!_vm->_awayMission.rdfStillDoDefaultAction)
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
roomActionPtr++;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2018-05-13 23:58:58 -04:00
|
|
|
Common::Point Room::getBeamInPosition(int crewmanIndex) {
|
|
|
|
int base = 0xaa + crewmanIndex * 4;
|
|
|
|
return Common::Point(readRdfWord(base), readRdfWord(base + 2));
|
|
|
|
}
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
|
|
|
|
// Interface for room-specific code
|
|
|
|
|
2018-05-23 19:56:15 -04:00
|
|
|
void Room::loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
|
2018-05-23 01:01:36 -04:00
|
|
|
Actor *actor = &_vm->_actorList[actorIndex];
|
|
|
|
|
|
|
|
if (x == -1 || y == -1) {
|
|
|
|
x = actor->sprite.pos.x;
|
|
|
|
y = actor->sprite.pos.y;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (actorIndex >= 0 && actorIndex < SCALED_ACTORS_END)
|
|
|
|
_vm->loadActorAnimWithRoomScaling(actorIndex, anim, x, y);
|
|
|
|
else
|
|
|
|
_vm->loadActorAnim(actorIndex, anim, x, y, 256);
|
|
|
|
|
2018-05-23 19:56:15 -04:00
|
|
|
if (finishedAnimActionParam != 0) {
|
|
|
|
actor->triggerActionWhenAnimFinished = true;
|
|
|
|
actor->finishedAnimActionParam = finishedAnimActionParam;
|
2018-05-23 01:01:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void Room::loadActorStandAnim(int actorIndex) {
|
|
|
|
if (_vm->_awayMission.redshirtDead && actorIndex == OBJECT_REDSHIRT)
|
|
|
|
_vm->removeActorFromScreen(actorIndex);
|
|
|
|
else {
|
|
|
|
Actor *actor = &_vm->_actorList[actorIndex];
|
|
|
|
if (actor->animationString[0] == '\0')
|
|
|
|
_vm->removeActorFromScreen(actorIndex);
|
|
|
|
else
|
|
|
|
_vm->initStandAnim(actorIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This is exactly the same as "loadActorAnim", but the game calls it at different times?
|
|
|
|
*/
|
2018-05-23 19:56:15 -04:00
|
|
|
void Room::loadActorAnim2(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
|
|
|
|
loadActorAnim(actorIndex, anim, x, y, finishedAnimActionParam);
|
2018-05-23 01:01:36 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int Room::showRoomSpecificText(const char **array) {
|
|
|
|
Common::String speaker;
|
|
|
|
byte textColor;
|
|
|
|
|
|
|
|
if (array[0] != nullptr && array[0][0] != '\0') { // TODO
|
|
|
|
speaker = Common::String(array[0]);
|
|
|
|
if (speaker.equalsIgnoreCase("Capt. Kirk"))
|
|
|
|
textColor = TEXTCOLOR_YELLOW;
|
|
|
|
else if (speaker.equalsIgnoreCase("Mr. Spock"))
|
|
|
|
textColor = TEXTCOLOR_BLUE;
|
|
|
|
else if (speaker.equalsIgnoreCase("Dr. McCoy"))
|
|
|
|
textColor = TEXTCOLOR_BLUE;
|
|
|
|
else if (speaker.equalsIgnoreCase("Mr. Chekov"))
|
|
|
|
textColor = TEXTCOLOR_YELLOW;
|
|
|
|
else if (speaker.equalsIgnoreCase("Mr. Scott"))
|
|
|
|
textColor = TEXTCOLOR_RED;
|
|
|
|
else if (speaker.hasPrefixIgnoreCase("Lt"))
|
|
|
|
textColor = TEXTCOLOR_RED;
|
|
|
|
else if (speaker.hasPrefixIgnoreCase("Ensign"))
|
|
|
|
textColor = TEXTCOLOR_RED;
|
|
|
|
else
|
|
|
|
textColor = TEXTCOLOR_GREY;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
textColor = TEXTCOLOR_YELLOW;
|
|
|
|
|
|
|
|
return _vm->showText(&StarTrekEngine::readTextFromArray, (uintptr)array, 20, 20, textColor, true, false, false);
|
|
|
|
}
|
|
|
|
|
2018-05-28 18:45:44 -04:00
|
|
|
int Room::showText(const int *textIDs) {
|
|
|
|
int numIDs = 0;
|
|
|
|
while (textIDs[numIDs] != TX_BLANK)
|
|
|
|
numIDs++;
|
|
|
|
|
|
|
|
const char **text = (const char **)malloc(sizeof(const char *) * (numIDs + 1));
|
|
|
|
for (int i = 0; i <= numIDs; i++)
|
|
|
|
text[i] = g_gameStrings[textIDs[i]];
|
|
|
|
int retval = showRoomSpecificText(text);
|
|
|
|
free(text);
|
|
|
|
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
|
|
|
int Room::showText(int speaker, int text) {
|
|
|
|
int textIDs[3];
|
|
|
|
textIDs[0] = speaker;
|
|
|
|
textIDs[1] = text;
|
|
|
|
textIDs[2] = TX_BLANK;
|
2018-05-28 19:42:04 -04:00
|
|
|
return showText(textIDs);
|
2018-05-28 18:45:44 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
int Room::showText(int text) {
|
2018-05-28 19:42:04 -04:00
|
|
|
return showText(TX_NULL, text);
|
2018-05-28 18:45:44 -04:00
|
|
|
}
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
void Room::giveItem(int item) {
|
2018-05-29 16:51:05 -04:00
|
|
|
assert(item >= ITEMS_START && item < ITEMS_END);
|
2018-05-23 01:01:36 -04:00
|
|
|
_vm->_itemList[item - ITEMS_START].have = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Room::loadRoomIndex(int roomIndex, int spawnIndex) {
|
2018-05-24 22:08:56 -04:00
|
|
|
if (_vm->_awayMission.crewDownBitset != 0)
|
2018-05-23 01:01:36 -04:00
|
|
|
return;
|
|
|
|
|
2018-05-30 21:06:31 -04:00
|
|
|
_vm->_missionToLoad = _vm->_missionName;
|
|
|
|
_vm->_roomIndexToLoad = roomIndex;
|
|
|
|
_vm->_spawnIndexToLoad = spawnIndex;
|
2018-05-23 01:01:36 -04:00
|
|
|
|
2018-05-30 21:06:31 -04:00
|
|
|
// WORKAROUND: original game manipulates the stack to return directly to the start of
|
|
|
|
// "runAwayMission". Instead, we set some variables and the room will be changed
|
|
|
|
// later. (We wouldn't want to delete the room we're currently in...)
|
2018-05-23 01:01:36 -04:00
|
|
|
}
|
|
|
|
|
2018-05-29 16:51:05 -04:00
|
|
|
void Room::loseItem(int item) {
|
|
|
|
assert(item >= ITEMS_START && item < ITEMS_END);
|
|
|
|
_vm->_itemList[item - ITEMS_START].have = false;
|
2018-05-30 21:06:31 -04:00
|
|
|
|
|
|
|
if (_vm->_awayMission.activeAction == ACTION_USE && _vm->_awayMission.activeObject == item) {
|
|
|
|
_vm->_awayMission.activeAction = ACTION_WALK;
|
|
|
|
_vm->chooseMouseBitmapForAction(ACTION_WALK, false);
|
|
|
|
_vm->hideInventoryIcons();
|
|
|
|
}
|
2018-05-29 16:51:05 -04:00
|
|
|
}
|
|
|
|
|
2018-05-23 19:56:15 -04:00
|
|
|
void Room::walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 finishedAnimActionParam) {
|
2018-05-25 21:53:18 -04:00
|
|
|
if (!(actorIndex >= OBJECT_KIRK && actorIndex <= OBJECT_REDSHIRT))
|
2018-05-23 01:01:36 -04:00
|
|
|
error("Tried to walk a non PC");
|
|
|
|
|
|
|
|
Actor *actor = &_vm->_actorList[actorIndex];
|
|
|
|
Common::String anim = _vm->getCrewmanAnimFilename(actorIndex, "walk");
|
|
|
|
bool success = _vm->actorWalkToPosition(actorIndex, anim, actor->pos.x, actor->pos.y, destX, destY);
|
|
|
|
|
2018-05-23 19:56:15 -04:00
|
|
|
if (success && finishedAnimActionParam != 0) {
|
|
|
|
actor->triggerActionWhenAnimFinished = true;
|
|
|
|
actor->finishedAnimActionParam = finishedAnimActionParam;
|
2018-05-23 01:01:36 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-05-25 21:53:18 -04:00
|
|
|
/**
|
|
|
|
* Loads a pair of .map and .iw files to change the room's collisions and pathfinding.
|
|
|
|
*/
|
|
|
|
void Room::loadMapFile(const Common::String &name) {
|
|
|
|
_vm->_mapFilename = name;
|
|
|
|
_vm->_iwFile.reset();
|
|
|
|
_vm->_mapFile.reset();
|
|
|
|
_vm->_iwFile = SharedPtr<IWFile>(new IWFile(_vm, name + ".iw"));
|
|
|
|
_vm->_mapFile = _vm->loadFile(name + ".map");
|
|
|
|
}
|
|
|
|
|
|
|
|
Common::Point Room::getActorPos(int actorIndex) {
|
|
|
|
return _vm->_actorList[actorIndex].pos;
|
|
|
|
}
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
void Room::playSoundEffectIndex(int soundEffect) {
|
|
|
|
_vm->playSoundEffectIndex(soundEffect);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Room::playMidiMusicTracks(int startTrack, int loopTrack) {
|
|
|
|
_vm->playMidiMusicTracks(startTrack, loopTrack);
|
|
|
|
}
|
|
|
|
|
2018-05-30 21:06:31 -04:00
|
|
|
void Room::endMission(int16 score, int16 arg1, int16 arg2) {
|
|
|
|
_vm->_awayMission.disableInput = true;
|
|
|
|
|
|
|
|
for (int i = 0; i < (_vm->_awayMission.redshirtDead ? 3 : 4); i++) {
|
|
|
|
Actor *actor = &_vm->_actorList[i];
|
|
|
|
Common::String anim = _vm->getCrewmanAnimFilename(i, "teled");
|
|
|
|
_vm->loadActorAnimWithRoomScaling(i, anim, actor->sprite.pos.x, actor->sprite.pos.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
_vm->_kirkActor->animationString[0] = '\0';
|
|
|
|
_vm->_spockActor->animationString[0] = '\0';
|
|
|
|
_vm->_mccoyActor->animationString[0] = '\0';
|
|
|
|
_vm->_redshirtActor->animationString[0] = '\0';
|
|
|
|
|
|
|
|
playSoundEffectIndex(8);
|
|
|
|
|
|
|
|
while (_vm->_kirkActor->spriteDrawn)
|
|
|
|
_vm->handleAwayMissionEvents();
|
|
|
|
|
|
|
|
_vm->_awayMission.disableInput = false;
|
|
|
|
|
|
|
|
// TODO: game mode switch
|
|
|
|
}
|
|
|
|
|
2018-05-24 22:08:56 -04:00
|
|
|
void Room::showGameOverMenu() {
|
|
|
|
_vm->showGameOverMenu();
|
2018-06-05 04:18:14 -04:00
|
|
|
// TODO: shouldn't do this within a room
|
2018-05-24 22:08:56 -04:00
|
|
|
}
|
|
|
|
|
2018-05-23 01:01:36 -04:00
|
|
|
void Room::playVoc(Common::String filename) {
|
|
|
|
_vm->_sound->playVoc(filename);
|
|
|
|
}
|
|
|
|
|
2018-03-10 22:47:52 -05:00
|
|
|
}
|