#include "main.hpp" bool Pickable::pick(Actor *owner, Actor *wearer) { if(wearer->container && wearer->container->add(owner)) { engine.actors.remove(owner); return true; } return false; } bool Pickable::use(Actor *owner, Actor *wearer) { if(wearer->container) { wearer->container->remove(owner); delete owner; return true; } return false; } void Pickable::drop(Actor *owner, Actor *wearer) { if(wearer->container) { wearer->container->remove(owner); engine.actors.push(owner); owner->x = wearer->x; owner->y = wearer->y; engine.gui->message(TCODColor::lightGrey, "%s drops a %s.", wearer->name, owner->name); } } /* Pickable Factory */ Pickable *Pickable::create(TCODZip &zip) { PickableType type=(PickableType)zip.getInt(); Pickable *pickable=NULL; switch(type) { case HEALER: pickable = new Healer(0); break; case LIGHTNING_BOLT: pickable = new LightningBolt(0, 0); break; case CONFUSER: pickable = new Confuser(0, 0); break; case FIREBALL: pickable = new Fireball(0, 0); break; } pickable->load(zip); return pickable; } TargetSelector::TargetSelector(SelectorType type, float range) : type(type), range(range) { } void TargetSelector::selectTargets(Actor *wearer, TCODList & list) { switch(type) { case CLOSEST_MONSTER: { Actor *closestMonster=engine.getClosestMonster(wearer->x, wearer->y, range); if(closestMonster) { list.push(closestMonster); } } break; case SELECTED_MONSTER: { int x, y; engine.gui->message(TCODColor::cyan, "Left-click to select a target,\nor right-click to cancel."); if(engine.pickATile(&x, &y, range)) { Actor *actor=engine.getActor(x, y); if(actor) { list.push(actor); } } } break; case WEARER_RANGE: for(Actor **iterator=engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor=*iterator; if(actor != wearer && actor->destructible && !actor->destructible->isDead() && actor->getDistance(wearer->x, wearer->y) <= range) { list.push(actor); } } break; case SELECTED_RANGE: int x, y; engine.gui->message(TCODColor::cyan, "Left-click to select a tile,\nor right-click to cancel."); if(engine.pickATile(&x, &y)) { for(Actor **iterator = engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor=*iterator; if(actor->destructible && !actor->destructible->isDead() && actor->getDistance(x, y) <= range) { list.push(actor); } } } break; } if(list.isEmpty()) { engine.gui->message(TCODColor::lightGrey, "No enemy is close enough"); } } /* Sub-Pickables */ Healer::Healer(float amount) : amount(amount) { } bool Healer::use(Actor *owner, Actor *wearer) { if(wearer->destructible) { float amountHealed = wearer->destructible->heal(amount); if(amountHealed > 0) { return Pickable::use(owner, wearer); } } return false; } void Healer::load(TCODZip &zip) { amount=zip.getFloat(); } void Healer::save(TCODZip &zip) { zip.putInt(HEALER); zip.putFloat(amount); } LightningBolt::LightningBolt(float range, float damage) : range(range), damage(damage) { } bool LightningBolt::use(Actor *owner, Actor *wearer) { Actor *closestMonster = engine.getClosestMonster( wearer->x, wearer->y, range); if(!closestMonster) { engine.gui->message(TCODColor::lightGrey, "No enemy is close enough to strike."); return false; } // Hit closest monster for hit points engine.gui->message(TCODColor::lightBlue, "A lightning bolt strikes the %s with a loud thunder!\nThe damage is %g hit points.", closestMonster->name, damage); closestMonster->destructible->takeDamage(closestMonster, damage); return Pickable::use(owner, wearer); } void LightningBolt::load(TCODZip &zip) { range=zip.getFloat(); damage=zip.getFloat(); } void LightningBolt::save(TCODZip &zip) { zip.putInt(LIGHTNING_BOLT); zip.putFloat(range); zip.putFloat(damage); } Fireball::Fireball(float range, float damage) : LightningBolt(range, damage) { } bool Fireball::use(Actor *owner, Actor *wearer) { engine.gui->message(TCODColor::cyan, "Left-click a target tile for the fireball,\nor right-click to cancel."); int x, y; if(!engine.pickATile(&x, &y)) { return false; } // Burn everything in (including player) engine.gui->message(TCODColor::orange, "The fireball explodes, burning everything within %g tiles!", range); for(Actor **iterator=engine.actors.begin(); iterator != engine.actors.end(); iterator++) { Actor *actor = *iterator; if(actor->destructible && !actor->destructible->isDead() && actor->getDistance(x, y) <= range) { engine.gui->message(TCODColor::orange, "The %s gets burned for %g hit points.", actor->name, damage); actor->destructible->takeDamage(actor, damage); } } return Pickable::use(owner, wearer); } void Fireball::save(TCODZip &zip) { zip.putInt(FIREBALL); zip.putFloat(range); zip.putFloat(damage); } Confuser::Confuser(int nbTurns, float range) : nbTurns(nbTurns), range(range) { } bool Confuser::use(Actor *owner, Actor *wearer) { engine.gui->message(TCODColor::cyan, "Left-click an enemy to confuse it,\nor right-click to cancel."); int x, y; if(!engine.pickATile(&x, &y, range)) { return false; } Actor *actor = engine.getActor(x, y); if(!actor) { return false; } // Confuse the monster for turns Ai *confusedAi = new ConfusedMonsterAi(nbTurns, actor->ai); actor->ai = confusedAi; engine.gui->message(TCODColor::lightGreen, "The eyes of the %s look vacant,\nas he starts to stumble around!", actor->name); return Pickable::use(owner, wearer); } void Confuser::load(TCODZip &zip) { nbTurns=zip.getInt(); range=zip.getFloat(); } void Confuser::save(TCODZip &zip) { zip.putInt(CONFUSER); zip.putInt(nbTurns); zip.putFloat(range); } HealthEffect::HealthEffect(float amount, const char *message) : amount(amount), message(message) { } bool HealthEffect::applyTo(Actor *actor) { if(!actor->destructible) return false; if(amount > 0) { float pointsHealed = actor->destructible->heal(amount); if(pointsHealed > 0) { if(message) { engine.gui->message(TCODColor::lightGrey, message, actor->name, pointsHealed); } return true; } } else { if(message && -amount-actor->destructible->defense > 0) { engine.gui->message(TCODColor::lightGrey, message, actor->name, -amount-actor->destructible->defense); } if(actor->destructible->takeDamage(actor, -amount) > 0) { return true; } } return false; }