/*
* factions.cpp
* Faction 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-2009 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 "factions.h"
#include "commands.h"
#include <iomanip>
//*********************************************************************
// FactionRegard
//*********************************************************************
FactionRegard::FactionRegard() {
memset(classRegard, 0, sizeof(classRegard));
memset(raceRegard, 0, sizeof(raceRegard));
memset(deityRegard, 0, sizeof(deityRegard));
}
void FactionRegard::load(xmlNodePtr rootNode) {
xmlNodePtr curNode = rootNode->children;
bstring temp = "";
while(curNode) {
if(NODE_NAME(curNode, "Class")) {
xml::copyPropToBString(temp, curNode, "Name");
xml::copyToNum(classRegard[gConfig->classtoNum(temp)], curNode);
}
else if(NODE_NAME(curNode, "Race")) {
xml::copyPropToBString(temp, curNode, "Name");
xml::copyToNum(raceRegard[gConfig->racetoNum(temp)], curNode);
}
else if(NODE_NAME(curNode, "Deity")) {
xml::copyPropToBString(temp, curNode, "Name");
xml::copyToNum(deityRegard[gConfig->deitytoNum(temp)], curNode);
}
curNode = curNode->next;
}
}
long FactionRegard::getClassRegard(int i) const {
return(classRegard[i]);
}
long FactionRegard::getRaceRegard(int i) const {
return(raceRegard[i]);
}
long FactionRegard::getDeityRegard(int i) const {
return(deityRegard[i]);
}
//*********************************************************************
// Faction
//*********************************************************************
Faction::Faction() {
name = displayName = social = "";
baseRegard = 0;
}
void Faction::load(xmlNodePtr rootNode) {
xmlNodePtr curNode = rootNode->children;
while(curNode) {
if(NODE_NAME(curNode, "Name")) { xml::copyToBString(name, curNode); }
else if(NODE_NAME(curNode, "DisplayName")) { xml::copyToBString(displayName, curNode); }
else if(NODE_NAME(curNode, "Social")) { xml::copyToBString(social, curNode); }
else if(NODE_NAME(curNode, "BaseRegard")) { xml::copyToNum( baseRegard, curNode); }
else if(NODE_NAME(curNode, "InitialRegard")) initial.load(curNode);
else if(NODE_NAME(curNode, "MaxRegard")) max.load(curNode);
else if(NODE_NAME(curNode, "MinRegard")) min.load(curNode);
curNode = curNode->next;
}
}
bstring Faction::getName() const {
return(name);
}
bstring Faction::getDisplayName() const {
return(displayName);
}
bstring Faction::getSocial() const {
return(social);
}
long Faction::getBaseRegard() const {
return(baseRegard);
}
long Faction::getClassRegard(int i) const {
return(initial.getClassRegard(i));
}
long Faction::getRaceRegard(int i) const {
return(initial.getRaceRegard(i));
}
long Faction::getDeityRegard(int i) const {
return(initial.getDeityRegard(i));
}
const FactionRegard* Faction::getInitial() {
return(&initial);
}
const FactionRegard* Faction::getMax() {
return(&max);
}
const FactionRegard* Faction::getMin() {
return(&min);
}
//*********************************************************************
// getInitialRegard
//*********************************************************************
long Faction::getInitialRegard(const Player* player) const {
long regard = baseRegard;
regard += initial.getClassRegard(player->getClass());
// illusioned race affects faction
regard += initial.getRaceRegard(player->getDisplayRace());
if(player->getDeity())
regard += initial.getDeityRegard(player->getDeity());
return(regard);
}
//*********************************************************************
// alwaysHates
//*********************************************************************
bool Faction::alwaysHates(Player* player) const {
if(initial.getClassRegard(player->getClass()) == ALWAYS_HATE)
return(true);
if(initial.getRaceRegard(player->getRace()) == ALWAYS_HATE)
return(true);
if(player->getDeity() && initial.getDeityRegard(player->getDeity()) == ALWAYS_HATE)
return(true);
return(false);
}
//*********************************************************************
// getUpperLimit
//*********************************************************************
long Faction::getUpperLimit(Player* player) const {
long limit = MAX, x;
x = max.getClassRegard(player->getClass());
if(x)
limit = MIN(x, limit);
x = max.getRaceRegard(player->getRace());
if(x)
limit = MIN(x, limit);
if(player->getDeity()) {
x = max.getDeityRegard(player->getDeity());
if(x)
limit = MIN(x, limit);
}
return(limit- getInitialRegard(player));
}
//*********************************************************************
// getLowerLimit
//*********************************************************************
long Faction::getLowerLimit(Player* player) const {
long limit = MIN, x;
x = min.getClassRegard(player->getClass());
if(x)
limit = MAX(x, limit);
x = min.getRaceRegard(player->getRace());
if(x)
limit = MAX(x, limit);
if(player->getDeity()) {
x = min.getDeityRegard(player->getDeity());
if(x)
limit = MAX(x, limit);
}
return(limit - getInitialRegard(player));
}
//*********************************************************************
// getFaction
//*********************************************************************
const Faction *Config::getFaction(bstring factionStr) const {
if(factionStr == "")
return(0);
std::map<bstring, Faction*>::const_iterator it = factions.find(factionStr);
if(it != factions.end())
return((*it).second);
return(0);
}
//*********************************************************************
// findAggro
//*********************************************************************
Player* Faction::findAggro(BaseRoom *room) {
return(0);
}
//*********************************************************************
// showRegard
//*********************************************************************
void showRegard(const Player* viewer, bstring type, const FactionRegard* regard) {
int a;
std::ostringstream oStr;
for(a=0; a<CLASS_COUNT; a++) {
if(regard->getClassRegard(a) != 0) {
oStr << getClassAbbrev(a) << ":"
<< Faction::getColor(regard->getClassRegard(a))
<< regard->getClassRegard(a) << "^X ";
}
}
for(a=0; a < RACE_COUNT; a++) {
if(regard->getRaceRegard(a) != 0) {
oStr << gConfig->getRace(a)->getAbbr() << ":"
<< Faction::getColor(regard->getRaceRegard(a))
<< regard->getRaceRegard(a) << "^X ";
}
}
for(a=0; a < DEITY_COUNT; a++) {
if(regard->getDeityRegard(a) != 0) {
oStr << gConfig->getDeity(a)->getName() << ":"
<< Faction::getColor(regard->getDeityRegard(a))
<< regard->getDeityRegard(a) << "^X ";
}
}
if(oStr.str() != "")
viewer->printColor(" ^y%s:^x %s\n", type.c_str(), oStr.str().c_str());
}
//*********************************************************************
// listFactions
//*********************************************************************
int listFactions(const Player* viewer, bool all) {
if(!all)
viewer->printColor("Type ^y*faction all^x to view all details.\n");
viewer->printColor("^xFactions\n%-20s - %40s\n^b---------------------------------------------------------------\n", "Name", "DisplayName");
std::map<bstring, Faction*>::iterator fIt;
Faction* faction=0;
for(fIt = gConfig->factions.begin() ; fIt != gConfig->factions.end() ; fIt++) {
bstring tmp;
faction = (*fIt).second;
viewer->print("%-20s - %40s\n", faction->getName().c_str(), faction->getDisplayName().c_str());
if(!all)
continue;
viewer->printColor(" Base Regard: %s%6d\n", (faction->getBaseRegard() >= 0 ? "^g" : "^R"), faction->getBaseRegard());
showRegard(viewer, "Initial Regard", faction->getInitial());
showRegard(viewer, "Max Regard", faction->getMax());
showRegard(viewer, "Min Regard", faction->getMin());
viewer->print("\n");
}
return(0);
}
int listFactions(const Player* viewer, const Creature* target) {
std::map<bstring, long>::const_iterator fIt;
std::ostringstream oStr;
const Faction* faction=0;
const Player* player = target->getConstPlayer();
long regard=0;
long i=0;
// Set left aligned
oStr.setf(std::ios::left, std::ios::adjustfield);
oStr.imbue(std::locale(""));
if(viewer == target)
viewer->print("Faction Standing");
else {
viewer->print("%s's Faction", target->name);
const Monster* monster = target->getConstMonster();
if(monster)
viewer->printColor(", primeFaction: ^y%s", monster->getPrimeFaction().c_str());
}
viewer->printColor("\n^b----------------------------------------------------------------------\n");
for(fIt = target->factions.begin() ; fIt != target->factions.end() ; fIt++) {
faction = gConfig->getFaction((*fIt).first);
regard = (*fIt).second;
if(!faction)
continue;
// if showing a mob, show straight faction
// if showing a player, show adjusted faction
if(player)
regard += faction->getInitialRegard(player);
oStr << std::setw(35) << faction->getDisplayName() << Faction::getColor(regard)
<< std::setw(13) << Faction::getNoun(regard) << "^x";
// this doesnt make sense for mobs
if(player)
oStr << Faction::getBar(regard, true);
if(viewer != target || target->isStaff()) {
oStr << " ";
if(player && i)
oStr << "(Adj:" << regard << " Unadj:" << (regard - i) << ")";
else
oStr << "(" << regard << ")";
}
oStr << "\n";
}
viewer->printColor("%s\n", oStr.str().c_str());
return(0);
}
//*********************************************************************
// dmShowFactions
//*********************************************************************
int dmShowFactions(Player *player, cmd* cmnd) {
bool all = !strcmp(cmnd->str[1], "all");
if(cmnd->num < 2 || all)
listFactions(player, all);
else {
if(player->getClass() == BUILDER) {
if(!player->canBuildMonsters())
return(cmdNoAuth(player));
if(!player->checkBuilder(player->parent_rom)) {
player->print("Error: room number not in any of your alotted ranges.\n");
return(0);
}
}
Creature* target=0;
Monster *mTarget=0;
target = player->getRoom()->findCreature(player, cmnd);
if(player->isCt()) {
cmnd->str[1][0] = up(cmnd->str[1][0]);
if(!target)
target = gServer->findPlayer(cmnd->str[1]);
}
if(target) {
if(player->getClass() == BUILDER) {
mTarget = target->getMonster();
if(mTarget && !player->checkBuilder(mTarget->info)) {
player->print("Error: monster index not in any of your alotted ranges.\n");
return(0);
} else if(!mTarget) {
player->print("Can't find '%s'\n", cmnd->str[1]);
return(0);
}
}
listFactions(player, target);
} else
player->print("Can't find '%s'\n", cmnd->str[1]);
}
return(0);
}
//*********************************************************************
// cmdFactions
//*********************************************************************
int cmdFactions(Player* player, cmd* cmnd) {
if(cmnd->num >= 2 && player->isStaff())
return(dmShowFactions(player, cmnd));
return(listFactions(player, player));
}
//*********************************************************************
// getCutoff
//*********************************************************************
int Faction::getCutoff(int attitude) {
switch(attitude) {
case WORSHIP:
return(WORSHIP_CUTOFF);
case REGARD:
return(REGARD_CUTOFF);
case ADMIRATION:
return(ADMIRATION_CUTOFF);
case FAVORABLE:
return(FAVORABLE_CUTOFF);
case INDIFFERENT:
return(INDIFFFERENT_CUTOFF);
case DISAPPROVE:
return(DISAPPROVE_CUTOFF);
case DISFAVOR:
return(DISFAVOR_CUTTOFF);
case CONTEMPT:
return(CONTEMPT_CUTOFF);
default:
return(0);
}
}
//*********************************************************************
// getAttitude
//*********************************************************************
int Faction::getAttitude(int regard) {
if(regard >= WORSHIP_CUTOFF)
return(WORSHIP);
else if(regard >= REGARD_CUTOFF)
return(REGARD);
else if(regard >= ADMIRATION_CUTOFF)
return(ADMIRATION);
else if(regard >= FAVORABLE_CUTOFF)
return(FAVORABLE);
else if(regard >= INDIFFFERENT_CUTOFF)
return(INDIFFERENT);
else if(regard >= DISAPPROVE_CUTOFF)
return(DISAPPROVE);
else if(regard >= DISFAVOR_CUTTOFF)
return(DISFAVOR);
else if(regard >= CONTEMPT_CUTOFF)
return(CONTEMPT);
else
return(MALICE);
}
//*********************************************************************
// getBar
//*********************************************************************
bstring Faction::getBar(int regard, bool alwaysPad) {
int attitude = getAttitude(regard);
if(attitude == WORSHIP || attitude == MALICE) {
if(alwaysPad)
return(" ");
return("");
}
int low = getCutoff(attitude);
int num = (regard - low) * 10 / (getCutoff(attitude+1) - low);
std::ostringstream oStr;
oStr << "[^m";
for(low = 0; low<num; low++)
oStr << "x";
for(; low<10; low++)
oStr << " ";
oStr << "^x]";
return(oStr.str());
}
//*********************************************************************
// getColor
//*********************************************************************
bstring Faction::getColor(int regard) {
if(regard >= FAVORABLE_CUTOFF)
return("^g");
else if(regard <= DISAPPROVE_CUTOFF)
return("^R");
return("");
}
//*********************************************************************
// getNoun
//*********************************************************************
bstring Faction::getNoun(int regard) {
switch(getAttitude(regard)) {
case WORSHIP:
return("worshipped");
case REGARD:
return("high regard");
case ADMIRATION:
return("admiration");
case FAVORABLE:
return("favorable");
case INDIFFERENT:
return("indifferent");
case DISAPPROVE:
return("disapproving");
case DISFAVOR:
return("unfavorable");
case CONTEMPT:
return("contempt");
case MALICE:
return("malice");
default:
return("confusion");
}
}
//*********************************************************************
// getFactionMessage
//*********************************************************************
// Returns the faction standing of the current creature with regards to 'faction'
bstring Player::getFactionMessage(bstring factionStr) const {
int regard = getFactionStanding(factionStr);
std::ostringstream oStr;
switch(Faction::getAttitude(regard)) {
case Faction::WORSHIP:
oStr << "worships you";
break;
case Faction::REGARD:
oStr << "holds you in the highest regard";
break;
case Faction::ADMIRATION:
oStr << "looks upon with you admiration";
break;
case Faction::FAVORABLE:
oStr << "looks upon you favorably";
break;
case Faction::INDIFFERENT:
oStr << "regards you indifferently";
break;
case Faction::DISAPPROVE:
oStr << "disapproves of your presence";
break;
case Faction::DISFAVOR:
oStr << "looks upon you unfavorably";
break;
case Faction::CONTEMPT:
oStr << "looks upon you with contempt";
break;
case Faction::MALICE:
oStr << "regards you with malice";
break;
default:
oStr << "stares at you with confusion";
break;
}
if(isDm())
oStr << " (" << factionStr << ":" << Faction::getColor(regard) << regard << "^x)";
return(oStr.str());
}
//*********************************************************************
// getFactionStanding
//*********************************************************************
// Gets the faction standing of Creature with regards to 'faction'
int Player::getFactionStanding(bstring factionStr) const {
int regard = 0;
std::map<bstring, long>::const_iterator it = factions.find(factionStr);
if(it != factions.end())
regard += (*it).second;
const Faction* faction = gConfig->getFaction(factionStr);
if(faction)
regard += faction->getInitialRegard(this);
return(regard);
}
//*********************************************************************
// adjustFactionStanding
//*********************************************************************
// Adjust the killer's faction standing for killing the victim
void Player::adjustFactionStanding(const std::map<bstring, long>& factionList) {
std::map<bstring, long>::const_iterator fIt;
std::ostringstream oStr;
const Faction *faction=0;
long regard=0, current=0, limit=0;
for(fIt = factionList.begin() ; fIt != factionList.end() ; fIt++) {
faction = gConfig->getFaction((*fIt).first);
regard = (*fIt).second;
if(!regard || !faction)
continue;
current = factions[faction->getName()];
// this is where adjusted faction comes into play;
// prevent them from going out-of-bounds
limit = faction->getUpperLimit(this);
if(current - regard > limit)
regard = current - limit;
limit = faction->getLowerLimit(this);
if(current - regard < limit)
regard = current - limit;
// it was adjusted to 0, meaning we can't move any more
if(!regard)
continue;
if(faction->alwaysHates(this) && regard <= 0)
continue;
oStr << (regard > 0 ? "^R" : "^c")
<< "Your faction standing with " << faction->getDisplayName()
<< (regard > 0 ? " has gotten worse." : " has improved.");
if(isDm())
oStr << " (" << abs(regard) << ")";
oStr << "\n";
factions[faction->getName()] -= regard;
}
printColor("%s", oStr.str().c_str());
}
//*********************************************************************
// worshipSocial
//*********************************************************************
void Faction::worshipSocial(Monster *monster) {
if(monster->getPrimeFaction() == "")
return;
// only certain monster types perform this action
if(!monType::isIntelligent(monster->getType()) && !monster->getRace())
return;
if(monster->inCombat())
return;
const Faction *faction = gConfig->getFaction(monster->getPrimeFaction());
if(!faction)
return;
bstring social = faction->getSocial();
if(social == "")
return;
// we've made sure everything is ok; let's continue
// with the actual work
ctag *cp = monster->getRoom()->first_ply;
Player* player;
cmd cmnd;
strcpy(cmnd.str[0], social.c_str());
cmnd.num = 2;
cmnd.val[1] = 1;
while(cp) {
player = cp->crt->getPlayer();
cp = cp->next_tag;
if( mrand(1,100)>2 ||
!player ||
player->flagIsSet(P_UNCONSCIOUS) ||
!monster->canSee(player) ||
monster->isEnmCrt(player->name) ||
player->isEffected("petrification") ||
player->inCombat() ||
player->isStaff()
)
continue;
if(getAttitude(player->getFactionStanding(monster->getPrimeFaction())) < WORSHIP)
continue;
strcpy(cmnd.str[1], player->name);
cmdProcess(monster, &cmnd);
}
}
//*********************************************************************
// willAggro
//*********************************************************************
bool Faction::willAggro(const Player* player, bstring faction) {
if(faction == "")
return(false);
int attitude = getAttitude(player->getFactionStanding(faction));
if(attitude <= MALICE)
return(true);
if(attitude == CONTEMPT && mrand(1,100) <= 10)
return(true);
return(false);
}
//*********************************************************************
// willSpeakWith
//*********************************************************************
bool Faction::willSpeakWith(const Player* player, bstring faction) {
if(faction == "")
return(true);
return(getAttitude(player->getFactionStanding(faction)) > CONTEMPT);
}
//*********************************************************************
// willDoBusinessWith
//*********************************************************************
bool Faction::willDoBusinessWith(const Player* player, bstring faction) {
if(faction == "")
return(true);
return(getAttitude(player->getFactionStanding(faction)) > DISFAVOR);
}
//*********************************************************************
// willBeneCast
//*********************************************************************
bool Faction::willBeneCast(const Player* player, bstring faction) {
if(faction == "")
return(true);
return(getAttitude(player->getFactionStanding(faction)) >= INDIFFERENT);
}
//*********************************************************************
// adjustPrice
//*********************************************************************
Money Faction::adjustPrice(const Player* player, bstring faction, Money money, bool sell) {
if(faction == "")
return(money);
int attitude = getAttitude(player->getFactionStanding(faction));
if(attitude <= DISAPPROVE) {
if(sell)
money.set(money[GOLD] * 2, GOLD);
else
money.set(money[GOLD] / 2, GOLD);
} else if(attitude >= FAVORABLE && attitude < REGARD) {
if(sell)
money.sub(money[GOLD] / 20, GOLD);
else
money.add(money[GOLD] / 20, GOLD);
} else if(attitude >= REGARD) {
if(sell)
money.sub(money[GOLD] / 10, GOLD);
else
money.add(money[GOLD] / 10, GOLD);
}
return(money);
}
//*********************************************************************
// canPledgeTo
//*********************************************************************
bool Faction::canPledgeTo(const Player* player, bstring faction) {
if(faction == "")
return(true);
return(getAttitude(player->getFactionStanding(faction)) >= INDIFFERENT);
}
// Clears factions
void Config::clearFactionList() {
std::map<bstring, Faction*>::iterator fIt;
Faction* faction;
for(fIt = factions.begin() ; fIt != factions.end() ; fIt++) {
faction = (*fIt).second;
delete faction;
//factions.erase(fIt);
}
factions.clear();
}
bool Config::loadFactions() {
xmlDocPtr xmlDoc;
//GuildCreation * curCreation;
xmlNodePtr cur;
char filename[80];
// build an XML tree from the file
sprintf(filename, "%s/factions.xml", CONFPATH);
xmlDoc = xml::loadFile(filename, "Factions");
if(xmlDoc == NULL)
return(false);
cur = xmlDocGetRootElement(xmlDoc);
// First level we expect a faction
cur = cur->children;
while(cur && xmlIsBlankNode(cur))
cur = cur->next;
if(cur == 0) {
xmlFreeDoc(xmlDoc);
xmlCleanupParser();
return(false);
}
clearFactionList();
Faction* faction;
while(cur != NULL) {
if(NODE_NAME(cur, "Faction")) {
faction = new Faction;
faction->load(cur);
factions[faction->getName()] = faction;
}
cur = cur->next;
}
xmlFreeDoc(xmlDoc);
xmlCleanupParser();
return(true);
}