ScummVM API documentation
secondarymovie.h
1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11 
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU General Public License for more details.
16 
17  * You should have received a copy of the GNU General Public License
18  * along with this program. If not, see <http://www.gnu.org/licenses/>.
19  *
20  */
21 
22 #ifndef NANCY_ACTION_SECONDARYMOVIE_H
23 #define NANCY_ACTION_SECONDARYMOVIE_H
24 
25 #include "common/ptr.h"
26 
27 #include "engines/nancy/action/actionrecord.h"
28 #include "engines/nancy/movieplayer.h"
29 
30 namespace Nancy {
31 namespace Action {
32 
33 class InteractiveVideo;
34 
35 // Plays an AVF or Bink video. Optionally supports:
36 // - playing a sound;
37 // - reverse playback;
38 // - moving with the scene's background frame;
39 // - hiding of player cursor (and thus, disabling input);
40 // - setting event flags on a specific frame, as well as at the end of the video;
41 // - changing the scene after playback ends
42 // Mostly used for cinematics, with some occasional uses for background animations.
43 //
44 // Construct with `isRandom = true` for Nancy 11's AT_PLAY_RANDOM_MOVIE (AR 45):
45 // the chunk holds a list of sequences and one is picked at readData() time.
47 public:
48  static const byte kMovieSceneChange = 5;
49  static const byte kMovieNoSceneChange = 6;
50 
51  static const byte kPlayerCursorAllowed = 1;
52  static const byte kNoPlayerCursorAllowed = 2;
53 
54  static const byte kPlayMovieForward = 1;
55  static const byte kPlayMovieReverse = 2;
56 
57  struct FlagAtFrame {
58  int16 frameID;
59  FlagDescription flagDesc;
60  };
61 
62  // Name of the next sequence to chain to once the current one finishes,
63  // plus its selection weight in the weighted random pick.
64  struct NextSequenceRef {
65  Common::Path name;
66  uint16 weight = 0;
67  };
68 
69  // `name` is both the sequence id and the movie filename.
70  struct RandomSequence {
71  Common::Path name;
72  uint16 startFrame = 0;
73  uint16 lastFrame = 0;
74  int32 minPauseMs = 0;
75  int32 maxPauseMs = 0;
76  // Weight assigned to "stay on this sequence" in the weighted random
77  // pick. A roll inside [0, stayWeight) means "don't transition";
78  // instead pause for [minPauseMs, maxPauseMs] and re-roll.
79  uint16 stayWeight = 0;
80  Common::Array<NextSequenceRef> nextSequences;
81  };
82 
83  PlaySecondaryMovie(bool isRandom = false);
84  virtual ~PlaySecondaryMovie();
85 
86  void init() override;
87  void onPause(bool pause) override;
88 
89  void readData(Common::SeekableReadStream &stream) override;
90  void execute() override;
91 
92  bool getIsFinished() const { return _isFinished; }
93 
94  // Enhancement: jump a cinematic straight to its end, as if it had finished
95  // playing on its own. Only movies that hide the player cursor are skipped;
96  // the rest are background animations the player isn't waiting on.
97  void skip();
98 
99  Common::Path _videoName;
100  Common::Path _paletteName;
101  Common::Path _bitmapOverlayName;
102 
103  // Container the record asks for; only meaningful from Nancy7 to Nancy12.
104  byte _videoPlaytype = kVideoPlaytypeAuto;
105 
106  uint16 _videoFormat = kLargeVideoFormat;
107  uint16 _videoSceneChange = kMovieNoSceneChange;
108  byte _playerCursorAllowed = kPlayerCursorAllowed;
109  byte _playDirection = kPlayMovieForward;
110  uint16 _firstFrame = 0;
111  uint16 _lastFrame = 0;
112  // Nancy14-only: when non-zero, hide the movie once it reaches its last frame.
113  uint16 _hideOnFinish = 0;
114  // Nancy15 AR 44: a "play style" selector (1 or 3). Read but currently
115  // unused by playback.
116  uint16 _playStyle = 1;
117 
118  // AR 47 "InteractiveVideo" (a PlaySecondaryMovie subclass): after the
119  // normal AR-44-style movie data it carries a name, a flag byte, and a
120  // list of named {value, flag} entries. Read but not yet acted on.
122  Common::Path name;
123  uint32 value = 0;
124  byte flag = 0;
125  };
126  Common::Path _interactiveName;
127  bool _interactiveFlag = false;
128  Common::Array<InteractiveEntry> _interactiveEntries;
129  Common::Array<FlagAtFrame> _frameFlags;
130  MultiEventFlagDescription _triggerFlags;
131  FlagDescription _videoStartFlag;
132 
133  SoundDescription _sound;
134 
135  SceneChangeDescription _sceneChange;
137 
138  MoviePlayer _decoder;
139 
140  // Random-movie state (only populated when _isRandom).
141  bool _isRandom = false;
142  // "RandomMovie" picks any sequence; otherwise it names the starting one.
143  Common::String _startingSequenceName;
144  uint16 _randomPlayerCursorAllowed = kPlayerCursorAllowed;
146 
147  // Nancy13+ carries one extra "secondary" movie after the sequence list: the
148  // character's recognition animation, played while the mouse hovers it.
149  RandomSequence _secondaryMovie;
150 
151  // Nancy13 talkable characters: the scene to open when the character is
152  // clicked (its conversation). kNoScene means the character isn't clickable.
153  uint16 _talkSceneID = kNoScene;
154  // Hover cursor for the character (a raw Nancy13 cursor id from the chunk).
155  uint16 _talkCursorType = 0;
156 
157  // Chain state. After a sequence's movie finishes the engine rolls a
158  // weighted pick: "stay" -> enter pause for a random duration and
159  // re-roll; valid next-sequence -> swap to that sequence's movie.
160  enum RandomChainState { kRandomPlaying, kRandomPaused };
161  int _activeSequenceIndex = -1;
162  RandomChainState _randomChainState = kRandomPlaying;
163  uint32 _randomPauseEndTime = 0;
164  bool _randomStopRequested = false;
165 
166  // Talkable-character hover state: whether the mouse is over the character,
167  // and whether the recognition (secondary) movie is currently playing.
168  bool _isHovered = false;
169  bool _playingSecondary = false;
170 
171  // Rewind state for the recognition movie. When the mouse leaves the
172  // character the movie is played backwards to its first frame so the
173  // character turns away again instead of snapping back to its idle pose.
174  // Bink can't be played in reverse by the decoder, so the frames are
175  // stepped through by hand.
176  bool _secondaryRewinding = false;
177  int _rewindFrame = 0;
178  uint32 _rewindLastFrameTime = 0;
179  uint32 _rewindFrameDelay = 66;
180 
181  // Called by PlayRandomMovieControl::execute() to wind down the AR.
182  void stopRandom() { _randomStopRequested = true; }
183 
184  // Pick & start a fresh random sequence. No-op when not a random AR.
185  void playRandomSequence();
186 
187  bool isViewportRelative() const override { return true; }
188 
189  bool survivesSceneChange(bool nextSceneIsNoArt) const override;
190 
191  // Nancy13 talkable characters expose the character's on-screen box as a
192  // clickable hotspot with a talk cursor; clicking opens _talkSceneID, and
193  // hovering plays the recognition ("turn around") movie.
194  void handleInput(NancyInput &input) override;
195  CursorManager::CursorType getHoverCursor() const override;
196  bool cursorSetFromScript() const override { return _isRandom && _talkSceneID != kNoScene; }
197 
198  Common::String getRecordExtraInfo() const override { return Common::String::format("Scene %d", _sceneChange.sceneID); }
199 
200 protected:
201  Common::String getRecordTypeName() const override {
202  return _isRandom ? "PlayRandomMovie" : "PlaySecondaryMovie";
203  }
204 
205  // `ser` and `stream` must wrap the same input; `stream` is only
206  // needed for SecondaryVideoDescription::readData.
207  void readRandomMovieData(Common::Serializer &ser, Common::SeekableReadStream &stream);
208  // Nancy14 reworked the random-movie layout (confirmed identical in Nancy15):
209  // a larger header (shared with the non-random AR) and a tail of two
210  // blt-descriptor lists separated by the recognition movie's name, in place
211  // of Nancy13's secondaryMovie record + hotspot list.
212  void readRandomMovieDataNancy14(Common::Serializer &ser, Common::SeekableReadStream &stream);
213  void readRandomSequence(Common::Serializer &ser, RandomSequence &seq);
214  void readSecondaryRandomMovie(Common::Serializer &ser, RandomSequence &seq);
215 
216  // Shared tail of the random-movie readers: pick the starting sequence
217  // (random or by name) and seed the flat playback fields from it.
218  void applyStartingRandomSequence();
219 
220  void readDataNancy14(Common::Serializer &ser, Common::SeekableReadStream &stream);
221 
222  // Apply a RandomSequence's playback config to the PSM flat fields
223  // and reload the decoder. Returns true on success.
224  bool activateRandomSequence(int index);
225 
226  // Load & start the recognition (secondary) movie in place of the idle loop.
227  bool activateSecondaryMovie();
228 
229  // Start playing the recognition movie backwards from wherever it is now.
230  void beginSecondaryRewind();
231  // Advance the manual rewind; returns the frame to draw, or nullptr when
232  // the next frame isn't due yet.
233  const Graphics::Surface *updateSecondaryRewind();
234 
235  // A Nancy13 talkable character: has a conversation scene and a recognition
236  // movie to swap to on hover.
237  bool isTalkable() const { return _isRandom && _talkSceneID != kNoScene && !_secondaryMovie.name.empty(); }
238 
239  // Pick the next sequence (or "stay") per the weighted random rules.
240  // Returns -1 if "stay" was picked (and sets up the pause state),
241  // or the chosen sequence index otherwise.
242  int rollNextSequence();
243 
244  // Enter the paused chain state for a random duration in the sequence's
245  // [minPauseMs, maxPauseMs] range. Always returns -1.
246  int beginRandomPause(const RandomSequence &seq);
247 
248  // Find a sequence by name, warning and returning -1 if it isn't present.
249  int lookupSequence(const Common::Path &name) const;
250 
251  // Resolve the -1/-2 "whole movie" sentinels in _firstFrame/_lastFrame
252  // against the loaded decoder's frame count. Random sequences only.
253  void resolveSentinelFrames();
254 
255  Graphics::ManagedSurface _fullFrame;
256  int _curViewportFrame = -1;
257  bool _isFinished = false;
258 };
259 
260 // Companion AR for the random-movie variant of PlaySecondaryMovie. When
261 // executed it stops the currently-active random PlaySecondaryMovie and
262 // optionally performs a scene change / event-flag set.
264 public:
266 
267  void readData(Common::SeekableReadStream &stream) override;
268  void execute() override;
269 
270  enum RandomMovieControlMode : byte {
271  kStopNow = 0,
272  kStopAfterSequence = 1,
273  kResume = 2
274  };
275 
276 protected:
277  Common::String getRecordTypeName() const override { return "PlayRandomMovieControl"; }
278 
279  byte _mode = kStopNow;
280  SceneChangeWithFlag _sceneChange;
281 };
282 
283 } // End of namespace Action
284 } // End of namespace Nancy
285 
286 #endif // NANCY_ACTION_SECONDARYMOVIE_H
Definition: managed_surface.h:51
Definition: commontypes.h:213
Definition: str.h:59
Definition: surface.h:67
static String format(MSVC_PRINTF const char *fmt,...) GCC_PRINTF(1
Definition: commontypes.h:165
Definition: movieplayer.h:56
Definition: array.h:52
Definition: path.h:52
Definition: stream.h:745
Definition: secondarymovie.h:46
Definition: input.h:41
Definition: serializer.h:80
Definition: commontypes.h:185
Definition: actionrecord.h:161
Definition: secondarymovie.h:121
bool empty() const
Definition: path.h:353
Definition: secondarymovie.h:57
Definition: actionrecord.h:98
Definition: commontypes.h:282
Definition: secondarymovie.h:263
Definition: commontypes.h:180
Definition: actionmanager.h:32