roh/conf.old/area/
roh/config/code/python/
roh/config/game/area/
roh/config/game/signs/
roh/help/dmhelp/
roh/help/help/
roh/log/
roh/log/staff/
roh/monsters/ocean/
roh/objects/misc/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.47e/
/*
 * stat.cpp
 *	 Stat code.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___ 
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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"



//#####################################################################
// Stat Modifier
//#####################################################################
StatModifier::StatModifier() {
    name = "none";
    modAmt = 0;
    modType = MOD_NONE;
}

StatModifier::StatModifier(bstring pName, int pModAmt, ModifierType pModType) {
	name = pName;
	modAmt = pModAmt;
	modType = pModType;
}

void StatModifier::adjust(int adjAmount) {
	modAmt += adjAmount;
}

void StatModifier::set(int newAmt) {
    modAmt = newAmt;
}
void StatModifier::setType(ModifierType newType) {
	modType = newType;
}

bstring StatModifier::getName() {
	return(name);
}
int StatModifier::getModAmt() {
	return(modAmt);
}
ModifierType StatModifier:: getModType() {
	return(modType);
}

double getConBonusPercentage(int pCon) {
//    const double a = 0.000002102555823;
//    const double b = 0.001366762953;
//    const double c = 0.982621217;
	const double a = 0.000007672564844;
	const double b = -0.0004485369366;
	const double c = 0.9939294404;
    const int x = pCon;
    double percentage = ((a*x*x)+(b*x)+c);
    percentage = tMAX<double>(1.0, percentage)-1.0;
    return(percentage);

}

double getIntBonusPercentage(int pInt) {

	const double a =  0.000004207194636;
    const double b = -0.0004146634469;
    const double c =  1.00300606;

    const int x = pInt;
    double percentage = ((a*x*x)+(b*x)+c);
    percentage = tMAX<double>(1.0, percentage)-1.0;
    return(percentage);

}

void Stat::reCalc() {
	if(!dirty)
		return;

	if(influencedBy) {
		if(name.equals("Hp"))
			setModifier("ConBonus", 0, MOD_MAX);
		else if(name.equals("Mp"))
			setModifier("IntBonus", 0, MOD_MAX);
	}

	cur = initial;
	max = initial;

	for(ModifierMap::value_type p : modifiers) {
		StatModifier *mod = p.second;
		if(!mod)
			continue;
		switch(mod->getModType()) {
		case MOD_MAX:
			max += mod->getModAmt();
			break;
		case MOD_CUR:
			cur += mod->getModAmt();
			break;
		case MOD_CUR_MAX:
			max += mod->getModAmt();
			cur += mod->getModAmt();
			break;
		default:
			break;
		}
	}


    if(influencedBy && name.equals("Hp")) {
    	double percentage = getConBonusPercentage(influencedBy->getCur());
    	int rounding = this->getModifierAmt("Rounding");
        int conBonus = (max-rounding) * percentage;
        setModifier("ConBonus", conBonus, MOD_MAX);

        max += conBonus;
    }
    if(influencedBy && name.equals("Mp")) {
    	double percentage = getIntBonusPercentage(influencedBy->getCur());
    	int rounding = this->getModifierAmt("Rounding");
        int intBonus = (max-rounding) * percentage;
        setModifier("IntBonus", intBonus, MOD_MAX);

        max += intBonus;
    }

    if(cur > max) {
    	adjustModifier("CurModifier", max - cur);
    	cur -= (cur - max);
    }
	dirty = false;
}
StatModifier* Stat::getModifier(bstring name) {
	ModifierMap::iterator it = modifiers.find(name);
	if(it == modifiers.end())
		return(NULL);
	else
		return(it->second);
}
int Stat::getModifierAmt(bstring name) {
	StatModifier* mod = getModifier(name);
	if(mod)
		return(mod->getModAmt());
	else
		return(0);
}
Stat* Creature::getStat(bstring statName) {
    if(statName == "strength") {
        return(&strength);
    } else if(statName == "dexterity") {
        return(&dexterity);
    } else if(statName == "constitution") {
        return(&constitution);
    } else if(statName == "intelligence") {
        return(&intelligence);
    } else if(statName == "piety") {
        return(&piety);
    } else if(statName == "hp") {
        return(&hp);
    } else if(statName == "mp") {
        return(&mp);
    } else if(statName == "focus" && isPlayer()) {
        return(&getAsPlayer()->focus);
    } else {
        return(NULL);
    }

}

bool Creature::addStatModifier(bstring statName, bstring modifierName, int modAmt, ModifierType modType) {
    return(addStatModifier(statName, new StatModifier(modifierName, modAmt, modType)));
}
bool Creature::addStatModifier(bstring statName, StatModifier* statModifier) {
    Stat* stat = getStat(statName);

    if(!stat) {
        delete(statModifier);
        return(false);
    }

    stat->addModifier(statModifier);
    if(statName.equals("constitution"))
        hp.setDirty();
    else if(statName.equals("intelligence"))
        mp.setDirty();

    return(true);
}
bool Creature::setStatDirty(bstring statName) {
    Stat* stat = getStat(statName);

    if(!stat)
        return(false);

    stat->setDirty();
    return(true);
}

bool Stat::addModifier(StatModifier* toAdd) {
	if(!toAdd)
		return(false);

	if(getModifier(toAdd->getName()) != NULL) {
	    std::cout << "Not adding modifer " << toAdd->getName() << std::endl;
		delete toAdd;
		return(false);
	}
	modifiers.insert(ModifierMap::value_type(toAdd->getName(), toAdd));
	setDirty();
	return(true);
}
void Stat::setDirty() {
    dirty = true;
    if(influences)
        influences->setDirty();
}
bool Stat::addModifier(bstring name, int modAmt, ModifierType modType) {
	if(getModifier(name) != NULL)
		return(false);
	return(addModifier(new StatModifier(name, modAmt, modType)));
}

bool Stat::removeModifier(bstring name) {
	ModifierMap::iterator it = modifiers.find(name);
	if(it == modifiers.end())
		return(false);

	modifiers.erase(it);
	setDirty();
	return(true);
}
void Stat::clearModifiers() {
    ModifierMap::iterator it = modifiers.begin();

    while(it != modifiers.end()) {
		delete it->second;
		modifiers.erase(it++);
    }
}
bool Stat::adjustModifier(bstring name, int modAmt, ModifierType modType) {
	StatModifier* mod = getModifier(name);
	if(!mod) {
		if(modAmt == 0)
			return(true);
		mod = new StatModifier(name, 0, modType);
		modifiers.insert(ModifierMap::value_type(name, mod));
	}
	mod->adjust(modAmt);

	if(mod->getModAmt() == 0) {
		return(removeModifier(name));
	}
	else
		mod->setType(modType);
	setDirty();
	return(true);
}

bool Stat::setModifier(bstring name, int newAmt, ModifierType modType) {
    StatModifier* mod = getModifier(name);
    if(newAmt == 0) {
    	if(!mod) {
    		return(true);
    	}
    	else {
    		return(removeModifier(name));
    	}
    }
    if(!mod) {
        mod = new StatModifier(name, 0, modType);
        modifiers.insert(ModifierMap::value_type(name, mod));
    }
    mod->set(newAmt);
    mod->setType(modType);
    setDirty();
    return(true);

}

//*********************************************************************
//						Stat
//*********************************************************************
Stat::Stat() {
	 cur = max = initial = 0;
	 dirty = true;
	 influences = influencedBy = 0;
}

StatModifier::StatModifier(StatModifier &sm) {
    name = sm.name;
    modAmt = sm.modAmt;
    modType = sm.modType;
}

Stat& Stat::operator=(const Stat& st) {
    doCopy(st);
    return(*this);
}
void Stat::doCopy(const Stat& st) {
    StatModifier* mod = 0;
    for(ModifierMap::value_type p : st.modifiers) {
        mod = new StatModifier();
        (*mod) = (*p.second);
        modifiers.insert(ModifierMap::value_type(mod->getName(), mod));
    }
    name = st.name;
    cur = st.cur;
    max = st.max;
    initial = st.initial;
    dirty = st.dirty;
    influences = 0;
    influencedBy = 0;
}

Stat::~Stat() {
    for(ModifierMap::value_type p : modifiers) {
        delete p.second;
    }
    modifiers.clear();
}

void Stat::setName(bstring pName) {
    name = pName;
}

//*********************************************************************
//						adjust
//*********************************************************************

int Stat::adjust(int amt) {
	if(amt > 0)
		return(increase(amt));
	else
		return(-1 * decrease(amt*-1));
}

//*********************************************************************
//						increase
//*********************************************************************

// Legacy for hp.increase()
int Stat::increase(int amt) {
	if(amt < 0)
		return(0);
		
	
	int increaseAmt = MAX(0, MIN(amt, getMax() - getCur()));
		
	adjustModifier("CurModifier", increaseAmt);
	
	return(increaseAmt);
}

//*********************************************************************
//						decrease
//*********************************************************************

// Legacy for hp.decrease()
int Stat::decrease(int amt) {
	if(amt < 0)
		return(0);
	
	int decreaseAmt = MIN(amt, getCur());
	
	adjustModifier("CurModifier", -decreaseAmt);
	
	return(decreaseAmt);
}

//*********************************************************************
//						getCur
//*********************************************************************

int Stat::getCur(bool recalc) {
	if(recalc)
		reCalc();
	return(cur);
}

//*********************************************************************
//						getMax
//*********************************************************************

int Stat::getMax() {
	reCalc();
    return(max);
}

//*********************************************************************
//						getInitial
//*********************************************************************

int Stat::getInitial() const { return(initial); }

//*********************************************************************
//						addInitial
//*********************************************************************

void Stat::addInitial(int a) { initial = MAX(1, initial + a); setDirty(); }

//*********************************************************************
//						setMax
//*********************************************************************
double round(double r) {
    return (r > 0.0) ? floor(r + 0.5) : ceil(r - 0.5);
}

void Stat::setMax(int newMax, bool allowZero) {
	newMax = MAX(allowZero ? 0 : 1, MIN(newMax, 30000));

	int dmSet = getModifierAmt("DmSet");
	int rounding = getModifierAmt("Rounding");
	int adjustment = 0;
	bool setHp = false, setMp = false;
	if(name.equals("Hp"))
		setHp = true;
	else if(name.equals("Mp"))
		setMp = true;

	if((setHp || setMp) && influencedBy) {
		double percentage =  0;
		int bonus = 0;
		if(setHp) {
			bonus = getModifierAmt("ConBonus");
			percentage = getConBonusPercentage(influencedBy->getCur());
		}
		if(setMp) {
			bonus = getModifierAmt("IntBonus");
			percentage = getIntBonusPercentage(influencedBy->getCur());
		}

		// Target max value we want
		double targetMax = newMax;

		// Target max value we want before any bonus
		double target = targetMax / (1.0+percentage);
		// Current amount, less bonus and any set amounts
		int curMax = getMax() - bonus - dmSet - rounding;
		// Adjustment needed to get to the target value before bonus
		adjustment = round(target) - curMax;

		// Calculated max based on new adjustment, without any rounding modifier
		int adjMax = (curMax + adjustment) * (1.0+percentage);

		this->setModifier("DmSet", adjustment, MOD_MAX);

		// Due to rounding with doubles we might miss the target by 1, this will adjust it
		if(adjMax != newMax) {
			setModifier("Rounding", newMax - adjMax, MOD_MAX);
		} else {
			setModifier("Rounding", 0, MOD_MAX);
		}
	} else {
		int curMax = getMax() - dmSet;
		adjustment = newMax - curMax;
		this->setModifier("DmSet", adjustment, MOD_MAX);
	}


}

//*********************************************************************
//						setCur
//*********************************************************************

void Stat::setCur(int newCur) {
	newCur = MIN(newCur, getMax());
	int modCur = newCur - getCur();
	adjustModifier("CurModifier", modCur);
}

void Stat::setInfluences(Stat* pInfluences) {
    influences = pInfluences;
}
void Stat::setInfluencedBy(Stat* pInfluencedBy) {
    influencedBy = pInfluencedBy;
}

std::ostream& operator<<(std::ostream& out, Stat& stat) {
    out << stat.toString();
    return(out);
}

bstring Stat::toString() {
    std::ostringstream oStr;

    oStr << "^C" << name << ": ^c" << getCur() << "/" << getMax() << "(" << getInitial() << ")\n";
    int i = 1;
    for(ModifierMap::value_type p : modifiers) {
        StatModifier* mod = p.second;
        oStr << "\t" << i++ << ") ";
        oStr << "^C" << mod->getName() << "^c ";
        switch(mod->getModType()) {
            case MOD_CUR:
                oStr << "CUR ";
                break;
            case MOD_MAX:
                oStr << "MAX ";
                break;
            case MOD_CUR_MAX:
                oStr << "CUR+MAX ";
                break;
            default:
                oStr << "UNKNOWN ";
                break;
        }
        if(mod->getModAmt() >= 0)
            oStr << "+";
        oStr << mod->getModAmt() << "\n";
    }
    return(oStr.str());
}

//*********************************************************************
//						setInitial
//*********************************************************************

void Stat::setInitial(int i) { initial = i; setDirty(); }

//*********************************************************************
//						restore
//*********************************************************************

int Stat::restore() {
	if(cur < max)
		setCur(max);
	return(cur);
}

//*********************************************************************
//						modifyStatTotalByEffect
//*********************************************************************

int modifyStatTotalByEffect(const Player* player, const bstring& effect) {
	const EffectInfo* ef = player->getEffect(effect);
	if(ef)
		return(ef->getStrength());
	return(0);
}

//*********************************************************************
//						statsAddUp
//*********************************************************************

bool Player::statsAddUp() const {
	if(isStaff())
		return(true);
	if(flagIsSet(P_PTESTER))
		return(true);

	return(true);
}

//*********************************************************************
//						addStatModEffect
//*********************************************************************

bool Creature::addStatModEffect(EffectInfo* effect) {
	Stat* stat=0;
	Player* pThis = getAsPlayer();
	bool good;
	ModifierType modType = MOD_CUR_MAX;

	if(effect->getName() == "strength") {
		if(pThis) {
			if(isEffected("berserk"))
			    removeEffect("berserk");
			if(isEffected("dkpray"))
				removeEffect("dkpray");
		}
		good = true;
		stat = &strength;
	} else if(effect->getName() == "enfeeblement") {
		good = false;
		stat = &strength;
	} else if(effect->getName() == "haste") {
		if(pThis && isEffected("frenzy"))
			removeEffect("frenzy");
		good = true;
		stat = &dexterity;
	} else if(effect->getName() == "slow") {
		good = false;
		stat = &dexterity;
	} else if(effect->getName() == "insight") {
		if(pThis && isEffected("confusion"))
			pThis->removeEffect("confusion");
		good = true;
		stat = &intelligence;
	} else if(effect->getName() == "feeblemind") {
		good = false;
		stat = &intelligence;
	} else if(effect->getName() == "prayer") {
		if(isEffected("pray"))
		    removeEffect("pray");
		good = true;
		stat = &piety;
	} else if(effect->getName() == "damnation") {
		good = false;
		stat = &piety;
	} else if(effect->getName() == "fortitude") {
		good = true;
		stat = &constitution;
	} else if(effect->getName() == "weakness") {
		good = false;
		stat = &constitution;
	} else if(effect->getName() == "berserk") {
	    good = true;
	    stat = &strength;
	} else if(effect->getName() == "frenzy") {
	    good = true;
	    stat = &dexterity;
	} else if(effect->getName() == "pray") {
	    good = true;
        stat = &piety;
	} else if(effect->getName() == "dkpray") {
	    good = true;
	    stat = &strength;
	} else if(effect->getName() == "bloodsac") {
	    modType = MOD_MAX;
	    good = true;
	    stat = &hp;
	}

	else {
		print("Unknown stat effect: %s\n", effect->getName().c_str());
		return(false);
	}

	int addAmt = effect->getStrength();
	if(good == false && addAmt > 0)
		addAmt *= -1;

	// Can't go outside the range
	int statMax = MAX_STAT_NUM;
	int statMin = MIN_STAT_NUM;

	// Different limits for hp & mp
	if(stat == &hp || stat == &mp) {
		statMax = 30000;
		statMin = 1;
	}
	addAmt = tMIN(addAmt, statMax - stat->getCur());
	addAmt = tMAX(addAmt, statMin - stat->getCur());
	
	effect->setStrength(addAmt);
	stat->addModifier(effect->getName(), addAmt, modType);

	if(pThis) {
		pThis->computeAttackPower();
		pThis->computeAC();
	}
	return(true);
}

//*********************************************************************
//						remStatModEffect
//*********************************************************************

bool Creature::remStatModEffect(EffectInfo* effect) {
	Stat* stat=0;
	Player* pThis = getAsPlayer();

	if(effect->getName() == "strength" || effect->getName() == "enfeeblement") {
		stat = &strength;
	} else if(effect->getName() == "haste" || effect->getName() == "slow") {
		stat = &dexterity;
	} else if(effect->getName() == "insight" || effect->getName() == "feeblemind") {
		stat = &intelligence;
	} else if(effect->getName() == "prayer" || effect->getName() == "damnation") {
		stat = &piety;
	} else if(effect->getName() == "fortitude" || effect->getName() == "weakness") {
		stat = &constitution;
	} else if(effect->getName() == "berserk") {
        stat = &strength;
    } else if(effect->getName() == "frenzy") {
        stat = &dexterity;
    } else if(effect->getName() == "pray") {
        stat = &piety;
    } else if(effect->getName() == "dkpray") {
        stat = &strength;
    } else if(effect->getName() == "bloodsac") {
        stat = &hp;
    }
	else {
		print("Unknown stat effect: %s\n", effect->getName().c_str());
		return(false);
	}

	stat->removeModifier(effect->getName());


	if(pThis) {
		pThis->computeAttackPower();
		pThis->computeAC();
		pThis->setFlag(P_JUST_STAT_MOD);
	}
	return(true);
}

//*********************************************************************
//						upgradeStats
//*********************************************************************

// Only used for upgradeStats
void Stat::upgradeSetCur(int newCur) {
	cur = newCur;
}

// Note: Used for upgradeStats
void checkEffect(Creature* creature, const bstring& effName, int& stat, bool positive)  {
	EffectInfo* eff = creature->getEffect(effName);
	if(eff) {
		int str = eff->getStrength();
		if(!positive)
			str *= -1;
		stat += str;
		creature->removeEffect(eff, false);
	}
}

void Player::recordLevelInfo() {
	std::cout << "Recording level info for " << getName() << std::endl;

	statistics.startLevelHistoryTracking();

	PlayerClass *pClass = gConfig->classes[getClassString()];
	LevelGain *lGain = 0;
	int hpAmt = 0;
	for(int l = level ; l > 1 ; l--) {
		lGain = pClass->getLevelGain(l);
		if(!lGain)
			continue;

		hpAmt = lGain->getHp();

		// Track level history
		statistics.setLevelInfo(l, new LevelInfo(l, lGain->getHp(), lGain->getMp(), lGain->getStat(), lGain->getSave(), time(0)));

	}
}
void Player::upgradeStats() {
	std::cout << "Upgrading stats for " << getName() << std::endl;
	*this << "Upgrading your stats to the new format.\n";

	loseRage();
	loseFrenzy();
	losePray();

	int cStr = strength.getCur(false);
	int cDex = dexterity.getCur(false);
	int cCon = constitution.getCur(false);
	int cInt = intelligence.getCur(false);
	int cPie = piety.getCur(false);

	checkEffect(this, "strength", cStr, true);
	checkEffect(this, "enfeeblement", cStr, false);
	checkEffect(this, "haste", cDex, true);
	checkEffect(this, "slow", cDex, false);

	checkEffect(this, "fortitude", cCon, true);
	checkEffect(this, "weakness", cCon, false);

	checkEffect(this, "insight", cInt, true);
	checkEffect(this, "feeblemind", cInt, false);

	checkEffect(this, "prayer", cPie, true);
	checkEffect(this, "damnation", cPie, false);

	PlayerClass *pClass = gConfig->classes[getClassString()];
	LevelGain *lGain = 0;

	int hpAmt = pClass->getBaseHp();

	hp.setInitial(hpAmt);
	mp.setInitial(pClass->getBaseMp());



	for(int l = level ; l > 1 ; l--) {
		lGain = pClass->getLevelGain(l);
		if(!lGain)
			continue;
		bstring modName = bstring("Level") + l;
		hpAmt = lGain->getHp();
		hp.addModifier(modName, hpAmt, MOD_CUR_MAX );

		if(cClass != BERSERKER && cClass != LICH) {
			mp.addModifier(modName, lGain->getMp(), MOD_CUR_MAX );
		}
		int switchNum = lGain->getStat();

		StatModifier* newMod = new StatModifier(modName, 10, MOD_CUR_MAX);
		switch(switchNum) {
		case STR:
			strength.addModifier(newMod);
			cStr -= 10;
			break;
		case DEX:
			dexterity.addModifier(newMod);
			cDex -= 10;
			break;
		case CON:
			constitution.addModifier(newMod);
			cCon -= 10;
			break;
		case INT:
			intelligence.addModifier(newMod);
			cInt -= 10;
			break;
		case PTY:
			piety.addModifier(newMod);
			cPie -= 10;
			break;
		}

	}

    if(cClass == FIGHTER && !cClass2 && flagIsSet(P_PTESTER)) {
        focus.setInitial(100);
        focus.clearModifiers();
        focus.addModifier("UnFocused", -100, MOD_CUR);
        mp.setInitial(0);
        mp.clearModifiers();

    }
	strength.setInitial(cStr);
	dexterity.setInitial(cDex);
	constitution.setInitial(cCon);
	intelligence.setInitial(cInt);
	piety.setInitial(cPie);

//
//	std::cout << "Str: O: " << cStr << " N: " << strength.getCur() << "\n";
//	std::cout << "Dex: O: " << cDex << " N: " << dexterity.getCur() << "\n";
//	std::cout << "Con: O: " << cCon << " N: " << constitution.getCur() << "\n";
//	std::cout << "Int: O: " << cInt << " N: " << intelligence.getCur() << "\n";
//	std::cout << "Pie: O: " << cPie << " N: " << piety.getCur() << "\n";

}


void Monster::upgradeStats() {
	std::cout << "Upgrading stats for " << getName() << std::endl;
	*this << "Upgrading your stats to the new format.\n";

	int cStr = strength.getCur(false);
	int cDex = dexterity.getCur(false);
	int cCon = constitution.getCur(false);
	int cInt = intelligence.getCur(false);
	int cPie = piety.getCur(false);

	int cHp = hp.getCur(false);
	int cMp = mp.getCur(false);

	checkEffect(this, "strength", cStr, true);
	checkEffect(this, "enfeeblement", cStr, false);
	checkEffect(this, "haste", cDex, true);
	checkEffect(this, "slow", cDex, false);

	checkEffect(this, "fortitude", cCon, true);
	checkEffect(this, "weakness", cCon, false);

	checkEffect(this, "insight", cInt, true);
	checkEffect(this, "feeblemind", cInt, false);

	checkEffect(this, "prayer", cPie, true);
	checkEffect(this, "damnation", cPie, false);

	hp.setInitial(cHp);
	mp.setInitial(cMp);

	strength.setInitial(cStr);
	dexterity.setInitial(cDex);
	constitution.setInitial(cCon);
	intelligence.setInitial(cInt);
	piety.setInitial(cPie);

}