aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Stewart2018-05-23 19:56:15 -0400
committerEugene Sandulenko2018-08-09 08:37:30 +0200
commitca3a9dcc8764909e163a860e4a472404620480e2 (patch)
tree4b546d6c1ad2466e25d4c24bbb642d7c0eb49c58
parentbd79e4d653f9476de1501de149589e583e47411d (diff)
downloadscummvm-rg350-ca3a9dcc8764909e163a860e4a472404620480e2.tar.gz
scummvm-rg350-ca3a9dcc8764909e163a860e4a472404620480e2.tar.bz2
scummvm-rg350-ca3a9dcc8764909e163a860e4a472404620480e2.zip
STARTREK: Finish implementing first room
Doors now work, and the system of "walking, then performing an action afterward" is implemented.
-rw-r--r--engines/startrek/action.h22
-rw-r--r--engines/startrek/awaymission.cpp98
-rw-r--r--engines/startrek/awaymission.h7
-rw-r--r--engines/startrek/object.h11
-rw-r--r--engines/startrek/room.cpp49
-rw-r--r--engines/startrek/room.h17
-rw-r--r--engines/startrek/rooms/demon0.cpp34
-rw-r--r--engines/startrek/rooms/function_map.h33
-rw-r--r--engines/startrek/startrek.cpp217
-rw-r--r--engines/startrek/startrek.h18
10 files changed, 410 insertions, 96 deletions
diff --git a/engines/startrek/action.h b/engines/startrek/action.h
index 68d5823000..7be09eb671 100644
--- a/engines/startrek/action.h
+++ b/engines/startrek/action.h
@@ -35,8 +35,8 @@ enum Acton {
ACTION_TOUCHED_WARP = 6,
ACTION_TOUCHED_HOTSPOT = 7, // Doors? (Or just hotspots activated by Kirk moving there?)
- ACTION_FINISHED_BEAMING_IN = 10,
- ACTION_FINISHED_ENTERING_ROOM = 12,
+ ACTION_FINISHED_ANIMATION = 10,
+ ACTION_FINISHED_WALKING = 12,
ACTION_OPTIONS = 13 // Not really an action, but selectable from action menu
};
@@ -46,6 +46,7 @@ struct Action {
byte b2;
byte b3;
+ Action() {}
Action(byte _type, byte _b1, byte _b2, byte _b3)
: type(_type),
b1(_b1),
@@ -60,6 +61,23 @@ struct Action {
bool operator==(const Action &a) const {
return type == a.type && b1 == a.b1 && b2 == a.b2 && b3 == a.b3;
}
+
+ uint32 getBitmask() const {
+ uint32 ret = 0;
+ if (type != 0xff)
+ ret |= (0xff << 24);
+ if (b1 != 0xff)
+ ret |= (0xff << 16);
+ if (b2 != 0xff)
+ ret |= (0xff << 8);
+ if (b3 != 0xff)
+ ret |= (0xff << 0);
+ return ret;
+ }
+
+ uint32 toUint32() const {
+ return (type << 24) | (b1 << 16) | (b2 << 8) | (b3 << 0);
+ }
};
#endif
diff --git a/engines/startrek/awaymission.cpp b/engines/startrek/awaymission.cpp
index 5db0271da1..5bd134df8f 100644
--- a/engines/startrek/awaymission.cpp
+++ b/engines/startrek/awaymission.cpp
@@ -128,8 +128,8 @@ void StarTrekEngine::initAwayCrewPositions(int warpEntryIndex) {
actorWalkToPosition(i, anim, srcX, srcY, destX, destY);
}
- _kirkActor->walkingIntoRoom = 1;
- _kirkActor->field66 = 0xff;
+ _kirkActor->triggerActionWhenAnimFinished = true;
+ _kirkActor->finishedAnimActionParam = 0xff;
_awayMission.transitioningIntoRoom = 1;
_warpHotspotsActive = false;
break;
@@ -140,8 +140,8 @@ void StarTrekEngine::initAwayCrewPositions(int warpEntryIndex) {
Common::Point warpPos = _room->getBeamInPosition(i);
loadActorAnimWithRoomScaling(i, animFilename, warpPos.x, warpPos.y);
}
- _kirkActor->walkingIntoRoom = 1;
- _kirkActor->field66 = 0xff;
+ _kirkActor->triggerActionWhenAnimFinished = true;
+ _kirkActor->finishedAnimActionParam = 0xff;
_awayMission.transitioningIntoRoom = 1;
playSoundEffectIndex(0x09);
_warpHotspotsActive = false;
@@ -179,26 +179,33 @@ void StarTrekEngine::handleAwayMissionEvents() {
break;
switch (_awayMission.activeAction) {
- case ACTION_WALK:
- if (_awayMission.field1c == 0) {
- _kirkActor->sprite.drawMode = 1; // Hide these objects for function call below?
- _spockActor->sprite.drawMode = 1;
- _mccoyActor->sprite.drawMode = 1;
- _redshirtActor->sprite.drawMode = 1;
-
- // findActorClickedOn();
- // ...
-
- _kirkActor->sprite.drawMode = 0;
- _spockActor->sprite.drawMode = 0;
- _mccoyActor->sprite.drawMode = 0;
- _redshirtActor->sprite.drawMode = 0;
-
- Common::String animFilename = getCrewmanAnimFilename(0, "walk");
+ case ACTION_WALK: {
+ if (_awayMission.field1c != 0)
+ break;
+ _kirkActor->sprite.drawMode = 1; // Hide these objects for function call below?
+ _spockActor->sprite.drawMode = 1;
+ _mccoyActor->sprite.drawMode = 1;
+ _redshirtActor->sprite.drawMode = 1;
+
+ int clickedObject = findObjectAt(_gfx->getMousePos());
+
+ _kirkActor->sprite.drawMode = 0;
+ _spockActor->sprite.drawMode = 0;
+ _mccoyActor->sprite.drawMode = 0;
+ _redshirtActor->sprite.drawMode = 0;
+
+ if (walkActiveObjectToHotspot())
+ break;
+
+ if (clickedObject > OBJECT_KIRK && clickedObject < ITEMS_START)
+ addAction(ACTION_WALK, clickedObject, 0, 0);
+ else {
+ Common::String animFilename = getCrewmanAnimFilename(OBJECT_KIRK, "walk");
Common::Point mousePos = _gfx->getMousePos();
- actorWalkToPosition(0, animFilename, _kirkActor->pos.x, _kirkActor->pos.y, mousePos.x, mousePos.y);
+ actorWalkToPosition(OBJECT_KIRK, animFilename, _kirkActor->pos.x, _kirkActor->pos.y, mousePos.x, mousePos.y);
}
break;
+ }
case ACTION_USE: {
if (_awayMission.activeObject == OBJECT_REDSHIRT && (_awayMission.redshirtDead || (_awayMission.field24 & 8))) {
@@ -246,7 +253,7 @@ void StarTrekEngine::handleAwayMissionEvents() {
|| (activeIsCrewman && passiveIsItem)
|| (activeIsItem && passiveIsItem)) {
if (_awayMission.passiveObject == OBJECT_ICOMM) {
- if (sub_2330c())
+ if (walkActiveObjectToHotspot())
break;
addAction(Action(ACTION_USE, OBJECT_ICOMM, 0, 0));
_sound->playVoc("commun30");
@@ -264,7 +271,7 @@ void StarTrekEngine::handleAwayMissionEvents() {
}
checkAddAction:
- if (!sub_2330c())
+ if (!walkActiveObjectToHotspot())
{
if (clickedObject != -2)
addAction(Action(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0));
@@ -291,13 +298,13 @@ checkShowInventory:
_awayMission.activeObject = clickedObject;
- if (sub_2330c())
+ if (walkActiveObjectToHotspot())
break;
if (clickedObject != -2)
addAction(Action(_awayMission.activeAction, _awayMission.activeObject, 0, 0));
- if (_awayMission.activeAction == ACTION_LOOK && !(_awayMission.field24 & 1))
+ if (_awayMission.activeAction == ACTION_LOOK && !(_awayMission.field24 & (1 << OBJECT_KIRK)))
showInventoryIcons(false);
}
break;
@@ -316,16 +323,15 @@ checkShowInventory:
playSoundEffectIndex(0x07);
_awayMission.activeAction = showActionMenu();
if (_awayMission.activeAction == ACTION_USE) {
- //int16 clickedObject = selectObjectForGetAction(); // TODO
- int16 clickedObject = 0;
+ int16 clickedObject = selectObjectForUseAction();
if (clickedObject == -1)
break;
else
_awayMission.activeObject = clickedObject;
}
if (_awayMission.activeAction == ACTION_USE
- && _awayMission.activeObject == OBJECT_ICOMM && (_awayMission.field24 & 1) == 0) { // TODO
- if (!sub_2330c()) {
+ && _awayMission.activeObject == OBJECT_ICOMM && (_awayMission.field24 & (1 << OBJECT_KIRK)) == 0) {
+ if (!walkActiveObjectToHotspot()) {
addAction(Action(_awayMission.activeAction, _awayMission.activeObject, 0, 0));
_sound->playVoc("communic");
_awayMission.activeAction = ACTION_WALK;
@@ -389,13 +395,17 @@ void StarTrekEngine::addAction(const Action &action) {
void StarTrekEngine::handleAwayMissionAction() {
Action action = _actionQueue.pop();
- if ((action.type == ACTION_FINISHED_BEAMING_IN || action.type == ACTION_FINISHED_ENTERING_ROOM) && action.b1 == 0xff) {
+ if ((action.type == ACTION_FINISHED_ANIMATION || action.type == ACTION_FINISHED_WALKING) && action.b1 == 0xff) {
_awayMission.transitioningIntoRoom = 0;
_warpHotspotsActive = true;
return;
}
- else if (action.type == ACTION_FINISHED_ENTERING_ROOM && action.b1 >= 0xe0) { // TODO
- return;
+ else if (action.type == ACTION_FINISHED_WALKING && action.b1 >= 0xe0) {
+ // Finished walking to a position; perform the action that was input back when
+ // they started walking over there.
+ int index = action.b1 - 0xe0;
+ addAction(_actionOnWalkCompletion[index]);
+ _actionOnWalkCompletionInUse[index] = false;
}
if (_room->handleAction(action))
@@ -403,10 +413,14 @@ void StarTrekEngine::handleAwayMissionAction() {
// Action not defined for the room, check for default behaviour
- switch (action.type) { // TODO: everything
+ switch (action.type) {
- case ACTION_WALK: // TODO
- warning("Unhandled walk action: %d %d %d", action.b1, action.b2, action.b3);
+ case ACTION_WALK:
+ if (!_room->handleActionWithBitmask(action)) {
+ Common::String animFilename = getCrewmanAnimFilename(OBJECT_KIRK, "walk");
+ Common::Point mousePos = _gfx->getMousePos();
+ actorWalkToPosition(OBJECT_KIRK, animFilename, _kirkActor->pos.x, _kirkActor->pos.y, mousePos.x, mousePos.y);
+ }
break;
case ACTION_USE: // TODO
@@ -446,8 +460,7 @@ void StarTrekEngine::handleAwayMissionAction() {
break;
case ACTION_TOUCHED_WARP:
- // if (!sub_203e1(action.type)) // Probably calls RDF code
- {
+ if (!_room->handleActionWithBitmask(action)) {
byte warpIndex = action.b1;
int16 roomIndex = _room->readRdfWord(RDF_WARP_ROOM_INDICES + warpIndex * 2);
unloadRoom();
@@ -538,4 +551,15 @@ bool StarTrekEngine::isPositionSolid(int16 x, int16 y) {
return _mapFile->readByte() & (0x80 >> (x % 8));
}
+void StarTrekEngine::loadRoomIndex(int roomIndex, int spawnIndex) {
+ unloadRoom();
+ _sound->loadMusicFile("ground");
+
+ loadRoom(_missionName, roomIndex);
+ initAwayCrewPositions(spawnIndex % 6);
+
+ // WORKAROUND: original game calls "retrieveStackVars" to return execution directly to
+ // the top of "runAwayMission". That can't really be done here. But does it matter?
+}
+
}
diff --git a/engines/startrek/awaymission.h b/engines/startrek/awaymission.h
index b89e9cf0fe..34cdb4ba25 100644
--- a/engines/startrek/awaymission.h
+++ b/engines/startrek/awaymission.h
@@ -29,7 +29,7 @@ struct AwayMission {
int16 mouseY;
byte field1c;
byte field1d;
- byte transitioningIntoRoom; // Set while beaming in or walking into a room
+ byte transitioningIntoRoom; // Set while beaming in or walking into a room. Disables control?
bool redshirtDead;
byte activeAction;
byte activeObject; // The item that is going to be used on something
@@ -39,8 +39,11 @@ struct AwayMission {
// any "default" code for the event, if any.
bool rdfStillDoDefaultAction;
+ // Bits 0-3 correspond to the crewmen? A bit is set if they're not selectable with
+ // the use action?
byte field24;
- int8 field25[4];
+
+ int8 field25[4]; // Sets an object's direction after they finish walking somewhere?
// Demon Word: nonzero if a rude response was given to the prelate.
int16 field29;
diff --git a/engines/startrek/object.h b/engines/startrek/object.h
index 2cfbbd2611..888289249d 100644
--- a/engines/startrek/object.h
+++ b/engines/startrek/object.h
@@ -56,6 +56,13 @@ const int ITEMS_END = ITEMS_START + NUM_ITEMS; // See items.h
const int NUM_OBJECTS = ITEMS_END;
+enum Directions {
+ DIR_N = 0,
+ DIR_S = 1,
+ DIR_E = 2,
+ DIR_W = 3
+};
+
// Some object indices are reserved (see items.h for item objects)
enum Objects {
OBJECT_KIRK = 0,
@@ -80,8 +87,8 @@ struct Actor {
Common::Point pos;
uint16 field60;
uint16 field62;
- uint16 walkingIntoRoom; // Walking or beaming into a room?
- uint16 field66;
+ bool triggerActionWhenAnimFinished;
+ uint16 finishedAnimActionParam;
char animationString2[8];
uint16 field70;
uint16 field72;
diff --git a/engines/startrek/room.cpp b/engines/startrek/room.cpp
index 555a72126b..17d66d8cf7 100644
--- a/engines/startrek/room.cpp
+++ b/engines/startrek/room.cpp
@@ -83,6 +83,23 @@ bool Room::handleAction(const Action &action) {
return false;
}
+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;
+}
+
Common::Point Room::getBeamInPosition(int crewmanIndex) {
int base = 0xaa + crewmanIndex * 4;
return Common::Point(readRdfWord(base), readRdfWord(base + 2));
@@ -91,7 +108,7 @@ Common::Point Room::getBeamInPosition(int crewmanIndex) {
// Interface for room-specific code
-void Room::loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y, uint16 field66) {
+void Room::loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
Actor *actor = &_vm->_actorList[actorIndex];
if (x == -1 || y == -1) {
@@ -104,9 +121,9 @@ void Room::loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y,
else
_vm->loadActorAnim(actorIndex, anim, x, y, 256);
- if (field66 != 0) {
- actor->walkingIntoRoom = 1;
- actor->field66 = field66;
+ if (finishedAnimActionParam != 0) {
+ actor->triggerActionWhenAnimFinished = true;
+ actor->finishedAnimActionParam = finishedAnimActionParam;
}
}
@@ -125,8 +142,8 @@ void Room::loadActorStandAnim(int actorIndex) {
/**
* This is exactly the same as "loadActorAnim", but the game calls it at different times?
*/
-void Room::loadActorAnim2(int actorIndex, Common::String anim, int16 x, int16 y, uint16 field66) {
- loadActorAnim(actorIndex, anim, x, y, field66);
+void Room::loadActorAnim2(int actorIndex, Common::String anim, int16 x, int16 y, uint16 finishedAnimActionParam) {
+ loadActorAnim(actorIndex, anim, x, y, finishedAnimActionParam);
}
// TODO: replace "rdfOffset" with a pointer, so we no longer read from RDF files? (This
@@ -168,17 +185,15 @@ void Room::loadRoomIndex(int roomIndex, int spawnIndex) {
if (_vm->_awayMission.field24 != 0)
return;
- _vm->unloadRoom();
- _vm->_sound->loadMusicFile("ground");
-
- _vm->loadRoom(_vm->_missionName, roomIndex);
- _vm->initAwayCrewPositions(spawnIndex % 6);
+ _vm->loadRoomIndex(roomIndex, spawnIndex);
- // TODO: "retrieveStackVars" call returns program counter directly to beginning of
- // away mission loop. How to handle this?
+ // This room has now been deleted, don't do anything else here.
+ // FIXME: this could a bit dangerous since this is generally called from room-specific
+ // code, which isn't guaranteed to do nothing afterward. Original game would
+ // manipulate the stack to jump directly back to the start of "runAwayMission"...
}
-void Room::walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 field66) {
+void Room::walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 finishedAnimActionParam) {
if (!(actorIndex >= OBJECT_KIRK && actorIndex < OBJECT_REDSHIRT))
error("Tried to walk a non PC");
@@ -186,9 +201,9 @@ void Room::walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 field66)
Common::String anim = _vm->getCrewmanAnimFilename(actorIndex, "walk");
bool success = _vm->actorWalkToPosition(actorIndex, anim, actor->pos.x, actor->pos.y, destX, destY);
- if (success && field66 != 0) {
- actor->walkingIntoRoom = 1;
- actor->field66 = field66;
+ if (success && finishedAnimActionParam != 0) {
+ actor->triggerActionWhenAnimFinished = true;
+ actor->finishedAnimActionParam = finishedAnimActionParam;
}
}
diff --git a/engines/startrek/room.h b/engines/startrek/room.h
index cb00815cff..220c7333ec 100644
--- a/engines/startrek/room.h
+++ b/engines/startrek/room.h
@@ -69,6 +69,9 @@ public:
bool actionHasCode(const Action &action);
bool handleAction(const Action &action);
+ // Same as above, but if any byte in the action is -1 (0xff), it matches any value.
+ bool handleActionWithBitmask(const Action &action);
+
uint16 getFirstHotspot() { return readRdfWord(0x12); }
uint16 getHotspotEnd() { return readRdfWord(0x14); }
@@ -94,10 +97,10 @@ private:
void loadActorAnim(int actorIndex, Common::String anim, int16 x, int16 y, uint16 field66); // Cmd 0x00
void loadActorStandAnim(int actorIndex); // Cmd 0x01
void loadActorAnim2(int actorIndex, Common::String anim, int16 x, int16 y, uint16 field66);// Cmd 0x02
- int showRoomSpecificText(const char **textAddr); // Cmd 0x03
+ int showRoomSpecificText(const char **textAddr); // Cmd 0x03
void giveItem(int item); // Cmd 0x04
void loadRoomIndex(int roomIndex, int spawnIndex); // Cmd 0x06
- void walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 field66); // Cmd 0x08
+ void walkCrewman(int actorIndex, int16 destX, int16 destY, uint16 finishedAnimActionParam);// Cmd 0x08
void playSoundEffectIndex(int soundEffect); // Cmd 0x0f
void playMidiMusicTracks(int startTrack, int loopTrack); // Cmd 0x10
void playVoc(Common::String filename); // Cmd 0x15
@@ -111,8 +114,12 @@ public:
void demon0Tick100();
void demon0Tick140();
void demon0TouchedWarp0();
+ void demon0WalkToBottomDoor();
void demon0TouchedHotspot1();
+ void demon0ReachedBottomDoor();
+ void demon0WalkToTopDoor();
void demon0TouchedHotspot0();
+ void demon0ReachedTopDoor();
void demon0TalkToPrelate();
void demon0LookAtPrelate();
void demon0UsePhaserOnSnow();
@@ -134,9 +141,9 @@ public:
void demon0TalkToMcCoy();
void demon0TalkToSpock();
void demon0AskPrelateAboutSightings();
- void useSTricorderAnywhere();
- void useMTricorderAnywhere();
- void useMTricorderOnPrelate();
+ void demon0UseSTricorderAnywhere();
+ void demon0UseMTricorderAnywhere();
+ void demon0UseMTricorderOnPrelate();
void demon0BadResponse();
};
diff --git a/engines/startrek/rooms/demon0.cpp b/engines/startrek/rooms/demon0.cpp
index f687eaaaa3..41a5bbf802 100644
--- a/engines/startrek/rooms/demon0.cpp
+++ b/engines/startrek/rooms/demon0.cpp
@@ -72,6 +72,13 @@ void Room::demon0TouchedWarp0() {
_vm->_awayMission.rdfStillDoDefaultAction = true;
}
+void Room::demon0WalkToBottomDoor() {
+ _vm->_awayMission.transitioningIntoRoom = 1;
+ _rdfData[0xcd] = 1; // FIXME
+ _vm->_awayMission.field25[OBJECT_KIRK] = DIR_E;
+ walkCrewman(OBJECT_KIRK, 243, 158, 1);
+}
+
void Room::demon0TouchedHotspot1() {
if (_rdfData[0xcd] != 1) // FIXME: not portable to mac/amiga?
return;
@@ -79,13 +86,32 @@ void Room::demon0TouchedHotspot1() {
playSoundEffectIndex(0x05);
}
+void Room::demon0ReachedBottomDoor() {
+ if (++_rdfData[0xca] != 2) // FIXME
+ return;
+ loadRoomIndex(5, 0);
+}
+
+void Room::demon0WalkToTopDoor() {
+ _vm->_awayMission.transitioningIntoRoom = 1;
+ _rdfData[0xcc] = 1; // FIXME
+ _vm->_awayMission.field25[OBJECT_KIRK] = DIR_E;
+ walkCrewman(OBJECT_KIRK, 157, 134, 2);
+}
+
void Room::demon0TouchedHotspot0() {
- if (_rdfData[0xcd] != 1) // FIXME: not portable to mac/amiga?
+ if (_rdfData[0xcc] != 1) // FIXME: not portable to mac/amiga?
return;
loadActorAnim2(10, "s0r0d2", 158, 130, 2);
playSoundEffectIndex(0x05);
}
+void Room::demon0ReachedTopDoor() {
+ if (++_rdfData[0xcb] != 2) // FIXME
+ return;
+ loadRoomIndex(6, 3);
+}
+
void Room::demon0TalkToPrelate() {
const char *options1[] = {
"Capt. Kirk",
@@ -471,7 +497,7 @@ void Room::demon0AskPrelateAboutSightings() {
_vm->_awayMission.field36 = 1;
}
-void Room::useSTricorderAnywhere() {
+void Room::demon0UseSTricorderAnywhere() {
loadActorAnim2(OBJECT_SPOCK, "sscans", -1, -1, 0);
playSoundEffectIndex(0x04);
@@ -484,7 +510,7 @@ void Room::useSTricorderAnywhere() {
showRoomSpecificText(text);
}
-void Room::useMTricorderAnywhere() {
+void Room::demon0UseMTricorderAnywhere() {
loadActorAnim2(OBJECT_MCCOY, "mscans", -1, -1, 0);
playSoundEffectIndex(0x04);
@@ -496,7 +522,7 @@ void Room::useMTricorderAnywhere() {
showRoomSpecificText(text);
}
-void Room::useMTricorderOnPrelate() {
+void Room::demon0UseMTricorderOnPrelate() {
loadActorAnim2(OBJECT_MCCOY, "mscans", -1, -1, 0);
playSoundEffectIndex(0x04);
diff --git a/engines/startrek/rooms/function_map.h b/engines/startrek/rooms/function_map.h
index 6b5dfd0307..ae7e8eb252 100644
--- a/engines/startrek/rooms/function_map.h
+++ b/engines/startrek/rooms/function_map.h
@@ -37,10 +37,17 @@ RoomAction demon0ActionList[] = {
{ Action(ACTION_TOUCHED_WARP, 0, 0, 0), &Room::demon0TouchedWarp0 },
- // TODO: Walk events; entered room event; beamed in event
-
+ { Action(ACTION_WALK, 0x27, 0, 0), &Room::demon0WalkToBottomDoor },
+ { Action(ACTION_WALK, 9, 0, 0), &Room::demon0WalkToBottomDoor },
{ Action(ACTION_TOUCHED_HOTSPOT, 1, 0, 0), &Room::demon0TouchedHotspot1 },
+ { Action(ACTION_FINISHED_WALKING, 1, 0, 0), &Room::demon0ReachedBottomDoor },
+ { Action(ACTION_FINISHED_ANIMATION, 1, 0, 0), &Room::demon0ReachedBottomDoor },
+
+ { Action(ACTION_WALK, 0x26, 0, 0), &Room::demon0WalkToTopDoor },
+ { Action(ACTION_WALK, 10, 0, 0), &Room::demon0WalkToTopDoor },
{ Action(ACTION_TOUCHED_HOTSPOT, 0, 0, 0), &Room::demon0TouchedHotspot0 },
+ { Action(ACTION_FINISHED_WALKING, 2, 0, 0), &Room::demon0ReachedTopDoor },
+ { Action(ACTION_FINISHED_ANIMATION, 2, 0, 0), &Room::demon0ReachedTopDoor },
{ Action(ACTION_TALK, 8, 0, 0), &Room::demon0TalkToPrelate },
{ Action(ACTION_LOOK, 8, 0, 0), &Room::demon0LookAtPrelate },
@@ -59,23 +66,23 @@ RoomAction demon0ActionList[] = {
{ Action(ACTION_LOOK, 0xff, 0, 0), &Room::demon0LookAnywhere },
{ Action(ACTION_LOOK, 0x24, 0, 0), &Room::demon0LookAtBushes },
- { Action(ACTION_LOOK, 0, 0, 0), &Room::demon0LookAtKirk },
- { Action(ACTION_LOOK, 2, 0, 0), &Room::demon0LookAtMcCoy },
- { Action(ACTION_LOOK, 3, 0, 0), &Room::demon0LookAtRedShirt },
- { Action(ACTION_LOOK, 1, 0, 0), &Room::demon0LookAtSpock },
+ { Action(ACTION_LOOK, OBJECT_KIRK, 0, 0), &Room::demon0LookAtKirk },
+ { Action(ACTION_LOOK, OBJECT_MCCOY, 0, 0), &Room::demon0LookAtMcCoy },
+ { Action(ACTION_LOOK, OBJECT_REDSHIRT, 0, 0), &Room::demon0LookAtRedShirt },
+ { Action(ACTION_LOOK, OBJECT_SPOCK, 0, 0), &Room::demon0LookAtSpock },
{ Action(ACTION_LOOK, 9, 0, 0), &Room::demon0LookAtShelter }, // Door 1
{ Action(ACTION_LOOK, 10, 0, 0), &Room::demon0LookAtShelter }, // Door 2
{ Action(ACTION_LOOK, 0x20, 0, 0), &Room::demon0LookAtShelter }, // Shelter itself
- { Action(ACTION_TALK, 0, 0, 0), &Room::demon0TalkToKirk },
- { Action(ACTION_TALK, 3, 0, 0), &Room::demon0TalkToRedshirt },
- { Action(ACTION_TALK, 2, 0, 0), &Room::demon0TalkToMcCoy },
- { Action(ACTION_TALK, 1, 0, 0), &Room::demon0TalkToSpock },
+ { Action(ACTION_TALK, OBJECT_KIRK, 0, 0), &Room::demon0TalkToKirk },
+ { Action(ACTION_TALK, OBJECT_REDSHIRT, 0, 0), &Room::demon0TalkToRedshirt },
+ { Action(ACTION_TALK, OBJECT_MCCOY, 0, 0), &Room::demon0TalkToMcCoy },
+ { Action(ACTION_TALK, OBJECT_SPOCK, 0, 0), &Room::demon0TalkToSpock },
- { Action(ACTION_USE, OBJECT_ISTRICOR, 0xff, 0), &Room::useSTricorderAnywhere },
- { Action(ACTION_USE, OBJECT_IMTRICOR, 0xff, 0), &Room::useMTricorderAnywhere },
- { Action(ACTION_USE, OBJECT_IMTRICOR, 8, 0), &Room::useMTricorderOnPrelate },
+ { Action(ACTION_USE, OBJECT_ISTRICOR, -1, 0), &Room::demon0UseSTricorderAnywhere },
+ { Action(ACTION_USE, OBJECT_IMTRICOR, -1, 0), &Room::demon0UseMTricorderAnywhere },
+ { Action(ACTION_USE, OBJECT_IMTRICOR, 8, 0), &Room::demon0UseMTricorderOnPrelate },
};
}
diff --git a/engines/startrek/startrek.cpp b/engines/startrek/startrek.cpp
index 0c1b6d8ade..b4777a9240 100644
--- a/engines/startrek/startrek.cpp
+++ b/engines/startrek/startrek.cpp
@@ -57,6 +57,8 @@ StarTrekEngine::StarTrekEngine(OSystem *syst, const StarTrekGameDescription *gam
_sound = nullptr;
_macResFork = nullptr;
+ memset(_actionOnWalkCompletionInUse, 0, sizeof(_actionOnWalkCompletionInUse));
+
_clockTicks = 0;
_musicEnabled = true;
@@ -368,8 +370,8 @@ int StarTrekEngine::loadActorAnim(int actorIndex, const Common::String &animName
drawActorToScreen(actor, animName, x, y, scale, true);
}
- actor->walkingIntoRoom = 0;
- actor->field66 = 0;
+ actor->triggerActionWhenAnimFinished = false;
+ actor->finishedAnimActionParam = 0;
return actorIndex;
}
@@ -383,7 +385,7 @@ bool StarTrekEngine::actorWalkToPosition(int actorIndex, const Common::String &a
Actor *actor = &_actorList[actorIndex];
- actor->walkingIntoRoom = 0;
+ actor->triggerActionWhenAnimFinished = false;
if (isPositionSolid(destX, destY))
return false;
@@ -400,7 +402,7 @@ bool StarTrekEngine::actorWalkToPosition(int actorIndex, const Common::String &a
actor->dest.x = destX;
actor->dest.y = destY;
actor->field92 = 0;
- actor->walkingIntoRoom = 0;
+ actor->triggerActionWhenAnimFinished = false;
actor->iwDestPosition = -1;
actor->iwSrcPosition = -1;
@@ -450,8 +452,8 @@ void StarTrekEngine::updateActorAnimations() {
if (actor->animFrame != nextAnimFrame) {
if (nextAnimFrame == actor->numAnimFrames - 1) {
actor->field62++;
- if (actor->walkingIntoRoom != 0) {
- addAction(Action(ACTION_FINISHED_BEAMING_IN, actor->field66, 0, 0));
+ if (actor->triggerActionWhenAnimFinished) {
+ addAction(Action(ACTION_FINISHED_ANIMATION, actor->finishedAnimActionParam, 0, 0));
}
}
}
@@ -520,9 +522,9 @@ void StarTrekEngine::updateActorAnimations() {
}
else { // actor->field90 == 0
if (actor->iwSrcPosition == -1) {
- if (actor->walkingIntoRoom != 0) {
- actor->walkingIntoRoom = 0;
- addAction(Action(ACTION_FINISHED_ENTERING_ROOM, actor->field66 & 0xff, 0, 0));
+ if (actor->triggerActionWhenAnimFinished) {
+ actor->triggerActionWhenAnimFinished = false;
+ addAction(Action(ACTION_FINISHED_WALKING, actor->finishedAnimActionParam & 0xff, 0, 0));
}
actor->sprite.bitmap.reset();
@@ -831,7 +833,7 @@ int StarTrekEngine::findObjectAt(int x, int y) {
error("findObject: Clicked on an unknown sprite");
}
- // word_4b418 = 0;
+ _objectHasWalkPosition = false;
int actionBit = 1 << (_awayMission.activeAction - 1);
int offset = _room->getFirstHotspot();
@@ -840,9 +842,9 @@ int StarTrekEngine::findObjectAt(int x, int y) {
if (word & 0x8000) {
if ((word & actionBit) && isPointInPolygon((int16 *)(_room->_rdfData + offset + 6), x, y)) {
int actorIndex = _room->readRdfWord(offset + 6);
- // word_4b418 = 1;
- // word_4a792 = _room->readRdfWord(offset + 2);
- // word_4a796 = _room->readRdfWord(offset + 4); // TODO
+ _objectHasWalkPosition = true;
+ _objectWalkPosition.x = _room->readRdfWord(offset + 2);
+ _objectWalkPosition.y = _room->readRdfWord(offset + 4);
return actorIndex;
}
@@ -962,6 +964,112 @@ SharedPtr<Bitmap> StarTrekEngine::loadAnimationFrame(const Common::String &filen
return bitmapToReturn;
}
+
+/**
+ * Called when the "get" action is first selected. Returns a selected object.
+ * This behaves like other menus in that it holds game execution, but no actual menu pops
+ * up; it just waits for the player to select something on the screen.
+ */
+int StarTrekEngine::selectObjectForUseAction() {
+ while (true) {
+ if (!(_awayMission.field24 & (1 << OBJECT_KIRK)))
+ showInventoryIcons(false);
+
+ TrekEvent event;
+
+ while (true) {
+ if (!getNextEvent(&event))
+ continue;
+
+ if (event.type == TREKEVENT_TICK) {
+ updateMouseBitmap();
+ _gfx->drawAllSprites();
+ _sound->checkLoopMusic();
+ }
+ else if (event.type == TREKEVENT_LBUTTONDOWN) {
+ removeNextEvent();
+ break;
+ }
+ else if (event.type == TREKEVENT_MOUSEMOVE) {
+ }
+ else if (event.type == TREKEVENT_RBUTTONDOWN) {
+ // Allow this to be processed by main away mission loop
+ break;
+ }
+ else if (event.type == TREKEVENT_KEYDOWN) {
+ if (event.kbd.keycode == Common::KEYCODE_ESCAPE
+ || event.kbd.keycode == Common::KEYCODE_w
+ || event.kbd.keycode == Common::KEYCODE_t
+ || event.kbd.keycode == Common::KEYCODE_u
+ || event.kbd.keycode == Common::KEYCODE_g
+ || event.kbd.keycode == Common::KEYCODE_l
+ || event.kbd.keycode == Common::KEYCODE_SPACE
+ || event.kbd.keycode == Common::KEYCODE_F2) {
+ // Allow these buttons to be processed by main away mission loop
+ break;
+ }
+ else if (event.kbd.keycode == Common::KEYCODE_i) {
+ removeNextEvent();
+ break;
+ }
+ else if (event.kbd.keycode == Common::KEYCODE_RETURN || event.kbd.keycode == Common::KEYCODE_KP_ENTER || event.kbd.keycode == Common::KEYCODE_F1) {
+ // Simulate left-click
+ removeNextEvent();
+ event.type = TREKEVENT_LBUTTONDOWN;
+ break;
+ }
+ }
+
+ removeNextEvent();
+ }
+
+ if (event.type == TREKEVENT_KEYDOWN && event.kbd.keycode == Common::KEYCODE_i) {
+ hideInventoryIcons();
+ int clickedObject = showInventoryMenu(50, 50, true);
+ if (clickedObject == -1)
+ continue;
+ return clickedObject;
+ }
+ else if (event.type == TREKEVENT_LBUTTONDOWN) {
+ int clickedObject = findObjectAt(_gfx->getMousePos());
+ hideInventoryIcons();
+
+ if (clickedObject == -1)
+ continue;
+ else if (isObjectUnusable(clickedObject, ACTION_USE))
+ continue;
+ else if (clickedObject == OBJECT_INVENTORY_ICON) {
+ clickedObject = showInventoryMenu(50, 50, false);
+ if (clickedObject == -1)
+ continue;
+ else
+ return clickedObject;
+ }
+ else if (clickedObject <= OBJECT_REDSHIRT)
+ return clickedObject;
+ else if (isObjectUnusable(OBJECT_KIRK, ACTION_USE))
+ continue;
+ else if (_room->actionHasCode(Action(ACTION_USE, OBJECT_KIRK, clickedObject, 0))
+ || _room->actionHasCode(Action(ACTION_GET, clickedObject, 0, 0))
+ || _room->actionHasCode(Action(ACTION_WALK, clickedObject, 0, 0))) {
+ _awayMission.activeObject = OBJECT_KIRK;
+ _awayMission.passiveObject = clickedObject;
+ _awayMission.activeAction = ACTION_USE;
+ clickedObject = OBJECT_KIRK;
+ if (!walkActiveObjectToHotspot())
+ addAction(Action(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0));
+ return clickedObject;
+ }
+ else
+ continue;
+ }
+ else {
+ hideInventoryIcons();
+ return -1;
+ }
+ }
+}
+
Common::String StarTrekEngine::getCrewmanAnimFilename(int actorIndex, const Common::String &basename) {
const char *crewmanChars = "ksmr";
assert(actorIndex >= 0 && actorIndex < 4);
@@ -1013,6 +1121,72 @@ void StarTrekEngine::updateMouseBitmap() {
chooseMouseBitmapForAction(action, withRedOutline);
}
+/**
+ * Checks whether to walk a crewman to a hotspot (the last one obtained from
+ * a "findObjectAt" call).
+ */
+bool StarTrekEngine::walkActiveObjectToHotspot() {
+ if (!_objectHasWalkPosition)
+ return false;
+
+ int objectIndex;
+ if (_awayMission.activeAction != ACTION_USE)
+ objectIndex = OBJECT_KIRK;
+ else if (_awayMission.activeObject >= OBJECT_KIRK && _awayMission.activeObject <= OBJECT_REDSHIRT)
+ objectIndex = _awayMission.activeObject;
+ else if (_awayMission.activeObject >= ITEMS_START && _awayMission.activeObject <= ITEMS_END) { // FIXME: "<= ITEMS_END" doesn't make sense?
+ if (_awayMission.activeObject == OBJECT_ISTRICOR)
+ objectIndex = OBJECT_SPOCK;
+ else if (_awayMission.activeObject == OBJECT_IMTRICOR)
+ objectIndex = OBJECT_MCCOY;
+ else
+ objectIndex = OBJECT_KIRK;
+ }
+ else // This is the original error message...
+ error("Jay didn't think about pmcheck");
+
+ byte finishedAnimActionParam = false;
+ bool walk = false;
+
+ if (_awayMission.activeAction == ACTION_WALK)
+ walk = true;
+ else {
+ // If this action has code defined for it in this room, buffer the action to be
+ // done after the object finished walking there.
+ Action action(_awayMission.activeAction, _awayMission.activeObject, 0, 0);
+ if (_awayMission.activeAction == ACTION_USE)
+ action.b2 = _awayMission.passiveObject;
+
+ if (_room->actionHasCode(action)) {
+ for (int i = 0; i < MAX_BUFFERED_WALK_ACTIONS; i++) {
+ if (!_actionOnWalkCompletionInUse[i]) {
+ finishedAnimActionParam = i + 0xe0;
+ _actionOnWalkCompletionInUse[i] = true;
+ _actionOnWalkCompletion[i] = action;
+ walk = true;
+ break;
+ }
+ }
+ }
+ }
+
+ if (walk) {
+ Actor *actor = &_actorList[objectIndex];
+ Common::String anim = getCrewmanAnimFilename(objectIndex, "walk");
+ actorWalkToPosition(objectIndex, anim, actor->pos.x, actor->pos.y, _objectWalkPosition.x, _objectWalkPosition.y);
+ if (finishedAnimActionParam != 0) {
+ actor->triggerActionWhenAnimFinished = true;
+ actor->finishedAnimActionParam = finishedAnimActionParam;
+ }
+ _objectHasWalkPosition = false;
+ return true;
+ }
+ else {
+ _objectHasWalkPosition = false;
+ return false;
+ }
+}
+
void StarTrekEngine::showInventoryIcons(bool showItem) {
const char *crewmanFilenames[] = {
"ikirk",
@@ -1057,6 +1231,23 @@ void StarTrekEngine::showInventoryIcons(bool showItem) {
_inventoryIconSprite.setBitmap(_gfx->loadBitmap("inv00"));
}
+/**
+ * Return true if an object is unselectable with use?
+ */
+bool StarTrekEngine::isObjectUnusable(int object, int action) {
+ if (action == ACTION_LOOK)
+ return false;
+ if (object == OBJECT_REDSHIRT && _awayMission.redshirtDead)
+ return true;
+ if (object >= OBJECT_KIRK && object <= OBJECT_REDSHIRT && (_awayMission.field24 & (1 << object)))
+ return true;
+ if (object == OBJECT_IMTRICOR && (_awayMission.field24 & (1 << OBJECT_MCCOY)))
+ return true;
+ if (object == OBJECT_ISTRICOR && (_awayMission.field24 & (1 << OBJECT_SPOCK)))
+ return true;
+ return false;
+}
+
void StarTrekEngine::hideInventoryIcons() {
// Clear these sprites from the screen
if (_itemIconSprite.drawMode == 2)
diff --git a/engines/startrek/startrek.h b/engines/startrek/startrek.h
index 217a06652b..b408b393b0 100644
--- a/engines/startrek/startrek.h
+++ b/engines/startrek/startrek.h
@@ -65,6 +65,8 @@ const int MAX_MENUBUTTONS = 32;
const int TEXTBOX_WIDTH = 26;
const int MAX_TEXTBOX_LINES = 12;
+const int MAX_BUFFERED_WALK_ACTIONS = 32;
+
enum StarTrekGameType {
GType_ST25 = 1,
@@ -190,12 +192,14 @@ public:
int loadActorAnimWithRoomScaling(int actorIndex, const Common::String &animName, int16 x, int16 y);
uint16 getActorScaleAtPosition(int16 y);
void addAction(const Action &action);
+ void addAction(byte b1, byte b2, byte b3, byte b4) { addAction(Action(b1, b2, b3, b4)); }
bool checkItemInteractionExists(int action, int activeItem, int passiveItem, int16 arg6);
void handleAwayMissionAction();
bool isPointInPolygon(int16 *data, int16 x, int16 y);
void checkTouchedLoadingZone(int16 x, int16 y);
bool isPositionSolid(int16 x, int16 y);
+ void loadRoomIndex(int roomIndex, int spawnIndex);
public:
SharedPtr<Room> getRoom();
@@ -231,9 +235,12 @@ public:
int findObjectAt(int x, int y);
int findObjectAt(Common::Point p) { return findObjectAt(p.x, p.y); }
SharedPtr<Bitmap> loadAnimationFrame(const Common::String &filename, Fixed16 scale);
+
+ int selectObjectForUseAction();
Common::String getCrewmanAnimFilename(int actorIndex, const Common::String &basename);
void updateMouseBitmap();
- bool sub_2330c() { return false; } // TODO
+ bool walkActiveObjectToHotspot();
+ bool isObjectUnusable(int objectIndex, int action);
void showInventoryIcons(bool showItem);
void hideInventoryIcons();
int showInventoryMenu(int x, int y, bool restoreMouse);
@@ -384,6 +391,15 @@ public:
Sprite _inventoryIconSprite;
Sprite _itemIconSprite;
+ // Certain hotspots store a position value where objects must walk to before
+ // interacting with them. After calling "findObjectAt", these values are updated.
+ bool _objectHasWalkPosition;
+ Common::Point _objectWalkPosition;
+
+ // Actions to perform after a crewman finishes walking to a position.
+ Action _actionOnWalkCompletion[MAX_BUFFERED_WALK_ACTIONS];
+ bool _actionOnWalkCompletionInUse[MAX_BUFFERED_WALK_ACTIONS];
+
// _clockTicks is based on DOS interrupt 1A, AH=0; read system clock counter.
// Updates 18.206 times every second.
uint32 _clockTicks;