scummvm/engines/lure/game.cpp
2021-12-26 18:48:43 +01:00

1121 lines
30 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 3 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, see <http://www.gnu.org/licenses/>.
*
*/
#include "lure/game.h"
#include "lure/animseq.h"
#include "lure/fights.h"
#include "lure/lure.h"
#include "lure/res_struct.h"
#include "lure/room.h"
#include "lure/scripts.h"
#include "lure/sound.h"
#include "lure/strings.h"
#include "common/config-manager.h"
#include "common/system.h"
namespace Lure {
static Game *int_game = nullptr;
bool Game::isCreated() {
return int_game != nullptr;
}
Game &Game::getReference() {
return *int_game;
}
Game::Game() {
int_game = this;
g_engine->setDebugger(new Debugger());
_fastTextFlag = false;
_preloadFlag = false;
_debugFlag = gDebugLevel >= ERROR_BASIC;
_soundFlag = true;
}
Game::~Game() {
}
void Game::tick() {
// Call the tick method for each hotspot - this is somewaht complicated
// by the fact that a tick proc can unload both itself and/or others,
// so we first get a list of the Ids, and call the tick proc for each
// id in sequence if it's still active
Resources &res = Resources::getReference();
ValueTableData &fields = res.fieldList();
HotspotList::iterator i;
uint16 *idList = new uint16[res.activeHotspots().size()];
int idSize = 0;
for (i = res.activeHotspots().begin(); i != res.activeHotspots().end(); ++i) {
Hotspot const &hotspot = **i;
if (!_preloadFlag || ((hotspot.layer() != 0xff) &&
(hotspot.hotspotId() < FIRST_NONCHARACTER_ID)))
// Add hotspot to list to execute
idList[idSize++] = hotspot.hotspotId();
}
debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot ticks begin");
for (int idCtr = 0; idCtr < idSize; ++idCtr) {
Hotspot *hotspot = res.getActiveHotspot(idList[idCtr]);
if (hotspot) {
fields.setField(CHARACTER_HOTSPOT_ID, hotspot->hotspotId());
hotspot->tick();
}
}
debugC(ERROR_DETAILED, kLureDebugAnimations, "Hotspot ticks end");
delete[] idList;
}
void Game::tickCheck() {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
bool remoteFlag = res.fieldList().getField(OLD_ROOM_NUMBER) != 0;
_state |= GS_TICK;
if ((room.roomNumber() == ROOMNUM_VILLAGE_SHOP) && !remoteFlag && ((_state & GS_TICK) != 0)) {
// In the village shop,
bool tockFlag = (_state & GS_TOCK) != 0;
Sound.addSound(tockFlag ? 16 : 50);
_state = _state ^ (GS_TICK | GS_TOCK);
}
}
void Game::nextFrame() {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
if (Fights.isFighting())
Fights.fightLoop();
res.pausedList().countdown();
room.update();
room.checkCursor();
tick();
Screen::getReference().update();
}
void Game::execute() {
OSystem &system = *g_system;
LureEngine &engine = LureEngine::getReference();
Room &room = Room::getReference();
Resources &res = Resources::getReference();
Events &events = Events::getReference();
Mouse &mouse = Mouse::getReference();
Screen &screen = Screen::getReference();
ValueTableData &fields = res.fieldList();
uint32 timerVal = system.getMillis();
uint32 timerVal2 = system.getMillis();
screen.empty();
screen.setPaletteEmpty();
bool _loadSavegame = false;
if (engine.gameToLoad() != -1)
_loadSavegame = engine.loadGame(engine.gameToLoad());
if (!_loadSavegame) {
// Flag for starting game
setState(GS_RESTART);
}
bool initialRestart = true;
while (!engine.shouldQuit()) {
if ((_state & GS_RESTART) != 0) {
res.reset();
Fights.reset();
if (!initialRestart) room.reset();
setState(0);
Script::execute(STARTUP_SCRIPT);
int bootParam = initialRestart ? ConfMan.getInt("boot_param") : 0;
handleBootParam(bootParam);
initialRestart = false;
}
room.update();
mouse.setCursorNum(CURSOR_ARROW);
mouse.cursorOn();
// Main game loop
while (!engine.shouldQuit() && ((_state & GS_RESTART) == 0)) {
// If time for next frame, allow everything to update
if (system.getMillis() > timerVal + GAME_FRAME_DELAY) {
timerVal = system.getMillis();
nextFrame();
res.delayList().tick();
Sound.musicInterface_ContinuePlaying();
}
// Also check if time to do another village shop tick check
if (system.getMillis() > timerVal2 + GAME_TICK_DELAY) {
timerVal2 = system.getMillis();
tickCheck();
}
while (events.pollEvent()) {
if (events.type() == Common::EVENT_KEYDOWN) {
uint16 roomNum = room.roomNumber();
// Handle special keys
bool handled = true;
switch (events.event().kbd.keycode) {
case Common::KEYCODE_F5:
if (isMenuAvailable())
SaveRestoreDialog::show(true);
break;
case Common::KEYCODE_F7:
SaveRestoreDialog::show(false);
break;
case Common::KEYCODE_F9:
doRestart();
break;
case Common::KEYCODE_KP_PLUS:
if (_debugFlag) {
while (++roomNum <= 51)
if (res.getRoom(roomNum) != nullptr) break;
if (roomNum == 52) roomNum = 1;
room.setRoomNumber(roomNum);
}
break;
case Common::KEYCODE_KP_MINUS:
if (_debugFlag) {
if (roomNum == 1) roomNum = 55;
while (res.getRoom(--roomNum) == nullptr)
;
room.setRoomNumber(roomNum);
}
break;
case Common::KEYCODE_KP_MULTIPLY:
if (_debugFlag)
res.getActiveHotspot(PLAYER_ID)->setRoomNumber(
room.roomNumber());
break;
case Common::KEYCODE_KP_DIVIDE:
case Common::KEYCODE_SLASH:
if (_debugFlag)
room.setShowInfo(!room.showInfo());
break;
case Common::KEYCODE_ESCAPE:
doQuit();
break;
default:
handled = false;
}
if (handled)
continue;
}
if ((events.type() == Common::EVENT_LBUTTONDOWN) ||
(events.type() == Common::EVENT_RBUTTONDOWN))
handleClick();
}
uint16 destRoom;
destRoom = fields.getField(NEW_ROOM_NUMBER);
if (destRoom != 0) {
// Need to change the current room
strcpy(room.statusLine(), "");
bool remoteFlag = fields.getField(OLD_ROOM_NUMBER) != 0;
room.setRoomNumber(destRoom, remoteFlag);
fields.setField(NEW_ROOM_NUMBER, 0);
}
destRoom = fields.playerNewPos().roomNumber;
if (destRoom != 0) {
playerChangeRoom();
}
system.updateScreen();
system.delayMillis(10);
}
room.leaveRoom();
// If Skorl catches player, show the catching animation
if ((_state & GS_CAUGHT) != 0) {
if (!engine.isEGA())
screen.paletteFadeOut();
Palette palette(SKORL_CATCH_PALETTE_ID);
mouse.cursorOff();
static const AnimSoundSequence catchSound[] = { { 12, 0xFF, 0xFF, 1, false }, { 1, 41, 41, 1, false }, {0, 0, 0, 0, false} };
AnimationSequence *anim = new AnimationSequence(SKORL_CATCH_ANIM_ID, palette, true, 5, catchSound);
anim->show();
delete anim;
if (!engine.isEGA())
screen.paletteFadeOut();
}
// If the Restart/Restore dialog is needed, show it
if ((_state & GS_RESTORE) != 0) {
// Show the Restore/Restart dialog
bool restartFlag = RestartRestoreDialog::show();
if (restartFlag)
setState(GS_RESTART);
}
}
}
void Game::handleMenuResponse(uint8 selection) {
Common::String filename;
switch (selection) {
case MENUITEM_CREDITS:
doShowCredits();
break;
case MENUITEM_RESTART_GAME:
doRestart();
break;
case MENUITEM_SAVE_GAME:
SaveRestoreDialog::show(true);
break;
case MENUITEM_RESTORE_GAME:
SaveRestoreDialog::show(false);
break;
case MENUITEM_QUIT:
doQuit();
break;
case MENUITEM_TEXT_SPEED:
doTextSpeed();
break;
case MENUITEM_SOUND:
doSound();
break;
default:
break;
}
}
void Game::playerChangeRoom() {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
ValueTableData &fields = res.fieldList();
SequenceDelayList &delayList = Resources::getReference().delayList();
uint16 roomNum = fields.playerNewPos().roomNumber;
fields.playerNewPos().roomNumber = 0;
Common::Point &newPos = fields.playerNewPos().position;
delayList.clear();
RoomData *roomData = res.getRoom(roomNum);
assert(roomData);
roomData->flags |= HOTSPOTFLAG_FOUND;
// Check for any room change animation
int animFlag = fields.getField(ROOM_EXIT_ANIMATION);
if (animFlag == 1)
displayChuteAnimation();
else if (animFlag != 0)
displayBarrelAnimation();
fields.setField(ROOM_EXIT_ANIMATION, 0);
roomData->exitTime = g_system->getMillis();
// Change to the new room
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
player->currentActions().clear();
player->setRoomNumber(roomNum);
player->setPosition((newPos.x & 0xfff8) | 5, newPos.y & 0xfff8);
player->setOccupied(true);
room.setRoomNumber(roomNum, false);
// Special check for change back from Selena
if ((roomNum != 31) && (roomNum != 14) && (fields.getField(74) != 0)) {
uint16 v = fields.getField(29);
if (v != 0) {
--v;
fields.setField(29, v);
if (v == 0)
res.delayList().add(2, 0xCB7, false);
}
}
}
void Game::displayChuteAnimation() {
Resources &res = Resources::getReference();
Mouse &mouse = Mouse::getReference();
ValueTableData &fields = res.fieldList();
Palette palette(CHUTE_PALETTE_ID);
mouse.setCursorNum(CURSOR_DISK);
if (!LureEngine::getReference().isEGA())
Screen::getReference().paletteFadeOut();
debugC(ERROR_INTERMEDIATE, kLureDebugAnimations, "Starting chute animation");
mouse.cursorOff();
Sound.killSounds();
AnimationSequence *anim = new AnimationSequence(CHUTE_ANIM_ID, palette, true);
Sound.musicInterface_Play(0x40, true);
AnimAbortType result = anim->show();
delete anim;
if (result != ABORT_END_INTRO) {
anim = new AnimationSequence(CHUTE2_ANIM_ID, palette, true, 5, nullptr, 4);
result = anim->show();
delete anim;
}
if (result != ABORT_END_INTRO) {
anim = new AnimationSequence(CHUTE3_ANIM_ID, palette, false);
anim->show();
delete anim;
}
Sound.killSounds();
mouse.cursorOn();
fields.setField(AREA_FLAG, 1);
// WORKAROUND When outside in the town, the game plays an ambient sound
// of twittering birds. When first entering town after falling through
// the chute, this sound does not play; it starts playing after you
// enter and exit a building. Calling removeSounds here triggers the
// function which manages the ambient sounds in town, so the bird
// sounds start playing. Because all other sounds have already been
// removed, this has no side effects.
Sound.removeSounds();
}
void Game::displayBarrelAnimation() {
Mouse &mouse = Mouse::getReference();
Resources &res = Resources::getReference();
LureEngine &engine = LureEngine::getReference();
Screen &screen = Screen::getReference();
mouse.setCursorNum(CURSOR_DISK);
if (!engine.isEGA())
screen.paletteFadeOut();
debugC(ERROR_INTERMEDIATE, kLureDebugAnimations, "Starting barrel animation");
Palette palette(BARREL_PALETTE_ID);
mouse.cursorOff();
Sound.killSounds();
Sound.musicInterface_Play(0x3B, true);
AnimationSequence *anim = new AnimationSequence(BARREL_ANIM_ID, palette, true);
anim->show();
delete anim;
if (!engine.shouldQuit() && !engine.isEGA())
screen.paletteFadeOut();
Sound.killSounds();
mouse.cursorOn();
// Disable town NPCs that are no longer needed
res.deactivateHotspot(SKORL_ID);
res.deactivateHotspot(BLACKSMITH_ID);
res.deactivateHotspot(GWEN_ID);
res.deactivateHotspot(MALLIN_ID);
res.deactivateHotspot(MONK1_ID);
res.deactivateHotspot(GOEWIN_ID);
res.deactivateHotspot(MONK2_ID);
res.deactivateHotspot(WAYNE_ID);
}
void Game::handleClick() {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
ValueTableData &fields = res.fieldList();
Mouse &mouse = Mouse::getReference();
uint16 oldRoomNumber = fields.getField(OLD_ROOM_NUMBER);
if (room.checkInTalkDialog()) {
// Close the active talk dialog
room.setTalkDialog(0, 0, 0, 0);
} else if (oldRoomNumber != 0) {
// Viewing a room remotely - handle returning to prior room
if ((room.roomNumber() != 35) || (fields.getField(87) == 0)) {
// Reset player tick proc and signal to change back to the old room
res.getActiveHotspot(PLAYER_ID)->setTickProc(PLAYER_TICK_PROC_ID);
fields.setField(NEW_ROOM_NUMBER, oldRoomNumber);
fields.setField(OLD_ROOM_NUMBER, 0);
}
} else if ((room.cursorState() == CS_TALKING) ||
(res.getTalkState() != TALK_NONE)) {
// Currently talking, so let its tick proc handle it
} else if (mouse.y() < MENUBAR_Y_SIZE) {
uint8 response = Menu::getReference().execute();
if (response != MENUITEM_NONE)
handleMenuResponse(response);
} else if ((room.cursorState() == CS_SEQUENCE) ||
(room.cursorState() == CS_BUMPED)) {
// No action necessary
} else {
if (mouse.lButton())
handleLeftClick();
else
handleRightClickMenu();
}
}
void Game::handleRightClickMenu() {
Room &room = Room::getReference();
Resources &res = Resources::getReference();
Screen &screen = Screen::getReference();
ValueTableData &fields = res.fieldList();
StringList &stringList = res.stringList();
StringData &strings = StringData::getReference();
Mouse &mouse = Mouse::getReference();
char *statusLine = room.statusLine();
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
HotspotData *hotspot, *useHotspot;
Action action;
uint32 actions;
uint16 itemId = 0xffff;
bool hasItems;
if (room.hotspotId() != 0) {
// Get hotspot actions
actions = room.hotspotActions();
} else {
// Standard actions - drink, examine, look, status
actions = 0x1184000;
}
// If no inventory items remove entries that require them
if (res.numInventoryItems() == 0)
actions &= 0xFEF3F9FD;
// If the player hasn't any money, remove any bribe entry
if (res.fieldList().numGroats() == 0)
actions &= 0xFF7FFFFF;
action = NONE;
hotspot = nullptr;
bool breakFlag = false;
while (!breakFlag) {
statusLine = room.statusLine();
strcpy(statusLine, "");
room.update();
screen.update();
action = PopupMenu::Show(actions);
if (action != NONE) {
sprintf(statusLine, "%s ", stringList.getString(action));
statusLine += strlen(statusLine);
}
switch (action) {
case LOOK:
case STATUS:
breakFlag = true;
break;
case ASK:
hotspot = res.getHotspot(room.hotspotId());
assert(hotspot);
strings.getString(hotspot->nameId, statusLine);
Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE);
statusLine += strlen(statusLine);
itemId = PopupMenu::ShowItems(GET, player->roomNumber());
breakFlag = ((itemId != 0xffff) && (itemId != 0xfffe));
break;
case TELL:
hotspot = res.getHotspot(room.hotspotId());
assert(hotspot);
strings.getString(hotspot->nameId, statusLine);
Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE);
breakFlag = GetTellActions();
break;
case GIVE:
case USE:
case EXAMINE:
case DRINK:
hasItems = (res.numInventoryItems() != 0);
if (!hasItems)
Common::strlcat(statusLine, stringList.getString(S_ACTION_NOTHING), MAX_DESC_SIZE);
statusLine += strlen(statusLine);
room.update();
screen.update();
mouse.waitForRelease();
if (hasItems) {
if (action != DRINK)
hotspot = res.getHotspot(room.hotspotId());
itemId = PopupMenu::ShowInventory();
breakFlag = (itemId != 0xffff);
if (breakFlag) {
fields.setField(USE_HOTSPOT_ID, itemId);
if ((action == GIVE) || (action == USE)) {
// Add in the "X to " or "X on " section of give/use action
useHotspot = res.getHotspot(itemId);
assert(useHotspot);
strings.getString(useHotspot->nameId, statusLine);
if (action == GIVE)
Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE);
else
Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE);
statusLine += strlen(statusLine);
}
else if ((action == DRINK) || (action == EXAMINE))
hotspot = res.getHotspot(itemId);
}
}
break;
default:
hotspot = res.getHotspot(room.hotspotId());
breakFlag = true;
break;
}
}
if (action != NONE) {
player->stopWalking();
if (hotspot == nullptr) {
doAction(action, 0, itemId);
} else {
if (action != TELL) {
// Add the hotspot name to the status line and then go do the action
if ((itemId != 0xffff) && (action != GIVE) && (action != USE)) {
HotspotData *itemHotspot = res.getHotspot(itemId);
if (itemHotspot != nullptr)
strings.getString(itemHotspot->nameId, statusLine);
}
else
strings.getString(hotspot->nameId, statusLine);
}
doAction(action, hotspot->hotspotId, itemId);
}
} else {
// Clear the status line
strcpy(room.statusLine(), "");
}
}
void Game::handleLeftClick() {
Room &room = Room::getReference();
Mouse &mouse = Mouse::getReference();
Resources &res = Resources::getReference();
StringData &strings = StringData::getReference();
StringList &stringList = res.stringList();
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
room.setCursorState(CS_NONE);
player->stopWalking();
player->setDestHotspot(0);
player->setActionCtr(0);
strcpy(room.statusLine(), "");
if ((room.destRoomNumber() == 0) && (room.hotspotId() != 0)) {
// Handle look at hotspot
sprintf(room.statusLine(), "%s ", stringList.getString(LOOK_AT));
HotspotData *hotspot = res.getHotspot(room.hotspotId());
assert(hotspot);
strings.getString(hotspot->nameId, room.statusLine() + strlen(room.statusLine()));
doAction(LOOK_AT, room.hotspotId(), 0xffff);
} else if (room.destRoomNumber() != 0) {
// Walk to another room
RoomExitCoordinateData &exitData =
res.coordinateList().getEntry(room.roomNumber()).getData(room.destRoomNumber());
player->walkTo((exitData.x & 0xfff8) | 5, (exitData.y & 0xfff8),
room.hotspotId() == 0 ? 0xffff : room.hotspotId());
} else {
// Walking within room
player->walkTo(mouse.x(), mouse.y(), 0);
}
}
bool Game::GetTellActions() {
Resources &res = Resources::getReference();
Screen &screen = Screen::getReference();
Room &room = Room::getReference();
StringData &strings = StringData::getReference();
StringList &stringList = res.stringList();
char *statusLine = room.statusLine();
uint16 *commands = &_tellCommands[1];
char *statusLinePos[MAX_TELL_COMMANDS][4];
int paramIndex = 0;
uint16 selectionId;
char selectionName[MAX_DESC_SIZE];
HotspotData *hotspot;
Action action;
const char *continueStrsList[2] = {stringList.getString(S_AND_THEN), stringList.getString(S_FINISH)};
// First word will be the destination character
_tellCommands[0] = room.hotspotId();
_numTellCommands = 0;
// Set up a room transfer list
Common::List<uint16> roomList;
roomList.push_front(room.roomNumber());
// Loop for getting tell commands
while ((_numTellCommands >= 0) && (_numTellCommands < MAX_TELL_COMMANDS)) {
// Loop for each sub-part of commands: Action, up to two params, and
// a "and then" selection to allow for more commands
while ((paramIndex >= 0) && (paramIndex <= 4)) {
// Update status line
statusLine += strlen(statusLine);
statusLinePos[_numTellCommands][paramIndex] = statusLine;
room.update();
screen.update();
switch (paramIndex) {
case 0:
// Prompt for selection of action to perform
action = PopupMenu::Show(0x6A07FD);
if (action == NONE) {
// Move backwards to prior specified action
--_numTellCommands;
if (_numTellCommands < 0)
paramIndex = -1;
else {
paramIndex = 3;
statusLine = statusLinePos[_numTellCommands][paramIndex];
*statusLine = '\0';
}
break;
}
// Add the action to the status line
sprintf(statusLine + strlen(statusLine), "%s ", stringList.getString(action));
// Handle any processing for the action
commands[_numTellCommands * 3] = (uint16) action;
commands[_numTellCommands * 3 + 1] = 0;
commands[_numTellCommands * 3 + 2] = 0;
++paramIndex;
break;
case 1:
// First parameter
action = (Action) commands[_numTellCommands * 3];
if (action != RETURN) {
// Prompt for selection
if ((action != USE) && (action != DRINK) && (action != GIVE))
selectionId = PopupMenu::ShowItems(action, *roomList.begin());
else
selectionId = PopupMenu::ShowItems(GET, *roomList.begin());
if ((selectionId == 0xffff) || (selectionId == 0xfffe)) {
// Move back to prompting for action
--paramIndex;
statusLine = statusLinePos[_numTellCommands][paramIndex];
*statusLine = '\0';
break;
}
if (selectionId < NOONE_ID) {
// Must be a room selection
strings.getString(selectionId, selectionName);
roomList.push_front(selectionId);
} else {
hotspot = res.getHotspot(selectionId);
assert(hotspot);
strings.getString(hotspot->nameId, selectionName);
}
// Store selected entry
commands[_numTellCommands * 3 + 1] = selectionId;
strcat(statusLine, selectionName);
}
++paramIndex;
break;
case 2:
// Second parameter
action = (Action) commands[_numTellCommands * 3];
if (action == ASK)
Common::strlcat(statusLine, stringList.getString(S_FOR), MAX_DESC_SIZE);
else if (action == GIVE)
Common::strlcat(statusLine, stringList.getString(S_TO), MAX_DESC_SIZE);
else if (action == USE)
Common::strlcat(statusLine, stringList.getString(S_ON), MAX_DESC_SIZE);
else {
// All other commads don't need a second parameter
++paramIndex;
break;
}
// Get the second parameter
selectionId = PopupMenu::ShowItems(GET, *roomList.begin());
if ((selectionId == 0xfffe) || (selectionId == 0xffff)) {
--paramIndex;
statusLine = statusLinePos[_numTellCommands][paramIndex];
*statusLine = '\0';
} else {
// Display the second parameter
hotspot = res.getHotspot(selectionId);
assert(hotspot);
strings.getString(hotspot->nameId, selectionName);
strcat(statusLine, selectionName);
commands[_numTellCommands * 3 + 2] = selectionId;
++paramIndex;
}
break;
case 3:
// Prompting for "and then" for more commands
if (_numTellCommands == MAX_TELL_COMMANDS - 1) {
// No more commands allowed
++_numTellCommands;
paramIndex = -1;
} else {
// Only prompt if less than 8 commands entered
selectionId = PopupMenu::Show(2, continueStrsList);
switch (selectionId) {
case 0:
// Get ready for next command
sprintf(statusLine + strlen(statusLine), " %s ", continueStrsList[0]);
++_numTellCommands;
paramIndex = 0;
break;
case 1:
// Increment for just selected command, and add a large amount
// to signal that the command sequence is complete
_numTellCommands += 1 + 0x100;
paramIndex = -1;
break;
default:
// Move to end of just completed command
action = (Action) commands[_numTellCommands * 3];
if (action == RETURN)
paramIndex = 0;
else if ((action == ASK) || (action == GIVE) || (action == USE))
paramIndex = 2;
else {
paramIndex = 1;
if (action == GO_TO)
// Remove top of the cached room change list
roomList.erase(roomList.begin());
}
statusLine = statusLinePos[_numTellCommands][paramIndex];
*statusLine = '\0';
}
}
break;
default:
break;
}
}
}
bool result = (_numTellCommands != -1);
if (result) {
_numTellCommands &= 0xff;
assert((_numTellCommands > 0) && (_numTellCommands <= MAX_TELL_COMMANDS));
strcpy(statusLinePos[0][0], "..");
room.update();
screen.update();
}
return result;
}
void Game::doAction(Action action, uint16 hotspotId, uint16 usedId) {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
ValueTableData &fields = res.fieldList();
Hotspot *player = res.getActiveHotspot(PLAYER_ID);
fields.setField(CHARACTER_HOTSPOT_ID, PLAYER_ID);
fields.setField(ACTIVE_HOTSPOT_ID, hotspotId);
res.setCurrentAction(action);
room.setCursorState(CS_ACTION);
// Set the action
if (action == TELL) {
// Tell action needs special handling because of the variable length parameter list - add in a
// placeholder entry, and then replace it's details with the TELL command data
player->currentActions().addFront(NONE, player->roomNumber(), 0, 0);
player->currentActions().top().supportData().setDetails2(TELL, _numTellCommands * 3 + 1, &_tellCommands[0]);
} else if (action == USE)
// Use action parameters are, for some reason, in reverse order from other 2 item actions
player->currentActions().addFront(action, player->roomNumber(), usedId, hotspotId);
else
// All other action types
player->currentActions().addFront(action, player->roomNumber(), hotspotId, usedId);
}
void Game::doShowCredits() {
Events &events = Events::getReference();
Mouse &mouse = Mouse::getReference();
Screen &screen = Screen::getReference();
Room &room = Room::getReference();
bool isEGA = LureEngine::getReference().isEGA();
Sound.pause();
mouse.cursorOff();
Surface *s = Surface::getScreen(CREDITS_RESOURCE_ID);
if (isEGA) {
s->copyToScreen(0, 0);
} else {
Palette p(CREDITS_RESOURCE_ID - 1);
screen.setPaletteEmpty();
s->copyToScreen(0, 0);
screen.setPalette(&p);
}
delete s;
events.waitForPress();
room.setRoomNumber(room.roomNumber());
mouse.cursorOn();
Sound.resume();
}
void Game::doQuit() {
Sound.pause();
if (getYN())
LureEngine::getReference().quitGame();
Sound.resume();
}
void Game::doRestart() {
Sound.pause();
if (getYN())
setState(GS_RESTART);
Sound.resume();
}
void Game::doTextSpeed() {
Menu &menu = Menu::getReference();
StringList &sl = Resources::getReference().stringList();
_fastTextFlag = !_fastTextFlag;
menu.getMenu(2).entries()[1] = sl.getString(_fastTextFlag ? S_FAST_TEXT : S_SLOW_TEXT);
}
void Game::doSound() {
Menu &menu = Menu::getReference();
StringList &sl = Resources::getReference().stringList();
_soundFlag = !_soundFlag;
menu.getMenu(2).entries()[2] = sl.getString(_soundFlag ? S_SOUND_ON : S_SOUND_OFF);
if (!_soundFlag)
// Stop all currently playing sounds
Sound.killSounds();
}
void Game::handleBootParam(int value) {
Resources &res = Resources::getReference();
ValueTableData &fields = res.fieldList();
Room &room = Room::getReference();
Hotspot *h;
switch (value) {
case 0:
// No parameter - load the first room
room.setRoomNumber(1);
break;
case 1:
// Set player to be in rack room with a few items
// Setup Skorl in cell room
h = res.getActiveHotspot(SKORL_ID);
h->setRoomNumber(1);
h->setPosition(140, 120);
h->currentActions().top().setSupportData(0x1400);
fields.setField(11, 1);
// Set up player
h = res.getActiveHotspot(PLAYER_ID);
h->setRoomNumber(4);
h->setPosition(150, 110);
res.getHotspot(0x2710)->roomNumber = PLAYER_ID; // Bottle
res.getHotspot(0x2713)->roomNumber = PLAYER_ID; // Knife
room.setRoomNumber(4);
break;
case 2:
// Set the player up in the outer cell with a full bottle & knife
h = res.getActiveHotspot(PLAYER_ID);
h->setRoomNumber(2);
h->setPosition(100, 110);
res.getHotspot(0x2710)->roomNumber = PLAYER_ID; // Bottle
fields.setField(BOTTLE_FILLED, 1);
res.getHotspot(0x2713)->roomNumber = PLAYER_ID; // Knife
room.setRoomNumber(2);
break;
default:
room.setRoomNumber(value);
break;
}
}
bool Game::getYN() {
Mouse &mouse = Mouse::getReference();
Events &events = Events::getReference();
Screen &screen = Screen::getReference();
Resources &res = Resources::getReference();
LureEngine &engine = LureEngine::getReference();
Common::Language l = LureEngine::getReference().getLanguage();
Common::KeyCode y = Common::KEYCODE_y;
if (l == Common::FR_FRA) y = Common::KEYCODE_o;
else if ((l == Common::DE_DEU) || (l == Common::NL_NLD)) y = Common::KEYCODE_j;
else if ((l == Common::ES_ESP) || (l == Common::IT_ITA)) y = Common::KEYCODE_s;
else if (l == Common::RU_RUS) y = Common::KEYCODE_l;
bool vKbdFlag = g_system->hasFeature(OSystem::kFeatureVirtualKeyboard);
if (!vKbdFlag)
mouse.cursorOff();
else
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, true);
Surface *s = Surface::newDialog(190, res.stringList().getString(S_CONFIRM_YN));
s->centerOnScreen();
delete s;
bool breakFlag = false;
bool result = false;
do {
while (events.pollEvent()) {
if (events.event().type == Common::EVENT_KEYDOWN) {
Common::KeyCode key = events.event().kbd.keycode;
if (l == Common::RU_RUS) {
if ((key == y) || (key == Common::KEYCODE_y) || (key == Common::KEYCODE_ESCAPE)) {
breakFlag = true;
result = key == y;
}
} else if ((key == y) || (key == Common::KEYCODE_n) ||
(key == Common::KEYCODE_ESCAPE)) {
breakFlag = true;
result = key == y;
}
}
if (events.event().type == Common::EVENT_LBUTTONUP) {
breakFlag = true;
result = true;
}
if (events.event().type == Common::EVENT_RBUTTONUP) {
breakFlag = true;
result = false;
}
}
g_system->delayMillis(10);
} while (!engine.shouldQuit() && !breakFlag);
screen.update();
if (!vKbdFlag)
mouse.cursorOn();
else
g_system->setFeatureState(OSystem::kFeatureVirtualKeyboard, false);
return result;
}
bool Game::isMenuAvailable() {
Resources &res = Resources::getReference();
Room &room = Room::getReference();
uint16 oldRoomNumber = res.fieldList().getField(OLD_ROOM_NUMBER);
if (oldRoomNumber != 0)
// Viewing a room remotely - so the menu isn't available
return false;
else if ((room.cursorState() == CS_TALKING) || (res.getTalkState() != TALK_NONE))
return false;
return true;
}
void Game::saveToStream(Common::WriteStream *stream) {
stream->writeByte(_fastTextFlag);
stream->writeByte(_soundFlag);
}
void Game::loadFromStream(Common::ReadStream *stream) {
Menu &menu = Menu::getReference();
StringList &sl = Resources::getReference().stringList();
_fastTextFlag = stream->readByte() != 0;
menu.getMenu(2).entries()[1] = sl.getString(_fastTextFlag ? S_FAST_TEXT : S_SLOW_TEXT);
_soundFlag = stream->readByte() != 0;
menu.getMenu(2).entries()[2] = sl.getString(_soundFlag ? S_SOUND_ON : S_SOUND_OFF);
// Reset game state flags
setState(0);
}
} // End of namespace Lure