kgrlevelplayer.cpp 26.8 KB
Newer Older
1 2
#include "kgrdebug.h"

3
/****************************************************************************
4
 *    Copyright 2009  Ian Wadham <iandw.au@gmail.com>                         *
5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
 *                                                                          *
 *    This program is free software; you can redistribute it and/or         *
 *    modify it under the terms of the GNU General Public License as        *
 *    published by the Free Software Foundation; either version 2 of        *
 *    the License, or (at your option) any later version.                   *
 *                                                                          *
 *    This program is distributed in the hope that it will be useful,       *
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *    GNU General Public License for more details.                          *
 *                                                                          *
 *    You should have received a copy of the GNU General Public License     *
 *    along with this program.  If not, see <http://www.gnu.org/licenses/>. *
 ****************************************************************************/

20 21
#include <stdio.h>
#include <stdlib.h>
22
#include <KDebug>
23
#include <KMessageBox>	// TODO - Remove.
24

25
#include <QTimer>
26
#include <QList>
27

28 29 30
// Include kgrgame.h only to access flags KGrGame::bugFix and KGrGame::logging.
#include "kgrgame.h"

31
#include "kgrcanvas.h"
32 33 34 35 36
#include "kgrlevelplayer.h"
#include "kgrrulebook.h"
#include "kgrlevelgrid.h"
#include "kgrrunner.h"

37
KGrLevelPlayer::KGrLevelPlayer (QObject * parent)
38
    :
39
    QObject          (parent),
40
    game             (parent),
41 42 43 44 45 46 47 48 49 50 51 52 53
    hero             (0),
    controlMode      (MOUSE),
    nuggets          (0),
    playState        (NotReady),
    targetI          (1),
    targetJ          (1),
    direction        (STAND),
    timer            (0),
    digCycleTime     (200),	// Milliseconds per dig-timing cycle (default).
    digCycleCount    (40),	// Cycles while hole is fully open (default).
    digOpeningCycles (5),	// Cycles for brick-opening animation.
    digClosingCycles (4),	// Cycles for brick-closing animation.
    digKillingTime   (2)	// Cycle at which enemy/hero gets killed.
54
{
55
    t.start(); // IDW
56 57
}

58 59
int KGrLevelPlayer::playerCount = 0;

60 61
KGrLevelPlayer::~KGrLevelPlayer()
{
62 63 64
    while (! dugBricks.isEmpty()) {
        delete dugBricks.takeFirst();
    }
65 66
    kDebug() << "LEVEL PLAYER BEING DELETED.";
    playerCount--;
67

68
    // TODO - Do we need this delete?
69 70 71 72 73
    // while (! enemies.isEmpty()) {
        // delete enemies.takeFirst();
    // }
}

74
void KGrLevelPlayer::init (KGrCanvas * view, const int mode,
75
                           const char rulesCode, const KGrLevelData * levelData)
