#include #include #include #include #include #include #include #include #include "ai_player.h" #include "blast.h" #include "bomb.h" #include "item.h" #include "playcanvas.h" #include "player.h" #include "resource.h" #include "exit.h" #include "rule.h" #include #include #include #include #include PlayCanvas::PlayCanvas(QObject *parent, const char *name) : QCanvas(parent, name) { m_level = NULL; m_away = false; m_enabled = false; m_localenabled = false; m_players = 3; m_rules = NULL; m_score = 0; m_clock = 0; weapon = NULL; weaponcounter = 0; exit = NULL; currPlayer = NULL; overlayfont = NULL; srand(time(0) + getpid()); resource = new Resource(); bombList.setAutoDelete(true); blastList.setAutoDelete(true); playerList.setAutoDelete(true); itemList.setAutoDelete(true); startTimer(100); monsterTimer = new QTimer(this); connect(monsterTimer, SIGNAL(timeout()), this, SLOT(monsterTimeout())); monsterTimer->start(500); clockTimer = new QTimer(this); connect(clockTimer, SIGNAL(timeout()), this, SLOT(clockTimeout())); clockTimer->start(1000); } PlayCanvas::~PlayCanvas() { if (monsterTimer) delete monsterTimer; playerList.remove(currPlayer); currPlayer = 0; } void PlayCanvas::setTheme(QString theme) { if(theme.isNull()) return; resource->setTheme(theme); // reload everything if(m_level) { QPixmap background(resource->path("background")); QImage im = background.convertToImage(); im = im.smoothScale(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height()); background.convertFromImage(im); setBackgroundPixmap(background); resize(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height()); } drawBackground(false, 0, 0); // greyed out if(m_enabled == false) { if(!backgroundPixmap().isNull()) { QImage im = backgroundPixmap().convertToImage(); for(int j = 0; j < im.height(); j += 2) for(int i = 0; i < im.width(); i += 2) im.setPixel(i, j, 0); QPixmap background; background.convertFromImage(im); /*QPainter p; QFont f(QString::null, 48, QFont::Black); p.begin(&background); p.setPen(QPen(QColor(255, 0, 0), 4)); p.setFont(f); QFontMetrics m(f); p.drawText(im.width() / 2 - m.width(i18n("Loser!")) / 2, im.height() / 2 - 24, i18n("Loser!")); p.end();*/ setBackgroundPixmap(background); } } // reload all items if(exit) { exit->setSprite(resource->pixmaparray("exit", 3)); exit->move(exit->x(), exit->y()); } if(weapon) { weapon->setSprite(resource->pixmaparray(weapontype(weaponcounter), 8)); weapon->move(weapon->x(), weapon->y()); } if(currPlayer) { currPlayer->setSprite(resource->pixmaparray("guy1", 1/*12*/)); currPlayer->move(currPlayer->x(), currPlayer->y()); } for (Player *player = playerList.first(); player != NULL; player = playerList.next()) { player->setSprite(resource->pixmaparray("guy1", 1/*12*/)); player->move(player->x(), player->y()); } for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { monster->setSprite(resource->pixmaparray("monster1", 2/*13*/)); monster->move(monster->x(), monster->y()); } for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) { bomb->setSprite(resource->pixmaparray("bomb", /*2*/8)); bomb->move(bomb->x(), bomb->y()); } for (Blast *blast = blastList.first(); blast != NULL; blast = blastList.next()) { blast->setSprite(resource->pixmaparray("blast", /*10*/4)); blast->move(blast->x(), blast->y()); } for (Item *item = itemList.first(); item != NULL; item = itemList.next()) { switch(item->type()) { case Level::invalid: // nothing break; case Level::ground: break; // background case Level::wall: // background break; case Level::bridge: // background break; case Level::space: // nothing break; case Level::itemScree: item->setSprite(resource->pixmaparray("item_scree", 3)); break; case Level::itemBomb: item->setSprite(resource->pixmaparray("item_bomb", 3)); break; case Level::itemLive: item->setSprite(resource->pixmaparray("item_live", 3)); break; case Level::itemPower: item->setSprite(resource->pixmaparray("item_power", 3)); break; case Level::itemGrail: item->setSprite(resource->pixmaparray("item_grail", 3)); break; case Level::itemSpeed: item->setSprite(resource->pixmaparray("item_speed", 3)); break; case Level::itemInvisible: item->setSprite(resource->pixmaparray("item_invisible", 3)); break; case Level::itemFlash: item->setSprite(resource->pixmaparray("item_flash", 3)); break; } item->move(item->x(), item->y()); } } void PlayCanvas::drawBackground(bool only, int only_x, int only_y) { QPainter painter; QString pixmap; QPixmap pm; if(!m_level) return; pm = backgroundPixmap(); if(pm.isNull()) return; QPixmap pmBridge = resource->pixmap("bridge", 0); QPixmap pmGround = resource->pixmap("ground", 0); QPixmap pmWall = resource->pixmap("wall", 0); painter.begin(&pm); for(int j = 0; j < m_level->height(); j++) { if((only) && (j != only_y)) continue; for(int i = 0; i < m_level->width(); i++) { if((only) && (i != only_x)) continue; switch (m_level->tile(i, j)) { case Level::wall: if((m_rules) && (m_rules->wall_damage)) pmWall = resource->pixmapmulti("walldamage", m_level->condition(i, j)); painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmWall); break; case Level::bridge: painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmBridge); break; case Level::space: break; case Level::ground: default: if((m_rules) && (m_rules->ground_damage)) pmGround = resource->pixmapmulti("grounddamage", m_level->condition(i, j)); painter.drawPixmap(resource->tilesize() * i, resource->tilesize() * j, pmGround); break; } } } painter.end(); setBackgroundPixmap(pm); } void PlayCanvas::buildLevel(QString levelfile) { int w, h; if(levelfile.isNull()) return; //kdDebug() << "build level with " << "'" << levelfile << "'" << endl; m_level = new Level(); bool success = m_level->loadLevel(resource->load(levelfile, Resource::level)); if(!success) return; //kdDebug() << "loaded file: '" << resource->load(levelfile, Resource::level) << "'" << endl; QPixmap background(resource->path("background")); QImage im = background.convertToImage(); im = im.smoothScale(resource->tilesize() * m_level->width(), resource->tilesize() * m_level->height()); background.convertFromImage(im); setBackgroundPixmap(background); /* paint the background */ drawBackground(false, 0, 0); w = resource->tilesize() * m_level->width(); h = resource->tilesize() * m_level->height(); resize(w, h); setAdvancePeriod(1); update(); } void PlayCanvas::createExit(int x, int y) { QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("exit", 3), this); sprite->setZ(-0.1); exit = new Exit(-1, x, y, sprite); } void PlayCanvas::createItem(int type, int id, int x, int y) { QCanvasSprite *sprite; sprite = NULL; switch(type) { case Level::itemScree: sprite = new QCanvasSprite(resource->pixmaparray("item_scree", 3), this); break; case Level::itemPower: sprite = new QCanvasSprite(resource->pixmaparray("item_power", 3), this); break; case Level::itemGrail: sprite = new QCanvasSprite(resource->pixmaparray("item_grail", 3), this); break; case Level::itemLive: sprite = new QCanvasSprite(resource->pixmaparray("item_live", 3), this); break; case Level::itemBomb: sprite = new QCanvasSprite(resource->pixmaparray("item_bomb", 3), this); break; case Level::itemSpeed: sprite = new QCanvasSprite(resource->pixmaparray("item_speed", 3), this); break; case Level::itemInvisible: sprite = new QCanvasSprite(resource->pixmaparray("item_invisible", 3), this); break; case Level::itemFlash: sprite = new QCanvasSprite(resource->pixmaparray("item_flash", 3), this); break; } Item *item = new Item(id, x, y, sprite, type); itemList.append(item); m_level->setTile(x, y, type); } void PlayCanvas::createPlayer(int id, int x, int y) { QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("guy1", 1/*12*/), this); if(sprite->frameCount() > 2) sprite->setFrame(2); Player *p = new Player(id, x, y, sprite); p->power(m_rules->player_power); p->numBombs(m_rules->player_bombs); p->numLives(m_rules->player_lives); playerList.append(p); // HACK! if(!currPlayer) currPlayer = p; } void PlayCanvas::createMonster(int id, int x, int y) { QCanvasSprite *sprite = new QCanvasSprite(resource->pixmaparray("monster1", 2/*13*/), this); if(sprite->frameCount() > 2) sprite->setFrame(2); AI_Player *monster = new AI_Player(id, x, y, sprite); monster->power(m_rules->monster_power); monster->numBombs(m_rules->monster_bombs); monster->numLives(m_rules->monster_lives); monsterList.append(monster); } void PlayCanvas::createMonster(int count) { int orig = count; if(!count) count = m_players; for (int i = 0; i < count; i++) { if(orig) { // FIXME: duplicate with placeDynamic -> generate query function QValueList freeCells; for (int j = 0; j < m_level->height(); j++) for (int i = 0; i < m_level->width(); i++) if (m_level->tile(i, j) != Level::invalid && m_level->tile(i, j) != Level::wall && m_level->tile(i, j) != Level::space) { QPoint point; point.setX(i); point.setY(j); freeCells.append(point); } int pos = random(freeCells.count()); createMonster(-1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } else { createMonster(-1, 0, 0); } } } void PlayCanvas::setPlayers(int players) { m_players = players; } void PlayCanvas::placeDynamic() { AI_Player *monster; Player *player; QValueList freeCells; int pos, i; if(!m_level) return; for (int j = 0; j < m_level->height(); j++) for (int i = 0; i < m_level->width(); i++) if (m_level->tile(i, j) != Level::invalid && m_level->tile(i, j) != Level::wall && m_level->tile(i, j) != Level::space) { QPoint point; point.setX(i); point.setY(j); freeCells.append(point); } int size = (int)(freeCells.count() * m_rules->scree_percentage / 100); // place the scree items for (i = 0; i < size; i++) { pos = random(freeCells.count()); createItem(Level::itemScree, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } // place the other items if(!m_rules->custom()) { for (i = 0; i < 10; i++) { pos = random(freeCells.count()); int type = Level::itemScree; switch (random(3)) { case 0: type = Level::itemBomb; break; case 1: type = Level::itemPower; break; case 2: type = Level::itemLive; break; } createItem(type, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } // place special items if(random(2)) { pos = random(freeCells.count()); createItem(Level::itemGrail, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } if(random(2)) { pos = random(freeCells.count()); createItem(Level::itemSpeed, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } if(random(2)) { pos = random(freeCells.count()); createItem(Level::itemFlash, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } if(random(2)) { pos = random(freeCells.count()); createItem(Level::itemInvisible, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } } else { for(i = 0; i < m_rules->bomb_number; i++) { pos = random(freeCells.count()); createItem(Level::itemBomb, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->life_number; i++) { pos = random(freeCells.count()); createItem(Level::itemLive, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->power_number; i++) { pos = random(freeCells.count()); createItem(Level::itemPower, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->grail_number; i++) { pos = random(freeCells.count()); createItem(Level::itemGrail, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->speed_number; i++) { pos = random(freeCells.count()); createItem(Level::itemSpeed, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->invisible_number; i++) { pos = random(freeCells.count()); createItem(Level::itemInvisible, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } for(i = 0; i < m_rules->flash_number; i++) { pos = random(freeCells.count()); createItem(Level::itemFlash, -1, freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } } // place the players for (player = playerList.first(); player != NULL; player = playerList.next()) { QPoint p = goodrandom(freeCells.count()); player->move(p.x(), p.y()); for(unsigned int i = 0; i < freeCells.count(); i++) if((freeCells[i].x() == p.x()) && (freeCells[i].y() == p.y())) freeCells.remove(freeCells[i]); } // place the monster for (monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { QPoint p = goodrandom(freeCells.count()); monster->move(p.x(), p.y()); for(unsigned int i = 0; i < freeCells.count(); i++) if((freeCells[i].x() == p.x()) && (freeCells[i].y() == p.y())) freeCells.remove(freeCells[i]); } // place the exit pos = random(freeCells.count()); createExit(freeCells[pos].x(), freeCells[pos].y()); freeCells.remove(freeCells[pos]); } QString PlayCanvas::weapontype(int weaponcounter) { switch(weaponcounter) { case 0: return "bomb"; break; case 1: if(m_rules->timebomb_allowed) return "timebomb"; else return "bomb"; break; case 2: if(m_rules->moveablebomb_allowed) return "moveablebomb"; else return "bomb"; break; } return QString::null; } void PlayCanvas::selectWeapon() { if(!m_enabled) return; if(weapon) { delete weapon; weaponcounter = (weaponcounter + 1) % 3; } QCanvasSprite *bombSprite = new QCanvasSprite(resource->pixmaparray(weapontype(weaponcounter), /*2*/8), this); bombSprite->setZ(0.1); weapon = new Bomb(-1, currPlayer->x(), currPlayer->y(), bombSprite, currPlayer); } bool PlayCanvas::walkableTile(int fromx, int fromy, int x, int y) { Bomb *bomb; Item *item; bool valid = true; if(!m_level) return false; // do we stay in range if ((x >= 0) && (y >= 0) && (x < m_level->width()) && (y < m_level->height())) { // is there something that could block us if (m_level->tile(x, y) == Level::wall || m_level->tile(x, y) == Level::invalid || m_level->tile(x, y) == Level::space) return false; if ((m_level->tile(x, y) == Level::itemBomb) || (m_level->tile(x, y) == Level::itemLive) || (m_level->tile(x, y) == Level::itemPower) || (m_level->tile(x, y) == Level::itemGrail) || (m_level->tile(x, y) == Level::itemSpeed) || (m_level->tile(x, y) == Level::itemFlash) || (m_level->tile(x, y) == Level::itemInvisible)) { if ((item = findItem(x, y)) != NULL) { if(!item->isActive()) return false; } } if(exit) { if (exit->x() == x && exit->y() == y && !exit->isHighlighted()) return false; } if (m_level->tile(x, y) == Level::itemScree) { if ((item = findItem(x, y)) != NULL) { return false; } } if (!bombList.isEmpty()) { for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) { if (bomb->x() == x && bomb->y() == y) { // check for moveable bombs if(bomb->moveable()) { if(walkableTile(x, y, fromx + (x - fromx) * 2, fromy + (y - fromy) * 2)) { bomb->move(fromx + (x - fromx) * 2, fromy + (y - fromy) * 2); return true; } else return false; } else return false; } } } } else { return false; } return valid; } bool PlayCanvas::action(int event, int option) { if(event == moveEvent) { currPlayer->steps(0); } return action_internal(event, option); } bool PlayCanvas::action_internal(int event, int option) { int x, y; Bomb *bomb; bool moved = true; //kdDebug() << "action " << m_away << endl; if (m_away == true) return false; if (m_enabled == false) return false; x = currPlayer->x(); y = currPlayer->y(); switch(event) { case moveEvent: switch(option) { case optLeft: x--; if(currPlayer->sprite()->frameCount() > 1) currPlayer->sprite()->setFrame(1); break; case optRight: x++; if(currPlayer->sprite()->frameCount() > 1) currPlayer->sprite()->setFrame(0); break; case optUp: y--; if(currPlayer->sprite()->frameCount() > 3) currPlayer->sprite()->setFrame(3); break; case optDown: if(currPlayer->sprite()->frameCount() > 3) currPlayer->sprite()->setFrame(2); y++; break; } if(m_localenabled) { if (walkableTile(currPlayer->x(), currPlayer->y(), x, y)) { if(weapon) { delete weapon; weapon = NULL; } currPlayer->move(x, y); checkItem(currPlayer, true); emit position(x, y); } else moved = false; // Double move? currPlayer->steps(currPlayer->steps() + 1); if(currPlayer->steps() < currPlayer->totalsteps()) action_internal(event, option); } else currPlayer->move(x, y); if(m_localenabled) { if (m_level->tile(x, y) == Level::space) currPlayer->numLives(0); } break; case fireEvent: placeBomb(currPlayer); break; case timebombEvent: for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) if(bomb->player() == currPlayer) bomb->delay(false); break; } emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power()); return moved; } void PlayCanvas::createBomb(int playerid, int id, int x, int y) { for(Player *p = playerList.first(); p != NULL; p = playerList.next()) if(p->id() == id) { placeBomb(p, id, x, y); return; } placeBomb(NULL, -1, x, y); } void PlayCanvas::placeBomb(Player *p) { if(p) placeBomb(p, -1, p->x(), p->y()); } void PlayCanvas::placeBomb(Player *p, int id, int x, int y) { Bomb *bomb; QString type; if(m_localenabled) { if ((p) && (p->numBombs() == 0)) return; if (!bombList.isEmpty()) { for (bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) if (bomb->x() == x && bomb->y() == y) return; } } if((m_localenabled) && (p) && (p == currPlayer)) { type = weapontype(weaponcounter); } else type = "bomb"; QCanvasSprite *bombSprite = new QCanvasSprite(resource->pixmaparray(type, /*2*/8), this); bomb = new Bomb(id, x, y, bombSprite, p); if(type == "timebomb") bomb->delay(true); if(type == "moveablebomb") bomb->setMoveable(true); bombList.append(bomb); if(p) { p->numBombs(p->numBombs() - 1); if(p->numFlash() > 0) { bomb->setFlash(true); p->numFlash(p->numFlash() - 1); } } } void PlayCanvas::checkItem(/*int x, int y*/ Player *p, bool isplayer) { Item *item; int x, y; x = p->x(); y = p->y(); /* check if player found something */ if ((item = findItem(x, y)) != NULL) { switch (item->type()) { case Level::itemBomb: p->numBombs(p->numBombs() + 1); break; case Level::itemLive: p->numLives(p->numLives() + 1); break; case Level::itemPower: p->power(p->power() + 1); break; case Level::itemGrail: for(Player *p2 = playerList.first(); p2 != NULL; p2 = playerList.next()) { if(p2->numLives() > 1) { p2->numLives(p2->numLives() - 1); p->numLives(p->numLives() + 1); } } for(AI_Player *p2 = monsterList.first(); p2 != NULL; p2 = monsterList.next()) { if(p2->numLives() > 1) { p2->numLives(p2->numLives() - 1); p->numLives(p->numLives() + 1); } } break; case Level::itemSpeed: p->totalsteps(2); p->stepcounter(50); break; case Level::itemInvisible: p->visibility(0); p->stepcounter(50); p->sprite()->hide(); break; case Level::itemFlash: p->numFlash(p->numFlash() + 1); break; } itemList.remove(item); m_level->setTile(x, y, Level::ground); if(p == currPlayer) m_score++; } if(isplayer) { if (exit->x() == x && exit->y() == y) if (exit->isActive()) { setOverlay(winner); emit levelFinished(); } } emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power()); } bool PlayCanvas::createBlast(int id, int x, int y) { QCanvasSprite *blastSprite; Blast *blast; if (m_level->tile(x, y) == Level::invalid || m_level->tile(x, y) == Level::wall) return false; blastSprite = new QCanvasSprite(resource->pixmaparray("blast", /*4*/10), this); blast = new Blast(id, x, y, blastSprite); blastList.append(blast); if (m_level->tile(x, y) > Level::space) return false; else return true; } void PlayCanvas::timerEvent(QTimerEvent *e) { Item *item; if(m_enabled == false) return; if(exit) exit->proceed(); for (Blast *blast = blastList.first(); blast != NULL; blast = blastList.next()) { blast->proceed(); if(m_localenabled == true) { /* check for player, standing in a blast area */ for (Player *player = playerList.first(); player != NULL; player = playerList.next()) { if (player->x() == blast->x() && player->y() == blast->y() && blast->step() == 1) player->numLives(player->numLives() - 1); } for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { if (monster->x() == blast->x() && monster->y() == blast->y() && blast->step() == 1) monster->numLives(monster->numLives() - 1); } /* check for other bombs, lieing in a blast area */ for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) { if (bomb->x() == blast->x() && bomb->y() == blast->y()) bomb->setStep(0); } /* when exit is hit, generate a new monster */ if (exit->x() == blast->x() && exit->y() == blast->y() && blast->step() == 1) { if (!exit->isActive()) exit->activate(true); if (!exit->isHighlighted()) createMonster(/*1*/-1, exit->x(), exit->y()); } /* check for other items, lieing in a blast area */ if ((item = findItem(blast->x(), blast->y())) != NULL) { if (blast->step() == 1) { if (!item->isActive()) if (item->type() == Level::itemScree) { m_level->setTile(item->x(), item->y(), Level::ground); itemList.remove(item); } else if(!item->isActive()) item->activate(true); else { itemList.remove(item); } } } if (blast->step() == 1) { int i = blast->x(); int j = blast->y(); if((m_level->tile(i, j) == Level::ground) && (m_rules->ground_damage)) { if(m_level->condition(i, j) < 5) m_level->setCondition(i, j, m_level->condition(i, j) + 1); else m_level->setTile(i, j, Level::space); drawBackground(true, i, j); } if((m_level->tile(i + 1, j) == Level::wall) && (m_rules->wall_damage)) { if(m_level->condition(i + 1, j) < 5) m_level->setCondition(i + 1, j, m_level->condition(i + 1, j) + 1); else { m_level->setTile(i + 1, j, Level::ground); m_level->setCondition(i + 1, j, 2); } drawBackground(true, i + 1, j); } if((m_level->tile(i, j + 1) == Level::wall) && (m_rules->wall_damage)) { if(m_level->condition(i, j + 1) < 5) m_level->setCondition(i, j + 1, m_level->condition(i, j + 1) + 1); else { m_level->setTile(i, j + 1, Level::ground); m_level->setCondition(i, j + 1, 2); } drawBackground(true, i, j + 1); } if((m_level->tile(i - 1, j) == Level::wall) && (m_rules->wall_damage)) { if(m_level->condition(i - 1, j) < 5) m_level->setCondition(i - 1, j, m_level->condition(i - 1, j) + 1); else { m_level->setTile(i - 1, j, Level::ground); m_level->setCondition(i - 1, j, 2); } drawBackground(true, i - 1, j); } if((m_level->tile(i, j - 1) == Level::wall) && (m_rules->wall_damage)) { if(m_level->condition(i, j - 1) < 5) m_level->setCondition(i, j - 1, m_level->condition(i, j - 1) + 1); else { m_level->setTile(i, j - 1, Level::ground); m_level->setCondition(i, j - 1, 2); } drawBackground(true, i, j - 1); } } if (blast->finished()) blastList.remove(blast); } } for (Bomb *bomb = bombList.first(); bomb != NULL; bomb = bombList.next()) { bomb->proceed(); if(m_localenabled == true) { if (bomb->finished()) { bomb->player()->numBombs(bomb->player()->numBombs() + 1); /* create explosion */ bool left, right, top, bottom, tl, tr, bl, br; createBlast(-1, bomb->x(), bomb->y()); left = right = top = bottom = true; tl = tr = bl = br = true; for (int i = 1; i < bomb->player()->power() + 1; i++) { if (right) right = createBlast(-1, bomb->x() + i, bomb->y()); if (left) left = createBlast(-1, bomb->x() - i, bomb->y()); if (top) top = createBlast(-1, bomb->x(), bomb->y() + i); if (bottom) bottom = createBlast(-1, bomb->x(), bomb->y() - i); if(bomb->flash()) { if(tl) tl = createBlast(-1, bomb->x() - i, bomb->y() - i); if(tr) tl = createBlast(-1, bomb->x() + i, bomb->y() - i); if(bl) tl = createBlast(-1, bomb->x() - i, bomb->y() + i); if(br) tl = createBlast(-1, bomb->x() + i, bomb->y() + i); } } bombList.remove(bomb); } } } if(m_localenabled == true) { for (Player *player = playerList.first(); player != NULL; player = playerList.next()) { for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { if (monster->x() == player->x() && monster->y() == player->y()) if(player->visibility()) player->numLives(player->numLives() - 1); } if (player->numLives() <= 0) { /* player died */ player->sprite()->setFrame(/*12*/0); setOverlay(loser); emit playerKilled(); } if(player->stepcounter()) { player->stepcounter(player->stepcounter() - 1); if(!player->stepcounter()) { player->totalsteps(1); player->visibility(1); currPlayer->sprite()->show(); } } } for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { if (monster->numLives() == 0) { /* monster died */ monster->sprite()->hide(); monsterList.remove(monster); } } if (monsterList.count() == 0) { if(exit) exit->setHighlighted(true); for (Item *item = itemList.first(); item != NULL; item = itemList.next()) { if(item->type() >= Level::itemBomb) item->setHighlighted(true); } } } for (Item *item = itemList.first(); item != NULL; item = itemList.next()) item->proceed(); if(currPlayer) emit information(currPlayer->numLives(), currPlayer->numBombs(), currPlayer->power()); } void PlayCanvas::moveMonster(int id, int direction) { for(AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) if(monster->id() == id) { int x = monster->x(); int y = monster->y(); switch(direction) { case AI_Player::Up: monster->move(x, y - 1); break; case AI_Player::Down: monster->move(x, y + 1); break; case AI_Player::Left: monster->move(x - 1, y); break; case AI_Player::Right: monster->move(x + 1, y); break; } monster->direction((AI_Player::Direction)direction); break; } } int PlayCanvas::monsterWaypoints(AI_Player *monster, int x, int y) { int ret; //kdDebug() << "- " << monster->x() << ":" << monster->y() << " - " << x << ":" << y << endl; if((monster->x() == x) && (monster->y() == y)) return 1; for(int j = y - 1; j <= y + 1; j++) for(int i = x - 1; i <= x + 1; i++) { if(((i == x) && (j == y)) || ((i != x) && (j != y))) continue; if(m_level->value(i, j)) continue; if(walkableTile(x, y, i, j)) { //kdDebug() << "walkabletile: " << i << ":" << j << endl; m_level->addValue(i, j, 1); ret = monsterWaypoints(monster, i, j); if(ret == 1) { //kdDebug() << "* " << x << ":" << y << endl; monster->pushWaypoint(x, y); return 1; } } } return 0; } void PlayCanvas::clockTimeout() { if((currPlayer) && (m_localenabled)) { m_clock++; emit informationScore(m_clock, m_score); } } void PlayCanvas::monsterTimeout() { const int sight = 100; bool foundtarget; Item *item; if(m_enabled == false) return; if(m_localenabled == false) return; for (AI_Player *monster = monsterList.first(); monster != NULL; monster = monsterList.next()) { int x = monster->x(); int y = monster->y(); // not too often yet because the dumb monsters kill themselves if(monster->numBombs() > 0) { if(random(200 / monster->numBombs()) == 0) { placeBomb(monster); } } foundtarget = false; // Intelligent search for new path if((!foundtarget) && (monster->numWaypoints() == 0)) { for(int j = y - sight; j < y + sight; j++) for(int i = x - sight; i < x + sight; i++) { if((i < 0) || (j < 0) || (i >= m_level->width()) || (j >= m_level->height())) continue; if ((m_level->tile(i, j) == Level::itemBomb) || (m_level->tile(i, j) == Level::itemLive) || (m_level->tile(i, j) == Level::itemPower) || (m_level->tile(i, j) == Level::itemGrail) || (m_level->tile(i, j) == Level::itemSpeed) || (m_level->tile(i, j) == Level::itemFlash) || (m_level->tile(i, j) == Level::itemInvisible)) { if ((item = findItem(i, j)) != NULL) { if(item->isActive()) { // Found target kdDebug() << "new target! " << i << ":" << j << endl; foundtarget = true; m_level->clearValues(); monster->clearWaypoints(); monsterWaypoints(monster, i, j); kdDebug() << "added " << monster->numWaypoints() << " waypoints." << endl; break; } } } } } // Stored path if(monster->numWaypoints() > 0) { foundtarget = true; int i = monster->nextX(); int j = monster->nextY(); monster->popWaypoint(); //kdDebug() << "goto: " << x << ":" << y << " - " << i << ":" << j << endl; if(!walkableTile(x, y, i, j)) { kdDebug() << "cache dirty, invalidated!" << endl; foundtarget = false; monster->clearWaypoints(); } else { if(i < x) monster->direction(AI_Player::Left); else if(i > x) monster->direction(AI_Player::Right); if(j < y) monster->direction(AI_Player::Up); else if(j > y) monster->direction(AI_Player::Down); } } else foundtarget = false; if(!foundtarget) { // Randomized switch of direction if (monster->counter() == 3) { if (random(2) == 0) { switch (monster->direction()) { case AI_Player::Up: monster->direction(AI_Player::Down); break; case AI_Player::Down: monster->direction(AI_Player::Up); break; case AI_Player::Left: monster->direction(AI_Player::Right); break; case AI_Player::Right: monster->direction(AI_Player::Left); break; } } } // Randomized switch of direction if (monster->counter() == 4) { if (random(2) == 0) { switch (monster->direction()) { case AI_Player::Up: monster->direction(AI_Player::Left); break; case AI_Player::Down: monster->direction(AI_Player::Right); break; case AI_Player::Left: monster->direction(AI_Player::Down); break; case AI_Player::Right: monster->direction(AI_Player::Up); break; } } monster->counter(0); } else { monster->counter(monster->counter() + 1); } } // Based on the direction, try to make a step. // Try adjacent direction if this fails. // Try opposite direction if even this fails. if (monster->direction() == AI_Player::Up) { if (walkableTile(x, y, x, y - 1)) { monster->move(x, y - 1); monster->direction(AI_Player::Up); checkItem(monster, false); } else if (walkableTile(x, y, x + 1, y)) { monster->move(x + 1, y); monster->direction(AI_Player::Right); checkItem(monster, false); } else if (walkableTile(x, y, x - 1, y)) { monster->move(x - 1, y); monster->direction(AI_Player::Left); checkItem(monster, false); } else if (walkableTile(x, y, x, y + 1)) { monster->move(x, y + 1); monster->direction(AI_Player::Down); checkItem(monster, false); } } else if (monster->direction() == AI_Player::Down) { if (walkableTile(x, y, x, y + 1)) { monster->move(x, y + 1); monster->direction(AI_Player::Down); checkItem(monster, false); } else if (walkableTile(x, y, x - 1, y)) { monster->move(x - 1, y); monster->direction(AI_Player::Left); checkItem(monster, false); } else if (walkableTile(x, y, x + 1, y)) { monster->move(x + 1, y); monster->direction(AI_Player::Right); checkItem(monster, false); } else if (walkableTile(x, y, x, y - 1)) { monster->move(x, y - 1); monster->direction(AI_Player::Up); checkItem(monster, false); } } else if (monster->direction() == AI_Player::Left) { if (walkableTile(x, y, x - 1, y)) { monster->move(x - 1, y); monster->direction(AI_Player::Left); checkItem(monster, false); } else if (walkableTile(x, y, x, y - 1)) { monster->move(x, y - 1); monster->direction(AI_Player::Up); checkItem(monster, false); } else if (walkableTile(x, y, x, y + 1)) { monster->move(x, y + 1); monster->direction(AI_Player::Down); checkItem(monster, false); } else if (walkableTile(x, y, x + 1, y)) { monster->move(x + 1, y); monster->direction(AI_Player::Right); checkItem(monster, false); } } else if (monster->direction() == AI_Player::Right) { if (walkableTile(x, y, x + 1, y)) { monster->move(x + 1, y); monster->direction(AI_Player::Right); checkItem(monster, false); } else if (walkableTile(x, y, x, y + 1)) { monster->move(x, y + 1); monster->direction(AI_Player::Down); checkItem(monster, false); } else if (walkableTile(x, y, x, y - 1)) { monster->move(x, y - 1); monster->direction(AI_Player::Up); checkItem(monster, false); } else if (walkableTile(x, y, x - 1, y)) { monster->move(x - 1, y); monster->direction(AI_Player::Left); checkItem(monster, false); } } } } int PlayCanvas::random(int max) { if(!max) return 0; return (rand() % max); } int PlayCanvas::isgoodrandom(int x, int y) { QPtrList plist = playerList; QPtrList mlist = monsterList; if((x < 0) || (y < 0) || (x >= m_level->width()) || (y >= m_level->height())) return -1; for (Player *player = plist.first(); player != NULL; player = plist.next()) if((player->x() == x) && (player->y() == y)) return -3; for (AI_Player *monster = mlist.first(); monster != NULL; monster = mlist.next()) if((monster->x() == x) && (monster->y() == y)) return -5; if(m_level->tile(x, y) == Level::ground) return 1; if(m_level->tile(x, y) == Level::bridge) return 1; return 0; } QPoint PlayCanvas::goodrandom(int max) { m_level->clearValues(); for (int j = 0; j < m_level->height(); j++) for (int i = 0; i < m_level->width(); i++) { m_level->addValue(i, j, isgoodrandom(i, j) * 10); m_level->addValue(i, j, isgoodrandom(i + 1, j)); m_level->addValue(i, j, isgoodrandom(i - 1, j)); m_level->addValue(i, j, isgoodrandom(i, j + 1)); m_level->addValue(i, j, isgoodrandom(i, j - 1)); if((isgoodrandom(i - 1, j) > 0) || (isgoodrandom(i, j - 1) > 0)) m_level->addValue(i, j, isgoodrandom(i - 1, j - 1)); if((isgoodrandom(i - 1, j) > 0) || (isgoodrandom(i, j + 1) > 0)) m_level->addValue(i, j, isgoodrandom(i - 1, j + 1)); if((isgoodrandom(i + 1, j) > 0) || (isgoodrandom(i, j + 1) > 0)) m_level->addValue(i, j, isgoodrandom(i + 1, j + 1)); if((isgoodrandom(i + 1, j) > 0) || (isgoodrandom(i, j - 1) > 0)) m_level->addValue(i, j, isgoodrandom(i + 1, j - 1)); } QValueList freeCells; int limit = 18; while((limit > 0) && (freeCells.count() < 5)) { for (int j = 0; j < m_level->height(); j++) for (int i = 0; i < m_level->width(); i++) { if(m_level->value(i, j) >= limit) { QPoint p; p.setX(i); p.setY(j); freeCells.append(p); } } limit--; } int pos = random(freeCells.count()); //kdDebug() << "freecells has " << freeCells.count() << " elements, selected " << freeCells[pos].x() << "/" << freeCells[pos].y() << endl; return QPoint(freeCells[pos].x(), freeCells[pos].y()); } Item *PlayCanvas::findItem(int x, int y) { Item *item; for (item = itemList.first(); item != NULL; item = itemList.next()) if (item->x() == x && item->y() == y) return item; return NULL; } Level *PlayCanvas::level() { return m_level; } void PlayCanvas::enable(bool enabled) { m_enabled = enabled; setTheme(resource->theme()); } void PlayCanvas::enableLocal(bool enabled) { m_localenabled = enabled; if(enabled) { createPlayer(0, 0, 0); createMonster(); placeDynamic(); } } void PlayCanvas::actionItem(int id, int type, int count) { for(Player *player = playerList.first(); player; player = playerList.next()) if(player->id() == id) { if(type == Level::itemLive) player->numLives(player->numLives() + count); else if(type == Level::itemBomb) player->numBombs(player->numBombs() + count); else if(type == Level::itemPower) player->power(player->power() + count); return; } for(AI_Player *monster = monsterList.first(); monster; monster = monsterList.next()) if(monster->id() == id) { if(type == Level::itemLive) monster->numLives(monster->numLives() + count); else if(type == Level::itemBomb) monster->numBombs(monster->numBombs() + count); else if(type == Level::itemPower) monster->power(monster->power() + count); return; } } void PlayCanvas::removeItem(int id) { for(Item *item = itemList.first(); item; item = itemList.next()) if(item->id() == id) { if(!item->isActive()) { m_level->setTile(item->x(), item->y(), Level::ground); } itemList.remove(item); return; } } void PlayCanvas::removeMonster(int id) { for(AI_Player *monster = monsterList.first(); monster; monster = monsterList.next()) if(monster->id() == id) { monsterList.remove(monster); return; } } void PlayCanvas::removePlayer(int id) { for(Player *player = playerList.first(); player; player = playerList.next()) if(player->id() == id) { playerList.remove(player); return; } } void PlayCanvas::removeBomb(int id) { for(Bomb *bomb = bombList.first(); bomb; bomb = bombList.next()) if(bomb->id() == id) { bombList.remove(bomb); return; } } void PlayCanvas::removeBlast(int id) { for(Blast *blast = blastList.first(); blast; blast = blastList.next()) if(blast->id() == id) { blastList.remove(blast); return; } } void PlayCanvas::activateItem(int id) { for(Item *item = itemList.first(); item; item = itemList.next()) if(item->id() == id) { item->activate(true); return; } } void PlayCanvas::activateExit() { if(exit) exit->activate(true); } void PlayCanvas::highlightItem(int id) { for(Item *item = itemList.first(); item; item = itemList.next()) if(item->id() == id) { item->setHighlighted(true); return; } } void PlayCanvas::highlightExit() { if(exit) exit->setHighlighted(true); } void PlayCanvas::setRules(Rule *rules) { m_rules = rules; } bool PlayCanvas::enabled() { return m_enabled; } void PlayCanvas::setOverlay(int overlay) { if(overlay != none) { if(!overlayfont) { QFont f(QString::null, 48, QFont::Black); overlayfont = new QCanvasText("", f, this); overlayfont->move(width() / 2 - 100, height() / 2 - 24); overlayfont->setZ(0.2); } } if(!overlayfont) return; switch(overlay) { case loser: overlayfont->setColor(QColor(255, 0, 0)); overlayfont->setText(i18n("Loser!")); break; case winner: overlayfont->setColor(QColor(0, 255, 0)); overlayfont->setText(i18n("Winner!")); break; case pause: overlayfont->setColor(QColor(255, 230, 0)); overlayfont->setText(i18n("Pause")); break; default: break; } if(overlay == none) overlayfont->hide(); else overlayfont->show(); }