roh/conf/area/
roh/game/talk/
roh/help/
roh/monsters/ocean/
roh/objects/ocean/
roh/player/
roh/rooms/area/1/
roh/rooms/misc/
roh/rooms/ocean/
roh/src-2.44b/
/*
 * skills.cpp
 *	 Skill manipulation functions.
 *   ____            _
 *  |  _ \ ___  __ _| |_ __ ___  ___
 *  | |_) / _ \/ _` | | '_ ` _ \/ __|
 *  |  _ <  __/ (_| | | | | | | \__ \
 *  |_| \_\___|\__,_|_|_| |_| |_|___/
 *
 * 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 "skills.h"
#include "commands.h"
#include "clans.h"
#include <sstream>

#define NOT_A_SKILL -10

Skill::Skill() {
	gainType = SKILL_NORMAL;
	knownOnly = false;
}

int Skill::getGainType() const { return(gainType); }
int CrtSkill::getGainBonus() const { return(gainBonus); }
bool Skill::isKnownOnly() const { return(knownOnly); }
bool Config::isKnownOnly(const bstring& skillName) const {
	std::map<bstring, Skill*>::const_iterator it = skills.find(skillName);
	if(it != skills.end())
		return(((*it).second)->isKnownOnly());
	return(false);
}
//*****************************************************************
// CrtSkills - Keeps track of skill information for a Creature
//*****************************************************************

//--------------------------------------------------------------------
// Constructors
CrtSkill::CrtSkill() {
	name = "";
	gained = 0;
	gainBonus = 0;
}

CrtSkill::CrtSkill(const bstring& pName, int pGained) {
	name = pName;
	gained = pGained;
	gainBonus = 0;
}
// End constructors
//--------------------------------------------------------------------




//--------------------------------------------------------------------
// Get/Set Functions
bstring CrtSkill::getName() const { return(name); }
int CrtSkill::getGained() const { return(gained); }
int CrtSkill::getGainType() const {
	Skill *s = gConfig->getSkill(name);
	return(s ? s->getGainType() : NOT_A_SKILL);
}
bstring CrtSkill::getDisplayName() const { return(gConfig->getSkillDisplayName(name)); }
bstring CrtSkill::getGroup() const { return(gConfig->getSkillGroup(name)); }

void CrtSkill::setGained(int pGained) {
	gained = pGained;
}

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

// End Get/Set Functions
//--------------------------------------------------------------------


//--------------------------------------------------------------------
// Misc Functions
void CrtSkill::upBonus(int amt) {
	gainBonus += amt;
}
void CrtSkill::clearBonus() {
	gainBonus = 0;
}
void CrtSkill::improve(int amt) {
	gained += amt;
}
// End Misc Functions
//--------------------------------------------------------------------




//*****************************************************************
//						Skill
//*****************************************************************
// Class to store base information about skills

//********************************************************************
//						setGroup
//********************************************************************
// Sets group, aborts if not valid

bool Skill::setGroup(bstring &pGroup) {
	ASSERTLOG(pGroup != "");
	bstring groupName = gConfig->getSkillGroupDisplayName(pGroup);
	if(groupName == "") {
		return(false);
	}
	group = pGroup;
	return true;
}

bstring Skill::getName() const { return(name); }
bstring Skill::getGroup() const { return(group); }
bstring Skill::getDisplayName() const { return(displayName); }
bstring Skill::getDescription() const { return(description); }

//********************************************************************
//						checkImprove
//********************************************************************
//  Parameters: skillName - What skill are we checking for improvement
//				success - Was the skill sucessfull
//				attribute - What attribute will be helpful in raising the skill? (default: INT)
//				bns - Any bonus to the improve calculation (default: 0)

void Creature::checkImprove(const bstring& skillName, bool success, int attribute, int bns) {
	if(isMonster())
		return;
	if(inJail())
		return;

	CrtSkill* crSkill = getSkill(skillName);
	if(!crSkill)
		return;


	int gainType = crSkill->getGainType();
	// not a skill!
	if(gainType == NOT_A_SKILL) {
		broadcast(::isDm, "^y*** Skill \"%s\" was requested by the mud, but was not\n    found in the skill list. Check *skills to verify.", skillName.c_str());
		return;
	}
	long	j=0,t;

	t = time(0);
	j = LT(this, LT_SKILL_INCREASE);
	//
	if(t < j)
		return;

	int chance = 0;
	int gained = crSkill->getGained();

	chance = 100 - (gained/3);
	// Chance is too high right now, reduce it by half
	chance /= 2;

	if(gained < 100) {
		// You learn more from your failures at lower skill levels
		if(!success)
			chance *= 2;
	} else {
		// You learn less from failures at higher skill levels
		if(!success)
			chance /= 2;
	}

	if(gainType == SKILL_EASY) {
		// make it harder
		chance /= 2;
	} else if(gainType != SKILL_NORMAL) {
		chance += crSkill->getGainBonus();
	}

	// Unless max for level, 1% chance minimum
	chance = MAX(chance, 1);

	// 10 skill points per level possible, can't go over that
	if(gained >= (level*10))
		chance = 0;

    int roll = mrand(1, 100);
	if(roll <= chance) {
		bool af = gConfig->isAprilFools();
		if(success) {
			printColor("Your practice %spays off, you have become %s at ^W%s^x!\n",
				af ? "fails to " : "", af ? "worse" : "better",
				gConfig->getSkillDisplayName(skillName).c_str());
		} else {
			printColor("You %slearn from your mistakes, you have become %s at ^W%s^x!\n",
				af ? "fail to " : "", af ? "worse" : "better",
				gConfig->getSkillDisplayName(skillName).c_str());
		}
		if(isPlayer()) {
			lasttime[LT_SKILL_INCREASE].ltime = t;
			// Length of wait is based on skill level, not player level
			long wait = 10L * (gained/10);
			wait = MIN(wait, 150L);
			lasttime[LT_SKILL_INCREASE].interval = wait;
		}
		// TODO: Add a chance for a double improve for a hard skill
		
		crSkill->clearBonus();		
		crSkill->improve();
	} else {
		// See if we have a hard skill on our hands, if so add a bonus to increase
		if(gainType == SKILL_MEDIUM) {
			crSkill->upBonus();
		} else if(gainType == SKILL_HARD) {
			crSkill->upBonus(2);
		}
	}
}


//********************************************************************
//						knowsSkill
//********************************************************************

bool Creature::knowsSkill(const bstring& skillName) const {
	if(isMonster())	return(true);
	if(isCt())	  		return(true);
	if(skillName == "")	return(false);

	std::map<bstring, CrtSkill*>::const_iterator csIt;
	if( (csIt = skills.find(skillName)) == skills.end())
		return(false);
	else
		return(true);
}

//********************************************************************
//						getSkill
//********************************************************************
// Returns the requested skill if it can be found on the creature

CrtSkill* Creature::getSkill(const bstring& skillName) const {
	if(skillName == "")	return(false);

	std::map<bstring, CrtSkill*>::const_iterator csIt;
	if( (csIt = skills.find(skillName)) == skills.end())
		return(NULL);
	else
		return((*csIt).second);
}

//********************************************************************
//						addSkill
//********************************************************************
// Add a new skill of 'skillName' at 'gained' level

void Creature::addSkill(const bstring& skillName, int gained) {
	if(skillName == "")
		return;

	CrtSkill* skill = new CrtSkill(skillName, gained);
	skills[skillName] = skill;
}

//********************************************************************
//						remSkill
//********************************************************************

void Creature::remSkill(const bstring& skillName) {
	if(skillName == "")
		return;
	skills.erase(skillName);
}

#define SKILL_CHART_SIZE		21
char skillLevelStr[][SKILL_CHART_SIZE] =
{
	"^rHorrible^x",        // 0-24
	"^rPoor^x",            // 25-49
	"^rFair^x",            // 50-74
	"^mMediocre^x",        // 75-99
	"^mDecent^x",          // 100-124
	"^mBelow Average^x",   // 125-149
	"^wCompetent^x",       // 150-174
	"^wAverage^x",         // 175-199
	"^wAbove Average^x",   // 200-224
	"^cProficient^x",      // 225-249
	"^cSkilled^x",         // 250-274
	"^cTalented^x",        // 275-299
	"^bVery Good^x",       // 300-324
	"^bAdept^x",           // 325-349
	"^bSuperb^x",          // 350-374
	"^gExceptional^x",     // 375-399
	"^gExpert^x",          // 400-424
	"^gMaster^x",          // 425-449
	"^ySuperior Master^x", // 450-474
	"^yGrand Master^x",    // 474-499
	"^yGodlike^x"          // 500
};

char craftSkillLevelStr[][25] =
{
	"Novice",
	"Apprentice",
	"Journeyman",
	"Expert",
	"Artisian",
	"Master",
	"Grand Master"
};


//********************************************************************
//						showSkills
//********************************************************************
// Show the skills and skill levels of 'player'  to 'sock'

int showSkills(Player* toShow, Creature* player, bool magicOnly=false) {
	std::map<bstring, bstring>::iterator sgIt;
	std::map<bstring, CrtSkill*>::iterator sIt;
	CrtSkill* crtSkill=0;
	int known=0;
	double skill=0;
	const Clan *clan=0;

	bool showProgress = player->flagIsSet(P_SHOW_SKILL_PROGRESS);
	bool showDigits = !showProgress;

	int barLength = 20;

	if(player->getClan())
		clan = gConfig->getClan(player->getClan());


	// Tell the player what skills they are looking at
	if(toShow->getPlayer() == player)
		toShow->printColor("^YYour Skills:");
	else
		toShow->printColor("^Y%s's Skills:", player->name);

	if(magicOnly)
		toShow->printColor(" ^Ytype \"skills\" to show non-magical skills.");
	else if(player->getClass() != BERSERKER)
		toShow->printColor(" ^Ytype \"skills magic\" to show magical skills.");

	toShow->print("\n");



	for(sgIt = gConfig->skillGroups.begin() ; sgIt != gConfig->skillGroups.end() ; sgIt++) {
		if(	(*sgIt).first == "arcane" ||
			(*sgIt).first == "divine" ||
			(*sgIt).first == "magic"
		) {
			if(!magicOnly)
				continue;
		} else {
			if(magicOnly)
				continue;
		}

		std::ostringstream oStr;
		known = 0;
		oStr << "^W" << (*sgIt).second << "^x\n";
		for(sIt = player->skills.begin() ; sIt != player->skills.end() ; sIt++) {
			crtSkill = (*sIt).second;
			if(crtSkill->getGroup() == (*sgIt).first) {
				//1+player->saves[st].chance)/10
				known++;

				bool isCraft = crtSkill->getGroup() == "craft";


				int curSkill = 0;
				float maxSkill = 0;
				if(isCraft) {
					maxSkill = MIN(player->getLevel()*10, 100);
					curSkill = MIN(crtSkill->getGained(), (int)maxSkill);

				} else {
					maxSkill = MIN(player->getLevel()*10.0, MAXALVL*10.0);
					curSkill = MIN(crtSkill->getGained(), maxSkill);
				}

				skill = curSkill;
				if(clan)
					skill += clan->getSkillBonus(crtSkill->getName());
				skill /= 25;

				int displayNum = (int)skill;

				displayNum = MAX(0, MIN(SKILL_CHART_SIZE-1, displayNum));

				//bstring progressBar(int barLength, float percentFull, bstring text, char progressChar, bool enclosed)
				oStr << " ";
				if(showProgress) {
					oStr << " ";

					bstring progress = "" + bstring(curSkill) + "/" + bstring(maxSkill);
					if(gConfig->isKnownOnly(crtSkill->getName()))
						oStr << progressBar(barLength, 1);
					else
						oStr << progressBar(barLength, (1.0*curSkill)/maxSkill, progress.c_str());
				}

				oStr << " " << crtSkill->getDisplayName() << " - ";
				if(gConfig->isKnownOnly(crtSkill->getName())) {
					oStr << "^WKnown^x\n";
					continue;
				}
				if(!isCraft) {
					// Not a crafting skill
					oStr << skillLevelStr[displayNum];
				} else {
					// Crafting Skill
					int disp = (curSkill/maxSkill)*6;
					oStr << craftSkillLevelStr[disp];
				}
				if(showDigits )
					oStr << " (" << curSkill << ")";

				skill = 0;
				if(clan)
					skill = clan->getSkillBonus(crtSkill->getName());
				if((int)skill)
					oStr << " (Clan: " << skill << ")";
				if((*sIt).first == "defense" && player->isEffected("protection"))
					oStr << " (Protection: 10)";

				if(toShow->isCt() && !isCraft) {
//					oStr << " (" << crtSkill->getGained() << ")";
					oStr << " [" << player->getSkillLevel(crtSkill->getName()) << "]";
				}
				oStr << "\n";
			}
		}
		if(known)
			toShow->printColor("%s", oStr.str().c_str());
	}
	return(0);
}

//********************************************************************
//						getSkillLevel
//********************************************************************
// Return the player level equilvalent of the given skill

double Creature::getSkillLevel(const bstring& skillName) const {
	if(isMonster())
		return(level);

	CrtSkill* skill = getSkill(skillName);
	if(skill==NULL) {
		if(isCt())
			return(MAXALVL);
		else
			return(0);
	}
	int gained = getSkillGained(skillName);

	double level = 0.0;
	if(gained <= 100) {
		// If less than 100, adding 5 to help out lower levels
		level = ((double)gained + 5.0) / 10.0;
	} else {
		// If over 100, divide by 9.5 to provide more returns for higher levels
		level = (double)gained / 9.5;
	}
	if(clan) {
		const Clan* c = gConfig->getClan(clan);
		if(clan)
			level += c->getSkillBonus(skillName);
	}
	return(level);
}

//********************************************************************
//						getSkillGained
//********************************************************************

double Creature::getSkillGained(const bstring& skillName) const {
	CrtSkill* skill = getSkill(skillName);
	if(skill==NULL) {
		if(isCt())
			return(MAXALVL*10);
		else
			return(0);
	}
	double gained = (skill->getGained()*1.0);
	// Prevents a level 30 from deleveling to 25 and still fighting like a level 30
	if(gained/10 > level)
		gained = level*10.0;

	return(gained);
}

//********************************************************************
//						dmSkills
//********************************************************************
// List the skill table, or optionally a player's known skills

int dmSkills(Player* player, cmd* cmnd) {


	if(cmnd->num < 2) {
		std::map<bstring, bstring>::iterator sgIt;
		std::map<bstring, Skill*>::iterator sIt;
		player->printColor("^xSkill Groups\n%-20s - %40s\n---------------------------------------------------------------\n", "Name", "DisplayName");
		for(sgIt = gConfig->skillGroups.begin() ; sgIt != gConfig->skillGroups.end() ; sgIt++) {
			player->print("%-20s - %40s\n", (*sgIt).first.c_str(), (*sgIt).second.c_str());
		}

		player->printColor("\n^xSkills\n%-20s - %20s - %-15s\n-------------------------------------------------------------\n", "Name", "DisplayName", "Group");
		Skill* skill;
		bstring curGroup;
		for(sgIt = gConfig->skillGroups.begin() ; sgIt != gConfig->skillGroups.end() ; sgIt++) {
			curGroup = (*sgIt).first;
			for(sIt = gConfig->skills.begin() ; sIt != gConfig->skills.end() ; sIt++) {
				skill = (*sIt).second;
				if(skill->getGroup() == curGroup) {
					char color[3] = "";
					if(skill->getGainType() == SKILL_MEDIUM)
						strcpy(color, "^y");
					else if(skill->getGainType() == SKILL_HARD)
						strcpy(color, "^r");
					else if(skill->getGainType() == SKILL_EASY)
						strcpy(color, "^g");
					player->printColor("%s%-20s - %20s - %-15s\n",color,
							skill->getName().c_str(), skill->getDisplayName().c_str(), skill->getGroup().c_str());
				}
			}
		}
	} else {
		Creature* target=0;
		cmnd->str[1][0] = up(cmnd->str[1][0]);
		target = gServer->findPlayer(cmnd->str[1]);
		cmnd->str[1][0] = low(cmnd->str[1][0]);
		if(!target || (!player->isCt() && target->flagIsSet(P_DM_INVIS)))
			target = player->getRoom()->findCreature(player, cmnd);

		if(!target) {
			player->print("Target not found.\n");
			return(0);
		}
		player->print("Skills for: %s\n", target->name);
		showSkills(player, target, true);
	}
	return(0);
}

//********************************************************************
//						dmSetSkills
//********************************************************************

int dmSetSkills(Player *admin, cmd* cmnd) {


	if(cmnd->num < 2) {
		admin->print("Set skills for who?\n");
		return(0);
	}
	Player* target=0;
	cmnd->str[1][0] = up(cmnd->str[1][0]);
	target = gServer->findPlayer(cmnd->str[1]);
	if(!target) {
		admin->print("No player logged on with that name.\n");
		return(0);
	}
	PlayerClass *pClass = gConfig->classes[target->getClassString()];
	if(pClass) {
		target->checkSkillsGain(pClass->getSkillBegin(), pClass->getSkillEnd(), true);
		LevelGain *lGain;
		for(int level = 2 ; level <= target->getLevel() ; level++ ) {
			lGain = pClass->getLevelGain(level);
			if(!lGain) {
				admin->print("Error: Can't find any information for level %d!\n", level);
			} else {
				if(lGain->hasSkills()) {
					target->checkSkillsGain(lGain->getSkillBegin(), lGain->getSkillEnd(), true);
				}
			}
		}
		admin->print("%s's skills have been set for %s level.\n", target->name, target->hisHer());
	} else {
          admin->print("Unable to find class %s.\n", target->getClassString().c_str());
	}

	return(0);
}

//********************************************************************
//						cmdSkills
//********************************************************************
// Display all skills a player knows and their level

int cmdSkills(Player* player, cmd* cmnd) {
	Creature* target = player;
	bool magicOnly=false;
	int pos = 1;

	magicOnly = cmnd->str[1][0] && !strncmp(cmnd->str[1], "magic", strlen(cmnd->str[1]));

	if(player->isCt()) {
		if(magicOnly)
			pos = 2;
		if(cmnd->str[pos][0]) {
			cmnd->str[pos][0] = up(cmnd->str[pos][0]);
			target = gServer->findPlayer(cmnd->str[pos]);
			if(!target) {
				player->print("No player logged on with that name.\n");
				return(0);
			}
		}
	}

	showSkills(player, target, magicOnly);
	return(0);
}
// End Skill functions related to Creatures
//--------------------------------------------------------------------





struct {
    const char *songstr;
    int songno;
    int (*songfn)();
} songlist[] = {
    { "healing",            SONG_HEAL,              (int(*)())songHeal             	},
    { "magic",              SONG_MP,                (int(*)())songMPHeal           	},
    { "restoration",        SONG_RESTORE,           (int(*)())songRestore          	},
    { "destruction",        SONG_DESTRUCTION,       (int(*)())songOffensive        	},
    { "mass-destruction",   SONG_MASS_DESTRUCTION,  (int(*)())songMultiOffensive	},
    { "holiness",           SONG_BLESS,             (int(*)())songBless            	},
    { "protection",         SONG_PROTECTION,        (int(*)())songProtection       	},
    { "flight",             SONG_FLIGHT,            (int(*)())songFlight         	},
    { "recall",             SONG_RECALL,            (int(*)())songRecall         	},
    { "safety",             SONG_SAFETY,            (int(*)())songSafety         	},
    { "@",                  -1,                     0                       		}
};
int songlist_size = sizeof(songlist)/sizeof(*songlist);


//**********************************************************************
// get_song_name
//**********************************************************************
const char *get_song_name(int nIndex) {
    // do bounds checking
    ASSERTLOG(nIndex >= 0);
    ASSERTLOG(nIndex < songlist_size);

    nIndex = MAX(0, MIN(nIndex, songlist_size));

    return(songlist[nIndex].songstr);
}

//**********************************************************************
// get_song_num()
//**********************************************************************
int get_song_num(int nIndex) {
    // do bounds checking
    ASSERTLOG(nIndex >= 0);
    ASSERTLOG(nIndex < songlist_size);

    nIndex = MAX(0, MIN(nIndex, songlist_size));

    return(songlist[nIndex].songno);
}


//**********************************************************************
// get_song_function()
//**********************************************************************
SONGFN get_song_function(int nIndex) {
    // do bounds checking
    ASSERTLOG(nIndex >= 0);
    ASSERTLOG(nIndex < songlist_size);

    nIndex = MAX(0, MIN(nIndex, songlist_size));

    return(songlist[nIndex].songfn);
}

//**********************************************************************
//						getMaxSong
//**********************************************************************

int Config::getMaxSong() {
    return(songlist_size-1);
}


// Clears skills
void Config::clearSkills() {
	// Clear skill groups
	skillGroups.clear();

	// Clear & delete skills
	std::map<bstring, Skill*>::iterator sIt;
	Skill* skill;
	for(sIt = skills.begin() ; sIt != skills.end() ; sIt++) {
		skill = (*sIt).second;
		delete skill;
		//skills.erase(sIt);
	}
	skills.clear();
}


//********************************************************************
//						skillExists
//********************************************************************
// True if the skill exists

bool Config::skillExists(const bstring& skillName) const {
	std::map<bstring, Skill*>::const_iterator it = skills.find(skillName);
	return(it != skills.end());
}

//********************************************************************
//						getSkill
//********************************************************************
// Returns the given skill skill

Skill* Config::getSkill(const bstring& skillName) const {
	std::map<bstring, Skill*>::const_iterator it = skills.find(skillName);
	if(it != skills.end())
		return((*it).second);
	return(0);
}

//********************************************************************
//						getSkillDisplayName
//********************************************************************
// Get the display name of the skill

bstring Config::getSkillDisplayName(const bstring& skillName) const {
	std::map<bstring, Skill*>::const_iterator it = skills.find(skillName);
	if(it != skills.end())
		return(((*it).second)->getDisplayName());
	return("");
}

//********************************************************************
//						getSkillGroupDisplayName
//********************************************************************
// Get the group display name of the skill

bstring Config::getSkillGroupDisplayName(const bstring& groupName) const {
	std::map<bstring, bstring>::const_iterator it = skillGroups.find(groupName);
	if(it != skillGroups.end())
		return((*it).second);
	return("");
}

//********************************************************************
//						getSkillGroup
//********************************************************************
// Get the skill group of the skill

bstring Config::getSkillGroup(const bstring& skillName) const {
	std::map<bstring, Skill*>::const_iterator it = skills.find(skillName);
	if(it != skills.end())
		return(((*it).second)->getGroup());
	return("");
}

// End Skill Related Functions
//--------------------------------------------------------------------