76
{
77 78 79 80 81
    playerCount++;
    if (playerCount > 1) {
        KMessageBox::information (view, QString("ERROR: KGrLevelPlayer Count = %1").arg(playerCount), "KGrLevelPlayer");
    }

82
    // Create the internal model of the level-layout.
83 84
    grid            = new KGrLevelGrid (this, levelData);

85
    controlMode     = mode;		// Set mouse/keyboard/laptop control.
86 87
    levelWidth      = levelData->width;
    levelHeight     = levelData->height;
88 89

    reappearIndex   = levelWidth;	// Initialise the enemy-rebirth code.
90 91
    reappearPos.fill (1, levelWidth);

92
    // Set the rules of this game.
93
    switch (rulesCode) {
94 95 96 97 98 99 100 101 102 103
    case TraditionalRules:
        rules = new KGrTraditionalRules (this);
        break;
    case KGoldrunnerRules:
        rules = new KGrKGoldrunnerRules (this);
        break;
    case ScavengerRules:
        rules = new KGrScavengerRules (this);
        break;
    }
104
    // TODO - Remove. rules->printRules();
105

106
    view->setGoldEnemiesRule (rules->enemiesShowGold());
107 108

    // Determine the access for hero and enemies to and from each grid-cell.
Ian Wadham's avatar
Ian Wadham committed
109
    grid->calculateAccess    (rules->runThruHole());
110

111 112 113
    // Connect to code that paints grid cells and start-positions of sprites.
    connect (this, SIGNAL (paintCell (int, int, char, int)),
             view, SLOT   (paintCell (int, int, char, int)));
114 115
    connect (this, SIGNAL (makeSprite (char, int, int)),
             view, SLOT   (makeSprite (char, int, int)));
116

117
    // Connect to the mouse-positioning code in the graphics.
118 119
    connect (this, SIGNAL (getMousePos (int &, int &)),
             view, SLOT   (getMousePos (int &, int &)));
120 121 122
    connect (this, SIGNAL (setMousePos (const int, const int)),
             view, SLOT   (setMousePos (const int, const int)));

123 124
    // Show the layout of this level in the view (KGrCanvas).
    int wall = ConcreteWall;
125
    int enemyCount = 0;
126 127
    for (int j = wall ; j < levelHeight + wall; j++) {
        for (int i = wall; i < levelWidth + wall; i++) {
128 129 130 131 132 133 134 135 136 137 138 139
            char type = grid->cellType (i, j);

            // Hide false bricks.
            if (type == FBRICK) {
                type = BRICK;
            }

            // Count the gold in this level.
            if (type == NUGGET) {
                nuggets++;
            }

140
            // If the hero is here, leave the tile empty.
141 142
            if (type == HERO) {
                emit paintCell (i, j, FREE, 0);
143
            }
144

145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162
            // If an enemy is here, count him and leave the tile empty.
            else if (type == ENEMY) {
                enemyCount++;
                emit paintCell (i, j, FREE, 0);
            }

            // Or, just paint this tile.
            else {
                emit paintCell (i, j, type, 0);
            }
        }
    }

    // Set the timing rules, maybe based on the number of enemies.
    rules->setTiming (enemyCount);
    rules->getDigTimes (digCycleTime, digCycleCount);

    // Create the hero (always sprite 0), with the proper timing.
163 164
    for (int j = wall ; j < levelHeight + wall; j++) {
        for (int i = wall; i < levelWidth + wall; i++) {
165 166
            char type = grid->cellType (i, j);
            if (type == HERO) {
167
                if (hero == 0) {
168 169
                    targetI = i;
                    targetJ = j;
170 171
                    heroId  = emit makeSprite (HERO, i, j);
                    hero    = new KGrHero (this, grid, i, j, heroId, rules);
172
                    hero->setNuggets (nuggets);
173
                    // TODO - Iff mouse mode, setMousePos();
174 175
                    emit setMousePos (targetI, targetJ);
                    grid->changeCellAt (i, j, FREE);	// Hero now a sprite.
176 177
                }
            }
178 179
        }
    }
180

181
    // Create the enemies (sprites 1-n), with the proper timing.
182 183
    for (int j = wall ; j < levelHeight + wall; j++) {
        for (int i = wall; i < levelWidth + wall; i++) {
184 185
            char type = grid->cellType (i, j);
            if (type == ENEMY) {
186
                KGrEnemy * enemy;
187
                int id = emit makeSprite (ENEMY, i, j);
188
                enemy = new KGrEnemy (this, grid, i, j, id, rules);
189
                enemies.append (enemy);
190
                grid->changeCellAt (i, j, FREE);	// Enemy now a sprite.
191
                grid->setEnemyOccupied (i, j, id);
192 193 194
            }
        }
    }
195

196
    // Connect the hero's and enemies' efforts to the graphics.
197 198
    connect (this, SIGNAL (gotGold (int, int, int, bool, bool)),
             view, SLOT   (gotGold (int, int, int, bool, bool)));
199

Ian Wadham's avatar
Ian Wadham committed
200 201 202
    // Connect mouse-clicks from KGrCanvas to digging slot.
    connect (view, SIGNAL (mouseClick (int)), SLOT (doDig (int)));

203
    // Connect the hero and enemies (if any) to the animation code.
204
    connect (hero, SIGNAL (startAnimation (int, bool, int, int, int,
205
                                           Direction, AnimationType)),
206
             view, SLOT   (startAnimation (int, bool, int, int, int,
207 208
                                           Direction, AnimationType)));
    foreach (KGrEnemy * enemy, enemies) {
209
        connect (enemy, SIGNAL (startAnimation (int, bool, int, int, int,
210
                                                Direction, AnimationType)),
211
                 view,  SLOT   (startAnimation (int, bool, int, int, int,
212 213
                                                Direction, AnimationType)));
    }
214

215 216 217 218 219 220 221 222
    // Connect the scoring.
    connect (hero, SIGNAL (incScore (const int)),
             game, SLOT   (incScore (const int)));
    foreach (KGrEnemy * enemy, enemies) {
        connect (enemy, SIGNAL (incScore (const int)),
                 game,  SLOT   (incScore (const int)));
    }

223
    // Connect the level player to the animation code (for use with dug bricks).
224
    connect (this, SIGNAL (startAnimation (int, bool, int, int, int,
225
                                           Direction, AnimationType)),
226
             view, SLOT   (startAnimation (int, bool, int, int, int,
227
                                           Direction, AnimationType)));
228 229 230 231 232 233
    connect (this, SIGNAL (deleteSprite (int)),
             view, SLOT   (deleteSprite (int)));

    // Connect the grid to the view, to show hidden ladders when the time comes.
    connect (grid, SIGNAL (showHiddenLadders (const QList<int> &, const int)),
             view, SLOT   (showHiddenLadders (const QList<int> &, const int)));
234 235 236 237 238

    // Connect and start the timer.  The tick() slot emits signal animation(),
    // so there is just one time-source for the model and the view.

    timer = new KGrTimer (this, TickTime);	// TickTime def in kgrglobals.h.
239 240 241

    connect (timer, SIGNAL (tick (bool, int)), this, SLOT (tick (bool, int)));
    connect (this,  SIGNAL (animation (bool)), view, SLOT (animate (bool)));
242 243
}

