TombsOfHarc/src/Pickable.cpp

223 lines
6.7 KiB
C++

#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<Actor *> & 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 <damage> 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 <range> (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 <nbTurns> 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;
}