/* 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 VCRUISE_RUNTIME_H
#define VCRUISE_RUNTIME_H
#include "graphics/pixelformat.h"
#include "common/hashmap.h"
#include "common/keyboard.h"
#include "common/mutex.h"
#include "common/rect.h"
#include "vcruise/detection.h"
class OSystem;
class MidiDriver;
namespace Common {
class MemoryReadStream;
class RandomSource;
class ReadStream;
class WriteStream;
} // End of namespace Commom
namespace Audio {
class AudioStream;
class SeekableAudioStream;
class LoopingAudioStream;
} // End of namespace Audio
namespace Graphics {
struct PixelFormat;
struct WinCursorGroup;
class ManagedSurface;
class Font;
class Cursor;
} // End of namespace Graphics
namespace Video {
class AVIDecoder;
} // End of namespace Video
namespace Image {
class AniDecoder;
} // End of namespace Image
namespace VCruise {
static const uint kNumDirections = 8;
static const uint kNumHighPrecisionDirections = 256;
static const uint kHighPrecisionDirectionMultiplier = kNumHighPrecisionDirections / kNumDirections;
static const uint kNumStartConfigs = 3;
enum StartConfig {
kStartConfigCheatMenu,
kStartConfigInitial,
kStartConfigAlt,
};
class AudioPlayer;
class CircuitPuzzle;
class MidiPlayer;
class MenuInterface;
class MenuPage;
class RuntimeMenuInterface;
class TextParser;
struct ScriptSet;
struct Script;
struct IScriptCompilerGlobalState;
struct Instruction;
struct RoomScriptSet;
struct SoundLoopInfo;
class SampleLoopAudioStream;
struct AD2044Graphics;
enum GameState {
kGameStateBoot, // Booting the game
kGameStateWaitingForAnimation, // Waiting for a blocking animation with no stop frame to complete, then resuming script
kGameStateWaitingForAnimationToDelay, // Waiting for a blocking animation with no stop frame to complete, then going to delay
kGameStateWaitingForFacing, // Waiting for a blocking animation with a stop frame to complete, then resuming script
kGameStateWaitingForFacingToAnim, // Waiting for a blocking animation to complete, then playing _postFacingAnimDef and switching to kGameStateWaitingForAnimation
kGameStateQuit, // Quitting
kGameStateIdle, // Waiting for input events
kGameStateDelay, // Waiting for delay completion time
kGameStateScript, // Running a script
kGameStateScriptReset, // Resetting script interpreter into a new script
kGameStateGyroIdle, // Waiting for mouse movement to run a gyro
kGameStateGyroAnimation, // Animating a gyro
kGameStatePanLeft,
kGameStatePanRight,
kGameStateMenu,
};
struct AD2044AnimationDef {
byte roomID;
byte lookupID;
short fwdAnimationID;
short revAnimationID;
};
struct AnimationDef {
AnimationDef();
int animNum; // May be negative if reversed
uint firstFrame;
uint lastFrame; // Inclusive
Common::Rect constraintRect;
Common::String animName;
};
struct RoomDef {
Common::HashMap animations;
Common::HashMap vars;
Common::HashMap values;
Common::HashMap texts;
Common::HashMap sounds;
Common::String name;
};
struct InteractionDef {
InteractionDef();
Common::Rect rect;
uint16 interactionID;
uint16 objectType;
};
struct MapScreenDirectionDef {
Common::Array interactions;
};
class MapLoader {
public:
virtual ~MapLoader();
virtual void setRoomNumber(uint roomNumber) = 0;
virtual const MapScreenDirectionDef *getScreenDirection(uint screen, uint direction) = 0;
virtual void unload() = 0;
protected:
static Common::SharedPtr loadScreenDirectionDef(Common::ReadStream &stream);
};
struct ScriptEnvironmentVars {
ScriptEnvironmentVars();
uint panInteractionID;
uint clickInteractionID;
uint fpsOverride;
uint lastHighlightedItem;
uint animChangeFrameOffset;
uint animChangeNumFrames;
bool lmb;
bool lmbDrag;
bool esc;
bool exitToMenu;
bool animChangeSet;
bool isEntryScript;
bool puzzleWasSet;
};
struct SfxSound {
Common::Array soundData;
Common::SharedPtr memoryStream;
Common::SharedPtr audioStream;
Common::SharedPtr audioPlayer;
};
struct SfxPlaylistEntry {
SfxPlaylistEntry();
uint frame;
Common::SharedPtr sample;
int8 balance;
int32 volume;
bool isUpdate;
};
struct SfxPlaylist {
SfxPlaylist();
Common::Array entries;
};
struct SfxData {
SfxData();
void reset();
void load(Common::SeekableReadStream &stream, Audio::Mixer *mixer);
typedef Common::HashMap > PlaylistMap_t;
typedef Common::HashMap > SoundMap_t;
PlaylistMap_t playlists;
SoundMap_t sounds;
};
struct SoundParams3D {
SoundParams3D();
uint minRange;
uint maxRange;
// Not sure what this does. It's always shorter than the min range but after many tests, I've been
// unable to detect any level changes from altering this parameter.
uint unknownRange;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream);
};
struct SoundCache {
SoundCache();
~SoundCache();
Common::SharedPtr loopInfo;
Common::SharedPtr stream;
Common::SharedPtr loopingStream;
Common::SharedPtr player;
bool isLoopActive;
};
struct SoundInstance {
SoundInstance();
~SoundInstance();
Common::String name;
Common::SharedPtr cache;
uint id;
int32 rampStartVolume;
int32 rampEndVolume;
int32 rampRatePerMSec;
uint32 rampStartTime;
bool rampTerminateOnCompletion;
int32 volume;
int32 balance;
uint effectiveVolume;
int32 effectiveBalance;
bool is3D;
bool isLooping;
bool isSpeech;
bool restartWhenAudible;
bool tryToLoopWhenRestarted;
int32 x;
int32 y;
SoundParams3D params3D;
uint32 startTime;
uint32 endTime;
uint32 duration;
};
struct RandomAmbientSound {
RandomAmbientSound();
Common::String name;
int32 volume;
int32 balance;
uint frequency;
uint sceneChangesRemaining;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream);
};
struct TriggeredOneShot {
TriggeredOneShot();
bool operator==(const TriggeredOneShot &other) const;
bool operator!=(const TriggeredOneShot &other) const;
uint soundID;
uint uniqueSlot;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream);
};
struct ScoreSectionDef {
ScoreSectionDef();
Common::String musicFileName; // If empty, this is silent
Common::String nextSection;
int32 volumeOrDurationInSeconds;
};
struct ScoreTrackDef {
typedef Common::HashMap ScoreSectionMap_t;
ScoreSectionMap_t sections;
};
struct StartConfigDef {
StartConfigDef();
uint disc;
uint room;
uint screen;
uint direction;
};
struct StaticAnimParams {
StaticAnimParams();
uint initialDelay;
uint repeatDelay;
bool lockInteractions;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream);
};
struct StaticAnimation {
StaticAnimation();
AnimationDef animDefs[2];
StaticAnimParams params;
uint32 nextStartTime;
uint currentAlternation;
};
struct FrameData {
FrameData();
uint32 frameIndex;
uint16 areaFrameIndex;
int8 roomNumber;
uint8 frameType; // 0x01 = Keyframe, 0x02 = Intra frame (not used in Schizm), 0x41 = Last frame
char areaID[4];
};
struct FrameData2 {
FrameData2();
int32 x;
int32 y;
int32 angle;
uint16 frameNumberInArea;
uint16 unknown; // Subarea or something?
};
struct AnimFrameRange {
AnimFrameRange();
uint animationNum;
uint firstFrame;
uint lastFrame; // Inclusive
};
struct InventoryItem {
InventoryItem();
Common::SharedPtr graphic;
Common::SharedPtr mask;
uint itemID;
bool highlighted;
};
struct Fraction {
Fraction();
Fraction(uint pNumerator, uint pDenominator);
uint numerator;
uint denominator;
};
enum LoadGameOutcome {
kLoadGameOutcomeSucceeded,
kLoadGameOutcomeSaveDataCorrupted,
kLoadGameOutcomeMissingVersion,
kLoadGameOutcomeInvalidVersion,
kLoadGameOutcomeSaveIsTooNew,
kLoadGameOutcomeSaveIsTooOld,
};
// State that is swapped when switching between characters in Schizm
struct SaveGameSwappableState {
struct InventoryItem {
InventoryItem();
uint itemID;
bool highlighted;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream);
};
struct Sound {
Sound();
Common::String name;
uint id;
int32 volume;
int32 balance;
bool is3D;
bool isLooping;
bool tryToLoopWhenRestarted;
bool isSpeech;
int32 x;
int32 y;
SoundParams3D params3D;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream, uint saveGameVersion);
};
SaveGameSwappableState();
uint roomNumber;
uint screenNumber;
uint direction;
uint disc;
bool havePendingPostSwapScreenReset;
uint loadedAnimation;
uint animDisplayingFrame;
bool haveIdleAnimationLoop;
uint idleAnimNum;
uint idleFirstFrame;
uint idleLastFrame;
int musicTrack;
Common::String scoreTrack;
Common::String scoreSection;
bool musicActive;
bool musicMuteDisabled;
int32 musicVolume;
int32 animVolume;
Common::Array inventory;
Common::Array sounds;
Common::Array randomAmbientSounds;
};
struct SaveGameSnapshot {
struct PagedInventoryItem {
PagedInventoryItem();
uint8 page;
uint8 slot;
uint8 itemID;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream, uint saveGameVersion);
};
struct PlacedInventoryItem {
PlacedInventoryItem();
uint32 locationID;
uint8 itemID;
void write(Common::WriteStream *stream) const;
void read(Common::ReadStream *stream, uint saveGameVersion);
};
SaveGameSnapshot();
void write(Common::WriteStream *stream) const;
LoadGameOutcome read(Common::ReadStream *stream);
static const uint kSaveGameIdentifier = 0x53566372;
static const uint kSaveGameCurrentVersion = 10;
static const uint kSaveGameEarliestSupportedVersion = 2;
static const uint kMaxStates = 2;
static Common::String safeReadString(Common::ReadStream *stream);
static void writeString(Common::WriteStream *stream, const Common::String &str);
uint hero;
uint swapOutRoom;
uint swapOutScreen;
uint swapOutDirection;
uint8 inventoryPage;
uint8 inventoryActiveItem;
uint numStates;
Common::SharedPtr states[kMaxStates];
bool escOn;
StaticAnimParams pendingStaticAnimParams;
SoundParams3D pendingSoundParams3D;
int32 listenerX;
int32 listenerY;
int32 listenerAngle;
Common::Array triggeredOneShots;
Common::HashMap sayCycles;
Common::HashMap variables;
Common::HashMap timers;
Common::Array pagedItems;
Common::Array placedItems;
};
enum OSEventType {
kOSEventTypeInvalid,
kOSEventTypeMouseMove,
kOSEventTypeLButtonDown,
kOSEventTypeLButtonUp,
kOSEventTypeKeymappedEvent,
};
enum KeymappedEvent {
kKeymappedEventNone,
kKeymappedEventEscape,
kKeymappedEventHelp,
kKeymappedEventSaveGame,
kKeymappedEventLoadGame,
kKeymappedEventSoundSettings,
kKeymappedEventQuit,
kKeymappedEventPause,
kKeymappedEventMusicToggle,
kKeymappedEventSoundToggle,
kKeymappedEventMusicVolumeDown,
kKeymappedEventMusicVolumeUp,
kKeymappedEventSoundVolumeDown,
kKeymappedEventSoundVolumeUp,
kKeymappedEventSkipAnimation,
kKeymappedEventPutItem,
};
struct OSEvent {
OSEvent();
OSEventType type;
Common::Point pos;
KeymappedEvent keymappedEvent;
uint32 timestamp;
};
struct TextStyleDef {
Common::String fontName;
uint size;
uint unknown1;
uint unknown2;
uint unknown3; // Seems to always be 0 for English, other values for other languages
uint colorRGB;
uint shadowColorRGB;
uint alignment; // Modulo 10 seems to be alignment: 0 = left, 1 = center, 2 = right
uint unknown5; // Possibly drop shadow offset
};
struct UILabelDef {
Common::String lineID;
Common::String styleDefID;
uint graphicLeft;
uint graphicTop;
uint graphicWidth;
uint graphicHeight;
};
struct FontCacheItem {
FontCacheItem();
Common::String fname;
uint size;
const Graphics::Font *font;
Common::SharedPtr keepAlive;
};
typedef Common::HashMap ScreenNameToRoomMap_t;
typedef Common::HashMap RoomToScreenNameToRoomMap_t;
struct AnimatedCursor {
struct FrameDef {
uint imageIndex;
uint delay;
};
Common::Array frames;
Common::Array images;
Common::Array > cursorKeepAlive;
Common::SharedPtr cursorGroupKeepAlive;
};
class Runtime {
public:
friend class RuntimeMenuInterface;
enum CharSet {
kCharSetLatin,
kCharSetGreek,
kCharSetCyrillic,
kCharSetJapanese,
kCharSetChineseTraditional,
kCharSetChineseSimplified,
};
Runtime(OSystem *system, Audio::Mixer *mixer, MidiDriver *midiDrv, const Common::FSNode &rootFSNode, VCruiseGameID gameID, Common::Language defaultLanguage);
virtual ~Runtime();
void initSections(const Common::Rect &gameRect, const Common::Rect &menuRect, const Common::Rect &trayRect, const Common::Rect &subtitleRect, const Common::Rect &fullscreenMenuRect, const Graphics::PixelFormat &pixFmt);
void loadCursors(const char *exeName);
void setDebugMode(bool debugMode);
void setFastAnimationMode(bool fastAnimationMode);
void setPreloadSounds(bool preloadSounds);
void setLowQualityGraphicsMode(bool lowQualityGraphicsMode);
bool runFrame();
void drawFrame();
void onLButtonDown(int16 x, int16 y);
void onLButtonUp(int16 x, int16 y);
void onMouseMove(int16 x, int16 y);
void onKeymappedEvent(KeymappedEvent evt);
bool canSave(bool onCurrentScreen) const;
bool canLoad() const;
void recordSaveGameSnapshot();
void recordSounds(SaveGameSwappableState &state);
void restoreSaveGameSnapshot();
Common::SharedPtr generateNewGameSnapshot() const;
void saveGame(Common::WriteStream *stream) const;
LoadGameOutcome loadGame(Common::ReadStream *stream);
bool bootGame(bool newGame);
static void resolveCodePageForLanguage(Common::Language lang, Common::CodePage &outCodePage, CharSet &outCharSet);
void drawLabel(Graphics::ManagedSurface *surface, const Common::String &labelID, const Common::Rect &contentRect);
void getLabelDef(const Common::String &labelID, const Graphics::Font *&outFont, const Common::String *&outTextUTF8, uint32 &outColor, uint32 &outShadowColor, uint32 &outShadowOffset);
void onMidiTimer();
private:
enum IndexParseType {
kIndexParseTypeNone,
kIndexParseTypeRoom,
kIndexParseTypeRRoom, // Rectangle room (constrains animation areas)
kIndexParseTypeYRoom, // Yes room (variable/ID mappings)
kIndexParseTypeVRoom, // Value room (value/ID mappings?)
kIndexParseTypeTRoom, // Text
kIndexParseTypeCRoom, // Const
kIndexParseTypeSRoom, // Sound
kIndexParseTypeNameRoom,
};
enum AnimDecoderState {
kAnimDecoderStateStopped,
kAnimDecoderStatePlaying,
kAnimDecoderStatePaused,
};
struct IndexPrefixTypePair {
const char *prefix;
IndexParseType parseType;
};
struct RenderSection {
Common::SharedPtr surf;
Common::Rect rect;
Graphics::PixelFormat pixFmt;
void init(const Common::Rect ¶mRect, const Graphics::PixelFormat &fmt);
};
struct Gyro {
static const uint kMaxPreviousStates = 3;
int32 currentState;
int32 requiredState;
int32 previousStates[kMaxPreviousStates];
int32 requiredPreviousStates[kMaxPreviousStates];
uint numPreviousStates;
uint numPreviousStatesRequired;
bool wrapAround;
bool requireState;
Gyro();
void reset();
void logState();
};
struct GyroState {
GyroState();
void reset();
static const uint kNumGyros = 5;
Gyro gyros[kNumGyros];
uint completeInteraction;
uint failureInteraction;
uint frameSeparation;
uint activeGyro;
uint dragMargin;
uint maxValue;
AnimationDef negAnim;
AnimationDef posAnim;
bool isVertical;
Common::Point dragBasePoint;
uint dragBaseState;
int32 dragCurrentState;
bool isWaitingForAnimation;
};
enum PanoramaCursorFlags {
kPanCursorDraggableHoriz = (1 << 0),
kPanCursorDraggableUp = (1 << 1),
kPanCursorDraggableDown = (1 << 2),
kPanCursorDirectionUp = (0 << 3),
kPanCursorDirectionLeft = (1 << 3),
kPanCursorDirectionRight = (2 << 3),
kPanCursorDirectionDown = (3 << 3),
kPanCursorMaxCount = (1 << 5),
};
enum PanoramaState {
kPanoramaStateInactive,
kPanoramaStatePanningUncertainDirection,
kPanoramaStatePanningLeft,
kPanoramaStatePanningRight,
kPanoramaStatePanningUp,
kPanoramaStatePanningDown,
};
enum InGameMenuState {
kInGameMenuStateInvisible,
kInGameMenuStateVisible,
kInGameMenuStateHoveringInactive,
kInGameMenuStateHoveringActive,
kInGameMenuStateClickingOver, // Mouse was pressed on a button and is holding on it
kInGameMenuStateClickingNotOver, // Mouse was pressed on a button and dragged off
kInGameMenuStateClickingInactive,
};
enum SoundLoopBehavior {
kSoundLoopBehaviorNo,
kSoundLoopBehaviorYes,
kSoundLoopBehaviorAuto,
};
static const uint kPanLeftInteraction = 1;
static const uint kPanDownInteraction = 2;
static const uint kPanRightInteraction = 3;
static const uint kPanUpInteraction = 4;
static const uint kPanoramaLeftFlag = 1;
static const uint kPanoramaRightFlag = 2;
static const uint kPanoramaUpFlag = 4;
static const uint kPanoramaDownFlag = 8;
static const uint kPanoramaHorizFlags = (kPanoramaLeftFlag | kPanoramaRightFlag);
static const uint kNumInventorySlots = 6;
static const uint kNumInventoryPages = 8;
typedef int32 ScriptArg_t;
typedef int32 StackInt_t;
struct StackValue {
enum StackValueType {
kNumber,
kString,
};
union ValueUnion {
StackInt_t i;
Common::String s;
ValueUnion();
explicit ValueUnion(StackInt_t iVal);
explicit ValueUnion(const Common::String &strVal);
explicit ValueUnion(Common::String &&strVal);
~ValueUnion();
};
StackValue();
StackValue(const StackValue &other);
StackValue(StackValue &&other);
explicit StackValue(StackInt_t i);
explicit StackValue(const Common::String &str);
explicit StackValue(Common::String &&str);
~StackValue();
StackValue &operator=(const StackValue &other);
StackValue &operator=(StackValue &&other);
StackValueType type;
ValueUnion value;
};
struct CallStackFrame {
CallStackFrame();
Common::SharedPtr