244 245
void KGrLevelPlayer::startDigging (Direction diggingDirection)
{
Ian Wadham's avatar
Ian Wadham committed
246 247
    int digI = 1;
    int digJ = 1;
248

249
    // We need the hero to decide if he CAN dig and if so, where.
Ian Wadham's avatar
Ian Wadham committed
250
    if (hero->dig (diggingDirection, digI, digJ)) {
251
        // The hero can dig as requested: the chosen brick is at (digI, digJ).
Ian Wadham's avatar
Ian Wadham committed
252
        grid->changeCellAt (digI, digJ, HOLE);
253 254

        // Delete the brick-image so that animations are visible in all themes.
255
        // TODO - Remove. emit paintCell (digI, digJ, FREE, 0);
256

257
        // Start the brick-opening animation (non-repeating).
258
        int id = emit makeSprite (BRICK, digI, digJ);
259 260 261 262 263 264 265 266 267 268 269 270 271
        emit startAnimation (id, false, digI, digJ,
                        (digOpeningCycles * digCycleTime), STAND, OPEN_BRICK);

        DugBrick * thisBrick = new DugBrick;
        DugBrick   brick     = {id, digCycleTime, digI, digJ,
                    (digCycleCount + digOpeningCycles + digClosingCycles - 1),
                    t.elapsed()}; // IDW test
        (* thisBrick)        = brick;
        dugBricks.append (thisBrick);
        // kDebug() << "DIG" << thisBrick->id << thisBrick->countdown
                 // << "time" << (t.elapsed() - thisBrick->startTime);
    }
}
272

