/*
* effects.cpp
* Effects
* ____ _
* | _ \ ___ __ _| |_ __ ___ ___
* | |_) / _ \/ _` | | '_ ` _ \/ __|
* | _ < __/ (_| | | | | | | \__ \
* |_| \_\___|\__,_|_|_| |_| |_|___/
*
* Permission to use, modify and distribute is granted via the
* Creative Commons - Attribution - Non Commercial - Share Alike 3.0 License
* http://creativecommons.org/licenses/by-nc-sa/3.0/
*
* Copyright (C) 2007-2012 Jason Mitchell, Randi Mitchell
* Contributions by Tim Callahan, Jonathan Hseu
* Based on Mordor (C) Brooke Paul, Brett J. Vickers, John P. Freeman
*
*/
#include "mud.h"
#include "effects.h"
#include "commands.h"
#include "pythonHandler.h"
// C++ includes
#include <iomanip>
#include <locale>
//*********************************************************************
// getDisplayName
//*********************************************************************
bstring EffectInfo::getDisplayName() const {
bstring displayName = myEffect->getDisplay();
if(myEffect->getName() != "drunkenness")
return(displayName);
// drunkenness has a different name based on strength
AlcoholState state = getAlcoholState(this);
if(state == ALCOHOL_SOBER)
displayName += "Sober"; // this state should never happen
else if(state == ALCOHOL_TIPSY)
displayName += "Tipsy";
else if(state == ALCOHOL_DRUNK)
displayName += "Drunk";
else if(state == ALCOHOL_INEBRIATED)
displayName += "Inebriated";
displayName += "^x";
return(displayName);
}
//*********************************************************************
// getAlcoholState
//*********************************************************************
AlcoholState getAlcoholState(const EffectInfo* effect) {
if(!effect || effect->getStrength() < 1)
return(ALCOHOL_SOBER);
if(effect->getStrength() < 20)
return(ALCOHOL_TIPSY);
if(effect->getStrength() < 66)
return(ALCOHOL_DRUNK);
return(ALCOHOL_INEBRIATED);
}
//*********************************************************************
// clearEffects
//*********************************************************************
void Config::clearEffects() {
for(std::pair<bstring, Effect*> ep : effects) {
delete ep.second;
}
effects.clear();
}
//*********************************************************************
// getEffect
//*********************************************************************
Effect* Config::getEffect(bstring eName) {
std::map<bstring, Effect*>::const_iterator eIt;
if( (eIt = effects.find(eName)) == effects.end())
return(NULL);
else
return((*eIt).second);
}
//*********************************************************************
// effectExists
//*********************************************************************
bool Config::effectExists(bstring eName) {
return(effects.find(eName) != effects.end());
}
//*********************************************************************
// dmEffectList
//*********************************************************************
int dmEffectList(Player* player, cmd* cmnd) {
Effect* effect=0;
bstring command = getFullstrText(cmnd->fullstr, 1);
bool all = (command == "all");
int id = command.toInt();
player->printColor("^YEffects\n");
player->printColor("Type ^y*effects all^x to see all effects or ^y*effects [num]^x to see a specific effect.\n");
int i = 0;
for(std::pair<bstring, Effect*> sp : gConfig->effects) {
effect = sp.second;
i++;
if(id != 0 && i != id)
continue;
player->printColor("%d)\tName: ^W%-20s^x Use Str: %s^x Display: %s\n", i,
effect->getName().c_str(), effect->usesStrength() ? "^gY" : "^rN",
effect->getDisplay().c_str());
if(!all && i != id)
continue;
player->printColor("\tOpposite Effect: %s\n", effect->getOppositeEffect().c_str());
player->printColor("\tSelfAddStr: %s^x\n", effect->getSelfAddStr().c_str());
player->printColor("\tRoomAddStr: %s^x\n", effect->getRoomAddStr().c_str());
player->printColor("\tSelfDelStr: %s^x\n", effect->getSelfDelStr().c_str());
player->printColor("\tRoomDelStr: %s^x\n", effect->getRoomDelStr().c_str());
player->printColor("\tPulsed: %s^x\n", effect->isPulsed() ? "^GPulsed" : "Non-Pulsed");
if(effect->isPulsed())
*player << ColorOn << "\tPulse Delay: " << effect->getPulseDelay() << "^x\n" << ColorOff;
player->printColor("\tSpell: %s^x\n", effect->isSpell() ? "^GYes" : "^RNo");
player->printColor("\tType: %s^x\n", effect->getType().c_str());
if(!effect->getApplyScript().empty())
player->printColor("\tApply Script: %s\n", effect->getApplyScript().c_str());
if(!effect->getPreApplyScript().empty())
player->printColor("\tPre-Apply Script: %s\n", effect->getPreApplyScript().c_str());
if(!effect->getPostApplyScript().empty())
player->printColor("\tPost-Apply Script: %s\n", effect->getPostApplyScript().c_str());
if(!effect->getComputeScript().empty())
player->printColor("\tCompute Script: %s\n", effect->getComputeScript().c_str());
if(!effect->getPulseScript().empty())
player->printColor("\tPulse Script: %s\n", effect->getPulseScript().c_str());
if(!effect->getUnApplyScript().empty())
player->printColor("\tUnApplyScript: %s\n", effect->getUnApplyScript().c_str());
}
return(0);
}
//*********************************************************************
// willOverWrite
//*********************************************************************
// Will the given effect overwrite the existing effect?
bool EffectInfo::willOverWrite(EffectInfo* existingEffect) const {
// No effect, it'll overwrite :-P
if(!existingEffect)
return(true);
// If the existing effect is due to an applier (currently only worn objects),
// there's no way to override that effect.
if(existingEffect->getApplier())
return(false);
// Perm effects take presedence
if(duration == -1 && existingEffect->duration != -1)
return(true);
if(strength < existingEffect->strength)
return(false);
// Don't overwrite perm effects with non perm
if(existingEffect->duration == -1 && duration != -1)
return(false);
return(true);
}
//*********************************************************************
// operator<<
//*********************************************************************
std::ostream& operator<<(std::ostream& out, const EffectInfo& eff) {
out.setf(std::ios::right, std::ios::adjustfield);
bstring display = eff.getDisplayName();
short count=0;
int len = display.getLength();
for(int i=0; i<len; i++) {
if(display.getAt(i) == '^') {
count += 2;
i++;
}
}
out << std::setw(38+count) << display << " - ";
if(eff.duration == -1)
out << "Permanent!";
else
out << timeStr(eff.duration);
return(out);
}
//*********************************************************************
// pulse
//*********************************************************************
// True on a sucessful pulse
// False if it's time to wear off
bool EffectInfo::pulse(time_t t) {
ASSERTLOG(myParent);
bool wearOff = updateLastMod(t);
if(wearOff)
return(false);
if(myEffect->isPulsed()) {
if(timeForPulse(t))
return(runScript(myEffect->getPulseScript()));
}
return(true);
}
//*********************************************************************
// updateLastMod
//*********************************************************************
// True if it's time for this effect to wear off
bool EffectInfo::updateLastMod(time_t t) {
time_t diff = t - lastMod;
lastMod = t;
diff = MIN(MAX(0, duration), diff);
duration -= diff;
if(myApplier) {
// for object appliers, keep the duration on the object in-sync with the effect duration
Object* object = myApplier->getAsObject();
if(object)
object->setEffectDuration(duration);
}
return(duration == 0);
}
//*********************************************************************
// timeForPulse
//*********************************************************************
bool EffectInfo::timeForPulse(time_t t) {
time_t diff = t - lastPulse;
if(diff < myEffect->getPulseDelay())
return(false);
lastPulse = t;
return(true);
}
//*********************************************************************
// remove
//*********************************************************************
bool EffectInfo::remove(bool show) {
bool success = false;
ASSERTLOG(myParent);
if(myEffect) {
success = true;
if(show) {
Creature* cParent = myParent->getAsCreature();
if(cParent) {
if(!myEffect->getSelfDelStr().empty())
cParent->printColor("%s\n", myEffect->getSelfDelStr().c_str());
if(!myEffect->getRoomDelStr().empty() && cParent->getRoomParent())
cParent->getRoomParent()->effectEcho(myEffect->getRoomDelStr(), cParent);
}
Exit* xParent = myParent->getAsExit();
if(xParent) {
if(!myEffect->getRoomDelStr().empty()) {
if(xParent->getRoom())
xParent->getRoom()->effectEcho(myEffect->getRoomDelStr(), xParent);
else
std::cout << "Exit with no parent room!!!\n";
}
}
BaseRoom* rParent = myParent->getAsRoom();
if(rParent) {
if(!myEffect->getRoomDelStr().empty())
rParent->effectEcho(myEffect->getRoomDelStr());
}
}
success &= runScript(myEffect->getUnApplyScript());
}
if(myApplier) {
// If object appliers are being worn when we remove the effect,
// the object will be broken and unequipped
Object* object = myApplier->getAsObject();
if(object && object->flagIsSet(O_WORN)) {
object->setShotsCur(0);
myParent->getAsPlayer()->breakObject(object, object->getWearflag());
}
}
return(success);
}
//*********************************************************************
// compute
//*********************************************************************
bool EffectInfo::compute(MudObject* applier) {
ASSERTLOG(myParent);
if(!myEffect)
return(false);
myApplier = applier;
bool retVal = runScript(myEffect->getComputeScript(), applier);
// Incase computing results in a non permanent effect that has a negative duration!
if(this->duration < -1) {
duration = 60;
}
return(retVal);
}
//*********************************************************************
// add
//*********************************************************************
bool EffectInfo::add() {
ASSERTLOG(myParent);
if(!myEffect)
return(false);
Creature* cParent = myParent->getAsCreature();
if(cParent) {
if(!myEffect->getSelfAddStr().empty())
cParent->printColor("%s\n", cParent->doReplace(myEffect->getSelfAddStr().c_str(), cParent, myApplier).c_str());
// TODO: replace *ACTOR* etc
if(!myEffect->getRoomAddStr().empty() && cParent->getRoomParent())
cParent->getRoomParent()->effectEcho(myEffect->getRoomAddStr(), cParent, myApplier);
}
Exit* xParent = myParent->getAsExit();
if(xParent) {
if(!myEffect->getRoomAddStr().empty()) {
if(xParent->getRoom())
xParent->getRoom()->effectEcho(myEffect->getRoomAddStr(), xParent);
else
std::cout << "Exit with no parent room!!!\n";
}
}
BaseRoom* rParent = myParent->getAsRoom();
if(rParent) {
if(!myEffect->getRoomAddStr().empty())
rParent->effectEcho(myEffect->getRoomAddStr());
}
return(true);
}
//*********************************************************************
// apply
//*********************************************************************
bool EffectInfo::apply() {
ASSERTLOG(myParent);
if(!myEffect)
return(false);
return(runScript(myEffect->getApplyScript()));
}
//*********************************************************************
// preApply
//*********************************************************************
bool EffectInfo::preApply() {
ASSERTLOG(myParent);
if(!myEffect)
return(false);
return(runScript(myEffect->getPreApplyScript()));
}
//*********************************************************************
// postApply
//*********************************************************************
bool EffectInfo::postApply(bool keepApplier) {
bool success = false;
ASSERTLOG(myParent);
if(myEffect)
success = runScript(myEffect->getPostApplyScript());
// If you're passing in keepApplier = true, be sure you know what you're doing!
// The effect will keep a pointer to the applier, and if the applier is removed
// from memory (the monster is killed, the object is pawned), you'll get an
// invalid pointer that might crash the game. Currently only equipped object
// appliers are supported for passing in keepApplier = true.
if(!keepApplier)
myApplier = 0;
return(success);
}
// End - EffectInfo functions
//*********************************************************************
// addEffect
//*********************************************************************
EffectInfo* MudObject::addEffect(const bstring& effect, long duration, int strength, MudObject* applier, bool show, const Creature* owner, bool keepApplier) {
return(effects.addEffect(effect, duration, strength, applier, show, this, owner, keepApplier));
}
EffectInfo* Effects::addEffect(const bstring& effect, long duration, int strength, MudObject* applier, bool show, MudObject* pParent, const Creature* owner, bool keepApplier) {
if(!gConfig->getEffect(effect))
return(null);
EffectInfo* newEffect = new EffectInfo(effect, time(0), duration, strength, pParent, owner);
if(!newEffect->compute(applier)) {
delete newEffect;
return(NULL);
}
if(strength != -2)
newEffect->setStrength(strength);
if(duration != -2)
newEffect->setDuration(duration);
return(addEffect(newEffect, show, 0, keepApplier));
}
EffectInfo* MudObject::addEffect(EffectInfo* newEffect, bool show, bool keepApplier) {
return(effects.addEffect(newEffect, show, this, keepApplier));
}
EffectInfo* Effects::addEffect(EffectInfo* newEffect, bool show, MudObject* pParent, bool keepApplier) {
if(pParent)
newEffect->setParent(pParent);
EffectInfo* oldEffect = getExactEffect(newEffect->getName());
bool success = true;
if(oldEffect && !newEffect->willOverWrite(oldEffect)) {
// The new effect won't overwrite, so don't add it
if(pParent->getAsPlayer() && show)
pParent->getAsPlayer()->print("The effect didn't take hold.\n");
delete newEffect;
return(NULL);
}
// pre-apply gets called BEFORE the replaced effect gets removed
newEffect->preApply();
// If no existing effect, or this one will overwrite remove the old one, and add the new one
removeEffect(newEffect->getName(), false, true); // Don't show the removal
// Only show if we're not overwriting an effect
if(!oldEffect && show)
newEffect->add();
success &= newEffect->apply();
effectList.push_back(newEffect);
if(newEffect->getParent()->getAsRoom())
newEffect->getParent()->getAsRoom()->addEffectsIndex();
else if(newEffect->getParent()->getAsExit() && newEffect->getParent()->getAsExit()->getRoom())
newEffect->getParent()->getAsExit()->getRoom()->addEffectsIndex();
// post-apply gets run after everything is done
newEffect->postApply(keepApplier);
return(newEffect);
}
//*********************************************************************
// addPermEffect
//*********************************************************************
EffectInfo* MudObject::addPermEffect(const bstring& effect, int strength, bool show) {
return(effects.addEffect(effect, -1, strength, NULL, show, this));
}
//*********************************************************************
// removeEffect
//*********************************************************************
// Remove the effect (Won't remove permanent effects if remPerm is false)
bool MudObject::removeEffect(const bstring& effect, bool show, bool remPerm, MudObject* fromApplier) {
return(effects.removeEffect(effect, show, remPerm, fromApplier));
}
bool Effects::removeEffect(const bstring& effect, bool show, bool remPerm, MudObject* fromApplier) {
EffectInfo* toDel = getExactEffect(effect);
if( toDel &&
(toDel->getDuration() != -1 || (toDel->getDuration() == -1 && remPerm)) &&
(!fromApplier || fromApplier == toDel->getApplier())
)
return(removeEffect(toDel, show));
return(false);
}
bool MudObject::removeEffect(EffectInfo* toDel, bool show) {
return(effects.removeEffect(toDel, show));
}
bool Effects::removeEffect(EffectInfo* toDel, bool show) {
if(!toDel)
return(false);
effectList.remove(toDel);
toDel->remove(show);
delete toDel;
return(true);
}
//*********************************************************************
// removeOwner
//*********************************************************************
// on suicide, we remove the owner of the effect
void Effects::removeOwner(const Creature* owner) {
EffectList::iterator it;
for(it = effectList.begin() ; it != effectList.end() ; it++) {
if((*it)->isOwner(owner))
(*it)->setOwner(0);
}
}
//*********************************************************************
// objectCanBestowEffect
//*********************************************************************
bool Effect::objectCanBestowEffect(const bstring& effect) {
return( effect != "" &&
effect != "vampirism" &&
effect != "porphyria" &&
effect != "lycanthropy"
);
}
//*********************************************************************
// isEffected
//*********************************************************************
bool MudObject::isEffected(const bstring& effect, bool exactMatch) const {
return(effects.isEffected(effect, exactMatch));
}
bool MudObject::isEffected(EffectInfo* effect) const {
return(effects.isEffected(effect));
}
// We are effected if we have an effect with this name, or an effect with a base effect
// of this name
bool Effects::isEffected(const bstring& effect, bool exactMatch) const {
//EffectList list;
for(EffectInfo* eff : effectList) {
if(eff->getName() == effect || (!exactMatch && eff->hasBaseEffect(effect)))
return(true);
}
return(false);
}
bool Effects::isEffected(EffectInfo* effect) const {
for(EffectInfo* eff : effectList) {
if(eff->getName() == effect->getName() || eff->hasBaseEffect(effect->getName()) || effect->hasBaseEffect(eff->getName()))
return(true);
}
return(false);
}
//*********************************************************************
// getBaseEffects
//*********************************************************************
const std::list<bstring>& Effect::getBaseEffects() {
return(baseEffects);
}
// Effect - Avian Aria, Base Effects = fly, levitate
// eff = fly
// Effect - Avian Aria, Base Effects = fly, levitate
// Effect Some Jackass's Flying Song. Base effect = fly, levitate
//*********************************************************************
// hasPermEffect
//*********************************************************************
bool MudObject::hasPermEffect(const bstring& effect) const {
EffectInfo* toCheck = effects.getEffect(effect);
return(toCheck && toCheck->getDuration() == -1);
}
//*********************************************************************
// getEffect
//*********************************************************************
EffectInfo* MudObject::getEffect(const bstring& effect) const {
return(effects.getEffect(effect));
}
// Returns the effect if we find one that the name matches or has
// the base effect mentioned
EffectInfo* Effects::getEffect(const bstring& effect) const {
EffectList::const_iterator eIt;
EffectInfo* toReturn = NULL;
for(eIt = effectList.begin() ; eIt != effectList.end() ; eIt++) {
if((*eIt) && ((*eIt)->getName() == effect || (*eIt)->hasBaseEffect(effect))) {
if(!toReturn)
toReturn = (*eIt);
else {
// If we have something to return, compare it, we'll return the
// effect with the highest strength
if((*eIt)->getStrength() > toReturn->getStrength())
toReturn = (*eIt);
}
}
}
return(toReturn);
}
//*********************************************************************
// getExactEffect
//*********************************************************************
EffectInfo* MudObject::getExactEffect(const bstring& effect) const {
return(effects.getExactEffect(effect));
}
// Returns the effect with an exact name match
EffectInfo* Effects::getExactEffect(const bstring& effect) const {
EffectList::const_iterator eIt;
for(eIt = effectList.begin() ; eIt != effectList.end() ; eIt++) {
if((*eIt) && (*eIt)->getName() == effect)
return((*eIt));
}
return(NULL);
}
//*********************************************************************
// pulseEffects
//*********************************************************************
// Pulse all effects on this creature
// return false if creature died because of this
bool Creature::pulseEffects(time_t t) {
bool pulsed = true;
bool poison = false;
EffectInfo* effect=0;
EffectList::iterator eIt;
deathtype = DT_NONE;
for(eIt = effects.effectList.begin() ; eIt != effects.effectList.end() ;) {
effect = (*eIt);
// Pulse!
ASSERTLOG(effect->getParent() == this);
pulsed = effect->pulse(t);
// If pulse returns false, purge this effect
if(!pulsed) {
effect->remove();
if(poison || effect->isPoison())
poison = true;
delete effect;
eIt = effects.effectList.erase(eIt);
} else
eIt++;
}
// pulse effects might kill them
if(deathtype != DT_NONE && hp.getCur() < 1) {
if(isPlayer())
getAsPlayer()->die(deathtype);
else
getAsMonster()->mobDeath();
return(false);
}
// if they're not poisoned anymore, clear poison
if(poison && !isPoisoned())
curePoison();
return(true);
}
//*********************************************************************
// pulseEffects
//*********************************************************************
bool BaseRoom::pulseEffects(time_t t) {
effects.pulse(t, this);
for(Exit* exit : exits) {
exit->pulseEffects(t);
}
return(true);
}
//*********************************************************************
// pulseEffects
//*********************************************************************
bool Exit::pulseEffects(time_t t) {
effects.pulse(t, this);
return(true);
}
//*********************************************************************
// pulse
//*********************************************************************
// generic pulse function that can be used on rooms and exits (because
// they can't die like players can)
void Effects::pulse(time_t t, MudObject* pParent) {
EffectList::iterator it;
EffectInfo* effect=0;
bool pulsed=false;
for(it = effectList.begin() ; it != effectList.end() ;) {
effect = (*it);
// Pulse!
ASSERTLOG(effect->getParent() == pParent);
pulsed = effect->pulse(t);
// If pulse returns false, purge this effect
if(!pulsed) {
effect->remove();
delete effect;
it = effectList.erase(it);
} else
it++;
}
}
//*********************************************************************
// removeAll
//*********************************************************************
void Effects::removeAll() {
EffectInfo* effect=0;
EffectList::iterator eIt;
for(eIt = effectList.begin() ; eIt != effectList.end() ; eIt++) {
effect = (*eIt);
delete effect;
(*eIt) = NULL;
}
effectList.clear();
}
//*********************************************************************
// copy
//*********************************************************************
void Effects::copy(const Effects* source, MudObject* pParent) {
EffectInfo* effect;
EffectList::const_iterator eIt;
for(eIt = source->effectList.begin() ; eIt != source->effectList.end() ; eIt++) {
effect = new EffectInfo();
(*effect) = *(*eIt);
effect->setParent(pParent);
effectList.push_back(effect);
}
}
//*********************************************************************
// cmdEffects
//*********************************************************************
int cmdEffects(Creature* creature, cmd* cmnd) {
Creature* target = creature;
int num=0;
if(creature->isCt()) {
if(cmnd->num > 1) {
target = creature->getParent()->findCreature(creature, cmnd);
cmnd->str[1][0] = up(cmnd->str[1][0]);
if(!target) {
target = gServer->findPlayer(cmnd->str[1]);
if(!target || !creature->canSee(target)) {
creature->print("Target not found.\n");
return(0);
}
}
}
}
num = target->effects.effectList.size();
creature->print("Current Effects for %s:\n", target->getCName());
creature->printColor("%s", target->effects.getEffectsString(creature).c_str());
creature->print("\n%d effect%s found.\n", num, num != 1 ? "s" : "");
return(0);
}
//*********************************************************************
// getEffectsList
//*********************************************************************
// Return a list of effects
bstring Effects::getEffectsList() const {
std::ostringstream effStr;
effStr << "Effects: ";
int num = 0;
const EffectInfo* effect;
EffectList::const_iterator eIt;
for(eIt = effectList.begin() ; eIt != effectList.end() ; eIt++) {
effect = (*eIt);
if(num != 0)
effStr << ", ";
effStr << effect->getName();
num++;
}
if(num == 0)
effStr << "None";
effStr << ".\n";
bstring toPrint = effStr.str();
return(toPrint);
}
//*********************************************************************
// getEffectsString
//*********************************************************************
// Used to print out what effects a creature is under
bstring Effects::getEffectsString(const Creature* viewer) {
const Object* object=0;
std::ostringstream effStr;
long t = time(0);
for(EffectInfo* effect : effectList) {
effect->updateLastMod(t);
effStr << *effect;
if(viewer->isStaff()) {
// if(!effect->getBaseEffect().empty())
// effStr << " Base(" << effect->getBaseEffect() << ")";
effStr << " ^WStrength:^x " << effect->getStrength();
if(effect->getExtra()) {
effStr << " ^WExtra:^x " << effect->getExtra();
// extra information
if(effect->getName() == "illusion") {
const RaceData* race = gConfig->getRace(effect->getExtra());
if(race)
effStr << " (" << race->getName() << ")";
} else if( (effect->getDuration() == -1) && (
effect->getName() == "wall-of-force" ||
effect->getName() == "wall-of-fire" ||
effect->getName() == "wall-of-thorns"
) ) {
// permanent walls are only down for a little bit
effStr << " (# pulses until reinstantiate)";
}
}
if(effect->getApplier()) {
object = effect->getApplier()->getAsConstObject();
if(object)
effStr << " ^WApplier:^x " << object->getName() << "^x";
}
}
effStr << "\n";
}
return(effStr.str());
}
//*********************************************************************
// convertOldEffects
//*********************************************************************
// This function will convert flag/lt combos into effects
void Creature::convertOldEffects() {
Player* pPlayer = getAsPlayer();
Monster* mMonster = getAsMonster();
if(version < "2.40") {
if(mMonster) {
// mMonster->convertToEffect("stoneskin", OLD_M_STONESKIN, -1);
// mMonster->convertToEffect("invisibility", OLD_M_INVISIBLE, OLD_LT_INVISIBILITY);
} else if(pPlayer) {
}
}
}
//*********************************************************************
// convertToEffect
//*********************************************************************
// Convert the given effect from a flag/lt to an effect
bool Creature::convertToEffect(const bstring& effect, int flag, int lt) {
if(!flagIsSet(flag))
return(false);
clearFlag(flag);
long duration = 0;
if(lt != -1 && lasttime[lt].interval != 0)
duration = lasttime[lt].interval;
else
duration = -1;
EffectInfo* newEffect = new EffectInfo(effect, time(0), duration, 1, this);
// if(lt != -1 && (effect == "armor" || effect == "stoneskin")) {
// newEffect->setStrength(lasttime[lt].misc);
// }
// Assuming that they're already properly under the effect, so just add it to the list
// and don't actually add it or compute it.
// IE: Strength buff -- they already have +str, so don't give them more str!!
effects.effectList.push_back(newEffect);
return(true);
}
//*********************************************************************
// removeOppositeEffect
//*********************************************************************
bool MudObject::removeOppositeEffect(const EffectInfo *effect) {
return(effects.removeOppositeEffect(effect));
}
bool Effects::removeOppositeEffect(const EffectInfo *effect) {
Effect* parentEffect = effect->getEffect();
if(!parentEffect)
return(false);
if(parentEffect->getOppositeEffect().empty())
return(false);
return(removeEffect(parentEffect->getOppositeEffect(), true, true));
}
//*********************************************************************
// exitEffectDamage
//*********************************************************************
// Wrapper to actually do the damage to the target
// Return: true if they were killed
bool exitEffectDamage(const EffectInfo *effect, Creature* target, Creature* owner, Realm realm, DeathType dt, const char* crtStr, const char* deathStr, const char* roomStr, const char* killerStr) {
Damage damage;
if(!effect || effect->getExtra() || effect->isOwner(owner))
return(false);
Player* killer=0;
bool online = true;
if(effect->getOwner() != "") {
killer = gServer->findPlayer(effect->getOwner());
if(!killer) {
if(loadPlayer(effect->getOwner().c_str(), &killer))
online = false;
else
killer = 0;
}
}
damage.set(mrand(effect->getStrength() / 2, effect->getStrength() * 3 / 2));
target->modifyDamage(0, MAGICAL, damage, realm);
if(killer && target->isMonster()) {
target->getAsMonster()->addEnemy(killer);
if(online)
target->getAsMonster()->adjustThreat(killer, damage.get());
}
target->printColor(crtStr, target->customColorize("*CC:DAMAGE*").c_str(), damage.get());
target->hp.decrease(damage.get());
if(target->hp.getCur() < 1) {
target->print(deathStr);
broadcast(target->getSock(), target->getRoomParent(), roomStr, target, target);
if(killer) {
if(online)
killer->print(killerStr, target);
target->die(killer);
killer->save(online);
if(!online)
free_crt(killer);
} else {
if(target->isPlayer())
target->getAsPlayer()->die(dt);
else
target->getAsMonster()->mobDeath();
}
return(true);
}
if(killer && !online)
free_crt(killer);
return(false);
}
//*********************************************************************
// doEffectDamage
//*********************************************************************
// Return: true if they were killed
bool Exit::doEffectDamage(Creature* target) {
Creature *owner = target;
if(target->isPet())
owner = target->getMaster();
if( exitEffectDamage(
getEffect("wall-of-fire"),
target, owner, FIRE, BURNED,
"The wall of fire burns you for %s%d^x damage.\n",
"You are burned to death!\n",
"%1M is engulfed by the wall of fire.\n%M burns to death!",
"%M is engulfed by your wall of fire and is incinerated!\n"
) )
return(true);
if( exitEffectDamage(
getEffect("wall-of-thorns"),
target, owner, EARTH, THORNS,
"The wall of thorns stabs you for %s%d^x damage.\n",
"You are stabbed to death!\n",
"%1M is engulfed by a wall of thorns.\n%M is stabbed to death!",
"%M is engulfed by your wall of thorns and is stabbed to death!\n"
) )
return(true);
return(false);
}
//*********************************************************************
// doReplace
//*********************************************************************
bstring Creature::doReplace(bstring fmt, const MudObject* actor, const MudObject* applier) const {
const Creature* cActor = actor->getAsConstCreature();
const Exit* xActor = actor->getAsConstExit();
const Creature* cApplier = applier->getAsConstCreature();
if(cActor) {
fmt.Replace("*ACTOR*", cActor->getCrtStr(this, CAP).c_str());
fmt.Replace("*LOW-ACTOR*", cActor->getCrtStr(this).c_str());
fmt.Replace("*A-HISHER*", cActor->hisHer());
fmt.Replace("*A-UPHISHER*", cActor->upHisHer());
} else if(xActor) {
fmt.Replace("*ACTOR*", xActor->getCName());
fmt.Replace("*LOW-ACTOR*", xActor->getCName());
}
if(cApplier) {
// Applier Possessive
if(cActor == cApplier) {
fmt.Replace("*APPLIER-POS*", cApplier->upHisHer());
fmt.Replace("*LOW-APPLIER-POS*", cApplier->hisHer());
fmt.Replace("*APPLIER-SELF-POS*", "Your");
fmt.Replace("*LOW-APPLIER-SELF-POS*", "your");
} else {
fmt.Replace("*APPLIER-POS*", bstring(cApplier->getCrtStr(this, CAP) + "'s").c_str());
fmt.Replace("*LOW-APPLIER-POS*", bstring(cApplier->getCrtStr(this) + "'s").c_str());
fmt.Replace("*APPLIER-SELF-POS*", bstring(cApplier->getCrtStr(this, CAP) + "'s").c_str());
fmt.Replace("*LOW-APPLIER-SELF-POS*", bstring(cApplier->getCrtStr(this) + "'s").c_str());
}
fmt.Replace("*APPLIER*", cApplier->getCrtStr(this, CAP).c_str());
fmt.Replace("*LOW-APPLIER*", cApplier->getCrtStr(this).c_str());
fmt.Replace("*AP-HISHER*", cApplier->hisHer());
fmt.Replace("*AP-UPHISHER*", cApplier->upHisHer());
}
return(fmt);
}
//*********************************************************************
// effectEcho
//*********************************************************************
void Container::effectEcho(bstring fmt, const MudObject* actor, const MudObject* applier, Socket* ignore) {
Socket* ignore2 = NULL;
if(actor->getAsConstCreature())
ignore2 = actor->getAsConstCreature()->getSock();
for(const Player* ply : players) {
if(!ply || (ply->getSock() && (ply->getSock() == ignore || ply->getSock() == ignore2)) || ply->isUnconscious())
continue;
bstring toSend = ply->doReplace(fmt, actor, applier);
ply->bPrint(toSend + "\n");
}
}
//*********************************************************************
// getPulseDelay
//*********************************************************************
int Effect::getPulseDelay() const {
return(pulseDelay);
}
//*********************************************************************
// runScript
//*********************************************************************
bool EffectInfo::runScript(const bstring& pyScript, MudObject* applier) {
// Legacy: Default action is return true, so play along with that
if(pyScript.empty())
return(true);
try {
object localNamespace( (handle<>(PyDict_New())));
object effectModule( (handle<>(PyImport_ImportModule("effectLib"))) );
localNamespace["effectLib"] = effectModule;
localNamespace["effect"] = ptr(this);
// Default retVal is true
localNamespace["retVal"] = true;
addMudObjectToDictionary(localNamespace, "actor", myParent);
addMudObjectToDictionary(localNamespace, "applier", applier);
gServer->runPython(pyScript, localNamespace);
bool retVal = extract<bool>(localNamespace["retVal"]);
//std::cout << "runScript returning: " << retVal << std::endl;
return(retVal);
}
catch( error_already_set) {
gServer->handlePythonError();
}
return(false);
}
//*********************************************************************
// pulseCreatureEffects
//*********************************************************************
// lastUserUpdate is set in updateUsers
void Server::pulseCreatureEffects(long t) {
Monster *monster=0;
const Socket *sock=0;
Player* player=0;
std::list<Socket*>::const_iterator it;
for(it = sockets.begin(); it != sockets.end() ; ) {
sock = *it;
it++;
if(!sock->isConnected())
continue;
player = sock->getPlayer();
if(player)
player->pulseEffects(t);
}
MonsterList::iterator mIt = activeList.begin();
while(mIt != activeList.end()) {
// Increment the iterator in case this monster dies during the update and is removed from the active list
monster = (*mIt++);
monster->pulseEffects(t);
}
}
//*********************************************************************
// pulseRoomEffects
//*********************************************************************
void Server::pulseRoomEffects(long t) {
std::list<BaseRoom*>::iterator it;
for(it = effectsIndex.begin() ; it != effectsIndex.end() ; ) {
(*it)->pulseEffects(t);
if(!(*it)->needsEffectsIndex())
it = effectsIndex.erase(it);
else
it++;
}
lastRoomPulseUpdate = t;
}
//*********************************************************************
// showEffectsIndex
//*********************************************************************
void Server::showEffectsIndex(const Player* player) {
std::list<BaseRoom*>::const_iterator it;
int i=0;
player->printColor("^YRoom Effects Index\n");
for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) {
player->print("%s\n", (*it)->fullName().c_str());
i++;
}
player->print("%d room%s in effects index.\n", i, i==1 ? "" : "s");
}
//*********************************************************************
// dmShowEffectsIndex
//*********************************************************************
int dmShowEffectsIndex(Player* player, cmd* cmnd) {
gServer->showEffectsIndex(player);
return(0);
}
//*********************************************************************
// addEffectsIndex
//*********************************************************************
void BaseRoom::addEffectsIndex() {
if(!needsEffectsIndex())
return;
gServer->addEffectsIndex(this);
}
void Server::addEffectsIndex(BaseRoom* room) {
if(!this)
return;
// you can only be in the list once!
std::list<BaseRoom*>::const_iterator it;
for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) {
if((*it) == room)
return;
}
effectsIndex.push_back(room);
}
//*********************************************************************
// needsEffectsIndex
//*********************************************************************
bool BaseRoom::needsEffectsIndex() const {
// any room effects?
if(effects.effectList.size())
return(true);
// any exit effects?
for(Exit* exit : exits) {
if(exit->effects.effectList.size())
return(true);
}
return(false);
}
//*********************************************************************
// removeEffectsIndex
//*********************************************************************
bool BaseRoom::removeEffectsIndex() {
if(needsEffectsIndex())
return(false);
gServer->removeEffectsIndex(this);
return(true);
}
void Server::removeEffectsIndex(BaseRoom* room) {
if(!this)
return;
std::list<BaseRoom*>::iterator it;
for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) {
if((*it) == room) {
effectsIndex.erase(it);
return;
}
}
}
//*********************************************************************
// removeEffectsOwner
//*********************************************************************
// on suicide, we remove the owner of the effect
void Server::removeEffectsOwner(const Creature* owner) {
std::list<BaseRoom*>::iterator it;
for(it = effectsIndex.begin() ; it != effectsIndex.end() ; it++) {
(*it)->effects.removeOwner(owner);
for(Exit* exit : (*it)->exits) {
exit->effects.removeOwner(owner);
}
}
}
//*********************************************************************
// Effect
//*********************************************************************
Effect::Effect(xmlNodePtr rootNode) {
xmlNodePtr curNode = rootNode->children;
pulsed = isSpellEffect = usesStr = false;
pulseDelay = 5;
while(curNode) {
if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
else if(NODE_NAME(curNode, "BaseEffect")) baseEffects.push_back(xml::getBString(curNode));
else if(NODE_NAME(curNode, "Display")) xml::copyToBString(display, curNode);
else if(NODE_NAME(curNode, "OppositeEffect")) xml::copyToBString(oppositeEffect, curNode);
else if(NODE_NAME(curNode, "SelfAddStr")) xml::copyToBString(selfAddStr, curNode);
else if(NODE_NAME(curNode, "SelfDelStr")) xml::copyToBString(selfDelStr, curNode);
else if(NODE_NAME(curNode, "RoomAddStr")) xml::copyToBString(roomAddStr, curNode);
else if(NODE_NAME(curNode, "RoomDelStr")) xml::copyToBString(roomDelStr, curNode);
else if(NODE_NAME(curNode, "Pulsed")) xml::copyToBool(pulsed, curNode);
else if(NODE_NAME(curNode, "PulseDelay")) xml::copyToNum(pulseDelay, curNode);
else if(NODE_NAME(curNode, "Type")) xml::copyToBString(type, curNode);
else if(NODE_NAME(curNode, "ComputeScript")) xml::copyToBString(computeScript, curNode);
else if(NODE_NAME(curNode, "ApplyScript")) xml::copyToBString(applyScript, curNode);
else if(NODE_NAME(curNode, "PreApplyScript")) xml::copyToBString(preApplyScript, curNode);
else if(NODE_NAME(curNode, "PostApplyScript")) xml::copyToBString(postApplyScript, curNode);
else if(NODE_NAME(curNode, "UnApplyScript")) xml::copyToBString(unApplyScript, curNode);
else if(NODE_NAME(curNode, "PulseScript")) xml::copyToBString(pulseScript, curNode);
else if(NODE_NAME(curNode, "Spell")) xml::copyToBool(isSpellEffect, curNode);
else if(NODE_NAME(curNode, "UsesStrength")) xml::copyToBool(usesStr, curNode);
curNode = curNode->next;
}
}
//*********************************************************************
// getPulseScript
//*********************************************************************
bstring Effect::getPulseScript() const {
return(pulseScript);
}
//*********************************************************************
// getUnApplyScript
//*********************************************************************
bstring Effect::getUnApplyScript() const {
return(unApplyScript);
}
//*********************************************************************
// getApplyScript
//*********************************************************************
bstring Effect::getApplyScript() const {
return(applyScript);
}
//*********************************************************************
// getPreApplyScript
//*********************************************************************
bstring Effect::getPreApplyScript() const {
return(preApplyScript);
}
//*********************************************************************
// getPostApplyScript
//*********************************************************************
bstring Effect::getPostApplyScript() const {
return(postApplyScript);
}
//*********************************************************************
// getComputeScript
//*********************************************************************
bstring Effect::getComputeScript() const {
return(computeScript);
}
//*********************************************************************
// getType
//*********************************************************************
bstring Effect::getType() const {
return(type);
}
//*********************************************************************
// isPulsed
//*********************************************************************
bool Effect::isPulsed() const {
return(pulsed);
}
//*********************************************************************
// isSpell
//*********************************************************************
bool Effect::isSpell() const {
return(isSpellEffect);
}
//*********************************************************************
// isSpell
//*********************************************************************
bool Effect::usesStrength() const {
return(usesStr);
}
//*********************************************************************
// getRoomDelStr
//*********************************************************************
bstring Effect::getRoomDelStr() const {
return(roomDelStr);
}
//*********************************************************************
// getRoomAddStr
//*********************************************************************
bstring Effect::getRoomAddStr() const {
return(roomAddStr);
}
//*********************************************************************
// getSelfDelStr
//*********************************************************************
bstring Effect::getSelfDelStr() const {
return(selfDelStr);
}
//*********************************************************************
// getSelfAddStr
//*********************************************************************
bstring Effect::getSelfAddStr() const {
return(selfAddStr);
}
//*********************************************************************
// getOppositeEffect
//*********************************************************************
bstring Effect::getOppositeEffect() const {
return(oppositeEffect);
}
//*********************************************************************
// getDisplay
//*********************************************************************
bstring Effect::getDisplay() const {
return(display);
}
//*********************************************************************
// hasBaseEffect
//*********************************************************************
bool EffectInfo::hasBaseEffect(const bstring& effect) const {
return(myEffect->hasBaseEffect(effect));
}
//*********************************************************************
// hasBaseEffect
//*********************************************************************
bool Effect::hasBaseEffect(const bstring& effect) const {
for(const bstring& be : baseEffects) {
if(be == effect)
return(true);
}
return(false);
}
//*********************************************************************
// getName
//*********************************************************************
bstring Effect::getName() const {
return(name);
}
//*********************************************************************
// EffectInfo
//*********************************************************************
// TODO: Add applier here
EffectInfo::EffectInfo(bstring pName, time_t pLastMod, long pDuration, int pStrength, MudObject* pParent, const Creature* owner):
name(pName), lastMod(pLastMod), lastPulse(pLastMod), duration(pDuration), strength(pStrength), myParent(pParent)
{
myEffect = gConfig->getEffect(pName);
if(!myEffect)
throw bstring("Can't find effect " + pName);
setOwner(owner);
}
//*********************************************************************
// EffectInfo
//*********************************************************************
EffectInfo::EffectInfo()
{
}
//*********************************************************************
// EffectInfo
//*********************************************************************
EffectInfo::EffectInfo(xmlNodePtr rootNode) {
xmlNodePtr curNode = rootNode->children;
while(curNode) {
if(NODE_NAME(curNode, "Name")) xml::copyToBString(name, curNode);
else if(NODE_NAME(curNode, "Duration")) xml::copyToNum(duration, curNode);
else if(NODE_NAME(curNode, "Strength")) xml::copyToNum(strength, curNode);
else if(NODE_NAME(curNode, "Extra")) xml::copyToNum(extra, curNode);
else if(NODE_NAME(curNode, "PulseModifier")) xml::copyToNum(pulseModifier, curNode);
curNode = curNode->next;
}
lastPulse = lastMod = time(0);
myEffect = gConfig->getEffect(name);
if(!myEffect) {
throw bstring("Can't find effect listing " + name);
}
}
//*********************************************************************
// EffectInfo
//*********************************************************************
EffectInfo::~EffectInfo() {
}
//*********************************************************************
// setParent
//*********************************************************************
void EffectInfo::setParent(MudObject* pParent) {
myParent = pParent;
}
//*********************************************************************
// getEffect
//*********************************************************************
Effect* EffectInfo::getEffect() const {
return(myEffect);
}
//*********************************************************************
// getName
//*********************************************************************
const bstring EffectInfo::getName() const {
return(name);
}
//*********************************************************************
// getOwner
//*********************************************************************
const bstring EffectInfo::getOwner() const {
return(pOwner);
}
//*********************************************************************
// isOwner
//*********************************************************************
bool EffectInfo::isOwner(const Creature* owner) const {
// currently, only players can own effects
return(owner && owner->isPlayer() && pOwner == owner->getName());
}
//*********************************************************************
// getLastMod
//*********************************************************************
time_t EffectInfo::getLastMod() const {
return(lastMod);
}
//*********************************************************************
// getDuration
//*********************************************************************
long EffectInfo::getDuration() const {
return(duration);
}
//*********************************************************************
// getStrength
//*********************************************************************
int EffectInfo::getStrength() const {
return(strength);
}
//*********************************************************************
// getExtra
//*********************************************************************
int EffectInfo::getExtra() const {
return(extra);
}
//*********************************************************************
// isPermanent
//*********************************************************************
bool EffectInfo::isPermanent() const {
return(duration == -1);
}
//*********************************************************************
// getParent
//*********************************************************************
MudObject* EffectInfo::getParent() const {
return(myParent);
}
//*********************************************************************
// getApplier
//*********************************************************************
MudObject* EffectInfo::getApplier() const {
return(myApplier);
}
//*********************************************************************
// setOwner
//*********************************************************************
void EffectInfo::setOwner(const Creature* owner) {
if(owner)
pOwner = owner->getName();
else
pOwner = "";
}
//*********************************************************************
// setStrength
//*********************************************************************
void EffectInfo::setStrength(int pStrength) {
if(!this)
return;
strength = pStrength;
}
//*********************************************************************
// setExtra
//*********************************************************************
void EffectInfo::setExtra(int pExtra) {
if(!this)
return;
extra = pExtra;
}
//*********************************************************************
// setDuration
//*********************************************************************
void EffectInfo::setDuration(long pDuration) {
if(!this)
return;
duration = pDuration;
}