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/puzzlerecord.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.
33 class CardGamePuzzle : public PuzzleRecord {
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  // Header flags / dimensions
86  byte _unknown21 = 0;
87  byte _switchTurnRule = 0; // data+0x22: how the turn passes after a play
88  byte _startPlayer = 0; // data+0x23: which side plays first (also the human side)
89  byte _dealMode = 0; // data+0x24: deal/scoring variant (0, 2 or 0xff)
90  uint16 _numCols = 0; // data+0x25
91  uint16 _numRows = 0; // data+0x27
92  uint16 _dealRounds = 0; // data+0x29
93 
94  // Source/destination rects (all addressed [row * kMaxCols + col] or [side * kMaxCols + col])
95  Common::Rect _turnHighlightSrc[2]; // data+0x2b
96  Common::Rect _turnHighlightDest[2]; // data+0x4b
97  Common::Array<Common::Rect> _faceUpSrc; // data+0x6b, 4 rows
98  Common::Rect _suitScoreSrc; // data+0x47b
99  Common::Array<Common::Rect> _scoreDest; // data+0x48b, 2 sides
100  Common::Rect _cardDisplayDest[2]; // data+0x633
101  Common::Array<Common::Rect> _columnButtons; // data+0x653, one per column
102  Common::Array<Common::Rect> _faceDownSrc; // data+0x723, 3 rows
103 
104  // Animation timing (data+0x62b)
105  uint16 _moveAnimSteps = 0;
106  uint16 _moveAnimDelta = 0;
107  uint32 _moveAnimDelay = 0;
108 
109  // Automaton (Betty) variant: each turn a hand-delivery sprite is frame-cycled at a fixed slot,
110  // per side. Indexed by side (1 = player @0x993/0x99b/0xa8b, 0 = Betty @0xa9b/0xaa3/0xb93). The
111  // grid-vs-human variant leaves these empty (frame count 0), so the animation is simply skipped.
112  uint16 _dealFrameCount[2] = {};
113  uint32 _dealFrameDelay[2] = {};
114  Common::Array<Common::Rect> _dealFrames[2]; // hand-sprite frame src rects, by side
115  Common::Rect _deliverDest[2]; // where the hand sprite is drawn, by side
116 
117  // Outcome scenes (data+0x1304 / 0x1320). The win block has two scene ids that share one set of
118  // transition params (frame/scroll/sound), held in _solveScene; the id is chosen by the result.
119  uint16 _winSceneStartPlayer = 0; // data+0x1304
120  uint16 _winSceneStartEnemy = 0; // data+0x1306
121  int16 _winFlagPlayer = -1; // data+0x131c
122  int16 _winFlagEnemy = -1; // data+0x131e
123  bool _gaveUp = false; // left via the exit hotspot rather than playing out
124 
125  // Voiced lines / SFX (all on the card-game channel). Read selectively from the 0xba3..0x1304 block.
126  Common::String _moveVoiceName; // data+0xbc4 (card-move SFX)
127  Common::String _dealVoiceName; // data+0xbe5 (card-deal SFX)
128  Common::String _matchVoice[2][kMaxCols]; // the "do you have any X?" ask, by side; 0xc2b (AI) / 0xf68 (player)
129  Common::String _madeMoveVoice[2]; // the "here you go" answer, by side; 0xe7d (player) / 0x11ba (AI)
130  Common::String _noMoveVoice[2]; // the "go fish" answer, by side; 0xdd8 (player) / 0x1115 (AI)
131  Common::String _enemyScoredVoiceName; // data+0xee0 (a set completed for the AI)
132  Common::String _playerScoredVoiceName; // data+0x121d (a set completed for the player)
133  Common::String _endVoiceName[2]; // data+0xf22 (AI wins) / 0x125f (player wins)
134  SoundDescription _voiceSound;
135 
136  // At game over, the winner's line plays before the result scene transition.
137  bool _awaitingEnd = false;
138  uint32 _endWaitUntil = 0;
139 
140  // Turn state machine. The mover asks, the ask voice plays, the cards move and the answer voice
141  // plays, then the turn either repeats (a take) or passes. The AI's asks run through the same
142  // phases so its turn is visible (Nancy's cards being taken) and voiced.
143  enum Phase {
144  kWaitInput, // the player's turn: waiting for a card click
145  kAskSound, // a "do you have any X?" voice is playing
146  kAnswerSound, // an answer voice is playing and/or the taken cards are sliding
147  kAiDelay // a short pause before the AI's next ask
148  };
149  Phase _phase = kWaitInput;
150  int _mover = 1; // which side is currently asking (0 = AI/Jane, 1 = player/Nancy)
151  int _askedCol = -1; // the rank being asked for
152  bool _goAgain = false; // whether the mover asks again once the answer finishes
153  uint32 _aiDelayUntil = 0;
154 
155  // Runtime board state
156  PlayerBoard _board[2];
157  byte _availMap[kMaxRows][kMaxCols]; // shared deck: 1 = card still on the table
158  int _deckRemaining = 0;
159  int _currentTurn = 0; // side owning the turn highlight (mirrors _mover)
160  int _lastAiColumn = -1; // the AI avoids immediately repeating its previous column
161  bool _gameOver = false;
162 
163  // Slide animation for cards that changed hands on the last move (visual only; the board state
164  // is already up to date). Appeared cards slide into place, departed cards slide away.
165  static const int kSlidePerStep = 12;
166  bool _appearing[kMaxRows][kMaxCols] = {};
167  bool _leaving[kMaxRows][kMaxCols] = {};
168  int _animStep = 0;
169  uint32 _animNextStep = 0;
170  bool _animating = false;
171 
172  // The mover's hand-delivery sprite, cycled once per turn (automaton variant only).
173  bool _handAnimActive = false;
174  int _handAnimSide = 1;
175  uint16 _handFrame = 0;
176  uint32 _handNextFrame = 0;
177 };
178 
179 } // End of namespace Action
180 } // End of namespace Nancy
181 
182 #endif // NANCY_ACTION_CARDGAMEPUZZLE_H
Definition: str.h:59
Definition: cardgamepuzzle.h:33
Definition: rect.h:536
Definition: stream.h:745
Definition: input.h:41
Definition: cardgamepuzzle.h:54
Definition: rect.h:144
Definition: puzzlerecord.h:37
Definition: commontypes.h:306
Definition: actionmanager.h:32