scummvm/queen/command.cpp

1582 lines
40 KiB
C++
Raw Normal View History

/* ScummVM - Scumm Interpreter
* Copyright (C) 2003 The ScummVM project
*
* 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* $Header$
*
*/
#include "stdafx.h"
#include "queen/command.h"
#include "queen/display.h"
#include "queen/input.h"
#include "queen/graphics.h"
#include "queen/logic.h"
#include "queen/sound.h"
#include "queen/talk.h"
#include "queen/walk.h"
namespace Queen {
void CmdText::clear() {
memset(_command, 0, sizeof(_command));
}
void CmdText::display(uint8 color) {
_graphics->textCurrentColor(color);
2003-11-02 16:46:12 +00:00
_graphics->textSetCentered(COMMAND_Y_POS, _command, false);
}
void CmdText::displayTemp(uint8 color, bool locked, const Verb& v, const char *name) {
char temp[MAX_COMMAND_LEN];
if (locked) {
sprintf(temp, "%s%s", _logic->joeResponse(39), v.name());
}
else {
strcpy(temp, v.name());
}
if (name != NULL) {
strcat(temp, " ");
strcat(temp, name);
}
_graphics->textCurrentColor(color);
2003-11-02 16:46:12 +00:00
_graphics->textSetCentered(COMMAND_Y_POS, temp, false);
}
void CmdText::displayTemp(uint8 color, const char *name) {
char temp[MAX_COMMAND_LEN];
sprintf(temp, "%s %s", _command, name);
_graphics->textCurrentColor(color);
2003-11-02 16:46:12 +00:00
_graphics->textSetCentered(COMMAND_Y_POS, temp, false);
}
void CmdText::setVerb(const Verb& v) {
strcpy(_command, v.name());
}
void CmdText::addLinkWord(const Verb& v) {
strcat(_command, " ");
strcat(_command, v.name());
}
void CmdText::addObject(const char *objName) {
strcat(_command, " ");
strcat(_command, objName);
}
bool CmdText::isEmpty() const {
return _command[0] == 0;
}
2003-11-03 19:52:14 +00:00
void CurrentCmdState::init() {
commandLevel = 1;
oldVerb = verb = action = Verb(VERB_NONE);
oldNoun = noun = subject1 = subject2 = 0;
}
void CurrentCmdState::addObject(int16 objNum) {
switch (commandLevel) {
case 1:
subject1 = objNum;
break;
case 2:
subject2 = objNum;
break;
}
}
void SelectedCmdState::init() {
action = defaultVerb = Verb(VERB_NONE);
noun = 0;
}
Command::Command(Logic *l, Graphics *g, Input *i, Walk *w, Sound *s)
2003-11-16 14:12:53 +00:00
: _logic(l), _graphics(g), _input(i), _sound(s), _walk(w) {
_cmdText._graphics = _graphics;
2003-11-02 16:46:12 +00:00
_cmdText._logic = _logic;
}
void Command::clear(bool clearTexts) {
_cmdText.clear();
if (clearTexts) {
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
}
_parse = false;
2003-11-03 19:52:14 +00:00
_curCmd.init();
_selCmd.init();
}
// FIXME: walk argument is useless (always true), as we don't handle the
// pinnacle room exactly like the original
void Command::executeCurrentAction(bool walk) {
_logic->entryObj(0);
if (_curCmd.commandLevel == 2 && _mouseKey == Input::MOUSE_RBUTTON) {
_mouseKey = Input::MOUSE_LBUTTON;
}
// XXX SUBJ1=SUBJECT[1]; SUBJ2=SUBJECT[2];
2003-11-03 19:52:14 +00:00
if (_mouseKey == Input::MOUSE_RBUTTON && _curCmd.subject1 != 0) {
// check to see if selecting default command for object/item
2003-11-03 19:52:14 +00:00
if (_curCmd.subject1 > 0) {
int16 i = _logic->findObjectGlobalNumber(_curCmd.noun);
if (_curCmd.noun == 0 ||
_curCmd.noun > _logic->currentRoomObjMax() ||
_logic->objectData(i)->name <= 0) {
cleanupCurrentAction();
return;
}
uint16 obj = _logic->currentRoomData() + _curCmd.noun;
_curCmd.verb = findDefault(obj, false);
if (_curCmd.verb.isNone()) {
// no match made, so command not yet completed, redefine as WALK_TO
_cmdText.setVerb(Verb(VERB_WALK_TO));
_selCmd.action = Verb(VERB_WALK_TO);
}
else {
_cmdText.setVerb(_curCmd.verb);
_selCmd.action = _curCmd.verb;
}
_cmdText.addObject(_logic->objectName(_logic->objectData(obj)->name));
}
}
// make sure that command is always highlighted when actioned!
_cmdText.display(INK_CMD_SELECT);
_selCmd.noun = _curCmd.noun;
_curCmd.commandLevel = 1;
// XXX SUBJECT[2]=0;
if (handleDefaultCommand(walk)) {
cleanupCurrentAction();
return;
}
// get the commands associated with object/item
uint16 comMax = 0;
uint16 matchingCmds[MAX_MATCHING_CMDS];
CmdListData *cmdList = &_cmdList[1];
uint16 i;
for (i = 1; i <= _numCmdList; ++i, ++cmdList) {
if (cmdList->match(_selCmd.action, _curCmd.subject1, _curCmd.subject2)) {
matchingCmds[comMax] = i;
++comMax;
}
}
if (comMax == 0) {
// no command match was found, so exit
2003-11-03 19:52:14 +00:00
// pass ACTION2 as parameter, as a new Command (and a new ACTION2)
// can be constructed while Joe speaks
2003-11-03 19:52:14 +00:00
executeStandardStuff(_selCmd.action, _curCmd.subject1, _curCmd.subject2);
cleanupCurrentAction();
return;
}
2003-11-08 23:45:45 +00:00
// process each associated command for the Object, until all done
// or one of the Gamestate tests fails...
int16 cond = 0;
2003-11-03 20:48:27 +00:00
CmdListData *com = &_cmdList[0];
uint16 comId = 0;
for (i = 1; i <= comMax; ++i) {
comId = matchingCmds[i - 1];
com = &_cmdList[comId];
// check the Gamestates and set them if necessary
cond = 0;
if (com->setConditions) {
cond = setConditions(comId, (i == comMax));
}
if (cond == -1 && i == comMax) {
// only exit on a condition fail if at last command
// Joe hasnt spoken, so do normal LOOK command
break;
}
else if (cond == -2 && i == comMax) {
// only exit on a condition fail if at last command
// Joe has spoken, so skip LOOK command
cleanupCurrentAction();
return;
}
else if (cond >= 0) {
// we've had a successful Gamestate check, so we must now exit
cond = executeCommand(comId, cond);
break;
}
}
if (cond <= 0 && _selCmd.action.value() == VERB_LOOK_AT) {
look();
}
else {
// only play song if it's a PLAY AFTER type
if (com->song < 0) {
_sound->playSong(-com->song);
}
clear(true);
2003-11-08 23:45:45 +00:00
}
cleanupCurrentAction();
}
void Command::updatePlayer() {
if (_input->cutawayRunning()) return;
lookCurrentRoom();
lookCurrentIcon();
if (!_input->keyVerb().isNone()) {
if (_input->keyVerb().isJournal()) {
_input->clearKeyVerb();
_logic->useJournal();
}
else if (!_input->keyVerb().isSkipText()) {
_curCmd.verb = _input->keyVerb();
if (_curCmd.verb.isInventory()) {
_curCmd.noun = _selCmd.noun = 0;
// Clear old noun and old verb in case we're pointing at an
// object (noun) or item (verb) and we want to use an item
// on it. This was the command will be redisplayed with the
// object/item that the cursor is currently on.
_curCmd.oldNoun = 0;
_curCmd.oldVerb = Verb(VERB_NONE);
grabSelectedItem();
}
else {
grabSelectedVerb();
}
_input->clearKeyVerb();
}
}
_mouseKey = _input->mouseButton();
_input->clearMouseButton();
if (_mouseKey > 0) {
grabCurrentSelection();
}
}
void Command::readCommandsFrom(byte *&ptr) {
uint16 i;
// Command List Data
_numCmdList = READ_BE_UINT16(ptr); ptr += 2;
_cmdList = new CmdListData[_numCmdList + 1];
memset(&_cmdList[0], 0, sizeof(CmdListData));
for (i = 1; i <= _numCmdList; i++) {
_cmdList[i].readFrom(ptr);
}
// Command AREA
_numCmdArea = READ_BE_UINT16(ptr); ptr += 2;
_cmdArea = new CmdArea[_numCmdArea + 1];
memset(&_cmdArea[0], 0, sizeof(CmdArea));
for (i = 1; i <= _numCmdArea; i++) {
_cmdArea[i].readFrom(ptr);
}
2003-11-08 23:45:45 +00:00
// Command OBJECT
_numCmdObject = READ_BE_UINT16(ptr); ptr += 2;
_cmdObject = new CmdObject[_numCmdObject + 1];
memset(&_cmdObject[0], 0, sizeof(CmdObject));
for (i = 1; i <= _numCmdObject; i++) {
_cmdObject[i].readFrom(ptr);
}
// Command INVENTORY
_numCmdInventory = READ_BE_UINT16(ptr); ptr += 2;
_cmdInventory = new CmdInventory[_numCmdInventory + 1];
memset(&_cmdInventory[0], 0, sizeof(CmdInventory));
for (i = 1; i <= _numCmdInventory; i++) {
_cmdInventory[i].readFrom(ptr);
}
2003-11-08 23:45:45 +00:00
// Command GAMESTATE
_numCmdGameState = READ_BE_UINT16(ptr); ptr += 2;
_cmdGameState = new CmdGameState[_numCmdGameState + 1];
memset(&_cmdGameState[0], 0, sizeof(CmdGameState));
for (i = 1; i <= _numCmdGameState; i++) {
_cmdGameState[i].readFrom(ptr);
}
}
int16 Command::executeCommand(uint16 comId, int16 condResult) {
// execute.c l.313-452
debug(0, "Command::executeCommand() - cond = %X, com = %X", condResult, comId);
CmdListData *com = &_cmdList[comId];
if (com->setAreas) {
setAreas(comId);
}
// Don't grab if action is TALK or WALK
if (_selCmd.action.value() != VERB_TALK_TO && _selCmd.action.value() != VERB_WALK_TO) {
if (_curCmd.subject1 > 0) {
_logic->joeGrab(_logic->objectData(_curCmd.subject1)->state, 0);
}
if (_curCmd.subject2 > 0) {
_logic->joeGrab(_logic->objectData(_curCmd.subject2)->state, 0);
}
}
bool cutDone = false;
if (condResult > 0) {
// as we don't handle the pinnacle room exactly like the original,
// the hack described on l.334-339 in execute.c became useless
// check for cutaway/dialogs before updating Objects
const char *desc = _logic->objectTextualDescription(condResult);
if (executeIfCutaway(desc)) {
condResult = 0;
cutDone = true;
}
else if (executeIfDialog(desc)) {
condResult = 0;
}
}
int16 oldImage = 0;
if (_curCmd.subject1 > 0) {
// an object (not an item)
oldImage = _logic->objectData(_curCmd.subject1)->image;
}
if (com->setObjects) {
setObjects(comId);
}
if (com->setItems) {
setItems(comId);
}
if (com->imageOrder != 0) {
ObjectData *od = _logic->objectData(_curCmd.subject1);
// we must update the graphic image of the object
if (com->imageOrder < 0) {
// instead of setting to -1 or -2, flag as negative
if (od->image > 0) {
// make sure that object is not already updated
od->image = -(od->image + 10);
}
}
else {
od->image = com->imageOrder;
}
_logic->roomRefreshObject(_curCmd.subject1);
}
else {
// this object is not being updated by command list, see if
// it has another image copied to it
if (_curCmd.subject1 > 0) {
// an object (not an item)
if (_logic->objectData(_curCmd.subject1)->image != oldImage) {
_logic->roomRefreshObject(_curCmd.subject1);
}
}
}
// don't play music on an OPEN/CLOSE command - in case the command fails
if (_selCmd.action.value() != VERB_OPEN && _selCmd.action.value() != VERB_CLOSE) {
// only play song if it's a PLAY BEFORE type
if (com->song > 0) {
_sound->playSong(com->song);
}
}
// do a special hardcoded section
// l.419-452 execute.c
switch (com->specialSection) {
case 1:
_logic->useJournal();
return condResult;
case 2:
_logic->joeUseDress(true);
break;
case 3:
_logic->joeUseClothes(true);
break;
case 4:
_logic->joeUseUnderwear();
break;
}
changeObjectState(_selCmd.action, _curCmd.subject1, com->song, cutDone);
// execute.c l.533-548
// FIXME: useless test, as .dog has already been played
// if (_selCmd.action.value() == VERB_TALK_TO && cond > 0) {
// if (executeIfDialog(_logic->objectTextualDescription(cond))) {
// cleanupCurrentAction();
// return;
// }
// }
// execute.c l.550-589
// FIXME: the EXECUTE_EXIT1 stuff can be omitted as it is
// more or less redundant code
if (condResult > 0) {
_logic->joeSpeak(condResult, true);
}
return condResult;
}
2003-11-03 19:52:14 +00:00
int16 Command::makeJoeWalkTo(int16 x, int16 y, int16 objNum, const Verb &v, bool mustWalk) {
// Check to see if object is actually an exit to another
// room. If so, then set up new room
ObjectData *objData = _logic->objectData(objNum);
if (objData->x != 0 || objData->y != 0) {
x = objData->x;
y = objData->y;
}
if (v.value() == VERB_WALK_TO) {
_logic->entryObj(objData->entryObj);
if (_logic->entryObj() != 0) {
_logic->newRoom(_logic->objectData(_logic->entryObj())->room);
// because this is an exit object, see if there is
// a walk off point and set (x,y) accordingly
WalkOffData *wod = _logic->walkOffPointForObject(objNum);
if (wod != NULL) {
x = wod->x;
y = wod->y;
}
}
}
else {
_logic->entryObj(0);
_logic->newRoom(0);
}
debug(0, "Command::makeJoeWalkTo() - x=%d y=%d newRoom=%d", x, y, _logic->newRoom());
2003-11-03 19:52:14 +00:00
int16 p = 0;
if (mustWalk) {
// determine which way for Joe to face Object
uint16 facing = State::findDirection(objData->state);
BobSlot *bobJoe = _graphics->bob(0);
if (x == bobJoe->x && y == bobJoe->y) {
_logic->joeFacing(facing);
_logic->joeFace();
}
else {
p = _walk->moveJoe(facing, x, y, false); // XXX inCutaway parameter
if (p != 0) {
_logic->newRoom(0); // cancel makeJoeWalkTo, that should be equivalent to cr10 fix
// XXX if(P != 0) P = FIND_VERB
}
2003-11-03 19:52:14 +00:00
}
}
return p;
}
void Command::grabCurrentSelection() {
_selPosX = _input->mousePosX();
_selPosY = _input->mousePosY();
uint16 zone = _logic->findObjectUnderCursor(_selPosX, _selPosY);
_curCmd.noun = _logic->findObjectRoomNumber(zone);
_curCmd.verb = _logic->findVerbUnderCursor(_selPosX, _selPosY);
_selPosX += _logic->display()->horizontalScroll();
if (_curCmd.verb.isAction()) {
grabSelectedVerb();
}
else if (_curCmd.verb.isInventory()) {
grabSelectedItem();
}
else if (_curCmd.noun > 0 && _curCmd.noun <= _logic->currentRoomObjMax()) {
grabSelectedNoun();
}
else if (_selPosY < ROOM_ZONE_HEIGHT && _curCmd.verb.isNone()) {
// select without a command, do a WALK
clear(true);
_logic->joeWalk(JWM_EXECUTE);
}
}
2003-11-03 19:52:14 +00:00
void Command::grabSelectedObject(int16 objNum, uint16 objState, uint16 objName) {
if (!_curCmd.action.isNone()) {
_cmdText.addObject(_logic->objectName(objName));
}
_curCmd.addObject(objNum);
2003-11-08 23:45:45 +00:00
// if first noun and it's a 2 level command then set up action word
2003-11-03 19:52:14 +00:00
if (_curCmd.action.value() == VERB_USE && _curCmd.commandLevel == 1) {
if (State::findUse(objState) == STATE_USE_ON) {
// object supports 2 levels
_curCmd.commandLevel = 2;
_cmdText.addLinkWord(Verb(VERB_PREP_WITH));
// command not fully constructed
_cmdText.display(INK_CMD_NORMAL);
_parse = false;
}
else {
_cmdText.display(INK_CMD_SELECT);
_parse = true;
}
}
else if (_curCmd.action.value() == VERB_GIVE && _curCmd.commandLevel == 1) {
_curCmd.commandLevel = 2;
_cmdText.addLinkWord(Verb(VERB_PREP_TO));
// command not fully constructed
_cmdText.display(INK_CMD_NORMAL);
_parse = false;
}
else {
_cmdText.display(INK_CMD_SELECT);
_parse = true;
}
if (_parse) {
_curCmd.verb = Verb(VERB_NONE);
//_logic->newRoom(0);
_logic->joeWalk(JWM_EXECUTE);
2003-11-03 19:52:14 +00:00
_selCmd.action = _curCmd.action;
_curCmd.action = Verb(VERB_NONE);
}
}
void Command::grabSelectedItem() {
// if the NOUN has been selected from screen then it is positive
// Otherwise it has been selected from inventory and is negative
// Set PARSE to TRUE, default FALSE if command half complete
_parse = true;
uint16 item = _logic->findInventoryItem(_curCmd.verb.inventoryItem());
if (item == 0 || _logic->itemData(item)->name == 0) {
return;
}
// If we've selected via keyboard, and there is no VERB then do
// the ITEMs default, otherwise keep constructing!
if (_mouseKey == Input::MOUSE_LBUTTON ||
(!_input->keyVerb().isNone() && !_curCmd.verb.isNone())) {
if (_curCmd.action.isNone()) {
if (!_input->keyVerb().isNone()) {
/* 2 - We've selected via the keyboard, no command is being */
/* constructed, so we shall find the item's default */
_curCmd.verb = findDefault(item, true);
if (_curCmd.verb.isNone()) {
// set to Look At
_curCmd.verb = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
_curCmd.action = _curCmd.verb;
}
else {
// Action>0 ONLY if command has been constructed
// Left Mouse Button pressed just do Look At
_curCmd.verb = Verb(VERB_LOOK_AT);
_curCmd.action = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
}
_curCmd.verb = Verb(VERB_NONE);
}
else {
if (_logic->joeWalk() == JWM_MOVE) {
_cmdText.clear();
_curCmd.commandLevel = 1;
_logic->joeWalk(JWM_NORMAL);
_curCmd.action = Verb(VERB_NONE);
lookCurrentIcon();
}
if (!_selCmd.defaultVerb.isNone()) {
alterDefault(_selCmd.defaultVerb, true);
_selCmd.defaultVerb = Verb(VERB_NONE);
clear(true);
return;
}
if (_cmdText.isEmpty()) {
_curCmd.verb = Verb(VERB_LOOK_AT);
_curCmd.action = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
else {
if (_curCmd.commandLevel == 2 && _parse) {
_curCmd.verb = _curCmd.action;
}
else {
_curCmd.verb = findDefault(item, true);
}
if (_curCmd.verb.isNone()) {
// No match made, so command not yet completed. Redefine as LOOK AT
_curCmd.action = Verb(VERB_LOOK_AT);
_cmdText.setVerb(Verb(VERB_LOOK_AT));
}
else {
_curCmd.action = _curCmd.verb;
}
_curCmd.verb = Verb(VERB_NONE);
}
}
2003-11-03 19:52:14 +00:00
grabSelectedObject(-item, _logic->itemData(item)->state, _logic->itemData(item)->name);
}
void Command::grabSelectedNoun() {
// if the NOUN has been selected from screen then it is positive
// otherwise it has been selected from inventory and is negative
2003-11-08 23:45:45 +00:00
// set PARSE to TRUE, default FALSE if command half complete
// click object without a command, if DEFAULT then
// do that, otherwise do a WALK!
uint16 objNum = _logic->currentRoomData() + _curCmd.noun;
int16 objName = _logic->objectData(objNum)->name;
if (objName <= 0) {
// selected a turned off object, so just walk
clear(true);
_curCmd.noun = 0;
//_logic->newRoom(0);
_logic->joeWalk(JWM_EXECUTE);
return;
}
if (_curCmd.verb.isNone()) {
if (_mouseKey == Input::MOUSE_LBUTTON) {
if ((_curCmd.commandLevel != 2 && _curCmd.action.isNone()) ||
(_curCmd.commandLevel == 2 && _parse)) {
// action2 > 0 only if command has been constructed
// lmb pressed, just walk
_curCmd.verb = Verb(VERB_WALK_TO);
_curCmd.action = Verb(VERB_WALK_TO);
_cmdText.setVerb(Verb(VERB_WALK_TO));
}
}
else if (_mouseKey == Input::MOUSE_RBUTTON) {
// rmb pressed, do default if one exists
if (!_selCmd.defaultVerb.isNone()) {
// change default of command
alterDefault(_selCmd.defaultVerb, false);
_selCmd.defaultVerb = Verb(VERB_NONE);
clear(true);
return;
}
if (_cmdText.isEmpty()) {
2003-11-08 23:45:45 +00:00
// Ensures that Right Mkey will select correct default
_curCmd.verb = findDefault(objNum, false);
if (!_curCmd.verb.isNone()) {
// no match made, redefine as Walk To
_selCmd.action = Verb(VERB_WALK_TO);
}
else {
_selCmd.action = _curCmd.verb;
}
_cmdText.setVerb(_selCmd.action);
_cmdText.addObject(_logic->objectName(_logic->objectData(objNum)->name));
}
else {
_curCmd.verb = Verb(VERB_NONE);
if ((_curCmd.commandLevel == 2 && !_parse) || !_curCmd.action.isNone()) {
_curCmd.verb = _curCmd.action;
}
else {
_curCmd.verb = findDefault(objNum, false);
}
if (_curCmd.verb.isNone()) {
_curCmd.action = Verb(VERB_WALK_TO);
_cmdText.setVerb(Verb(VERB_WALK_TO));
}
else {
_curCmd.action = _curCmd.verb;
}
_curCmd.verb = Verb(VERB_NONE);
}
}
}
_selCmd.noun = 0;
// XXX PARSE=1;
// XXX if((ACTION==6 || ACTION==5) && CLEVEL==1) PARSE=0;
2003-11-03 19:52:14 +00:00
grabSelectedObject(objNum, _logic->objectData(objNum)->state, objName);
}
2003-11-08 23:45:45 +00:00
void Command::grabSelectedVerb() {
if (_curCmd.verb.isScrollInventory()) {
2003-11-08 23:45:45 +00:00
// move through inventory (by four if right mouse button)
uint16 scroll = _mouseKey == Input::MOUSE_RBUTTON ? 4 : 1;
_logic->inventoryScroll(scroll, _curCmd.verb.value() == VERB_SCROLL_UP);
}
else if (_curCmd.verb.isPanelCommand() || _curCmd.verb.value() == VERB_WALK_TO) {
_curCmd.action = _curCmd.verb;
2003-11-03 19:52:14 +00:00
_curCmd.subject1 = 0;
_curCmd.subject2 = 0;
// if right mouse key selected, then store command VERB
if (_mouseKey == Input::MOUSE_RBUTTON) {
_selCmd.defaultVerb = _curCmd.verb;
_cmdText.displayTemp(11, true, _curCmd.verb);
2003-11-08 23:45:45 +00:00
}
else {
_selCmd.defaultVerb = Verb(VERB_NONE);
if (_logic->joeWalk() == JWM_MOVE && !_curCmd.verb.isNone()) {
_logic->joeWalk(JWM_NORMAL);
}
_curCmd.commandLevel = 1;
_curCmd.oldVerb = Verb(VERB_NONE);
_curCmd.oldNoun = 0;
_cmdText.setVerb(_curCmd.verb);
_cmdText.display(INK_CMD_NORMAL);
}
}
}
bool Command::executeIfCutaway(const char *description) {
2003-11-02 16:46:12 +00:00
if (strlen(description) > 4 &&
scumm_stricmp(description + strlen(description) - 4, ".cut") == 0) {
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
char nextCutaway[20];
memset(nextCutaway, 0, sizeof(nextCutaway));
_logic->playCutaway(description, nextCutaway);
while (nextCutaway[0] != '\0') {
_logic->playCutaway(nextCutaway, nextCutaway);
}
return true;
}
return false;
}
bool Command::executeIfDialog(const char *description) {
2003-11-06 08:53:09 +00:00
if (strlen(description) > 4 &&
scumm_stricmp(description + strlen(description) - 4, ".dog") == 0) {
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
2003-11-06 08:53:09 +00:00
char cutaway[20];
memset(cutaway, 0, sizeof(cutaway));
_logic->dialogue(description, _selCmd.noun, cutaway);
2003-11-06 08:53:09 +00:00
while (cutaway[0] != '\0') {
char currentCutaway[20];
strcpy(currentCutaway, cutaway);
_logic->playCutaway(currentCutaway, cutaway);
}
2003-11-06 08:53:09 +00:00
return true;
}
return false;
}
bool Command::handleDefaultCommand(bool walk) {
// l.96-141 execute.c
uint16 objMax = _logic->currentRoomObjMax();
2003-11-03 19:52:14 +00:00
uint16 roomData = _logic->currentRoomData();
// select without a command or WALK TO ; do a WALK
if ((_selCmd.action.value() == VERB_WALK_TO || _selCmd.action.isNone()) &&
(_selCmd.noun > objMax || _selCmd.noun == 0)) {
if (_selCmd.action.isNone()) {
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
}
_walk->moveJoe(0, _selPosX, _selPosY, false); // XXX inCutaway parameter
return true;
}
// check to see if one of the objects is hidden
2003-11-03 19:52:14 +00:00
if (_curCmd.subject1 > 0 && _logic->objectData(_curCmd.subject1)->name <= 0) {
return true;
}
if (// _selCmd.action.value() == VERB_GIVE && // can be TALK_TO !
2003-11-03 19:52:14 +00:00
_curCmd.subject2 > 0 && _logic->objectData(_curCmd.subject2)->name <= 0) {
return true;
}
2003-11-08 23:45:45 +00:00
// check for USE command on exists
2003-11-03 19:52:14 +00:00
if (_selCmd.action.value() == VERB_USE &&
_curCmd.subject1 > 0 && _logic->objectData(_curCmd.subject1)->entryObj > 0) {
_selCmd.action = Verb(VERB_WALK_TO);
}
if (_selCmd.noun > 0 && _selCmd.noun <= objMax) {
2003-11-03 19:52:14 +00:00
uint16 objNum = _logic->currentRoomData() + _selCmd.noun;
if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _selCmd.action, walk) != 0) {
return true;
}
if (_selCmd.action.value() == VERB_WALK_TO && _logic->objectData(roomData + _selCmd.noun)->entryObj < 0) {
return true;
}
}
return false;
}
void Command::executeStandardStuff(const Verb& action, int16 subj1, int16 subj2) {
// l.158-272 execute.c
uint16 k;
switch (action.value()) {
case VERB_LOOK_AT:
look();
break;
2003-11-08 23:45:45 +00:00
case VERB_OPEN:
// 'it doesn't seem to open'
_logic->joeSpeak(1);
break;
case VERB_USE:
if (subj1 < 0) {
2003-11-03 19:52:14 +00:00
k = _logic->itemData(-subj1)->sfxDescription;
if (k > 0) {
_logic->joeSpeak(k, true);
}
else {
_logic->joeSpeak(2);
}
}
else {
_logic->joeSpeak(2);
}
break;
2003-11-08 23:45:45 +00:00
case VERB_TALK_TO:
_logic->joeSpeak(24 + Logic::randomizer.getRandomNumber(2));
break;
2003-11-08 23:45:45 +00:00
case VERB_CLOSE:
_logic->joeSpeak(2);
break;
case VERB_MOVE:
// 'I can't move it'
if (subj1 > 0) {
int16 img = _logic->objectData(subj1)->image;
if (img == -4 || img == -3) {
_logic->joeSpeak(18);
}
else {
_logic->joeSpeak(3);
}
}
else {
_logic->joeSpeak(3);
}
break;
case VERB_GIVE:
// 'I can't give the subj1 to subj2'
if (subj1 < 0) {
k = 11;
if (subj2 > 0) {
int16 img = _logic->objectData(subj2)->image;
if (img == -4 || img == -3) {
_logic->joeSpeak(27 + Logic::randomizer.getRandomNumber(2));
}
}
else {
_logic->joeSpeak(11);
}
}
else {
_logic->joeSpeak(12);
}
break;
case VERB_PICK_UP:
if (subj1 < 0) {
_logic->joeSpeak(14);
}
else {
int16 img = _logic->objectData(subj2)->image;
if (img == -4 || img == -3) {
// Trying to get a person
_logic->joeSpeak(20);
}
else {
switch(Logic::randomizer.getRandomNumber(3)) {
case 0:
// 'I can't pick that up'
_logic->joeSpeak(5);
break;
case 1:
// 'I don't think I need that'
_logic->joeSpeak(6);
break;
case 2:
// 'I'd rather leave it here'
_logic->joeSpeak(7);
break;
case 3:
// 'I don't think I'd have any use for that'
_logic->joeSpeak(8);
break;
}
}
}
break;
default:
break;
}
}
void Command::changeObjectState(const Verb& action, int16 obj, int16 song, bool cutDone) {
// l.456-533 execute.c
ObjectData *objData = _logic->objectData(obj);
if (action.value() == VERB_OPEN && !cutDone) {
if (State::findOn(objData->state) == STATE_ON_ON) {
State::alterOn(&objData->state, STATE_ON_OFF);
State::alterDefaultVerb(&objData->state, Verb(VERB_NONE));
// play music if it exists... (or SFX for open/close door)
if (song != 0) {
_sound->playSong(ABS(song));
}
if (objData->entryObj != 0) {
2003-11-08 23:45:45 +00:00
// if it's a door, then update door that it links to
openOrCloseAssociatedObject(action, objData->entryObj);
objData->entryObj = ABS(objData->entryObj);
}
}
else {
// 'it's already open !'
_logic->joeSpeak(9);
}
}
else if (action.value() == VERB_CLOSE && !cutDone) {
if (State::findOn(objData->state) == STATE_ON_OFF) {
State::alterOn(&objData->state, STATE_ON_ON);
State::alterDefaultVerb(&objData->state, Verb(VERB_OPEN));
// play music if it exists... (or SFX for open/close door)
if (song != 0) {
_sound->playSong(ABS(song));
}
if (objData->entryObj != 0) {
2003-11-08 23:45:45 +00:00
// if it's a door, then update door that it links to
openOrCloseAssociatedObject(action, objData->entryObj);
objData->entryObj = -ABS(objData->entryObj);
}
}
else {
// 'it's already closed !'
_logic->joeSpeak(10);
}
}
else if (action.value() == VERB_MOVE) {
State::alterOn(&objData->state, STATE_ON_OFF);
}
}
void Command::cleanupCurrentAction() {
// l.595-597 execute.c
_logic->joeFace();
_curCmd.oldNoun = 0;
_curCmd.oldVerb = Verb(VERB_NONE);
}
Verb Command::findDefault(uint16 obj, bool itemType) {
uint16 s = itemType ? _logic->itemData(obj)->state : _logic->objectData(obj)->state;
return State::findDefaultVerb(s);
}
void Command::alterDefault(const Verb& def, bool itemType) {
uint16 *newDefaultState = 0;
const char *name = NULL;
_curCmd.noun = _logic->findObjectUnderCursor(_selPosX, _selPosY);
if (!itemType) {
if (_curCmd.noun == 0) {
return;
}
uint16 i = _logic->findObjectGlobalNumber(_curCmd.noun);
ObjectData *od = _logic->objectData(i);
if (od->name < 0) {
return;
}
newDefaultState = &od->state;
name = _logic->objectTextualDescription(od->name);
}
else {
uint16 item = _logic->findInventoryItem(_curCmd.verb.inventoryItem());
if (item == 0 || _logic->itemData(item)->name == 0) {
return;
}
ItemData *id = _logic->itemData(item);
newDefaultState = &id->state;
name = _logic->objectTextualDescription(id->name);
}
State::alterDefaultVerb(newDefaultState, def);
if (_curCmd.noun == 0) {
_cmdText.clear();
}
else {
_cmdText.setVerb(def.isNone() ? Verb(VERB_WALK_TO) : def);
}
_cmdText.displayTemp(INK_CMD_NORMAL, name);
_curCmd.oldNoun = _curCmd.noun;
}
void Command::openOrCloseAssociatedObject(const Verb& action, int16 otherObj) {
warning("using Command::openOrCloseAssociatedObject()");
CmdListData *cmdList = &_cmdList[1];
uint16 com = 0;
uint16 i;
for (i = 1; i <= _numCmdList && com != 0; ++i, ++cmdList) {
if (cmdList->match(action, otherObj, 0)) {
if (cmdList->setConditions) {
CmdGameState *cmdGs = _cmdGameState;
uint16 j;
for (j = 1; j <= _numCmdGameState; ++j) {
if (cmdGs[j].id == i && cmdGs[i].gameStateSlot > 0) {
2003-11-03 19:52:14 +00:00
// FIXME: weird, why using 'i' instead of 'j' ?
if (_logic->gameState(cmdGs[i].gameStateSlot) == cmdGs[i].gameStateValue) {
com = i;
break;
}
}
}
}
else {
com = i;
break;
}
}
}
if (com != 0) {
cmdList = &_cmdList[com];
ObjectData *objData = _logic->objectData(otherObj);
if (cmdList->imageOrder != 0) {
// update the graphic image of object
objData->image = cmdList->imageOrder;
}
if (action.value() == VERB_OPEN) {
if (State::findOn(objData->state) == STATE_ON_ON) {
State::alterOn(&objData->state, STATE_ON_OFF);
State::alterDefaultVerb(&objData->state, Verb(VERB_NONE));
objData->entryObj = ABS(objData->entryObj);
}
}
else if (action.value() == VERB_CLOSE) {
if (State::findOn(objData->state) == STATE_ON_OFF) {
State::alterOn(&objData->state, STATE_ON_ON);
State::alterDefaultVerb(&objData->state, Verb(VERB_OPEN));
objData->entryObj = -ABS(objData->entryObj);
}
}
}
}
2003-11-08 23:45:45 +00:00
int16 Command::setConditions(uint16 command, bool lastCmd) {
debug(9, "Command::setConditions(%d, %d)", command, lastCmd);
// Test conditions, if FAIL write && exit, Return -1
// if(Joe speaks before he returns, -2 is returned
// This way a -1 return will allow Joe to speak normal description
uint16 temp[21];
memset(temp, 0, sizeof(temp));
uint16 tempInd = 0;
int16 ret = 0;
uint16 i;
CmdGameState *cmdGs = &_cmdGameState[1];
for (i = 1; i <= _numCmdGameState; ++i, ++cmdGs) {
if (cmdGs->id == command) {
if (cmdGs->gameStateSlot > 0) {
if (_logic->gameState(cmdGs->gameStateSlot) != cmdGs->gameStateValue) {
2003-11-08 16:36:54 +00:00
debug(0, "Command::setConditions() - GS[%d] == %d (should be %d)", cmdGs->gameStateSlot, _logic->gameState(cmdGs->gameStateSlot), cmdGs->gameStateValue);
// failed test
ret = i;
break;
}
}
else {
temp[tempInd] = i;
++tempInd;
}
}
}
if (ret > 0) {
// we've failed, so see if we need to make Joe speak
cmdGs = &_cmdGameState[ret];
2003-11-05 10:09:54 +00:00
if (cmdGs->speakValue > 0 && lastCmd) {
// check to see if fail state is in fact a cutaway
const char *objDesc = _logic->objectTextualDescription(cmdGs->speakValue);
if (!executeIfCutaway(objDesc) && !executeIfDialog(objDesc)) {
_logic->joeSpeak(cmdGs->speakValue, true);
}
ret = -2;
}
else {
ret = -1;
}
}
else {
ret = 0;
// all tests were okay, now set gamestates
for (i = 0; i < tempInd; ++i) {
cmdGs = &_cmdGameState[temp[i]];
_logic->gameState(ABS(cmdGs->gameStateSlot), cmdGs->gameStateValue);
// set return value for Joe to say something
ret = cmdGs->speakValue;
}
}
return ret;
}
void Command::setAreas(uint16 command) {
debug(9, "Command::setAreas(%d)", command);
CmdArea *cmdArea = &_cmdArea[1];
uint16 i;
for (i = 1; i <= _numCmdArea; ++i, ++cmdArea) {
if (cmdArea->id == command) {
uint16 areaNum = ABS(cmdArea->area);
Area *area = _logic->area(cmdArea->room, areaNum);
if (cmdArea->area > 0) {
// turn on area
area->mapNeighbours = ABS(area->mapNeighbours);
}
else {
// turn off area
area->mapNeighbours = -ABS(area->mapNeighbours);
}
}
}
}
void Command::setObjects(uint16 command) {
2003-11-08 23:45:45 +00:00
debug(9, "Command::setObjects(%d)", command);
CmdObject *cmdObj = &_cmdObject[1];
uint16 i;
for (i = 1; i <= _numCmdObject; ++i, ++cmdObj) {
if (cmdObj->id == command) {
// found an object
uint16 dstObj = ABS(cmdObj->dstObj);
ObjectData *objData = _logic->objectData(dstObj);
debug(0, "Command::setObjects() - dstObj=%X srcObj=%X _curCmd.subject1=%X", cmdObj->dstObj, cmdObj->srcObj, _curCmd.subject1);
if (cmdObj->dstObj > 0) {
// show the object
objData->name = ABS(objData->name);
// test that the object has not already been deleted
// by checking if it is not equal to zero
if (cmdObj->srcObj == -1 && objData->name != 0) {
// delete object by setting its name to 0 and
// turning off graphic image
objData->name = 0;
if (objData->room == _logic->currentRoom()) {
2003-11-03 19:52:14 +00:00
if (dstObj != _curCmd.subject1) {
// if the new object we have updated is on screen and is not the
// current object, then we can update. This is because we turn
// current object off ourselves by COM_LIST(com, 8)
if (objData->image != -3 && objData->image != -4) {
// it is a normal object (not a person)
// turn the graphic image off for the object
objData->image = -(objData->image + 10);
}
}
// invalidate object area
uint16 objZone = dstObj - _logic->currentRoomData();
_logic->zoneSet(ZONE_ROOM, objZone, 0, 0, 1, 1);
}
}
if (cmdObj->srcObj > 0) {
// copy data from dummy object to object
int16 image1 = objData->image;
int16 image2 = _logic->objectData(cmdObj->srcObj)->image;
_logic->objectCopy(cmdObj->srcObj, dstObj);
if (image1 != 0 && image2 == 0 && objData->room == _logic->currentRoom()) {
uint16 bobNum = _logic->findBob(dstObj);
if (bobNum != 0) {
_graphics->bobClear(bobNum);
}
}
}
2003-11-03 19:52:14 +00:00
if (dstObj != _curCmd.subject1) {
// if the new object we have updated is on screen and
// is not current object then update it
_logic->roomRefreshObject(dstObj);
}
}
else {
// hide the object
if (objData->name > 0) {
objData->name = -objData->name;
// may need to turn BOBs off for objects to be hidden on current
// screen ! if the new object we have updated is on screen and
// is not current object then update it
_logic->roomRefreshObject(dstObj);
}
}
}
}
}
void Command::setItems(uint16 command) {
debug(9, "Command::setItems(%d)", command);
CmdInventory *cmdInv = &_cmdInventory[1];
ItemData *items = _logic->itemData(0);
uint16 i;
for (i = 1; i <= _numCmdInventory; ++i, ++cmdInv) {
if (cmdInv->id == command) {
uint16 dstItem = ABS(cmdInv->dstItem);
// found an item
if (cmdInv->dstItem > 0) {
// add item to inventory
if (cmdInv->srcItem > 0) {
// copy data from source item to item
items[dstItem] = items[cmdInv->srcItem];
// enable it
items[dstItem].name = ABS(items[dstItem].name);
}
_logic->inventoryInsertItem(cmdInv->dstItem);
}
else {
// delete item
if (items[dstItem].name > 0) {
_logic->inventoryDeleteItem(dstItem);
}
if (cmdInv->srcItem > 0) {
// copy data from source item to item
items[dstItem] = items[cmdInv->srcItem];
// disable it
items[dstItem].name = -ABS(items[dstItem].name);
}
}
}
}
}
uint16 Command::nextObjectDescription(ObjectDescription* objDesc, uint16 firstDesc) {
// l.69-103 select.c
uint16 i;
uint16 diff = objDesc->lastDescription - firstDesc;
debug(0, "Command::updateNextDescription() - diff = %d, type = %d", diff, objDesc->type);
switch (objDesc->type) {
case 0:
// random type, start with first description
if (objDesc->lastSeenNumber == 0) {
// first time look at called
objDesc->lastSeenNumber = firstDesc;
}
else {
// already displayed first, do a random
i = objDesc->lastSeenNumber;
while (i == objDesc->lastSeenNumber) {
i = firstDesc + Logic::randomizer.getRandomNumber(diff);
}
objDesc->lastSeenNumber = i;
}
break;
case 1:
i = objDesc->lastSeenNumber;
while (i == objDesc->lastSeenNumber) {
i = firstDesc + Logic::randomizer.getRandomNumber(diff);
}
objDesc->lastSeenNumber = i;
break;
case 2:
// sequential, but loop
++objDesc->lastSeenNumber;
if (objDesc->lastSeenNumber > objDesc->lastDescription) {
objDesc->lastSeenNumber = firstDesc;
}
break;
case 3:
// sequential without looping
if (objDesc->lastSeenNumber != objDesc->lastDescription) {
++objDesc->lastSeenNumber;
}
break;
}
return objDesc->lastSeenNumber;
}
void Command::look() {
if (_selCmd.noun > 0 && _selCmd.noun <= _logic->currentRoomObjMax()) {
2003-11-03 19:52:14 +00:00
uint16 objNum = _logic->currentRoomData() + _selCmd.noun;
if (_logic->objectData(objNum)->entryObj == 0) {
if (makeJoeWalkTo(_selPosX, _selPosY, objNum, _selCmd.action, false) == -2) { // XXX inCutaway parameter
// 'I can't get close enough to have a look.'
_logic->joeSpeak(13);
}
}
}
// if object type and disabled, don't look
2003-11-03 19:52:14 +00:00
if (_curCmd.subject1 > 0 && _logic->objectData(_curCmd.subject1)->name <= 0) {
return;
}
uint16 desc;
2003-11-03 19:52:14 +00:00
if (_curCmd.subject1 < 0) {
desc = _logic->itemData(-_curCmd.subject1)->description;
}
else {
2003-11-03 19:52:14 +00:00
desc = _logic->objectData(_curCmd.subject1)->description;
}
2003-11-03 19:52:14 +00:00
debug(0, "Command::look() - desc = %X, _curCmd.subject1 = %X", desc, _curCmd.subject1);
// check to see if the object/item has a series of description
ObjectDescription *objDesc = _logic->objectDescription(1);
uint16 i;
for (i = 1; i <= _logic->objectDescriptionCount(); ++i, ++objDesc) {
2003-11-03 19:52:14 +00:00
if (objDesc->object == _curCmd.subject1) {
desc = nextObjectDescription(objDesc, desc);
break;
}
}
_logic->joeSpeak(desc, true);
_logic->joeFace();
}
void Command::lookCurrentItem() {
if (_curCmd.verb.isInventory()) {
uint16 item = _logic->findInventoryItem(_curCmd.verb.inventoryItem());
if (item != 0) {
ItemData *itemData = _logic->itemData(item);
const char *name = _logic->objectName(itemData->name);
if (_curCmd.action.isNone()) {
Verb v = State::findDefaultVerb(itemData->state);
_cmdText.setVerb(v.isNone() ? Verb(VERB_LOOK_AT) : v);
}
if (!_selCmd.defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _selCmd.defaultVerb, name);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, name);
}
_curCmd.oldVerb = _curCmd.verb;
}
}
}
void Command::lookCurrentRoom() {
_curCmd.noun = _logic->findObjectUnderCursor(_input->mousePosX(), _input->mousePosY());
if (_logic->joeWalk() == JWM_MOVE) {
return;
}
int16 aObjName = 0;
uint16 k = _logic->currentRoomData();
int16 i = 0;
if (_curCmd.noun > _logic->currentRoomObjMax()) {
uint16 obj = _logic->currentRoomArea(_curCmd.noun - _logic->currentRoomObjMax())->object;
if (obj) {
aObjName = _logic->objectData(obj)->name;
if (aObjName > 0) {
i = aObjName;
_curCmd.noun = obj - k;
}
}
}
else {
i = _logic->objectData(k + _curCmd.noun)->name;
}
if (_curCmd.oldNoun == _curCmd.noun) {
return;
}
// if pointing at an Area then exit
// if the AREA is linked to an object, then dont exit. Find
// the object its linked to && store in AOBJ
if (_curCmd.noun > _logic->currentRoomObjMax() && aObjName <= 0) {
if (_curCmd.oldNoun != 0) {
if (!_selCmd.defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _selCmd.defaultVerb);
}
else if (!_curCmd.action.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
_curCmd.oldNoun = 0;
return;
}
}
if (i <= 0) {
_curCmd.oldNoun = _curCmd.noun;
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
if (!_selCmd.defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _selCmd.defaultVerb);
}
else if (!_curCmd.action.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
return;
}
// if no command yet selected, then use DEFAULT command, if any
if (_curCmd.action.isNone()) {
Verb v = State::findDefaultVerb(_logic->objectData(k + _curCmd.noun)->state);
_cmdText.setVerb(v.isNone() ? Verb(VERB_WALK_TO) : v);
if (_curCmd.noun == 0) {
_cmdText.clear();
}
}
const char *objName = "";
if (_curCmd.noun > 0) {
objName = _logic->objectName(i);
}
if (!_selCmd.defaultVerb.isNone()) {
_cmdText.displayTemp(INK_CMD_LOCK, true, _selCmd.defaultVerb, objName);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, objName);
}
_curCmd.oldNoun = _curCmd.noun;
}
void Command::lookCurrentIcon() {
_curCmd.verb = _logic->findVerbUnderCursor(_input->mousePosX(), _input->mousePosY());
if (_curCmd.verb != _curCmd.oldVerb && _logic->joeWalk() != JWM_MOVE) {
if (_curCmd.action.isNone()) {
_cmdText.clear();
}
_graphics->textClear(CmdText::COMMAND_Y_POS, CmdText::COMMAND_Y_POS);
lookCurrentItem();
// ensure that registers when move to top screen
if (_curCmd.noun > 0) {
_curCmd.oldNoun = -1;
}
_curCmd.oldVerb = _curCmd.verb;
if (_curCmd.verb.isPanelCommand() || _curCmd.verb.value() == VERB_WALK_TO) {
if (_curCmd.verb.isNone()) {
_cmdText.display(INK_CMD_NORMAL);
}
else {
_cmdText.displayTemp(INK_CMD_NORMAL, false, _curCmd.verb);
}
}
}
}
} // End of namespace Queen