/*************************************************************************** * 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 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 *e) #else void KGrHero::setEnemyList(TQList *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);cat(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 ((ywhatIam()) { 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 *e) #else void KGrEnemy::setEnemyList(TQList *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"