Commit d83f6329 authored by Ian Wadham's avatar Ian Wadham

Enemies starting to run and search for the hero.

svn path=/branches/work/kgoldrunner/; revision=930313
parent 646f0471
......@@ -498,22 +498,18 @@ QPoint KGrCanvas::getMousePos()
return (QPoint (i, j));
}
void KGrCanvas::setMousePos (int i, int j)
void KGrCanvas::setMousePos (const int i, const int j)
{
// In KGoldrunner, the top-left visible cell is [1,1]: in KGrSprite [0,0].
i--; j--;
m->setPos (mapToGlobal (QPoint (
topLeft.x() + i * imgW + imgW / 2,
topLeft.y() + j * imgH + imgH / 2)));
m->setPos (mapToGlobal (QPoint (topLeft.x() + (i - 1) * imgW + imgW / 2,
topLeft.y() + (j - 1) * imgH + imgH / 2)));
}
void KGrCanvas::animate (bool missed)
{
foreach (KGrSprite * sprite, (* sprites)) {
if (sprite != 0) {
if (sprite->spriteType() != ENEMY) {
sprite->animate (missed); // Animate the hero and dug bricks.
}
sprite->animate (missed);
}
}
}
......
......@@ -46,7 +46,6 @@ public:
virtual ~KGrCanvas();
QPoint getMousePos();
void setMousePos (int, int);
void setBaseScale();
......@@ -74,6 +73,7 @@ public:
inline void setGoldEnemiesRule (bool showIt) { enemiesShowGold = showIt;}
public slots:
void setMousePos (const int, const int);
void animate (bool missed);
void paintCell (const int i, const int j, const char type,
const int offset = 0);
......
......@@ -376,6 +376,23 @@ void KGrGame::incScore (int n)
emit showScore (score); // collect gold 250, complete the level 1500.
}
void KGrGame::endLevel (const int result)
{
if (levelPlayer) {
// Delete the level-player, hero, enemies, grid, rule-book, etc.
// Delete sprites in KGrCanvas later, so they stay visible for a while.
delete levelPlayer;
levelPlayer = 0;
}
if (result == WON_LEVEL) {
levelCompleted();
}
else if (result == DEAD) {
herosDead();
}
}
void KGrGame::herosDead()
{
if ((level < 1) || (lives <= 0))
......@@ -383,7 +400,9 @@ void KGrGame::herosDead()
// Lose a life.
if (--lives > 0) {
#ifdef ENABLE_SOUND_SUPPORT
effects->play (fx[DeathSound]);
#endif
// Still some life left, so PAUSE and then re-start the level.
emit showLives (lives);
gameFrozen = true; // Freeze the animation and let
......@@ -393,7 +412,9 @@ void KGrGame::herosDead()
}
else {
// Game over.
#ifdef ENABLE_SOUND_SUPPORT
effects->play (fx[GameOverSound]);
#endif
emit showLives (lives);
freeze();
QString gameOver = "<NOBR><B>" + i18n ("GAME OVER !!!") + "</B></NOBR>";
......@@ -458,7 +479,9 @@ void KGrGame::showHiddenLadders()
void KGrGame::levelCompleted()
{
#ifdef ENABLE_SOUND_SUPPORT
effects->play (fx[CompletedSound]);
#endif
connect (view, SIGNAL (fadeFinished()), this, SLOT (goUpOneLevel()));
view->fadeOut();
}
......@@ -721,6 +744,10 @@ int KGrGame::loadLevel (int levelNo)
return 0;
}
// Clean up any sprites remaining from a previous level. This is done late,
// so that the end-state of the sprites will be visible for a short while.
view->deleteAllSprites();
view->setLevel (levelNo); // Switch and render background if reqd.
view->fadeIn(); // Then run the fade-in animation.
startScore = score; // The score we will save, if asked.
......@@ -779,6 +806,11 @@ int KGrGame::loadLevel (int levelNo)
gameGroup.sync(); // Ensure that the entry goes to disk.
}
// Use a queued connection here, to ensure that levelPlayer has finished
// executing when control goes to the endLevel (const int heroStatus) slot.
connect (levelPlayer, SIGNAL (endLevel (const int)),
this, SLOT (endLevel (const int)), Qt::QueuedConnection);
// Re-enable player input.
loading = false;
......
......@@ -103,6 +103,7 @@ public slots:
void showHighScores(); // Show high scores for current game.
void incScore (int); // Update the score.
void endLevel (const int result); // Hero completed the level or he died.
void herosDead(); // Hero was caught or he quit (key Q).
void showHiddenLadders(); // Show hidden ladders (nuggets gone).
void levelCompleted(); // Hero completed the level.
......
......@@ -83,6 +83,6 @@ enum DebugCodes {
const int TickTime = 20;
enum HeroStatus {
WON_LEVEL, CAUGHT_IN_BRICK, NO_ACTION, STATIONARY, MOVING};
NORMAL, WON_LEVEL, DEAD};
#endif // KGRGLOBALS_H
/****************************************************************************
* Copyright 2009 Ian Wadham <ianwau@gmail.com> *
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
......
/****************************************************************************
* Copyright 2009 Ian Wadham <ianwau@gmail.com> *
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
......
/****************************************************************************
* Copyright 2009 Ian Wadham <ianwau@gmail.com> *
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
......@@ -56,6 +56,10 @@ KGrLevelPlayer::KGrLevelPlayer (QObject * parent,
KGrLevelPlayer::~KGrLevelPlayer()
{
while (! dugBricks.isEmpty()) {
delete dugBricks.takeFirst();
}
// TODO - Do we need this delete?
// while (! enemies.isEmpty()) {
// delete enemies.takeFirst();
......@@ -92,8 +96,13 @@ void KGrLevelPlayer::init (KGrCanvas * view, const Control mode)
connect (this, SIGNAL (makeSprite (char, int, int)),
view, SLOT (makeSprite (char, int, int)));
// Connect to the mouse-positioning code in the graphics.
connect (this, SIGNAL (setMousePos (const int, const int)),
view, SLOT (setMousePos (const int, const int)));
// Show the layout of this level in the view (KGrCanvas).
int wall = ConcreteWall;
int enemyCount = 0;
for (int j = wall ; j < levelData->height + wall; j++) {
for (int i = wall; i < levelData->width + wall; i++) {
char type = grid->cellType (i, j);
......@@ -108,47 +117,61 @@ void KGrLevelPlayer::init (KGrCanvas * view, const Control mode)
nuggets++;
}
// TODO - Do hero in pass 1 and enemies in pass 2, to ensure the hero has id 0.
// Either, create a hero and paint him on the background ...
// If the hero is here, leave the tile empty.
if (type == HERO) {
emit paintCell (i, j, FREE, 0);
grid->changeCellAt (i, j, FREE); // Hero becomes sprite.
}
// 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);
// TODO - Do hero in pass 1 and enemies in pass 2, to ensure the hero has id 0.
// Create the hero (always sprite 0), with the proper timing.
for (int j = wall ; j < levelData->height + wall; j++) {
for (int i = wall; i < levelData->width + wall; i++) {
char type = grid->cellType (i, j);
if (type == HERO) {
if (hero == 0) {
targetI = i;
targetJ = j;
heroID = emit makeSprite (HERO, i, j);
hero = new KGrHero (this, grid, i, j, heroID, rules);
// TODO - Iff mouse mode, setMousePos();
view->setMousePos (targetI, targetJ); // ??????????
emit setMousePos (targetI, targetJ);
grid->changeCellAt (i, j, FREE); // Hero now a sprite.
}
}
}
}
// 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.
// Create the enemies (sprites 1-n), with the proper timing.
for (int j = wall ; j < levelData->height + wall; j++) {
for (int i = wall; i < levelData->width + wall; i++) {
char type = grid->cellType (i, j);
if (type == ENEMY) {
KGrEnemy * enemy;
int id = emit makeSprite (ENEMY, i, j);
enemy = new KGrEnemy (this, grid, i, j, id, rules);
enemies.append (enemy);
}
// Or, just paint this tile.
else {
emit paintCell (i, j, type, 0);
grid->changeCellAt (i, j, FREE); // Enemy now a sprite.
}
}
}
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)));
......@@ -266,7 +289,7 @@ void KGrLevelPlayer::prepareToPlay()
{
// TODO - Should this be a signal?
kDebug() << "Set mouse to:" << targetI << targetJ;
mView->setMousePos (targetI, targetJ);
emit setMousePos (targetI, targetJ);
playState = Ready;
}
......@@ -372,6 +395,14 @@ Direction KGrLevelPlayer::getDirection (int heroI, int heroJ)
return direction;
}
Direction KGrLevelPlayer::getEnemyDirection (int enemyI, int enemyJ)
{
int heroI, heroJ, point, pointsPerCell;
pointsPerCell = hero->whereAreYou (heroI, heroJ, point);
return rules->findBestWay (enemyI, enemyJ, heroI, heroJ, grid);
}
void KGrLevelPlayer::tick (bool missed, int scaledTime)
{
if (playState != Playing) {
......@@ -383,11 +414,23 @@ void KGrLevelPlayer::tick (bool missed, int scaledTime)
}
HeroStatus status = hero->run (scaledTime);
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);
}
emit animation (missed);
}
void KGrLevelPlayer::runnerGotGold (const int spriteID,
int KGrLevelPlayer::runnerGotGold (const int spriteID,
const int i, const int j,
const bool hasGold)
{
......@@ -402,6 +445,7 @@ void KGrLevelPlayer::runnerGotGold (const int spriteID,
grid->placeHiddenLadders();
}
}
return nuggets;
}
void KGrLevelPlayer::pause (bool stop)
......
/****************************************************************************
* Copyright 2009 Ian Wadham <ianwau@gmail.com> *
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
......@@ -39,26 +39,29 @@ class KGrLevelPlayer : public QObject
{
Q_OBJECT
public:
KGrLevelPlayer (QObject * parent, KGrGameData * theGameData,
KGrLevelData * theLevelData);
KGrLevelPlayer (QObject * parent, KGrGameData * theGameData,
KGrLevelData * theLevelData);
~KGrLevelPlayer();
void init (KGrCanvas * view, const Control mode);
void prepareToPlay();
void init (KGrCanvas * view, const Control mode);
void prepareToPlay ();
inline void setControlMode (const Control mode) { controlMode = mode; }
inline void setControlMode (const Control mode) { controlMode = mode; }
void setTarget (int pointerI, int pointerJ);
void setDirectionByKey (Direction dirn);
Direction getDirection (int heroI, int heroJ);
void setTarget (int pointerI, int pointerJ);
void setDirectionByKey (Direction dirn);
Direction getDirection (int heroI, int heroJ);
Direction getEnemyDirection (int enemyI, int enemyJ);
void runnerGotGold (const int spriteID, const int i, const int j,
const bool hasGold);
int runnerGotGold (const int spriteID, const int i, const int j,
const bool hasGold);
void pause (bool stop);
void dbgControl (int code); // Authors' debugging aids.
void pause (bool stop);
void dbgControl (int code); // Authors' debugging aids.
signals:
void endLevel (const int result);
void setMousePos (const int i, const int j);
void animation (bool missed);
void paintCell (int i, int j, char tileType, int diggingStage = 0);
int makeSprite (char spriteType, int i, int j);
......
/****************************************************************************
* Copyright 2009 Ian Wadham <ianwau@gmail.com> *
* Copyright 2009 Ian Wadham <iandw.au@gmail.com> *
* *
* This program is free software; you can redistribute it and/or *
* modify it under the terms of the GNU General Public License as *
......@@ -15,6 +15,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
****************************************************************************/
#include "kgrlevelgrid.h"
#include "kgrrulebook.h"
KGrRuleBook::KGrRuleBook (QObject * parent)
......@@ -48,12 +49,12 @@ void KGrRuleBook::setTiming (const int enemyCount)
if (mVariableTiming) {
choice = (enemyCount < 0) ? 0 : enemyCount;
choice = (enemyCount > 5) ? 5 : enemyCount;
times = varTiming [enemyCount];
times = varTiming [choice];
}
}
// Initialise the static flags for the rules.
// Initialise the flags for the rules.
KGrTraditionalRules::KGrTraditionalRules (QObject * parent)
:
......@@ -76,13 +77,388 @@ KGrTraditionalRules::~KGrTraditionalRules()
{
}
Direction KGrTraditionalRules::findBestWay (const QPoint & enemyPosition,
const QPoint & heroPosition,
const KGrLevelGrid & grid)
Direction KGrTraditionalRules::findBestWay (const int eI, const int eJ,
const int hI, const int hJ,
KGrLevelGrid * pGrid)
// TODO - Should be const ... const KGrLevelGrid * pGrid)
{
return RIGHT;
grid = pGrid;
if (grid->cellType (eI, eJ) == USEDHOLE) { // Could not get out of hole
return UP; // (e.g. brick above is closed):
} // but keep trying.
// TODO - Add !standOnEnemy() && as a condition here.
// TODO - And maybe !((*playfield)[x][y+1]->whatIam() == HOLE)) not just out of hole,
bool canStand = (grid->enemyMoves (eI, eJ) & dFlag [STAND]);
if (! canStand) {
return DOWN;
}
// Traditional search strategy.
Direction dirn = STAND;
if (eJ == hJ) {
dirn = getHero (eI, eJ, hI); // Hero on same row.
if (dirn != STAND) {
return dirn; // Can go towards him.
}
}
if (eJ >= hJ) { // Hero above or on same row.
dirn = searchUp (eI, eJ, hJ); // Find a way that leads up.
}
else { // Hero below enemy.
dirn = searchDown (eI, eJ, hJ); // Find way that leads down.
if (dirn == STAND) {
dirn = searchUp (eI, eJ, hJ); // No go: try going up first.
}
}
if (dirn == STAND) { // When all else fails, look for
dirn = searchDown (eI, eJ, eJ - 1); // a way below the hero.
}
return dirn;
}
Direction KGrTraditionalRules::searchUp (int ew, int eh, int hh)
{
int i, ilen, ipos, j, jlen, jpos, deltah, rungs;
deltah = eh - hh; // Get distance up to hero's level.
// Search for the best ladder right here or on the left.
i = ew; ilen = 0; ipos = -1;
while (i >= 1) {
rungs = distanceUp (i, eh, deltah);
if (rungs > ilen) {
ilen = rungs; // This the best yet.
ipos = i;
}
if (searchOK (-1, i, eh))
i--; // Look further to the left.
else
i = -1; // Cannot go any further to the left.
}
// Search for the best ladder on the right.
j = ew; jlen = 0; jpos = -1;
while (j < FIELDWIDTH) {
if (searchOK (+1, j, eh)) {
j++; // Look further to the right.
rungs = distanceUp (j, eh, deltah);
if (rungs > jlen) {
jlen = rungs; // This the best yet.
jpos = j;
}
}
else
j = FIELDWIDTH+1; // Cannot go any further to the right.
}
if ((ilen == 0) && (jlen == 0)) // No ladder found.
return STAND;
// Choose a ladder to go to.
if (ilen != jlen) { // If the ladders are not the same
// length, choose the longer one.
if (ilen > jlen) {
if (ipos == ew) // If already on the best ladder, go up.
return UP;
else
return LEFT;
}
else
return RIGHT;
}
else { // Both ladders are the same length.
if (ipos == ew) // If already on the best ladder, go up.
return UP;
else if (ilen == deltah) { // If both reach the hero's level,
if ((ew - ipos) <= (jpos - ew)) // choose the closest.
return LEFT;
else
return RIGHT;
}
else return LEFT; // Else choose the left ladder.
}
}
Direction KGrTraditionalRules::searchDown (int ew, int eh, int hh)
{
int i, ilen, ipos, j, jlen, jpos, deltah, rungs, path;
deltah = hh - eh; // Get distance down to hero's level.
// Search for the best way down, right here or on the left.
ilen = 0; ipos = -1;
i = (willNotFall (ew, eh)) ? ew : -1;
rungs = distanceDown (ew, eh, deltah);
if (rungs > 0) {
ilen = rungs; ipos = ew;
}
while (i >= 1) {
rungs = distanceDown (i - 1, eh, deltah);
if (((rungs > 0) && (ilen == 0)) ||
((deltah > 0) && (rungs > ilen)) ||
((deltah <= 0) && (rungs < ilen) && (rungs != 0))) {
ilen = rungs; // This the best way yet.
ipos = i - 1;
}
if (searchOK (-1, i, eh))
i--; // Look further to the left.
else
i = -1; // Cannot go any further to the left.
}
// Search for the best way down, on the right.
j = ew; jlen = 0; jpos = -1;
while (j < FIELDWIDTH) {
rungs = distanceDown (j + 1, eh, deltah);
if (((rungs > 0) && (jlen == 0)) ||
((deltah > 0) && (rungs > jlen)) ||
((deltah <= 0) && (rungs < jlen) && (rungs != 0))) {
jlen = rungs; // This the best way yet.
jpos = j + 1;
}
if (searchOK (+1, j, eh)) {
j++; // Look further to the right.
}
else
j = FIELDWIDTH+1; // Cannot go any further to the right.
}
if ((ilen == 0) && (jlen == 0)) // Found no way down.
return STAND;
// Choose a way down to follow.
if (ilen == 0)
path = jpos;
else if (jlen == 0)
path = ipos;
else if (ilen != jlen) { // If the ways down are not same length,
// choose closest to hero's level.
if (deltah > 0) {
if (jlen > ilen)
path = jpos;
else
path = ipos;
}
else {
if (jlen > ilen)
path = ipos;
else
path = jpos;
}
}
else { // Both ways down are the same length.
if ((deltah > 0) && // If both reach the hero's level,
(ilen == deltah)) { // choose the closest.
if ((ew - ipos) <= (jpos - ew))
path = ipos;
else
path = jpos;
}
else
path = ipos; // Else, go left or down.
}
if (path == ew)
return DOWN;
else if (path < ew)
return LEFT;
else
return RIGHT;
}
Direction KGrTraditionalRules::getHero (int eI, int eJ, int hI)
{
int i, inc, returnValue;
inc = (eI > hI) ? -1 : +1;
i = eI;
while (i != hI) {
returnValue = canWalkLR (inc, i, eJ);
if (returnValue > 0)
i = i + inc; // Can run further towards the hero.
else if (returnValue < 0)
break; // Will run into a wall regardless.
else
return STAND; // Won't run over a hole.
}
if (i < eI) return LEFT;
else if (i > eI) return RIGHT;
else return STAND;
}
int KGrTraditionalRules::distanceUp (int x, int y, int deltah)
{
int rungs = 0;
// If there is a ladder at (x,y), return its length, else return zero.
while (grid->cellType (x, y - rungs) == LADDER) {
rungs++;
if (rungs >= deltah) { // To hero's level is enough.
break;
}
}
return rungs;
}
int KGrTraditionalRules::distanceDown (int x, int y, int deltah)
{
// When deltah > 0, we want an exit sideways at the hero's level or above.