You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdegames/kgoldrunner/src/kgrfigure.cpp

1804 lines
48 KiB

/***************************************************************************
* kgrfigure.cpp - description *
* ------------------- *
* Copyright (C) 2003 by Ian Wadham and Marco Krüger *
* email : See menu "Help, About KGoldrunner" *
* *
* 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. *
***************************************************************************/
#include "kgrconsts.h"
#include "kgrobject.h"
#include "kgrcanvas.h"
#include "kgrfigure.h"
#include <stdio.h>
KGrFigure :: KGrFigure (int px, int py)
{
x = mem_x = px;
y = mem_y = py;
relx = mem_relx = 0;
rely = mem_rely = 0;
absx = px*16;
absy = py*16;
nuggets = 0;
status = STANDING;
walkTimer = new TQTimer (this);
fallTimer = new TQTimer (this);
}
// Initialise the global settings flags.
bool KGrFigure::variableTiming = TRUE;
bool KGrFigure::alwaysCollectNugget = TRUE;
bool KGrFigure::runThruHole = TRUE;
bool KGrFigure::reappearAtTop = TRUE;
SearchStrategy KGrFigure::searchStrategy = LOW;
int KGrFigure::herox = 0;
int KGrFigure::heroy = 0;
// Initialise the global game-speed factors.
int KGrFigure::speed = NSPEED;
int KGrBrick::speed = NSPEED;
// Initialise constants for fixed (KGoldrunner) and variable (Traditional)
// timing. Each row contains timings for hero walk and fall, enemy walk and
// fall, enemy captured in hole and dug brick.
Timing KGrFigure::fixedTiming = {45, 50, 55, 100, 500, 40}; // KGr original.
Timing KGrFigure::varTiming[6] = { // Traditional.
{40, 58, 78, 88, 170, 23}, // No enemies.
{50, 68, 78, 88, 170, 32}, // 1 enemy.
{57, 67, 114, 128, 270, 37}, // 2 enemies.
{60, 70, 134, 136, 330, 40}, // 3 enemies.
{63, 76, 165, 150, 400, 46}, // 4 enemies.
{70, 80, 189, 165, 460, 51} // >4 enemies.
};
int KGrBrick::HOLETIME = 0;
int KGrFigure::getx()
{
return absx;
}
int KGrFigure::gety()
{
return absy;
}
Status KGrFigure::getStatus()
{
return status;
}
void KGrFigure::init(int a,int b)
{
walkTimer->stop();
fallTimer->stop();
x = mem_x = a;
y = mem_y = b;
relx = mem_relx = 0;
rely = mem_rely = 0;
nuggets = 0;
status = STANDING;
}
void KGrFigure:: setNuggets(int n)
{
nuggets = n;
}
bool KGrFigure::canWalkRight()
{
return (((*playfield)[x+1][y]->whatIam() != BRICK) &&
((*playfield)[x+1][y]->whatIam() != BETON) &&
((*playfield)[x+1][y]->whatIam() != FBRICK));
}
bool KGrFigure::canWalkLeft()
{
return (((*playfield)[x-1][y]->whatIam() != BRICK) &&
((*playfield)[x-1][y]->whatIam() != BETON) &&
((*playfield)[x-1][y]->whatIam() != FBRICK));
}
bool KGrFigure::canWalkUp()
{
return (((*playfield)[x][y-1]->whatIam() != BRICK) &&
((*playfield)[x][y-1]->whatIam() != BETON) &&
((*playfield)[x][y-1]->whatIam() != FBRICK) &&
((*playfield)[x][y]->whatIam() == LADDER));
}
bool KGrFigure::canWalkDown()
{
return (((*playfield)[x][y+1]->whatIam() != BRICK) &&
((*playfield)[x][y+1]->whatIam() != BETON) &&
// v0.3 FIX - Let figure step down into FBRICK from a ladder.
// ((*playfield)[x][y+1]->whatIam() != FBRICK)&&
(((*playfield)[x][y+1]->whatIam() == LADDER)||
((*playfield)[x][y]->whatIam() == LADDER)));
}
bool KGrFigure::canStand()
{
return (((*playfield)[x][y+1]->whatIam() == BRICK) ||
((*playfield)[x][y+1]->whatIam() == BETON) ||
((*playfield)[x][y+1]->whatIam() == USEDHOLE)||
((*playfield)[x][y+1]->whatIam() == LADDER)||
((*playfield)[x][y]->whatIam() == LADDER)||
standOnEnemy());
}
bool KGrFigure::hangAtPole()
{
return ((*playfield)[x][y]->whatIam() == POLE);
}
void KGrFigure::walkUp(int WALKDELAY)
{
actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1;
if (walkCounter++ < 4) {
// Not end of 4-step cycle: move one step up, if possible.
if (canWalkUp()) {
rely -= STEP;
absy -= STEP;
}
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
// End of 4-step cycle: move up to next cell, if possible.
if (canWalkUp()) {
y--;
}
// Always reset position, in case we are stuck partly into a brick.
rely = 0;
absy = y*16;
// Wait for caller to set next direction.
status = STANDING;
}
}
void KGrFigure::walkDown(int WALKDELAY, int FALLDELAY)
{
if (hangAtPole() || (! canStand())) {
// On bar or no firm ground underneath: so must fall.
initFall (FALL2, FALLDELAY);
}
else {
actualPixmap = (actualPixmap == CLIMB1) ? CLIMB2 : CLIMB1;
if (walkCounter++ < 4) {
// Not end of 4-step cycle: move one step down, if possible.
if (canWalkDown()) {
rely += STEP;
absy += STEP;
}
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
// End of 4-step cycle: move down to next cell, if possible.
if (canWalkDown()) {
y++;
}
// Always reset position, in case we are stuck partly into a brick.
rely = 0;
absy = y*16;
// Must be able to halt at a pole when going down.
if (! (canStand() || hangAtPole()))
initFall(FALL2, FALLDELAY); // kein Halt....urgs
else
// Wait for caller to set next direction.
status = STANDING;
}
}
}
void KGrFigure::walkLeft (int WALKDELAY, int FALLDELAY)
{
// If counter != 0, the figure is walking, otherwise he is turning around.
if (walkCounter++ != 0) {
// Change to the next pixmap in the animation.
if ((++actualPixmap%4) != 0) {
// Not end of 4-pixmap cycle: move one step left, if possible.
if (canWalkLeft()) {
relx -= STEP;
absx -=STEP;
}
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
// End of 4-pixmap cycle: start again, in next cell if possible.
actualPixmap -= 4;
if (canWalkLeft()) {
x--;
}
// Always reset position, in case we are stuck partly into a brick.
relx = 0;
absx = x*16;
// If cannot stand or hang, start fall, else await next assignment.
if (! (canStand() || hangAtPole()))
initFall (FALL1, FALLDELAY);
else
status = STANDING; // Caller should set next direction.
}
}
else {
status = STANDING; // The figure is turning around.
}
}
void KGrFigure::walkRight(int WALKDELAY, int FALLDELAY)
{
if (walkCounter++) { // wenn 0, soll sich Figur nur umdrehen
if (++actualPixmap % 4) { // wenn true, dann ist kein vollständiger Schritt gemacht
if (canWalkRight()) {
relx += STEP;
absx += STEP; // nur vorwärts gehen, wenn es auch möglich ist
}
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
actualPixmap -= 4; // Schritt war vollendet
if (canWalkRight()) {
x++;
} //gehe entgültig nach rechts
// Always reset position, in case we are stuck partly into a brick.
relx = 0;
absx = x*16;
if (!(canStand()||hangAtPole())) // kein Halt mehr...arrrgghhh
initFall (FALL2, FALLDELAY);
else
status = STANDING; // Figur hat Halt
}
}
else {
status = STANDING; // Figur sollte sich nur Umdrehen.
}
}
void KGrFigure::initFall(int apm, int FALLDELAY)
{
status = FALLING;
actualPixmap = apm;
walkCounter=1;
walkTimer->stop();
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
}
void KGrFigure::showFigure ()
{
}
void KGrFigure::setPlayfield (KGrObject * (*p)[30][22])
{
playfield = p;
}
KGrFigure :: ~KGrFigure ()
{
}
KGrHero :: KGrHero (KGrCanvas * view, int x, int y)
:KGrFigure (x, y)
{
heroView = view;
status = STANDING;
actualPixmap = FALL1;
herox = x;
heroy = y;
started = FALSE;
mouseMode = TRUE;
walkCounter = 1;
walkFrozen = FALSE;
fallFrozen = FALSE;
connect (walkTimer, TQT_SIGNAL (timeout ()), TQT_SLOT (walkTimeDone ()));
connect (fallTimer, TQT_SIGNAL (timeout ()), TQT_SLOT (fallTimeDone ()));
}
int KGrHero::WALKDELAY = 0;
int KGrHero::FALLDELAY = 0;
/* Es ist Notwendig der eigentlichen Timerfunktion diese
Startwalk vorzuschalten, um bei einem evtl. notwendigem
Richtungswechsel der Figur diese Bewegung mit einzufügen */
void KGrHero::startWalk ()
{
switch (nextDir) {
case UP:
if ((*playfield)[x][y]->whatIam () == LADDER)
{walkCounter = 1;
direction = UP;}
break;
case RIGHT:
if (hangAtPole())
actualPixmap = RIGHTCLIMB1;
else
actualPixmap = RIGHTWALK1;
if (direction != RIGHT)
walkCounter = 0;
else
walkCounter = 1;
direction = RIGHT;
break;
case DOWN:
if (((*playfield)[x][y]->whatIam () == LADDER)||
((*playfield)[x][y+1]->whatIam () == LADDER))
{walkCounter = 1;
direction = DOWN;}
else // wenn figur an Stange haengt und nichts unter ihr,
if (hangAtPole() && (!canStand())) // dann soll sie fallen
{ status = STANDING;
actualPixmap = (direction==RIGHT)?19:18;
walkCounter=1;
direction=STAND;
walkTimer->stop();
}
break;
case LEFT:
if (hangAtPole())
actualPixmap = LEFTCLIMB1;
else
actualPixmap = LEFTWALK1;
if (direction != LEFT)
walkCounter = 0;
else
walkCounter = 1;
direction = LEFT;
break;
default :
direction = STAND;
status = FALLING;
break;
}
nextDir = STAND;
if (status != FALLING)//immer ausführen, ausser beim fallen
{ status = WALKING; // da sonst der FALLINGstatus wieder
showFigure (); // geaendert wird und der falsche Timer anspringt.
}
} // END KGrHero::startWalk
void KGrHero::setKey(Direction key)
{
// Keyboard control of hero: direction is fixed until next key is pressed.
// Sets a simulated mouse-pointer above, below, left, right or on the hero.
mouseMode = FALSE;
stopped = FALSE;
switch (key) {
case UP: mousex = x; mousey = 0; break;
case DOWN: mousex = x; mousey = FIELDHEIGHT + 1; break;
case LEFT: mousex = 0; mousey = y; break;
case RIGHT: mousex = FIELDWIDTH + 1; mousey = y; break;
case STAND: stopped = TRUE; mousex = x; mousey = y; break;
}
}
void KGrHero::setDirection(int i, int j)
{
// Mouse control of hero: direction is updated continually on a timer.
mouseMode = TRUE;
stopped = FALSE;
mousex = i;
mousey = j;
}
void KGrHero::setNextDir()
{
int dx, dy;
if (! mouseMode) {
// Keyboard control of hero: adjust simulated mouse-pointer.
if (stopped) {
mousex = x;
mousey = y;
}
if ((mousey < 1) || (mousey > FIELDHEIGHT)) {
mousex = x; // Stay directly above/below the hero.
}
else if ((mousex < 1) || (mousex > FIELDWIDTH)) {
mousey = y; // Stay directly left/right of the hero.
}
}
dx = mousex - x; dy = mousey - y;
if ((dy == 0) && (y == 1) && (nuggets <= 0)) {
nextDir = UP;
}
else if ((dy > 0) &&
(canWalkDown() ||
standOnEnemy() ||
(hangAtPole() && ((*playfield)[x][y+1]->whatIam() != BRICK) &&
((*playfield)[x][y+1]->whatIam() != BETON)))) {
nextDir = DOWN;
}
else if ((dy < 0) && canWalkUp ()) {
nextDir = UP;
}
else if (dx > 0) {
nextDir = RIGHT;
}
else if (dx < 0) {
nextDir = LEFT;
}
else if (dx == 0) {
nextDir = STAND;
}
}
void KGrHero::doStep() {
if (walkFrozen) {
walkFrozen = FALSE;
walkTimeDone();
}
if (fallFrozen) {
fallFrozen = FALSE;
fallTimeDone();
}
}
void KGrHero::showState(char option)
{
printf("(%02d,%02d) - Hero ", x, y);
switch (option) {
case 'p': printf ("\n"); break;
case 's': printf (" nuggets %02d status %d walk-ctr %d ",
nuggets, status, walkCounter);
printf ("dirn %d next dirn %d\n", direction, nextDir);
printf (" rel (%02d,%02d) abs (%03d,%03d)",
relx, rely, absx, absy);
printf (" pix %02d", actualPixmap);
printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely);
if (walkFrozen) printf (" wBlock");
if (fallFrozen) printf (" fBlock");
printf ("\n");
break;
}
}
void KGrHero::init(int a,int b)
{
walkTimer->stop();
fallTimer->stop();
walkCounter = 1;
started = FALSE;
x = mem_x = a;
y = mem_y = b;
relx = mem_relx = 0;
rely = mem_rely = 0;
absx = 16*x;
absy = 16*y;
nuggets = 0;
if (herox < 1) { // If first call to init, ...
heroView->makeHeroSprite (x, y, actualPixmap);
}
herox = x;
heroy = y;
actualPixmap = FALL2;
heroView->moveHero (absx, absy, actualPixmap);
}
void KGrHero::start()
{
started = TRUE;
walkFrozen = FALSE;
fallFrozen = FALSE;
if (!(canStand()||hangAtPole())) { // Held muss wohl fallen...
status = FALLING;
fallTimeDone();
}
else {
status = STANDING;
walkTimeDone();
}
}
void KGrHero::setSpeed (int gamespeed)
{
if (gamespeed >= 0) {
if (gamespeed < MINSPEED)
speed++; // Increase speed.
else
speed = gamespeed; // Set selected speed.
if (speed > MAXSPEED)
speed = MAXSPEED; // Set upper limit.
}
else {
speed--; // Reduce speed.
if (speed < MINSPEED)
speed = MINSPEED; // Set lower limit.
}
KGrBrick::speed = speed; // Make a copy for bricks.
}
void KGrHero::walkTimeDone ()
{
if (! started) return; // Ignore signals from earlier play.
if (KGrObject::frozen) {walkFrozen = TRUE; return; }
if ((*playfield)[x][y]->whatIam() == BRICK) {
emit caughtHero(); // Brick closed over hero.
return;
}
if ((y==1)&&(nuggets<=0)) { // If on top row and all nuggets collected,
emit leaveLevel(); // the hero has won and can go to next level.
return;
}
if (status == STANDING)
setNextDir();
if ((status == STANDING) && (nextDir != STAND)) {
if ((standOnEnemy()) && (nextDir == DOWN)) {
emit caughtHero(); // Hero is going to step down into an enemy.
return;
}
startWalk();
}
if (status != STANDING) {
switch (direction) {
case UP: walkUp (WALKDELAY); break;
case DOWN: walkDown (WALKDELAY, FALLDELAY); break;
case RIGHT: walkRight (WALKDELAY, FALLDELAY); break;
case LEFT: walkLeft (WALKDELAY, FALLDELAY); break;
default :
// The following code is strange. It makes the hero fall off a pole.
// It works because of other strange code in "startWalk(), case DOWN:".
if (!canStand()||hangAtPole()) // falling
initFall(FALL1, FALLDELAY);
else status = STANDING;
break;
}
herox=x;heroy=y; // Koordinatenvariablen neu
// wenn Held genau ein Feld weitergelaufen ist,
if ((relx==0)&&(rely==0)) // dann setzte statische
{
collectNugget(); // und nehme evtl. Nugget
}
showFigure(); // Is this REDUNDANT now? See showFigure() below.
//////////////////////////////////////////////////
}
if (status == STANDING)
if (!canStand()&&!hangAtPole())
initFall(FALL1, FALLDELAY);
else
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
// This additional showFigure() is to update the hero position after it is
// altered by the hero-enemy deadlock fix in standOnEnemy(). Messy, but ...
////////////////////////////////////////////////////////////////////////////
showFigure();
if(isInEnemy()) {
walkTimer->stop();
emit caughtHero();
}
}
void KGrHero::fallTimeDone()
{
if (! started) return; // Ignore signals from earlier play.
if (KGrObject::frozen) {fallFrozen = TRUE; return; }
if (!standOnEnemy()) {
if (walkCounter++ < 4) { // Held fällt vier Positionen
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
rely+=STEP;
absy+=STEP;
}
else { //Held ist ein Feld weitergefallen
// Verschiebung der Figur zum 0-Punkt des Objekts (Brick etc...)
heroy = ++y;
rely = 0;
absy = y*16; // wird Null und Figur eins runter
collectNugget(); // gesetzt. Zeit evtl. Nugget zu nehmen
if (! (canStand()||hangAtPole())) { // Held muss wohl weiterfallen.
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
walkCounter = 1;
}
else { // Held hat Boden (oder Feind) unter den
status = STANDING; // Füssen oder hängt an Stange -> steh!
walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE);
direction = (actualPixmap == 19) ? RIGHT : LEFT;
if ((*playfield)[x][y]->whatIam() == POLE)
actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1;
// else
// Reduce jerkiness when descending over a falling enemy.
// actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1;
}
}
showFigure();
}
else {
if (rely == 0) {
// If at the bottom of a cell, try to walk or just stand still.
status = STANDING;
direction = (actualPixmap == 19) ? RIGHT : LEFT;
if ((*playfield)[x][y]->whatIam() == POLE)
actualPixmap = (direction == RIGHT)? RIGHTCLIMB1:LEFTCLIMB1;
// else
// Reduce jerkiness when descending over a falling enemy.
// actualPixmap = (direction == RIGHT)? RIGHTWALK1:LEFTWALK1;
walkTimer->start((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
// Else, freeze hero until enemy moves out of the way.
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
}
}
if (isInEnemy() && (! standOnEnemy()))
emit caughtHero();
}
void KGrHero::showFigure () {
heroView->moveHero (absx, absy, actualPixmap);
// Merke alte Werte zum löschen der Figur
mem_x = x;
mem_y = y;
mem_relx = relx;
mem_rely = rely;
}
void KGrHero::dig(){
if (direction == LEFT)
digLeft();
else
if (direction == RIGHT)
digRight();
}
void KGrHero::digLeft(){
int i = 1; // If stationary or moving up/down, dig at x-1.
if (status == STANDING)
setNextDir();
if ((status == WALKING) ||
((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) {
if ((direction == LEFT) && canWalkLeft())
i = 2; // If walking left, dig at x-2 and stop at x-1.
else if ((direction == RIGHT) && canWalkRight())
i = 0; // If walking right, dig at x and stop at x+1.
}
if (((*playfield)[x-i][y+1]->whatIam() == BRICK)&&
(((*playfield)[x-i][y]->whatIam() == HLADDER)||
((*playfield)[x-i][y]->whatIam() == FREE)||
((*playfield)[x-i][y]->whatIam() == HOLE)))
((KGrBrick*)(*playfield)[x-i][y+1])->dig();
}
void KGrHero::digRight(){
int i = 1; // If stationary or moving up/down, dig at x+1.
if (status == STANDING)
setNextDir();
if ((status == WALKING) ||
((status == STANDING) && ((nextDir == LEFT) || (nextDir == RIGHT)))) {
if ((direction == LEFT) && canWalkLeft())
i = 0; // If walking left, dig at x and stop at x-1.
else if ((direction == RIGHT) && canWalkRight())
i = 2; // If walking right, dig at x+2 and stop at x+1.
}
if (((*playfield)[x+i][y+1]->whatIam() == BRICK)&&
(((*playfield)[x+i][y]->whatIam() == HLADDER)||
((*playfield)[x+i][y]->whatIam() == FREE)||
((*playfield)[x+i][y]->whatIam() == HOLE)))
((KGrBrick*)(*playfield)[x+i][y+1])->dig();
}
#ifdef QT3
void KGrHero::setEnemyList(TQPtrList<KGrEnemy> *e)
#else
void KGrHero::setEnemyList(TQList<KGrEnemy> *e)
#endif
{
enemies = e;
}
bool KGrHero::standOnEnemy()
{
int c = 0;
int range = enemies->count();
if (range > 0) {
for (KGrEnemy * enemy = enemies->at (c); c < range; ) {
enemy = enemies->at(c++);
// Test if hero's foot is at or just below enemy's head (tolerance
// of 4 pixels) and the two figures overlap in the X direction.
if ((((absy + 16) == enemy->gety()) ||
((absy + 12) == enemy->gety())) &&
(((absx - 16) < enemy->getx()) &&
((absx + 16) > enemy->getx()))) {
if (((absy + 12) == enemy->gety()) &&
(enemy->getStatus() != FALLING)) {
absy = absy - rely; // Bounce back from overlap, to avoid
rely = 0; // hero-enemy mid-cycle deadlock.
walkCounter = 1;
}
return true;
}
}
}
return false;
}
void KGrHero::collectNugget(){
if ((*playfield)[x][y]->whatIam() == NUGGET)
{
((KGrFree *)(*playfield)[x][y])->setNugget(false);
if (!(--nuggets))
emit haveAllNuggets(); // sendet der Application dass alle Nuggets
// gesammelt sind, um versteckte Leitern zu zeigen
emit gotNugget(250); // sendet der Application ein Nugget um Score zu erhöhen
}
}
void KGrHero::loseNugget() {
// Enemy trapped or dead and could not drop nugget (NO SCORE for this).
if (! (--nuggets))
emit haveAllNuggets(); // Sendet der Application dass alle Nuggets
// gesammelt sind, um versteckte Leitern zu zeigen.
}
bool KGrHero::isInEnemy(){
int c=0;
int range=enemies->count();
if (range)
for (KGrEnemy *enemy=enemies->at(c);c<range; )
{enemy = enemies->at(c++);
if (isInside(enemy->getx(),enemy->gety())||
isInside(enemy->getx()-15,enemy->gety())||
isInside(enemy->getx(),enemy->gety()-15))
return true;}
return false;
}
bool KGrHero::isInside(int enemyx, int enemyy){
return ((absx >= enemyx)&&
(absx <= enemyx+15)&&
(absy >= enemyy)&&
(absy <= enemyy+15));
}
KGrHero :: ~KGrHero (){
delete walkTimer;
delete fallTimer;
}
KGrEnemy :: KGrEnemy (KGrCanvas * view, int x, int y)
: KGrFigure (x, y)
{
enemyView = view;
actualPixmap = FALL1;
nuggets = 0;
enemyView->makeEnemySprite (x, y, actualPixmap);
walkCounter = 1;
captiveCounter = 0;
searchStatus = HORIZONTAL;
birthX=x;
birthY=y;
walkFrozen = FALSE;
fallFrozen = FALSE;
captiveFrozen = FALSE;
captiveTimer = new TQTimer (this);
connect (captiveTimer,TQT_SIGNAL(timeout()),TQT_SLOT(captiveTimeDone()));
connect (walkTimer, TQT_SIGNAL (timeout ()), TQT_SLOT (walkTimeDone ()));
connect (fallTimer, TQT_SIGNAL (timeout ()), TQT_SLOT (fallTimeDone ()));
}
int KGrEnemy::WALKDELAY = 0;
int KGrEnemy::FALLDELAY = 0;
int KGrEnemy::CAPTIVEDELAY = 0;
void KGrEnemy::doStep() {
if (walkFrozen) {
walkFrozen = FALSE;
walkTimeDone();
}
if (fallFrozen) {
fallFrozen = FALSE;
fallTimeDone();
}
if (captiveFrozen) {
captiveFrozen = FALSE;
captiveTimeDone();
}
}
void KGrEnemy::showState(char option)
{
printf("(%02d,%02d) - Enemy [%d]", x, y, enemyId);
switch (option) {
case 'p': printf ("\n"); break;
case 's': printf (" nuggets %02d status %d walk-ctr %d ",
nuggets, status, walkCounter);
printf ("dirn %d search %d capt-ctr %d\n",
direction, searchStatus, captiveCounter);
printf (" rel (%02d,%02d) abs (%03d,%03d)",
relx, rely, absx, absy);
printf (" pix %02d", actualPixmap);
printf (" mem %d %d %d %d", mem_x, mem_y, mem_relx, mem_rely);
if (walkFrozen) printf (" wBlock");
if (fallFrozen) printf (" fBlock");
if (captiveFrozen) printf (" cBlock");
printf ("\n");
break;
}
}
void KGrEnemy::init(int a,int b)
{
walkTimer->stop(); // alles stoppen bevor die Werte neu gesetzt
fallTimer->stop(); // werden, da es sonst zu ungewollten Effekten
captiveTimer->stop(); // kommen kann
walkCounter = 1;
captiveCounter = 0;
x = mem_x = a;
y = mem_y = b;
relx = mem_relx = 0;
rely = mem_rely = 0;
absx=16*x;
absy=16*y;
actualPixmap = 19;
status = STANDING;
}
void KGrEnemy::walkTimeDone ()
{
if (KGrObject::frozen) {walkFrozen = TRUE; return; }
// Check we are alive BEFORE checking for friends being in the way.
// Maybe a friend overhead is blocking our escape from a brick.
if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick
dieAndReappear(); // sein, dann stirbt er wohl
return; // Must leave "walkTimeDone" when an enemy dies.
}
if (! bumpingFriend()) {
switch (direction) {
case UP: walkUp (WALKDELAY);
if ((rely == 0) &&
((*playfield)[x][y+1]->whatIam() == USEDHOLE))
// Enemy kletterte grad aus einem Loch hinaus
// -> gib es frei!
((KGrBrick *)(*playfield)[x][y+1])->unUseHole();
break;
case DOWN: walkDown (WALKDELAY, FALLDELAY); break;
case RIGHT: walkRight (WALKDELAY, FALLDELAY); break;
case LEFT: walkLeft (WALKDELAY, FALLDELAY); break;
default: // Switch search direction in KGoldrunner search (only).
searchStatus = (searchStatus==VERTIKAL) ?
HORIZONTAL : VERTIKAL;
// In KGoldrunner rules, if a hole opens under an enemy
// who is standing and waiting to move, he should fall.
if (!(canStand()||hangAtPole())) {
initFall (actualPixmap, FALLDELAY);
}
else {
status = STANDING;
}
break;
}
// wenn die Figur genau ein Feld gelaufen ist
if (status == STANDING) { // dann suche den Helden
direction = searchbestway(x,y,herox,heroy); // und
if (walkCounter >= 4) {
if (! nuggets)
collectNugget();
else
dropNugget();
}
status = WALKING; // initialisiere die Zählervariablen und
walkCounter = 1; // den Timer um den Held weiter
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE); // zu jagen
startWalk ();
}
}
else {
// A friend is in the way. Try a new direction, but not if leaving a hole.
Direction dirn;
// In KGoldrunner rules, change the search strategy,
// to avoid enemy-enemy deadlock.
searchStatus = (searchStatus==VERTIKAL) ? HORIZONTAL : VERTIKAL;
dirn = searchbestway (x, y, herox, heroy);
if ((dirn != direction) && ((*playfield)[x][y]->whatIam() != USEDHOLE)) {
direction = dirn;
status = WALKING;
walkCounter = 1;
relx = 0; absx = 16 * x;
rely = 0; absy = 16 * y;
startWalk ();
}
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
showFigure();
}
void KGrEnemy::fallTimeDone ()
{
if (KGrObject::frozen) {fallFrozen = TRUE; return; }
if ((*playfield)[x][y+1]->whatIam() == HOLE) { // wenn Enemy ins Loch fällt
((KGrBrick*)(*playfield)[x][y+1])->useHole(); // reserviere das Loch, damit
// kein anderer es benutzt und
if (nuggets) { // er muss Gold vorher fallen lassen
nuggets=0;
switch ((*playfield)[x][y]->whatIam()) {
case FREE:
case HLADDER:
((KGrFree *)(*playfield)[x][y])->setNugget(true); break;
default:
emit lostNugget(); break; // Cannot drop the nugget here.
}
}
emit trapped (75); // Enemy trapped: score 75.
}
else if (walkCounter <= 1) {
// Enemies collect nuggets when falling.
if (!nuggets) {
collectNugget();
}
}
if ((*playfield)[x][y]->whatIam()==BRICK) { // sollte er aber in einem Brick
dieAndReappear(); // sein, dann stirbt er wohl
return; // Must leave "fallTimeDone" when an enemy dies.
}
if (standOnEnemy()) { // Don't fall into a friend.
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
return;
}
if (walkCounter++ < 4){
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
{ rely+=STEP; absy+=STEP;}
}
else {
rely = 0; y ++; absy=16*y;
if ((*playfield)[x][y]->whatIam() == USEDHOLE) {
captiveCounter = 0;
status = CAPTIVE;
captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE);
}
else if (!(canStand()||hangAtPole())) {
fallTimer->start((FALLDELAY * NSPEED) / speed, TRUE);
walkCounter=1;
}
else {
status = STANDING;
if (hangAtPole())
actualPixmap=(direction ==RIGHT)?8:12;
}
}
if (status == STANDING) {
status = WALKING;
walkCounter = 1;
direction = searchbestway(x,y,herox,heroy);
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
startWalk ();
if (!nuggets)
collectNugget();
else
dropNugget();
}
showFigure();
}
void KGrEnemy::captiveTimeDone()
{
if (KGrObject::frozen) {captiveFrozen = TRUE; return; }
if ((*playfield)[x][y]->whatIam()==BRICK)
dieAndReappear();
else
if (captiveCounter > 6){
status = WALKING;
walkCounter = 1;
direction = UP;
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
captiveCounter = 0;
} else {
captiveCounter ++;
captiveTimer->start((CAPTIVEDELAY * NSPEED) / speed, TRUE);
showFigure();
}
}
void KGrEnemy::startSearching()
{
// Called from "KGoldrunner::startPlaying" and "KGrEnemy::dieAndReappear".
init(x,y);
if (canStand()||((*playfield)[x][y+1]->whatIam()==USEDHOLE)) {
status = WALKING;
walkTimer->start ((WALKDELAY * NSPEED) / speed, TRUE);
}
else {
status = FALLING;
fallTimer->start ((FALLDELAY * NSPEED) / speed, TRUE);
}
walkCounter = 1;
direction = searchbestway(x,y,herox,heroy);
startWalk();
}
void KGrEnemy::collectNugget()
{
if (((*playfield)[x][y]->whatIam() == NUGGET) && (nuggets == 0) &&
(alwaysCollectNugget || ((int)(5.0*rand()/RAND_MAX+1.0) > 4))){
((KGrFree *)(*playfield)[x][y])->setNugget(false);
nuggets=1;
}
}
void KGrEnemy::dropNugget()
{
if (((int)(DROPNUGGETDELAY*rand()/RAND_MAX+1.0) > DROPNUGGETDELAY-5) &&
((*playfield)[x][y]->whatIam() == FREE)) {
((KGrFree *)(*playfield)[x][y])->setNugget(true);
nuggets=0;
}
}
void KGrEnemy::showFigure ()
{
enemyView->moveEnemy (enemyId, absx, absy, actualPixmap, nuggets);
// Merke alte Werte zum löschen der Figur
mem_x = x;
mem_y = y;
mem_relx = relx;
mem_rely = rely;
}
bool KGrEnemy::canWalkUp()
{
return (((*playfield)[x][y-1]->whatIam() != BRICK) &&
((*playfield)[x][y-1]->whatIam() != BETON) &&
((*playfield)[x][y-1]->whatIam() != FBRICK) &&
(((*playfield)[x][y]->whatIam() == USEDHOLE) ||
((*playfield)[x][y]->whatIam() == LADDER)));
}
void KGrEnemy::startWalk ()
{
switch (direction) {
case UP: break;
case RIGHT: if (hangAtPole())
actualPixmap = RIGHTCLIMB1;
else
actualPixmap = RIGHTWALK1;
break;
case DOWN: break;
case LEFT: if (hangAtPole())
actualPixmap = LEFTCLIMB1;
else
actualPixmap = LEFTWALK1;
break;
default: break;
}
}
void KGrEnemy::dieAndReappear()
{
bool looking;
int i;
if (nuggets > 0) {
nuggets = 0; // Enemy died and could not drop nugget.
emit lostNugget(); // NO SCORE for lost nugget.
}
emit killed (75); // Killed an enemy: score 75.
if (reappearAtTop) {
// Follow Traditional rules: enemies reappear at top.
looking = TRUE;
y = 2;
// Randomly look for a free spot in row 2. Limit the number of tries.
for (i = 1; ((i <= 3) && looking); i++) {
x = (int)((FIELDWIDTH * (float) rand()) / RAND_MAX) + 1;
switch ((*playfield)[x][2]->whatIam()) {
case FREE:
case HLADDER:
looking = FALSE;
break;
default:
break;
}
}
// If unsuccessful, choose the first free spot in row 3 or below.
while ((y<FIELDHEIGHT) && looking) {
y++;
x = 0;
while ((x<FIELDWIDTH) && looking) {
x++;
switch ((*playfield)[x][y]->whatIam()) {
case FREE:
case HLADDER:
looking = FALSE;
break;
default:
break;
}
}
}
}
else {
// Follow KGoldrunner rules: enemies reappear where they started.
x = birthX;
y = birthY;
}
// Enemy reappears and starts searching for the hero.
startSearching();
}
Direction KGrEnemy::searchbestway(int ew,int eh,int hw,int hh)
{
Direction dirn;
if ((*playfield)[x][y]->whatIam() == USEDHOLE) // Could not get out of
return UP; // hole (eg. brick above
// closed): keep trying.
if (!canStand() && // Cannot stand,
!hangAtPole() && // not on pole, not
!standOnEnemy() && // walking on friend and
!((*playfield)[x][y+1]->whatIam() == HOLE)) // not just out of hole,
return DOWN; // so must fall.
switch (searchStrategy) {
// Traditional search strategy.
case LOW:
dirn = STAND;
if (eh == hh) { // Hero on same row.
dirn = lowGetHero (ew, eh, hw);
}
if (dirn != STAND) return (dirn); // Can go towards him.
if (eh >= hh) { // Hero above enemy.
dirn = lowSearchUp (ew, eh, hh); // Find a way up.
}
else { // Hero below enemy.
dirn = lowSearchDown (ew, eh, hh); // Find way down to him.
if (dirn == STAND) {
dirn = lowSearchUp (ew, eh, hh); // No go: try up.
}
}
if (dirn == STAND) { // When all else fails,
dirn = lowSearchDown (ew, eh, eh - 1); // find way below hero.
}
return dirn;
break;
// KGoldrunner search strategy.
case MEDIUM:
case HIGH:
if(searchStatus==VERTIKAL){
if (eh > hh)
return searchupway(ew,eh);
if (eh < hh)
return searchdownway(ew,eh);
return STAND;
} else {
if (ew > hw)
return searchleftway(ew,eh);
if (ew < hw)
return searchrightway(ew,eh);
return STAND;
}
break;
}
return STAND;
}
///////////////////////////////////////////////
// Methods for medium-level search strategy. //
///////////////////////////////////////////////
Direction KGrEnemy :: searchdownway(int ew,int eh){
int i,j;
i=j=ew;
if ( (*playfield)[ew][eh]->searchValue & CANWALKDOWN)
return DOWN;
else while((i>=0)||(j<=FIELDWIDTH)){
if (i>=0)
if ( (*playfield)[i][eh]->searchValue & CANWALKDOWN)
return LEFT;
else
if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) ||
(runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE))))
i=-1;
if (j<=FIELDWIDTH)
if ( (*playfield)[j][eh]->searchValue & CANWALKDOWN)
return RIGHT;
else
if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) ||
(runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE))))
j=FIELDWIDTH+1;
}
return STAND;
}
Direction KGrEnemy :: searchupway(int ew,int eh){
int i,j;
i=j=ew;
if ( (*playfield)[ew][eh]->searchValue & CANWALKUP)
return UP;
else while((i>=0)||(j<=FIELDWIDTH)){// search for the first way up
if (i>=0)
if ( (*playfield)[i][eh]->searchValue & CANWALKUP)
return LEFT;
else
if (!(( (*playfield)[i--][eh]->searchValue & CANWALKLEFT) ||
(runThruHole && ( (*playfield)[i][eh]->whatIam() == HOLE))))
i=-1;
if (j<=FIELDWIDTH)
if ( (*playfield)[j][eh]->searchValue & CANWALKUP)
return RIGHT;
else
if (!(( (*playfield)[j++][eh]->searchValue & CANWALKRIGHT) ||
(runThruHole && ( (*playfield)[j][eh]->whatIam() == HOLE))))
j=FIELDWIDTH+1;
}
// BUG FIX - Ian W., 30/4/01 - Don't leave an enemy standing in mid air.
if (!canStand()) return DOWN; else return STAND;
}
Direction KGrEnemy :: searchleftway(int ew,int eh){
int i,j;
i=j=eh;
if ( ((*playfield)[ew][eh]->searchValue & CANWALKLEFT) || /* kann figur nach links laufen ?*/
(runThruHole && ( (*playfield)[ew-1][eh]->whatIam() == HOLE)))
return LEFT;
else while((i>=0)||(j<=FIELDHEIGHT)){ /* in den grenzen ?*/
if (i>=0)
if ( ((*playfield)[ew][i]->searchValue & CANWALKLEFT) || /* ein weg nach links- oben gefunden ?*/
(runThruHole && ( (*playfield)[ew-1][i]->whatIam() == HOLE)))
return UP; /* geh nach oben */
else
if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP)) /* sonst ...*/
i=-1;
if (j<=FIELDHEIGHT)
if ( ((*playfield)[ew][j]->searchValue & CANWALKLEFT) || /* ein weg nach links- unten gefunden ?*/
(runThruHole && ( (*playfield)[ew-1][j]->whatIam() == HOLE)))
return DOWN; /* geh nach unten */
else
if (!( (*playfield)[ew][j++]->searchValue&CANWALKDOWN)) /* sonst ... */
j=FIELDHEIGHT+1;
}
return STAND; /* default */
}
Direction KGrEnemy :: searchrightway(int ew,int eh)
{
int i,j;
i=j=eh;
if ( ((*playfield)[ew][eh]->searchValue & CANWALKRIGHT) ||
(runThruHole && ( (*playfield)[ew+1][eh]->whatIam() == HOLE)))
return RIGHT;
else while((i>=0)||(j<=FIELDHEIGHT)){
if (i>=0)
if ( ((*playfield)[ew][i]->searchValue & CANWALKRIGHT) ||
(runThruHole && ( (*playfield)[ew+1][i]->whatIam() == HOLE)))
return UP;
else
if (!( (*playfield)[ew][i--]->searchValue & CANWALKUP))
i=-1;
if (j<=FIELDHEIGHT)
if ( ((*playfield)[ew][j]->searchValue & CANWALKRIGHT) ||
(runThruHole && ( (*playfield)[ew+1][j]->whatIam() == HOLE)))
return DOWN;
else
if (!( (*playfield)[ew][j++]->searchValue & CANWALKDOWN))
j=FIELDHEIGHT+1;
}
return STAND;
}
////////////////////////////////////////////
// Methods for low-level search strategy. //
////////////////////////////////////////////
Direction KGrEnemy::lowSearchUp (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 KGrEnemy::lowSearchDown (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 KGrEnemy::lowGetHero (int ew, int eh, int hw)
{
int i, inc, returnValue;
inc = (ew > hw) ? -1 : +1;
i = ew;
while (i != hw) {
returnValue = canWalkLR (inc, i, eh);
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 < ew) return LEFT;
else if (i > ew) return RIGHT;
else return STAND;
}
int KGrEnemy::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 ( (*playfield)[x][y - rungs]->whatIam() == LADDER) {
rungs++;
if (rungs >= deltah) // To hero's level is enough.
break;
}
return rungs;
}
int KGrEnemy::distanceDown (int x, int y, int deltah)
{
// When deltah > 0, we want an exit sideways at the hero's level or above.
// When deltah <= 0, we can go down any distance (as a last resort).
int rungs = -1;
int exitRung = 0;
bool canGoThru = TRUE;
char objType;
// If there is a way down at (x,y), return its length, else return zero.
// Because rungs == -1, we first check that level y is not blocked here.
while (canGoThru) {
objType = (*playfield)[x][y + rungs + 1]->whatIam();
switch (objType) {
case BRICK:
case BETON:
case HOLE: // Enemy cannot go DOWN through a hole.
case USEDHOLE:
if ((deltah > 0) && (rungs <= deltah))
exitRung = rungs;
if ((objType == HOLE) && (rungs < 0))
rungs = 0; // Enemy can go SIDEWAYS through a hole.
else
canGoThru = FALSE; // Cannot go through here.
break;
case LADDER:
case POLE: // Can go through or stop.
rungs++; // Add to path length.
if ((deltah > 0) && (rungs >= 0)) {
// If at or above hero's level, check for an exit from ladder.
if ((rungs - 1) <= deltah) {
// Maybe can stand on top of ladder and exit L or R.
if ((objType == LADDER) && (searchOK (-1, x, y+rungs-1) ||
searchOK (+1, x, y+rungs-1)))
exitRung = rungs - 1;
// Maybe can exit L or R from ladder body or pole.
if ((rungs <= deltah) && (searchOK (-1, x, y+rungs) ||
searchOK (+1, x, y+rungs)))
exitRung = rungs;
}
else
canGoThru = FALSE; // Should stop at hero's level.
}
break;
default:
rungs++; // Can go through. Add to path length.
break;
}
}
if (rungs == 1) {
for (KGrEnemy *enemy=enemies->first();enemy!=0;enemy=enemies->next()) {
if((x*16==enemy->getx()) && (y*16+16==enemy->gety()))
rungs = 0; // Pit is blocked. Find another way.
}
}
if (rungs <= 0)
return 0; // There is no way down.
else if (deltah > 0)
return exitRung; // We want to take an exit, if any.
else
return rungs; // We can go down all the way.
}
bool KGrEnemy::searchOK (int direction, int x, int y)
{
// Check whether it is OK to search left or right.
if (canWalkLR (direction, x, y) > 0) {
// Can go into that cell, but check for a fall.
if (willNotFall (x+direction, y))
return TRUE;
}
return FALSE; // Cannot go into and through that cell.
}
int KGrEnemy::canWalkLR (int direction, int x, int y)
{
if (willNotFall (x, y)) {
switch ((*playfield)[x+direction][y]->whatIam()) {
case BETON:
case BRICK:
case USEDHOLE:
return -1; break; // Will be halted in current cell.
default:
// NB. FREE, LADDER, HLADDER, NUGGET and POLE are OK of course,
// but enemies can also walk left/right through a HOLE and
// THINK they can walk left/right through a FBRICK.
return +1; break; // Can walk into next cell.
}
}
else
return 0; // Will fall before getting there.
}
bool KGrEnemy::willNotFall (int x, int y)
{
int c, cmax;
KGrEnemy *enemy;
// Check the ceiling.
switch ( (*playfield)[x][y]->whatIam()) {
case LADDER:
case POLE:
return TRUE; break; // OK, can hang on ladder or pole.
default:
break;
}
// Check the floor.
switch ( (*playfield)[x][y+1]->whatIam()) {
// Cases where the enemy knows he will fall.
case FREE:
case HLADDER:
case FBRICK:
// N.B. The enemy THINKS he can run over a NUGGET, a buried POLE or a HOLE.
// The last of these cases allows the hero to trap the enemy, of course.
// Note that there are several Traditional levels that require an enemy to
// be trapped permanently in a pit containing a nugget, as he runs towards
// you. It is also possible to use a buried POLE in the same way.
cmax = enemies->count();
for (c = 0; c < cmax; c++) {
enemy = enemies->at(c);
if ((enemy->getx()==16*x) && (enemy->gety()==16*(y+1)))
return TRUE; // Standing on a friend.
}
return FALSE; break; // Will fall: there is no floor.
default:
return TRUE; break; // OK, will not fall.
}
}
#ifdef QT3
void KGrEnemy::setEnemyList(TQPtrList<KGrEnemy> *e)
#else
void KGrEnemy::setEnemyList(TQList<KGrEnemy> *e)
#endif
{
enemies = e;
}
bool KGrEnemy::standOnEnemy()
{
int c = 0;
int range = enemies->count();
if (range > 1) {
for (KGrEnemy * enemy = enemies->at (c); c < range; ) {
enemy = enemies->at(c++);
// Test if enemy's foot is at or just below enemy's head (tolerance
// of 4 pixels) and the two figures overlap in the X direction.
if ((((absy + 16) == enemy->gety()) ||
((absy + 12) == enemy->gety())) &&
(((absx - 16) < enemy->getx()) &&
((absx + 16) > enemy->getx()))) {
return true;
}
}
}
return false;
}
bool KGrEnemy::bumpingFriend()
{
// Enemies that are falling are treated as being invisible (i.e. a walking
// enemy will walk through them or they will stop on that enemy's head).
//
// If two enemies are moving in opposite directions, they are allowed to come
// within two cell widths of each other (8 steps). Then one must stop before
// entering the next cell and let the other one go into it. If both are about
// to enter a new cell, the one on the right or above gives way to the one on
// the left or below (implemented by letting the latter approach to 7 steps
// apart before detecting an impending collision, by which time the first
// enemy should have stopped and given way).
//
// In all other cases, enemies are allowed to approach to 4 steps apart (i.e.
// bumping a friend), before being forced to stop and wait. If they somehow
// get closer than 4 steps (i.e. overlapping), the lower ID enemy is allowed
// to move out while the higher ID enemy waits. This can happen if one enemy
// falls into another or is reborn where another enemy is located.
int c, cmax;
KGrEnemy *enemy;
int dx, dy;
cmax = enemies->count();
for (c = 0; c < cmax; c++) {
enemy = enemies->at(c);
if ((enemy->enemyId != enemyId) && (enemy->status != FALLING)) {
dx = enemy->getx() - absx;
dy = enemy->gety() - absy;
switch (direction) {
case LEFT:
if ((dx >= -32) && (dx < 16) && (dy > -16) && (dy < 16)) {
if ((enemy->direction == RIGHT) &&
(enemy->status == WALKING) && (absx%16==0)) {
return TRUE;
}
else if (dx >= -16) {
if ((dx > -16) && (enemyId < enemy->enemyId))
return FALSE;
else
return TRUE; // Wait for the one in front.
}
}
break;
case RIGHT:
if ((dx > -16) && (dx < 32) && (dy > -16) && (dy < 16)) {
if ((enemy->direction == LEFT) &&
(enemy->status == WALKING) && (absx%16==0)) {
return TRUE;
}
else if (dx <= 16) {
if ((dx < 16) && (enemyId < enemy->enemyId))
return FALSE;
else
return TRUE; // Wait for the one in front.
}
}
break;
case UP:
if ((dy >= -32) && (dy < 16) && (dx > -16) && (dx < 16)) {
if ((enemy->direction == DOWN) &&
(enemy->status == WALKING) && (absy%16==0)) {
return TRUE;
}
else if (dy >= -16) {
if ((dy > -16) && (enemyId < enemy->enemyId))
return FALSE;
else
return TRUE; // Wait for the one above.
}
}
break;
case DOWN:
if ((dy > -16) && (dy < 32) && (dx > -16) && (dx < 16)) {
if ((enemy->direction == UP) &&
(enemy->status == WALKING) && (absy%16==0)) {
return TRUE;
}
else if (dy <= 16) {
if ((dy < 16) && (enemyId < enemy->enemyId))
return FALSE;
else
return TRUE; // Wait for the one below.
}
}
break;
default:
break;
}
}
}
return FALSE;
}
KGrEnemy :: ~KGrEnemy ()
{
delete captiveTimer;
delete walkTimer;
delete fallTimer;
}
#include "kgrfigure.moc"