273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298
void KGrLevelPlayer::processDugBricks (const int scaledTime)
{
    DugBrick * dugBrick;
    QMutableListIterator<DugBrick *> iterator (dugBricks);

    while (iterator.hasNext()) {
        dugBrick = iterator.next();
        dugBrick->cycleTimeLeft -= scaledTime;
        if (dugBrick->cycleTimeLeft < scaledTime) {
            dugBrick->cycleTimeLeft += digCycleTime;
            if (--dugBrick->countdown == digClosingCycles) {
                // Start the brick-closing animation (non-repeating).
                // kDebug() << "Brick" << dugBrick->digI << dugBrick->digJ <<
                            // "count" << dugBrick->countdown;
                emit startAnimation (dugBrick->id, false,
                                     dugBrick->digI, dugBrick->digJ,
                                     (digClosingCycles * digCycleTime),
                                     STAND, CLOSE_BRICK);
            }
            if (dugBrick->countdown == digKillingTime) {
                // kDebug() << "Brick" << dugBrick->digI << dugBrick->digJ <<
                            // "count" << dugBrick->countdown;
                // Close the hole and maybe capture the hero or an enemy.
                grid->changeCellAt (dugBrick->digI, dugBrick->digJ, BRICK);
            }
            if (dugBrick->countdown <= 0) {
299 300
                // kDebug() << "DIG" << dugBrick->id << dugBrick->countdown
                         // << "time" << (t.elapsed() - dugBrick->startTime);
301 302 303 304 305 306 307 308 309 310
                // Dispose of the dug brick and remove it from the list.
                emit deleteSprite (dugBrick->id);
                // TODO - Remove. emit paintCell (dugBrick->digI, dugBrick->digJ, BRICK);
                delete dugBrick;
                iterator.remove();
            }
            // TODO - Hero gets hidden by dug-brick in the Egyptian theme.
            // TODO - Why do we get so many MISSED ticks when we dig?
            // TODO - Implement speed-variation as a parameter of tick().
        }
Ian Wadham's avatar
Ian Wadham committed
311 312 313
    }
}

314 315 316 317
void KGrLevelPlayer::prepareToPlay()
{
    // TODO - Should this be a signal?
    kDebug() << "Set mouse to:" << targetI << targetJ;
318
    emit setMousePos (targetI, targetJ);
319 320 321
    playState = Ready;
}

322 323 324 325 326 327 328 329 330 331
void KGrLevelPlayer::pause (bool stop)
{
    if (stop) {
        timer->pause();
    }
    else {
        timer->resume();
    }
}

332
void KGrLevelPlayer::setTarget (int pointerI, int pointerJ)
333
{
Ian Wadham's avatar
Ian Wadham committed
334
    // Mouse or other pointer device (eg. laptop touchpad) controls the hero.
335 336
    switch (playState) {
    case NotReady:
Ian Wadham's avatar
Ian Wadham committed
337
        // Ignore the pointer until KGrLevelPlayer is ready to start.
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353
        break;
    case Ready:
        // Wait until the human player is ready to start playing.
        if ((pointerI == targetI) && (pointerJ == targetJ)) {
            // The pointer is still over the hero: do not start playing yet.
            break;
        }
        else {
            // The pointer moved: fall into "case Playing:" and start playing.
            playState = Playing;
        }
    case Playing:
        // The human player is playing now.
        targetI = pointerI;
        targetJ = pointerJ;
        break;
354
    }
355 356
}

Ian Wadham's avatar
Ian Wadham committed
357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377
void KGrLevelPlayer::doDig (int button)
{
    // If not ready or game control is not by mouse, ignore mouse-clicks.
    if ((playState == NotReady) || (controlMode != MOUSE)) {
        return;
    }

    playState = Playing;
    switch (button) {
    case Qt::LeftButton:
        startDigging (DIG_LEFT);
        break;
    case Qt::RightButton:
        startDigging (DIG_RIGHT);
        break;
    default:
        break;
    }
}

