/* 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. * * Copyright 2020 Google * */ #ifndef HADESCH_VIDEOROOM_H #define HADESCH_VIDEOROOM_H #include "common/scummsys.h" #include "common/array.h" #include "audio/audiostream.h" #include "audio/mixer.h" #include "common/rect.h" #include "common/ptr.h" #include "hadesch/pod_file.h" #include "hadesch/pod_image.h" #include "common/hashmap.h" #include "common/str.h" #include "hadesch/enums.h" #include "hadesch/event.h" #include "hadesch/hotzone.h" #include "hadesch/table.h" #include "common/queue.h" namespace Video { class SmackerDecoder; } namespace Hadesch { class PodImage; class HadeschEngine; class TagFile; static const int kDefaultSpeed = 100; class Renderable { public: Renderable(Common::Array images); const PodImage &getFrame(int time); void startAnimation(int startms, int msperframe, bool loop, int first, int last); bool isAnimationFinished(int time); void selectFrame(int frame); int getAnimationFrameNum(int time); int getNumFrames() { return _images.size(); } private: int getLen(); Common::Array _images; int _msperframe; int _startms; int _first; int _last; bool _loop; }; class LayerId { public: LayerId(const Common::String &name) { _name = name; _idx = -1; } LayerId() { _idx = -1; } LayerId(const char *name) { _name = name; _idx = -1; } LayerId(const Common::String &name, int idx, const Common::String &qualifier) { _qualifier = qualifier; _name = name; _idx = idx; } Common::String getFilename() const { return _name; } Common::String getDebug() const; bool operator== (const LayerId &b) const; private: Common::String _name; int _idx; Common::String _qualifier; }; struct Animation { Audio::SoundHandle _soundHandle; LayerId _animName; EventHandlerWrapper _callbackEvent; bool _finished; bool _keepLastFrame; bool _skippable; int _subtitleID; }; class PlayAnimParams { public: static PlayAnimParams loop(); static PlayAnimParams keepLastFrame(); static PlayAnimParams disappear(); bool getKeepLastFrame(); bool isLoop(); int getSpeed(); int getFirstFrame(); int getLastFrame(); PlayAnimParams partial(int first, int last) const; PlayAnimParams speed(int msperframe) const; PlayAnimParams backwards() const; private: PlayAnimParams(bool loop, bool keepLastFrame); bool _loop; bool _keepLastFrame; int _firstFrame; int _lastFrame; int _msperframe; }; struct TranscribedSound { const char *soundName; const char *transcript; static TranscribedSound make(const char *s, const char *t) { TranscribedSound res; res.soundName = s; res.transcript = t; return res; } }; class VideoRoom { public: VideoRoom(const Common::String &dir, const Common::String &pod, const Common::String &assetMapFile); ~VideoRoom(); void nextFrame(Common::SharedPtr context, int time, bool stopVideo); uint getWidth(); uint getHeight(); int getCursor(); // Hotzones and mouse void setHotzoneEnabled(const Common::String &name, bool enabled); void enableHotzone(const Common::String &name); void disableHotzone(const Common::String &name); void pushHotZones(const Common::String &hotzoneFile, bool enable = true, Common::Point offset = Common::Point(0, 0)); void popHotZones(); void loadHotZones(const Common::String &hotzoneFile, bool enable = true, Common::Point offset = Common::Point(0, 0)); void computeHotZone(int time, Common::Point mousePos); Common::String getHotZone(); void setHotZoneOffset(const Common::String &name, Common::Point offset); Common::String mapClick(Common::Point mousePos); void enableMouse() { _mouseEnabled = true; } void disableMouse() { _mouseEnabled = false; } bool isMouseEnabled() { return _mouseEnabled; } int getCursorAnimationFrame(int time); // Animations and layers void setLayerEnabled(const LayerId &name, bool enabled); void setLayerParallax(const LayerId &name, int val); void setColorScale(const LayerId &name, int val); void setScale(const LayerId &name, int val); int getNumFrames(const LayerId &animName); // Main animation API void playAnimWithSpeech(const LayerId &animName, const TranscribedSound &sound, int zValue, PlayAnimParams params, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); void playAnimWithSFX(const LayerId &animName, const Common::String &soundName, int zValue, PlayAnimParams params, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); void playAnimWithMusic(const LayerId &animName, const Common::String &soundName, int zValue, PlayAnimParams params, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); void playAnim(const LayerId &animName, int zValue, PlayAnimParams params, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); void stopAnim(const LayerId &animName); // Like stopAnim but also remove layer altogether void purgeAnim(const LayerId &animName); bool isAnimationFinished(const LayerId &name, int time); void addStaticLayer(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0)); void selectFrame(const LayerId &name, int zValue, int val, Common::Point offset = Common::Point(0, 0)); bool doesLayerExist(const LayerId &name); PodImage getLayerFrame(const LayerId &name); int getAnimFrameNum(const LayerId &name); void dumpLayers(); // Convenience wrappers void playAnimLoop(const LayerId &animName, int zValue, Common::Point offset = Common::Point(0, 0)); void playAnimKeepLastFrame(const LayerId &animName, int zValue, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); // Videos void playVideo(const Common::String &name, int zValue, EventHandlerWrapper callbackEvent = EventHandlerWrapper(), Common::Point offset = Common::Point(0, 0)); void cancelVideo(); bool isVideoPlaying(); // Panning void panLeftAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper()); void panRightAnim(EventHandlerWrapper callbackEvent = EventHandlerWrapper()); void panRightInstant(); void setPannable(bool pannable); void setUserPanCallback(EventHandlerWrapper leftStart, EventHandlerWrapper leftEnd, EventHandlerWrapper rightStart, EventHandlerWrapper rightEnd); bool isPanLeft() { return _pan == 0; } bool isPanRight() { return _pan == 640; } // Hero belt void enableHeroBelt() { _heroBeltEnabled = true; } void disableHeroBelt() { _heroBeltEnabled = false; } bool isHeroBeltEnabled() { return _heroBeltEnabled; } // Font void renderString(const Common::String &font, const Common::U32String &str, Common::Point startPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter"); void renderStringCentered(const Common::String &font, const Common::U32String &str, Common::Point centerPos, int zVal, int fontDelta = 0, const Common::String &extraId = "letter"); void hideString(const Common::String &font, size_t maxLen, const Common::String &extraId = "letter"); int computeStringWidth(const Common::String &font, const Common::U32String &str, int fontDelta = 0); // Misc void playSFX(const Common::String &soundName, EventHandlerWrapper callbackEvent = EventHandlerWrapper()); void playMusic(const Common::String &soundName, EventHandlerWrapper callbackEvent = EventHandlerWrapper()); void playSFXLoop(const Common::String &soundName); void playMusicLoop(const Common::String &soundName); void playSpeech(const TranscribedSound &sound, EventHandlerWrapper callbackEvent = EventHandlerWrapper()); void playStatueSMK(StatueId id, const LayerId &animName, int zValue, const Common::Array &smkNames, int startOfLoop, int startOfEnd, Common::Point offset = Common::Point(0, 0)); Common::SeekableReadStream *openFile(const Common::String &name); void fadeOut(int ms, const EventHandlerWrapper &callback); void resetFade(); void resetLayers(); void drag(const Common::String &name, int frame, Common::Point hotspot); PodImage *getDragged(); void clearDrag(); void pause(); void unpause(); void finish(); void cancelAllSubtitles(); void setViewportOffset(Common::Point vp) { _viewportOffset = vp; } private: struct Layer { Common::SharedPtr renderable; LayerId name; Common::Point offset; bool isEnabled; int genCounter; int zValue; int parallax; int colorScale; // From 0 to 0x100 int scale; // From 0 to 100 }; struct SubtitleLine { Common::U32String line; int32 maxTime; int ID; }; void playAnimWithSoundInternal(const LayerId &animName, const Common::String &soundName, Audio::Mixer::SoundType soundType, int zValue, PlayAnimParams params, EventHandlerWrapper callbackEvent, Common::Point offset, int subID = -1); void playSubtitles(const char *text, int subID); void addLayer(Renderable *renderable, const LayerId &name, int zValue, bool isEnabled = true, Common::Point offset = Common::Point(0, 0)); void startAnimationInternal(const LayerId &name, int zValue, int msperframe, bool loop, bool fixedFrame, int first, int last, Common::Point offset); Audio::RewindableAudioStream *getAudioStream(const Common::String &soundName); Common::String mapAsset(const Common::String &name); Common::String mapAsset(const LayerId &name); void addAnimLayerInternal(const LayerId &name, int zValue, Common::Point offset = Common::Point(0, 0)); void playSoundInternal(const Common::String &soundName, EventHandlerWrapper callbackEvent, bool loop, bool skippable, Audio::Mixer::SoundType soundType, int subtitleID = -1); static int layerComparator (const Layer &a, const Layer &b); void loadFontWidth(const Common::String &font); uint _videoW, _videoH; Common::Point _videoOffset, _videoSurfOffset; Common::SharedPtr _videoPixels; byte _videoPalette[256 * 3]; HotZoneArray _hotZones; Common::Array _hotZoneStack; Common::SortedArray _layers; int _layerGenCounter; int _startHotTime; int _hotZone; int _cursor; // We need to keep cursor pointer valid for at // least one more frame. Hence use circular buffer PodImage _draggedImage[5]; int _draggingPtr; bool _isDragging; int _pan, _panSpeed; EventHandlerWrapper _panCallback; EventHandlerWrapper _userPanStartLeftCallback; EventHandlerWrapper _userPanStartRightCallback; EventHandlerWrapper _userPanEndLeftCallback; EventHandlerWrapper _userPanEndRightCallback; bool _pannable; bool _leftEdge; bool _rightEdge; bool _heroBeltEnabled; int _edgeStartTime; Common::String _smkPath; Common::String _podPath; Common::SharedPtr _videoDecoder; Common::Point _viewportOffset; Common::Array _anims; EventHandlerWrapper _videoDecoderEndEvent; int _videoZ; Common::SharedPtr _podFile; Common::HashMap > _fontWidths; Common::Queue _subtitles; Common::HashMap _countQueuedSubtitles; TextTable _assetMap; bool _mouseEnabled; int _finalFade, _finalFadeSpeed; EventHandlerWrapper _finalFadeCallback; }; static const int kVideoWidth = 640; static const int kVideoHeight = 480; #define kOffsetRightRoom (Common::Point(kVideoWidth, 0)) #define kZeroPoint (Common::Point(10, 50)) struct PrePoint { int x, y; Common::Point get() const { return Common::Point(x, y); } }; } #endif