#include "main.hpp" #include #include // How many turns the monster chases the player // After losing his sight static const int TRACKING_TURNS = 3; Ai *Ai::create(TCODZip &zip) { AiType type=(AiType)zip.getInt(); Ai *ai=NULL; switch(type) { case PLAYER: ai = new PlayerAi(); break; case MONSTER: ai = new MonsterAi(); break; case CONFUSED_MONSTER: ai = new ConfusedMonsterAi(0, NULL); break; } ai->load(zip); return ai; } /* Player AI */ PlayerAi::PlayerAi() : xpLevel(1) { } const int LEVEL_UP_BASE=200; const int LEVEL_UP_FACTOR=150; int PlayerAi::getNextLevelXp() { return LEVEL_UP_BASE + xpLevel*LEVEL_UP_FACTOR; } void PlayerAi::update(Actor *owner) { int levelUpXp = getNextLevelXp(); if(owner->destructible->xp >= levelUpXp) { xpLevel++; owner->destructible->xp -= levelUpXp; engine.gui->message(TCODColor::yellow, "Your battle skills grow stronger! You reached level %d", xpLevel); engine.gui->menu.clear(); engine.gui->menu.addItem(Menu::CONSTITUTION, "Constitution (+20HP)"); engine.gui->menu.addItem(Menu::STRENGTH, "Strength (+1 Attack)"); engine.gui->menu.addItem(Menu::AGILITY, "Agility (+1 Defense)"); Menu::MenuItemCode menuItem=engine.gui->menu.pick(Menu::PAUSE); switch(menuItem) { case Menu::CONSTITUTION: owner->destructible->maxHp+=20; owner->destructible->hp+=20; break; case Menu::STRENGTH: owner->attacker->power+=1; break; case Menu::AGILITY: owner->destructible->defense+=1; break; default: break; } } if(owner->destructible && owner->destructible->isDead()) { return; } int dx=0, dy=0; switch(engine.lastKey.vk) { case TCODK_UP: case TCODK_KP8: dy=-1; break; case TCODK_DOWN: case TCODK_KP2: dy=1; break; case TCODK_LEFT: case TCODK_KP4: dx=-1; break; case TCODK_RIGHT: case TCODK_KP6: dx=1; break; case TCODK_KP7: dy=dx=-1; break; case TCODK_KP9: dy=-1;dx=1; break; case TCODK_KP1: dy=1;dx=-1; break; case TCODK_KP3: dy=dx=1; break; case TCODK_CHAR: handleActionKey(owner, engine.lastKey.c); break; default:break; } if(dx != 0 || dy != 0) { engine.gameStatus=Engine::NEW_TURN; if(moveOrAttack(owner, owner->x+dx, owner->y+dy)) { engine.map->computeFov(); } } } bool PlayerAi::moveOrAttack(Actor *owner, int targetx, int targety) { if(engine.map->isWall(targetx, targety)) return false; // look for living actors to attack for(Actor **iterator=engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor=*iterator; if(actor->destructible && !actor->destructible->isDead() && actor->x == targetx && actor->y == targety) { owner->attacker->attack(owner, actor); return false; } } // Look for corpses or items for(Actor **iterator=engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor = *iterator; bool corpseOrItem=(actor->destructible && actor->destructible->isDead()) || actor->pickable; if(corpseOrItem && actor->x == targetx && actor->y == targety) { engine.gui->message(TCODColor::lightGrey, "There's a %s here\n", actor->name); } } owner->x = targetx; owner->y = targety; return true; } void PlayerAi::handleActionKey(Actor *owner, int ascii) { switch(ascii) { case 'g': // pickup item { bool found=false; for(Actor **iterator=engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor = *iterator; if(actor->pickable && actor->x == owner->x && actor->y == owner->y) { if(actor->pickable->pick(actor, owner)) { found=true; engine.gui->message(TCODColor::lightGrey, "Picked up the %s.", actor->name); break; } else if(!found) { found = true; engine.gui->message(TCODColor::red, "Your inventory is full."); } } } if(!found) { engine.gui->message(TCODColor::lightGrey, "There's nothing here."); } engine.gameStatus=Engine::NEW_TURN; } break; case 'i': // Display inventory { Actor *actor=choseFromInventory(owner); if(actor) { actor->pickable->use(actor, owner); engine.gameStatus=Engine::NEW_TURN; } } break; case 'd': // Drop item { Actor *actor = choseFromInventory(owner); if(actor) { actor->pickable->drop(actor, owner); engine.gameStatus = Engine::NEW_TURN; } } break; case '>': if(engine.stairs->x == owner->x && engine.stairs->y == owner->y) { engine.nextLevel(); } else { engine.gui->message(TCODColor::lightGrey,"There are no stairs here."); } break; } } Actor *PlayerAi::choseFromInventory(Actor *owner) { static const int INVENTORY_WIDTH=50; static const int INVENTORY_HEIGHT=28; static TCODConsole con(INVENTORY_WIDTH, INVENTORY_HEIGHT); // Display the inventory frame con.setDefaultForeground(TCODColor(200, 180, 50)); con.printFrame(0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT, true, TCOD_BKGND_DEFAULT, "inventory"); // Display the items with their keyboard shortcut con.setDefaultForeground(TCODColor::white); int shortcut='a'; int y=1; for(Actor **it=owner->container->inventory.begin(); it != owner->container->inventory.end(); it++) { Actor *actor = *it; con.print(2, y, "(%c) %s", shortcut, actor->name); y++; shortcut++; } // blit the inventory console on the root console TCODConsole::blit(&con, 0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT, TCODConsole::root, engine.screenWidth/2 - INVENTORY_WIDTH/2, engine.screenHeight/2 - INVENTORY_HEIGHT/2); TCODConsole::flush(); // wait for a key press TCOD_key_t key; TCODSystem::waitForEvent(TCOD_EVENT_KEY_PRESS, &key, NULL, true); if(key.vk == TCODK_CHAR) { int actorIndex = key.c - 'a'; if(actorIndex >= 0 && actorIndex < owner->container->inventory.size()) { return owner->container->inventory.get(actorIndex); } } return NULL; } void PlayerAi::load(TCODZip &zip) { } void PlayerAi::save(TCODZip &zip) { zip.putInt(PLAYER); } /* Monster AI */ void MonsterAi::update(Actor *owner) { if(owner->destructible && owner->destructible->isDead()) { return; } if(engine.map->isInFov(owner->x, owner->y)) { // We can see the player. Move towards him. moveCount=TRACKING_TURNS; } else { moveCount--; } if(moveCount > 0) { moveOrAttack(owner, engine.player->x, engine.player->y); } } void MonsterAi::moveOrAttack(Actor *owner, int targetx, int targety) { int dx = targetx - owner->x; int dy = targety - owner->y; int stepdx = (dx > 0 ? 1:-1); int stepdy = (dy > 0 ? 1:-1); float distance=sqrtf(dx*dx+dy*dy); if(distance >= 2) { dx = (int)(round(dx/distance)); dy = (int)(round(dy/distance)); if(engine.map->canWalk(owner->x+dx, owner->y+dy)) { owner->x += dx; owner->y += dy; } else if(engine.map->canWalk(owner->x + stepdx, owner->y)) { owner->x += stepdx; } else if(engine.map->canWalk(owner->x, owner->y + stepdy)) { owner->y += stepdy; } } else if(owner->attacker) { owner->attacker->attack(owner, engine.player); } } void MonsterAi::load(TCODZip &zip) { moveCount = zip.getInt(); } void MonsterAi::save(TCODZip &zip) { zip.putInt(MONSTER); zip.putInt(moveCount); } TemporaryAi::TemporaryAi(int nbTurns) : nbTurns(nbTurns) { } /* RIGHT HERE */ ConfusedMonsterAi::ConfusedMonsterAi(int nbTurns, Ai *oldAi) : nbTurns(nbTurns), oldAi(oldAi) { } void ConfusedMonsterAi::update(Actor *owner) { TCODRandom *rng = TCODRandom::getInstance(); int dx = rng->getInt(-1, 1); int dy = rng->getInt(-1, 1); if(dx != 0 || dy != 0) { int destx = owner->x + dx; int desty = owner->y + dy; if(engine.map->canWalk(destx, desty)) { owner->x = destx; owner->y = desty; } else { Actor *actor = engine.getActor(destx, desty); if(actor) { owner->attacker->attack(owner, actor); } } } nbTurns--; if(nbTurns == 0) { owner->ai = oldAi; delete this; } } void ConfusedMonsterAi::load(TCODZip &zip) { nbTurns=zip.getInt(); oldAi=Ai::create(zip); } void ConfusedMonsterAi::save(TCODZip &zip) { zip.putInt(CONFUSED_MONSTER); zip.putInt(nbTurns); oldAi->save(zip); }