File indexing completed on 2024-04-28 04:02:14

0001 /*
0002     SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #ifndef KGRLEVELPLAYER_H
0008 #define KGRLEVELPLAYER_H
0009 
0010 #include "kgrglobals.h"
0011 
0012 #include <QList>
0013 #include <QObject>
0014 #include <QVarLengthArray>
0015 
0016 #include <QElapsedTimer> // IDW testing
0017 
0018 class KGrTimer;
0019 class KGrLevelGrid;
0020 class KGrRuleBook;
0021 class KGrView;
0022 class KGrHero;
0023 class KGrGame;
0024 class KGrEnemy;
0025 
0026 class QRandomGenerator;
0027 
0028 /**
0029  * @short Class to play, record and play back a level of a game
0030  *
0031  * This class constructs and plays a single level of a KGoldrunner game.  A
0032  * KGrLevelPlayer object is created as each level begins and is destroyed as
0033  * the level finishes, whether the human player wins the level or loses it.
0034  * Each level is either recorded as it is played or played back from an earlier
0035  * recording by emulating the same inputs as were used previously.
0036  *
0037  * The KGrLevelPlayer object in turn creates all the objects needed to play
0038  * the level, such as a hero, enemies, a play-area grid and a rule-book, which
0039  * are all derived from data in a KGrLevelData structure.  After the level and
0040  * all its objects are set up, most of the work is done by the private slot
0041  * tick(), including signals to update the graphics animation.  Tick() is
0042  * activated periodically by time-signals from the KGrTimer object, which also
0043  * handles the standard Pause action and variations in the overall speed of
0044  * KGoldrunner, from Beginner to Champion.  KGrLevelPlayer is controlled by
0045  * inputs from the mouse, keyboard or touchpad, which in turn control the
0046  * movement of the hero and the digging of bricks.  The enemies are controlled
0047  * by algorithms in the polymorphic KGrRuleBook object.  Each set of rules has
0048  * its own distinct algorithm.  In playback mode, the inputs are emulated.
0049  *
0050  * KGrLevelPlayer and friends are the internal model and game-engine of
0051  * KGoldrunner and they communicate with the view, KGrCanvas and friends,
0052  * solely via signals that indicate what is moving and what has to be painted.
0053  */
0054 
0055 class KGrLevelPlayer : public QObject
0056 {
0057     Q_OBJECT
0058 public:
0059     /**
0060      * The constructor of KGrLevelPlayer.
0061      *
0062      * @param parent     The object that owns the level-player and will destroy
0063      *                   it if the KGoldrunner application is terminated during
0064      *                   play.
0065      * @param pRandomGen A shared source of random numbers for all enemies.
0066      */
0067     KGrLevelPlayer             (KGrGame * parent, QRandomGenerator * pRandomGen);
0068     ~KGrLevelPlayer() override;
0069 
0070     /**
0071      * The main initialisation of KGrLevelPlayer.  This method establishes the
0072      * playing rules to be used, creates the internal playing-grid, creates the
0073      * hero and enemies and connects the various signals and slots together.  It
0074      * also initialises the recording or playback of moves made by the hero and
0075      * enemies.  Note that this is the only place where KGrLevelPlayer uses the
0076      * view directly.  All other references are via signals and slots.
0077      *
0078      *
0079      * @param view       Points to the KGrCanvas object that provides graphics.
0080      * @param pRecording Points to a data-object that contains all the data for
0081      *                   the level, including the layout of the maze and the
0082      *                   starting positions of hero, enemies and gold, plus the
0083      *                   variant of the rules to be followed: Traditional,
0084      *                   KGoldrunner or Scavenger.  The level is either recorded
0085      *                   as it is played or re-played from an earlier recording.
0086      *                   If playing "live", the pRecording object has empty
0087      *                   buffers into which the level player will store moves.
0088      *                   If re-playing, the buffers contain hero's moves to be
0089      *                   played back and random numbers for the enemies to
0090      *                   re-use, so that all play can be faithfully reproduced,
0091      *                   even if the random-number generator's code changes.
0092      * @param pPlayback  If false, play "live" and record the play.  If true,
0093      *                   play back a previously recorded level.
0094      * @param gameFrozen If true, go into pause-mode when the level starts.
0095      */
0096     void init                   (KGrView *            view,
0097                                  KGrRecording *       pRecording,
0098                                  const bool           pPlayback,
0099                                  const bool           gameFrozen);
0100 
0101     /**
0102      * Indicate that setup is complete and the human player can start playing
0103      * at any time, by moving the pointer device or pressing a key.
0104      */
0105     void prepareToPlay          ();
0106 
0107     /**
0108      * Pause or resume the gameplay in this level.
0109      *
0110      * @param stop      If true, pause: if false, resume.
0111      */
0112     void pause                  (bool stop);
0113 
0114     /**
0115      * Stop playback of a recorded level and adjust the content of the recording
0116      * so that the user can continue playing and recording from that point, if
0117      * required, as in the Instant Replay or Replay Last Level actions.
0118      */
0119     void interruptPlayback();
0120 
0121     /**
0122      * If not in playback mode, add a code to the recording and kill the hero.
0123      */
0124     void killHero();
0125 
0126     /**
0127      * Change the input-mode during play.
0128      *
0129      * @param mode      The new input-mode to use to control the hero: mouse, 
0130      *                  keyboard or hybrid touchpad and keyboard mode.
0131      */
0132     void setControlMode  (const int mode);
0133 
0134     /**
0135      * Change the keyboard click/hold option during play.
0136      *
0137      * @param option    The new option for keyboard operation: either CLICK_KEY
0138      *                  to start running non-stop when a direction key is
0139      *                  clicked or HOLD_KEY to start when a key is pressed and
0140      *                  stop when it is released, with simultaneous key-holding
0141      *                  allowed.
0142      */
0143     void setHoldKeyOption  (const int option);
0144 
0145     /**
0146      * Set the overall speed of gameplay.
0147      *
0148      * @param timeScale Value 10 is for normal speed.  Range is 2 to 20.
0149      *                  5 is for beginner speed: 15 for champion speed.
0150      */
0151     void setTimeScale    (const int timeScale);
0152 
0153     /**
0154      * Set a point for the hero to aim at when using mouse or touchpad control.
0155      *
0156      * @param pointerI  The required column-number on the playing-grid (>=1).
0157      * @param pointerJ  The required row-number on the playing-grid (>=1).
0158      */
0159     void setTarget              (int pointerI, int pointerJ);
0160 
0161     /**
0162      * Set a direction for the hero to move or dig when using keyboard control.
0163      *
0164      * @param dirn      The required direction (values defined by enum Direction
0165      *                  in file kgrglobals.h).
0166      * @param pressed   Tells whether the direction-key was pressed or released.
0167      */
0168     void setDirectionByKey      (const Direction dirn, const bool pressed);
0169 
0170     /**
0171      * Helper function for the hero to find his next direction when using mouse
0172      * or touchpad control.  Uses the point from setTarget() as a guide.
0173      *
0174      * @param heroI     The column-number where the hero is now (>=1).
0175      * @param heroJ     The row-number where the hero is now (>=1).
0176      *
0177      * @return          The required direction (values defined by enum Direction
0178      *                  in file kgrglobals.h).
0179      */
0180     Direction getDirection      (int heroI, int heroJ);
0181 
0182     /**
0183      * Helper function for an enemy to find his next direction, based on where
0184      * the hero is and the search algorithm implemented in the level's rules.
0185      *
0186      * @param enemyI    The column-number where the enemy is now (>=1).
0187      * @param enemyJ    The row-number where the enemy is now (>=1).
0188      * @param leftRightSearch The search-direction (for KGoldrunner rules only).
0189      *
0190      * @return          The required direction (values defined by enum Direction
0191      *                  in file kgrglobals.h).
0192      */
0193     Direction getEnemyDirection (int enemyI, int enemyJ, bool leftRightSearch);
0194 
0195     /**
0196      * Helper function for an enemy to pick up or drop gold or the hero to
0197      * collect gold.  Records the presence or absence of the gold on the
0198      * internal grid and on the screen.  Also pops up the hidden ladders (if
0199      * any) when there is no gold left.
0200      *
0201      * @param spriteId  The identifier of the hero or enemy.
0202      * @param i         The column-number where the gold is (>=1).
0203      * @param j         The row-number where the gold is (>=1).
0204      * @param hasGold   True if gold was picked up: false if it was dropped.
0205      * @param lost      True if gold is lost.
0206      *
0207      * @return          The number of pieces of gold remaining in this level.
0208      */
0209     int  runnerGotGold          (const int  spriteId, const int i, const int j,
0210                                  const bool hasGold, const bool lost = false);
0211 
0212     /**
0213      * Helper function to determine whether the hero has collided with an enemy
0214      * and must lose a life (unless he is standing on the enemy's head).
0215      *
0216      * @param heroX     The X grid-position of the hero (within a cell).
0217      * @param heroY     The Y grid-position of the hero (within a cell).
0218      *
0219      * @return          True if the hero is touching an enemy.
0220      */
0221     bool heroCaught             (const int heroX, const int heroY);
0222 
0223     /**
0224      * Helper function to determine whether the hero or an enemy is standing on
0225      * an enemy's head.
0226      *
0227      * @param spriteId  The identifier of the hero or enemy.
0228      * @param x         The X grid-position of the sprite (within a cell).
0229      * @param y         The Y grid-position of the sprite (within a cell).
0230      *
0231      * @return          Pointer to the enemy the sprite is standing on - or 0.
0232      */
0233     KGrEnemy * standOnEnemy     (const int spriteId, const int x, const int y);
0234 
0235     /**
0236      * Helper function to determine whether an enemy is colliding with another
0237      * enemy.  This to prevent enemies occupying the same cell, depending on
0238      * what the rules for this level allow.
0239      *
0240      * @param spriteId  The identifier of the enemy.
0241      * @param dirn      The direction in which the enemy wants to go.
0242      * @param gridI     The column-position of the enemy.
0243      * @param gridJ     The row-position of the enemy.
0244      *
0245      * @return          True if the enemy is too close to another enemy.
0246      */
0247     bool bumpingFriend          (const int spriteId, const Direction dirn,
0248                                  const int gridI,    const int gridJ);
0249 
0250     /**
0251      * Helper function to remove an enemy from among several stacked in a cell.
0252      *
0253      * @param spriteId  The identifier of the enemy.
0254      * @param gridI     The column-position of the enemy.
0255      * @param gridJ     The row-position of the enemy.
0256      * @param prevEnemy The previously stacked enemy in the same cell (or -1).
0257      */
0258     void unstackEnemy           (const int spriteId,
0259                                  const int gridI, const int gridJ,
0260                                  const int prevEnemy);
0261     /**
0262      * Helper function to determine where an enemy should reappear after being
0263      * trapped in a brick.  This applies with Traditional and Scavenger rules
0264      * only.  The procedure chooses a random place in row 2 or row 1.
0265      *
0266      * @param gridI     A randomly-chosen column (return by reference).
0267      * @param gridJ     Row 2 Traditional or 1 Scavenger (return by reference).
0268      */
0269     void enemyReappear          (int & gridI, int & gridJ);
0270 
0271     /**
0272      * Helper function to provide enemies with random numbers for reappearing
0273      * and deciding whether to pick up or drop gold.  The random bytes
0274      * generated are stored during recording and re-used during playback, so
0275      * that play is completely reproducible, even if the library's random number
0276      * generator behavior should vary across platforms or in future versions.
0277      *
0278      * @param limit     The upper limit for the number to be returned.
0279      *
0280      * @return          The random number, value >= 0 and < limit.
0281      */
0282     uchar randomByte            (const uchar limit);
0283 
0284     /**
0285      * Implement author's debugging aids, which are activated only if the level
0286      * is paused and the KConfig file contains group Debugging with setting
0287      * DebuggingShortcuts=true.  The main actions are to do timer steps one at
0288      * a time, activate/deactivate a bug-fix or new-feature patch dynamically,
0289      * activate/deactivate logging output from fprintf or qCDebug(KGOLDRUNNER_LOG) dynamically,
0290      * print the status of a cell pointed to by the mouse and print the status
0291      * of the hero or an enemy.  See the code in file kgoldrunner.cpp, at the
0292      * end of KGoldrunner::setupActions() for details of codes and keystrokes.
0293      *
0294      * To use the BUG_FIX or LOGGING options, first patch in and compile some
0295      * code to achieve the effect required, with tests of static bool flags
0296      * KGrGame::bugFix or KGrGame::logging surrounding that code.  The relevant
0297      * keystrokes then toggle those flags, so as to execute or skip the code
0298      * dynamically as the game runs.
0299      *
0300      * @param code      A code to indicate the action required (see enum
0301      *                  DebugCodes in file kgrglobals.h).
0302      */
0303     void dbgControl             (int code); // Authors' debugging aids.
0304 
0305 Q_SIGNALS:
0306     void endLevel       (const int result);
0307     void getMousePos    (int & i, int & j);
0308     void setMousePos    (const int i, const int j);
0309 
0310     /**
0311      * Requests the view to update animated sprites. Each sprite moves as
0312      * decided by the parameters of its last startAnimation() signal.
0313      *
0314      * @param missed       If true, moves and frame changes occur normally, but
0315      *                     they are not displayed on the screen. This is to
0316      *                     allow the graphics to catch up if Qt has missed one
0317      *                     or more time signals.
0318      */
0319     void animation      (bool missed);
0320 
0321     /**
0322      * Requests the view to display a particular type of tile at a particular
0323      * cell, or make it empty and show the background (tileType = FREE). Used
0324      * when loading level-layouts.
0325      *
0326      * @param i            The column-number of the cell to paint.
0327      * @param j            The row-number of the cell to paint.
0328      * @param tileType     The type of tile to paint (gold, brick, ladder, etc).
0329      */
0330     void paintCell      (int i, int j, char tileType);
0331 
0332     int  makeSprite     (char spriteType, int i, int j);
0333 
0334     /**
0335      * Requests the view to display an animation of a dug brick at a
0336      * particular cell, cancelling and superseding any current animation.
0337      *
0338      * @param spriteId     The ID of the sprite (dug brick).
0339      * @param repeating    If true, repeat the animation (false for dug brick).
0340      * @param i            The column-number of the cell to dig.
0341      * @param j            The row-number of the cell to dig.
0342      * @param time         The time in which to traverse one cell.
0343      * @param dirn         The direction of motion, always STAND.
0344      * @param type         The type of animation (open or close the brick).
0345      */
0346     void startAnimation (const int spriteId, const bool repeating,
0347                          const int i, const int j, const int time,
0348                          const Direction dirn, const AnimationType type);
0349 
0350     void deleteSprite   (const int spriteId);
0351     void gotGold        (const int  spriteId, const int i, const int j,
0352                          const bool hasGold, const bool lost);
0353     void interruptDemo  ();
0354 
0355 private Q_SLOTS:
0356     /**
0357      * This slot powers the whole game. KGrLevelPlayer connects it to KGrTimer's
0358      * tick() signal. In this slot, KGrLevelPlayer EITHER plays back a recorded
0359      * tick OR checks the mouse/trackpad/keyboard for user-input, then processes
0360      * dug bricks, moves the hero, moves the enemies and finally emits the
0361      * animation() signal, which causes the view to update the screen.
0362      *
0363      * @param missed       If true, the QTimer has missed one or more ticks, due
0364      *                     to overheads elsewhere in Qt or the O/S. The game
0365      *                     catches up on the missed signal(s) and the graphics
0366      *                     view avoids painting any sprites until the catchup
0367      *                     is complete, thus saving further overheads. The
0368      *                     sprites may "jump" a little when this happens, but
0369      *                     at least the game stays on-time in wall-clock time.
0370      * @param pScaledTime  The number of milliseconds per tick. Usually this is
0371      *                     tickTime (= 20 msec), but it is less when the game is
0372      *                     slowed down or more when it is speeded up. If the
0373      *                     scaled time is 10 (beginner speed), the game will
0374      *                     take 2 ticks of 20 msec (i.e. 40 msec) to do what it
0375      *                     normally does in 20 msec.
0376      */
0377     void tick           (bool missed, int scaledTime);
0378 
0379     void doDig          (int button);   // Dig using mouse-buttons.
0380 
0381 private:
0382     KGrGame *            game;
0383     QRandomGenerator *   randomGen;
0384     KGrLevelGrid *       grid;
0385     KGrRuleBook *        rules;
0386     KGrHero *            hero;
0387     int                  heroId;
0388     QList<KGrEnemy *>    enemies;
0389 
0390     int                  controlMode;
0391     int                  holdKeyOption;
0392     int                  levelWidth;
0393     int                  levelHeight;
0394 
0395     int                  nuggets;
0396 
0397     enum                 PlayState {NotReady, Ready, Playing};
0398     PlayState            playState;
0399 
0400     KGrRecording *       recording;
0401     bool                 playback;
0402     int                  recIndex;
0403     int                  recCount;
0404     int                  randIndex;
0405 
0406     int                  targetI;   // Where the mouse is pointing.
0407     int                  targetJ;
0408 
0409     Direction setDirectionByDelta (const int di, const int dj,
0410                                    const int heroI, const int heroJ);
0411     Direction            direction; // Direction for the hero to take.
0412     Direction            newDirection;  // Next direction for the hero to take.
0413     KGrTimer *           timer;     // The time-standard for the level.
0414 
0415     void startDigging (Direction diggingDirection);
0416     void processDugBricks (const int scaledTime);
0417 
0418     int  digCycleTime;          // Milliseconds per dig-timing cycle.
0419     int  digCycleCount;         // Number of cycles hole is fully open.
0420     int  digOpeningCycles;      // Cycles for brick-opening animation.
0421     int  digClosingCycles;      // Cycles for brick-closing animation.
0422     int  digKillingTime;        // Cycle when enemy/hero gets killed.
0423 
0424     int                  dX;        // X motion for KEYBOARD + HOLD_KEY.
0425     int                  dY;        // Y motion for KEYBOARD + HOLD_KEY.
0426 
0427     typedef struct {
0428         int  id;
0429         int  cycleTimeLeft;
0430         int  digI;
0431         int  digJ;
0432         int  countdown;
0433         qint64  startTime; // IDW testing
0434     } DugBrick;
0435 
0436     QList <DugBrick *> dugBricks;
0437 
0438     int          reappearIndex;
0439     QList<int>   reappearPos;
0440     void         makeReappearanceSequence();
0441     bool         doRecordedMove();
0442     void         recordInitialWaitTime (const int ms);
0443     void         record (const int bytes, const int n1, const int n2 = 0);
0444 
0445 /******************************************************************************/
0446 /**************************  AUTHORS' DEBUGGING AIDS **************************/
0447 /******************************************************************************/
0448 
0449     static int playerCount;
0450 
0451     void bugFix();      // Turn a bug fix on/off dynamically.
0452     void startLogging();    // Turn logging on/off.
0453     void showFigurePositions(); // Show everybody's co-ordinates.
0454     void showObjectState(); // Show an object's state.
0455     void showEnemyState (int);  // Show enemy's co-ordinates and state.
0456 
0457     /// TODO - Remove these ...
0458     QElapsedTimer t; // IDW testing
0459     int   T; // IDW testing
0460 };
0461 
0462 #endif // KGRLEVELPLAYER_H