void KGrLevelPlayer::setDirectionByKey (Direction dirn)
378
{
Ian Wadham's avatar
Ian Wadham committed
379 380 381 382 383 384 385 386 387 388 389 390 391 392 393
    // Keystrokes control the hero.
    if ((playState == NotReady) || (controlMode == MOUSE)) {
        return;
    }

    if ((dirn == DIG_LEFT) || (dirn == DIG_RIGHT)) {
	// Control mode is KEYBOARD or LAPTOP (hybrid: pointer + dig-keys).
        playState = Playing;
        direction = STAND;
        startDigging (dirn);
    }
    else if (controlMode == KEYBOARD) {
        playState = Playing;
        direction = dirn;
    }
394 395
}

396
Direction KGrLevelPlayer::getDirection (int heroI, int heroJ)
397
{
Ian Wadham's avatar
Ian Wadham committed
398
    if ((controlMode == MOUSE) || (controlMode == LAPTOP)) {
399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426
        // If using a pointer device, calculate the hero's next direction,
        // starting from last pointer position and hero's current position.

        int di = targetI - heroI;
        int dj = targetJ - heroJ;

        // kDebug() << "di" << di << "dj" << dj;

        if ((dj > 0) && (grid->heroMoves (heroI, heroJ) & dFlag [DOWN])) {
            // kDebug() << "Go down";
            direction = DOWN;
        }
        else if ((dj < 0) && (grid->heroMoves (heroI, heroJ) & dFlag [UP])) {
            // kDebug() << "Go up";
            direction = UP;
        }
        else if (di > 0) {
            // kDebug() << "Go right";
            direction = RIGHT;
        }
        else if (di < 0) {
            // kDebug() << "Go left";
            direction = LEFT;
        }
        else {		// Note: di is zero, but dj is not necessarily zero.
            // kDebug() << "Stand";
            direction = STAND;
        }
427
    }
428

429
    // kDebug() << "Hero at" << heroI << heroJ << "mouse at" << targetI << targetJ << "Direction" << direction;
430 431 432
    return direction;
}

433 434
Direction KGrLevelPlayer::getEnemyDirection (int  enemyI, int enemyJ,
                                             bool leftRightSearch)
435
{
436
    int heroX, heroY, pointsPerCell;
437

438 439 440
    pointsPerCell = hero->whereAreYou (heroX, heroY);
    return rules->findBestWay (enemyI, enemyJ,
                               heroX / pointsPerCell, heroY / pointsPerCell,
441
                               grid, leftRightSearch);
442 443 444 445 446 447 448
}

bool KGrLevelPlayer::heroCaught (const int heroX, const int heroY)
{
    if (enemies.count() == 0) {
        return false;
    }
449
    int enemyX, enemyY, pointsPerCell_1;
450
    foreach (KGrEnemy * enemy, enemies) {
451 452 453 454 455
        pointsPerCell_1 = enemy->whereAreYou (enemyX, enemyY) - 1;
        if (((heroX < enemyX) ? ((heroX + pointsPerCell_1) >= enemyX) :
                                 (heroX <= (enemyX + pointsPerCell_1))) &&
            ((heroY < enemyY) ? ((heroY + pointsPerCell_1) >= enemyY) :
                                 (heroY <= (enemyY + pointsPerCell_1)))) {
456 457 458 459 460 461 462 463 464 465 466 467
            return true;
        }
    }
    return false;
}

bool KGrLevelPlayer::standOnEnemy (const int spriteId, const int x, const int y)
{
    int minEnemies = (spriteId == heroId) ? 1 : 2;
    if (enemies.count() < minEnemies) {
        return false;
    }
468
    int enemyX, enemyY, pointsPerCell;
469
    foreach (KGrEnemy * enemy, enemies) {
470 471 472 473 474
        pointsPerCell = enemy->whereAreYou (enemyX, enemyY);
        if (((enemyY == (y + pointsPerCell)) ||
             (enemyY == (y + pointsPerCell - 1))) &&
            (enemyX > (x - pointsPerCell)) &&
            (enemyX < (x + pointsPerCell))) {
475 476 477 478 479 480
            return true;
        }
    }
    return false;
}

