Commit 646f0471 authored by Ian Wadham's avatar Ian Wadham

Digging is working. Hidden ladders appear when gold is all gone.

svn path=/branches/work/kgoldrunner/; revision=928658
parent e571a26b
......@@ -507,11 +507,13 @@ void KGrCanvas::setMousePos (int i, int j)
topLeft.y() + j * imgH + imgH / 2)));
}
void KGrCanvas::animate ()
void KGrCanvas::animate (bool missed)
{
foreach (KGrSprite * sprite, (* sprites)) {
if (sprite->spriteType() != ENEMY) {
sprite->animate(); // Animate the hero and dug bricks.
if (sprite != 0) {
if (sprite->spriteType() != ENEMY) {
sprite->animate (missed); // Animate the hero and dug bricks.
}
}
}
}
......@@ -550,6 +552,8 @@ int KGrCanvas::makeSprite (const char type, int i, int j)
sprite->addFrames (tileset, topLeft, scale);
frame1 = KGrTheme::BrickTile;
z = 1;
// Erase the brick-image so that animations are visible in all themes.
paintCell (i, j, FREE, 0);
break;
default:
break;
......@@ -560,12 +564,13 @@ int KGrCanvas::makeSprite (const char type, int i, int j)
sprite->setZ (z);
sprite->show();
kDebug() << "Sprite ID" << spriteId << "sprite type" << type;
kDebug() << "Sprite ID" << spriteId << "sprite type" << type
<< "at" << i << j;
return spriteId;
}
void KGrCanvas::startAnimation (const int id, const int i, const int j,
const int time,
void KGrCanvas::startAnimation (const int id, const bool repeating,
const int i, const int j, const int time,
const Direction dirn, const AnimationType type)
{
// TODO - Save last direction somehow, to use in facing and centering code.
......@@ -627,8 +632,8 @@ void KGrCanvas::startAnimation (const int id, const int i, const int j,
}
kDebug() << "id" << id << "data" << i << j << dx * bgw << dy * bgw << frame << time;
// TODO - Generalise nFrameChanges = 4, also the tick time = 20 new sprite.
sprites->at(id)->setAnimation ((i - 1) * bgw, (j - 1) * bgh, frame,
nFrames, dx * bgw, dy * bgh, time, nFrameChanges);
sprites->at(id)->setAnimation (repeating, (i - 1) * bgw, (j - 1) * bgh,
frame, nFrames, dx * bgw, dy * bgh, time, nFrameChanges);
}
void KGrCanvas::resynchAnimation (const int id, const int i, const int j,
......@@ -649,11 +654,30 @@ void KGrCanvas::gotGold (const int spriteID, const int i, const int j,
}
}
void KGrCanvas::deleteSprite (const int spriteId)
void KGrCanvas::showHiddenLadders (const QList<int> & ladders, const int width)
{
delete sprites->at(spriteId);
(* sprites)[spriteId] = 0;
int offset, i, j;
foreach (offset, ladders) {
i = offset % width;
j = offset / width;
paintCell (i, j, LADDER);
}
}
void KGrCanvas::deleteSprite (const int spriteID)
{
QPoint loc = sprites->at(spriteID)->currentLoc();
bool brick = (sprites->at(spriteID)->spriteType() == BRICK);
delete sprites->at(spriteID);
(* sprites)[spriteID] = 0;
emptySprites++;
if (brick) {
// Dug-brick sprite erased: restore the tile that was at that location.
paintCell ((loc.x()/bgw) + 1, (loc.y()/bgh) + 1, BRICK, 0);
}
kDebug() << "Sprite ID" << spriteID << "emptySprites" << emptySprites;
}
void KGrCanvas::deleteAllSprites()
......
......@@ -74,20 +74,21 @@ public:
inline void setGoldEnemiesRule (bool showIt) { enemiesShowGold = showIt;}
public slots:
void animate ();
void paintCell (const int i, const int j, const char type,
const int offset = 0);
int makeSprite (const char type, int i, int j);
void startAnimation (const int id, const int i, const int j,
const int time,
const Direction dirn, const AnimationType type);
void resynchAnimation (const int id, const int i, const int j,
const bool stop);
void gotGold (const int spriteID, const int i, const int j,
const bool spriteHasGold);
void deleteSprite (const int id);
void deleteAllSprites ();
void animate (bool missed);
void paintCell (const int i, const int j, const char type,
const int offset = 0);
int makeSprite (const char type, int i, int j);
void startAnimation (const int id, const bool repeating,
const int i, const int j, const int time,
const Direction dirn, const AnimationType type);
void resynchAnimation (const int id, const int i, const int j,
const bool stop);
void gotGold (const int spriteID, const int i, const int j,
const bool spriteHasGold);
void showHiddenLadders (const QList<int> & ladders, const int width);
void deleteSprite (const int id);
void deleteAllSprites ();
signals:
void mouseClick (int);
......
......@@ -82,4 +82,7 @@ enum DebugCodes {
const int TickTime = 20;
enum HeroStatus {
WON_LEVEL, CAUGHT_IN_BRICK, NO_ACTION, STATIONARY, MOVING};
#endif // KGRGLOBALS_H
......@@ -37,7 +37,6 @@ KGrLevelGrid::KGrLevelGrid (QObject * parent, KGrLevelData * theLevelData)
// Initialise the flags for each cell.
heroAccess.fill (0, size);
enemyAccess.fill (0, size);
cellStates.fill (0, size);
// Copy the cells of the layout, but enclosed within the concrete wall.
int inRow = 0;
......@@ -75,11 +74,11 @@ KGrLevelGrid::~KGrLevelGrid()
}
// Inline functions (see kgrlevelgrid.h).
// char cellType
// char heroMoves
// char enemyMoves
// char cellState
// void gotGold
// char cellType (int i, int j)
// char heroMoves (int i, int j)
// char enemyMoves (int i, int j)
// void gotGold (const int i, const int j, const bool runnerHasGold)
// int index (int i, int j)
void KGrLevelGrid::calculateAccess (bool pRunThruHole)
{
......@@ -131,23 +130,23 @@ void KGrLevelGrid::calculateCellAccess (const int i, const int j)
char below = cellType (i, j + 1);
access = heroMoves (i, j) & ENTERABLE;
fprintf (stderr, "[%02d,%02d] %c access %02x below %c\n",
i, j, here, access, below);
// fprintf (stderr, "[%02d,%02d] %c access %02x below %c\n",
// i, j, here, access, below);
// Cannot enter brick or concrete: can enter false brick from above.
// Cannot enter brick, concrete or used hole: can drop into a false brick.
if (! (access & ENTERABLE) && (here != FBRICK)) {
access = 0;
}
// If can stand or hang on anything, allow down, left and right.
else if ((below == BRICK) || (below == CONCRETE) || (below == USEDHOLE) ||
(below == LADDER) || (here == LADDER) || (here == POLE)) {
fprintf (stderr, "Can stand\n");
// fprintf (stderr, "Can stand\n");
access |= (dFlag [STAND] | dFlag [DOWN] |
dFlag [LEFT] | dFlag [RIGHT]);
}
// If cannot stand or hang, can only go down (space or false brick).
else {
fprintf (stderr, "Cannot stand\n");
// fprintf (stderr, "Cannot stand\n");
access |= dFlag [DOWN];
}
// Can only go up if there is a ladder here.
......@@ -161,24 +160,27 @@ void KGrLevelGrid::calculateCellAccess (const int i, const int j)
int d = (access & dFlag [DOWN]) ? 1 : 0;
int l = (access & dFlag [LEFT]) ? 1 : 0;
int r = (access & dFlag [RIGHT]) ? 1 : 0;
if (below == HOLE)
fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d ***\n",
i, j, here, access, enter, stand, u, d, l, r);
// Mask out directions that are blocked above, below, L or R.
if (! (heroMoves (i, j - 1) & ENTERABLE)) {
access = ~dFlag [UP] & access; // Cannot go up.
}
if (! (heroMoves (i - 1, j) & ENTERABLE)) {
access = ~dFlag [LEFT] & access; // Cannot go left.
}
if (! (heroMoves (i + 1, j) & ENTERABLE)) {
access = ~dFlag [RIGHT] & access; // Cannot go right.
}
if (! (heroMoves (i, j + 1) & ENTERABLE)) {
if (below != FBRICK) {
access = ~dFlag [DOWN] & access; // Cannot go down.
}
// if (below == HOLE)
// fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d ***\n",
// i, j, here, access, enter, stand, u, d, l, r);
// Mask out directions that are blocked above, below, L or R, but not for
// concrete/brick at edge of grid (or elsewhere) to avoid indexing errors.
if (access != 0) {
if (! (heroMoves (i, j - 1) & ENTERABLE)) {
access = ~dFlag [UP] & access; // Cannot go up.
}
if (! (heroMoves (i - 1, j) & ENTERABLE)) {
access = ~dFlag [LEFT] & access; // Cannot go left.
}
if (! (heroMoves (i + 1, j) & ENTERABLE)) {
access = ~dFlag [RIGHT] & access; // Cannot go right.
}
if (! (heroMoves (i, j + 1) & ENTERABLE)) {
if (below != FBRICK) {
access = ~dFlag [DOWN] & access; // Cannot go down.
}
}
}
enter = (access & ENTERABLE) ? 1 : 0;
......@@ -187,9 +189,9 @@ void KGrLevelGrid::calculateCellAccess (const int i, const int j)
d = (access & dFlag [DOWN]) ? 1 : 0;
l = (access & dFlag [LEFT]) ? 1 : 0;
r = (access & dFlag [RIGHT]) ? 1 : 0;
if (below == HOLE)
fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d ***\n",
i, j, here, access, enter, stand, u, d, l, r);
// if (below == HOLE)
// fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d ***\n",
// i, j, here, access, enter, stand, u, d, l, r);
heroAccess [index (i, j)] = access;
enter = (access & ENTERABLE) ? 1 : 0;
stand = (access & dFlag [STAND]) ? 1 : 0;
......@@ -197,8 +199,8 @@ void KGrLevelGrid::calculateCellAccess (const int i, const int j)
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\n",
i, j, here, access, enter, stand, u, d, l, r);
// fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d\n",
// i, j, here, access, enter, stand, u, d, l, r);
// Enemy access is the same as the hero's when no holes are open.
enemyAccess [index (i, j)] = heroAccess [index (i, j)];
......@@ -206,22 +208,38 @@ void KGrLevelGrid::calculateCellAccess (const int i, const int j)
if (here == USEDHOLE) {
enemyAccess [index (i, j)] = UP; // Can only climb out of hole.
}
if (! runThruHole) { // Check the rule.
else if (! runThruHole) { // Check the rule.
char mask;
mask = (cellType (i - 1, j) == HOLE) ? dFlag [LEFT] : 0;
mask = (cellType (i + 1, j) == HOLE) ? (dFlag [RIGHT] | mask) : mask;
enemyAccess [index (i, j)] &= ~mask; // Block access to holes at L/R.
}
heroAccess [index (i, j)] |= access;
access = heroAccess [index (i, j)];
// TODO - Remove the debugging code below.
access = enemyAccess [index (i, j)];
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 Enem\n",
i, j, here, access, enter, stand, u, d, l, r);
// fprintf (stderr, "[%02d,%02d] %c %02x E %d S %d U %d D %d L %d R %d Enem\n",
// i, j, here, access, enter, stand, u, d, l, r);
}
void KGrLevelGrid::placeHiddenLadders()
{
int offset, i, j;
fprintf (stderr, "KGrLevelGrid::placeHiddenLadders() %02d width %02d\n",
hiddenLadders.count(), width);
foreach (offset, hiddenLadders) {
i = offset % width;
j = offset / width;
changeCellAt (i, j, LADDER);
fprintf (stderr, "Show ladder at %04d [%02d,%02d]\n", offset, i, j);
}
emit showHiddenLadders (hiddenLadders, width);
hiddenLadders.clear();
}
#include "kgrlevelgrid.moc"
......@@ -44,10 +44,6 @@ public:
return enemyAccess [i + j * width];
}
inline char cellState (int i, int j) {
return cellStates [i + j * width];
}
inline void gotGold (const int i, const int j, const bool runnerHasGold) {
layout [i + j * width] = (runnerHasGold) ? FREE : NUGGET;
}
......@@ -56,6 +52,11 @@ public:
void changeCellAt (const int i, const int j, const char type);
void placeHiddenLadders();
signals:
void showHiddenLadders (const QList<int> & ladders, const int width);
private:
inline int index (int i, int j) {
return (i + j * width);
......@@ -71,7 +72,6 @@ private:
QVector<char> layout;
QVector<Flags> heroAccess;
QVector<Flags> enemyAccess;
QVector<char> cellStates;
QList<int> hiddenLadders;
QList<int> hiddenEnemies;
......
......@@ -18,6 +18,7 @@
#include <KDebug>
#include <QTimer>
#include <QList>
#include "kgrcanvas.h"
#include "kgrlevelplayer.h"
......@@ -30,19 +31,25 @@ KGrLevelPlayer::KGrLevelPlayer (QObject * parent,
KGrGameData * theGameData,
KGrLevelData * theLevelData)
:
QObject (parent),
gameData (theGameData),
levelData (theLevelData),
grid (new KGrLevelGrid (this, theLevelData)),
hero (0),
controlMode (MOUSE),
nuggets (0),
playState (NotReady),
targetI (1),
targetJ (1),
direction (STAND),
timer (0)
QObject (parent),
gameData (theGameData),
levelData (theLevelData),
grid (new KGrLevelGrid (this, theLevelData)),
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.
{
t.start(); // IDW
gameLogging = false;
bugFixed = false;
}
......@@ -105,6 +112,7 @@ void KGrLevelPlayer::init (KGrCanvas * view, const Control mode)
// Either, create a hero and paint him on the background ...
if (type == HERO) {
emit paintCell (i, j, FREE, 0);
grid->changeCellAt (i, j, FREE); // Hero becomes sprite.
if (hero == 0) {
targetI = i;
......@@ -119,6 +127,7 @@ void KGrLevelPlayer::init (KGrCanvas * view, const Control mode)
// Or, create an enemy and paint him on the background ...
else if (type == ENEMY) {
emit paintCell (i, j, FREE, 0);
grid->changeCellAt (i, j, FREE); // Enemy becomes sprite.
KGrEnemy * enemy;
int id = emit makeSprite (ENEMY, i, j);
......@@ -133,63 +142,123 @@ void KGrLevelPlayer::init (KGrCanvas * view, const Control mode)
}
}
// Connect the hero's and ememies' efforts to the graphics.
if (rules->variableTiming()) {
// Game-speed depends on number of enemies (as in Traditional rules).
rules->setTiming (enemies.count());
}
rules->getDigTimes (digCycleTime, digCycleCount);
// Connect the hero's and enemies' efforts to the graphics.
connect (this, SIGNAL (gotGold (int, int, int, bool)),
view, SLOT (gotGold (int, int, int, bool)));
// Connect mouse-clicks from KGrCanvas to digging slot.
connect (view, SIGNAL (mouseClick (int)), SLOT (doDig (int)));
// Let the hero create and delete sprites for animating dug bricks.
connect (hero, SIGNAL (makeSprite (char, int, int)),
view, SLOT (makeSprite (char, int, int)));
// Connect the new hero and enemies (if any) to the animation code.
connect (hero, SIGNAL (startAnimation (int, int, int, int,
connect (hero, SIGNAL (startAnimation (int, bool, int, int, int,
Direction, AnimationType)),
view, SLOT (startAnimation (int, int, int, int,
view, SLOT (startAnimation (int, bool, int, int, int,
Direction, AnimationType)));
foreach (KGrEnemy * enemy, enemies) {
connect (enemy, SIGNAL (startAnimation (int, int, int, int,
connect (enemy, SIGNAL (startAnimation (int, bool, int, int, int,
Direction, AnimationType)),
view, SLOT (startAnimation (int, int, int, int,
view, SLOT (startAnimation (int, bool, int, int, int,
Direction, AnimationType)));
}
// Connect the level player to the animation code (for use with dug bricks).
connect (this, SIGNAL (startAnimation (int, int, int, int,
connect (this, SIGNAL (startAnimation (int, bool, int, int, int,
Direction, AnimationType)),
view, SLOT (startAnimation (int, int, int, int,
view, SLOT (startAnimation (int, bool, int, int, int,
Direction, AnimationType)));
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)));
// 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.
connect (timer, SIGNAL (tick(bool)), this, SLOT (tick(bool)));
connect (this, SIGNAL (animation()), view, SLOT (animate()));
connect (timer, SIGNAL (tick (bool, int)), this, SLOT (tick (bool, int)));
connect (this, SIGNAL (animation (bool)), view, SLOT (animate (bool)));
// timer->start (TickTime); // Interval = TickTime, defined in kgrglobals.h.
}
void KGrLevelPlayer::startDigging (Direction diggingDirection) {
// TODO - Think about levelPlayer managing all the dug bricks.
void KGrLevelPlayer::startDigging (Direction diggingDirection)
{
int digI = 1;
int digJ = 1;
// We need the hero to decide if he CAN dig and if so, where.
if (hero->dig (diggingDirection, digI, digJ)) {
// The hero can dig as requested: the chosen brick is at (digI, digJ).
grid->changeCellAt (digI, digJ, HOLE);
// Delete the brick-image so that animations are visible in all themes.
emit paintCell (digI, digJ, FREE, 0);
// TODO - Remove. emit paintCell (digI, digJ, FREE, 0);
// Start the brick-opening animation.
// Start the brick-opening animation (non-repeating).
int id = emit makeSprite (BRICK, digI, digJ);
emit startAnimation (id, digI, digJ, 1000, STAND, OPEN_BRICK);
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);
}
}
// TODO - Need to keep a list of digging in progress.
// TODO - Maybe a list of integer counters of the 200 msec intervals?
// TODO - And what about the sprite ID and (digI, digJ)?
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) {
int id = dugBrick->id; // IDW testing
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) {
kDebug() << "DIG" << id << dugBrick->countdown
<< "time" << (t.elapsed() - dugBrick->startTime);
// 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 - Hero falls through floor when caught in a closing brick.
// TODO - Implement speed-variation as a parameter of tick().
}
}
}
......@@ -303,13 +372,19 @@ Direction KGrLevelPlayer::getDirection (int heroI, int heroJ)
return direction;
}
void KGrLevelPlayer::tick (bool missed)
void KGrLevelPlayer::tick (bool missed, int scaledTime)
{
if (playState == Playing) {
hero->run();
if (missed) kDebug() << "TIMER SIGNAL MISSED ...";
emit animation();
if (playState != Playing) {
return;
}
if (dugBricks.count() > 0) {
processDugBricks (scaledTime);
}
HeroStatus status = hero->run (scaledTime);
emit animation (missed);
}
void KGrLevelPlayer::runnerGotGold (const int spriteID,
......@@ -324,6 +399,7 @@ void KGrLevelPlayer::runnerGotGold (const int spriteID,
kDebug() << "Collected gold";
if (--nuggets <= 0) {
kDebug() << "ALL GOLD COLLECTED";
grid->placeHiddenLadders();
}
}
}
......
......@@ -21,6 +21,8 @@
#include <QObject>
#include <QList>
#include <QTime> // IDW testing
#include "kgrconsts.h" // OBSOLESCENT - 1/1/09
#include "kgrglobals.h"
......@@ -57,17 +59,18 @@ public:
void dbgControl (int code); // Authors' debugging aids.
signals:
void animation ();
void animation (bool missed);
void paintCell (int i, int j, char tileType, int diggingStage = 0);
int makeSprite (char spriteType, int i, int j);
void startAnimation (const int spriteId, const int i, const int j,
const int time,
void startAnimation (const int spriteId, const bool repeating,
const int i, const int j, const int time,
const Direction dirn, const AnimationType type);
void deleteSprite (const int spriteId);
void gotGold (const int spriteID, const int i, const int j,
const bool hasGold);
private slots:
void tick (bool missed);
void tick (bool missed, int scaledTime);
void doDig (int button); // Dig using mouse-buttons.
private:
......@@ -100,6 +103,24 @@ private:
void restart(); // Kickstart the game action.
void startDigging (Direction diggingDirection);
void processDugBricks (const int scaledTime);
int digCycleTime; // Milliseconds per dig-timing cycle.
int digCycleCount; // Number of cycles hole is fully open.