mirror of
https://github.com/libretro/scummvm.git
synced 2024-12-16 06:39:17 +00:00
534 lines
16 KiB
C++
534 lines
16 KiB
C++
/* 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/iwfile.h"
|
|
#include "startrek/startrek.h"
|
|
|
|
namespace StarTrek {
|
|
|
|
void StarTrekEngine::initAwayMission() {
|
|
_awayMission = AwayMission(); // Initialize members to 0
|
|
|
|
// memset(bitmapBuffer->pixels, 0, 0xfa00);
|
|
|
|
_txtFilename = "ground";
|
|
_loadedText = "";
|
|
|
|
// sub_23a60(); // TODO
|
|
_sound->loadMusicFile("ground");
|
|
|
|
loadRoom(_missionToLoad, _roomIndexToLoad);
|
|
|
|
// Load crew positions for beaming in
|
|
initAwayCrewPositions(4);
|
|
}
|
|
|
|
void StarTrekEngine::runAwayMission() {
|
|
while (true) {
|
|
handleAwayMissionEvents();
|
|
|
|
Common::Point mousePos = _gfx->getMousePos();
|
|
_awayMission.mouseX = mousePos.x;
|
|
_awayMission.mouseY = mousePos.y;
|
|
|
|
assert(_commandQueue.size() <= 16);
|
|
if (!_commandQueue.empty()) {
|
|
// sub_200e7();
|
|
// sub_20118();
|
|
handleAwayMissionCommand();
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrekEngine::cleanupAwayMission() {
|
|
// TODO
|
|
}
|
|
|
|
void StarTrekEngine::loadRoom(const Common::String &missionName, int roomIndex) {
|
|
_keyboardControlsMouse = true;
|
|
|
|
_missionName = _missionToLoad;
|
|
_roomIndex = roomIndex;
|
|
|
|
_roomFrameCounter = 0;
|
|
_awayMission.transitioningIntoRoom = 0;
|
|
|
|
_gfx->fadeoutScreen();
|
|
_sound->stopAllVocSounds();
|
|
|
|
_screenName = _missionName + (char)(_roomIndex + '0');
|
|
|
|
_gfx->setBackgroundImage(_gfx->loadBitmap(_screenName));
|
|
_gfx->loadPri(_screenName + ".pri");
|
|
_gfx->loadPalette("palette");
|
|
_gfx->copyBackgroundScreen();
|
|
|
|
_room = SharedPtr<Room>(new Room(this, _screenName));
|
|
|
|
// Original sets up bytes 0-3 of rdf file as "remote function caller"
|
|
|
|
// Load map file
|
|
_awayMission.activeAction = ACTION_WALK;
|
|
_mapFilename = _screenName;
|
|
_mapFile = loadFile(_mapFilename + ".map");
|
|
_iwFile = SharedPtr<IWFile>(new IWFile(this, _mapFilename + ".iw"));
|
|
|
|
actorFunc1();
|
|
initActors();
|
|
|
|
double num = _room->getVar0c() - _room->getVar0a();
|
|
double den = _room->getVar06() - _room->getVar08() + 1;
|
|
_playerActorScale = (int32)(num * 256 / den);
|
|
|
|
// TODO: RDF vars 1e/1f and 20/21; relates to BAN files?
|
|
|
|
_commandQueue.clear();
|
|
}
|
|
|
|
void StarTrekEngine::initAwayCrewPositions(int warpEntryIndex) {
|
|
_sound->stopAllVocSounds();
|
|
|
|
memset(_awayMission.field25, 0xff, 4);
|
|
|
|
switch (warpEntryIndex) {
|
|
case 0: // 0-3: Read warp positions from RDF file
|
|
case 1:
|
|
case 2:
|
|
case 3:
|
|
for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
|
|
Common::String anim = getCrewmanAnimFilename(i, "walk");
|
|
|
|
int16 rdfOffset = RDF_ROOM_ENTRY_POSITIONS + warpEntryIndex * 32 + i * 8;
|
|
|
|
int16 srcX = _room->readRdfWord(rdfOffset + 0); // Position to spawn at
|
|
int16 srcY = _room->readRdfWord(rdfOffset + 2);
|
|
int16 destX = _room->readRdfWord(rdfOffset + 4); // Position to walk to
|
|
int16 destY = _room->readRdfWord(rdfOffset + 6);
|
|
|
|
actorWalkToPosition(i, anim, srcX, srcY, destX, destY);
|
|
}
|
|
|
|
_kirkActor->walkingIntoRoom = 1;
|
|
_kirkActor->field66 = 0xff;
|
|
_awayMission.transitioningIntoRoom = 1;
|
|
_warpHotspotsActive = false;
|
|
break;
|
|
case 4: // Crew is beaming in.
|
|
warpEntryIndex -= 4;
|
|
for (int i = 0; i < (_awayMission.redshirtDead ? 3 : 4); i++) {
|
|
Common::String animFilename = getCrewmanAnimFilename(i, "tele");
|
|
Common::Point warpPos = _room->getBeamInPosition(i);
|
|
loadActorAnimWithRoomScaling(i, animFilename, warpPos.x, warpPos.y);
|
|
}
|
|
_kirkActor->walkingIntoRoom = 1;
|
|
_kirkActor->field66 = 0xff;
|
|
_awayMission.transitioningIntoRoom = 1;
|
|
playSoundEffectIndex(0x09);
|
|
_warpHotspotsActive = false;
|
|
break;
|
|
case 5:
|
|
break;
|
|
case 6:
|
|
break;
|
|
}
|
|
}
|
|
|
|
void StarTrekEngine::handleAwayMissionEvents() {
|
|
TrekEvent event;
|
|
|
|
if (popNextEvent(&event)) {
|
|
switch (event.type) {
|
|
case TREKEVENT_TICK:
|
|
updateActorAnimations();
|
|
// sub_236bb();
|
|
updateMouseBitmap();
|
|
// doSomethingWithBanData1();
|
|
_gfx->drawAllSprites();
|
|
// doSomethingWithBanData2();
|
|
_sound->checkLoopMusic();
|
|
// sub_22de0();
|
|
_frameIndex++;
|
|
_roomFrameCounter++;
|
|
addCommand(Command(COMMAND_TICK, _roomFrameCounter & 0xff, (_roomFrameCounter >> 8) & 0xff, 0));
|
|
if (_roomFrameCounter >= 2)
|
|
_gfx->incPaletteFadeLevel();
|
|
break;
|
|
|
|
case TREKEVENT_LBUTTONDOWN:
|
|
if (_awayMission.transitioningIntoRoom != 0)
|
|
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");
|
|
Common::Point mousePos = _gfx->getMousePos();
|
|
actorWalkToPosition(0, 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))) {
|
|
hideInventoryIcons();
|
|
_awayMission.activeAction = ACTION_WALK;
|
|
break;
|
|
}
|
|
|
|
int clickedObject = findObjectAt(_gfx->getMousePos());
|
|
hideInventoryIcons();
|
|
|
|
if (clickedObject == OBJECT_INVENTORY_ICON)
|
|
clickedObject = showInventoryMenu(50, 50, false);
|
|
|
|
if (clickedObject == -1)
|
|
clickedObject = -2;
|
|
|
|
_awayMission.passiveObject = clickedObject;
|
|
|
|
bool activeIsCrewman = _awayMission.activeObject <= OBJECT_REDSHIRT;
|
|
bool activeIsItem = _awayMission.activeObject >= ITEMS_START && _awayMission.activeObject < ITEMS_END;
|
|
bool passiveIsCrewman = _awayMission.passiveObject <= OBJECT_REDSHIRT;
|
|
bool passiveIsItem = _awayMission.passiveObject >= ITEMS_START && _awayMission.passiveObject <= ITEMS_END; // FIXME: "<= ITEMS_END" doesn't make sense?
|
|
|
|
if (clickedObject == -2)
|
|
goto checkAddCommand;
|
|
if (checkItemInteractionExists(ACTION_USE, _awayMission.activeObject, _awayMission.passiveObject, 0))
|
|
goto checkAddCommand;
|
|
if (_awayMission.activeObject == OBJECT_MCCOY) {
|
|
if (checkItemInteractionExists(ACTION_USE, OBJECT_IMEDKIT, _awayMission.passiveObject, 0))
|
|
goto checkAddCommand;
|
|
if (checkItemInteractionExists(ACTION_USE, OBJECT_IMEDKIT, _awayMission.passiveObject, 0))
|
|
goto checkAddCommand;
|
|
}
|
|
else if (_awayMission.activeObject == OBJECT_SPOCK) {
|
|
if (checkItemInteractionExists(ACTION_USE, OBJECT_ISTRICOR, _awayMission.passiveObject, 0))
|
|
goto checkAddCommand;
|
|
}
|
|
|
|
if ((activeIsCrewman && passiveIsCrewman)
|
|
|| (activeIsCrewman && passiveIsItem)
|
|
|| (activeIsItem && passiveIsItem)) {
|
|
if (_awayMission.passiveObject == OBJECT_ICOMM) {
|
|
if (sub_2330c())
|
|
break;
|
|
addCommand(Command(ACTION_USE, OBJECT_ICOMM, 0, 0));
|
|
_sound->playVoc("commun30");
|
|
if (_awayMission.activeObject <= OBJECT_REDSHIRT) {
|
|
goto checkShowInventory;
|
|
}
|
|
else {
|
|
_awayMission.activeAction = ACTION_WALK;
|
|
break;
|
|
}
|
|
}
|
|
|
|
_awayMission.activeObject = _awayMission.passiveObject;
|
|
goto checkShowInventory;
|
|
}
|
|
|
|
checkAddCommand:
|
|
if (!sub_2330c())
|
|
{
|
|
if (clickedObject != -2)
|
|
addCommand(Command(_awayMission.activeAction, _awayMission.activeObject, _awayMission.passiveObject, 0));
|
|
|
|
checkShowInventory:
|
|
if (!(_awayMission.field24 & 1))
|
|
showInventoryIcons(true);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ACTION_GET:
|
|
case ACTION_LOOK:
|
|
case ACTION_TALK: {
|
|
int clickedObject = findObjectAt(_gfx->getMousePos());
|
|
// if (!sub_23611(clickedObject, _awayMission.activeAction)) // TODO
|
|
{
|
|
hideInventoryIcons();
|
|
if (clickedObject == OBJECT_INVENTORY_ICON) {
|
|
clickedObject = showInventoryMenu(50, 50, false);
|
|
if (clickedObject == -1)
|
|
clickedObject = -2;
|
|
}
|
|
|
|
_awayMission.activeObject = clickedObject;
|
|
|
|
if (sub_2330c())
|
|
break;
|
|
|
|
if (clickedObject != -2)
|
|
addCommand(Command(_awayMission.activeAction, _awayMission.activeObject, 0, 0));
|
|
|
|
if (_awayMission.activeAction == ACTION_LOOK && !(_awayMission.field24 & 1))
|
|
showInventoryIcons(false);
|
|
}
|
|
break;
|
|
}
|
|
|
|
}
|
|
break; // End of TREKEVENT_LBUTTONDOWN
|
|
|
|
case TREKEVENT_MOUSEMOVE:
|
|
break;
|
|
|
|
case TREKEVENT_RBUTTONDOWN: // TODO: also triggered by key press?
|
|
if (_awayMission.field1d)
|
|
break;
|
|
hideInventoryIcons();
|
|
playSoundEffectIndex(0x07);
|
|
_awayMission.activeAction = showActionMenu();
|
|
if (_awayMission.activeAction == ACTION_USE) { // TODO
|
|
/*
|
|
int16 clickedObject = sub_22f08();
|
|
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()) {
|
|
addCommand(Command(_awayMission.activeAction, _awayMission.activeObject, 0, 0));
|
|
_sound->playVoc("communic");
|
|
_awayMission.activeAction = ACTION_WALK;
|
|
}
|
|
}
|
|
else if (_awayMission.activeAction == ACTION_LOOK)
|
|
showInventoryIcons(false);
|
|
else if (_awayMission.activeAction == ACTION_USE && (_awayMission.field24 & 1) == 0)
|
|
showInventoryIcons(true);
|
|
break;
|
|
|
|
case TREKEVENT_KEYDOWN: // TODO
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void StarTrekEngine::unloadRoom() {
|
|
_gfx->fadeoutScreen();
|
|
// sub_2394b(); // TODO
|
|
actorFunc1();
|
|
_room.reset();
|
|
_mapFile.reset();
|
|
}
|
|
|
|
/**
|
|
* Similar to loadActorAnim, but scale is determined by the y-position in the room. The
|
|
* further up (away) the object is, the smaller it is.
|
|
*/
|
|
int StarTrekEngine::loadActorAnimWithRoomScaling(int actorIndex, const Common::String &animName, int16 x, int16 y) {
|
|
uint16 scale = getActorScaleAtPosition(y);
|
|
return loadActorAnim(actorIndex, animName, x, y, scale);
|
|
}
|
|
|
|
uint16 StarTrekEngine::getActorScaleAtPosition(int16 y) {
|
|
int16 var06 = _room->getVar06();
|
|
int16 var08 = _room->getVar08();
|
|
int16 var0a = _room->getVar0a();
|
|
|
|
if (var06 < y)
|
|
y = var06;
|
|
if (var08 > y)
|
|
y = var08;
|
|
|
|
return ((_playerActorScale * (y - var08)) >> 8) + var0a;
|
|
}
|
|
|
|
SharedPtr<Room> StarTrekEngine::getRoom() {
|
|
return _room;
|
|
}
|
|
|
|
void StarTrekEngine::addCommand(const Command &command) {
|
|
if (command.type != COMMAND_TICK)
|
|
debug("Command %d: %x, %x, %x", command.type, command.gen.b1, command.gen.b2, command.gen.b3);
|
|
_commandQueue.push(command);
|
|
}
|
|
|
|
bool StarTrekEngine::checkItemInteractionExists(int action, int activeItem, int passiveItem, int16 arg6) {
|
|
// TODO
|
|
return false;
|
|
}
|
|
|
|
void StarTrekEngine::handleAwayMissionCommand() {
|
|
Command command = _commandQueue.pop();
|
|
|
|
if ((command.type == COMMAND_FINISHED_BEAMING_IN || command.type == COMMAND_FINISHED_ENTERING_ROOM) && command.gen.b1 == 0xff) {
|
|
_awayMission.transitioningIntoRoom = 0;
|
|
_warpHotspotsActive = true;
|
|
return;
|
|
}
|
|
else if (command.type == COMMAND_FINISHED_ENTERING_ROOM && command.gen.b1 >= 0xe0) { // TODO
|
|
return;
|
|
}
|
|
|
|
switch (command.type) { // TODO: everything
|
|
|
|
case COMMAND_WALK: // TODO
|
|
break;
|
|
|
|
case COMMAND_USE: // TODO
|
|
break;
|
|
|
|
case COMMAND_GET: // TODO
|
|
break;
|
|
|
|
case COMMAND_LOOK:
|
|
if (command.action.activeObject >= ITEMS_START && command.action.activeObject < ITEMS_END) {
|
|
int i = command.action.activeObject - ITEMS_START;
|
|
Common::String text = getItemDescription(_itemList[i].textIndex);
|
|
showTextbox("", text, 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
}
|
|
else if (command.action.activeObject == OBJECT_KIRK)
|
|
showTextbox("", getItemDescription(0x49), 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
else if (command.action.activeObject == OBJECT_SPOCK)
|
|
showTextbox("", getItemDescription(0x4a), 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
else if (command.action.activeObject == OBJECT_MCCOY)
|
|
showTextbox("", getItemDescription(0x4b), 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
else {
|
|
if (command.action.activeObject == OBJECT_REDSHIRT)
|
|
showTextbox("", getItemDescription(0x4c), 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
|
|
// Show generic "nothing of note" text.
|
|
// BUG? This text is also shown after looking at the redshirt. However, his
|
|
// text is normally overridden on a per-mission basis, so perhaps this bug
|
|
// never manifests itself?
|
|
showTextbox("", getItemDescription(0x4d), 20, 20, TEXTCOLOR_YELLOW, 0);
|
|
}
|
|
break;
|
|
|
|
case COMMAND_TALK: // TODO
|
|
break;
|
|
|
|
case COMMAND_TOUCHED_WARP:
|
|
// if (!sub_203e1(command.type)) // Probably calls RDF code
|
|
{
|
|
byte warpIndex = command.gen.b1;
|
|
int16 roomIndex = _room->readRdfWord(RDF_WARP_ROOM_INDICES + warpIndex * 2);
|
|
unloadRoom();
|
|
_sound->loadMusicFile("ground");
|
|
loadRoom(_missionName, roomIndex);
|
|
initAwayCrewPositions(warpIndex ^ 1);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given position is contained in a polygon?
|
|
*
|
|
* The data passed contains the following words in this order:
|
|
* * Index of polygon (unused here)
|
|
* * Number of vertices in polygon
|
|
* * For each vertex: x and y coordinates.
|
|
*/
|
|
bool StarTrekEngine::isPointInPolygon(int16 *data, int16 x, int16 y) {
|
|
int16 numVertices = data[1];
|
|
int16 *vertData = &data[2];
|
|
|
|
for (int i = 0; i < numVertices; i++) {
|
|
Common::Point p1(vertData[0], vertData[1]);
|
|
Common::Point p2;
|
|
if (i == numVertices - 1) // Loop to 1st vertex
|
|
p2 = Common::Point(data[2], data[3]);
|
|
else
|
|
p2 = Common::Point(vertData[2], vertData[3]);
|
|
|
|
if ((p2.x - p1.x) * (y - p1.y) - (p2.y - p1.y) * (x - p1.x) < 0)
|
|
return false;
|
|
|
|
vertData += 2;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void StarTrekEngine::checkTouchedLoadingZone(int16 x, int16 y) {
|
|
int16 offset = _room->getFirstDoorPolygonOffset();
|
|
|
|
while (offset != _room->getDoorPolygonEndOffset()) {
|
|
if (isPointInPolygon((int16*)(_room->_rdfData + offset), x, y)) {
|
|
uint16 var = _room->readRdfWord(offset);
|
|
if (_activeDoorWarpHotspot != var) {
|
|
_activeDoorWarpHotspot = var;
|
|
addCommand(Command(COMMAND_7, var & 0xff, 0, 0));
|
|
}
|
|
return;
|
|
}
|
|
|
|
int16 numVertices = _room->readRdfWord(offset + 2);
|
|
offset += numVertices * 4 + 4;
|
|
}
|
|
_activeDoorWarpHotspot = -1;
|
|
|
|
if (_awayMission.field24 == 0 && _warpHotspotsActive) {
|
|
offset = _room->getFirstWarpPolygonOffset();
|
|
|
|
while (offset != _room->getWarpPolygonEndOffset()) {
|
|
if (isPointInPolygon((int16*)(_room->_rdfData + offset), x, y)) {
|
|
uint16 var = _room->readRdfWord(offset);
|
|
if (_activeWarpHotspot != var) {
|
|
_activeWarpHotspot = var;
|
|
addCommand(Command(COMMAND_TOUCHED_WARP, var & 0xff, 0, 0));
|
|
}
|
|
return;
|
|
}
|
|
|
|
int16 numVertices = _room->readRdfWord(offset + 2);
|
|
offset += numVertices * 4 + 4;
|
|
}
|
|
}
|
|
_activeWarpHotspot = -1;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the given position in the room is solid (not walkable).
|
|
* Reads from a ".map" file which has a bit for each position in the room, which is true
|
|
* when that position is solid.
|
|
*/
|
|
bool StarTrekEngine::isPositionSolid(int16 x, int16 y) {
|
|
assert(x >= 0 && x < SCREEN_WIDTH && y >= 0 && y < SCREEN_HEIGHT);
|
|
|
|
_mapFile->seek((y * SCREEN_WIDTH + x) / 8, SEEK_SET);
|
|
return _mapFile->readByte() & (0x80 >> (x % 8));
|
|
}
|
|
|
|
}
|