481 482
bool KGrLevelPlayer::bumpingFriend (const int spriteId, const Direction dirn,
                                    const int gridI,  const int gridJ)
483
{
484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506
    int dI = 0;
    int dJ = 0;
    switch (dirn) {
    case LEFT:
         dI = -1;
         break;
    case RIGHT:
         dI = +1;
         break;
    case UP:
         dJ = -1;
         break;
    case DOWN:
         dJ = +1;
         break;
    default:
         break;
    }

    int otherEnemy;
    if (dI != 0) {
        otherEnemy = grid->enemyOccupied (gridI + dI, gridJ);
        if (otherEnemy > 0) {
507
            dbk3 << otherEnemy << "at" << (gridI + dI) << gridJ
508 509 510
                     << "dirn" << ((otherEnemy > 0) ?
                               (enemies.at (otherEnemy - 1)->direction()) : 0)
                     << "me" << spriteId << "dirn" << dirn;
511
            if (enemies.at (otherEnemy - 1)->direction() != dirn) {
512
                dbk3 << spriteId << "wants" << dirn << ":" << otherEnemy
513 514 515 516 517 518 519 520 521
                         << "at" << (gridI + dI) << gridJ << "wants"
                         << (enemies.at (otherEnemy - 1)->direction());
                return true;
            }
        }
    }
    if (dJ != 0) {
        otherEnemy = grid->enemyOccupied (gridI, gridJ + dJ);
        if (otherEnemy > 0) {
522
            dbk3 << otherEnemy << "at" << gridI << (gridJ + dJ)
523 524 525
                     << "dirn" << ((otherEnemy > 0) ?
                               (enemies.at (otherEnemy - 1)->direction()) : 0)
                     << "me" << spriteId << "dirn" << dirn;
526
            if (enemies.at (otherEnemy - 1)->direction() != dirn) {
527
                dbk3 << spriteId << "wants" << dirn << ":" << otherEnemy
528 529 530 531 532 533
                         << "at" << gridI << (gridJ + dJ) << "wants"
                         << (enemies.at (otherEnemy - 1)->direction());
                return true;
            }
        }
    }
534
    return false;
535 536
}

537 538 539 540
void KGrLevelPlayer::unstackEnemy (const int spriteId,
                                   const int gridI, const int gridJ,
                                   const int prevEnemy)
{
541
    dbe2 "KGrLevelPlayer::unstackEnemy (%02d at [%02d,%02d] prevEnemy %02d)\n",
542 543 544 545 546
        spriteId, gridI, gridJ, prevEnemy);
    int nextId = grid->enemyOccupied (gridI, gridJ);
    int prevId;
    while (nextId > 0) {
        prevId = enemies.at (nextId - 1)->getPrevInCell();
547
        dbe2 "Next %02d prev %02d\n", nextId, prevId);
548
        if (prevId == spriteId) {
549
            dbe2 "    SET IDs - id %02d prev %02d\n", nextId, prevEnemy);
550 551 552 553 554 555 556
            enemies.at (nextId - 1)->setPrevInCell (prevEnemy);
            // break;
        }
        nextId = prevId;
    }
}

557
void KGrLevelPlayer::tick (bool missed, int scaledTime)
558
{
559 560 561 562
    int i, j;
    emit getMousePos (i, j);
    setTarget (i, j);

563 564 565 566 567 568
    if (playState != Playing) {
        return;
    }

    if (dugBricks.count() > 0) {
        processDugBricks (scaledTime);
569
    }
570 571

    HeroStatus status = hero->run (scaledTime);
572 573 574 575 576 577 578 579 580 581 582 583
    if ((status == WON_LEVEL) || (status == DEAD)) {
        timer->pause();
        // TODO ? If caught in a brick, brick-closing animation is unfinished.
        // Queued connection ensures KGrGame slot runs AFTER return from here.
        emit endLevel (status);
        kDebug() << "END OF LEVEL";
        return;
    }

    foreach (KGrEnemy * enemy, enemies) {
        enemy->run (scaledTime);
    }
584 585

    emit animation (missed);
586 587
}

