/* 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 FREESCAPE_H #define FREESCAPE_H #include "common/bitarray.h" #include "common/events.h" #include "common/file.h" #include "common/memstream.h" #include "engines/advancedDetector.h" #include "graphics/managed_surface.h" #include "graphics/surface.h" #include "audio/decoders/wave.h" #include "audio/mixer.h" #include "audio/softsynth/pcspk.h" #include "graphics/framelimiter.h" #include "freescape/area.h" #include "freescape/font.h" #include "freescape/gfx.h" #include "freescape/objects/entrance.h" #include "freescape/objects/geometricobject.h" #include "freescape/objects/sensor.h" #include "freescape/sound.h" namespace Common { class RandomSource; } namespace Freescape { class Renderer; #define FREESCAPE_DATA_BUNDLE "freescape.dat" enum CameraMovement { kForwardMovement, kBackwardMovement, kLeftMovement, kRightMovement }; enum FreescapeAction { kActionNone, kActionEscape, kActionSave, kActionLoad, kActionToggleSound, kActionMoveUp, kActionMoveDown, kActionMoveLeft, kActionMoveRight, kActionShoot, kActionActivate, kActionIncreaseAngle, kActionDecreaseAngle, kActionChangeStepSize, kActionToggleRiseLower, kActionRiseOrFlyUp, kActionLowerOrFlyDown, kActionChangeMode, kActionSkip, kActionFaceForward, kActionRotateUp, kActionRotateDown, kActionRotateLeft, kActionRotateRight, kActionTurnBack, kActionInfoMenu, kActionIncreaseStepSize, kActionDecreaseStepSize, kActionToggleFlyMode, kActionToggleClipMode, // Driller kActionDeployDrillingRig, kActionCollectDrillingRig, kActionRollLeft, kActionRollRight, // Total Eclipse kActionRest, // Castle kActionRunMode, kActionWalkMode, kActionCrawlMode, kActionSelectPrince, kActionSelectPrincess, kActionQuit, // Demo actions kActionUnknownKey, kActionWait }; typedef Common::HashMap AreaMap; typedef Common::Array ColorMap; typedef Common::HashMap StateVars; enum { kFreescapeDebugMove = 1, kFreescapeDebugParser, kFreescapeDebugCode, kFreescapeDebugMedia, kFreescapeDebugGroup, }; enum { kFreescapeDefaultVolume = 192, }; enum GameStateControl { kFreescapeGameStateStart, kFreescapeGameStatePlaying, kFreescapeGameStateDemo, kFreescapeGameStateEnd, kFreescapeGameStateRestart, }; struct CGAPaletteEntry { int areaId; byte *palette; }; extern Common::String shiftStr(const Common::String &str, int shift); extern Common::String centerAndPadString(const Common::String &str, int size); class EventManagerWrapper { public: EventManagerWrapper(Common::EventManager *delegate); // EventManager API bool pollEvent(Common::Event &event); void purgeKeyboardEvents(); void purgeMouseEvents(); void pushEvent(Common::Event &event); void clearExitEvents(); bool isActionActive(const Common::CustomEventType &action); bool isKeyPressed(); private: // for continuous events (keyDown) enum { kKeyRepeatInitialDelay = 400, kKeyRepeatSustainDelay = 100 }; Common::EventManager *_delegate; Common::KeyState _currentKeyDown; Common::CustomEventType _currentActionDown; uint32 _keyRepeatTime; }; class FreescapeEngine : public Engine { public: FreescapeEngine(OSystem *syst, const ADGameDescription *gd); ~FreescapeEngine(); const ADGameDescription *_gameDescription; GameStateControl _gameStateControl; bool isDemo() const; // Game selection uint32 _variant; Common::Language _language; bool isSpaceStationOblivion() { return _targetName.hasPrefix("spacestationoblivion"); } bool isDriller() { return _targetName.hasPrefix("driller") || _targetName.hasPrefix("spacestationoblivion"); } bool isDark() { return _targetName.hasPrefix("darkside"); } bool isEclipse() { return _targetName.hasPrefix("totaleclipse"); } // This will match Total Eclipse 1 and 2. bool isEclipse2() { return _targetName.hasPrefix("totaleclipse2"); } bool isCastle() { return _targetName.hasPrefix("castle"); } bool isAmiga() { return _gameDescription->platform == Common::kPlatformAmiga; } bool isAtariST() { return _gameDescription->platform == Common::kPlatformAtariST; } bool isDOS() { return _gameDescription->platform == Common::kPlatformDOS; } bool isSpectrum() { return _gameDescription->platform == Common::kPlatformZX; } bool isCPC() { return _gameDescription->platform == Common::kPlatformAmstradCPC; } bool isC64() { return _gameDescription->platform == Common::kPlatformC64; } virtual void beforeStarting(); Common::Error run() override; // UI Common::Rect _viewArea; Common::Rect _fullscreenViewArea; void centerCrossair(); virtual void borderScreen(); virtual void titleScreen(); void drawFullscreenSurface(Graphics::Surface *surface); virtual void loadBorder(); virtual void processBorder(); void waitInLoop(int maxWait); void drawBorder(); void drawTitle(); virtual void drawBackground(); void clearBackground(); virtual void drawUI(); virtual void drawInfoMenu(); void drawBorderScreenAndWait(Graphics::Surface *surface, int maxWait = INT_MAX); virtual void drawCrossair(Graphics::Surface *surface); Graphics::ManagedSurface *_border; Graphics::ManagedSurface *_title; Graphics::ManagedSurface *_background; Texture *_borderTexture; Texture *_titleTexture; Texture *_uiTexture; Texture *_skyTexture; Common::Array_indicators; Common::HashMap _borderCGAByArea; Common::HashMap _paletteCGAByArea; // Parsing assets uint8 _binaryBits; virtual void loadAssets(); virtual void loadAssetsDemo(); virtual void loadAssetsFullGame(); virtual void loadAssetsAtariFullGame(); virtual void loadAssetsAtariDemo(); virtual void loadAssetsAmigaFullGame(); virtual void loadAssetsAmigaDemo(); virtual void loadAssetsDOSFullGame(); virtual void loadAssetsDOSDemo(); virtual void loadAssetsZXFullGame(); virtual void loadAssetsZXDemo(); virtual void loadAssetsCPCFullGame(); virtual void loadAssetsCPCDemo(); virtual void loadAssetsC64FullGame(); virtual void loadAssetsC64Demo(); virtual void drawDOSUI(Graphics::Surface *surface); virtual void drawZXUI(Graphics::Surface *surface); virtual void drawCPCUI(Graphics::Surface *surface); virtual void drawC64UI(Graphics::Surface *surface); virtual void drawAmigaAtariSTUI(Graphics::Surface *surface); Common::Archive *_dataBundle; void loadDataBundle(); Graphics::Surface *loadBundledImage(const Common::String &name, bool appendRenderMode = true); byte *getPaletteFromNeoImage(Common::SeekableReadStream *stream, int offset); Graphics::ManagedSurface *loadAndConvertNeoImage(Common::SeekableReadStream *stream, int offset, byte *palette = nullptr); Graphics::ManagedSurface *loadAndConvertScrImage(Common::SeekableReadStream *stream); Graphics::ManagedSurface *loadAndConvertDoodleImage(Common::SeekableReadStream *bitmap, Common::SeekableReadStream *color1, Common::SeekableReadStream *color2, byte *palette); void loadPalettes(Common::SeekableReadStream *file, int offset); byte *loadPalette(Common::SeekableReadStream *file); void swapPalette(uint16 areaID); virtual byte *findCGAPalette(uint16 levelID); Common::HashMap _paletteByArea; void loadColorPalette(); // Demo Common::Array _demoData; int _demoIndex; int _currentDemoInputCode; int _currentDemoInputRepetition; Common::Array _demoEvents; Common::Point _currentDemoMousePosition; void loadDemoData(Common::SeekableReadStream *file, int offset, int size); int decodeAmigaAtariKey(int code); int decodeDOSKey(int code); Common::Event decodeDOSMouseEvent(int code, int repetition); uint16 readField(Common::SeekableReadStream *file, int nbits); uint16 readPtr(Common::SeekableReadStream *file); Common::Array readArray(Common::SeekableReadStream *file, int size); // 8-bit void load8bitBinary(Common::SeekableReadStream *file, int offset, int ncolors); Area *load8bitArea(Common::SeekableReadStream *file, uint16 ncolors); Object *load8bitObject(Common::SeekableReadStream *file); Group *load8bitGroup(Common::SeekableReadStream *file, byte rawFlagsAndType); Group *load8bitGroupV1(Common::SeekableReadStream *file, byte rawFlagsAndType); Group *load8bitGroupV2(Common::SeekableReadStream *file, byte rawFlagsAndType); void loadGlobalObjects(Common::SeekableReadStream *file, int offset, int size); void renderPixels8bitBinImage(Graphics::ManagedSurface *surface, int row, int column, int bit, int count); void renderPixels8bitBinCGAImage(Graphics::ManagedSurface *surface, int &i, int &j, uint8 pixels, int color); void renderPixels8bitBinEGAImage(Graphics::ManagedSurface *surface, int &i, int &j, uint8 pixels, int color); Graphics::ManagedSurface *load8bitBinImage(Common::SeekableReadStream *file, int offset); void load8bitBinImageRow(Common::SeekableReadStream *file, Graphics::ManagedSurface *surface, int row); void load8bitBinImageRowIteration(Common::SeekableReadStream *file, Graphics::ManagedSurface *surface, int row, int bit); int execute8bitBinImageCommand(Common::SeekableReadStream *file, Graphics::ManagedSurface *surface, int row, int pixels, int bit); int execute8bitBinImageSingleCommand(Common::SeekableReadStream *file, Graphics::ManagedSurface *surface, int row, int pixels, int bit, int count); int execute8bitBinImageMultiCommand(Common::SeekableReadStream *file, Graphics::ManagedSurface *surface, int row, int pixels, int bit, int count); void parseAmigaAtariHeader(Common::SeekableReadStream *file); Common::SeekableReadStream *decryptFileAmigaAtari(const Common::Path &packed, const Common::Path &unpacker, uint32 unpackArrayOffset); // Areas uint16 _startArea; uint16 _endArea; AreaMap _areaMap; Area *_currentArea; bool _gotoExecuted; Math::Vector3d _scale; virtual void gotoArea(uint16 areaID, int entranceID); // Entrance uint16 _startEntrance; uint16 _endEntrance; Common::HashMap _entranceTable; // Input bool _demoMode; bool _disableDemoMode; bool _flyMode; bool _shootMode; bool _noClipMode; bool _invertY; bool _smoothMovement; // Player movement state bool _moveForward; bool _moveBackward; bool _strafeLeft; bool _strafeRight; bool _moveUp; bool _moveDown; virtual void initKeymaps(Common::Keymap *engineKeyMap, Common::Keymap *infoScreenKeyMap, const char *target); EventManagerWrapper *_eventManager; void processInput(); void resetInput(); void stopMovement(); void generateDemoInput(); virtual void pressedKey(const int keycode); virtual void releasedKey(const int keycode); Common::Point getNormalizedPosition(Common::Point position); virtual bool onScreenControls(Common::Point mouse); void updatePlayerMovement(float deltaTime); void updatePlayerMovementSmooth(float deltaTime); void updatePlayerMovementClassic(float deltaTime); void resolveCollisions(Math::Vector3d newPosition); virtual void checkIfStillInArea(); void changePlayerHeight(int index); void increaseStepSize(); void decreaseStepSize(); void changeStepSize(); void changeAngle(int offset, bool wrapAround); bool rise(); void lower(); bool checkFloor(Math::Vector3d currentPosition); bool tryStepUp(Math::Vector3d currentPosition); bool tryStepDown(Math::Vector3d currentPosition); bool _hasFallen; bool _isCollidingWithWall; bool _isSteppingUp; bool _isSteppingDown; bool _isFalling; int _maxFallingDistance; int _maxShield; int _maxEnergy; void rotate(float xoffset, float yoffset, float zoffset); // Input state float _lastFrame; // Interaction void activate(); void shoot(); void traverseEntrance(uint16 entranceID); // Euler Angles float _yaw; float _pitch; int _roll; int _angleRotationIndex; Common::Array _angleRotations; Math::Vector3d directionToVector(float pitch, float heading, bool useTable); void updateCamera(); // Camera options Common::Point _crossairPosition; float _mouseSensitivity; Math::Vector3d _upVector; // const Math::Vector3d _cameraFront, _cameraRight; // Spacial attributes Math::Vector3d _position, _rotation, _velocity; Math::Vector3d _lastPosition; int _playerHeightNumber; int _playerHeightMaxNumber; uint16 _playerHeight; uint16 _playerWidth; uint16 _playerDepth; uint16 _stepUpDistance; int _playerStepIndex; Common::Array _playerSteps; Common::Point crossairPosToMousePos(const Common::Point &crossairPos); Common::Point mousePosToCrossairPos(const Common::Point &mousePos); void warpMouseToCrossair(void); // Effects Common::Array _conditionSources; Common::Array _conditions; bool runCollisionConditions(Math::Vector3d const lastPosition, Math::Vector3d const newPosition); Math::Vector3d _objExecutingCodeSize; bool _executingGlobalCode; virtual void executeMovementConditions(); bool executeObjectConditions(GeometricObject *obj, bool shot, bool collided, bool activated); void executeEntranceConditions(Entrance *entrance); void executeLocalGlobalConditions(bool shot, bool collided, bool timer); bool executeCode(FCLInstructionVector &code, bool shot, bool collided, bool timer, bool activated); // Instructions bool checkConditional(FCLInstruction &instruction, bool shot, bool collided, bool timer, bool activated); bool checkIfGreaterOrEqual(FCLInstruction &instruction); bool checkIfLessOrEqual(FCLInstruction &instruction); void executeExecute(FCLInstruction &instruction); void executeIncrementVariable(FCLInstruction &instruction); void executeDecrementVariable(FCLInstruction &instruction); void executeSetVariable(FCLInstruction &instruction); void executeGoto(FCLInstruction &instruction); void executeIfThenElse(FCLInstruction &instruction); virtual void executeMakeInvisible(FCLInstruction &instruction); void executeMakeVisible(FCLInstruction &instruction); void executeToggleVisibility(FCLInstruction &instruction); virtual void executeDestroy(FCLInstruction &instruction); virtual void executeRedraw(FCLInstruction &instruction); void executeSound(FCLInstruction &instruction); void executeDelay(FCLInstruction &instruction); bool executeEndIfNotEqual(FCLInstruction &instruction); void executeSetBit(FCLInstruction &instruction); void executeClearBit(FCLInstruction &instruction); void executeToggleBit(FCLInstruction &instruction); bool executeEndIfBitNotEqual(FCLInstruction &instruction); bool executeEndIfVisibilityIsEqual(FCLInstruction &instruction); void executeSwapJet(FCLInstruction &instruction); virtual void executePrint(FCLInstruction &instruction); void executeSPFX(FCLInstruction &instruction); void executeStartAnim(FCLInstruction &instruction); // Sound Audio::SoundHandle _soundFxHandle; Audio::SoundHandle _musicHandle; Audio::SoundHandle _movementSoundHandle; Freescape::SizedPCSpeaker *_speaker; bool _syncSound; bool _firstSound; bool _usePrerecordedSounds; void waitForSounds(); void stopAllSounds(Audio::SoundHandle &handle); bool isPlayingSound(); void playSound(int index, bool sync, Audio::SoundHandle &handle); void playWav(const Common::Path &filename); void playMusic(const Common::Path &filename); void queueSoundConst(double hzFreq, int duration); void playSilence(int duration, bool sync); void playSoundConst(double hzFreq, int duration, bool sync); void playSoundSweepIncWL(double hzFreq1, double hzFreq2, double wlStepPerMS, int resolution, bool sync); uint16 playSoundDOSSpeaker(uint16 startFrequency, soundSpeakerFx *speakerFxInfo); void playSoundDOS(soundSpeakerFx *speakerFxInfo, bool sync, Audio::SoundHandle &handle); virtual void playSoundFx(int index, bool sync); virtual void loadSoundsFx(Common::SeekableReadStream *file, int offset, int number); Common::HashMap _soundsFx; void loadSpeakerFxDOS(Common::SeekableReadStream *file, int offsetFreq, int offsetDuration, int numberSounds); void loadSpeakerFxZX(Common::SeekableReadStream *file, int sfxTable, int sfxData); Common::HashMap _soundsSpeakerFx; void playSoundZX(Common::Array *data, Audio::SoundHandle &handle); Common::HashMap*> _soundsSpeakerFxZX; int _soundIndexShoot; int _soundIndexCollide; int _soundIndexStepDown; int _soundIndexStepUp; int _soundIndexFall; int _soundIndexMenu; int _soundIndexStart; int _soundIndexAreaChange; int _soundIndexHit; int _soundIndexNoShield; int _soundIndexNoEnergy; int _soundIndexFallen; int _soundIndexTimeout; int _soundIndexForceEndGame; int _soundIndexCrushed; int _soundIndexMissionComplete; // Rendering int _screenW, _screenH; Renderer *_gfx; Graphics::FrameLimiter *_frameLimiter; bool _vsyncEnabled; Common::RenderMode _renderMode; ColorMap _colorMap; int _underFireFrames; int _avoidRenderingFrames; int _shootingFrames; GeometricObject *_delayedShootObject; void drawFrame(); void flashScreen(int backgroundColor); uint8 _colorNumber; Math::Vector3d _scaleVector; float _nearClipPlane; float _farClipPlane; // Text messages and Fonts void insertTemporaryMessage(const Common::String &message, int deadline); void getLatestMessages(Common::String &message, int &deadline); void clearTemporalMessages(); Common::StringArray _temporaryMessages; Common::Array _temporaryMessageDeadlines; Common::StringArray _messagesList; Common::String _noShieldMessage; Common::String _noEnergyMessage; Common::String _fallenMessage; Common::String _timeoutMessage; Common::String _forceEndGameMessage; Common::String _crushedMessage; Common::String _outOfReachMessage; Common::String _noEffectMessage; void loadMessagesFixedSize(Common::SeekableReadStream *file, int offset, int size, int number); virtual void loadMessagesVariableSize(Common::SeekableReadStream *file, int offset, int number); void drawFullscreenMessageAndWait(Common::String message); void drawFullscreenMessage(Common::String message, uint32 front, Graphics::Surface *surface); // Font loading and rendering void loadFonts(Common::SeekableReadStream *file, int offset); void loadFonts(byte *font, int charNumber); Common::Array getChars(Common::SeekableReadStream *file, int offset, int charsNumber); Common::Array getCharsAmigaAtariInternal(int sizeX, int sizeY, int additional, int m1, int m2, Common::SeekableReadStream *file, int offset, int charsNumber); Common::Array getCharsAmigaAtari(Common::SeekableReadStream *file, int offset, int charsNumber); Common::StringArray _currentAreaMessages; Common::StringArray _currentEphymeralMessages; Font _font; bool _fontLoaded; virtual void drawStringInSurface(const Common::String &str, int x, int y, uint32 fontColor, uint32 backColor, Graphics::Surface *surface, int offset = 0); virtual void drawStringInSurface(const Common::String &str, int x, int y, uint32 primaryFontColor, uint32 secondaryFontColor, uint32 backColor, Graphics::Surface *surface, int offset = 0); Graphics::Surface *drawStringsInSurface(const Common::Array &lines, Graphics::Surface *surface); // Game state virtual void initGameState(); void setGameBit(int index); void clearGameBit(int index); void toggleGameBit(int index); uint16 getGameBit(int index); StateVars _gameStateVars; uint32 _gameStateBits; void checkIfPlayerWasCrushed(); virtual bool checkIfGameEnded(); virtual void endGame(); int _endGameDelayTicks; bool _endGameKeyPressed; bool _endGamePlayerEndArea; bool _forceEndGame; bool _playerWasCrushed; Common::HashMap _exploredAreas; ObjectArray _sensors; virtual void checkSensors(); virtual void drawSensorShoot(Sensor *sensor); void takeDamageFromSensor(); bool hasFeature(EngineFeature f) const override; bool canLoadGameStateCurrently(Common::U32String *msg = nullptr) override { return true; } bool canSaveAutosaveCurrently() override { return false; } bool canSaveGameStateCurrently(Common::U32String *msg = nullptr) override { return _gameStateControl == kFreescapeGameStatePlaying && _currentArea; } Common::Error loadGameStream(Common::SeekableReadStream *stream) override; Common::Error saveGameStream(Common::WriteStream *stream, bool isAutosave = false) override; virtual Common::Error saveGameStreamExtended(Common::WriteStream *stream, bool isAutosave = false); virtual Common::Error loadGameStreamExtended(Common::SeekableReadStream *stream); Graphics::Surface *_savedScreen; void pauseEngineIntern(bool pause) override; // Timers bool startCountdown(uint32 delay); void removeTimers(); bool _timerStarted; int _initialCountdown; int _countdown; int _ticks; int _ticksFromEnd; int _lastTick; int _lastMinute; void getTimeFromCountdown(int &seconds, int &minutes, int &hours); virtual void updateTimeVariables(); // Cheats bool _useExtendedTimer; bool _disableSensors; bool _disableFalling; // Random Common::RandomSource *_rnd; // C64 specifics byte *decompressC64RLE(byte *buffer, int *size, byte marker); byte *_extraBuffer; }; enum GameReleaseFlags { GF_AMIGA_RETAIL = (1 << 0), GF_AMIGA_BUDGET = (1 << 1), GF_ZX_RETAIL = (1 << 2), GF_ZX_BUDGET = (1 << 3), GF_ZX_DISC = (1 << 4), GF_CPC_RETAIL = (1 << 5), GF_CPC_RETAIL_ALT = (1 << 6), GF_CPC_BUDGET = (1 << 7), GF_CPC_VIRTUALWORLDS = (1 << 8), GF_ATARI_RETAIL = (1 << 9), GF_ATARI_BUDGET = (1 << 10), GF_C64_TAPE = (1 << 11), GF_C64_DISC = (1 << 12), }; extern FreescapeEngine *g_freescape; } // namespace Freescape #endif // FREESCAPE_H