ScummVM API documentation
drivingpuzzle.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_DRIVINGPUZZLE_H
23 #define NANCY_ACTION_DRIVINGPUZZLE_H
24 
25 #include "engines/nancy/action/actionrecord.h"
26 #include "engines/nancy/commontypes.h"
27 #include "engines/nancy/action/navigationrecords.h"
28 #include "engines/nancy/action/actionzone.h"
29 
30 namespace Nancy {
31 namespace Action {
32 
33 // Real-time top-down driving minigames introduced in Nancy12. Two closely related
34 // action records share the same engine:
35 // 160 - kDriving (drive Nancy's car around the Titusville town map, entering
36 // locations by driving into them)
37 // 167 - kChase (kDriving plus a chaser car - Jane - that plays back a recorded path
38 // in real time; Nancy has to keep her in view. An event-flag-gated
39 // state machine drives the outcome: the win is the chaser completing
40 // its second path once Jane is caught; letting her drive off-view, or
41 // driving into a trigger zone, ends it otherwise)
42 //
43 // The map scrolls under a car-centered camera; the car is drawn as a rotation-atlas
44 // sprite whose frame is chosen from its heading. The map is populated with an
45 // ActionZone array: type 0x11 zones are location entrances (each carries the
46 // destination scene id and the transition effect), type 0x0d zones are cosmetic
47 // decorations (buildings, parked cars, potholes and animated cows/flags/fountains),
48 // and the rest are the driving hazards.
49 //
50 // Controls: the car steers to face the cursor; the left mouse button drives forward
51 // (the further from the car the cursor is, the faster) and the right button reverses.
52 // Fuel is a UI resource (index _frictionIndex) drained with the distance driven; potholes
53 // damage the tires; at 100 damage a tire blows and the car leaves for the flat-tire scene
54 // (blob+0x80). Mud slows the car; a location is entered by parking in its zone and pressing
55 // space. The car position, heading and accumulated tire damage persist across visits
56 // (DrivingData), like the original's retainState. The dashboard gas/tire gauges are not
57 // part of this record: they are the scene's own OverlayStaticTerse records gated by
58 // DT_RESOURCE dependencies on the fuel and tire UI resources.
59 //
60 // Collision uses the "...Collision" mask, whose white streets are drivable and dark
61 // areas are off-road; the type 0x14 boundary rects are only a fallback if it fails to load.
62 //
63 // TODO:
64 // - kChase: the "caught Jane" transition (state 1 -> 2, the win) is gated on an event
65 // flag the chase scene is expected to set (nothing in this record sets it); confirm
66 // what triggers it so a missed catch can't still win.
68 public:
69  enum Variant { kDriving = 0, kChase };
70 
71  DrivingPuzzle(Variant variant) : RenderActionRecord(7), _variant(variant) {}
72  virtual ~DrivingPuzzle() {}
73 
74  void init() override;
75 
76  void readData(Common::SeekableReadStream &stream) override;
77  void execute() override;
78  void handleInput(NancyInput &input) override;
79 
80  bool isViewportRelative() const override { return true; }
81 
82 protected:
83  Common::String getRecordTypeName() const override {
84  return _variant == kChase ? "ChasePuzzle" : "DrivingPuzzle";
85  }
86 
87  // A destination the car can drive into: a location entrance (type 0x11) or a drive-in
88  // scene trigger (type 0x0c, used in the chase). Entering its map-space rect optionally
89  // sets an event flag and transitions to the scene through a fade. Location entrances
90  // need a spacebar press to enter (you park first); the drive-in trigger (autoTrigger)
91  // fires the moment the car drives into it.
92  struct DestinationZone {
93  Common::Rect rect;
95  bool hasFade = false;
96  byte fadeType = 0;
97  uint16 fadeTotalTime = 0;
98  uint16 fadeToBlackTime = 0;
99  Common::Rect fadeRect;
100  int16 eventFlag = -1;
101  byte eventFlagValue = 0;
102  bool autoTrigger = false; // true for the chase finish (drive-in), false for parking
103  bool carInside = false; // the car was inside this zone last frame
104  };
105 
106  // Index into _chaseParams (167). The five values are the chase's outcome scenes and
107  // the event flags that gate its state machine.
108  enum ChaseParam {
109  kChaseGate01Flag = 0, // clears to advance from kPursuit to kShortcut
110  kChaseOffViewScene = 1, // scene entered when the chaser leaves the viewport
111  kChaseOffViewFlag = 2, // flag set (to 1) on the off-viewport outcome
112  kChaseGate12Flag = 3, // sets to advance from kShortcut to kCaught (second path)
113  kChasePathEndScene = 4 // scene entered when the chaser finishes its route
114  };
115 
116  // The chase runs through three phases, driven by checkpoint flags:
117  enum ChaseState {
118  kPursuit = 0, // Nancy must keep Jane in sight; losing her off-screen is a loss
119  kShortcut = 1, // Nancy lets Jane go and races her to the state line by another road
120  kCaught = 2 // Jane is caught and plays her crash sequence on the second path
121  };
122 
123  // A checkpoint (type 0x0b): driving into it sets an event flag, but only while its own
124  // base condition holds. The chase sequences its phases this way - one checkpoint clears
125  // the pursuit gate (starting the shortcut), a later one (gated on that) sets the caught
126  // flag - so the condition must be honored, not just the rect.
127  struct Checkpoint {
128  Common::Rect rect;
129  int16 flagId = -1; // tail: the flag driven over sets
130  byte flagValue = 0; // tail: the value it sets
131  int16 condFlag = -1; // base zone val49: flag gating whether it can fire
132  byte condValue = 0; // base zone val4b: the value that arms it
133  bool wasActive = false; // (condition held AND car inside) last frame
134  };
135 
136  // A mud puddle (type 0x03): slows the car (adds to its velocity decay) while inside.
137  struct MudZone {
138  Common::Rect rect;
139  double decel = 0.0;
140  };
141 
142  // A pothole (type 0x17): driving into it damages the tires by a random amount in
143  // [minDamage, maxDamage].
144  struct Pothole {
145  Common::Rect rect;
146  int32 minDamage = 0;
147  int32 maxDamage = 0;
148  bool carInside = false; // the car was inside this zone last frame
149  };
150 
151  // A removable road obstacle (type 0x14): a cow (or similar) that blocks the car while
152  // its event-flag condition holds. Each has a matching 0x0d cow overlay gated on the same
153  // flag, so the sprite and the collision appear and vanish together with the story state.
154  struct Obstacle {
155  Common::Rect rect;
156  int16 condFlag = -1; // base zone val49: event flag gating the block
157  byte condValue = 0; // base zone val4b: the flag value that activates it
158  };
159 
160  // A cosmetic map decoration (type 0x0d): a sprite drawn onto the map at destRect.
161  // A single source rect is static; several are animation frames cycled over time.
162  // It is only visible while its event-flag condition holds (condFlag == -1 = always).
163  struct Overlay {
164  int imageIndex = -1;
166  Common::Rect destRect; // map space
167  int16 condFlag = -1; // base zone val49: event flag gating visibility
168  byte condValue = 0; // base zone val4b: the flag value that shows it
169  bool aboveCar = false; // layer 1 draws over the car (tall props), 0 under
170  };
171 
172  // A recorded chaser-path waypoint (kChase): the pursuer plays these back in real
173  // time, jumping to the entry whose timestamp the elapsed chase time has passed.
174  struct Waypoint {
175  uint32 timeMs = 0;
176  int16 x = 0;
177  int16 y = 0;
178  double heading = 0.0; // radians
179  };
180 
181  // Reads an int16-prefixed array of Rects (a rotation-frame table).
182  void readFrameRects(Common::SeekableReadStream &stream, Common::Array<Common::Rect> &out);
183 
184  // Reads an int16-prefixed array of chaser-path waypoints (16 bytes each).
185  void readWaypoints(Common::SeekableReadStream &stream, Common::Array<Waypoint> &out);
186 
187  // Reads the 130-byte PuzzleBase header blob: three filenames (map image, collision
188  // map, car sprite atlas) and the car's physics parameters.
189  void readBlob(Common::SeekableReadStream &stream);
190 
191  // Sorts an ActionZone array into its gameplay roles (destination, checkpoint and
192  // boundary zones), decoding the destination scenes and their transition effects.
193  void classifyZones(const Common::Array<ActionZone> &zones);
194 
195  // Plays one (randomly chosen) entry of a random-sound block.
196  void playSoundBlock(const RandomSoundBlock &block);
197 
198  // Arms a pending exit (applied in kActionTrigger): from a destination zone (keeping
199  // its fade), or from a raw scene id plus an optional event flag to set.
200  void armExit(const DestinationZone &dest);
201  void armExitScene(uint16 sceneID, int16 flag, byte flagValue);
202 
203  // Advances the car's velocity/position for one frame. Throttle is +1 forward, -1
204  // reverse, 0 coast; cursorDist (how far the cursor is from the car) sets the forward
205  // speed. The heading is set separately (steering toward the cursor).
206  void updatePhysics(int throttle, double cursorDist);
207 
208  // Top-left of the car-centered camera window into the map, clamped to its bounds.
209  Common::Point cameraOffset() const;
210 
211  // Persists the car's position/heading and tire state so it survives leaving the map
212  // (and saving). Only does anything when the header's retainState flag is set.
213  void saveState() const;
214 
215  // Refills the gas tank to the full amount from the UIRC boot chunk (the infinite-fuel cheat).
216  void refillFuel();
217 
218  // Clears the accumulated pothole wear and restores the spare tire to good (the fix-tire cheat).
219  void repairTire();
220 
221  // Whether a map-space point is off the road: off the map, or a non-white (dark)
222  // pixel in the collision mask (its white marks the drivable streets).
223  bool isWall(int px, int py) const;
224 
225  // Whether the car may not drive at this map-space point (off the road).
226  bool isBlocked(const Common::Point &p) const;
227 
228  // Advances the chaser along its recorded path (kChase) and slows the player's
229  // speed cap the closer the chaser gets.
230  void updateChaser();
231 
232  // Chooses a rotation-atlas frame from a heading.
233  uint frameIndexForHeading(double heading, uint frameCount) const;
234 
235  // Loads (and caches) a decoration sprite by name, returning its index into
236  // _overlayImages, or -1 on failure.
237  int overlayImageIndex(const Common::String &name);
238 
239  // Draws the map's cosmetic decorations (animated frames cycled over time), offset by
240  // the camera and clipped to the visible window.
241  void drawOverlays(const Common::Point &cam, bool aboveCar);
242 
243  // Redraws the scrolling map (car-centered camera) and the car sprite(s) on top.
244  void drawScene();
245 
246  Variant _variant;
247 
248  // Three filenames decoded from the header blob.
249  Common::Path _imageName; // visible town map ("MAP_Titusville")
250  Common::Path _collisionName; // collision mask ("MAP_TitusvilleCollision")
251  Common::Path _carSpriteName; // car rotation atlas ("MAP_Roadster_OVL")
252 
253  // Car physics parameters decoded from the header blob.
254  int32 _startX = 0; // blob+0x63: start position (map space)
255  int32 _startY = 0; // blob+0x67
256  int32 _startAngle = 0; // blob+0x6b: start heading, degrees
257  int32 _forwardSpeed = 0; // blob+0x6f: forward speed cap
258  int32 _reverseSpeed = 0; // blob+0x73
259  int16 _frictionIndex = 0; // blob+0x77: UIRC resource index for the fuel gauge
260  static const uint kTireResourceIndex = 2; // UIRC resource index for the tire gauge (1 = good)
261  int32 _distanceDivisor = 0; // blob+0x7b
262  bool _retainState = false; // blob+0x7f: resume from the saved position
263  uint16 _finishScene = kNoScene; // blob+0x80: the scene entered when a tire goes flat
264 
265  // Three random-sound blocks (tire blowout, horn, engine) and a rotation-frame
266  // rect table precede the ActionZone array.
267  RandomSoundBlock _soundBlocks[3];
268  Common::Array<Common::Rect> _frameRects;
270 
271  // kChase (167) extras: five id/scene values, a second (chaser) car sprite
272  // name, a second rotation-frame table, a second ActionZone array and two
273  // recorded chaser paths (a main route and a shorter one).
274  int16 _chaseParams[5] = {};
275  Common::Path _chaseCarImageName;
276  Common::Array<Common::Rect> _frameRects2;
278  Common::Array<Waypoint> _chaserPathA;
279  Common::Array<Waypoint> _chaserPathB;
280 
281  // ActionZone gameplay roles.
282  Common::Array<DestinationZone> _destinations; // types 0x11 / 0x0c (parking spaces)
283  Common::Array<Checkpoint> _checkpoints; // type 0x0b
284  Common::Array<MudZone> _mudZones; // type 0x03
285  Common::Array<Pothole> _potholes; // type 0x17
286  Common::Array<Overlay> _overlays; // type 0x0d (map decorations)
287  Common::Array<Obstacle> _obstacles; // type 0x14 (flag-gated road obstacles)
289  Common::Array<Common::String> _overlayImageNames;
290 
291  // Runtime state
292  double _carX = 0.0; // current car position (map space)
293  double _carY = 0.0;
294  double _carHeading = 0.0; // radians
295  double _carVelocity = 0.0; // pixels per second
296  double _speedCap = 0.0; // current forward speed cap (lowered as the chaser closes in)
297  uint32 _lastPhysicsMs = 0; // real time of the last physics step (frame-rate independence)
298  int _parkedDest = -1; // destination zone the car is currently parked in (-1 == none)
299 
300  // A pending exit to another scene (a location, the chase finish, or a chase outcome).
301  // Armed via armExit()/armExitScene(); applied and finished in the kActionTrigger state.
302  SceneChangeDescription _exitScene;
303  bool _exitHasFade = false;
304  byte _exitFadeType = 0;
305  uint16 _exitFadeTotalTime = 0;
306  uint16 _exitFadeToBlackTime = 0;
307  Common::Rect _exitFadeRect;
308  int16 _exitFlag = -1;
309  byte _exitFlagValue = 0;
310 
311  // Chase (167) state machine: 0 = following the first path, 1 = waiting to switch,
312  // 2 = following the second path.
313  ChaseState _chaseState = kPursuit;
314  bool _chaserOnPathB = false;
315 
316  // Fuel + tire hazards. Fuel is the gas-gauge UI resource (index _frictionIndex),
317  // drained as the car drives; the fractional part is accumulated here. Tire damage
318  // builds up from potholes; once it blows a tire the car leaves for the flat-tire
319  // scene while _flatTirePending waits for the blowout sound to finish.
320  double _fuelBurnAccum = 0.0;
321  int _tireDamage = 0;
322  bool _flatTirePending = false;
323  bool _infiniteFuel = false; // cheat: Ctrl+Shift+G tops the tank and stops it draining
324 
325  // Chaser (kChase) runtime state.
326  bool _chaseStarted = false;
327  uint32 _chaseStartTime = 0;
328  uint _chaserWaypoint = 0;
329  double _chaserX = 0.0;
330  double _chaserY = 0.0;
331  double _chaserHeading = 0.0;
332 
333  Graphics::ManagedSurface _image; // the town map
334  Graphics::ManagedSurface _carImage; // the player car rotation atlas
335  Graphics::ManagedSurface _chaseCarImage; // the chaser car rotation atlas
336  Graphics::ManagedSurface _collisionMask; // road/off-road mask ("MAP_TitusvilleCollision")
337 };
338 
339 } // End of namespace Action
340 } // End of namespace Nancy
341 
342 #endif // NANCY_ACTION_DRIVINGPUZZLE_H
Definition: managed_surface.h:51
Definition: drivingpuzzle.h:92
Definition: drivingpuzzle.h:154
Definition: drivingpuzzle.h:163
Definition: str.h:59
Definition: commontypes.h:165
Definition: drivingpuzzle.h:137
Definition: drivingpuzzle.h:144
Definition: rect.h:536
Definition: path.h:52
Definition: stream.h:745
Definition: input.h:41
Definition: actionrecord.h:161
Definition: commontypes.h:300
Definition: rect.h:144
Definition: drivingpuzzle.h:127
Definition: actionmanager.h:32
Definition: drivingpuzzle.h:67
Definition: drivingpuzzle.h:174