1 /*
2     SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #ifndef KGRLEVELPLAYER_H
8 #define KGRLEVELPLAYER_H
9 
10 #include "kgrglobals.h"
11 
12 #include <QList>
13 #include <QObject>
14 #include <QVarLengthArray>
15 
16 #include <QElapsedTimer> // IDW testing
17 
18 class KGrTimer;
19 class KGrLevelGrid;
20 class KGrRuleBook;
21 class KGrView;
22 class KGrHero;
23 class KGrGame;
24 class KGrEnemy;
25 
26 class QRandomGenerator;
27 
28 /**
29  * @short Class to play, record and play back a level of a game
30  *
31  * This class constructs and plays a single level of a KGoldrunner game.  A
32  * KGrLevelPlayer object is created as each level begins and is destroyed as
33  * the level finishes, whether the human player wins the level or loses it.
34  * Each level is either recorded as it is played or played back from an earlier
35  * recording by emulating the same inputs as were used previously.
36  *
37  * The KGrLevelPlayer object in turn creates all the objects needed to play
38  * the level, such as a hero, enemies, a play-area grid and a rule-book, which
39  * are all derived from data in a KGrLevelData structure.  After the level and
40  * all its objects are set up, most of the work is done by the private slot
41  * tick(), including signals to update the graphics animation.  Tick() is
42  * activated periodically by time-signals from the KGrTimer object, which also
43  * handles the standard Pause action and variations in the overall speed of
44  * KGoldrunner, from Beginner to Champion.  KGrLevelPlayer is controlled by
45  * inputs from the mouse, keyboard or touchpad, which in turn control the
46  * movement of the hero and the digging of bricks.  The enemies are controlled
47  * by algorithms in the polymorphic KGrRuleBook object.  Each set of rules has
48  * its own distinct algorithm.  In playback mode, the inputs are emulated.
49  *
50  * KGrLevelPlayer and friends are the internal model and game-engine of
51  * KGoldrunner and they communicate with the view, KGrCanvas and friends,
52  * solely via signals that indicate what is moving and what has to be painted.
53  */
54 
55 class KGrLevelPlayer : public QObject
56 {
57     Q_OBJECT
58 public:
59     /**
60      * The constructor of KGrLevelPlayer.
61      *
62      * @param parent     The object that owns the level-player and will destroy
63      *                   it if the KGoldrunner application is terminated during
64      *                   play.
65      * @param pRandomGen A shared source of random numbers for all enemies.
66      */
67     KGrLevelPlayer             (KGrGame * parent, QRandomGenerator * pRandomGen);
68     ~KGrLevelPlayer() override;
69 
70     /**
71      * The main initialisation of KGrLevelPlayer.  This method establishes the
72      * playing rules to be used, creates the internal playing-grid, creates the
73      * hero and enemies and connects the various signals and slots together.  It
74      * also initialises the recording or playback of moves made by the hero and
75      * enemies.  Note that this is the only place where KGrLevelPlayer uses the
76      * view directly.  All other references are via signals and slots.
77      *
78      *
79      * @param view       Points to the KGrCanvas object that provides graphics.
80      * @param pRecording Points to a data-object that contains all the data for
81      *                   the level, including the layout of the maze and the
82      *                   starting positions of hero, enemies and gold, plus the
83      *                   variant of the rules to be followed: Traditional,
84      *                   KGoldrunner or Scavenger.  The level is either recorded
85      *                   as it is played or re-played from an earlier recording.
86      *                   If playing "live", the pRecording object has empty
87      *                   buffers into which the level player will store moves.
88      *                   If re-playing, the buffers contain hero's moves to be
89      *                   played back and random numbers for the enemies to
90      *                   re-use, so that all play can be faithfully reproduced,
91      *                   even if the random-number generator's code changes.
92      * @param pPlayback  If false, play "live" and record the play.  If true,
93      *                   play back a previously recorded level.
94      * @param gameFrozen If true, go into pause-mode when the level starts.
95      */
96     void init                   (KGrView *            view,
97                                  KGrRecording *       pRecording,
98                                  const bool           pPlayback,
99                                  const bool           gameFrozen);
100 
101     /**
102      * Indicate that setup is complete and the human player can start playing
103      * at any time, by moving the pointer device or pressing a key.
104      */
105     void prepareToPlay          ();
106 
107     /**
108      * Pause or resume the gameplay in this level.
109      *
110      * @param stop      If true, pause: if false, resume.
111      */
112     void pause                  (bool stop);
113 
114     /**
115      * Stop playback of a recorded level and adjust the content of the recording
116      * so that the user can continue playing and recording from that point, if
117      * required, as in the Instant Replay or Replay Last Level actions.
118      */
119     void interruptPlayback();
120 
121     /**
122      * If not in playback mode, add a code to the recording and kill the hero.
123      */
124     void killHero();
125 
126     /**
127      * Change the input-mode during play.
128      *
129      * @param mode      The new input-mode to use to control the hero: mouse,
130      *                  keyboard or hybrid touchpad and keyboard mode.
131      */
132     void setControlMode  (const int mode);
133 
134     /**
135      * Change the keyboard click/hold option during play.
136      *
137      * @param option    The new option for keyboard operation: either CLICK_KEY
138      *                  to start running non-stop when a direction key is
139      *                  clicked or HOLD_KEY to start when a key is pressed and
140      *                  stop when it is released, with simultaneous key-holding
141      *                  allowed.
142      */
143     void setHoldKeyOption  (const int option);
144 
145     /**
146      * Set the overall speed of gameplay.
147      *
148      * @param timeScale Value 10 is for normal speed.  Range is 2 to 20.
149      *                  5 is for beginner speed: 15 for champion speed.
150      */
151     void setTimeScale    (const int timeScale);
152 
153     /**
154      * Set a point for the hero to aim at when using mouse or touchpad control.
155      *
156      * @param pointerI  The required column-number on the playing-grid (>=1).
157      * @param pointerJ  The required row-number on the playing-grid (>=1).
158      */
159     void setTarget              (int pointerI, int pointerJ);
160 
161     /**
162      * Set a direction for the hero to move or dig when using keyboard control.
163      *
164      * @param dirn      The required direction (values defined by enum Direction
165      *                  in file kgrglobals.h).
166      * @param pressed   Tells whether the direction-key was pressed or released.
167      */
168     void setDirectionByKey      (const Direction dirn, const bool pressed);
169 
170     /**
171      * Helper function for the hero to find his next direction when using mouse
172      * or touchpad control.  Uses the point from setTarget() as a guide.
173      *
174      * @param heroI     The column-number where the hero is now (>=1).
175      * @param heroJ     The row-number where the hero is now (>=1).
176      *
177      * @return          The required direction (values defined by enum Direction
178      *                  in file kgrglobals.h).
179      */
180     Direction getDirection      (int heroI, int heroJ);
181 
182     /**
183      * Helper function for an enemy to find his next direction, based on where
184      * the hero is and the search algorithm implemented in the level's rules.
185      *
186      * @param enemyI    The column-number where the enemy is now (>=1).
187      * @param enemyJ    The row-number where the enemy is now (>=1).
188      * @param leftRightSearch The search-direction (for KGoldrunner rules only).
189      *
190      * @return          The required direction (values defined by enum Direction
191      *                  in file kgrglobals.h).
192      */
193     Direction getEnemyDirection (int enemyI, int enemyJ, bool leftRightSearch);
194 
195     /**
196      * Helper function for an enemy to pick up or drop gold or the hero to
197      * collect gold.  Records the presence or absence of the gold on the
198      * internal grid and on the screen.  Also pops up the hidden ladders (if
199      * any) when there is no gold left.
200      *
201      * @param spriteId  The identifier of the hero or enemy.
202      * @param i         The column-number where the gold is (>=1).
203      * @param j         The row-number where the gold is (>=1).
204      * @param hasGold   True if gold was picked up: false if it was dropped.
205      * @param lost      True if gold is lost.
206      *
207      * @return          The number of pieces of gold remaining in this level.
208      */
209     int  runnerGotGold          (const int  spriteId, const int i, const int j,
210                                  const bool hasGold, const bool lost = false);
211 
212     /**
213      * Helper function to determine whether the hero has collided with an enemy
214      * and must lose a life (unless he is standing on the enemy's head).
215      *
216      * @param heroX     The X grid-position of the hero (within a cell).
217      * @param heroY     The Y grid-position of the hero (within a cell).
218      *
219      * @return          True if the hero is touching an enemy.
220      */
221     bool heroCaught             (const int heroX, const int heroY);
222 
223     /**
224      * Helper function to determine whether the hero or an enemy is standing on
225      * an enemy's head.
226      *
227      * @param spriteId  The identifier of the hero or enemy.
228      * @param x         The X grid-position of the sprite (within a cell).
229      * @param y         The Y grid-position of the sprite (within a cell).
230      *
231      * @return          Pointer to the enemy the sprite is standing on - or 0.
232      */
233     KGrEnemy * standOnEnemy     (const int spriteId, const int x, const int y);
234 
235     /**
236      * Helper function to determine whether an enemy is colliding with another
237      * enemy.  This to prevent enemies occupying the same cell, depending on
238      * what the rules for this level allow.
239      *
240      * @param spriteId  The identifier of the enemy.
241      * @param dirn      The direction in which the enemy wants to go.
242      * @param gridI     The column-position of the enemy.
243      * @param gridJ     The row-position of the enemy.
244      *
245      * @return          True if the enemy is too close to another enemy.
246      */
247     bool bumpingFriend          (const int spriteId, const Direction dirn,
248                                  const int gridI,    const int gridJ);
249 
250     /**
251      * Helper function to remove an enemy from among several stacked in a cell.
252      *
253      * @param spriteId  The identifier of the enemy.
254      * @param gridI     The column-position of the enemy.
255      * @param gridJ     The row-position of the enemy.
256      * @param prevEnemy The previously stacked enemy in the same cell (or -1).
257      */
258     void unstackEnemy           (const int spriteId,
259                                  const int gridI, const int gridJ,
260                                  const int prevEnemy);
261     /**
262      * Helper function to determine where an enemy should reappear after being
263      * trapped in a brick.  This applies with Traditional and Scavenger rules
264      * only.  The procedure chooses a random place in row 2 or row 1.
265      *
266      * @param gridI     A randomly-chosen column (return by reference).
267      * @param gridJ     Row 2 Traditional or 1 Scavenger (return by reference).
268      */
269     void enemyReappear          (int & gridI, int & gridJ);
270 
271     /**
272      * Helper function to provide enemies with random numbers for reappearing
273      * and deciding whether to pick up or drop gold.  The random bytes
274      * generated are stored during recording and re-used during playback, so
275      * that play is completely reproducible, even if the library's random number
276      * generator behavior should vary across platforms or in future versions.
277      *
278      * @param limit     The upper limit for the number to be returned.
279      *
280      * @return          The random number, value >= 0 and < limit.
281      */
282     uchar randomByte            (const uchar limit);
283 
284     /**
285      * Implement author's debugging aids, which are activated only if the level
286      * is paused and the KConfig file contains group Debugging with setting
287      * DebuggingShortcuts=true.  The main actions are to do timer steps one at
288      * a time, activate/deactivate a bug-fix or new-feature patch dynamically,
289      * activate/deactivate logging output from fprintf or qCDebug(KGOLDRUNNER_LOG) dynamically,
290      * print the status of a cell pointed to by the mouse and print the status
291      * of the hero or an enemy.  See the code in file kgoldrunner.cpp, at the
292      * end of KGoldrunner::setupActions() for details of codes and keystrokes.
293      *
294      * To use the BUG_FIX or LOGGING options, first patch in and compile some
295      * code to achieve the effect required, with tests of static bool flags
296      * KGrGame::bugFix or KGrGame::logging surrounding that code.  The relevant
297      * keystrokes then toggle those flags, so as to execute or skip the code
298      * dynamically as the game runs.
299      *
300      * @param code      A code to indicate the action required (see enum
301      *                  DebugCodes in file kgrglobals.h).
302      */
303     void dbgControl             (int code);	// Authors' debugging aids.
304 
305 Q_SIGNALS:
306     void endLevel       (const int result);
307     void getMousePos    (int & i, int & j);
308     void setMousePos    (const int i, const int j);
309 
310     /**
311      * Requests the view to update animated sprites. Each sprite moves as
312      * decided by the parameters of its last startAnimation() signal.
313      *
314      * @param missed       If true, moves and frame changes occur normally, but
315      *                     they are not displayed on the screen. This is to
316      *                     allow the graphics to catch up if Qt has missed one
317      *                     or more time signals.
318      */
319     void animation      (bool missed);
320 
321     /**
322      * Requests the view to display a particular type of tile at a particular
323      * cell, or make it empty and show the background (tileType = FREE). Used
324      * when loading level-layouts.
325      *
326      * @param i            The column-number of the cell to paint.
327      * @param j            The row-number of the cell to paint.
328      * @param tileType     The type of tile to paint (gold, brick, ladder, etc).
329      */
330     void paintCell      (int i, int j, char tileType);
331 
332     int  makeSprite     (char spriteType, int i, int j);
333 
334     /**
335      * Requests the view to display an animation of a dug brick at a
336      * particular cell, cancelling and superseding any current animation.
337      *
338      * @param spriteId     The ID of the sprite (dug brick).
339      * @param repeating    If true, repeat the animation (false for dug brick).
340      * @param i            The column-number of the cell to dig.
341      * @param j            The row-number of the cell to dig.
342      * @param time         The time in which to traverse one cell.
343      * @param dirn         The direction of motion, always STAND.
344      * @param type         The type of animation (open or close the brick).
345      */
346     void startAnimation (const int spriteId, const bool repeating,
347                          const int i, const int j, const int time,
348                          const Direction dirn, const AnimationType type);
349 
350     void deleteSprite   (const int spriteId);
351     void gotGold        (const int  spriteId, const int i, const int j,
352                          const bool hasGold, const bool lost);
353     void interruptDemo  ();
354 
355 private Q_SLOTS:
356     /**
357      * This slot powers the whole game. KGrLevelPlayer connects it to KGrTimer's
358      * tick() signal. In this slot, KGrLevelPlayer EITHER plays back a recorded
359      * tick OR checks the mouse/trackpad/keyboard for user-input, then processes
360      * dug bricks, moves the hero, moves the enemies and finally emits the
361      * animation() signal, which causes the view to update the screen.
362      *
363      * @param missed       If true, the QTimer has missed one or more ticks, due
364      *                     to overheads elsewhere in Qt or the O/S. The game
365      *                     catches up on the missed signal(s) and the graphics
366      *                     view avoids painting any sprites until the catchup
367      *                     is complete, thus saving further overheads. The
368      *                     sprites may "jump" a little when this happens, but
369      *                     at least the game stays on-time in wall-clock time.
370      * @param pScaledTime  The number of milliseconds per tick. Usually this is
371      *                     tickTime (= 20 msec), but it is less when the game is
372      *                     slowed down or more when it is speeded up. If the
373      *                     scaled time is 10 (beginner speed), the game will
374      *                     take 2 ticks of 20 msec (i.e. 40 msec) to do what it
375      *                     normally does in 20 msec.
376      */
377     void tick           (bool missed, int scaledTime);
378 
379     void doDig          (int button);	// Dig using mouse-buttons.
380 
381 private:
382     KGrGame *            game;
383     QRandomGenerator *   randomGen;
384     KGrLevelGrid *       grid;
385     KGrRuleBook *        rules;
386     KGrHero *            hero;
387     int                  heroId;
388     QList<KGrEnemy *>    enemies;
389 
390     int                  controlMode;
391     int                  holdKeyOption;
392     int                  levelWidth;
393     int                  levelHeight;
394 
395     int                  nuggets;
396 
397     enum                 PlayState {NotReady, Ready, Playing};
398     PlayState            playState;
399 
400     KGrRecording *       recording;
401     bool                 playback;
402     int                  recIndex;
403     int                  recCount;
404     int                  randIndex;
405 
406     int                  targetI;	// Where the mouse is pointing.
407     int                  targetJ;
408 
409     Direction setDirectionByDelta (const int di, const int dj,
410                                    const int heroI, const int heroJ);
411     Direction            direction;	// Direction for the hero to take.
412     Direction            newDirection;	// Next direction for the hero to take.
413     KGrTimer *           timer;		// The time-standard for the level.
414 
415     void startDigging (Direction diggingDirection);
416     void processDugBricks (const int scaledTime);
417 
418     int  digCycleTime;			// Milliseconds per dig-timing cycle.
419     int  digCycleCount;			// Number of cycles hole is fully open.
420     int  digOpeningCycles;		// Cycles for brick-opening animation.
421     int  digClosingCycles;		// Cycles for brick-closing animation.
422     int  digKillingTime;		// Cycle when enemy/hero gets killed.
423 
424     int                  dX;		// X motion for KEYBOARD + HOLD_KEY.
425     int                  dY;		// Y motion for KEYBOARD + HOLD_KEY.
426 
427     typedef struct {
428         int  id;
429         int  cycleTimeLeft;
430         int  digI;
431         int  digJ;
432         int  countdown;
433         qint64  startTime; // IDW testing
434     } DugBrick;
435 
436     QList <DugBrick *> dugBricks;
437 
438     int          reappearIndex;
439     QVector<int> reappearPos;
440     void         makeReappearanceSequence();
441     bool         doRecordedMove();
442     void         recordInitialWaitTime (const int ms);
443     void         record (const int bytes, const int n1, const int n2 = 0);
444 
445 /******************************************************************************/
446 /**************************  AUTHORS' DEBUGGING AIDS **************************/
447 /******************************************************************************/
448 
449     static int playerCount;
450 
451     void bugFix();		// Turn a bug fix on/off dynamically.
452     void startLogging();	// Turn logging on/off.
453     void showFigurePositions();	// Show everybody's co-ordinates.
454     void showObjectState();	// Show an object's state.
455     void showEnemyState (int);	// Show enemy's co-ordinates and state.
456 
457     /// TODO - Remove these ...
458     QElapsedTimer t; // IDW testing
459     int   T; // IDW testing
460 };
461 
462 #endif // KGRLEVELPLAYER_H
463