/* 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. * */ #ifndef TOUCHE_TOUCHE_H #define TOUCHE_TOUCHE_H #include "common/array.h" #include "common/endian.h" #include "common/file.h" #include "common/random.h" #include "common/rect.h" #include "common/util.h" #include "audio/mixer.h" #include "engines/engine.h" #include "touche/console.h" /** * This is the namespace of the Touche engine. * * Status of this engine: ??? * * Games using this engine: * - Touche: The Adventures of the Fifth Musketeer */ namespace Touche { struct Area { Common::Rect r; int16 srcX, srcY; Area() { srcX = srcY = 0; } Area(int16 x, int16 y, int16 w, int16 h) { r = Common::Rect(x, y, x + w, y + h); srcX = srcY = 0; } bool clip(const Common::Rect &rect) { const int dx = r.left - rect.left; if (dx < 0) { srcX -= dx; } const int dy = r.top - rect.top; if (dy < 0) { srcY -= dy; } if (rect.left > r.left) { r.left = rect.left; } if (rect.top > r.top) { r.top = rect.top; } if (rect.right < r.right) { r.right = rect.right; } if (rect.bottom < r.bottom) { r.bottom = rect.bottom; } return (r.right > r.left && r.bottom > r.top); } }; struct KeyChar { uint16 num; uint16 flags; int16 currentAnimCounter; int16 strNum; int16 walkDataNum; int16 spriteNum; Common::Rect prevBoundingRect; Common::Rect boundingRect; int16 xPos; int16 yPos; int16 zPos; int16 xPosPrev; int16 yPosPrev; int16 zPosPrev; int16 prevWalkDataNum; uint16 textColor; int16 inventoryItems[4]; int16 money; int16 pointsDataNum; int16 currentWalkBox; uint16 prevPointsDataNum; int16 currentAnim; int16 facingDirection; int16 currentAnimSpeed; int16 framesList[16]; int16 framesListCount; int16 currentFrame; int16 anim1Start; int16 anim1Count; int16 anim2Start; int16 anim2Count; int16 anim3Start; int16 anim3Count; int16 followingKeyCharNum; int16 followingKeyCharPos; uint16 sequenceDataIndex; uint16 sequenceDataOffset; int16 walkPointsListIndex; int16 walkPointsList[40]; uint16 scriptDataStartOffset; uint16 scriptDataOffset; int16 *scriptStackPtr; int16 delay; int16 waitingKeyChar; int16 waitingKeyCharPosTable[3]; int16 scriptStackTable[40]; }; struct Script { uint8 opcodeNum; uint32 dataOffset; int16 keyCharNum; uint8 *dataPtr; int16 *stackDataPtr; int16 *stackDataBasePtr; int16 quitFlag; int16 stackDataTable[500]; void init(uint8 *data) { dataPtr = data; stackDataPtr = stackDataBasePtr = &stackDataTable[499]; dataOffset = 0; quitFlag = 0; } uint8 readByte(uint32 offs) const { return *(dataPtr + offs); } int16 readWord(uint32 offs) const { return READ_LE_UINT16(dataPtr + offs); } uint8 readNextByte() { uint8 val = readByte(dataOffset); ++dataOffset; return val; } int16 readNextWord() { int16 val = readWord(dataOffset); dataOffset += 2; return val; } }; struct TalkEntry { int16 otherKeyChar; int16 talkingKeyChar; int16 num; }; struct ConversationChoice { int16 num; int16 msg; }; struct AnimationEntry { int16 num; int16 x; int16 y; int16 dx; int16 dy; int16 posNum; int16 delayCounter; int16 displayCounter; Common::Rect displayRect; }; struct SequenceEntry { int16 sprNum; int16 seqNum; }; struct SpriteData { uint32 size; uint8 *ptr; uint16 bitmapWidth; uint16 bitmapHeight; uint16 w; uint16 h; }; struct InventoryState { int16 displayOffset; int16 lastItem; int16 itemsPerLine; int16 *itemsList; }; struct ProgramPointData { int16 x, y, z; int16 order; }; struct ProgramWalkData { int16 point1; int16 point2; int16 clippingRect; int16 area1; int16 area2; }; struct ProgramAreaData { Area area; int16 id; int16 state; int16 animCount; int16 animNext; }; struct ProgramBackgroundData { Area area; int16 type; int16 offset; int16 scaleMul; int16 scaleDiv; }; struct ProgramHitBoxData { int16 item; int16 talk; uint16 state; int16 str; int16 defaultStr; int16 actions[8]; Common::Rect hitBoxes[2]; }; struct ProgramActionScriptOffsetData { int16 object1; int16 action; int16 object2; uint16 offset; }; struct ProgramKeyCharScriptOffsetData { int16 keyChar; uint16 offset; }; struct ProgramConversationData { int16 num; uint16 offset; int16 msg; }; enum { kDebugEngine = 1 << 0, kDebugGraphics = 1 << 1, kDebugResource = 1 << 2, kDebugOpcodes = 1 << 3, kDebugMenu = 1 << 4, kDebugCharset = 1 << 5 }; enum ResourceType { kResourceTypeRoomImage = 0, kResourceTypeSequence, kResourceTypeSpriteImage, kResourceTypeIconImage, kResourceTypeRoomInfo, kResourceTypeProgram, kResourceTypeMusic, kResourceTypeSound }; enum TalkMode { kTalkModeTextOnly = 0, kTalkModeVoiceOnly, kTalkModeVoiceAndText, kTalkModeCount }; enum ScriptFlag { kScriptStopped = 1 << 0, kScriptPaused = 1 << 1 }; enum SaveLoadMode { kSaveGameState = 0, kLoadGameState }; enum InventoryArea { kInventoryCharacter = 0, kInventoryMoneyDisplay, kInventoryGoldCoins, kInventorySilverCoins, kInventoryMoney, kInventoryScroller1, kInventoryObject1, kInventoryObject2, kInventoryObject3, kInventoryObject4, kInventoryObject5, kInventoryObject6, kInventoryScroller2 }; enum { kScreenWidth = 640, kScreenHeight = 400, kRoomHeight = 352, kStartupEpisode = 90, // TODO: If the following truncation is intentional (it probably is) it should be clearly marked as such kCycleDelay = 1000 / (1193180 / 32768), kIconWidth = 58, kIconHeight = 42, kCursorWidth = 58, kCursorHeight = 42, kTextHeight = 16, kMaxProgramDataSize = 61440, kMaxSaveStates = 100 }; enum StringType { kStringTypeDefault, kStringTypeConversation }; enum GameState { kGameStateGameLoop, kGameStateOptionsDialog, kGameStateQuitDialog, kGameStateNone }; enum ActionId { kActionNone, // settings menu kActionLoadMenu, kActionSaveMenu, kActionRestartGame, kActionPlayGame, kActionQuitGame, kActionTextOnly, kActionVoiceOnly, kActionTextAndVoice, kActionLowerVolume, kActionUpperVolume, // saveLoad menu kActionGameState1, kActionGameState2, kActionGameState3, kActionGameState4, kActionGameState5, kActionGameState6, kActionGameState7, kActionGameState8, kActionGameState9, kActionGameState10, kActionScrollUpSaves, kActionScrollDownSaves, kActionPerformSaveLoad, kActionCancelSaveLoad }; enum MenuMode { kMenuSettingsMode = 0, kMenuLoadStateMode, kMenuSaveStateMode }; enum ButtonFlags { kButtonBorder = 1 << 0, kButtonText = 1 << 1, kButtonArrow = 1 << 2 }; struct Button { int x, y; int w, h; ActionId action; int data; uint8 flags; }; struct MenuData { MenuMode mode; Button *buttonsTable; uint buttonsCount; bool quit; bool exit; char saveLoadDescriptionsTable[kMaxSaveStates][33]; void removeLastCharFromDescription(int slot) { char *description = saveLoadDescriptionsTable[slot]; int descriptionLen = strlen(description); if (descriptionLen > 0) { --descriptionLen; description[descriptionLen] = 0; } } void addCharToDescription(int slot, char chr) { char *description = saveLoadDescriptionsTable[slot]; int descriptionLen = strlen(description); if (descriptionLen < 32 && Common::isPrint(chr)) { description[descriptionLen] = chr; description[descriptionLen + 1] = 0; } } const Button *findButtonUnderCursor(int cursorX, int cursorY) const { for (uint i = 0; i < buttonsCount; ++i) { const Button *button = &buttonsTable[i]; if (cursorX >= button->x && cursorX < button->x + button->w && cursorY >= button->y && cursorY < button->y + button->h) { return button; } } return 0; } }; void readGameStateDescription(Common::ReadStream *f, char *description, int len); Common::String generateGameStateFileName(const char *target, int slot, bool prefixOnly = false); int getGameStateFileSlot(const char *filename); class MidiPlayer; class ToucheEngine: public Engine { public: enum { NUM_FLAGS = 2000, NUM_KEYCHARS = 32, NUM_SPRITES = 7, NUM_SEQUENCES = 7, NUM_CONVERSATION_CHOICES = 40, NUM_TALK_ENTRIES = 16, NUM_ANIMATION_ENTRIES = 4, NUM_INVENTORY_ITEMS = 100, NUM_DIRTY_RECTS = 30, NUM_DIRECTIONS = 135 }; typedef void (ToucheEngine::*OpcodeProc)(); ToucheEngine(OSystem *system, Common::Language language); virtual ~ToucheEngine(); // Engine APIs virtual Common::Error run(); virtual bool hasFeature(EngineFeature f) const; virtual void syncSoundSettings(); GUI::Debugger *getDebugger() { return _console; } protected: void restart(); void readConfigurationSettings(); void writeConfigurationSettings(); void mainLoop(); void processEvents(bool handleKeyEvents = true); void runCycle(); int16 getRandomNumber(int max); void changePaletteRange(); void playSoundInRange(); void resetSortedKeyCharsTable(); void setupEpisode(int num); void setupNewEpisode(); void drawKeyChar(KeyChar *key); void sortKeyChars(); void runKeyCharScript(KeyChar *key); void runCurrentKeyCharScript(int mode); void executeScriptOpcode(int16 param); void initKeyChars(int keyChar); void setKeyCharTextColor(int keyChar, uint16 color); void waitForKeyCharPosition(int keyChar); void setKeyCharBox(int keyChar, int value); void setKeyCharFrame(int keyChar, int16 type, int16 value1, int16 value2); void setKeyCharFacingDirection(int keyChar, int16 dir); void initKeyCharScript(int keyChar, int16 spriteNum, int16 seqDataIndex, int16 seqDataOffs); uint16 findProgramKeyCharScriptOffset(int keyChar) const; bool scrollRoom(int keyChar); void drawIcon(int x, int y, int num); void centerScreenToKeyChar(int keyChar); void waitForKeyCharsSet(); void redrawRoom(); void fadePalette(int firstColor, int colorCount, int scale, int scaleInc, int fadingStepsCount); void fadePaletteFromFlags(); void moveKeyChar(uint8 *dst, int dstPitch, KeyChar *key); void changeKeyCharFrame(KeyChar *key, int keyChar); void setKeyCharRandomFrame(KeyChar *key); void setKeyCharMoney(); const char *getString(int num) const; int getStringWidth(int num) const; void drawString(uint16 color, int x, int y, int16 num, StringType strType = kStringTypeDefault); void drawGameString(uint16 color, int x1, int y, const char *str); int restartKeyCharScriptOnAction(int action, int obj1, int obj2); void buildSpriteScalingTable(int z1, int z2); void drawSpriteOnBackdrop(int num, int x, int y); void updateTalkFrames(int keyChar); void setKeyCharTalkingFrame(int keyChar); void lockUnlockHitBox(int num, int lock); void drawHitBoxes(); void showCursor(bool show); void setCursor(int num); void setDefaultCursor(int num); void handleLeftMouseButtonClickOnInventory(); void handleRightMouseButtonClickOnInventory(); void handleMouseInput(int flag); void handleMouseClickOnRoom(int flag); void handleMouseClickOnInventory(int flag); void scrollScreenToPos(int num); void clearRoomArea(); void startNewMusic(); void startNewSound(); void updateSpeech(); int handleActionMenuUnderCursor(const int16 *actions, int offs, int y, int str); void redrawBackground(); void addRoomArea(int num, int flag); void updateRoomAreas(int num, int flags); void setRoomAreaState(int num, uint16 state); void findAndRedrawRoomRegion(int num); void updateRoomRegions(); void redrawRoomRegion(int num, bool markForRedraw); void initInventoryObjectsTable(); void initInventoryLists(); void setupInventoryAreas(); void drawInventory(int index, int flag); void drawAmountOfMoneyInInventory(); void packInventoryItems(int index); void appendItemToInventoryList(int index); void addItemToInventory(int inventory, int16 item); void removeItemFromInventory(int inventory, int16 item); void resetTalkingVars(); int updateKeyCharTalk(int pauseFlag); const char *formatTalkText(int *y, int *h, const char *text); void addToTalkTable(int talkingKeyChar, int num, int otherKeyChar); void removeFromTalkTable(int keyChar); void addConversationChoice(int16 num); void removeConversationChoice(int16 num); void runConversationScript(uint16 offset); void findConversationByNum(int16 num); void clearConversationChoices(); void scrollDownConversationChoice(); void scrollUpConversationChoice(); void drawCharacterConversation(); void drawConversationString(int num, uint16 color); void clearConversationArea(); void setupConversationScript(int num); void handleConversation(); void buildWalkPointsList(int keyChar); int findWalkDataNum(int pointNum1, int pointNum2); void changeWalkPath(int num1, int num2, int16 val); void adjustKeyCharPosToWalkBox(KeyChar *key, int moveType); void lockWalkPath(int num1, int num2); void unlockWalkPath(int num1, int num2); void resetPointsData(int num); bool sortPointsData(int num1, int num2); void updateKeyCharWalkPath(KeyChar *key, int16 dx, int16 dy, int16 dz); void markWalkPoints(int keyChar); void buildWalkPath(int dstPosX, int dstPosY, int keyChar); void addToAnimationTable(int num, int posNum, int keyChar, int delayCounter); void copyAnimationImage(int dstX, int dstY, int w, int h, const uint8 *src, int srcX, int srcY, int fillColor); void drawAnimationImage(AnimationEntry *anim); void processAnimationTable(); void clearAnimationTable(); void addToDirtyRect(const Common::Rect &r); void clearDirtyRects(); void setPalette(int firstColor, int colorCount, int redScale, int greenScale, int blueScale); void updateScreenArea(int x, int y, int w, int h); void updateEntireScreen(); void updateDirtyScreenAreas(); void updatePalette(); void saveGameStateData(Common::WriteStream *stream); void loadGameStateData(Common::ReadStream *stream); virtual Common::Error saveGameState(int num, const Common::String &description); virtual Common::Error loadGameState(int num); virtual bool canLoadGameStateCurrently(); virtual bool canSaveGameStateCurrently(); ToucheConsole *_console; void setupOpcodes(); void op_nop(); void op_jnz(); void op_jz(); void op_jmp(); void op_true(); void op_false(); void op_push(); void op_not(); void op_add(); void op_sub(); void op_mul(); void op_div(); void op_mod(); void op_and(); void op_or(); void op_neg(); void op_testGreater(); void op_testEquals(); void op_testLower(); void op_fetchScriptWord(); void op_testGreaterOrEquals(); void op_testLowerOrEquals(); void op_testNotEquals(); void op_endConversation(); void op_stopScript(); void op_getFlag(); void op_setFlag(); void op_fetchScriptByte(); void op_getKeyCharWalkBox(); void op_startSound(); void op_moveKeyCharToPos(); void op_loadRoom(); void op_updateRoom(); void op_startTalk(); void op_loadSprite(); void op_loadSequence(); void op_setKeyCharBox(); void op_initKeyCharScript(); void op_setKeyCharFrame(); void op_setKeyCharDirection(); void op_clearConversationChoices(); void op_addConversationChoice(); void op_removeConversationChoice(); void op_getInventoryItem(); void op_setInventoryItem(); void op_startEpisode(); void op_setConversationNum(); void op_enableInput(); void op_disableInput(); void op_faceKeyChar(); void op_getKeyCharCurrentAnim(); void op_getCurrentKeyChar(); void op_isKeyCharActive(); void op_setPalette(); void op_changeWalkPath(); void op_lockWalkPath(); void op_initializeKeyChar(); void op_setupWaitingKeyChars(); void op_updateRoomAreas(); void op_unlockWalkPath(); void op_addItemToInventoryAndRedraw(); void op_giveItemTo(); void op_setHitBoxText(); void op_fadePalette(); void op_getInventoryItemFlags(); void op_drawInventory(); void op_stopKeyCharScript(); void op_restartKeyCharScript(); void op_getKeyCharCurrentWalkBox(); void op_getKeyCharPointsDataNum(); void op_setupFollowingKeyChar(); void op_startAnimation(); void op_setKeyCharTextColor(); void op_startMusic(); void op_sleep(); void op_setKeyCharDelay(); void op_lockHitBox(); void op_removeItemFromInventory(); void op_unlockHitBox(); void op_addRoomArea(); void op_setKeyCharFlags(); void op_unsetKeyCharFlags(); void op_loadSpeechSegment(); void op_drawSpriteOnBackdrop(); void op_startPaletteFadeIn(); void op_startPaletteFadeOut(); void op_setRoomAreaState(); void res_openDataFile(); void res_closeDataFile(); void res_allocateTables(); void res_deallocateTables(); uint32 res_getDataOffset(ResourceType type, int num, uint32 *size = NULL); void res_loadSpriteImage(int num, uint8 *dst); void res_loadProgram(int num); void res_decodeProgramData(); void res_loadRoom(int num); void res_loadSprite(int num, int index); void res_loadSequence(int num, int index); void res_decodeScanLineImageRLE(uint8 *dst, int lineWidth); void res_loadBackdrop(); void res_loadImage(int num, uint8 *dst); void res_loadImageHelper(uint8 *imgData, int imgWidth, int imgHeight); void res_loadSound(int flag, int num); void res_stopSound(); void res_loadMusic(int num); void res_loadSpeech(int num); void res_loadSpeechSegment(int num); void res_stopSpeech(); void drawButton(Button *button); void redrawMenu(MenuData *menu); void handleMenuAction(MenuData *menu, int actionId); void handleOptions(int forceDisplay); void drawActionsPanel(int dstX, int dstY, int deltaX, int deltaY); void drawConversationPanelBorder(int dstY, int srcX, int srcY); void drawConversationPanel(); void printStatusString(const char *str); void clearStatusString(); int displayQuitDialog(); void displayTextMode(int str); Common::Point getMousePos() const; MidiPlayer *_midiPlayer; int _musicVolume; Audio::SoundHandle _musicHandle; void initMusic(); public: // To allow access from console void startMusic(int num); void stopMusic(); protected: int getMusicVolume(); void setMusicVolume(int volume); void adjustMusicVolume(int diff); Common::Language _language; Common::RandomSource _rnd; bool _inp_leftMouseButtonPressed; bool _inp_rightMouseButtonPressed; int _disabledInputCounter; bool _hideInventoryTexts; GameState _gameState; bool _displayQuitDialog; int _saveLoadCurrentPage; int _saveLoadCurrentSlot; int _newMusicNum; int _currentMusicNum; int _newSoundNum; int _newSoundDelay; int _newSoundPriority; int _playSoundCounter; bool _speechPlaying; Audio::SoundHandle _sfxHandle; Audio::SoundHandle _speechHandle; int16 _inventoryList1[101]; int16 _inventoryList2[101]; int16 _inventoryList3[7]; InventoryState _inventoryStateTable[3]; int16 _inventoryItemsInfoTable[NUM_INVENTORY_ITEMS]; int16 *_inventoryVar1; int16 *_inventoryVar2; int _currentCursorObject; Common::Rect _inventoryAreasTable[13]; int _talkTextMode; int _talkListEnd; int _talkListCurrent; bool _talkTextRectDefined; bool _talkTextDisplayed; bool _talkTextInitialized; bool _skipTalkText; int _talkTextSpeed; int _keyCharTalkCounter; int _talkTableLastTalkingKeyChar; int _talkTableLastOtherKeyChar; int _talkTableLastStringNum; int _objectDescriptionNum; TalkEntry _talkTable[NUM_TALK_ENTRIES]; bool _conversationChoicesUpdated; int _conversationReplyNum; bool _conversationEnded; int _conversationNum; int _scrollConversationChoiceOffset; int _currentConversation; bool _disableConversationScript; bool _conversationAreaCleared; ConversationChoice _conversationChoicesTable[NUM_CONVERSATION_CHOICES]; int16 _flagsTable[NUM_FLAGS]; KeyChar _keyCharsTable[NUM_KEYCHARS]; KeyChar *_sortedKeyCharsTable[NUM_KEYCHARS]; int _currentKeyCharNum; int _newEpisodeNum; int _currentEpisodeNum; int _currentAmountOfMoney; int _giveItemToKeyCharNum; int _giveItemToObjectNum; int _giveItemToCounter; int _currentRoomNum; int _waitingSetKeyCharNum1; int _waitingSetKeyCharNum2; int _waitingSetKeyCharNum3; uint8 _updatedRoomAreasTable[200]; Common::Rect _moveKeyCharRect; Common::Point _screenOffset; int _currentObjectNum; int _processRandomPaletteCounter; int16 _spriteScalingIndex[1000]; int16 _spriteScalingTable[1000]; bool _fastWalkMode; bool _fastMode; AnimationEntry _animationTable[NUM_ANIMATION_ENTRIES]; Script _script; const OpcodeProc *_opcodesTable; int _numOpcodes; Common::File _fData; Common::File _fSpeech[2]; int _compressedSpeechData; uint8 *_textData; uint8 *_backdropBuffer; uint8 *_menuKitData; uint8 *_convKitData; uint8 *_sequenceDataTable[NUM_SEQUENCES]; uint8 *_programData; uint32 _programDataSize; uint8 *_mouseData; uint8 *_iconData; SpriteData _spritesTable[NUM_SPRITES]; SequenceEntry _sequenceEntryTable[NUM_SEQUENCES]; int _currentBitmapWidth; int _currentBitmapHeight; int _currentImageWidth; int _currentImageHeight; int _roomWidth; uint8 *_programTextDataPtr; Common::Array _programRectsTable; Common::Array _programPointsTable; Common::Array _programWalkTable; Common::Array _programAreaTable; Common::Array _programBackgroundTable; Common::Array _programHitBoxTable; Common::Array _programActionScriptOffsetTable; Common::Array _programKeyCharScriptOffsetTable; Common::Array _programConversationTable; Common::Rect _cursorObjectRect; Common::Rect _talkTextRect, _talkTextRect2; Common::Rect _screenRect; Common::Rect _roomAreaRect; bool _roomNeedRedraw; int _fullRedrawCounter; int _menuRedrawCounter; uint8 *_offscreenBuffer; uint8 _paletteBuffer[256 * 3]; Common::Rect _dirtyRectsTable[NUM_DIRTY_RECTS]; int _dirtyRectsTableCount; static const uint8 _directionsTable[NUM_DIRECTIONS]; }; /* FLAGS LIST 115 : don't set backdrop palette on room loading 118 : current amount of money 119 : current cursor object 176 : keychar max direction 266 : keychar direction override 267 : don't decode picture/sprite images (in load_image_helper) 268 : don't decode picture/sprite images 269 : disable room background animations 270 : play random sound 290 : process random palette 295 : game cycle counter (incremented) 296 : game cycle counter (incremented) 297 : game cycle counter (incremented) 298 : game cycle counter (decremented) 299 : game cycle counter (decremented) 600 : last ascii key press 603 : fade palette "scale" increment (in vbl handler) 605 : fade palette "scale" 606 : inventory redraw disabled 607 : first palette color to fade 608 : last palette color to fade 609 : max fade palette "scale" 610 : min fade palette "scale" 611 : quit game 612 : random number modulo 613 : last generated random number 614 : room scroll x offset 615 : room scroll y offset 616 : disable room scrolling 617 : current speech file number 618 : hide mouse cursor 621 : enable french version "features" 902 : debug/draw walk boxes 911 : load scripts/programs from external files */ } // namespace Touche #endif