223 lines
6.7 KiB
C++
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;
|
|
}
|