/* 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 . * */ #ifndef DGDS_SCENE_H #define DGDS_SCENE_H #include "common/array.h" #include "common/serializer.h" #include "dgds/dialog.h" #include "dgds/head.h" #include "dgds/dgds_rect.h" #include "dgds/minigames/shell_game.h" #include "dgds/scene_condition.h" #include "dgds/scene_op.h" namespace Dgds { class ResourceManager; class Decompressor; class DgdsFont; class SoundRaw; class TTMInterpreter; class TTMEnviro; class HotArea { public: DgdsRect _rect; uint16 _num; // uint16 _cursorNum; // Used in Willy Beamish uint16 _cursorNum2; uint16 _objInteractionRectNum; Common::Array enableConditions; Common::Array onLookOps; Common::Array onPickUpOps; Common::Array onUseOps; virtual ~HotArea() {} virtual Common::String dump(const Common::String &indent) const; }; class DynamicRect { public: DynamicRect() : _num(0) {}; uint16 _num; DgdsRect _rect; }; enum GameItemState { kItemStateDragging = 1, kItemStateWasInInv = 0x10000, }; class GameItem : public HotArea { public: Common::Array onDragFinishedOps; Common::Array onBothButtonsOps; uint16 _altCursor; uint16 _iconNum; // mutable values uint16 _inSceneNum; uint32 _flags; uint16 _quality; Common::String dump(const Common::String &indent) const override; }; class MouseCursor { public: MouseCursor(uint16 hotX, uint16 hotY) : _hot(hotX, hotY) {} Common::String dump(const Common::String &indent) const; const Common::Point getHot() const { return _hot; } private: Common::Point _hot; }; // Interactions between two objects when one is dropped on the other class ObjectInteraction { public: ObjectInteraction(uint16 dropped, uint16 target) : _droppedItemNum(dropped), _targetItemNum(target) {} Common::Array opList; bool matches(uint16 droppedItemNum, uint16 targetItemNum) const { return (_droppedItemNum == 0xFFFF || _droppedItemNum == droppedItemNum) && _targetItemNum == targetItemNum; } Common::String dump(const Common::String &indent) const; private: uint16 _droppedItemNum; uint16 _targetItemNum; }; class SceneTrigger { public: SceneTrigger(uint16 num) : _num(num), _enabled(false), _timesToCheckBeforeRunning(0), _checksUntilRun(0) {} Common::String dump(const Common::String &indent) const; Common::Array conditionList; Common::Array sceneOpList; uint16 _timesToCheckBeforeRunning; // Only used in Beamish. uint16 _checksUntilRun; bool _enabled; uint16 getNum() const { return _num; } private: uint16 _num; }; /* A global value that only applies on a per-SDS-scene, but stays with the GDS data as it sticks around during the game */ class PerSceneGlobal { public: PerSceneGlobal(uint16 num, uint16 scene) : _num(num), _sceneNo(scene), _val(0) {} Common::String dump(const Common::String &indent) const; bool matches(uint16 num, uint16 scene) const { return num == _num && (_sceneNo == 0 || _sceneNo == scene); } bool numMatches(uint16 num) const { return num == _num; } int16 _val; private: // Immutable, read from the data file uint16 _num; uint16 _sceneNo; }; /** * A scene is described by an SDS file, which points to the ADS script to load * and holds the dialog info. */ class Scene { public: Scene(); virtual ~Scene() {}; virtual bool parse(Common::SeekableReadStream *s) = 0; bool isVersionOver(const char *version) const; bool isVersionUnder(const char *version) const; uint32 getMagic() const { return _magic; } const Common::String &getVersion() const { return _version; } bool runPreTickOps() { return runOps(_preTickOps); } bool runPostTickOps() { return runOps(_postTickOps); } static bool runOps(const Common::Array ops, int16 addMinutes = 0); virtual Common::Error syncState(Common::Serializer &s) = 0; // These are all static as they are potentially run over scene changes. static void segmentStateOps(const Common::Array &args); static void setItemAttrOp(const Common::Array &args); static void setDragItemOp(const Common::Array &args); protected: bool readConditionList(Common::SeekableReadStream *s, Common::Array &list) const; bool readHotArea(Common::SeekableReadStream *s, HotArea &dst) const; bool readHotAreaList(Common::SeekableReadStream *s, Common::List &list) const; bool readGameItemList(Common::SeekableReadStream *s, Common::Array &list) const; bool readMouseHotspotList(Common::SeekableReadStream *s, Common::Array &list) const; bool readObjInteractionList(Common::SeekableReadStream *s, Common::Array &list) const; bool readOpList(Common::SeekableReadStream *s, Common::Array &list) const; bool readDialogList(Common::SeekableReadStream *s, Common::List &list, int16 filenum = 0) const; bool readTriggerList(Common::SeekableReadStream *s, Common::Array &list) const; bool readDialogActionList(Common::SeekableReadStream *s, Common::Array &list) const; bool readConditionalSceneOpList(Common::SeekableReadStream *s, Common::Array &list) const; uint32 _magic; Common::String _version; Common::Array _preTickOps; Common::Array _postTickOps; Common::Array _conditionalOps; // Beamish only }; class GDSScene : public Scene { public: GDSScene(); bool load(const Common::String &filename, ResourceManager *resourceManager, Decompressor *decompressor); bool loadRestart(const Common::String &filename, ResourceManager *resourceManager, Decompressor *decompressor); void loadGameStateFromFile(Common::SeekableReadStream *file, const Common::String &filename); bool parse(Common::SeekableReadStream *s) override; bool parseInf(Common::SeekableReadStream *s); const Common::String &getIconFile() const { return _iconFile; } bool readPerSceneGlobals(Common::SeekableReadStream *s); Common::String dump(const Common::String &indent) const; void runStartGameOps() { runOps(_startGameOps); } void runQuitGameOps() { runOps(_quitGameOps); } void runChangeSceneOps() { runOps(_onChangeSceneOps); } void globalOps(const Common::Array &args); int16 getGlobal(uint16 num) const; int16 setGlobal(uint16 num, int16 val); const Common::Array &getCursorList() const { return _cursorList; } void drawItems(Graphics::ManagedSurface &surf); Common::Array &getGameItems() { return _gameItems; } int countItemsInInventory() const; const Common::Array &getObjInteractions1() const { return _objInteractions1; } const Common::Array &getObjInteractions2() const { return _objInteractions2; } Common::Error syncState(Common::Serializer &s) override; void initIconSizes(); GameItem *getActiveItem(); int16 getDefaultMouseCursor() const { return _defaultMouseCursor; } int16 getDefaultMouseCursor2() const { return _defaultMouseCursor2; } int16 getOtherDefaultMouseCursor() const { return _defaultOtherMouseCursor; } uint16 getInvIconNum() const { return _invIconNum; } int16 getInvIconMouseCursor() const { return _invIconMouseCursor; } private: Common::String _iconFile; Common::Array _gameItems; Common::Array _startGameOps; Common::Array _quitGameOps; Common::Array _onChangeSceneOps; Common::Array _cursorList; Common::Array _perSceneGlobals; Common::Array _objInteractions2; Common::Array _objInteractions1; // Additional fields that appear in Willy Beamish (unused in others) int16 _defaultMouseCursor; int16 _defaultMouseCursor2; uint16 _invIconNum; int16 _invIconMouseCursor; int16 _defaultOtherMouseCursor; }; class SDSScene : public Scene { public: SDSScene(); bool load(const Common::String &filename, ResourceManager *resourceManager, Decompressor *decompressor); bool parse(Common::SeekableReadStream *s) override; void unload(); const Common::String &getAdsFile() const { return _adsFile; } void runEnterSceneOps() { runOps(_enterSceneOps); } void runLeaveSceneOps() { runOps(_leaveSceneOps); } void checkTriggers(); int getNum() const { return _num; } Common::String dump(const Common::String &indent) const; bool checkDialogActive(); void drawActiveDialogBgs(Graphics::ManagedSurface *dst); bool drawAndUpdateDialogs(Graphics::ManagedSurface *dst); bool checkForClearedDialogs(); void mouseUpdate(const Common::Point &pt); void mouseLDown(const Common::Point &pt); void mouseLUp(const Common::Point &pt); void mouseRDown(const Common::Point &pt); void mouseRUp(const Common::Point &pt); void addInvButtonToHotAreaList(); void removeInvButtonFromHotAreaList(); const Common::List &getHotAreas() const { return _hotAreaList; } const GameItem *getDragItem() const { return _dragItem; } GameItem *getDragItem() { return _dragItem; } void setDragItem(GameItem *item) { _dragItem = item; } const Common::Array &getObjInteractions1() const { return _objInteractions1; } const Common::Array &getObjInteractions2() const { return _objInteractions2; } bool hasVisibleDialog(); bool hasVisibleOrOpeningDialog() const; Common::Error syncState(Common::Serializer &s) override; void onDragFinish(const Common::Point &pt); void enableTrigger(uint16 sceneNum, uint16 num, bool enable = true); void loadDialogData(uint16 num); void freeDialogData(uint16 num); bool loadTalkData(uint16 num); bool freeTalkData(uint16 num); void clearVisibleTalkers(); bool loadTalkDataAndSetFlags(uint16 talknum, uint16 headnum); void drawAndUpdateHeads(Graphics::ManagedSurface &dst); bool hasVisibleHead() const; // dragon-specific scene ops void addAndShowTiredDialog(); void prevChoice(); void nextChoice(); void activateChoice(); bool isTriggerEnabled(uint16 num); bool isLButtonDown() const { return _lbuttonDown; } bool isRButtonDown() const { return _rbuttonDown; } void showDialog(uint16 fileNum, uint16 dlgNum); const Common::Array &getConditionalOps() { return _conditionalOps; } void updateHotAreasFromDynamicRects(); void setDynamicSceneRect(int16 num, int16 x, int16 y, int16 width, int16 height); void setSceneNum(int16 num) { _num = num; } void drawDebugHotAreas(Graphics::ManagedSurface &dst) const; void setIgnoreMouseUp() { _ignoreMouseUp = true; } void setShouldClearDlg() { _shouldClearDlg = true; } protected: HotArea *findAreaUnderMouse(const Common::Point &pt); private: Dialog *getVisibleDialog(); bool readTalkData(Common::SeekableReadStream *s, TalkData &dst); void leftButtonAction(const HotArea *area); void bothButtonAction(const Common::Point &pt); void rightButtonAction(const Common::Point &pt); void doPickUp(HotArea *area); void doLook(const HotArea *area); int _num; Common::Array _enterSceneOps; Common::Array _leaveSceneOps; //uint _field5_0x12; uint _field6_0x14; Common::String _adsFile; //uint _field8_0x23; Common::List _hotAreaList; Common::Array _objInteractions1; Common::Array _objInteractions2; Common::Array _dynamicRects; // Only used in Willy Beamish //uint _field12_0x2b; //uint _field15_0x33; Common::Array _talkData; // From here on is mutable stuff that might need saving // Dialogs must be in List, not Array - they can be dynamically added // from another dialog, so pointers need to stay valid while adding more. Common::List _dialogs; Common::Array _triggers; Conversation _conversation; GameItem *_dragItem; bool _shouldClearDlg; bool _ignoreMouseUp; bool _lbuttonDown; bool _rbuttonDown; bool _lbuttonDownWithDrag; HotArea *_mouseDownArea; int _mouseDownCounter; /// Only changes in beamish - toggle between use (0), look (1) and target (2) int16 _lookMode; static bool _dlgWithFlagLo8IsClosing; static DialogFlags _sceneDialogFlags; }; } // End of namespace Dgds #endif // DGDS_SCENE_H