588
int KGrLevelPlayer::runnerGotGold (const int  spriteId,
589 590
                                   const int  i, const int j,
                                   const bool hasGold, const bool lost)
591
{
592
    if (hasGold) {
593
        dbk3 << "GOLD COLLECTED BY" << spriteId << "AT" << i << j;
594
    }
595
    else if (lost) {
596
        dbk3 << "GOLD LOST BY" << spriteId << "AT" << i << j;
597
    }
598
    else {
599
        dbk3 << "GOLD DROPPED BY" << spriteId << "AT" << i << j;
600
    }
601 602 603 604
    if (! lost) {
        grid->gotGold (i, j, hasGold);		// Record pickup/drop on grid.
    }
    emit gotGold (spriteId, i, j, hasGold, lost); // Erase/show gold on screen.
Ian Wadham's avatar
Ian Wadham committed
605 606

    // If hero got gold, score, maybe show hidden ladders, maybe end the level.
607
    if ((spriteId == heroId) || lost) {
Ian Wadham's avatar
Ian Wadham committed
608
        if (--nuggets <= 0) {
609
            grid->placeHiddenLadders();		// All gold picked up or lost.
Ian Wadham's avatar
Ian Wadham committed
610
        }
611
    }
612 613 614
    if (lost) {
        hero->setNuggets (nuggets);		// Update hero re lost gold.
    }
615
    return nuggets;
616 617
}

618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633
void KGrLevelPlayer::makeReappearanceSequence()
{
    // The idea is to make each possible x co-ord come up once per levelWidth
    // reappearances of enemies.  This is not truly random, but it reduces the
    // tedium in levels where you must keep killing enemies until a particular
    // x or range of x comes up (e.g. if they have to collect gold for you).

    // First put the positions in ascending sequence.
    for (int k = 0; k < levelWidth; k++) {
        reappearPos [k] = k + 1;
    }

    int z;
    int left = levelWidth;
    int temp;

634
    // Shuffle the co-ordinates of reappearance positions (1 to levelWidth).
635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692
    for (int k = 0; k < levelWidth; k++) {
        // Pick a random element from those that are left.
        z = (int)((left * (float) rand()) / RAND_MAX);
        // Exchange its value with the last of the ones left.
        temp = reappearPos [z];
        reappearPos [z] = reappearPos [left - 1];
        reappearPos [left - 1] = temp;
        left--;
    }
    kDebug() << "Randoms" << reappearPos;
    reappearIndex = 0;
}

void KGrLevelPlayer::enemyReappear (int & gridI, int & gridJ)
{
    bool looking = true;
    int  i, j, k;

    // Follow Traditional or Scavenger rules: enemies reappear at top.
    j = rules->reappearRow();

    // Randomly look for a free spot in the row.  Limit the number of tries.
    for (k = 1; ((k <= 3) && looking); k++) {
        if (reappearIndex >= levelWidth) {
            makeReappearanceSequence();	// Get next array of random i.
        }
        i = reappearPos [reappearIndex++];
        switch (grid->cellType (i, j)) {
        case FREE:
        case HLADDER:
            looking = false;
            break;
        default:
            break;
        }
    }

    // If unsuccessful, choose the first free spot in the rows below.
    while ((j < levelHeight) && looking) {
        j++;
        i = 0;
        while ((i < levelWidth) && looking) {
            i++;
            switch (grid->cellType (i, j)) {
            case FREE:
            case HLADDER:
                looking = false;
                break;
            default:
                break;
            }
        }
    }
    kDebug() << "Reappear at" << i << j;
    gridI = i;
    gridJ = j;
}

693 694 695 696 697 698 699 700
/******************************************************************************/
/**************************  AUTHORS' DEBUGGING AIDS **************************/
/******************************************************************************/

