File indexing completed on 2024-10-13 03:43:41
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