Generic Pickables
This commit is contained in:
parent
ae72140657
commit
d8a83e2768
291
src/Ai.cpp
291
src/Ai.cpp
@ -6,71 +6,108 @@
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
MonsterAi::MonsterAi() : moveCount(0) { }
|
||||
void MonsterAi::update(Actor *owner) {
|
||||
if(owner->destructible && owner->destructible->isDead()) {
|
||||
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) {
|
||||
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_UP: dy=-1; break;
|
||||
case TCODK_DOWN: dy=1; break;
|
||||
case TCODK_LEFT: dx=-1; break;
|
||||
case TCODK_RIGHT: dx=1; break;
|
||||
case TCODK_CHAR: handleActionKey(owner, engine.lastKey.c); break;
|
||||
default:break;
|
||||
default: break;
|
||||
}
|
||||
if(dx != 0 || dy != 0) {
|
||||
engine.gameStatus=Engine::NEW_TURN;
|
||||
engine.gameStatus = Engine::NEW_TURN;
|
||||
if(moveOrAttack(owner, owner->x+dx, owner->y+dy)) {
|
||||
engine.map->computeFov();
|
||||
}
|
||||
@ -106,6 +143,15 @@ bool PlayerAi::moveOrAttack(Actor *owner, int targetx, int targety) {
|
||||
|
||||
void PlayerAi::handleActionKey(Actor *owner, int 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
|
||||
{
|
||||
bool found=false;
|
||||
@ -131,22 +177,13 @@ void PlayerAi::handleActionKey(Actor *owner, int ascii) {
|
||||
break;
|
||||
case 'i': // Display inventory
|
||||
{
|
||||
Actor *actor=choseFromInventory(owner);
|
||||
Actor *actor=chooseFromInventory(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();
|
||||
@ -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_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");
|
||||
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';
|
||||
@ -175,11 +215,13 @@ Actor *PlayerAi::choseFromInventory(Actor *owner) {
|
||||
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);
|
||||
@ -192,94 +234,29 @@ Actor *PlayerAi::choseFromInventory(Actor *owner) {
|
||||
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::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);
|
||||
}
|
||||
|
18
src/Ai.hpp
18
src/Ai.hpp
@ -20,11 +20,12 @@ public :
|
||||
protected :
|
||||
bool moveOrAttack(Actor *owner, int targetx, int targety);
|
||||
void handleActionKey(Actor *owner, int ascii);
|
||||
Actor *choseFromInventory(Actor *owner);
|
||||
Actor *chooseFromInventory(Actor *owner);
|
||||
};
|
||||
|
||||
class MonsterAi : public Ai {
|
||||
public :
|
||||
MonsterAi();
|
||||
void update(Actor *owner);
|
||||
void load(TCODZip &zip);
|
||||
void save(TCODZip &zip);
|
||||
@ -38,18 +39,15 @@ public:
|
||||
TemporaryAi(int nbTurns);
|
||||
void update(Actor *owner);
|
||||
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 save(TCODZip &zip);
|
||||
protected:
|
||||
int nbTurns;
|
||||
Ai *oldAi;
|
||||
};
|
||||
|
||||
class ConfusedMonsterAi : public TemporaryAi {
|
||||
public:
|
||||
ConfusedMonsterAi(int nbTurns);
|
||||
void update(Actor *owner);
|
||||
};
|
||||
|
21
src/Map.cpp
21
src/Map.cpp
@ -178,28 +178,41 @@ void Map::addItem(int x, int y) {
|
||||
Actor *healthPotion = new Actor(x, y, '!', "Health Potion",
|
||||
TCODColor::violet);
|
||||
healthPotion->blocks=false;
|
||||
healthPotion->pickable=new Healer(4);
|
||||
healthPotion->pickable=new Pickable(NULL, new HealthEffect(4, NULL));
|
||||
engine.actors.push(healthPotion);
|
||||
} else if(dice < 70 + 10) {
|
||||
// Create a scroll of lightning bolt
|
||||
Actor *scrollOfLightningBolt = new Actor(x, y, '#',
|
||||
"Scroll of lightning bolt", TCODColor::lightYellow);
|
||||
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);
|
||||
} else if(dice < 70 + 10 + 10) {
|
||||
// Create a scroll of fireball
|
||||
Actor *scrollOfFireball = new Actor(x, y, '#', "Scroll of fireball",
|
||||
TCODColor::lightYellow);
|
||||
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);
|
||||
} else {
|
||||
// create a scroll of confusion
|
||||
Actor *scrollOfConfusion = new Actor(x, y, '#',
|
||||
"Scroll of confusion", TCODColor::lightYellow);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
254
src/Pickable.cpp
254
src/Pickable.cpp
@ -1,49 +1,6 @@
|
||||
#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) { }
|
||||
TargetSelector::TargetSelector(SelectorType type, float range) : type(type), range(range) { }
|
||||
void TargetSelector::selectTargets(Actor *wearer, TCODList<Actor *> & list) {
|
||||
switch(type) {
|
||||
case CLOSEST_MONSTER: {
|
||||
@ -95,114 +52,34 @@ void TargetSelector::selectTargets(Actor *wearer, TCODList<Actor *> & list) {
|
||||
}
|
||||
}
|
||||
|
||||
/* 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);
|
||||
}
|
||||
void TargetSelector::save(TCODZip &zip) {
|
||||
int persistType = 0;
|
||||
switch(type) {
|
||||
case CLOSEST_MONSTER: persistType = 1; break;
|
||||
case SELECTED_MONSTER: persistType = 2; break;
|
||||
case WEARER_RANGE: persistType = 3; break;
|
||||
case SELECTED_RANGE: persistType = 4; break;
|
||||
}
|
||||
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.putInt(persistType);
|
||||
zip.putFloat(range);
|
||||
}
|
||||
|
||||
HealthEffect::HealthEffect(float amount, const char *message)
|
||||
: amount(amount), message(message) { }
|
||||
void TargetSelector::load(TCODZip &zip) {
|
||||
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) {
|
||||
if(!actor->destructible) return false;
|
||||
if(amount > 0) {
|
||||
float pointsHealed = actor->destructible->heal(amount);
|
||||
float pointsHealed=actor->destructible->heal(amount);
|
||||
if(pointsHealed > 0) {
|
||||
if(message) {
|
||||
engine.gui->message(TCODColor::lightGrey, message, actor->name, pointsHealed);
|
||||
@ -220,3 +97,92 @@ bool HealthEffect::applyTo(Actor *actor) {
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1,4 +1,4 @@
|
||||
class TargetSelector {
|
||||
class TargetSelector : public Persistent {
|
||||
public:
|
||||
enum SelectorType {
|
||||
CLOSEST_MONSTER,
|
||||
@ -8,63 +8,35 @@ public:
|
||||
};
|
||||
TargetSelector(SelectorType type, float range);
|
||||
void selectTargets(Actor *wearer, TCODList<Actor *>& list);
|
||||
void save(TCODZip &zip);
|
||||
void load(TCODZip &zip);
|
||||
|
||||
protected:
|
||||
SelectorType type;
|
||||
float range;
|
||||
};
|
||||
|
||||
class Effect {
|
||||
class Effect /*: public Persistent */{
|
||||
public:
|
||||
virtual bool applyTo(Actor *actor) = 0;
|
||||
// void save(TCODZip &zip);
|
||||
// void load(TCODZip &zip);
|
||||
};
|
||||
|
||||
class Pickable : public Persistent {
|
||||
public:
|
||||
Pickable(TargetSelector *selector, Effect *effect);
|
||||
virtual ~Pickable();
|
||||
bool pick(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);
|
||||
void save(TCODZip &zip);
|
||||
void load(TCODZip &zip);
|
||||
|
||||
protected:
|
||||
enum PickableType {
|
||||
HEALER, LIGHTNING_BOLT, CONFUSER, FIREBALL
|
||||
};
|
||||
};
|
||||
|
||||
/* 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);
|
||||
TargetSelector *selector;
|
||||
Effect *effect;
|
||||
};
|
||||
|
||||
/* Effects */
|
||||
@ -76,3 +48,11 @@ public:
|
||||
bool applyTo(Actor *actor);
|
||||
};
|
||||
|
||||
class AiChangeEffect : public Effect {
|
||||
public:
|
||||
TemporaryAi *newAi;
|
||||
const char *message;
|
||||
|
||||
AiChangeEffect(TemporaryAi *newAi, const char *message);
|
||||
bool applyTo(Actor *actor);
|
||||
};
|
||||
|
BIN
src/main.hpp.gch
BIN
src/main.hpp.gch
Binary file not shown.
Loading…
Reference in New Issue
Block a user