diff options
Diffstat (limited to 'engines/mads/action.cpp')
-rw-r--r-- | engines/mads/action.cpp | 708 |
1 files changed, 708 insertions, 0 deletions
diff --git a/engines/mads/action.cpp b/engines/mads/action.cpp new file mode 100644 index 0000000000..80480cbdfd --- /dev/null +++ b/engines/mads/action.cpp @@ -0,0 +1,708 @@ +/* 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 "common/scummsys.h" +#include "mads/mads.h" +#include "mads/action.h" +#include "mads/inventory.h" +#include "mads/resources.h" +#include "mads/scene.h" +#include "mads/staticres.h" + +namespace MADS { + +void ActionDetails::synchronize(Common::Serializer &s) { + s.syncAsUint16LE(_verbId); + s.syncAsUint16LE(_objectNameId); + s.syncAsUint16LE(_indirectObjectId); +} + +void ActionSavedFields::synchronize(Common::Serializer &s) { + s.syncAsByte(_commandError); + s.syncAsSint16LE(_commandSource); + s.syncAsSint16LE(_command); + s.syncAsSint16LE(_mainObject); + s.syncAsSint16LE(_secondObject); + s.syncAsSint16LE(_mainObjectSource); + s.syncAsSint16LE(_secondObjectSource); + s.syncAsSint16LE(_articleNumber); + s.syncAsSint16LE(_lookFlag); +} + +/*------------------------------------------------------------------------*/ + +MADSAction::MADSAction(MADSEngine *vm) : _vm(vm) { + clear(); + _statusTextIndex = -1; + _selectedAction = 0; + _inProgress = false; + _pickedWord = -1; + + _savedFields._commandSource = 0; + _savedFields._mainObjectSource = 0; + _savedFields._command = -1; + _savedFields._mainObject = 0; + _savedFields._secondObject = 0; + _savedFields._secondObjectSource = 0; + _savedFields._articleNumber = PREP_NONE; + _savedFields._lookFlag = false; + + _activeAction._verbId = VERB_NONE; + _activeAction._objectNameId = -1; + _activeAction._indirectObjectId = -1; + _savedFields._commandError = false; + _verbType = VERB_INIT; + _prepType = PREP_NONE; +} + +void MADSAction::clear() { + _interAwaiting = AWAITING_COMMAND; + _commandSource = CAT_NONE; + _mainObjectSource = CAT_NONE; + _secondObjectSource = CAT_NONE; + _recentCommandSource = CAT_NONE; + _articleNumber = 0; + _lookFlag = false; + _pointEstablished = 0; + _statusText.clear(); + _selectedRow = -1; + _hotspotId = -1; + _secondObject = -1; + _recentCommand = -1; + _action._verbId = VERB_NONE; + _action._objectNameId = -1; + _action._indirectObjectId = -1; + _textChanged = true; +} + +void MADSAction::appendVocab(int vocabId, bool capitalize) { + Common::String vocabStr = _vm->_game->_scene.getVocab(vocabId); + if (capitalize) + vocabStr.setChar(toupper(vocabStr[0]), 0); + + _statusText += vocabStr; + _statusText += " "; +} + +void MADSAction::startWalkingDirectly(int walkType) { + Scene &scene = _vm->_game->_scene; + Player &player = _vm->_game->_player; + + if (_pointEstablished && (walkType == -3 || _savedFields._command < 0)) { + player._needToWalk = true; + player._prepareWalkPos = scene._customDest; + } +} + +void MADSAction::set() { + Scene &scene = _vm->_game->_scene; + UserInterface &userInterface = scene._userInterface; + _statusText = ""; + + _action._verbId = VERB_NONE; + _action._objectNameId = -1; + _action._indirectObjectId = -1; + + if (_commandSource == CAT_TALK_ENTRY) { + // Handle showing the conversation selection. Rex at least doesn't actually seem to use this + if (_selectedRow >= 0) { + _action._verbId = userInterface._talkIds[_selectedRow]; + Common::String desc = userInterface._talkStrings[_selectedRow]; + if (!desc.empty()) + _statusText = desc; + } + } else if (_lookFlag && (_selectedRow == 0)) { + // Two 'look' actions in succession, so the action becomes 'Look around' + _statusText = kLookAroundStr; + } else { + bool flag = false; + if ((_commandSource == CAT_INV_VOCAB) && (_selectedRow >= 0) + && (_verbType == VERB_THAT) && (_prepType == PREP_NONE)) { + // Use/to action + int invIndex = userInterface._selectedInvIndex; + InventoryObject &objEntry = _vm->_game->_objects.getItem(invIndex); + + _action._objectNameId = objEntry._descId; + _action._verbId = objEntry._vocabList[_selectedRow]._vocabId; + + // Set up the status text string + _statusText = kUseStr; + appendVocab(_action._objectNameId); + _statusText += kToStr; + appendVocab(_action._verbId); + } else { + // Handling for if an action has been selected + if (_selectedRow >= 0) { + if (_commandSource == CAT_COMMAND) { + // Standard verb action + _action._verbId = scene._verbList[_selectedRow]._id; + } else { + // Selected action on an inventory object + int invIndex = userInterface._selectedInvIndex; + InventoryObject &objEntry = _vm->_game->_objects.getItem(invIndex); + + _action._verbId = objEntry._vocabList[_selectedRow]._vocabId; + } + + appendVocab(_action._verbId, true); + + if (_action._verbId == VERB_LOOK) { + // Add in the word 'add' + _statusText += kArticleList[PREP_AT]; + _statusText += " "; + } + } + + // Add in any necessary article if necessary + if ((_hotspotId >= 0) && (_selectedRow >= 0) && (_articleNumber > 0) && (_verbType == VERB_THAT)) { + flag = true; + + _statusText += kArticleList[_articleNumber]; + _statusText += " "; + } + + // Handling for hotspot + if (_hotspotId >= 0) { + if (_selectedRow < 0) { + int verbId; + + if (_hotspotId < (int)scene._hotspots.size()) { + // Get the verb Id from the hotspot + verbId = scene._hotspots[_hotspotId]._verbId; + } else { + // Get the verb Id from the scene object + verbId = scene._dynamicHotspots[_hotspotId - scene._hotspots.size()]._verbId; + } + + if (verbId > 0) { + // Set the specified action + _action._verbId = verbId; + appendVocab(_action._verbId, true); + } else { + // Default to a standard 'walk to' + _action._verbId = VERB_WALKTO; + _statusText += kWalkToStr; + } + } + + if ((_mainObjectSource == CAT_INV_LIST) || (_mainObjectSource == CAT_INV_ANIM)) { + // Get name from given inventory object + InventoryObject &invObject = _vm->_game->_objects.getItem(_hotspotId); + _action._objectNameId = invObject._descId; + } else if (_hotspotId < (int)scene._hotspots.size()) { + // Get name from scene hotspot + _action._objectNameId = scene._hotspots[_hotspotId]._vocabId; + } else { + // Get name from temporary scene hotspot + _action._objectNameId = scene._dynamicHotspots[_hotspotId - scene._hotspots.size()]._descId; + } + appendVocab(_action._objectNameId); + } + } + + if (_secondObject >= 0) { + if (_secondObjectSource == CAT_INV_LIST || _secondObjectSource == CAT_INV_ANIM) { + InventoryObject &invObject = _vm->_game->_objects.getItem(_secondObject); + _action._indirectObjectId = invObject._descId; + } else if (_secondObject < (int)scene._hotspots.size()) { + _action._indirectObjectId = scene._hotspots[_secondObject]._vocabId; + } else { + _action._indirectObjectId = scene._dynamicHotspots[_secondObject - scene._hotspots.size()]._descId; + } + } + + if ((_hotspotId >= 0) && (_articleNumber > 0) && !flag) { + if (_articleNumber == PREP_RELATIONAL) { + if (_secondObject >= 0) { + int articleNum = 0; + + if ((_secondObjectSource == 2) || (_secondObjectSource == 5)) { + InventoryObject &invObject = _vm->_game->_objects.getItem(_hotspotId); + articleNum = invObject._article; + } else if (_hotspotId < (int)scene._hotspots.size()) { + articleNum = scene._hotspots[_hotspotId]._articleNumber; + } else { + articleNum = scene._dynamicHotspots[_hotspotId - scene._hotspots.size()]._articleNumber; + } + + _statusText += kArticleList[articleNum]; + } + } else if ((_articleNumber == VERB_LOOK) || (_vm->getGameID() != GType_RexNebular) || + (_action._indirectObjectId >= 0 && scene._vocabStrings[_action._indirectObjectId] != kFenceStr)) { + // Write out the article + _statusText += kArticleList[_articleNumber]; + } else { + // Special case for a 'fence' entry in Rex Nebular + _statusText += kOverStr; + } + + _statusText += " "; + } + + // Append object description if necessary + if (_secondObject >= 0) + appendVocab(_action._indirectObjectId); + + // Remove any trailing space character + if (_statusText.hasSuffix(" ")) + _statusText.deleteLastChar(); + } + + _textChanged = true; +} + +void MADSAction::refresh() { + Scene &scene = _vm->_game->_scene; + + // Exit immediately if nothing has changed + if (!_textChanged) + return; + + // Remove any old copy of the status text + if (_statusTextIndex >= 0) { + scene._textDisplay.expire(_statusTextIndex); + _statusTextIndex = -1; + } + + if (!_statusText.empty()) { + if ((_vm->_game->_screenObjects._inputMode == kInputBuildingSentences) || + (_vm->_game->_screenObjects._inputMode == kInputLimitedSentences)) { + Font *font = _vm->_font->getFont(FONT_MAIN); + int textSpacing = -1; + + int strWidth = font->getWidth(_statusText); + if (strWidth > MADS_SCREEN_WIDTH) { + // Too large to fit, so fall back on interface font + font = _vm->_font->getFont(FONT_INTERFACE); + strWidth = font->getWidth(_statusText, 0); + textSpacing = 0; + } + + // Add a new text display entry to display the status text at the bottom of the screen area + _statusTextIndex = scene._textDisplay.add(160 - (strWidth / 2), + MADS_SCENE_HEIGHT + scene._posAdjust.y - 13, 3, textSpacing, _statusText, font); + } + } + + _textChanged = false; +} + +void MADSAction::startAction() { + Game &game = *_vm->_game; + Player &player = game._player; + Scene &scene = _vm->_game->_scene; + DynamicHotspots &dynHotspots = scene._dynamicHotspots; + Hotspots &hotspots = scene._hotspots; + + player.cancelCommand(); + + _inProgress = true; + _savedFields._commandError = false; + _savedFields._command = _selectedRow; + _savedFields._mainObject = _hotspotId; + _savedFields._secondObject = _secondObject; + _savedFields._articleNumber = _articleNumber; + _savedFields._commandSource = _commandSource; + _savedFields._mainObjectSource = _mainObjectSource; + _savedFields._secondObjectSource = _secondObjectSource; + _savedFields._lookFlag = _lookFlag; + _activeAction = _action; + + // Copy the action to be active + _activeAction = _action; + _sentence = _statusText; + + if ((_mainObjectSource == CAT_HOTSPOT) && (_secondObjectSource == 4)) + _savedFields._commandError = true; + + player._needToWalk = false; + int hotspotId = -1; + + if (!_savedFields._lookFlag && (_vm->_game->_screenObjects._inputMode != kInputConversation)) { + if (_savedFields._mainObjectSource == CAT_HOTSPOT) + hotspotId = _savedFields._mainObject; + else if (_secondObjectSource == 4) + hotspotId = _savedFields._secondObject; + + if (hotspotId >= (int)hotspots.size()) { + DynamicHotspot &hs = dynHotspots[hotspotId - hotspots.size()]; + if ((hs._feetPos.x == -1) || (hs._feetPos.x == -3)) { + startWalkingDirectly(hs._feetPos.x); + } else if (hs._feetPos.x == 0) { + player._prepareWalkFacing = hs._facing; + } else if (_savedFields._commandSource == CAT_NONE || hs._cursor < CURSOR_WAIT) { + player._needToWalk = true; + player._prepareWalkPos = hs._feetPos; + } + + player._prepareWalkFacing = hs._facing; + hotspotId = -1; + } + } + + if (hotspotId >= 0) { + Hotspot &hs = hotspots[hotspotId]; + + if (hs._feetPos.x == -1 || hs._feetPos.x == -3) { + startWalkingDirectly(hs._feetPos.x); + } else if (hs._feetPos.x >= 0) { + if (_savedFields._commandSource == CAT_NONE || hs._cursor < CURSOR_WAIT) { + player._needToWalk = true; + player._prepareWalkPos = hs._feetPos; + } + } + + player._prepareWalkFacing = hs._facing; + } + + player._readyToWalk = player._needToWalk; +} + +void MADSAction::checkAction() { + if (isAction(VERB_LOOK) || isAction(VERB_THROW)) + _vm->_game->_player._needToWalk = false; +} + +bool MADSAction::isAction(int verbId, int objectNameId, int indirectObjectId) { + if (_activeAction._verbId != verbId) + return false; + if ((objectNameId != 0) && (_activeAction._objectNameId != objectNameId)) + return false; + if ((indirectObjectId != 0) && (_activeAction._indirectObjectId != indirectObjectId)) + return false; + + return true; +} + +bool MADSAction::isObject(int objectNameId) { + return _activeAction._objectNameId == objectNameId; +} + +bool MADSAction::isTarget(int objectNameId) { + return _activeAction._indirectObjectId == objectNameId; +} + +void MADSAction::checkActionAtMousePos() { + Scene &scene = _vm->_game->_scene; + UserInterface &userInterface = scene._userInterface; + + if ((userInterface._category == CAT_COMMAND || userInterface._category == CAT_INV_VOCAB) && + _interAwaiting != AWAITING_COMMAND && _pickedWord >= 0) { + if (_recentCommandSource == userInterface._category || _recentCommand != _pickedWord || + (_interAwaiting != AWAITING_THIS && _interAwaiting != 3)) + clear(); + else if (_selectedRow != 0 || userInterface._category != CAT_COMMAND) + scene._lookFlag = false; + else + scene._lookFlag = true; + } + + if (_vm->_events->_rightMousePressed && _vm->_events->_mouseButtons) { + switch (userInterface._category) { + case CAT_COMMAND: + case CAT_INV_VOCAB: + return; + + case CAT_INV_LIST: + case CAT_HOTSPOT: + case CAT_INV_ANIM: + if (_interAwaiting != AWAITING_THAT) { + if (userInterface._selectedActionIndex >= 0) { + _commandSource = CAT_COMMAND; + _selectedRow = userInterface._selectedActionIndex; + _verbType = scene._verbList[_selectedRow]._verbType; + _prepType = scene._verbList[_selectedRow]._prepType; + _interAwaiting = AWAITING_THIS; + } else if (userInterface._selectedItemVocabIdx >= 0) { + _commandSource = CAT_INV_VOCAB; + _selectedRow = userInterface._selectedItemVocabIdx; + int objectId = _vm->_game->_objects._inventoryList[_selectedRow]; + InventoryObject &invObject = _vm->_game->_objects[objectId]; + + _verbType = invObject._vocabList[_selectedRow - 1]._verbType; + _prepType = invObject._vocabList[_selectedRow - 1]._prepType; + _mainObjectSource = CAT_INV_LIST; + _hotspotId = userInterface._selectedInvIndex; + _articleNumber = _prepType; + + if ((_verbType == VERB_THIS && _prepType == PREP_NONE) || + (_verbType == VERB_THAT && _prepType != PREP_NONE)) + _interAwaiting = AWAITING_RIGHT_MOUSE; + else + _interAwaiting = AWAITING_THAT; + } + } + break; + + default: + break; + } + } + + switch (_interAwaiting) { + case AWAITING_COMMAND: + _articleNumber = 0; + switch (userInterface._category) { + case CAT_COMMAND: + _commandSource = CAT_COMMAND; + _selectedRow = _pickedWord; + if (_selectedRow >= 0) { + _verbType = scene._verbList[_selectedRow]._verbType; + _prepType = scene._verbList[_selectedRow]._prepType; + } + break; + + case CAT_INV_VOCAB: + _commandSource = CAT_INV_VOCAB; + _selectedRow = _pickedWord; + if (_selectedRow < 0) { + _hotspotId = -1; + _mainObjectSource = CAT_NONE; + } else { + InventoryObject &invObject = _vm->_game->_objects.getItem(userInterface._selectedInvIndex); + _verbType = invObject._vocabList[_selectedRow]._verbType; + _prepType = invObject._vocabList[_selectedRow]._prepType; + _hotspotId = userInterface._selectedInvIndex; + _mainObjectSource = CAT_INV_LIST; + + if (_verbType == VERB_THAT) + _articleNumber = _prepType; + } + break; + + case CAT_HOTSPOT: + _selectedRow = -1; + _commandSource = CAT_NONE; + _mainObjectSource = CAT_HOTSPOT; + _hotspotId = _pickedWord; + break; + + case CAT_TALK_ENTRY: + _commandSource = CAT_TALK_ENTRY; + _selectedRow = _pickedWord; + break; + + default: + break; + } + break; + + case AWAITING_THIS: + _articleNumber = 0; + switch (userInterface._category) { + case CAT_INV_LIST: + case CAT_HOTSPOT: + case CAT_INV_ANIM: + _mainObjectSource = userInterface._category; + _hotspotId = _pickedWord; + break; + default: + break; + } + break; + + case AWAITING_THAT: + switch (userInterface._category) { + case CAT_INV_LIST: + case CAT_HOTSPOT: + case CAT_INV_ANIM: + _secondObjectSource = userInterface._category; + _secondObject = _pickedWord; + break; + default: + break; + } + break; + + default: + break; + } +} + +void MADSAction::leftClick() { + Scene &scene = _vm->_game->_scene; + UserInterface &userInterface = scene._userInterface; + bool abortFlag = false; + + if ((userInterface._category == CAT_COMMAND || userInterface._category == CAT_INV_VOCAB) && + _interAwaiting != 1 && _pickedWord >= 0 && + _recentCommandSource == userInterface._category && _recentCommand == _pickedWord && + (_interAwaiting == 2 || userInterface._category == CAT_INV_VOCAB)) { + abortFlag = true; + if (_selectedRow == 0 && userInterface._category == CAT_COMMAND) { + _selectedAction = CAT_COMMAND; + scene._lookFlag = true; + } else { + _selectedAction = CAT_NONE; + scene._lookFlag = false; + clear(); + } + } + + if (abortFlag || (_vm->_events->_rightMousePressed && (userInterface._category == CAT_COMMAND || + userInterface._category == CAT_INV_VOCAB))) + return; + + switch (_interAwaiting) { + case AWAITING_COMMAND: + switch (userInterface._category) { + case CAT_COMMAND: + if (_selectedRow >= 0) { + if (_verbType == VERB_ONLY) { + _selectedAction = -1; + } + else { + _recentCommand = _selectedRow; + _recentCommandSource = _commandSource; + _interAwaiting = AWAITING_THIS; + } + } + break; + + case CAT_INV_LIST: + if (_pickedWord >= 0) { + userInterface.selectObject(_pickedWord); + } + break; + + case CAT_INV_VOCAB: + if (_selectedRow >= 0) { + if (_verbType != VERB_THIS || _prepType != PREP_NONE) { + if (_verbType != VERB_THAT || _prepType == PREP_NONE) { + _interAwaiting = AWAITING_THAT; + _articleNumber = _prepType; + } else { + _articleNumber = _prepType; + _selectedAction = -1; + } + } else { + _selectedAction = -1; + } + + _recentCommand = _selectedRow; + _recentCommandSource = _commandSource; + } + break; + + case CAT_HOTSPOT: + _recentCommand = -1; + _recentCommandSource = CAT_NONE; + + if (_vm->_events->currentPos().y < MADS_SCENE_HEIGHT) { + scene._customDest = _vm->_events->currentPos() + scene._posAdjust; + _selectedAction = -1; + _pointEstablished = true; + } + break; + + case CAT_TALK_ENTRY: + if (_selectedRow >= 0) + _selectedAction = -1; + break; + + default: + break; + } + break; + + case AWAITING_THIS: + switch (userInterface._category) { + case CAT_INV_LIST: + case CAT_HOTSPOT: + case CAT_INV_ANIM: + if (_hotspotId >= 0) { + if (_prepType) { + _articleNumber = _prepType; + _interAwaiting = AWAITING_THAT; + } else { + _selectedAction = -1; + } + + if (userInterface._category == CAT_HOTSPOT) { + scene._customDest = _vm->_events->mousePos() + scene._posAdjust; + _pointEstablished = true; + } + } + break; + default: + break; + } + break; + + case AWAITING_THAT: + switch (userInterface._category) { + case CAT_INV_LIST: + case CAT_HOTSPOT: + case CAT_INV_ANIM: + if (_secondObject >= 0) { + _selectedAction = -1; + + if (userInterface._category == CAT_HOTSPOT) { + if (!_pointEstablished) { + scene._customDest = _vm->_events->mousePos() + scene._posAdjust; + _pointEstablished = true; + } + } + } + break; + default: + break; + } + break; + + default: + break; + } +} + +void MADSAction::synchronize(Common::Serializer &s) { + _action.synchronize(s); + _activeAction.synchronize(s); + s.syncAsSint16LE(_articleNumber); + s.syncAsByte(_lookFlag); + s.syncAsByte(_textChanged); + s.syncAsSint16LE(_selectedRow); + s.syncAsSint16LE(_selectedAction); + s.syncAsSint16LE(_statusTextIndex); + s.syncAsSint16LE(_hotspotId); + _savedFields.synchronize(s); + + // TODO: When saving in Rex Village Hut, _senetence size() doesn't match + // string length. Find out why not + _sentence = Common::String(_sentence.c_str()); + s.syncString(_sentence); + + s.syncAsSint16LE(_verbType); + s.syncAsSint16LE(_prepType); + s.syncAsSint16LE(_commandSource); + s.syncAsSint16LE(_mainObjectSource); + s.syncAsSint16LE(_secondObject); + s.syncAsSint16LE(_secondObjectSource); + s.syncAsSint16LE(_recentCommandSource); + s.syncAsSint16LE(_recentCommand); + s.syncAsSint16LE(_interAwaiting); + s.syncAsSint16LE(_pickedWord); + s.syncAsByte(_pointEstablished); + s.syncAsByte(_inProgress); +} + +} // End of namespace MADS |