Generic Pickables

This commit is contained in:
Brian Buller 2015-03-18 18:03:20 -05:00
parent ae72140657
commit d8a83e2768
6 changed files with 293 additions and 359 deletions

View File

@ -6,71 +6,108 @@
// After losing his sight // After losing his sight
static const int TRACKING_TURNS = 3; static const int TRACKING_TURNS = 3;
Ai *Ai::create(TCODZip &zip) { MonsterAi::MonsterAi() : moveCount(0) { }
AiType type=(AiType)zip.getInt(); void MonsterAi::update(Actor *owner) {
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()) { if(owner->destructible && owner->destructible->isDead()) {
return; return;
} }
int dx=0, dy=0; 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) { }
void TemporaryAi::update(Actor *owner) {
nbTurns--;
if(nbTurns == 0) {
owner->ai = oldAi;
delete this;
}
}
void TemporaryAi::applyTo(Actor *actor) {
oldAi = actor->ai;
actor->ai = this;
}
void TemporaryAi::load(TCODZip &zip) {
nbTurns=zip.getInt();
oldAi=Ai::create(zip);
}
void TemporaryAi::save(TCODZip &zip) {
zip.putInt(CONFUSED_MONSTER);
zip.putInt(nbTurns);
oldAi->save(zip);
}
ConfusedMonsterAi::ConfusedMonsterAi(int nbTurns) : TemporaryAi(nbTurns) { }
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);
}
}
}
TemporaryAi::update(owner);
}
void PlayerAi::update(Actor *owner) {
if(owner->destructible && owner->destructible->isDead()) {
return;
}
int dx=0,dy=0;
switch(engine.lastKey.vk) { switch(engine.lastKey.vk) {
case TCODK_UP: case TCODK_KP8: dy=-1; break; case TCODK_UP: dy=-1; break;
case TCODK_DOWN: case TCODK_KP2: dy=1; break; case TCODK_DOWN: dy=1; break;
case TCODK_LEFT: case TCODK_KP4: dx=-1; break; case TCODK_LEFT: dx=-1; break;
case TCODK_RIGHT: case TCODK_KP6: dx=1; break; case TCODK_RIGHT: 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; case TCODK_CHAR: handleActionKey(owner, engine.lastKey.c); break;
default:break; default: break;
} }
if(dx != 0 || dy != 0) { if(dx != 0 || dy != 0) {
engine.gameStatus=Engine::NEW_TURN; engine.gameStatus = Engine::NEW_TURN;
if(moveOrAttack(owner, owner->x+dx, owner->y+dy)) { if(moveOrAttack(owner, owner->x+dx, owner->y+dy)) {
engine.map->computeFov(); engine.map->computeFov();
} }
@ -106,6 +143,15 @@ bool PlayerAi::moveOrAttack(Actor *owner, int targetx, int targety) {
void PlayerAi::handleActionKey(Actor *owner, int ascii) { void PlayerAi::handleActionKey(Actor *owner, int ascii) {
switch(ascii) { switch(ascii) {
case 'd': // Drop item
{
Actor *actor = chooseFromInventory(owner);
if(actor) {
actor->pickable->drop(actor, owner);
engine.gameStatus = Engine::NEW_TURN;
}
}
break;
case 'g': // pickup item case 'g': // pickup item
{ {
bool found=false; bool found=false;
@ -131,22 +177,13 @@ void PlayerAi::handleActionKey(Actor *owner, int ascii) {
break; break;
case 'i': // Display inventory case 'i': // Display inventory
{ {
Actor *actor=choseFromInventory(owner); Actor *actor=chooseFromInventory(owner);
if(actor) { if(actor) {
actor->pickable->use(actor, owner); actor->pickable->use(actor, owner);
engine.gameStatus=Engine::NEW_TURN; engine.gameStatus=Engine::NEW_TURN;
} }
} }
break; break;
case 'd': // Drop item
{
Actor *actor = choseFromInventory(owner);
if(actor) {
actor->pickable->drop(actor, owner);
engine.gameStatus = Engine::NEW_TURN;
}
}
break;
case '>': case '>':
if(engine.stairs->x == owner->x && engine.stairs->y == owner->y) { if(engine.stairs->x == owner->x && engine.stairs->y == owner->y) {
engine.nextLevel(); engine.nextLevel();
@ -157,13 +194,16 @@ void PlayerAi::handleActionKey(Actor *owner, int ascii) {
} }
} }
Actor *PlayerAi::choseFromInventory(Actor *owner) { Actor *PlayerAi::chooseFromInventory(Actor *owner) {
static const int INVENTORY_WIDTH=50; static const int INVENTORY_WIDTH=50;
static const int INVENTORY_HEIGHT=28; static const int INVENTORY_HEIGHT=28;
static TCODConsole con(INVENTORY_WIDTH, INVENTORY_HEIGHT); static TCODConsole con(INVENTORY_WIDTH, INVENTORY_HEIGHT);
// Display the inventory frame // Display the inventory frame
con.setDefaultForeground(TCODColor(200, 180, 50)); con.setDefaultForeground(TCODColor(200, 180, 50));
con.printFrame(0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT, true, TCOD_BKGND_DEFAULT, "inventory"); con.printFrame(0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT, true,
TCOD_BKGND_DEFAULT, "Inventory");
// Display the items with their keyboard shortcut // Display the items with their keyboard shortcut
con.setDefaultForeground(TCODColor::white); con.setDefaultForeground(TCODColor::white);
int shortcut='a'; int shortcut='a';
@ -175,11 +215,13 @@ Actor *PlayerAi::choseFromInventory(Actor *owner) {
y++; y++;
shortcut++; shortcut++;
} }
// blit the inventory console on the root console // blit the inventory console on the root console
TCODConsole::blit(&con, 0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT, TCODConsole::blit(&con, 0, 0, INVENTORY_WIDTH, INVENTORY_HEIGHT,
TCODConsole::root, engine.screenWidth/2 - INVENTORY_WIDTH/2, TCODConsole::root, engine.screenWidth/2 - INVENTORY_WIDTH/2,
engine.screenHeight/2 - INVENTORY_HEIGHT/2); engine.screenHeight/2 - INVENTORY_HEIGHT/2);
TCODConsole::flush(); TCODConsole::flush();
// wait for a key press // wait for a key press
TCOD_key_t key; TCOD_key_t key;
TCODSystem::waitForEvent(TCOD_EVENT_KEY_PRESS, &key, NULL, true); TCODSystem::waitForEvent(TCOD_EVENT_KEY_PRESS, &key, NULL, true);
@ -192,94 +234,29 @@ Actor *PlayerAi::choseFromInventory(Actor *owner) {
return NULL; return NULL;
} }
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); 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::load(TCODZip &zip) { } void PlayerAi::load(TCODZip &zip) { }
void PlayerAi::save(TCODZip &zip) { void PlayerAi::save(TCODZip &zip) {
zip.putInt(PLAYER); 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);
}

View File

@ -20,11 +20,12 @@ public :
protected : protected :
bool moveOrAttack(Actor *owner, int targetx, int targety); bool moveOrAttack(Actor *owner, int targetx, int targety);
void handleActionKey(Actor *owner, int ascii); void handleActionKey(Actor *owner, int ascii);
Actor *choseFromInventory(Actor *owner); Actor *chooseFromInventory(Actor *owner);
}; };
class MonsterAi : public Ai { class MonsterAi : public Ai {
public : public :
MonsterAi();
void update(Actor *owner); void update(Actor *owner);
void load(TCODZip &zip); void load(TCODZip &zip);
void save(TCODZip &zip); void save(TCODZip &zip);
@ -38,18 +39,15 @@ public:
TemporaryAi(int nbTurns); TemporaryAi(int nbTurns);
void update(Actor *owner); void update(Actor *owner);
void applyTo(Actor *actor); void applyTo(Actor *actor);
protected:
int nbTurns;
Ai *oldAi;
};
class ConfusedMonsterAi : public Ai {
public:
ConfusedMonsterAi(int nbTurns, Ai *oldAi);
void update(Actor *owner);
void load(TCODZip &zip); void load(TCODZip &zip);
void save(TCODZip &zip); void save(TCODZip &zip);
protected: protected:
int nbTurns; int nbTurns;
Ai *oldAi; Ai *oldAi;
}; };
class ConfusedMonsterAi : public TemporaryAi {
public:
ConfusedMonsterAi(int nbTurns);
void update(Actor *owner);
};

View File

@ -178,28 +178,41 @@ void Map::addItem(int x, int y) {
Actor *healthPotion = new Actor(x, y, '!', "Health Potion", Actor *healthPotion = new Actor(x, y, '!', "Health Potion",
TCODColor::violet); TCODColor::violet);
healthPotion->blocks=false; healthPotion->blocks=false;
healthPotion->pickable=new Healer(4); healthPotion->pickable=new Pickable(NULL, new HealthEffect(4, NULL));
engine.actors.push(healthPotion); engine.actors.push(healthPotion);
} else if(dice < 70 + 10) { } else if(dice < 70 + 10) {
// Create a scroll of lightning bolt // Create a scroll of lightning bolt
Actor *scrollOfLightningBolt = new Actor(x, y, '#', Actor *scrollOfLightningBolt = new Actor(x, y, '#',
"Scroll of lightning bolt", TCODColor::lightYellow); "Scroll of lightning bolt", TCODColor::lightYellow);
scrollOfLightningBolt->blocks = false; scrollOfLightningBolt->blocks = false;
scrollOfLightningBolt->pickable = new LightningBolt(5, 20); scrollOfLightningBolt->pickable = new Pickable(
new TargetSelector(TargetSelector::CLOSEST_MONSTER, 5),
new HealthEffect(-20, "A lightning bolt strikes the %s with a loud CRACK!\n"
"The damage is %g hit points."
)
);
engine.actors.push(scrollOfLightningBolt); engine.actors.push(scrollOfLightningBolt);
} else if(dice < 70 + 10 + 10) { } else if(dice < 70 + 10 + 10) {
// Create a scroll of fireball // Create a scroll of fireball
Actor *scrollOfFireball = new Actor(x, y, '#', "Scroll of fireball", Actor *scrollOfFireball = new Actor(x, y, '#', "Scroll of fireball",
TCODColor::lightYellow); TCODColor::lightYellow);
scrollOfFireball->blocks = false; scrollOfFireball->blocks = false;
scrollOfFireball->pickable = new Fireball(3, 12); scrollOfFireball->pickable = new Pickable(
new TargetSelector(TargetSelector::SELECTED_RANGE, 3),
new HealthEffect(-12, "The %s gets burned for %g points.")
);
engine.actors.push(scrollOfFireball); engine.actors.push(scrollOfFireball);
} else { } else {
// create a scroll of confusion // create a scroll of confusion
Actor *scrollOfConfusion = new Actor(x, y, '#', Actor *scrollOfConfusion = new Actor(x, y, '#',
"Scroll of confusion", TCODColor::lightYellow); "Scroll of confusion", TCODColor::lightYellow);
scrollOfConfusion->blocks=false; scrollOfConfusion->blocks=false;
scrollOfConfusion->pickable = new Confuser(10,8); scrollOfConfusion->pickable = new Pickable(
new TargetSelector(TargetSelector::SELECTED_MONSTER, 5),
new AiChangeEffect(new ConfusedMonsterAi(10),
"The eyes of the %s look vacant,\nas it starts to stumble around."
)
);
engine.actors.push(scrollOfConfusion); engine.actors.push(scrollOfConfusion);
} }
} }

View File

@ -1,49 +1,6 @@
#include "main.hpp" #include "main.hpp"
bool Pickable::pick(Actor *owner, Actor *wearer) { TargetSelector::TargetSelector(SelectorType type, float range) : type(type), range(range) { }
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) { void TargetSelector::selectTargets(Actor *wearer, TCODList<Actor *> & list) {
switch(type) { switch(type) {
case CLOSEST_MONSTER: { case CLOSEST_MONSTER: {
@ -95,114 +52,34 @@ void TargetSelector::selectTargets(Actor *wearer, TCODList<Actor *> & list) {
} }
} }
/* Sub-Pickables */ void TargetSelector::save(TCODZip &zip) {
Healer::Healer(float amount) : amount(amount) { } int persistType = 0;
bool Healer::use(Actor *owner, Actor *wearer) { switch(type) {
if(wearer->destructible) { case CLOSEST_MONSTER: persistType = 1; break;
float amountHealed = wearer->destructible->heal(amount); case SELECTED_MONSTER: persistType = 2; break;
if(amountHealed > 0) { case WEARER_RANGE: persistType = 3; break;
return Pickable::use(owner, wearer); case SELECTED_RANGE: persistType = 4; break;
}
} }
return false; zip.putInt(persistType);
}
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); zip.putFloat(range);
} }
HealthEffect::HealthEffect(float amount, const char *message) void TargetSelector::load(TCODZip &zip) {
: amount(amount), message(message) { } int persistType = zip.getInt();
switch(persistType) {
case 1: type = CLOSEST_MONSTER; break;
case 2: type = SELECTED_MONSTER; break;
case 3: type = WEARER_RANGE; break;
case 4: type = SELECTED_RANGE; break;
}
range = zip.getFloat();
}
HealthEffect::HealthEffect(float amount, const char *message) : amount(amount), message(message) { }
bool HealthEffect::applyTo(Actor *actor) { bool HealthEffect::applyTo(Actor *actor) {
if(!actor->destructible) return false; if(!actor->destructible) return false;
if(amount > 0) { if(amount > 0) {
float pointsHealed = actor->destructible->heal(amount); float pointsHealed=actor->destructible->heal(amount);
if(pointsHealed > 0) { if(pointsHealed > 0) {
if(message) { if(message) {
engine.gui->message(TCODColor::lightGrey, message, actor->name, pointsHealed); engine.gui->message(TCODColor::lightGrey, message, actor->name, pointsHealed);
@ -211,7 +88,7 @@ bool HealthEffect::applyTo(Actor *actor) {
} }
} else { } else {
if(message && -amount-actor->destructible->defense > 0) { if(message && -amount-actor->destructible->defense > 0) {
engine.gui->message(TCODColor::lightGrey, message, actor->name, engine.gui->message(TCODColor::lightGrey, message, actor->name,
-amount-actor->destructible->defense); -amount-actor->destructible->defense);
} }
if(actor->destructible->takeDamage(actor, -amount) > 0) { if(actor->destructible->takeDamage(actor, -amount) > 0) {
@ -220,3 +97,92 @@ bool HealthEffect::applyTo(Actor *actor) {
} }
return false; return false;
} }
AiChangeEffect::AiChangeEffect(TemporaryAi *newAi, const char *message) :
newAi(newAi), message(message) { }
bool AiChangeEffect::applyTo(Actor *actor) {
newAi->applyTo(actor);
if(message) {
engine.gui->message(TCODColor::lightGrey, message, actor->name);
}
return true;
}
Pickable::Pickable(TargetSelector *selector, Effect *effect)
: selector(selector), effect(effect) { }
Pickable::~Pickable() {
if(selector) delete selector;
if(effect) delete effect;
}
bool Pickable::pick(Actor *owner, Actor *wearer) {
if(wearer->container && wearer->container->add(owner)) {
engine.actors.remove(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);
}
}
bool Pickable::use(Actor *owner, Actor *wearer) {
TCODList<Actor *> list;
if(selector) {
selector->selectTargets(wearer, list);
} else {
list.push(wearer);
}
bool succeed=false;
for(Actor **it=list.begin(); it!=list.end(); it++) {
if(effect->applyTo(*it)) {
succeed=true;
}
}
if(succeed) {
if(wearer->container) {
wearer->container->remove(owner);
delete owner;
}
}
return succeed;
}
void Pickable::save(TCODZip &zip) {
zip.putInt(selector != NULL);
zip.putInt(effect != NULL);
if(selector) selector->save(zip);
//if(effect) effect->save(zip);
}
void Pickable::load(TCODZip &zip) {
bool hasSelector = zip.getInt();
bool hasEffect = zip.getInt();
if(hasSelector) {
selector = new TargetSelector(TargetSelector::CLOSEST_MONSTER, 0);
selector->load(zip);
}
/*
if(hasEffect) {
effect = new Effect();
effect->load(zip);
}
*/
}
/* Pickable Factory */
Pickable *Pickable::create(TCODZip &zip) {
Pickable *pickable=NULL;
pickable->load(zip);
return pickable;
}

View File

@ -1,4 +1,4 @@
class TargetSelector { class TargetSelector : public Persistent {
public: public:
enum SelectorType { enum SelectorType {
CLOSEST_MONSTER, CLOSEST_MONSTER,
@ -8,63 +8,35 @@ public:
}; };
TargetSelector(SelectorType type, float range); TargetSelector(SelectorType type, float range);
void selectTargets(Actor *wearer, TCODList<Actor *>& list); void selectTargets(Actor *wearer, TCODList<Actor *>& list);
void save(TCODZip &zip);
void load(TCODZip &zip);
protected: protected:
SelectorType type; SelectorType type;
float range; float range;
}; };
class Effect { class Effect /*: public Persistent */{
public: public:
virtual bool applyTo(Actor *actor) = 0; virtual bool applyTo(Actor *actor) = 0;
// void save(TCODZip &zip);
// void load(TCODZip &zip);
}; };
class Pickable : public Persistent { class Pickable : public Persistent {
public: public:
Pickable(TargetSelector *selector, Effect *effect);
virtual ~Pickable();
bool pick(Actor *owner, Actor *wearer); bool pick(Actor *owner, Actor *wearer);
void drop(Actor *owner, Actor *wearer); void drop(Actor *owner, Actor *wearer);
virtual bool use(Actor *owner, Actor *wearer); bool use(Actor *owner, Actor *wearer);
static Pickable *create(TCODZip &zip); static Pickable *create(TCODZip &zip);
void save(TCODZip &zip);
void load(TCODZip &zip);
protected: protected:
enum PickableType { TargetSelector *selector;
HEALER, LIGHTNING_BOLT, CONFUSER, FIREBALL Effect *effect;
};
};
/* Sub-Pickables */
class Healer : public Pickable {
public:
float amount; // how much?
Healer(float amount);
bool use(Actor *owner, Actor *wearer);
void load(TCODZip &zip);
void save(TCODZip &zip);
};
/* Aggro Spells */
class LightningBolt: public Pickable {
public:
float range, damage;
LightningBolt(float range, float damage);
bool use(Actor *owner, Actor *wearer);
void load(TCODZip &zip);
void save(TCODZip &zip);
};
class Fireball : public LightningBolt {
public:
Fireball(float range, float damage);
bool use(Actor *owner, Actor *wearer);
void save(TCODZip &zip);
};
class Confuser : public Pickable {
public:
int nbTurns;
float range;
Confuser(int nbTurns, float range);
bool use(Actor *owner, Actor *wearer);
void load(TCODZip &zip);
void save(TCODZip &zip);
}; };
/* Effects */ /* Effects */
@ -76,3 +48,11 @@ public:
bool applyTo(Actor *actor); bool applyTo(Actor *actor);
}; };
class AiChangeEffect : public Effect {
public:
TemporaryAi *newAi;
const char *message;
AiChangeEffect(TemporaryAi *newAi, const char *message);
bool applyTo(Actor *actor);
};

Binary file not shown.