void KGrLevelPlayer::dbgControl (int code)
{
    switch (code) {
    case DO_STEP:
701
        timer->step();			// Do one timer step only.
702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726
        break;
    case BUG_FIX:
        bugFix();			// Turn a bug fix on/off dynamically.
        break;
    case LOGGING:
        startLogging();			// Turn logging on/off.
        break;
    case S_POSNS:
        showFigurePositions();		// Show everybody's co-ordinates.
        break;
    case S_HERO:
        hero->showState ('s');		// Show hero's co-ordinates and state.
        break;
    case S_OBJ:
        showObjectState();		// Show an object's state.
        break;
    default:
        showEnemyState (code - ENEMY_0); // Show enemy co-ords and state.
        break;
    }
}

void KGrLevelPlayer::bugFix()
{
    // Toggle a bug fix on/off dynamically.
727
    KGrGame::bugFix = (KGrGame::bugFix) ? false : true;
728 729
    fprintf (stderr, "%s", (KGrGame::bugFix) ? "\n" : "");
    fprintf (stderr, ">> Bug fix is %s\n", (KGrGame::bugFix) ? "ON" : "OFF\n");
730 731 732 733 734
}

void KGrLevelPlayer::startLogging()
{
    // Toggle logging on/off dynamically.
735
    KGrGame::logging = (KGrGame::logging) ? false : true;
736 737
    fprintf (stderr, "%s", (KGrGame::logging) ? "\n" : "");
    fprintf (stderr, ">> Logging is %s\n", (KGrGame::logging) ? "ON" : "OFF\n");
738 739 740 741 742 743 744 745 746 747 748 749
}

void KGrLevelPlayer::showFigurePositions()
{
    hero->showState ('p');
    foreach (KGrEnemy * enemy, enemies) {
        enemy->showState ('p');
    }
}

void KGrLevelPlayer::showObjectState()
{
750 751 752 753
    int   i       = targetI;
    int   j       = targetJ;
    char  here    = grid->cellType (i, j);
    Flags access  = grid->heroMoves (i, j);
754
    int   enemyId = grid->enemyOccupied (i, j);
755 756 757 758 759 760 761 762

    int enter     = (access & ENTERABLE)         ? 1 : 0;
    int stand     = (access & dFlag [STAND])     ? 1 : 0;
    int u         = (access & dFlag [UP])        ? 1 : 0;
    int d         = (access & dFlag [DOWN])      ? 1 : 0;
    int l         = (access & dFlag [LEFT])      ? 1 : 0;
    int r         = (access & dFlag [RIGHT])     ? 1 : 0;
    fprintf (stderr,
763 764
             "[%02d,%02d] [%c] %02x E %d S %d U %d D %d L %d R %d occ %02d\n",
	     i, j, here, access, enter, stand, u, d, l, r, enemyId);
765 766 767 768 769 770 771 772 773 774 775 776 777 778

    Flags eAccess = grid->enemyMoves (i, j);
    if (eAccess != access) {
        access    = eAccess;
        enter     = (access & ENTERABLE)         ? 1 : 0;
        stand     = (access & dFlag [STAND])     ? 1 : 0;
        u         = (access & dFlag [UP])        ? 1 : 0;
        d         = (access & dFlag [DOWN])      ? 1 : 0;
        l         = (access & dFlag [LEFT])      ? 1 : 0;
        r         = (access & dFlag [RIGHT])     ? 1 : 0;
        fprintf (stderr,
             "[%02d,%02d] [%c] %02x E %d S %d U %d D %d L %d R %d Enemy\n",
	     i, j, here, access, enter, stand, u, d, l, r);
    }
779 780 781 782 783 784 785 786 787 788
}

void KGrLevelPlayer::showEnemyState (int enemyId)
{
    if (enemyId < enemies.count()) {
        enemies.at(enemyId)->showState ('s');
    }
}


789
#include "kgrlevelplayer.moc"