ScummVM API documentation
cardgamepuzzle.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_CARDGAMEPUZZLE_H
23 #define NANCY_ACTION_CARDGAMEPUZZLE_H
24 
25 #include "engines/nancy/action/actionrecord.h"
26 
27 namespace Nancy {
28 namespace Action {
29 
30 // A "Go fish!" card game, new in Nancy 11 (Curse of Blackmoor Manor, AR 246).
31 // There are two variants, one with the cards placed in a grid vs a human NPC, and
32 // one with the cards placed in columns vs an automaton.
34 public:
36  virtual ~CardGamePuzzle() {}
37 
38  void init() override;
39  void updateGraphics() override;
40 
41  void readData(Common::SeekableReadStream &stream) override;
42  void execute() override;
43  void handleInput(NancyInput &input) override;
44 
45  bool isViewportRelative() const override { return true; }
46 
47 protected:
48  // Up to 13 columns / 4 rows are addressable in the data (per-row stride is 13 card rects).
49  static const int kMaxCols = 13;
50  static const int kMaxRows = 4;
51 
52  // Per-side board: a grid of dealt cards, per-column counts (capped at 3), per-column "complete"
53  // flags (a full column of 3 scores a set) and the running score.
54  struct PlayerBoard {
55  int grid[kMaxRows][kMaxCols];
56  int colCount[kMaxCols];
57  int colComplete[kMaxCols];
58  int score;
59  };
60 
61  Common::String getRecordTypeName() const override { return "CardGamePuzzle"; }
62 
63  int dealOne(int player); // draw a card from the deck to a side; returns its column, or -1 if the deck is empty
64  void drawBoard();
65  // True when the scene has a bottom button row (the player clicks those); false when it doesn't,
66  // in which case the player clicks their own cards in the tableau directly.
67  bool usesColumnButtons() const { return _columnButtons[0].top != _columnButtons[0].bottom; }
68  // The column the player is pointing at (owning 1-2 cards in it), or -1 if none is under the mouse.
69  int columnUnderMouse(const Common::Point &mousePos) const;
70  bool hasPlayableColumn(int side) const; // whether a side holds any incomplete rank it can ask for
71  int aiPickColumn(); // the AI's chosen rank to ask for, or -1 if it holds no incomplete rank
72 
73  // Go Fish turn flow, driven as a small state machine so asks and answers are voiced and sequenced.
74  bool takeCards(int side, int col); // transfer every opponent card of a rank to the side; true if any moved
75  void beginAsk(int side, int col); // start an ask: play the "do you have any X?" line, enter kAskSound
76  void resolveAsk(); // ask voice done: take or go fish, play the answer, start its wait
77  void advanceTurn(); // answer done: ask again, or pass the turn to the other side
78  void startPlayerTurn(); // enter kWaitInput (or auto-go-fish + pass if no rank to ask)
79  void startAiAsk(); // the AI picks a rank and asks, or goes fish and passes
80  void endGame();
81  // Compare side 1's grid against a pre-move snapshot and start sliding the changed cards.
82  void startMoveAnimation(const bool beforeGrid[kMaxRows][kMaxCols]);
83  void playVoice(const Common::String &name); // play a voiced line / SFX on the card-game channel
84 
85  Common::Path _imageName;
86 
87  // Header flags / dimensions
88  byte _unknown21 = 0;
89  byte _switchTurnRule = 0; // data+0x22: how the turn passes after a play
90  byte _startPlayer = 0; // data+0x23: which side plays first (also the human side)
91  byte _dealMode = 0; // data+0x24: deal/scoring variant (0, 2 or 0xff)
92  uint16 _numCols = 0; // data+0x25
93  uint16 _numRows = 0; // data+0x27
94  uint16 _dealRounds = 0; // data+0x29
95 
96  // Source/destination rects (all addressed [row * kMaxCols + col] or [side * kMaxCols + col])
97  Common::Rect _turnHighlightSrc[2]; // data+0x2b
98  Common::Rect _turnHighlightDest[2]; // data+0x4b
99  Common::Array<Common::Rect> _faceUpSrc; // data+0x6b, 4 rows
100  Common::Rect _suitScoreSrc; // data+0x47b
101  Common::Array<Common::Rect> _scoreDest; // data+0x48b, 2 sides
102  Common::Rect _cardDisplayDest[2]; // data+0x633
103  Common::Array<Common::Rect> _columnButtons; // data+0x653, one per column
104  Common::Array<Common::Rect> _faceDownSrc; // data+0x723, 3 rows
105  Common::Rect _exitHotspot; // data+0x1336
106 
107  // Animation timing (data+0x62b)
108  uint16 _moveAnimSteps = 0;
109  uint16 _moveAnimDelta = 0;
110  uint32 _moveAnimDelay = 0;
111 
112  // Automaton (Betty) variant: each turn a hand-delivery sprite is frame-cycled at a fixed slot,
113  // per side. Indexed by side (1 = player @0x993/0x99b/0xa8b, 0 = Betty @0xa9b/0xaa3/0xb93). The
114  // grid-vs-human variant leaves these empty (frame count 0), so the animation is simply skipped.
115  uint16 _dealFrameCount[2] = {};
116  uint32 _dealFrameDelay[2] = {};
117  Common::Array<Common::Rect> _dealFrames[2]; // hand-sprite frame src rects, by side
118  Common::Rect _deliverDest[2]; // where the hand sprite is drawn, by side
119 
120  // Outcome scenes (data+0x1304 / 0x1320). The win block has two scene ids that share one set of
121  // transition params (frame/scroll/sound), held in _winScene; the id is chosen by the result.
122  uint16 _winSceneStartPlayer = 0; // data+0x1304
123  uint16 _winSceneStartEnemy = 0; // data+0x1306
124  SceneChangeDescription _winScene;
125  int16 _winFlagPlayer = -1; // data+0x131c
126  int16 _winFlagEnemy = -1; // data+0x131e
127  uint16 _exitScene = 0; // data+0x1320
128  SceneChangeDescription _exitSceneChange;
129  bool _gaveUp = false; // left via the exit hotspot rather than playing out
130 
131  // Voiced lines / SFX (all on the card-game channel). Read selectively from the 0xba3..0x1304 block.
132  Common::String _moveVoiceName; // data+0xbc4 (card-move SFX)
133  Common::String _dealVoiceName; // data+0xbe5 (card-deal SFX)
134  Common::String _matchVoice[2][kMaxCols]; // the "do you have any X?" ask, by side; 0xc2b (AI) / 0xf68 (player)
135  Common::String _madeMoveVoice[2]; // the "here you go" answer, by side; 0xe7d (player) / 0x11ba (AI)
136  Common::String _noMoveVoice[2]; // the "go fish" answer, by side; 0xdd8 (player) / 0x1115 (AI)
137  Common::String _enemyScoredVoiceName; // data+0xee0 (a set completed for the AI)
138  Common::String _playerScoredVoiceName; // data+0x121d (a set completed for the player)
139  Common::String _endVoiceName[2]; // data+0xf22 (AI wins) / 0x125f (player wins)
140  SoundDescription _voiceSound;
141 
142  // At game over, the winner's line plays before the result scene transition.
143  bool _awaitingEnd = false;
144  uint32 _endWaitUntil = 0;
145 
146  // Turn state machine. The mover asks, the ask voice plays, the cards move and the answer voice
147  // plays, then the turn either repeats (a take) or passes. The AI's asks run through the same
148  // phases so its turn is visible (Nancy's cards being taken) and voiced.
149  enum Phase {
150  kWaitInput, // the player's turn: waiting for a card click
151  kAskSound, // a "do you have any X?" voice is playing
152  kAnswerSound, // an answer voice is playing and/or the taken cards are sliding
153  kAiDelay // a short pause before the AI's next ask
154  };
155  Phase _phase = kWaitInput;
156  int _mover = 1; // which side is currently asking (0 = AI/Jane, 1 = player/Nancy)
157  int _askedCol = -1; // the rank being asked for
158  bool _goAgain = false; // whether the mover asks again once the answer finishes
159  uint32 _aiDelayUntil = 0;
160 
161  // Runtime board state
163  PlayerBoard _board[2];
164  byte _availMap[kMaxRows][kMaxCols]; // shared deck: 1 = card still on the table
165  int _deckRemaining = 0;
166  int _currentTurn = 0; // side owning the turn highlight (mirrors _mover)
167  int _lastAiColumn = -1; // the AI avoids immediately repeating its previous column
168  bool _gameOver = false;
169 
170  // Slide animation for cards that changed hands on the last move (visual only; the board state
171  // is already up to date). Appeared cards slide into place, departed cards slide away.
172  static const int kSlidePerStep = 12;
173  bool _appearing[kMaxRows][kMaxCols] = {};
174  bool _leaving[kMaxRows][kMaxCols] = {};
175  int _animStep = 0;
176  uint32 _animNextStep = 0;
177  bool _animating = false;
178 
179  // The mover's hand-delivery sprite, cycled once per turn (automaton variant only).
180  bool _handAnimActive = false;
181  int _handAnimSide = 1;
182  uint16 _handFrame = 0;
183  uint32 _handNextFrame = 0;
184 };
185 
186 } // End of namespace Action
187 } // End of namespace Nancy
188 
189 #endif // NANCY_ACTION_CARDGAMEPUZZLE_H
Definition: managed_surface.h:51
Definition: str.h:59
Definition: cardgamepuzzle.h:33
Definition: commontypes.h:165
Definition: rect.h:536
Definition: path.h:52
Definition: stream.h:745
Definition: input.h:41
Definition: cardgamepuzzle.h:54
Definition: actionrecord.h:161
Definition: rect.h:144
Definition: commontypes.h:282
Definition: actionmanager.h:32