• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

MineWorker Skill

bomba

Member
Joined
Feb 26, 2008
Messages
635
Reaction score
7
Location
Brazil
I can't use it:
doPlayerAddSkillTry(cid, SKILL_MINEWORKER, 1)

My "000-constant.LUA":
Code:
CONDITION_PARAM_OWNER = 1
CONDITION_PARAM_TICKS = 2
CONDITION_PARAM_OUTFIT = 3
CONDITION_PARAM_HEALTHGAIN = 4
CONDITION_PARAM_HEALTHTICKS = 5
CONDITION_PARAM_MANAGAIN = 6
CONDITION_PARAM_MANATICKS = 7
CONDITION_PARAM_DELAYED = 8
CONDITION_PARAM_SPEED = 9
CONDITION_PARAM_LIGHT_LEVEL = 10
CONDITION_PARAM_LIGHT_COLOR = 11
CONDITION_PARAM_SOULGAIN = 12
CONDITION_PARAM_SOULTICKS = 13
CONDITION_PARAM_MINVALUE = 14
CONDITION_PARAM_MAXVALUE = 15
CONDITION_PARAM_STARTVALUE = 16
CONDITION_PARAM_TICKINTERVAL = 17
CONDITION_PARAM_FORCEUPDATE = 18
CONDITION_PARAM_SKILL_MELEE = 19
CONDITION_PARAM_SKILL_FIST = 20
CONDITION_PARAM_SKILL_CLUB = 21
CONDITION_PARAM_SKILL_SWORD = 22
CONDITION_PARAM_SKILL_AXE = 23
CONDITION_PARAM_SKILL_DISTANCE = 24
CONDITION_PARAM_SKILL_SHIELD = 25
CONDITION_PARAM_SKILL_FISHING = 26
CONDITION_PARAM_STAT_MAXHEALTH = 27
CONDITION_PARAM_STAT_MAXMANA = 28
CONDITION_PARAM_STAT_SOUL = 29
CONDITION_PARAM_STAT_MAGICLEVEL = 30
CONDITION_PARAM_STAT_MAXHEALTHPERCENT = 31
CONDITION_PARAM_STAT_MAXMANAPERCENT = 32
CONDITION_PARAM_STAT_SOULPERCENT = 33
CONDITION_PARAM_STAT_MAGICLEVELPERCENT = 34
CONDITION_PARAM_SKILL_MELEEPERCENT = 35
CONDITION_PARAM_SKILL_FISTPERCENT = 36
CONDITION_PARAM_SKILL_CLUBPERCENT = 37
CONDITION_PARAM_SKILL_SWORDPERCENT = 38
CONDITION_PARAM_SKILL_AXEPERCENT = 39
CONDITION_PARAM_SKILL_DISTANCEPERCENT = 40
CONDITION_PARAM_SKILL_SHIELDPERCENT = 41
CONDITION_PARAM_SKILL_FISHINGPERCENT = 42
CONDITION_PARAM_PERIODICDAMAGE = 43
CONDITION_PARAM_BUFF = 44
CONDITION_PARAM_SUBID = 45
CONDITION_PARAM_SKILL_MINEWORKER = 46
CONDITION_PARAM_SKILL_MINEWORKERPERCENT = 47
Code:
SKILL_FIST = 0
SKILL_CLUB = 1
SKILL_SWORD = 2
SKILL_AXE = 3
SKILL_DISTANCE = 4
SKILL_SHIELD = 5
SKILL_FISHING = 6
SKILL_MINEWORKER = 7
SKILL__MAGLEVEL = 8
SKILL__LEVEL = 9

SKILL_NAMES = {
	[SKILL_FIST] = "fist fighting",
	[SKILL_CLUB] = "club fighting",
	[SKILL_SWORD] = "sword fighting",
	[SKILL_AXE] = "axe fighting",
	[SKILL_DISTANCE] = "distance fighting",
	[SKILL_SHIELD] = "shielding",
	[SKILL_FISHING] = "fishing",
	[SKILL_MINEWORKER] = "mineworker",
	[SKILL__LEVEL] = "level",
	[SKILL__MAGLEVEL] = "magic level"
}
Code:
SKILL_IDS = {
	["fist"] = SKILL_FIST,
	["club"] = SKILL_CLUB,
	["sword"] = SKILL_SWORD,
	["axe"] = SKILL_AXE,
	["distance"] = SKILL_DISTANCE,
	["dist"] = SKILL_DISTANCE,
	["shielding"] = SKILL_SHIELD,
	["shield"] = SKILL_SHIELD,
	["fishing"] = SKILL_FISHING,
	["fish"] = SKILL_FISHING,
	["mine"] = SKILL_MINEWORKER,
	["level"] = SKILL__LEVEL,
	["magic"] = SKILL__MAGLEVEL
}

Now the CPP codes:
 
condition.CPP:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"

#include "condition.h"
#include "tools.h"

#include "game.h"
#include "creature.h"
#include "combat.h"

extern Game g_game;

Condition::Condition(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
id(_id), subId(_subId), ticks(_ticks), endTime(0), conditionType(_type), buff(_buff)
{
	//
}

bool Condition::setParam(ConditionParam_t param, int32_t value)
{
	switch(param)
	{
		case CONDITIONPARAM_TICKS:
			ticks = value;
			return true;

		case CONDITIONPARAM_BUFF:
			buff = (value != 0);
			return true;

		case CONDITIONPARAM_SUBID:
			subId = value;
			return true;

		default:
			break;
	}

	return false;
}

bool Condition::unserialize(PropStream& propStream)
{
	uint8_t attrType;
	while(propStream.GET_UCHAR(attrType) && attrType != CONDITIONATTR_END)
	{
		if(!unserializeProp((ConditionAttr_t)attrType, propStream))
			return false;
	}

	return true;
}

bool Condition::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_TYPE:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			conditionType = (ConditionType_t)value;
			return true;
		}

		case CONDITIONATTR_ID:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			id = (ConditionId_t)value;
			return true;
		}

		case CONDITIONATTR_TICKS:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			ticks = value;
			return true;
		}

		case CONDITIONATTR_BUFF:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			buff = value != 0;
			return true;
		}

		case CONDITIONATTR_SUBID:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			subId = value;
			return true;
		}

		case CONDITIONATTR_END:
			return true;

		default:
			break;
	}

	return false;
}

bool Condition::serialize(PropWriteStream& propWriteStream)
{
	propWriteStream.ADD_UCHAR(CONDITIONATTR_TYPE);
	propWriteStream.ADD_VALUE((int32_t)conditionType);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_ID);
	propWriteStream.ADD_VALUE((int32_t)id);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_TICKS);
	propWriteStream.ADD_VALUE((int32_t)ticks);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_BUFF);
	propWriteStream.ADD_VALUE((int32_t)buff ? 1 : 0);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_SUBID);
	propWriteStream.ADD_VALUE((int32_t)subId);
	return true;
}

void Condition::setTicks(int32_t _ticks)
{
	ticks = _ticks;
	if(_ticks > 0)
		endTime = OTSYS_TIME() + _ticks;
}

bool Condition::startCondition(Creature* creature)
{
	if(ticks > 0)
		endTime = OTSYS_TIME() + ticks;

	return true;
}

bool Condition::executeCondition(Creature* creature, int32_t interval)
{
	if(interval > 0)
	{
		bool tmp = false;
		creature->onTickCondition(getType(), interval, tmp);
	}

	if(ticks == -1)
		return true;

	ticks = std::max((int32_t)0, (ticks - interval));
	return (endTime >= OTSYS_TIME());
}

Condition* Condition::createCondition(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, int32_t param/* = 0*/, bool _buff/* = false*/, uint32_t _subId/* = 0*/)
{
	switch((int32_t)_type)
	{
		case CONDITION_FIRE:
		case CONDITION_ENERGY:
		case CONDITION_POISON:
		case CONDITION_FREEZING:
		case CONDITION_DAZZLED:
		case CONDITION_CURSED:
		case CONDITION_DROWN:
		case CONDITION_PHYSICAL:
			return new ConditionDamage(_id, _type, _buff, _subId);

		case CONDITION_HASTE:
		case CONDITION_PARALYZE:
			return new ConditionSpeed(_id, _type, _ticks, _buff, _subId, param);

		case CONDITION_OUTFIT:
			return new ConditionOutfit(_id, _type, _ticks, _buff, _subId);

		case CONDITION_LIGHT:
			return new ConditionLight(_id, _type, _ticks, _buff, _subId, param & 0xFF, (param & 0xFF00) >> 8);

		case CONDITION_REGENERATION:
			return new ConditionRegeneration(_id, _type, _ticks, _buff, _subId);

		case CONDITION_SOUL:
			return new ConditionSoul(_id, _type, _ticks, _buff, _subId);

		case CONDITION_MANASHIELD:
			return new ConditionManaShield(_id, _type, _ticks, _buff, _subId);

		case CONDITION_ATTRIBUTES:
			return new ConditionAttributes(_id, _type, _ticks, _buff, _subId);

		case CONDITION_INVISIBLE:
		case CONDITION_HUNTING:
		case CONDITION_INFIGHT:
		case CONDITION_MUTED:
		case CONDITION_EXHAUST:
		case CONDITION_DRUNK:
		case CONDITION_PACIFIED:
		case CONDITION_GAMEMASTER:
			return new ConditionGeneric(_id, _type, _ticks, _buff, _subId);

		default:
			break;
	}

	return NULL;
}

Condition* Condition::createCondition(PropStream& propStream)
{
	uint8_t attr = 0;
	if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_TYPE)
		return NULL;

	uint32_t _type = 0;
	if(!propStream.GET_ULONG(_type))
		return NULL;

	if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_ID)
		return NULL;

	uint32_t _id = 0;
	if(!propStream.GET_ULONG(_id))
		return NULL;

	if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_TICKS)
		return NULL;

	uint32_t _ticks = 0;
	if(!propStream.GET_ULONG(_ticks))
		return NULL;

	if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_BUFF)
		return NULL;

	uint32_t _buff = 0;
	if(!propStream.GET_ULONG(_buff))
		return NULL;

	if(!propStream.GET_UCHAR(attr) || attr != CONDITIONATTR_SUBID)
		return NULL;

	uint32_t _subId = 0;
	if(!propStream.GET_ULONG(_subId))
		return NULL;

	return createCondition((ConditionId_t)_id, (ConditionType_t)_type, _ticks, 0, _buff != 0, _subId);
}

bool Condition::updateCondition(const Condition* addCondition)
{
	return conditionType == addCondition->getType() && (ticks != -1 || addCondition->getTicks() < 1)
		&& (addCondition->getTicks() < 0 || endTime <= (OTSYS_TIME() + addCondition->getTicks()));
}

Icons_t Condition::getIcons() const
{
	if(buff)
		return ICON_BUFF;

	return ICON_NONE;
}

ConditionGeneric::ConditionGeneric(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
Condition(_id, _type, _ticks, _buff, _subId)
{
	//
}

void ConditionGeneric::addCondition(Creature* creature, const Condition* addCondition)
{
	if(updateCondition(addCondition))
		setTicks(addCondition->getTicks());
}

Icons_t ConditionGeneric::getIcons() const
{
	Icons_t icon = Condition::getIcons();
	if(icon != ICON_NONE)
		return icon;

	switch(conditionType)
	{
		case CONDITION_INFIGHT:
			return ICON_SWORDS;

		case CONDITION_DRUNK:
			return ICON_DRUNK;

		default:
			break;
	}

	return ICON_NONE;
}

ConditionManaShield::ConditionManaShield(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
ConditionGeneric(_id, _type, _ticks, _buff, _subId)
{
	//
}

Icons_t ConditionManaShield::getIcons() const
{
	Icons_t icon = Condition::getIcons();
	if(icon != ICON_NONE)
		return icon;

	return ICON_MANASHIELD;
}

ConditionAttributes::ConditionAttributes(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
ConditionGeneric(_id, _type, _ticks, _buff, _subId)
{
	currentSkill = currentStat = 0;
	memset(skills, 0, sizeof(skills));
	memset(skillsPercent, 0, sizeof(skillsPercent));
	memset(stats, 0, sizeof(stats));
	memset(statsPercent, 0, sizeof(statsPercent));
}

void ConditionAttributes::addCondition(Creature* creature, const Condition* addCondition)
{
	if(!updateCondition(addCondition))
		return;

	setTicks(addCondition->getTicks());
	const ConditionAttributes& conditionAttrs = static_cast<const ConditionAttributes&>(*addCondition);
	endCondition(creature, CONDITIONEND_ABORT);

	//Apply the new one
	memcpy(skills, conditionAttrs.skills, sizeof(skills));
	memcpy(skillsPercent, conditionAttrs.skillsPercent, sizeof(skillsPercent));
	memcpy(stats, conditionAttrs.stats, sizeof(stats));
	memcpy(statsPercent, conditionAttrs.statsPercent, sizeof(statsPercent));
	if(Player* player = creature->getPlayer())
	{
		updatePercentSkills(player);
		updateSkills(player);
		updatePercentStats(player);
		updateStats(player);
	}
}

bool ConditionAttributes::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_SKILLS:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			skills[currentSkill++] = value;
			return true;
		}

		case CONDITIONATTR_STATS:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			stats[currentStat++] = value;
			return true;
		}

		default:
			break;
	}

	return ConditionGeneric::unserializeProp(attr, propStream);
}

bool ConditionAttributes::serialize(PropWriteStream& propWriteStream)
{
	if(!ConditionGeneric::serialize(propWriteStream))
		return false;

	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		propWriteStream.ADD_UCHAR(CONDITIONATTR_SKILLS);
		propWriteStream.ADD_VALUE(skills[i]);
	}

	for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i)
	{
		propWriteStream.ADD_UCHAR(CONDITIONATTR_STATS);
		propWriteStream.ADD_VALUE(stats[i]);
	}

	return true;
}

bool ConditionAttributes::startCondition(Creature* creature)
{
	if(Player* player = creature->getPlayer())
	{
		updatePercentSkills(player);
		updateSkills(player);
		updatePercentStats(player);
		updateStats(player);
	}

	return Condition::startCondition(creature);
}

void ConditionAttributes::updatePercentSkills(Player* player)
{
	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		if(skillsPercent[i])
			skills[i] += (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((skillsPercent[i] - 100) / 100.f));
	}
}

void ConditionAttributes::updatePercentStats(Player* player)
{
	for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i)
	{
		if(statsPercent[i])
			stats[i] += (int32_t)(player->getDefaultStats((stats_t)i)  * ((statsPercent[i] - 100) / 100.f));
	}
}

void ConditionAttributes::updateSkills(Player* player)
{
	bool needUpdateSkills = false;
	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		if(!skills[i])
			continue;

		player->setVarSkill((skills_t)i, skills[i]);
		if(!needUpdateSkills)
			needUpdateSkills = true;
	}

	if(needUpdateSkills)
		player->sendSkills();
}

void ConditionAttributes::updateStats(Player* player)
{
	bool needUpdateStats = false;
	for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i)
	{
		if(!stats[i])
			continue;

		player->setVarStats((stats_t)i, stats[i]);
		if(!needUpdateStats)
			needUpdateStats = true;
	}

	if(needUpdateStats)
		player->sendStats();
}

bool ConditionAttributes::executeCondition(Creature* creature, int32_t interval)
{
	return ConditionGeneric::executeCondition(creature, interval);
}

void ConditionAttributes::endCondition(Creature* creature, ConditionEnd_t reason)
{
	Player* player = creature->getPlayer();
	if(!player)
		return;

	bool needUpdateSkills = false;
	for(int32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		if(!skills[i])
			continue;

		needUpdateSkills = true;
		player->setVarSkill((skills_t)i, -skills[i]);
	}

	if(needUpdateSkills)
		player->sendSkills();

	bool needUpdateStats = false;
	for(int32_t i = STAT_FIRST; i <= STAT_LAST; ++i)
	{
		if(!stats[i])
			continue;

		needUpdateStats = true;
		player->setVarStats((stats_t)i, -stats[i]);
	}

	if(needUpdateStats)
		player->sendStats();
}

bool ConditionAttributes::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = ConditionGeneric::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_SKILL_MELEE:
			skills[SKILL_CLUB] = skills[SKILL_AXE] = skills[SKILL_SWORD] = value;
			return true;

		case CONDITIONPARAM_SKILL_FIST:
			skills[SKILL_FIST] = value;
			return true;

		case CONDITIONPARAM_SKILL_CLUB:
			skills[SKILL_CLUB] = value;
			return true;

		case CONDITIONPARAM_SKILL_SWORD:
			skills[SKILL_SWORD] = value;
			return true;

		case CONDITIONPARAM_SKILL_AXE:
			skills[SKILL_AXE] = value;
			return true;

		case CONDITIONPARAM_SKILL_DISTANCE:
			skills[SKILL_DIST] = value;
			return true;

		case CONDITIONPARAM_SKILL_SHIELD:
			skills[SKILL_SHIELD] = value;
			return true;

		case CONDITIONPARAM_SKILL_FISHING:
			skills[SKILL_FISH] = value;
			return true;

		case CONDITIONPARAM_STAT_MAXHEALTH:
			stats[STAT_MAXHEALTH] = value;
			return true;

		case CONDITIONPARAM_STAT_MAXMANA:
			stats[STAT_MAXMANA] = value;
			return true;

		case CONDITIONPARAM_STAT_SOUL:
			stats[STAT_SOUL] = value;
			return true;

		case CONDITIONPARAM_STAT_MAGICLEVEL:
			stats[STAT_MAGICLEVEL] = value;
			return true;

		case CONDITIONPARAM_STAT_MAXHEALTHPERCENT:
			statsPercent[STAT_MAXHEALTH] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_STAT_MAXMANAPERCENT:
			statsPercent[STAT_MAXMANA] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_STAT_SOULPERCENT:
			statsPercent[STAT_SOUL] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_STAT_MAGICLEVELPERCENT:
			statsPercent[STAT_MAGICLEVEL] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_MELEEPERCENT:
			skillsPercent[SKILL_CLUB] = skillsPercent[SKILL_AXE] = skillsPercent[SKILL_SWORD] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_FISTPERCENT:
			skillsPercent[SKILL_FIST] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_CLUBPERCENT:
			skillsPercent[SKILL_CLUB] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_SWORDPERCENT:
			skillsPercent[SKILL_SWORD] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_AXEPERCENT:
			skillsPercent[SKILL_AXE] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_DISTANCEPERCENT:
			skillsPercent[SKILL_DIST] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_SHIELDPERCENT:
			skillsPercent[SKILL_SHIELD] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_FISHINGPERCENT:
			skillsPercent[SKILL_FISH] = std::max((int32_t)0, value);
			return true;

		case CONDITIONPARAM_SKILL_MINEWORKER:
		     skills[SKILL_MINE] = value;
			return true;

		case CONDITIONPARAM_SKILL_MINEWORKERPERCENT:
			skillsPercent[SKILL_MINE] = std::max((int32_t)0, value);
			return true;
			
		default:
			break;
	}

	return ret;
}

ConditionRegeneration::ConditionRegeneration(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
ConditionGeneric(_id, _type, _ticks, _buff, _subId)
{
	internalHealthTicks = internalManaTicks = healthGain = manaGain = 0;
	healthTicks = manaTicks = 1000;
}

void ConditionRegeneration::addCondition(Creature* creature, const Condition* addCondition)
{
	if(!updateCondition(addCondition))
		return;

	setTicks(addCondition->getTicks());
	const ConditionRegeneration& conditionRegen = static_cast<const ConditionRegeneration&>(*addCondition);

	healthTicks = conditionRegen.healthTicks;
	manaTicks = conditionRegen.manaTicks;

	healthGain = conditionRegen.healthGain;
	manaGain = conditionRegen.manaGain;
}

bool ConditionRegeneration::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_HEALTHTICKS:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			healthTicks = value;
			return true;
		}

		case CONDITIONATTR_HEALTHGAIN:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			healthGain = value;
			return true;
		}

		case CONDITIONATTR_MANATICKS:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			manaTicks = value;
			return true;
		}

		case CONDITIONATTR_MANAGAIN:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			manaGain = value;
			return true;
		}

		default:
			break;
	}

	return ConditionGeneric::unserializeProp(attr, propStream);
}

bool ConditionRegeneration::serialize(PropWriteStream& propWriteStream)
{
	if(!ConditionGeneric::serialize(propWriteStream))
		return false;

	propWriteStream.ADD_UCHAR(CONDITIONATTR_HEALTHTICKS);
	propWriteStream.ADD_VALUE(healthTicks);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_HEALTHGAIN);
	propWriteStream.ADD_VALUE(healthGain);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_MANATICKS);
	propWriteStream.ADD_VALUE(manaTicks);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_MANAGAIN);
	propWriteStream.ADD_VALUE(manaGain);
	return true;
}

bool ConditionRegeneration::executeCondition(Creature* creature, int32_t interval)
{
	internalHealthTicks += interval;
	internalManaTicks += interval;
	if(creature->getZone() != ZONE_PROTECTION)
	{
		if(internalHealthTicks >= healthTicks)
		{
			internalHealthTicks = 0;
			creature->changeHealth(healthGain);
		}

		if(internalManaTicks >= manaTicks)
		{
			internalManaTicks = 0;
			creature->changeMana(manaGain);
		}
	}

	return ConditionGeneric::executeCondition(creature, interval);
}

bool ConditionRegeneration::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = ConditionGeneric::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_HEALTHGAIN:
			healthGain = value;
			return true;

		case CONDITIONPARAM_HEALTHTICKS:
			healthTicks = value;
			return true;

		case CONDITIONPARAM_MANAGAIN:
			manaGain = value;
			return true;

		case CONDITIONPARAM_MANATICKS:
			manaTicks = value;
			return true;

		default:
			break;
	}

	return ret;
}

ConditionSoul::ConditionSoul(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
ConditionGeneric(_id, _type, _ticks, _buff, _subId)
{
	internalSoulTicks = soulTicks = soulGain = 0;
}

void ConditionSoul::addCondition(Creature* creature, const Condition* addCondition)
{
	if(!updateCondition(addCondition))
		return;

	setTicks(addCondition->getTicks());
	const ConditionSoul& conditionSoul = static_cast<const ConditionSoul&>(*addCondition);

	soulTicks = conditionSoul.soulTicks;
	soulGain = conditionSoul.soulGain;
}

bool ConditionSoul::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_SOULGAIN:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			soulGain = value;
			return true;
		}

		case CONDITIONATTR_SOULTICKS:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			soulTicks = value;
			return true;
		}

		default:
			break;
	}

	return ConditionGeneric::unserializeProp(attr, propStream);
}

bool ConditionSoul::serialize(PropWriteStream& propWriteStream)
{
	if(!ConditionGeneric::serialize(propWriteStream))
		return false;

	propWriteStream.ADD_UCHAR(CONDITIONATTR_SOULGAIN);
	propWriteStream.ADD_VALUE(soulGain);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_SOULTICKS);
	propWriteStream.ADD_VALUE(soulTicks);

	return true;
}

bool ConditionSoul::executeCondition(Creature* creature, int32_t interval)
{
	internalSoulTicks += interval;
	if(Player* player = creature->getPlayer())
	{
		if(player->getZone() != ZONE_PROTECTION && internalSoulTicks >= soulTicks)
		{
			internalSoulTicks = 0;
			player->changeSoul(soulGain);
		}
	}

	return ConditionGeneric::executeCondition(creature, interval);
}

bool ConditionSoul::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = ConditionGeneric::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_SOULGAIN:
			soulGain = value;
			return true;

		case CONDITIONPARAM_SOULTICKS:
			soulTicks = value;
			return true;

		default:
			break;
	}

	return ret;
}

ConditionDamage::ConditionDamage(ConditionId_t _id, ConditionType_t _type, bool _buff, uint32_t _subId):
Condition(_id, _type, 0, _buff, _subId)
{
	tickInterval = 2000;
	delayed = forceUpdate = false;
	owner = minDamage = maxDamage = startDamage = periodDamage = periodDamageTick = 0;
}

bool ConditionDamage::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = Condition::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_OWNER:
			owner = value;
			return true;

		case CONDITIONPARAM_FORCEUPDATE:
			forceUpdate = value;
			return true;

		case CONDITIONPARAM_DELAYED:
			delayed = value;
			return true;

		case CONDITIONPARAM_MAXVALUE:
			maxDamage = std::abs(value);
			break;

		case CONDITIONPARAM_MINVALUE:
			minDamage = std::abs(value);
			break;

		case CONDITIONPARAM_STARTVALUE:
			startDamage = std::abs(value);
			break;

		case CONDITIONPARAM_TICKINTERVAL:
			tickInterval = std::abs(value);
			break;

		case CONDITIONPARAM_PERIODICDAMAGE:
			periodDamage = value;
			break;

		default:
			break;
	}

	return ret;
}

bool ConditionDamage::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_DELAYED:
		{
			bool value = false;
			if(!propStream.GET_VALUE(value))
				return false;

			delayed = value;
			return true;
		}

		case CONDITIONATTR_PERIODDAMAGE:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			periodDamage = value;
			return true;
		}

		case CONDITIONATTR_OWNER:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			owner = value;
			return true;
		}

		case CONDITIONATTR_INTERVALDATA:
		{
			IntervalInfo damageInfo;
			if(!propStream.GET_VALUE(damageInfo))
				return false;

			damageList.push_back(damageInfo);
			if(getTicks() != -1)
				setTicks(getTicks() + damageInfo.interval);

			return true;
		}

		default:
			break;
	}

	return Condition::unserializeProp(attr, propStream);
}

bool ConditionDamage::serialize(PropWriteStream& propWriteStream)
{
	if(!Condition::serialize(propWriteStream))
		return false;

	propWriteStream.ADD_UCHAR(CONDITIONATTR_DELAYED);
	propWriteStream.ADD_VALUE(delayed);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_PERIODDAMAGE);
	propWriteStream.ADD_VALUE(periodDamage);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_OWNER);
	propWriteStream.ADD_VALUE(owner);

	for(DamageList::const_iterator it = damageList.begin(); it != damageList.end(); ++it)
	{
		propWriteStream.ADD_UCHAR(CONDITIONATTR_INTERVALDATA);
		propWriteStream.ADD_VALUE(*it);
	}

	return true;
}

bool ConditionDamage::updateCondition(const ConditionDamage* addCondition)
{
	if(addCondition->doForceUpdate())
		return true;

	if(getTicks() == -1 && addCondition->getTicks() > 0)
		return false;

	if(addCondition->getTicks() <= getTicks())
		return false;

	if(addCondition->getTotalDamage() < getTotalDamage())
		return false;

	if(addCondition->periodDamage < periodDamage)
		return false;

	return true;
}

bool ConditionDamage::addDamage(int32_t rounds, int32_t time, int32_t value)
{
	if(rounds == -1) //periodic damage
	{
		setParam(CONDITIONPARAM_TICKINTERVAL, time);
		setParam(CONDITIONPARAM_TICKS, -1);

		periodDamage = value;
		return true;
	}

	if(periodDamage > 0)
		return false;

	//rounds, time, damage
	for(int32_t i = 0; i < rounds; ++i)
	{
		IntervalInfo damageInfo;
		damageInfo.interval = time;
		damageInfo.timeLeft = time;
		damageInfo.value = value;

		damageList.push_back(damageInfo);
		if(getTicks() != -1)
			setTicks(getTicks() + damageInfo.interval);
	}

	return true;
}

bool ConditionDamage::init()
{
	if(periodDamage)
		return true;

	if(!damageList.empty())
		return true;

	setTicks(0);
	int32_t amount = random_range(minDamage, maxDamage);
	if(!amount)
		return false;

	if(startDamage > maxDamage)
		startDamage = maxDamage;
	else if(!startDamage)
		startDamage = std::max((int32_t)1, (int32_t)std::ceil(((float)amount / 20.0)));

	std::list<int32_t> list;
	ConditionDamage::generateDamageList(amount, startDamage, list);
	for(std::list<int32_t>::iterator it = list.begin(); it != list.end(); ++it)
		addDamage(1, tickInterval, -(*it));

	return !damageList.empty();
}

bool ConditionDamage::startCondition(Creature* creature)
{
	if(!Condition::startCondition(creature) || !init())
		return false;

	if(delayed)
		return true;

	int32_t damage = 0;
	return !getNextDamage(damage) || doDamage(creature, damage);
}

bool ConditionDamage::executeCondition(Creature* creature, int32_t interval)
{
	if(periodDamage)
	{
		periodDamageTick += interval;
		if(periodDamageTick >= tickInterval)
		{
			periodDamageTick = 0;
			doDamage(creature, periodDamage);
		}
	}
	else if(!damageList.empty())
	{
		bool remove = getTicks() != -1;
		creature->onTickCondition(getType(), interval, remove);

		IntervalInfo& damageInfo = damageList.front();
		damageInfo.timeLeft -= interval;
		if(damageInfo.timeLeft <= 0)
		{
			int32_t damage = damageInfo.value;
			if(remove)
				damageList.pop_front();
			else
				damageInfo.timeLeft = damageInfo.interval;

			doDamage(creature, damage);
		}

		if(!remove)
		{
			if(getTicks() > 0)
				endTime += interval;

			interval = 0;
		}
	}

	return Condition::executeCondition(creature, interval);
}

bool ConditionDamage::getNextDamage(int32_t& damage)
{
	if(periodDamage)
	{
		damage = periodDamage;
		return true;
	}

	if(damageList.empty())
		return false;

	IntervalInfo& damageInfo = damageList.front();
	damage = damageInfo.value;
	if(getTicks() != -1)
		damageList.pop_front();

	return true;
}

bool ConditionDamage::doDamage(Creature* creature, int32_t damage)
{
	if(creature->isSuppress(getType()))
		return true;

	CombatType_t combatType = Combat::ConditionToDamageType(conditionType);
	Creature* attacker = g_game.getCreatureByID(owner);
	if(g_game.combatBlockHit(combatType, attacker, creature, damage, false, false))
		return false;

	return g_game.combatChangeHealth(combatType, attacker, creature, damage);
}

void ConditionDamage::addCondition(Creature* creature, const Condition* addCondition)
{
	if(addCondition->getType() != conditionType)
		return;

	const ConditionDamage& conditionDamage = static_cast<const ConditionDamage&>(*addCondition);
	if(!updateCondition(&conditionDamage))
		return;

	setTicks(addCondition->getTicks());
	owner = conditionDamage.owner;
	maxDamage = conditionDamage.maxDamage;
	minDamage = conditionDamage.minDamage;
	startDamage = conditionDamage.startDamage;
	tickInterval = conditionDamage.tickInterval;
	periodDamage = conditionDamage.periodDamage;

	int32_t nextTimeLeft = tickInterval;
	if(!damageList.empty()) //save previous timeLeft
	{
		IntervalInfo& damageInfo = damageList.front();
		nextTimeLeft = damageInfo.timeLeft;
		damageList.clear();
	}

	damageList = conditionDamage.damageList;
	if(!init())
		return;

	if(!damageList.empty()) //restore last timeLeft
	{
		IntervalInfo& damageInfo = damageList.front();
		damageInfo.timeLeft = nextTimeLeft;
	}

	if(!delayed)
	{
		int32_t damage = 0;
		if(getNextDamage(damage))
			doDamage(creature, damage);
	}
}

int32_t ConditionDamage::getTotalDamage() const
{
	int32_t result = 0;
	if(!damageList.empty())
	{
		for(DamageList::const_iterator it = damageList.begin(); it != damageList.end(); ++it)
			result += it->value;
	}
	else
		result = maxDamage + (maxDamage - minDamage) / 2;

	return std::abs(result);
}

Icons_t ConditionDamage::getIcons() const
{
	Icons_t icon = Condition::getIcons();
	if(icon != ICON_NONE)
		return icon;

	switch(conditionType)
	{
		case CONDITION_FIRE:
			return ICON_BURN;

		case CONDITION_ENERGY:
			return ICON_ENERGY;

		case CONDITION_POISON:
			return ICON_POISON;

		case CONDITION_FREEZING:
			return ICON_FREEZING;

		case CONDITION_DAZZLED:
			return ICON_DAZZLED;

		case CONDITION_CURSED:
			return ICON_CURSED;

		case CONDITION_DROWN:
			return ICON_DROWNING;

		default:
			break;
	}

	return ICON_NONE;
}

void ConditionDamage::generateDamageList(int32_t amount, int32_t start, std::list<int32_t>& list)
{
	int32_t sum = 0, med = 0;
	float x1, x2;

	amount = std::abs(amount);
	for(int32_t i = start; i > 0; --i)
	{
		med = ((start + 1 - i) * amount) / start;
		do
		{
			sum += i;
			list.push_back(i);

			x1 = std::fabs(1.0 - (((float)sum) + i) / med);
			x2 = std::fabs(1.0 - (((float)sum) / med));
		}
		while(x1 < x2);
	}
}

ConditionSpeed::ConditionSpeed(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId, int32_t changeSpeed):
ConditionOutfit(_id, _type, _ticks, _buff, _subId)
{
	speedDelta = changeSpeed;
	mina = minb = maxa = maxb = 0.0f;
}

void ConditionSpeed::setFormulaVars(float _mina, float _minb, float _maxa, float _maxb)
{
	mina = _mina;
	minb = _minb;
	maxa = _maxa;
	maxb = _maxb;
}

void ConditionSpeed::getFormulaValues(int32_t var, int32_t& min, int32_t& max) const
{
	min = (int32_t)std::ceil(var * 1.f * mina + minb);
	max = (int32_t)std::ceil(var * 1.f * maxa + maxb);
}

bool ConditionSpeed::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = ConditionOutfit::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_SPEED:
		{
			speedDelta = value;
			if(value > 0)
				conditionType = CONDITION_HASTE;
			else
				conditionType = CONDITION_PARALYZE;

			return true;
		}

		default:
			break;
	}

	return ret;
}

bool ConditionSpeed::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_SPEEDDELTA:
		{
			int32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			speedDelta = value;
			return true;
		}

		case CONDITIONATTR_FORMULA_MINA:
		{
			float value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			mina = value;
			return true;
		}

		case CONDITIONATTR_FORMULA_MINB:
		{
			float value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			minb = value;
			return true;
		}

		case CONDITIONATTR_FORMULA_MAXA:
		{
			float value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			maxa = value;
			return true;
		}

		case CONDITIONATTR_FORMULA_MAXB:
		{
			float value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			maxb = value;
			return true;
		}

		default:
			break;
	}

	return ConditionOutfit::unserializeProp(attr, propStream);
}

bool ConditionSpeed::serialize(PropWriteStream& propWriteStream)
{
	if(!ConditionOutfit::serialize(propWriteStream))
		return false;

	propWriteStream.ADD_UCHAR(CONDITIONATTR_SPEEDDELTA);
	propWriteStream.ADD_VALUE(speedDelta);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MINA);
	propWriteStream.ADD_VALUE(mina);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MINB);
	propWriteStream.ADD_VALUE(minb);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MAXA);
	propWriteStream.ADD_VALUE(maxa);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_FORMULA_MAXB);
	propWriteStream.ADD_VALUE(maxb);
	return true;
}

bool ConditionSpeed::startCondition(Creature* creature)
{
	if(!speedDelta)
	{
		int32_t min, max;
		getFormulaValues(creature->getBaseSpeed(), min, max);
		speedDelta = random_range(min, max);
	}

	g_game.changeSpeed(creature, speedDelta);
	return ConditionOutfit::startCondition(creature);
}

void ConditionSpeed::endCondition(Creature* creature, ConditionEnd_t reason)
{
	g_game.changeSpeed(creature, -speedDelta);
	ConditionOutfit::endCondition(creature, reason);
}

void ConditionSpeed::addCondition(Creature* creature, const Condition* addCondition)
{
	if(conditionType != addCondition->getType() || (ticks == -1 && addCondition->getTicks() > 0))
		return;

	setTicks(addCondition->getTicks());
	const ConditionSpeed& conditionSpeed = static_cast<const ConditionSpeed&>(*addCondition);
	int32_t oldSpeedDelta = speedDelta;

	mina = conditionSpeed.mina;
	maxa = conditionSpeed.maxa;
	minb = conditionSpeed.minb;
	maxb = conditionSpeed.maxb;

	speedDelta = conditionSpeed.speedDelta;
	outfits = conditionSpeed.outfits;

	changeOutfit(creature);
	if(!speedDelta)
	{
		int32_t min, max;
		getFormulaValues(creature->getBaseSpeed(), min, max);
		speedDelta = random_range(min, max);
	}

	int32_t newSpeedChange = speedDelta - oldSpeedDelta;
	if(newSpeedChange)
		g_game.changeSpeed(creature, newSpeedChange);
}

Icons_t ConditionSpeed::getIcons() const
{
	Icons_t icon = Condition::getIcons();
	if(icon != ICON_NONE)
		return icon;

	switch(conditionType)
	{
		case CONDITION_HASTE:
			return ICON_HASTE;

		case CONDITION_PARALYZE:
			return ICON_PARALYZE;

		default:
			break;
	}

	return ICON_NONE;
}

ConditionOutfit::ConditionOutfit(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId):
Condition(_id, _type, _ticks, _buff, _subId)
{
	//
}

bool ConditionOutfit::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	Outfit_t outfit;
	switch(attr)
	{
		case CONDITIONATTR_OUTFIT:
		{
			if(!propStream.GET_VALUE(outfit))
				return false;

			outfits.push_back(outfit);
			return true;
		}

		default:
			break;
	}

	return Condition::unserializeProp(attr, propStream);
}

bool ConditionOutfit::serialize(PropWriteStream& propWriteStream)
{
	if(!Condition::serialize(propWriteStream))
		return false;

	for(std::vector<Outfit_t>::const_iterator it = outfits.begin(); it != outfits.end(); ++it)
	{
		propWriteStream.ADD_UCHAR(CONDITIONATTR_OUTFIT);
		propWriteStream.ADD_VALUE(*it);
	}

	return true;
}

bool ConditionOutfit::startCondition(Creature* creature)
{
	changeOutfit(creature);
	return Condition::startCondition(creature);
}

void ConditionOutfit::changeOutfit(Creature* creature, int32_t index/* = -1*/)
{
	if(outfits.empty())
		return;

	if(index == -1)
		index = random_range(0, outfits.size() - 1);

	g_game.internalCreatureChangeOutfit(creature, outfits[index], true);
}

void ConditionOutfit::endCondition(Creature* creature, ConditionEnd_t reason)
{
	if(!outfits.empty())
		g_game.internalCreatureChangeOutfit(creature, creature->getDefaultOutfit(), true);
}

void ConditionOutfit::addCondition(Creature* creature, const Condition* addCondition)
{
	if(!updateCondition(addCondition))
		return;

	setTicks(addCondition->getTicks());
	const ConditionOutfit& conditionOutfit = static_cast<const ConditionOutfit&>(*addCondition);

	outfits = conditionOutfit.outfits;
	changeOutfit(creature);
}

ConditionLight::ConditionLight(ConditionId_t _id, ConditionType_t _type, int32_t _ticks, bool _buff, uint32_t _subId, int32_t lightLevel, int32_t lightColor):
Condition(_id, _type, _ticks, _buff, _subId)
{
	lightInfo.level = lightLevel;
	lightInfo.color = lightColor;
	internalLightTicks = lightChangeInterval = 0;
}

bool ConditionLight::startCondition(Creature* creature)
{
	internalLightTicks = 0;
	lightChangeInterval = ticks / lightInfo.level;

	creature->setCreatureLight(lightInfo);
	g_game.changeLight(creature);
	return Condition::startCondition(creature);
}

bool ConditionLight::executeCondition(Creature* creature, int32_t interval)
{
	internalLightTicks += interval;
	if(internalLightTicks >= lightChangeInterval)
	{
		LightInfo creatureLight;
		creature->getCreatureLight(creatureLight);

		internalLightTicks = 0;
		if(creatureLight.level > 0)
		{
			--creatureLight.level;
			creature->setCreatureLight(creatureLight);
			g_game.changeLight(creature);
		}
	}

	return Condition::executeCondition(creature, interval);
}

void ConditionLight::endCondition(Creature* creature, ConditionEnd_t reason)
{
	creature->setNormalCreatureLight();
	g_game.changeLight(creature);
}

void ConditionLight::addCondition(Creature* creature, const Condition* addCondition)
{
	if(updateCondition(addCondition))
	{
		setTicks(addCondition->getTicks());
		const ConditionLight& conditionLight = static_cast<const ConditionLight&>(*addCondition);

		lightInfo.level = conditionLight.lightInfo.level;
		lightInfo.color = conditionLight.lightInfo.color;

		lightChangeInterval = getTicks() / lightInfo.level;
		internalLightTicks = 0;

		creature->setCreatureLight(lightInfo);
		g_game.changeLight(creature);
	}
}

bool ConditionLight::setParam(ConditionParam_t param, int32_t value)
{
	bool ret = Condition::setParam(param, value);
	switch(param)
	{
		case CONDITIONPARAM_LIGHT_LEVEL:
			lightInfo.level = value;
			return true;

		case CONDITIONPARAM_LIGHT_COLOR:
			lightInfo.color = value;
			return true;

		default:
			break;
	}

	return ret;
}

bool ConditionLight::unserializeProp(ConditionAttr_t attr, PropStream& propStream)
{
	switch(attr)
	{
		case CONDITIONATTR_LIGHTCOLOR:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			lightInfo.color = value;
			return true;
		}

		case CONDITIONATTR_LIGHTLEVEL:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			lightInfo.level = value;
			return true;
		}

		case CONDITIONATTR_LIGHTTICKS:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			internalLightTicks = value;
			return true;
		}

		case CONDITIONATTR_LIGHTINTERVAL:
		{
			uint32_t value = 0;
			if(!propStream.GET_VALUE(value))
				return false;

			lightChangeInterval = value;
			return true;
		}

		default:
			break;
	}

	return Condition::unserializeProp(attr, propStream);
}

bool ConditionLight::serialize(PropWriteStream& propWriteStream)
{
	if(!Condition::serialize(propWriteStream))
		return false;

	propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTCOLOR);
	propWriteStream.ADD_VALUE(lightInfo.color);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTLEVEL);
	propWriteStream.ADD_VALUE(lightInfo.level);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTTICKS);
	propWriteStream.ADD_VALUE(internalLightTicks);

	propWriteStream.ADD_UCHAR(CONDITIONATTR_LIGHTINTERVAL);
	propWriteStream.ADD_VALUE(lightChangeInterval);

	return true;
}
 
enums.H:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////

#ifndef __ENUMS__
#define __ENUMS__

#include <string>
#include <list>

enum DatabaseEngine_t
{
	DATABASE_ENGINE_NONE = 0,
	DATABASE_ENGINE_MYSQL,
	DATABASE_ENGINE_SQLITE,
	DATABASE_ENGINE_POSTGRESQL,
	DATABASE_ENGINE_ODBC
};

enum Encryption_t
{
	ENCRYPTION_PLAIN = 0,
	ENCRYPTION_MD5,
	ENCRYPTION_SHA1
};

enum GuildLevel_t
{
	GUILDLEVEL_NONE = 0,
	GUILDLEVEL_MEMBER,
	GUILDLEVEL_VICE,
	GUILDLEVEL_LEADER
};

enum OperatingSystem_t
{
	CLIENTOS_LINUX = 0x01,
	CLIENTOS_WINDOWS = 0x02
};

enum Channels_t
{
	CHANNEL_GUILD = 0x00,
	CHANNEL_PARTY = 0x01,
	CHANNEL_RVR = 0x03,
	CHANNEL_HELP = 0x09,
	CHANNEL_DEFAULT = 0xFFFE, //internal usage only, there is no such channel
	CHANNEL_PRIVATE = 0xFFFF
};

enum ViolationAction_t
{
	ACTION_NOTATION = 0,
	ACTION_NAMEREPORT,
	ACTION_BANISHMENT,
	ACTION_BANREPORT,
	ACTION_BANFINAL,
	ACTION_BANREPORTFINAL,
	ACTION_STATEMENT,
	//internal use
	ACTION_DELETION,
	ACTION_NAMELOCK,
	ACTION_BANLOCK,
	ACTION_BANLOCKFINAL,
	ACTION_PLACEHOLDER
};

enum RaceType_t
{
	RACE_NONE = 0,
	RACE_VENOM,
	RACE_BLOOD,
	RACE_UNDEAD,
	RACE_FIRE,
	RACE_ENERGY
};

enum CombatType_t
{
	COMBAT_FIRST		= 0,
	COMBAT_NONE		= COMBAT_FIRST,
	COMBAT_PHYSICALDAMAGE	= 1 << 0,
	COMBAT_ENERGYDAMAGE	= 1 << 1,
	COMBAT_EARTHDAMAGE	= 1 << 2,
	COMBAT_FIREDAMAGE	= 1 << 3,
	COMBAT_UNDEFINEDDAMAGE	= 1 << 4,
	COMBAT_LIFEDRAIN	= 1 << 5,
	COMBAT_MANADRAIN	= 1 << 6,
	COMBAT_HEALING		= 1 << 7,
	COMBAT_DROWNDAMAGE	= 1 << 8,
	COMBAT_ICEDAMAGE	= 1 << 9,
	COMBAT_HOLYDAMAGE	= 1 << 10,
	COMBAT_DEATHDAMAGE	= 1 << 11,
	COMBAT_LAST		= COMBAT_DEATHDAMAGE
};

enum CombatParam_t
{
	COMBATPARAM_NONE = 0,
	COMBATPARAM_COMBATTYPE,
	COMBATPARAM_EFFECT,
	COMBATPARAM_DISTANCEEFFECT,
	COMBATPARAM_BLOCKEDBYSHIELD,
	COMBATPARAM_BLOCKEDBYARMOR,
	COMBATPARAM_TARGETCASTERORTOPMOST,
	COMBATPARAM_CREATEITEM,
	COMBATPARAM_AGGRESSIVE,
	COMBATPARAM_DISPEL,
	COMBATPARAM_USECHARGES,
	COMBATPARAM_TARGETPLAYERSORSUMMONS,
	COMBATPARAM_DIFFERENTAREADAMAGE,
	COMBATPARAM_HITEFFECT,
	COMBATPARAM_HITCOLOR
};

enum CallBackParam_t
{
	CALLBACKPARAM_NONE = 0,
	CALLBACKPARAM_LEVELMAGICVALUE,
	CALLBACKPARAM_SKILLVALUE,
	CALLBACKPARAM_TARGETTILECALLBACK,
	CALLBACKPARAM_TARGETCREATURECALLBACK
};

enum ConditionParam_t
{
	CONDITIONPARAM_OWNER = 1,
	CONDITIONPARAM_TICKS = 2,
	CONDITIONPARAM_OUTFIT = 3,
	CONDITIONPARAM_HEALTHGAIN = 4,
	CONDITIONPARAM_HEALTHTICKS = 5,
	CONDITIONPARAM_MANAGAIN = 6,
	CONDITIONPARAM_MANATICKS = 7,
	CONDITIONPARAM_DELAYED = 8,
	CONDITIONPARAM_SPEED = 9,
	CONDITIONPARAM_LIGHT_LEVEL = 10,
	CONDITIONPARAM_LIGHT_COLOR = 11,
	CONDITIONPARAM_SOULGAIN = 12,
	CONDITIONPARAM_SOULTICKS = 13,
	CONDITIONPARAM_MINVALUE = 14,
	CONDITIONPARAM_MAXVALUE = 15,
	CONDITIONPARAM_STARTVALUE = 16,
	CONDITIONPARAM_TICKINTERVAL = 17,
	CONDITIONPARAM_FORCEUPDATE = 18,
	CONDITIONPARAM_SKILL_MELEE = 19,
	CONDITIONPARAM_SKILL_FIST = 20,
	CONDITIONPARAM_SKILL_CLUB = 21,
	CONDITIONPARAM_SKILL_SWORD = 22,
	CONDITIONPARAM_SKILL_AXE = 23,
	CONDITIONPARAM_SKILL_DISTANCE = 24,
	CONDITIONPARAM_SKILL_SHIELD = 25,
	CONDITIONPARAM_SKILL_FISHING = 26,
	CONDITIONPARAM_STAT_MAXHEALTH = 27,
	CONDITIONPARAM_STAT_MAXMANA = 28,
	CONDITIONPARAM_STAT_SOUL = 29,
	CONDITIONPARAM_STAT_MAGICLEVEL = 30,
	CONDITIONPARAM_STAT_MAXHEALTHPERCENT = 31,
	CONDITIONPARAM_STAT_MAXMANAPERCENT = 32,
	CONDITIONPARAM_STAT_SOULPERCENT = 33,
	CONDITIONPARAM_STAT_MAGICLEVELPERCENT = 34,
	CONDITIONPARAM_SKILL_MELEEPERCENT = 35,
	CONDITIONPARAM_SKILL_FISTPERCENT = 36,
	CONDITIONPARAM_SKILL_CLUBPERCENT = 37,
	CONDITIONPARAM_SKILL_SWORDPERCENT = 38,
	CONDITIONPARAM_SKILL_AXEPERCENT = 39,
	CONDITIONPARAM_SKILL_DISTANCEPERCENT = 40,
	CONDITIONPARAM_SKILL_SHIELDPERCENT = 41,
	CONDITIONPARAM_SKILL_FISHINGPERCENT = 42,
	CONDITIONPARAM_PERIODICDAMAGE = 43,
	CONDITIONPARAM_BUFF = 44,
	CONDITIONPARAM_SUBID = 45,
	CONDITIONPARAM_SKILL_MINEWORKER = 46,
    CONDITIONPARAM_SKILL_MINEWORKERPERCENT = 47,
};

enum BlockType_t
{
	BLOCK_NONE = 0,
	BLOCK_DEFENSE,
	BLOCK_ARMOR,
	BLOCK_IMMUNITY
};

enum Reflect_t
{
	REFLECT_FIRST = 0,
	REFLECT_PERCENT = REFLECT_FIRST,
	REFLECT_CHANCE,
	REFLECT_LAST = REFLECT_CHANCE
};

enum Increment_t
{
	INCREMENT_FIRST = 0,
	HEALING_VALUE = INCREMENT_FIRST,
	HEALING_PERCENT,
	MAGIC_VALUE,
	MAGIC_PERCENT,
	INCREMENT_LAST = MAGIC_PERCENT
};

enum skills_t
{
	SKILL_FIRST = 0,
	SKILL_FIST = SKILL_FIRST,
	SKILL_CLUB,
	SKILL_SWORD,
	SKILL_AXE,
	SKILL_DIST,
	SKILL_SHIELD,
	SKILL_FISH,
	SKILL_MINE,
	SKILL__MAGLEVEL,
	SKILL__LEVEL,
	SKILL_LAST = SKILL_MINE,
	SKILL__LAST = SKILL__LEVEL
};

enum stats_t
{
	STAT_FIRST = 0,
	STAT_MAXHEALTH = STAT_FIRST,
	STAT_MAXMANA,
	STAT_SOUL,
	STAT_LEVEL,
	STAT_MAGICLEVEL,
	STAT_LAST = STAT_MAGICLEVEL
};

enum lossTypes_t
{
	LOSS_FIRST = 0,
	LOSS_EXPERIENCE = LOSS_FIRST,
	LOSS_MANA,
	LOSS_SKILLS,
	LOSS_CONTAINERS,
	LOSS_ITEMS,
	LOSS_LAST = LOSS_ITEMS
};

enum formulaType_t
{
	FORMULA_UNDEFINED = 0,
	FORMULA_LEVELMAGIC,
	FORMULA_SKILL,
	FORMULA_VALUE
};

enum ConditionId_t
{
	CONDITIONID_DEFAULT = -1,
	CONDITIONID_COMBAT = 0,
	CONDITIONID_HEAD,
	CONDITIONID_NECKLACE,
	CONDITIONID_BACKPACK,
	CONDITIONID_ARMOR,
	CONDITIONID_RIGHT,
	CONDITIONID_LEFT,
	CONDITIONID_LEGS,
	CONDITIONID_FEET,
	CONDITIONID_RING,
	CONDITIONID_AMMO,
	CONDITIONID_OUTFIT
};

enum PlayerSex_t
{
	PLAYERSEX_FEMALE = 0,
	PLAYERSEX_MALE
	// DO NOT ADD HERE! Every higher sex is only for your
	// own use- each female should be even and male odd.
};

struct Outfit_t
{
	Outfit_t() {lookHead = lookBody = lookLegs = lookFeet = lookType = lookTypeEx = lookAddons = 0;}
	uint16_t lookType, lookTypeEx;
	uint8_t lookHead, lookBody, lookLegs, lookFeet, lookAddons;

	bool operator==(const Outfit_t o) const
	{
		return (o.lookAddons == lookAddons
			&& o.lookType == lookType && o.lookTypeEx == lookTypeEx
			&& o.lookHead == lookHead && o.lookBody == lookBody
			&& o.lookLegs == lookLegs && o.lookFeet == lookFeet);
	}

	bool operator!=(const Outfit_t o) const
	{
		return !(*this == o);
	}
};

struct LightInfo
{
	uint32_t level, color;

	LightInfo() {level = color = 0;}
	LightInfo(uint32_t _level, uint32_t _color):
		level(_level), color(_color) {}
};

struct ShopInfo
{
	uint32_t itemId;
	int32_t subType, buyPrice, sellPrice;
	std::string itemName;

	ShopInfo()
	{
		itemId = 0;
		subType = 1;
		buyPrice = sellPrice = -1;
		itemName = "";
	}
	ShopInfo(uint32_t _itemId, int32_t _subType = 1, int32_t _buyPrice = -1, int32_t _sellPrice = -1,
		const std::string& _itemName = ""): itemId(_itemId), subType(_subType), buyPrice(_buyPrice),
		sellPrice(_sellPrice), itemName(_itemName) {}
};

typedef std::list<ShopInfo> ShopInfoList;
#endif
 
monsters.CPP:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "monsters.h"
#include "tools.h"
#include "monster.h"

#include "luascript.h"
#include "container.h"
#include "weapons.h"

#include "spells.h"
#include "combat.h"

#include "configmanager.h"
#include "game.h"

extern Game g_game;
extern Spells* g_spells;
extern Monsters g_monsters;
extern ConfigManager g_config;

void MonsterType::reset()
{
	canPushItems = canPushCreatures = isSummonable = isIllusionable = isConvinceable = isLureable = isWalkable = hideName = hideHealth = false;
	pushable = isAttackable = isHostile = true;

	outfit.lookHead = outfit.lookBody = outfit.lookLegs = outfit.lookFeet = outfit.lookType = outfit.lookTypeEx = outfit.lookAddons = 0;
	runAwayHealth = manaCost = lightLevel = lightColor = yellSpeedTicks = yellChance = changeTargetSpeed = changeTargetChance = 0;
	experience = defense = armor = lookCorpse = corpseUnique = corpseAction = conditionImmunities = damageImmunities = 0;

	maxSummons = -1;
	targetDistance = 1;
	staticAttackChance = 95;
	health = healthMax = 100;
	baseSpeed = 200;

	race = RACE_BLOOD;
	skull = SKULL_NONE;
	partyShield = SHIELD_NONE;
	lootMessage = LOOTMSG_IGNORE;

	for(SpellList::iterator it = spellAttackList.begin(); it != spellAttackList.end(); ++it)
	{
		if(!it->combatSpell)
			continue;

		delete it->spell;
		it->spell = NULL;
	}

	spellAttackList.clear();
	for(SpellList::iterator it = spellDefenseList.begin(); it != spellDefenseList.end(); ++it)
	{
		if(!it->combatSpell)
			continue;

		delete it->spell;
		it->spell = NULL;
	}

	spellDefenseList.clear();
	summonList.clear();
	scriptList.clear();

	voiceVector.clear();
	lootItems.clear();
	elementMap.clear();
}

uint16_t Monsters::getLootRandom()
{
	return (uint16_t)std::ceil((double)random_range(0, MAX_LOOTCHANCE) / g_config.getDouble(ConfigManager::RATE_LOOT));
}

void MonsterType::dropLoot(Container* corpse)
{
	Item* tmpItem = NULL;
	for(LootItems::const_iterator it = lootItems.begin(); it != lootItems.end() && !corpse->full(); ++it)
	{
		if((tmpItem = createLoot(*it)))
		{
			if(Container* container = tmpItem->getContainer())
			{
				if(createChildLoot(container, (*it)))
					corpse->__internalAddThing(tmpItem);
				else
					delete container;
			}
			else
				corpse->__internalAddThing(tmpItem);
		}
	}

	corpse->__startDecaying();
	uint32_t ownerId = corpse->getCorpseOwner();
	if(!ownerId)
		return;

	Player* owner = g_game.getPlayerByID(ownerId);
	if(!owner)
		return;

	LootMessage_t message = lootMessage;
	if(message == LOOTMSG_IGNORE)
		message = (LootMessage_t)g_config.getNumber(ConfigManager::LOOT_MESSAGE);

	if(message < LOOTMSG_PLAYER)
		return;

	std::stringstream ss;
	ss << "Devido a morte de " << nameDescription << " você achou: " << corpse->getContentDescription() << ".";
	if(owner->getParty() && message > LOOTMSG_PLAYER)
		owner->getParty()->broadcastMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
	else if(message == LOOTMSG_PLAYER || message == LOOTMSG_BOTH)
		owner->sendTextMessage((MessageClasses)g_config.getNumber(ConfigManager::LOOT_MESSAGE_TYPE), ss.str());
}

Item* MonsterType::createLoot(const LootBlock& lootBlock)
{
	uint16_t item = lootBlock.ids[0], random = Monsters::getLootRandom();
	if(lootBlock.ids.size() > 1)
		item = lootBlock.ids[random_range((size_t)0, lootBlock.ids.size() - 1)];

	Item* tmpItem = NULL;
	if(Item::items[item].stackable)
	{
		if(random < lootBlock.chance)
			tmpItem = Item::CreateItem(item, (random % lootBlock.count + 1));
	}
	else if(random < lootBlock.chance)
		tmpItem = Item::CreateItem(item, 0);

	if(!tmpItem)
		return NULL;

	if(lootBlock.subType != -1)
		tmpItem->setSubType(lootBlock.subType);

	if(lootBlock.actionId != -1)
		tmpItem->setActionId(lootBlock.actionId);

	if(lootBlock.uniqueId != -1)
		tmpItem->setUniqueId(lootBlock.uniqueId);

	if(!lootBlock.text.empty())
		tmpItem->setText(lootBlock.text);

	return tmpItem;
}

bool MonsterType::createChildLoot(Container* parent, const LootBlock& lootBlock)
{
	LootItems::const_iterator it = lootBlock.childLoot.begin();
	if(it == lootBlock.childLoot.end())
		return true;

	Item* tmpItem = NULL;
	for(; it != lootBlock.childLoot.end() && !parent->full(); ++it)
	{
		if((tmpItem = createLoot(*it)))
		{
			if(Container* container = tmpItem->getContainer())
			{
				if(createChildLoot(container, (*it)))
					parent->__internalAddThing(container);
				else
					delete container;
			}
			else
				parent->__internalAddThing(tmpItem);
		}
	}

	return !parent->empty();
}

bool Monsters::loadFromXml(bool reloading /*= false*/)
{
	loaded = false;
	xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_OTHER, "monster/monsters.xml").c_str());
	if(!doc)
	{
		std::cout << "[Warning - Monsters::loadFromXml] Cannot load monsters file." << std::endl;
		std::cout << getLastXMLError() << std::endl;
		return false;
	}

	xmlNodePtr p, root = xmlDocGetRootElement(doc);
	if(xmlStrcmp(root->name,(const xmlChar*)"monsters"))
	{
		std::cout << "[Error - Monsters::loadFromXml] Malformed monsters file." << std::endl;
		xmlFreeDoc(doc);
		return false;
	}

	p = root->children;
	while(p)
	{
		if(p->type != XML_ELEMENT_NODE)
		{
			p = p->next;
			continue;
		}

		if(xmlStrcmp(p->name, (const xmlChar*)"monster"))
		{
			std::cout << "[Warning - Monsters::loadFromXml] Unknown node name (" << p->name << ")." << std::endl;
			p = p->next;
			continue;
		}

		std::string file, name;
		if(readXMLString(p, "file", file) && readXMLString(p, "name", name))
		{
			file = getFilePath(FILE_TYPE_OTHER, "monster/" + file);
			loadMonster(file, name, reloading);
		}

		p = p->next;
	}

	xmlFreeDoc(doc);
	loaded = true;
	return loaded;
}

ConditionDamage* Monsters::getDamageCondition(ConditionType_t conditionType,
	int32_t maxDamage, int32_t minDamage, int32_t startDamage, uint32_t tickInterval)
{
	if(ConditionDamage* condition = dynamic_cast<ConditionDamage*>(Condition::createCondition(CONDITIONID_COMBAT, conditionType, 0)))
	{
		condition->setParam(CONDITIONPARAM_TICKINTERVAL, tickInterval);
		condition->setParam(CONDITIONPARAM_MINVALUE, minDamage);
		condition->setParam(CONDITIONPARAM_MAXVALUE, maxDamage);
		condition->setParam(CONDITIONPARAM_STARTVALUE, startDamage);
		condition->setParam(CONDITIONPARAM_DELAYED, 1);
		return condition;
	}

	return NULL;
}

bool Monsters::deserializeSpell(xmlNodePtr node, spellBlock_t& sb, const std::string& description)
{
	sb.chance = 100;
	sb.speed = 2000;
	sb.range = sb.minCombatValue = sb.maxCombatValue = 0;
	sb.combatSpell = sb.isMelee = false;

	std::string name = "", scriptName = "";
	bool isScripted = false;
	if(readXMLString(node, "script", scriptName))
		isScripted = true;
	else if(!readXMLString(node, "name", name))
		return false;

	int32_t intValue;
	std::string strValue;
	if(readXMLInteger(node, "speed", intValue) || readXMLInteger(node, "interval", intValue))
		sb.speed = std::max(1, intValue);

	if(readXMLInteger(node, "chance", intValue))
	{
		if(intValue < 0 || intValue > 100)
			intValue = 100;

		sb.chance = intValue;
	}

	if(readXMLInteger(node, "range", intValue))
	{
		if(intValue < 0)
			intValue = 0;

		if(intValue > Map::maxViewportX * 2)
			intValue = Map::maxViewportX * 2;

		sb.range = intValue;
	}

	if(readXMLInteger(node, "min", intValue))
		sb.minCombatValue = intValue;

	if(readXMLInteger(node, "max", intValue))
	{
		sb.maxCombatValue = intValue;
		//normalize values
		if(std::abs(sb.minCombatValue) > std::abs(sb.maxCombatValue))
			std::swap(sb.minCombatValue, sb.maxCombatValue);
	}

	if((sb.spell = g_spells->getSpellByName(name)))
		return true;

	CombatSpell* combatSpell = NULL;
	bool needTarget = false, needDirection = false;
	if(isScripted)
	{
		if(readXMLString(node, "direction", strValue))
			needDirection = booleanString(strValue);

		if(readXMLString(node, "target", strValue))
			needTarget = booleanString(strValue);

		combatSpell = new CombatSpell(NULL, needTarget, needDirection);
		if(!combatSpell->loadScript(getFilePath(FILE_TYPE_OTHER, g_spells->getScriptBaseName() + "/scripts/" + scriptName), true))
		{
			delete combatSpell;
			return false;
		}

		if(!combatSpell->loadScriptCombat())
		{
			delete combatSpell;
			return false;

		}

		combatSpell->getCombat()->setPlayerCombatValues(FORMULA_VALUE, sb.minCombatValue, 0, sb.maxCombatValue, 0, 0, 0, 0, 0, 0, 0);
	}
	else
	{
		Combat* combat = new Combat;
		sb.combatSpell = true;
		if(readXMLInteger(node, "length", intValue))
		{
			int32_t length = intValue;
			if(length > 0)
			{
				int32_t spread = 3;
				//need direction spell
				if(readXMLInteger(node, "spread", intValue))
					spread = std::max(0, intValue);

				CombatArea* area = new CombatArea();
				area->setupArea(length, spread);

				combat->setArea(area);
				needDirection = true;
			}
		}

		if(readXMLInteger(node, "radius", intValue))
		{
			int32_t radius = intValue;
			//target spell
			if(readXMLInteger(node, "target", intValue))
				needTarget = (intValue != 0);

			CombatArea* area = new CombatArea();
			area->setupArea(radius);
			combat->setArea(area);
		}

		std::string tmpName = asLowerCaseString(name);
		if(tmpName == "melee")
		{
			int32_t attack = 0, skill = 0;
			if(readXMLInteger(node, "attack", attack) && readXMLInteger(node, "skill", skill))
			{
				sb.minCombatValue = 0;
				sb.maxCombatValue = -Weapons::getMaxMeleeDamage(skill, attack);
			}

			uint32_t tickInterval = 10000;
			ConditionType_t conditionType = CONDITION_NONE;
			if(readXMLInteger(node, "physical", intValue))
				conditionType = CONDITION_PHYSICAL;
			else if(readXMLInteger(node, "fire", intValue))
				conditionType = CONDITION_FIRE;
			else if(readXMLInteger(node, "energy", intValue))
				conditionType = CONDITION_ENERGY;
			else if(readXMLInteger(node, "earth", intValue))
				conditionType = CONDITION_POISON;
			else if(readXMLInteger(node, "freeze", intValue))
				conditionType = CONDITION_FREEZING;
			else if(readXMLInteger(node, "dazzle", intValue))
				conditionType = CONDITION_DAZZLED;
			else if(readXMLInteger(node, "curse", intValue))
				conditionType = CONDITION_CURSED;
			else if(readXMLInteger(node, "drown", intValue))
			{
				conditionType = CONDITION_DROWN;
				tickInterval = 5000;
			}
			else if(readXMLInteger(node, "poison", intValue))
			{
				conditionType = CONDITION_POISON;
				tickInterval = 5000;
			}

			uint32_t damage = std::abs(intValue);
			if(readXMLInteger(node, "tick", intValue) && intValue > 0)
				tickInterval = intValue;

			if(conditionType != CONDITION_NONE)
			{
				Condition* condition = getDamageCondition(conditionType, damage, damage, 0, tickInterval);
				if(condition)
					combat->setCondition(condition);
			}

			sb.isMelee = true;
			sb.range = 1;

			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_PHYSICALDAMAGE);
			combat->setParam(COMBATPARAM_BLOCKEDBYSHIELD, 1);
			combat->setParam(COMBATPARAM_BLOCKEDBYARMOR, 1);
		}
		else if(tmpName == "physical")
		{
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_PHYSICALDAMAGE);
			combat->setParam(COMBATPARAM_BLOCKEDBYARMOR, 1);
		}
		else if(tmpName == "drown")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_DROWNDAMAGE);
		else if(tmpName == "fire")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_FIREDAMAGE);
		else if(tmpName == "energy")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_ENERGYDAMAGE);
		else if(tmpName == "poison" || tmpName == "earth")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_EARTHDAMAGE);
		else if(tmpName == "ice")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_ICEDAMAGE);
		else if(tmpName == "holy")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_HOLYDAMAGE);
		else if(tmpName == "death")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_DEATHDAMAGE);
		else if(tmpName == "lifedrain")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_LIFEDRAIN);
		else if(tmpName == "manadrain")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_MANADRAIN);
		else if(tmpName == "healing")
		{
			bool aggressive = false;
			if(readXMLInteger(node, "self", intValue))
				aggressive = intValue;

			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_HEALING);
			combat->setParam(COMBATPARAM_AGGRESSIVE, aggressive);
		}
		else if(tmpName == "undefined")
			combat->setParam(COMBATPARAM_COMBATTYPE, COMBAT_UNDEFINEDDAMAGE);
		else if(tmpName == "speed")
		{
			int32_t speedChange = 0, duration = 10000;
			if(readXMLInteger(node, "duration", intValue))
				duration = intValue;

			enum Aggressive {
				NO,
				YES,
				AUTO
			} aggressive = AUTO;
			if(readXMLInteger(node, "self", intValue))
				aggressive = (Aggressive)intValue;

			if(readXMLInteger(node, "speedchange", intValue))
				speedChange = std::max(-1000, intValue); //cant be slower than 100%

			std::vector<Outfit_t> outfits;
			for(xmlNodePtr tmpNode = node->children; tmpNode; tmpNode = tmpNode->next)
			{
				if(xmlStrcmp(tmpNode->name,(const xmlChar*)"outfit"))
					continue;

				if(readXMLInteger(tmpNode, "type", intValue))
				{
					Outfit_t outfit;
					outfit.lookType = intValue;
					if(readXMLInteger(tmpNode, "head", intValue))
						outfit.lookHead = intValue;

					if(readXMLInteger(tmpNode, "body", intValue))
						outfit.lookBody = intValue;

					if(readXMLInteger(tmpNode, "legs", intValue))
						outfit.lookLegs = intValue;

					if(readXMLInteger(tmpNode, "feet", intValue))
						outfit.lookFeet = intValue;

					if(readXMLInteger(tmpNode, "addons", intValue))
						outfit.lookAddons = intValue;

					outfits.push_back(outfit);
				}

				if(readXMLInteger(tmpNode, "typeex", intValue) || readXMLInteger(tmpNode, "item", intValue))
				{
					Outfit_t outfit;
					outfit.lookTypeEx = intValue;
					outfits.push_back(outfit);
				}

				if(readXMLString(tmpNode, "monster", strValue))
				{
					if(MonsterType* mType = g_monsters.getMonsterType(strValue))
						outfits.push_back(mType->outfit);
				}
			}

			ConditionType_t conditionType = CONDITION_PARALYZE;
			if(speedChange > 0)
			{
				conditionType = CONDITION_HASTE;
				if(aggressive == AUTO)
					aggressive = NO;
			}
			else if(aggressive == AUTO)
				aggressive = YES;

			if(ConditionSpeed* condition = dynamic_cast<ConditionSpeed*>(Condition::createCondition(
				CONDITIONID_COMBAT, conditionType, duration)))
			{
				condition->setFormulaVars((speedChange / 1000.), 0, (speedChange / 1000.), 0);
				if(!outfits.empty())
					condition->setOutfits(outfits);

				combat->setCondition(condition);
				combat->setParam(COMBATPARAM_AGGRESSIVE, aggressive);
			}
		}
		else if(tmpName == "outfit")
		{
			std::vector<Outfit_t> outfits;
			for(xmlNodePtr tmpNode = node->children; tmpNode; tmpNode = tmpNode->next)
			{
				if(xmlStrcmp(tmpNode->name,(const xmlChar*)"outfit"))
					continue;

				if(readXMLInteger(tmpNode, "type", intValue))
				{
					Outfit_t outfit;
					outfit.lookType = intValue;
					if(readXMLInteger(tmpNode, "head", intValue))
						outfit.lookHead = intValue;

					if(readXMLInteger(tmpNode, "body", intValue))
						outfit.lookBody = intValue;

					if(readXMLInteger(tmpNode, "legs", intValue))
						outfit.lookLegs = intValue;

					if(readXMLInteger(tmpNode, "feet", intValue))
						outfit.lookFeet = intValue;

					if(readXMLInteger(tmpNode, "addons", intValue))
						outfit.lookAddons = intValue;

					outfits.push_back(outfit);
				}

				if(readXMLInteger(tmpNode, "typeex", intValue) || readXMLInteger(tmpNode, "item", intValue))
				{
					Outfit_t outfit;
					outfit.lookTypeEx = intValue;
					outfits.push_back(outfit);
				}

				if(readXMLString(tmpNode, "monster", strValue))
				{
					if(MonsterType* mType = g_monsters.getMonsterType(strValue))
						outfits.push_back(mType->outfit);
				}
			}

			if(outfits.empty())
			{
				if(readXMLInteger(node, "type", intValue))
				{
					Outfit_t outfit;
					outfit.lookType = intValue;
					if(readXMLInteger(node, "head", intValue))
						outfit.lookHead = intValue;

					if(readXMLInteger(node, "body", intValue))
						outfit.lookBody = intValue;

					if(readXMLInteger(node, "legs", intValue))
						outfit.lookLegs = intValue;

					if(readXMLInteger(node, "feet", intValue))
						outfit.lookFeet = intValue;

					if(readXMLInteger(node, "addons", intValue))
						outfit.lookAddons = intValue;

					outfits.push_back(outfit);
				}

				if(readXMLInteger(node, "typeex", intValue) || readXMLInteger(node, "item", intValue))
				{
					Outfit_t outfit;
					outfit.lookTypeEx = intValue;
					outfits.push_back(outfit);
				}

				if(readXMLString(node, "monster", strValue))
				{
					if(MonsterType* mType = g_monsters.getMonsterType(strValue))
						outfits.push_back(mType->outfit);
				}
			}

			if(!outfits.empty())
			{
				int32_t duration = 10000;
				if(readXMLInteger(node, "duration", intValue))
					duration = intValue;

				bool aggressive = false;
				if(readXMLInteger(node, "self", intValue))
					aggressive = intValue;

				if(ConditionOutfit* condition = dynamic_cast<ConditionOutfit*>(Condition::createCondition(
					CONDITIONID_COMBAT, CONDITION_OUTFIT, duration)))
				{
					condition->setOutfits(outfits);
					combat->setCondition(condition);
					combat->setParam(COMBATPARAM_AGGRESSIVE, aggressive);
				}
			}
		}
		else if(tmpName == "invisible")
		{
			int32_t duration = 10000;
			if(readXMLInteger(node, "duration", intValue))
				duration = intValue;

			bool aggressive = false;
			if(readXMLInteger(node, "self", intValue))
				aggressive = intValue;

			if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_INVISIBLE, duration))
			{
				combat->setParam(COMBATPARAM_AGGRESSIVE, aggressive);
				combat->setCondition(condition);
			}
		}
		else if(tmpName == "drunk")
		{
			int32_t duration = 10000;
			if(readXMLInteger(node, "duration", intValue))
				duration = intValue;

			if(Condition* condition = Condition::createCondition(CONDITIONID_COMBAT, CONDITION_DRUNK, duration))
				combat->setCondition(condition);
		}
		else if(tmpName == "skills" || tmpName == "attributes")
		{
			uint32_t duration = 10000, subId = 0;
			if(readXMLInteger(node, "duration", intValue))
				duration = intValue;

			if(readXMLInteger(node, "subid", intValue))
				subId = intValue;

			intValue = 0;
			ConditionParam_t param = CONDITIONPARAM_BUFF; //to know was it loaded
			if(readXMLInteger(node, "melee", intValue))
				param = CONDITIONPARAM_SKILL_MELEE;
			else if(readXMLInteger(node, "fist", intValue))
				param = CONDITIONPARAM_SKILL_FIST;
			else if(readXMLInteger(node, "club", intValue))
				param = CONDITIONPARAM_SKILL_CLUB;
			else if(readXMLInteger(node, "axe", intValue))
				param = CONDITIONPARAM_SKILL_AXE;
			else if(readXMLInteger(node, "sword", intValue))
				param = CONDITIONPARAM_SKILL_SWORD;
			else if(readXMLInteger(node, "distance", intValue) || readXMLInteger(node, "dist", intValue))
				param = CONDITIONPARAM_SKILL_DISTANCE;
			else if(readXMLInteger(node, "shielding", intValue) || readXMLInteger(node, "shield", intValue))
				param = CONDITIONPARAM_SKILL_SHIELD;
			else if(readXMLInteger(node, "fishing", intValue) || readXMLInteger(node, "fish", intValue))
				param = CONDITIONPARAM_SKILL_FISHING;
			else if(readXMLInteger(node, "mineworker", intValue) || readXMLInteger(node, "mine", intValue))
				param = CONDITIONPARAM_SKILL_MINEWORKER;
			else if(readXMLInteger(node, "meleePercent", intValue))
				param = CONDITIONPARAM_SKILL_MELEEPERCENT;
			else if(readXMLInteger(node, "fistPercent", intValue))
				param = CONDITIONPARAM_SKILL_FISTPERCENT;
			else if(readXMLInteger(node, "clubPercent", intValue))
				param = CONDITIONPARAM_SKILL_CLUBPERCENT;
			else if(readXMLInteger(node, "axePercent", intValue))
				param = CONDITIONPARAM_SKILL_AXEPERCENT;
			else if(readXMLInteger(node, "swordPercent", intValue))
				param = CONDITIONPARAM_SKILL_SWORDPERCENT;
			else if(readXMLInteger(node, "distancePercent", intValue) || readXMLInteger(node, "distPercent", intValue))
				param = CONDITIONPARAM_SKILL_DISTANCEPERCENT;
			else if(readXMLInteger(node, "shieldingPercent", intValue) || readXMLInteger(node, "shieldPercent", intValue))
				param = CONDITIONPARAM_SKILL_SHIELDPERCENT;
			else if(readXMLInteger(node, "fishingPercent", intValue) || readXMLInteger(node, "fishPercent", intValue))
				param = CONDITIONPARAM_SKILL_FISHINGPERCENT;
			else if(readXMLInteger(node, "mineworkerPercent", intValue) || readXMLInteger(node, "minePercent", intValue))
				param = CONDITIONPARAM_SKILL_MINEWORKERPERCENT;
			else if(readXMLInteger(node, "maxhealth", intValue))
				param = CONDITIONPARAM_STAT_MAXHEALTHPERCENT;
			else if(readXMLInteger(node, "maxmana", intValue))
				param = CONDITIONPARAM_STAT_MAXMANAPERCENT;
			else if(readXMLInteger(node, "soul", intValue))
				param = CONDITIONPARAM_STAT_SOULPERCENT;
			else if(readXMLInteger(node, "magiclevel", intValue) || readXMLInteger(node, "maglevel", intValue))
				param = CONDITIONPARAM_STAT_MAGICLEVELPERCENT;
			else if(readXMLInteger(node, "maxhealthPercent", intValue))
				param = CONDITIONPARAM_STAT_MAXHEALTHPERCENT;
			else if(readXMLInteger(node, "maxmanaPercent", intValue))
				param = CONDITIONPARAM_STAT_MAXMANAPERCENT;
			else if(readXMLInteger(node, "soulPercent", intValue))
				param = CONDITIONPARAM_STAT_SOULPERCENT;
			else if(readXMLInteger(node, "magiclevelPercent", intValue) || readXMLInteger(node, "maglevelPercent", intValue))
				param = CONDITIONPARAM_STAT_MAGICLEVELPERCENT;

			if(param != CONDITIONPARAM_BUFF)
			{
				if(ConditionAttributes* condition = dynamic_cast<ConditionAttributes*>(Condition::createCondition(
					CONDITIONID_COMBAT, CONDITION_ATTRIBUTES, duration, false, subId)))
				{
					condition->setParam(param, intValue);
					combat->setCondition(condition);
				}
			}
		}
		else if(tmpName == "firefield")
			combat->setParam(COMBATPARAM_CREATEITEM, 1492);
		else if(tmpName == "poisonfield")
			combat->setParam(COMBATPARAM_CREATEITEM, 1496);
		else if(tmpName == "energyfield")
			combat->setParam(COMBATPARAM_CREATEITEM, 1495);
		else if(tmpName == "firecondition" || tmpName == "energycondition" || tmpName == "drowncondition" ||
			tmpName == "poisoncondition" || tmpName == "earthcondition" || tmpName == "freezecondition" ||
			tmpName == "cursecondition" || tmpName == "dazzlecondition")
		{
			ConditionType_t conditionType = CONDITION_NONE;
			uint32_t tickInterval = 2000;
			if(tmpName == "physicalcondition")
			{
				conditionType = CONDITION_PHYSICAL;
				tickInterval = 5000;
			}
			else if(tmpName == "firecondition")
			{
				conditionType = CONDITION_FIRE;
				tickInterval = 10000;
			}
			else if(tmpName == "energycondition")
			{
				conditionType = CONDITION_ENERGY;
				tickInterval = 10000;
			}
			else if(tmpName == "earthcondition")
			{
				conditionType = CONDITION_POISON;
				tickInterval = 10000;
			}
			else if(tmpName == "freezecondition")
			{
				conditionType = CONDITION_FREEZING;
				tickInterval = 10000;
			}
			else if(tmpName == "cursecondition")
			{
				conditionType = CONDITION_CURSED;
				tickInterval = 10000;
			}
			else if(tmpName == "dazzlecondition")
			{
				conditionType = CONDITION_DAZZLED;
				tickInterval = 10000;
			}
			else if(tmpName == "drowncondition")
			{
				conditionType = CONDITION_DROWN;
				tickInterval = 5000;
			}
			else if(tmpName == "poisoncondition")
			{
				conditionType = CONDITION_POISON;
				tickInterval = 5000;
			}

			if(readXMLInteger(node, "tick", intValue) && intValue > 0)
				tickInterval = intValue;

			int32_t startDamage = 0, minDamage = std::abs(sb.minCombatValue), maxDamage = std::abs(sb.maxCombatValue);
			if(readXMLInteger(node, "start", intValue))
				startDamage = std::max(std::abs(intValue), minDamage);

			if(Condition* condition = getDamageCondition(conditionType, maxDamage, minDamage, startDamage, tickInterval))
				combat->setCondition(condition);
		}
		else if(tmpName == "strength")
		{
			//TODO: monster extra strength
		}
		else if(tmpName == "effect")
			{/*show some effect and bye bye!*/}
		else
		{
			delete combat;
			std::cout << "[Error - Monsters::deserializeSpell] " << description << " - Unknown spell name: " << name << std::endl;
			return false;
		}

		combat->setPlayerCombatValues(FORMULA_VALUE, sb.minCombatValue, 0, sb.maxCombatValue, 0, 0, 0, 0, 0, 0, 0);
		combatSpell = new CombatSpell(combat, needTarget, needDirection);

		xmlNodePtr attributeNode = node->children;
		while(attributeNode)
		{
			if(!xmlStrcmp(attributeNode->name, (const xmlChar*)"attribute"))
			{
				if(readXMLString(attributeNode, "key", strValue))
				{
					std::string tmpStrValue = asLowerCaseString(strValue);
					if(tmpStrValue == "shooteffect")
					{
						if(readXMLString(attributeNode, "value", strValue))
						{
							ShootEffect_t shoot = getShootType(strValue);
							if(shoot != SHOOT_EFFECT_UNKNOWN)
								combat->setParam(COMBATPARAM_DISTANCEEFFECT, shoot);
							else
								std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown shootEffect: " << strValue << std::endl;
						}
					}
					else if(tmpStrValue == "areaeffect")
					{
						if(readXMLString(attributeNode, "value", strValue))
						{
							MagicEffect_t effect = getMagicEffect(strValue);
							if(effect != MAGIC_EFFECT_UNKNOWN)
								combat->setParam(COMBATPARAM_EFFECT, effect);
							else
								std::cout << "[Warning - Monsters::deserializeSpell] " << description << " - Unknown areaEffect: " << strValue << std::endl;
						}
					}
					else
						std::cout << "[Warning - Monsters::deserializeSpells] Effect type \"" << strValue << "\" does not exist." << std::endl;
				}
			}
			attributeNode = attributeNode->next;
		}
	}

	sb.spell = combatSpell;
	return true;
}

#define SHOW_XML_WARNING(desc) std::cout << "[Warning - Monsters::loadMonster] " << desc << ". (" << file << ")" << std::endl;
#define SHOW_XML_ERROR(desc) std::cout << "[Error - Monsters::loadMonster] " << desc << ". (" << file << ")" << std::endl;

bool Monsters::loadMonster(const std::string& file, const std::string& monsterName, bool reloading/* = false*/)
{
	if(getIdByName(monsterName) && !reloading)
	{
		std::cout << "[Warning - Monsters::loadMonster] Duplicate registered monster with name: " << monsterName << std::endl;
		return true;
	}

	bool monsterLoad, new_mType = true;
	MonsterType* mType = NULL;
	if(reloading)
	{
		uint32_t id = getIdByName(monsterName);
		if(id != 0)
		{
			mType = getMonsterType(id);
			if(mType != NULL)
			{
				new_mType = false;
				mType->reset();
			}
		}
	}

	if(new_mType)
		mType = new MonsterType();

	xmlDocPtr doc = xmlParseFile(file.c_str());
	if(!doc)
	{
		std::cout << "[Warning - Monsters::loadMonster] Cannot load monster (" << monsterName << ") file (" << file << ")." << std::endl;
		std::cout << getLastXMLError() << std::endl;
		return false;
	}

	monsterLoad = true;
	xmlNodePtr p, root = xmlDocGetRootElement(doc);
	if(xmlStrcmp(root->name,(const xmlChar*)"monster"))
	{
		std::cout << "[Error - Monsters::loadMonster] Malformed monster (" << monsterName << ") file (" << file << ")." << std::endl;
		xmlFreeDoc(doc);
		return false;
	}

	int32_t intValue;
	std::string strValue;
	if(readXMLString(root, "name", strValue))
		mType->name = strValue;
	else
		monsterLoad = false;

	if(readXMLString(root, "nameDescription", strValue))
		mType->nameDescription = strValue;
	else
	{
		mType->nameDescription = "a " + mType->name;
		toLowerCaseString(mType->nameDescription);
	}

	if(readXMLString(root, "race", strValue))
	{
		std::string tmpStrValue = asLowerCaseString(strValue);
		if(tmpStrValue == "venom" || atoi(strValue.c_str()) == 1)
			mType->race = RACE_VENOM;
		else if(tmpStrValue == "blood" || atoi(strValue.c_str()) == 2)
			mType->race = RACE_BLOOD;
		else if(tmpStrValue == "undead" || atoi(strValue.c_str()) == 3)
			mType->race = RACE_UNDEAD;
		else if(tmpStrValue == "fire" || atoi(strValue.c_str()) == 4)
			mType->race = RACE_FIRE;
		else if(tmpStrValue == "energy" || atoi(strValue.c_str()) == 5)
			mType->race = RACE_ENERGY;
		else
			SHOW_XML_WARNING("Unknown race type " << strValue);
	}

	if(readXMLInteger(root, "experience", intValue))
		mType->experience = intValue;

	if(readXMLInteger(root, "speed", intValue))
		mType->baseSpeed = intValue;

	if(readXMLInteger(root, "manacost", intValue))
		mType->manaCost = intValue;

	if(readXMLString(root, "skull", strValue))
		mType->skull = getSkull(strValue);

	if(readXMLString(root, "shield", strValue))
		mType->partyShield = getPartyShield(strValue);

	p = root->children;
	while(p && monsterLoad)
	{
		if(p->type != XML_ELEMENT_NODE)
		{
			p = p->next;
			continue;
		}

		if(!xmlStrcmp(p->name, (const xmlChar*)"health"))
		{
			if(readXMLInteger(p, "now", intValue))
				mType->health = intValue;
			else
			{
				SHOW_XML_ERROR("Missing health.now");
				monsterLoad = false;
			}

			if(readXMLInteger(p, "max", intValue))
				mType->healthMax = intValue;
			else
			{
				SHOW_XML_ERROR("Missing health.max");
				monsterLoad = false;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"flags"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(xmlStrcmp(tmpNode->name, (const xmlChar*)"flag") == 0)
				{
					if(readXMLString(tmpNode, "summonable", strValue))
						mType->isSummonable = booleanString(strValue);

					if(readXMLString(tmpNode, "attackable", strValue))
						mType->isAttackable = booleanString(strValue);

					if(readXMLString(tmpNode, "hostile", strValue))
						mType->isHostile = booleanString(strValue);

					if(readXMLString(tmpNode, "illusionable", strValue))
						mType->isIllusionable = booleanString(strValue);

					if(readXMLString(tmpNode, "convinceable", strValue))
						mType->isConvinceable = booleanString(strValue);

					if(readXMLString(tmpNode, "pushable", strValue))
						mType->pushable = booleanString(strValue);

					if(readXMLString(tmpNode, "canpushitems", strValue))
						mType->canPushItems = booleanString(strValue);

					if(readXMLString(tmpNode, "canpushcreatures", strValue))
						mType->canPushCreatures = booleanString(strValue);

					if(readXMLString(tmpNode, "hidename", strValue))
						mType->hideName = booleanString(strValue);

					if(readXMLString(tmpNode, "hidehealth", strValue))
						mType->hideHealth = booleanString(strValue);

					if(readXMLInteger(tmpNode, "lootmessage", intValue))
						mType->lootMessage = (LootMessage_t)intValue;

					if(readXMLInteger(tmpNode, "staticattack", intValue))
					{
						if(intValue < 0 || intValue > 100)
						{
							SHOW_XML_WARNING("staticattack lower than 0 or greater than 100");
							intValue = 0;
						}

						mType->staticAttackChance = intValue;
					}

					if(readXMLInteger(tmpNode, "lightlevel", intValue))
						mType->lightLevel = intValue;

					if(readXMLInteger(tmpNode, "lightcolor", intValue))
						mType->lightColor = intValue;

					if(readXMLInteger(tmpNode, "targetdistance", intValue))
					{
						if(intValue > Map::maxViewportX)
							SHOW_XML_WARNING("targetdistance greater than maxViewportX");

						mType->targetDistance = std::max(1, intValue);
					}

					if(readXMLInteger(tmpNode, "runonhealth", intValue))
						mType->runAwayHealth = intValue;

					if(readXMLString(tmpNode, "lureable", strValue))
						mType->isLureable = booleanString(strValue);

					if(readXMLString(tmpNode, "walkable", strValue))
						mType->isWalkable = booleanString(strValue);

					if(readXMLString(tmpNode, "skull", strValue))
						mType->skull = getSkull(strValue);

					if(readXMLString(tmpNode, "shield", strValue))
						mType->partyShield = getPartyShield(strValue);
				}

				tmpNode = tmpNode->next;
			}

			//if a monster can push creatures, it should not be pushable
			if(mType->canPushCreatures && mType->pushable)
				mType->pushable = false;
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"targetchange"))
		{
			if(readXMLInteger(p, "speed", intValue) || readXMLInteger(p, "interval", intValue))
				mType->changeTargetSpeed = std::max(1, intValue);
			else
				SHOW_XML_WARNING("Missing targetchange.speed");

			if(readXMLInteger(p, "chance", intValue))
				mType->changeTargetChance = intValue;
			else
				SHOW_XML_WARNING("Missing targetchange.chance");
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"strategy"))
		{
			if(readXMLInteger(p, "attack", intValue)) {}
				//mType->attackStrength = intValue;

			if(readXMLInteger(p, "defense", intValue)) {}
				//mType->defenseStrength = intValue;
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"look"))
		{
			if(readXMLInteger(p, "type", intValue))
			{
				mType->outfit.lookType = intValue;
				if(readXMLInteger(p, "head", intValue))
					mType->outfit.lookHead = intValue;

				if(readXMLInteger(p, "body", intValue))
					mType->outfit.lookBody = intValue;

				if(readXMLInteger(p, "legs", intValue))
					mType->outfit.lookLegs = intValue;

				if(readXMLInteger(p, "feet", intValue))
					mType->outfit.lookFeet = intValue;

				if(readXMLInteger(p, "addons", intValue))
					mType->outfit.lookAddons = intValue;
			}
			else if(readXMLInteger(p, "typeex", intValue))
				mType->outfit.lookTypeEx = intValue;
			else
				SHOW_XML_WARNING("Missing look type/typeex");

			if(readXMLInteger(p, "corpse", intValue))
				mType->lookCorpse = intValue;

			if(readXMLInteger(p, "corpseUniqueId", intValue) || readXMLInteger(p, "corpseUid", intValue))
				mType->corpseUnique = intValue;

			if(readXMLInteger(p, "corpseActionId", intValue) || readXMLInteger(p, "corpseAid", intValue))
				mType->corpseAction = intValue;
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"attacks"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"attack"))
				{
					spellBlock_t sb;
					if(deserializeSpell(tmpNode, sb, monsterName))
						mType->spellAttackList.push_back(sb);
					else
						SHOW_XML_WARNING("Cant load spell");
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"defenses"))
		{
			if(readXMLInteger(p, "defense", intValue))
				mType->defense = intValue;

			if(readXMLInteger(p, "armor", intValue))
				mType->armor = intValue;

			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"defense"))
				{
					spellBlock_t sb;
					if(deserializeSpell(tmpNode, sb, monsterName))
						mType->spellDefenseList.push_back(sb);
					else
						SHOW_XML_WARNING("Cant load spell");
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"immunities"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"immunity"))
				{
					if(readXMLString(tmpNode, "name", strValue))
					{
						std::string tmpStrValue = asLowerCaseString(strValue);
						if(tmpStrValue == "physical")
						{
							mType->damageImmunities |= COMBAT_PHYSICALDAMAGE;
							mType->conditionImmunities |= CONDITION_PHYSICAL;
						}
						else if(tmpStrValue == "energy")
						{
							mType->damageImmunities |= COMBAT_ENERGYDAMAGE;
							mType->conditionImmunities |= CONDITION_ENERGY;
						}
						else if(tmpStrValue == "fire")
						{
							mType->damageImmunities |= COMBAT_FIREDAMAGE;
							mType->conditionImmunities |= CONDITION_FIRE;
						}
						else if(tmpStrValue == "poison" || tmpStrValue == "earth")
						{
							mType->damageImmunities |= COMBAT_EARTHDAMAGE;
							mType->conditionImmunities |= CONDITION_POISON;
						}
						else if(tmpStrValue == "ice")
						{
							mType->damageImmunities |= COMBAT_ICEDAMAGE;
							mType->conditionImmunities |= CONDITION_FREEZING;
						}
						else if(tmpStrValue == "holy")
						{
							mType->damageImmunities |= COMBAT_HOLYDAMAGE;
							mType->conditionImmunities |= CONDITION_DAZZLED;
						}
						else if(tmpStrValue == "death")
						{
							mType->damageImmunities |= COMBAT_DEATHDAMAGE;
							mType->conditionImmunities |= CONDITION_CURSED;
						}
						else if(tmpStrValue == "drown")
						{
							mType->damageImmunities |= COMBAT_DROWNDAMAGE;
							mType->conditionImmunities |= CONDITION_DROWN;
						}
						else if(tmpStrValue == "lifedrain")
							mType->damageImmunities |= COMBAT_LIFEDRAIN;
						else if(tmpStrValue == "manadrain")
							mType->damageImmunities |= COMBAT_MANADRAIN;
						else if(tmpStrValue == "paralyze")
							mType->conditionImmunities |= CONDITION_PARALYZE;
						else if(tmpStrValue == "outfit")
							mType->conditionImmunities |= CONDITION_OUTFIT;
						else if(tmpStrValue == "drunk")
							mType->conditionImmunities |= CONDITION_DRUNK;
						else if(tmpStrValue == "invisible")
							mType->conditionImmunities |= CONDITION_INVISIBLE;
						else
							SHOW_XML_WARNING("Unknown immunity name " << strValue);
					}
					else if(readXMLString(tmpNode, "physical", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_PHYSICALDAMAGE;
						//mType->conditionImmunities |= CONDITION_PHYSICAL;
					}
					else if(readXMLString(tmpNode, "energy", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_ENERGYDAMAGE;
						mType->conditionImmunities |= CONDITION_ENERGY;
					}
					else if(readXMLString(tmpNode, "fire", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_FIREDAMAGE;
						mType->conditionImmunities |= CONDITION_FIRE;
					}
					else if((readXMLString(tmpNode, "poison", strValue) || readXMLString(tmpNode, "earth", strValue))
						&& booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_EARTHDAMAGE;
						mType->conditionImmunities |= CONDITION_POISON;
					}
					else if(readXMLString(tmpNode, "drown", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_DROWNDAMAGE;
						mType->conditionImmunities |= CONDITION_DROWN;
					}
					else if(readXMLString(tmpNode, "ice", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_ICEDAMAGE;
						mType->conditionImmunities |= CONDITION_FREEZING;
					}
					else if(readXMLString(tmpNode, "holy", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_HOLYDAMAGE;
						mType->conditionImmunities |= CONDITION_DAZZLED;
					}
					else if(readXMLString(tmpNode, "death", strValue) && booleanString(strValue))
					{
						mType->damageImmunities |= COMBAT_DEATHDAMAGE;
						mType->conditionImmunities |= CONDITION_CURSED;
					}
					else if(readXMLString(tmpNode, "lifedrain", strValue) && booleanString(strValue))
						mType->damageImmunities |= COMBAT_LIFEDRAIN;
					else if(readXMLString(tmpNode, "manadrain", strValue) && booleanString(strValue))
						mType->damageImmunities |= COMBAT_LIFEDRAIN;
					else if(readXMLString(tmpNode, "paralyze", strValue) && booleanString(strValue))
						mType->conditionImmunities |= CONDITION_PARALYZE;
					else if(readXMLString(tmpNode, "outfit", strValue) && booleanString(strValue))
						mType->conditionImmunities |= CONDITION_OUTFIT;
					else if(readXMLString(tmpNode, "drunk", strValue) && booleanString(strValue))
						mType->conditionImmunities |= CONDITION_DRUNK;
					else if(readXMLString(tmpNode, "invisible", strValue) && booleanString(strValue))
						mType->conditionImmunities |= CONDITION_INVISIBLE;
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"voices"))
		{
			if(readXMLInteger(p, "speed", intValue) || readXMLInteger(p, "interval", intValue))
				mType->yellSpeedTicks = intValue;
			else
				SHOW_XML_WARNING("Missing voices.speed");

			if(readXMLInteger(p, "chance", intValue))
				mType->yellChance = intValue;
			else
				SHOW_XML_WARNING("Missing voices.chance");

			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"voice"))
				{
					voiceBlock_t vb;
					vb.text = "";
					vb.yellText = false;

					if(readXMLString(tmpNode, "sentence", strValue))
						vb.text = strValue;
					else
						SHOW_XML_WARNING("Missing voice.sentence");

					if(readXMLString(tmpNode, "yell", strValue))
						vb.yellText = booleanString(strValue);

					mType->voiceVector.push_back(vb);
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"loot"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(tmpNode->type != XML_ELEMENT_NODE)
				{
					tmpNode = tmpNode->next;
					continue;
				}

				LootBlock rootBlock;
				if(loadLoot(tmpNode, rootBlock))
					mType->lootItems.push_back(rootBlock);
				else
					SHOW_XML_WARNING("Cant load loot");

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"elements"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"element"))
				{
					if(readXMLInteger(tmpNode, "firePercent", intValue))
						mType->elementMap[COMBAT_FIREDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "energyPercent", intValue))
						mType->elementMap[COMBAT_ENERGYDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "icePercent", intValue))
						mType->elementMap[COMBAT_ICEDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "poisonPercent", intValue) || readXMLInteger(tmpNode, "earthPercent", intValue))
						mType->elementMap[COMBAT_EARTHDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "holyPercent", intValue))
						mType->elementMap[COMBAT_HOLYDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "deathPercent", intValue))
						mType->elementMap[COMBAT_DEATHDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "drownPercent", intValue))
						mType->elementMap[COMBAT_DROWNDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "physicalPercent", intValue))
						mType->elementMap[COMBAT_PHYSICALDAMAGE] = intValue;
					else if(readXMLInteger(tmpNode, "lifeDrainPercent", intValue))
						mType->elementMap[COMBAT_LIFEDRAIN] = intValue;
					else if(readXMLInteger(tmpNode, "manaDrainPercent", intValue))
						mType->elementMap[COMBAT_MANADRAIN] = intValue;
					else if(readXMLInteger(tmpNode, "healingPercent", intValue))
						mType->elementMap[COMBAT_HEALING] = intValue;
					else if(readXMLInteger(tmpNode, "undefinedPercent", intValue))
						mType->elementMap[COMBAT_UNDEFINEDDAMAGE] = intValue;
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"summons"))
		{
			if(readXMLInteger(p, "maxSummons", intValue) || readXMLInteger(p, "max", intValue))
				mType->maxSummons = intValue;

			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"summon"))
				{
					uint32_t chance = 100, interval = 1000, amount = 1;
					if(readXMLInteger(tmpNode, "speed", intValue) || readXMLInteger(tmpNode, "interval", intValue))
						interval = intValue;

					if(readXMLInteger(tmpNode, "chance", intValue))
						chance = intValue;

					if(readXMLInteger(tmpNode, "amount", intValue) || readXMLInteger(tmpNode, "max", intValue))
						amount = intValue;

					if(readXMLString(tmpNode, "name", strValue))
					{
						summonBlock_t sb;
						sb.name = strValue;
						sb.interval = interval;
						sb.chance = chance;
						sb.amount = amount;

						mType->summonList.push_back(sb);
					}
					else
						SHOW_XML_WARNING("Missing summon.name");
				}

				tmpNode = tmpNode->next;
			}
		}
		else if(!xmlStrcmp(p->name, (const xmlChar*)"script"))
		{
			xmlNodePtr tmpNode = p->children;
			while(tmpNode)
			{
				if(!xmlStrcmp(tmpNode->name, (const xmlChar*)"event"))
				{
					if(readXMLString(tmpNode, "name", strValue))
						mType->scriptList.push_back(strValue);
					else
						SHOW_XML_WARNING("Missing name for script event");
				}

				tmpNode = tmpNode->next;
			}
		}
		else
			SHOW_XML_WARNING("Unknown attribute type - " << p->name);

		p = p->next;
	}

	xmlFreeDoc(doc);
	if(monsterLoad)
	{
		static uint32_t id = 0;
		if(new_mType)
		{
			id++;
			monsterNames[asLowerCaseString(monsterName)] = id;
			monsters[id] = mType;
		}

		return true;
	}

	if(new_mType)
		delete mType;

	return false;
}

bool Monsters::loadLoot(xmlNodePtr node, LootBlock& lootBlock)
{
	std::string strValue;
	if(readXMLString(node, "id", strValue) || readXMLString(node, "ids", strValue))
	{
		IntegerVec idsVec;
		parseIntegerVec(strValue, idsVec);
		for(IntegerVec::iterator it = idsVec.begin(); it != idsVec.end(); ++it)
		{
			lootBlock.ids.push_back(*it);
			if(Item::items[(*it)].isContainer())
				loadChildLoot(node, lootBlock);
		}
	}
	else if(readXMLString(node, "name", strValue) || readXMLString(node, "names", strValue))
	{
		StringVec names = explodeString(strValue, ";");
		for(StringVec::iterator it = names.begin(); it != names.end(); ++it)
		{
			uint16_t tmp = Item::items.getItemIdByName(strValue);
			if(!tmp)
				continue;

			lootBlock.ids.push_back(tmp);
			if(Item::items[tmp].isContainer())
				loadChildLoot(node, lootBlock);
		}
	}

	if(lootBlock.ids.empty())
		return false;

	int32_t intValue;
	if(readXMLInteger(node, "count", intValue) || readXMLInteger(node, "countmax", intValue))
		lootBlock.count = std::max(1, std::min(100, intValue));
	else
		lootBlock.count = 1;

	if(readXMLInteger(node, "chance", intValue) || readXMLInteger(node, "chance1", intValue))
		lootBlock.chance = std::min(MAX_LOOTCHANCE, intValue);
	else
		lootBlock.chance = MAX_LOOTCHANCE;

	if(readXMLInteger(node, "subtype", intValue) || readXMLInteger(node, "subType", intValue))
		lootBlock.subType = intValue;

	if(readXMLInteger(node, "actionId", intValue) || readXMLInteger(node, "actionid", intValue)
		|| readXMLInteger(node, "aid", intValue))
		lootBlock.actionId = intValue;

	if(readXMLInteger(node, "uniqueId", intValue) || readXMLInteger(node, "uniqueid", intValue)
		|| readXMLInteger(node, "uid", intValue))
		lootBlock.uniqueId = intValue;

	if(readXMLString(node, "text", strValue))
		lootBlock.text = strValue;

	return true;
}

bool Monsters::loadChildLoot(xmlNodePtr node, LootBlock& parentBlock)
{
	if(!node)
		return false;

	xmlNodePtr p = node->children, insideNode;
	while(p)
	{
		if(!xmlStrcmp(p->name, (const xmlChar*)"inside"))
		{
			insideNode = p->children;
			while(insideNode)
			{
				LootBlock childBlock;
				if(loadLoot(insideNode, childBlock))
					parentBlock.childLoot.push_back(childBlock);

				insideNode = insideNode->next;
			}

			p = p->next;
			continue;
		}

		LootBlock childBlock;
		if(loadLoot(p, childBlock))
			parentBlock.childLoot.push_back(childBlock);

		p = p->next;
	}

	return true;
}

MonsterType* Monsters::getMonsterType(const std::string& name)
{
	uint32_t mId = getIdByName(name);
	if(mId != 0)
		return getMonsterType(mId);

	return NULL;
}

MonsterType* Monsters::getMonsterType(uint32_t mid)
{
	MonsterMap::iterator it = monsters.find(mid);
	if(it != monsters.end())
		return it->second;

	return NULL;
}

uint32_t Monsters::getIdByName(const std::string& name)
{
	std::string tmp = name;
	MonsterNameMap::iterator it = monsterNames.find(asLowerCaseString(tmp));
	if(it != monsterNames.end())
		return it->second;

	return 0;
}

Monsters::~Monsters()
{
	loaded = false;
	for(MonsterMap::iterator it = monsters.begin(); it != monsters.end(); it++)
		delete it->second;
}
 
outfit.CPP:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "outfit.h"
#include "tools.h"

#include "player.h"
#include "condition.h"

#include "game.h"
extern Game g_game;

bool Outfits::parseOutfitNode(xmlNodePtr p)
{
	if(xmlStrcmp(p->name, (const xmlChar*)"outfit"))
		return false;

	int32_t intValue;
	if(!readXMLInteger(p, "id", intValue))
	{
		std::cout << "[Error - Outfits::parseOutfitNode] Missing outfit id, skipping" << std::endl;
		return false;
	}

	Outfit newOutfit;
	newOutfit.outfitId = intValue;

	std::string name, strValue;
	if(readXMLString(p, "default", strValue))
		newOutfit.isDefault = booleanString(strValue);

	if(!readXMLString(p, "name", strValue))
	{
		std::stringstream ss;
		ss << "Outfit #" << newOutfit.outfitId;
		ss >> name;
	}
	else
		name = strValue;

	bool override = false;
	if(readXMLString(p, "override", strValue) && booleanString(strValue))
		override = true;

	if(readXMLInteger(p, "access", intValue))
		newOutfit.accessLevel = intValue;

	if(readXMLInteger(p, "quest", intValue))
	{
		newOutfit.storageId = intValue;
		newOutfit.storageValue = "1";
	}
	else
	{
		if(readXMLInteger(p, "storageId", intValue))
			newOutfit.storageId = intValue;

		if(readXMLString(p, "storageValue", strValue))
			newOutfit.storageValue = strValue;
	}

	if(readXMLString(p, "premium", strValue))
		newOutfit.isPremium = booleanString(strValue);

	for(xmlNodePtr listNode = p->children; listNode != NULL; listNode = listNode->next)
	{
		if(xmlStrcmp(listNode->name, (const xmlChar*)"list"))
			continue;

		Outfit outfit = newOutfit;
		if(!readXMLInteger(listNode, "looktype", intValue) && !readXMLInteger(listNode, "lookType", intValue))
		{
			std::cout << "[Error - Outfits::parseOutfitNode] Missing looktype for an outfit with id " << outfit.outfitId << std::endl;
			continue;
		}

		outfit.lookType = intValue;
		if(!readXMLString(listNode, "gender", strValue) && !readXMLString(listNode, "type", strValue) && !readXMLString(listNode, "sex", strValue))
		{
			std::cout << "[Error - Outfits::parseOutfitNode] Missing gender(s) for an outfit with id " << outfit.outfitId
				<< " and looktype " << outfit.lookType << std::endl;
			continue;
		}

		IntegerVec intVector;
		if(!parseIntegerVec(strValue, intVector))
		{
			std::cout << "[Error - Outfits::parseOutfitNode] Invalid gender(s) for an outfit with id " << outfit.outfitId
				<< " and looktype " << outfit.lookType << std::endl;
			continue;
		}

		if(readXMLInteger(listNode, "addons", intValue))
			outfit.addons = intValue;

		if(readXMLString(listNode, "name", strValue))
			outfit.name = strValue;
		else
			outfit.name = name;

		if(readXMLString(listNode, "requirement", strValue))
		{
			std::string tmpStrValue = asLowerCaseString(strValue);
			if(tmpStrValue == "none")
				outfit.requirement = REQUIREMENT_NONE;
			else if(tmpStrValue == "first")
				outfit.requirement = REQUIREMENT_FIRST;
			else if(tmpStrValue == "second")
				outfit.requirement = REQUIREMENT_SECOND;
			else if(tmpStrValue == "any")
				outfit.requirement = REQUIREMENT_ANY;
			else if(tmpStrValue != "both")
				std::cout << "[Warning - Outfits::loadFromXml] Unknown requirement tag value, using default (both)" << std::endl;
		}

		if(readXMLString(listNode, "manaShield", strValue))
			outfit.manaShield = booleanString(strValue);

		if(readXMLString(listNode, "invisible", strValue))
			outfit.invisible = booleanString(strValue);

		if(readXMLInteger(listNode, "healthGain", intValue))
		{
			outfit.healthGain = intValue;
			outfit.regeneration = true;
		}

		if(readXMLInteger(listNode, "healthTicks", intValue))
		{
			outfit.healthTicks = intValue;
			outfit.regeneration = true;
		}

		if(readXMLInteger(listNode, "manaGain", intValue))
		{
			outfit.manaGain = intValue;
			outfit.regeneration = true;
		}

		if(readXMLInteger(listNode, "manaTicks", intValue))
		{
			outfit.manaTicks = intValue;
			outfit.regeneration = true;
		}

		if(readXMLInteger(listNode, "speed", intValue))
			outfit.speed = intValue;

		for(xmlNodePtr configNode = listNode->children; configNode != NULL; configNode = configNode->next)
		{
			if(!xmlStrcmp(configNode->name, (const xmlChar*)"reflect"))
			{
				if(readXMLInteger(configNode, "percentAll", intValue))
				{
					for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
						outfit.reflect[REFLECT_PERCENT][(CombatType_t)i] += intValue;
				}

				if(readXMLInteger(configNode, "percentElements", intValue))
				{
					outfit.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "percentMagic", intValue))
				{
					outfit.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue;
					outfit.reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "percentEnergy", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_ENERGYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentFire", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_FIREDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_EARTHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentIce", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_ICEDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentHoly", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_HOLYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentDeath", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_DEATHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentLifeDrain", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_LIFEDRAIN] += intValue;

				if(readXMLInteger(configNode, "percentManaDrain", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_MANADRAIN] += intValue;

				if(readXMLInteger(configNode, "percentDrown", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_DROWNDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentPhysical", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_PHYSICALDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentHealing", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_HEALING] += intValue;

				if(readXMLInteger(configNode, "percentUndefined", intValue))
					outfit.reflect[REFLECT_PERCENT][COMBAT_UNDEFINEDDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceAll", intValue))
				{
					for(uint32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
						outfit.reflect[REFLECT_CHANCE][(CombatType_t)i] += intValue;
				}

				if(readXMLInteger(configNode, "chanceElements", intValue))
				{
					outfit.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "chanceMagic", intValue))
				{
					outfit.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue;
					outfit.reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "chanceEnergy", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_ENERGYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceFire", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_FIREDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chancePoison", intValue) || readXMLInteger(configNode, "chanceEarth", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_EARTHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceIce", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_ICEDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceHoly", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_HOLYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceDeath", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_DEATHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceLifeDrain", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_LIFEDRAIN] += intValue;

				if(readXMLInteger(configNode, "chanceManaDrain", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_MANADRAIN] += intValue;

				if(readXMLInteger(configNode, "chanceDrown", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_DROWNDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chancePhysical", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_PHYSICALDAMAGE] += intValue;

				if(readXMLInteger(configNode, "chanceHealing", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_HEALING] += intValue;

				if(readXMLInteger(configNode, "chanceUndefined", intValue))
					outfit.reflect[REFLECT_CHANCE][COMBAT_UNDEFINEDDAMAGE] += intValue;
			}
			else if(!xmlStrcmp(configNode->name, (const xmlChar*)"absorb"))
			{
				if(readXMLInteger(configNode, "percentAll", intValue))
				{
					for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
						outfit.absorb[(CombatType_t)i] += intValue;
				}

				if(readXMLInteger(configNode, "percentElements", intValue))
				{
					outfit.absorb[COMBAT_ENERGYDAMAGE] += intValue;
					outfit.absorb[COMBAT_FIREDAMAGE] += intValue;
					outfit.absorb[COMBAT_EARTHDAMAGE] += intValue;
					outfit.absorb[COMBAT_ICEDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "percentMagic", intValue))
				{
					outfit.absorb[COMBAT_ENERGYDAMAGE] += intValue;
					outfit.absorb[COMBAT_FIREDAMAGE] += intValue;
					outfit.absorb[COMBAT_EARTHDAMAGE] += intValue;
					outfit.absorb[COMBAT_ICEDAMAGE] += intValue;
					outfit.absorb[COMBAT_HOLYDAMAGE] += intValue;
					outfit.absorb[COMBAT_DEATHDAMAGE] += intValue;
				}

				if(readXMLInteger(configNode, "percentEnergy", intValue))
					outfit.absorb[COMBAT_ENERGYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentFire", intValue))
					outfit.absorb[COMBAT_FIREDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue))
					outfit.absorb[COMBAT_EARTHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentIce", intValue))
					outfit.absorb[COMBAT_ICEDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentHoly", intValue))
					outfit.absorb[COMBAT_HOLYDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentDeath", intValue))
					outfit.absorb[COMBAT_DEATHDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentLifeDrain", intValue))
					outfit.absorb[COMBAT_LIFEDRAIN] += intValue;

				if(readXMLInteger(configNode, "percentManaDrain", intValue))
					outfit.absorb[COMBAT_MANADRAIN] += intValue;

				if(readXMLInteger(configNode, "percentDrown", intValue))
					outfit.absorb[COMBAT_DROWNDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentPhysical", intValue))
					outfit.absorb[COMBAT_PHYSICALDAMAGE] += intValue;

				if(readXMLInteger(configNode, "percentHealing", intValue))
					outfit.absorb[COMBAT_HEALING] += intValue;

				if(readXMLInteger(configNode, "percentUndefined", intValue))
					outfit.absorb[COMBAT_UNDEFINEDDAMAGE] += intValue;
			}
			else if(!xmlStrcmp(configNode->name, (const xmlChar*)"skills"))
			{
				if(readXMLInteger(configNode, "fist", intValue))
					outfit.skills[SKILL_FIST] += intValue;

				if(readXMLInteger(configNode, "club", intValue))
					outfit.skills[SKILL_CLUB] += intValue;

				if(readXMLInteger(configNode, "axe", intValue))
					outfit.skills[SKILL_AXE] += intValue;

				if(readXMLInteger(configNode, "sword", intValue))
					outfit.skills[SKILL_SWORD] += intValue;

				if(readXMLInteger(configNode, "distance", intValue) || readXMLInteger(configNode, "dist", intValue))
					outfit.skills[SKILL_DIST] += intValue;

				if(readXMLInteger(configNode, "shielding", intValue) || readXMLInteger(configNode, "shield", intValue))
					outfit.skills[SKILL_SHIELD] = intValue;

				if(readXMLInteger(configNode, "fishing", intValue) || readXMLInteger(configNode, "fish", intValue))
					outfit.skills[SKILL_FISH] = intValue;

				if(readXMLInteger(configNode, "mine worker", intValue) || readXMLInteger(configNode, "mine", intValue))
					outfit.skills[SKILL_MINE] = intValue;

				if(readXMLInteger(configNode, "melee", intValue))
				{
					outfit.skills[SKILL_FIST] += intValue;
					outfit.skills[SKILL_CLUB] += intValue;
					outfit.skills[SKILL_SWORD] += intValue;
					outfit.skills[SKILL_AXE] += intValue;
				}

				if(readXMLInteger(configNode, "weapon", intValue) || readXMLInteger(configNode, "weapons", intValue))
				{
					outfit.skills[SKILL_CLUB] += intValue;
					outfit.skills[SKILL_SWORD] += intValue;
					outfit.skills[SKILL_AXE] += intValue;
					outfit.skills[SKILL_DIST] += intValue;
				}

				if(readXMLInteger(configNode, "fistPercent", intValue))
					outfit.skillsPercent[SKILL_FIST] += intValue;

				if(readXMLInteger(configNode, "clubPercent", intValue))
					outfit.skillsPercent[SKILL_CLUB] += intValue;

				if(readXMLInteger(configNode, "mineworkerPercent", intValue) || readXMLInteger(configNode, "minePercent", intValue))
					outfit.skillsPercent[SKILL_MINE] = intValue;

				if(readXMLInteger(configNode, "swordPercent", intValue))
					outfit.skillsPercent[SKILL_SWORD] += intValue;

				if(readXMLInteger(configNode, "axePercent", intValue))
					outfit.skillsPercent[SKILL_AXE] += intValue;

				if(readXMLInteger(configNode, "distancePercent", intValue) || readXMLInteger(configNode, "distPercent", intValue))
					outfit.skillsPercent[SKILL_DIST] += intValue;

				if(readXMLInteger(configNode, "shieldingPercent", intValue) || readXMLInteger(configNode, "shieldPercent", intValue))
					outfit.skillsPercent[SKILL_SHIELD] = intValue;

				if(readXMLInteger(configNode, "fishingPercent", intValue) || readXMLInteger(configNode, "fishPercent", intValue))
					outfit.skillsPercent[SKILL_FISH] = intValue;

				if(readXMLInteger(configNode, "meleePercent", intValue))
				{
					outfit.skillsPercent[SKILL_FIST] += intValue;
					outfit.skillsPercent[SKILL_CLUB] += intValue;
					outfit.skillsPercent[SKILL_SWORD] += intValue;
					outfit.skillsPercent[SKILL_AXE] += intValue;
				}

				if(readXMLInteger(configNode, "weaponPercent", intValue) || readXMLInteger(configNode, "weaponsPercent", intValue))
				{
					outfit.skillsPercent[SKILL_CLUB] += intValue;
					outfit.skillsPercent[SKILL_SWORD] += intValue;
					outfit.skillsPercent[SKILL_AXE] += intValue;
					outfit.skillsPercent[SKILL_DIST] += intValue;
				}
			}
			else if(!xmlStrcmp(configNode->name, (const xmlChar*)"stats"))
			{
				if(readXMLInteger(configNode, "maxHealth", intValue))
					outfit.stats[STAT_MAXHEALTH] = intValue;

				if(readXMLInteger(configNode, "maxMana", intValue))
					outfit.stats[STAT_MAXMANA] = intValue;

				if(readXMLInteger(configNode, "soul", intValue))
					outfit.stats[STAT_SOUL] = intValue;

				if(readXMLInteger(configNode, "level", intValue))
					outfit.stats[STAT_LEVEL] = intValue;

				if(readXMLInteger(configNode, "magLevel", intValue) ||
					readXMLInteger(configNode, "magicLevel", intValue))
					outfit.stats[STAT_MAGICLEVEL] = intValue;

				if(readXMLInteger(configNode, "maxHealthPercent", intValue))
					outfit.statsPercent[STAT_MAXHEALTH] = intValue;

				if(readXMLInteger(configNode, "maxManaPercent", intValue))
					outfit.statsPercent[STAT_MAXMANA] = intValue;

				if(readXMLInteger(configNode, "soulPercent", intValue))
					outfit.statsPercent[STAT_SOUL] = intValue;

				if(readXMLInteger(configNode, "levelPercent", intValue))
					outfit.statsPercent[STAT_LEVEL] = intValue;

				if(readXMLInteger(configNode, "magLevelPercent", intValue) ||
					readXMLInteger(configNode, "magicLevelPercent", intValue))
					outfit.statsPercent[STAT_MAGICLEVEL] = intValue;
			}
			else if(!xmlStrcmp(configNode->name, (const xmlChar*)"suppress"))
			{
				if(readXMLString(configNode, "poison", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_POISON;

				if(readXMLString(configNode, "fire", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_FIRE;

				if(readXMLString(configNode, "energy", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_ENERGY;

				if(readXMLString(configNode, "physical", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_PHYSICAL;

				if(readXMLString(configNode, "haste", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_HASTE;

				if(readXMLString(configNode, "paralyze", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_PARALYZE;

				if(readXMLString(configNode, "outfit", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_OUTFIT;

				if(readXMLString(configNode, "invisible", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_INVISIBLE;

				if(readXMLString(configNode, "light", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_LIGHT;

				if(readXMLString(configNode, "manaShield", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_MANASHIELD;

				if(readXMLString(configNode, "infight", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_INFIGHT;

				if(readXMLString(configNode, "drunk", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_DRUNK;

				if(readXMLString(configNode, "exhaust", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_EXHAUST;

				if(readXMLString(configNode, "regeneration", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_REGENERATION;

				if(readXMLString(configNode, "soul", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_SOUL;

				if(readXMLString(configNode, "drown", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_DROWN;

				if(readXMLString(configNode, "muted", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_MUTED;

				if(readXMLString(configNode, "attributes", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_ATTRIBUTES;

				if(readXMLString(configNode, "freezing", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_FREEZING;

				if(readXMLString(configNode, "dazzled", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_DAZZLED;

				if(readXMLString(configNode, "cursed", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_CURSED;

				if(readXMLString(configNode, "pacified", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_PACIFIED;

				if(readXMLString(configNode, "gamemaster", strValue) && booleanString(strValue))
					outfit.conditionSuppressions |= CONDITION_GAMEMASTER;
			}
		}

		bool add = false;
		OutfitMap::iterator fit;
		for(IntegerVec::iterator it = intVector.begin(); it != intVector.end(); ++it)
		{
			fit = outfitsMap[(*it)].find(outfit.outfitId);
			if(fit != outfitsMap[(*it)].end())
			{
				if(override)
				{
					fit->second = outfit;
					if(!add)
						add = true;
				}
				else
					std::cout << "[Warning - Outfits::parseOutfitNode] Duplicated outfit for gender " << (*it) << " with lookType " << outfit.outfitId << std::endl;
			}
			else
			{
				outfitsMap[(*it)][outfit.outfitId] = outfit;
				if(!add)
					add = true;
			}
		}

		if(add)
			allOutfits.push_back(outfit);
	}

	return true;
}

bool Outfits::loadFromXml()
{
	xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML, "roupas.xml").c_str());
	if(!doc)
	{
		std::cout << "[Warning - Outfits::loadFromXml] Cannot load outfits file, using defaults." << std::endl;
		std::cout << getLastXMLError() << std::endl;
		return false;
	}

	xmlNodePtr p, root = xmlDocGetRootElement(doc);
	if(xmlStrcmp(root->name,(const xmlChar*)"outfits"))
	{
		std::cout << "[Error - Outfits::loadFromXml] Malformed outfits file." << std::endl;
		xmlFreeDoc(doc);
		return false;
	}

	p = root->children;
	while(p)
	{
		parseOutfitNode(p);
		p = p->next;
	}

	xmlFreeDoc(doc);
	return true;
}

uint32_t Outfits::getOutfitId(uint32_t lookType)
{
	for(OutfitList::iterator it = allOutfits.begin(); it != allOutfits.end(); ++it)
	{
		if(it->lookType == lookType)
			return it->outfitId;
	}

	return 0;
}

bool Outfits::getOutfit(uint32_t lookType, Outfit& outfit)
{
	for(OutfitList::iterator it = allOutfits.begin(); it != allOutfits.end(); ++it)
	{
		if(it->lookType != lookType)
			continue;

		outfit = *it;
		return true;
	}

	return false;
}

bool Outfits::getOutfit(uint32_t outfitId, uint16_t sex, Outfit& outfit)
{
	OutfitMap map = outfitsMap[sex];
	OutfitMap::iterator it = map.find(outfitId);
	if(it == map.end())
		return false;

	outfit = it->second;
	return true;
}

bool Outfits::addAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex, uint16_t addons)
{
	Player* player = g_game.getPlayerByID(playerId);
	if(!player || player->isRemoved())
		return false;

	OutfitMap map = outfitsMap[sex];
	OutfitMap::iterator it = map.find(outfitId);
	if(it == map.end())
		return false;

	Outfit outfit = it->second;
	if(outfit.requirement != (AddonRequirement_t)addons && (outfit.requirement != REQUIREMENT_ANY || !addons))
		return false;

	if(outfit.invisible)
	{
		Condition* condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_INVISIBLE, -1, 0);
		player->addCondition(condition);
	}

	if(outfit.manaShield)
	{
		Condition* condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_MANASHIELD, -1, 0);
		player->addCondition(condition);
	}

	if(outfit.speed)
		g_game.changeSpeed(player, outfit.speed);

	if(outfit.conditionSuppressions)
	{
		player->setConditionSuppressions(outfit.conditionSuppressions, false);
		player->sendIcons();
	}

	if(outfit.regeneration)
	{
		Condition* condition = Condition::createCondition(CONDITIONID_OUTFIT, CONDITION_REGENERATION, -1, 0);
		if(outfit.healthGain)
			condition->setParam(CONDITIONPARAM_HEALTHGAIN, outfit.healthGain);

		if(outfit.healthTicks)
			condition->setParam(CONDITIONPARAM_HEALTHTICKS, outfit.healthTicks);

		if(outfit.manaGain)
			condition->setParam(CONDITIONPARAM_MANAGAIN, outfit.manaGain);

		if(outfit.manaTicks)
			condition->setParam(CONDITIONPARAM_MANATICKS, outfit.manaTicks);

		player->addCondition(condition);
	}

	bool needUpdateSkills = false;
	for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		if(outfit.skills[i])
		{
			needUpdateSkills = true;
			player->setVarSkill((skills_t)i, outfit.skills[i]);
		}

		if(outfit.skillsPercent[i])
		{
			needUpdateSkills = true;
			player->setVarSkill((skills_t)i, (int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((outfit.skillsPercent[i] - 100) / 100.f)));
		}
	}

	if(needUpdateSkills)
		player->sendSkills();

	bool needUpdateStats = false;
	for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s)
	{
		if(outfit.stats[s])
		{
			needUpdateStats = true;
			player->setVarStats((stats_t)s, outfit.stats[s]);
		}

		if(outfit.statsPercent[s])
		{
			needUpdateStats = true;
			player->setVarStats((stats_t)s, (int32_t)(player->getDefaultStats((stats_t)s) * ((outfit.statsPercent[s] - 100) / 100.f)));
		}
	}

	if(needUpdateStats)
		player->sendStats();

	return true;
}

bool Outfits::removeAttributes(uint32_t playerId, uint32_t outfitId, uint16_t sex)
{
	Player* player = g_game.getPlayerByID(playerId);
	if(!player || player->isRemoved())
		return false;

	OutfitMap map = outfitsMap[sex];
	OutfitMap::iterator it = map.find(outfitId);
	if(it == map.end())
		return false;

	Outfit outfit = it->second;
	if(outfit.invisible)
		player->removeCondition(CONDITION_INVISIBLE, CONDITIONID_OUTFIT);

	if(outfit.manaShield)
		player->removeCondition(CONDITION_MANASHIELD, CONDITIONID_OUTFIT);

	if(outfit.speed)
		g_game.changeSpeed(player, -outfit.speed);

	if(outfit.conditionSuppressions)
	{
		player->setConditionSuppressions(outfit.conditionSuppressions, true);
		player->sendIcons();
	}

	if(outfit.regeneration)
		player->removeCondition(CONDITION_REGENERATION, CONDITIONID_OUTFIT);

	bool needUpdateSkills = false;
	for(uint32_t i = SKILL_FIRST; i <= SKILL_LAST; ++i)
	{
		if(outfit.skills[i])
		{
			needUpdateSkills = true;
			player->setVarSkill((skills_t)i, -outfit.skills[i]);
		}

		if(outfit.skillsPercent[i])
		{
			needUpdateSkills = true;
			player->setVarSkill((skills_t)i, -(int32_t)(player->getSkill((skills_t)i, SKILL_LEVEL) * ((outfit.skillsPercent[i] - 100) / 100.f)));
		}
	}

	if(needUpdateSkills)
		player->sendSkills();

	bool needUpdateStats = false;
	for(uint32_t s = STAT_FIRST; s <= STAT_LAST; ++s)
	{
		if(outfit.stats[s])
		{
			needUpdateStats = true;
			player->setVarStats((stats_t)s, -outfit.stats[s]);
		}

		if(outfit.statsPercent[s])
		{
			needUpdateStats = true;
			player->setVarStats((stats_t)s, -(int32_t)(player->getDefaultStats((stats_t)s) * ((outfit.statsPercent[s] - 100) / 100.f)));
		}
	}

	if(needUpdateStats)
		player->sendStats();

	return true;
}

int16_t Outfits::getOutfitAbsorb(uint32_t lookType, uint16_t sex, CombatType_t combat)
{
	OutfitMap map = outfitsMap[sex];
	if(!map.size())
		return 0;

	for(OutfitMap::iterator it = map.begin(); it != map.end(); ++it)
	{
		if(it->second.lookType == lookType)
			return it->second.absorb[combat];
	}

	return 0;
}

int16_t Outfits::getOutfitReflect(uint32_t lookType, uint16_t sex, CombatType_t combat)
{
	OutfitMap map = outfitsMap[sex];
	if(!map.size())
		return 0;

	for(OutfitMap::iterator it = map.begin(); it != map.end(); ++it)
	{
		if(it->second.lookType != lookType)
			continue;

		if(it->second.reflect[REFLECT_CHANCE][combat] < random_range(0, 100))
			return it->second.reflect[REFLECT_PERCENT][combat];
	}

	return 0;
}
 
tools.CPP:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include <iostream>
#include <iomanip>

#include "tools.h"
#include "md5.h"
#include "sha1.h"

#include "vocation.h"
#include "configmanager.h"
extern ConfigManager g_config;

std::string transformToSHA1(std::string plainText, bool upperCase)
{
	SHA1 sha1;
	unsigned sha1Hash[5];
	std::stringstream hexStream;

	sha1.Input((const uint8_t*)plainText.c_str(), plainText.length());
	sha1.Result(sha1Hash);

	hexStream.flags(std::ios::hex | std::ios::uppercase);
	for(uint32_t i = 0; i < 5; ++i)
		hexStream << std::setw(8) << std::setfill('0') << (uint32_t)sha1Hash[i];

	std::string hexStr = hexStream.str();
	if(!upperCase)
		toLowerCaseString(hexStr);

	return hexStr;
}

std::string transformToMD5(std::string plainText, bool upperCase)
{
	MD5_CTX m_md5;
	std::stringstream hexStream;

	MD5Init(&m_md5, 0);
	MD5Update(&m_md5, (const uint8_t*)plainText.c_str(), plainText.length());
	MD5Final(&m_md5);

	hexStream.flags(std::ios::hex | std::ios::uppercase);
	for(uint32_t i = 0; i < 16; ++i)
		hexStream << std::setw(2) << std::setfill('0') << (uint32_t)m_md5.digest[i];

	std::string hexStr = hexStream.str();
	if(!upperCase)
		toLowerCaseString(hexStr);

	return hexStr;
}

void _encrypt(std::string& str, bool upperCase)
{
	switch(g_config.getNumber(ConfigManager::ENCRYPTION))
	{
		case ENCRYPTION_MD5:
			str = transformToMD5(str, upperCase);
			break;
		case ENCRYPTION_SHA1:
			str = transformToSHA1(str, upperCase);
			break;
		default:
		{
			if(upperCase)
				std::transform(str.begin(), str.end(), str.begin(), upchar);

			break;
		}
	}
}

bool encryptTest(std::string plain, std::string& hash)
{
	std::transform(hash.begin(), hash.end(), hash.begin(), upchar);
	_encrypt(plain, true);
	return plain == hash;
}

void replaceString(std::string& text, const std::string key, const std::string value)
{
	if(value.find(key) != std::string::npos) //don't allow infinite loops
		return;

	for(std::string::size_type keyStart = text.find(key); keyStart
		!= std::string::npos; keyStart = text.find(key))
		text.replace(keyStart, key.size(), value);
}

void trim_right(std::string& source, const std::string& t)
{
	source.erase(source.find_last_not_of(t)+1);
}

void trim_left(std::string& source, const std::string& t)
{
	source.erase(0, source.find_first_not_of(t));
}

void toLowerCaseString(std::string& source)
{
	std::transform(source.begin(), source.end(), source.begin(), tolower);
}

void toUpperCaseString(std::string& source)
{
	std::transform(source.begin(), source.end(), source.begin(), upchar);
}

std::string asLowerCaseString(const std::string& source)
{
	std::string s = source;
	toLowerCaseString(s);
	return s;
}

std::string asUpperCaseString(const std::string& source)
{
	std::string s = source;
	toUpperCaseString(s);
	return s;
}

bool booleanString(std::string source)
{
	toLowerCaseString(source);
	return (source == "yes" || source == "true" || atoi(source.c_str()) > 0);
}

bool readXMLInteger(xmlNodePtr node, const char* tag, int& value)
{
	char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag);
	if(!nodeValue)
		return false;

	value = atoi(nodeValue);
	xmlFree(nodeValue);
	return true;
}

#if defined WINDOWS && !defined __GNUC__
bool readXMLInteger(xmlNodePtr node, const char* tag, int32_t& value)
{
	char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag);
	if(!nodeValue)
		return false;

	value = atoi(nodeValue);
	xmlFree(nodeValue);
	return true;
}
#endif

bool readXMLInteger64(xmlNodePtr node, const char* tag, int64_t& value)
{
	char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag);
	if(!nodeValue)
		return false;

	value = atoll(nodeValue);
	xmlFree(nodeValue);
	return true;
}

bool readXMLFloat(xmlNodePtr node, const char* tag, float& value)
{
	char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag);
	if(!nodeValue)
		return false;

	value = atof(nodeValue);
	xmlFree(nodeValue);
	return true;
}

bool readXMLString(xmlNodePtr node, const char* tag, std::string& value)
{
	char* nodeValue = (char*)xmlGetProp(node, (xmlChar*)tag);
	if(!nodeValue)
		return false;

	if(!utf8ToLatin1(nodeValue, value))
		value = nodeValue;

	xmlFree(nodeValue);
	return true;
}

bool readXMLContentString(xmlNodePtr node, std::string& value)
{
	char* nodeValue = (char*)xmlNodeGetContent(node);
	if(!nodeValue)
		return false;

	if(!utf8ToLatin1(nodeValue, value))
		value = nodeValue;

	xmlFree(nodeValue);
	return true;
}

bool parseXMLContentString(xmlNodePtr node, std::string& value)
{
	bool result = false;
	std::string compareValue;
	while(node)
	{
		if(xmlStrcmp(node->name, (const xmlChar*)"text") && node->type != XML_CDATA_SECTION_NODE)
		{
			node = node->next;
			continue;
		}

		if(!readXMLContentString(node, compareValue))
		{
			node = node->next;
			continue;
		}

		trim_left(compareValue, "\r");
		trim_left(compareValue, "\n");
		trim_left(compareValue, " ");
		if(compareValue.length() > value.length())
		{
			value = compareValue;
			if(!result)
				result = true;
		}

		node = node->next;
	}

	return result;
}

std::string getLastXMLError()
{
	std::stringstream ss;
	xmlErrorPtr lastError = xmlGetLastError();
	if(lastError->line)
		ss << "Line: " << lastError->line << ", ";

	ss << "Info: " << lastError->message << std::endl;
	return ss.str();
}

bool utf8ToLatin1(char* intext, std::string& outtext)
{
	outtext = "";
	if(!intext)
		return false;

	int32_t inlen = strlen(intext);
	if(!inlen)
		return false;

	int32_t outlen = inlen * 2;
	uint8_t* outbuf = new uint8_t[outlen];

	int32_t res = UTF8Toisolat1(outbuf, &outlen, (uint8_t*)intext, &inlen);
	if(res < 0)
	{
		delete[] outbuf;
		return false;
	}

	outbuf[outlen] = '\0';
	outtext = (char*)outbuf;

	delete[] outbuf;
	return true;
}

StringVec explodeString(const std::string& string, const std::string& separator)
{
	StringVec returnVector;
	size_t start = 0, end = 0;
	while((end = string.find(separator, start)) != std::string::npos)
	{
		returnVector.push_back(string.substr(start, end - start));
		start = end + separator.size();
	}

	returnVector.push_back(string.substr(start));
	return returnVector;
}

IntegerVec vectorAtoi(StringVec stringVector)
{
	IntegerVec returnVector;
	for(StringVec::iterator it = stringVector.begin(); it != stringVector.end(); ++it)
		returnVector.push_back(atoi((*it).c_str()));

	return returnVector;
}

bool hasBitSet(uint32_t flag, uint32_t flags)
{
	return ((flags & flag) == flag);
}

int32_t round(float v)
{
	int32_t t = (int32_t)std::floor(v);
	if((v - t) > 0.5)
		return t + 1;

	return t;
}

uint32_t rand24b()
{
	return ((rand() << 12) ^ (rand())) & (0xFFFFFF);
}

float box_muller(float m, float s)
{
	// normal random variate generator
	// mean m, standard deviation s
	float x1, x2, w, y1;
	static float y2;

	static bool useLast = false;
	if(useLast) // use value from previous call
	{
		y1 = y2;
		useLast = false;
		return (m + y1 * s);
	}

	do
	{
		double r1 = (((float)(rand()) / RAND_MAX));
		double r2 = (((float)(rand()) / RAND_MAX));

		x1 = 2.0 * r1 - 1.0;
		x2 = 2.0 * r2 - 1.0;
		w = x1 * x1 + x2 * x2;
	}
	while(w >= 1.0);
	w = sqrt((-2.0 * log(w)) / w);

	y1 = x1 * w;
	y2 = x2 * w;

	useLast = true;
	return (m + y1 * s);
}

int32_t random_range(int32_t lowestNumber, int32_t highestNumber, DistributionType_t type /*= DISTRO_UNIFORM*/)
{
	if(highestNumber == lowestNumber)
		return lowestNumber;

	if(lowestNumber > highestNumber)
		std::swap(lowestNumber, highestNumber);

	switch(type)
	{
		case DISTRO_UNIFORM:
			return (lowestNumber + ((int32_t)rand24b() % (highestNumber - lowestNumber + 1)));
		case DISTRO_NORMAL:
			return (lowestNumber + int32_t(float(highestNumber - lowestNumber) * (float)std::min((float)1, std::max((float)0, box_muller(0.5, 0.25)))));
		default:
			break;
	}

	const float randMax = 16777216;
	return (lowestNumber + int32_t(float(highestNumber - lowestNumber) * float(1.f - sqrt((1.f * rand24b()) / randMax))));
}

char upchar(char character)
{
	if((character >= 97 && character <= 122) || (character <= -1 && character >= -32))
		character -= 32;

	return character;
}

bool isNumber(char character)
{
	return (character >= 48 && character <= 57);
}

bool isLowercaseLetter(char character)
{
	return (character >= 97 && character <= 122);
}

bool isUppercaseLetter(char character)
{
	return (character >= 65 && character <= 90);
}

bool isPasswordCharacter(char character)
{
	return ((character >= 33 && character <= 47) || (character >= 58 && character <= 64) || (character >= 91 && character <= 96) || (character >= 123 && character <= 126));
}

bool isValidAccountName(std::string text)
{
	toLowerCaseString(text);

	uint32_t textLength = text.length();
	for(uint32_t size = 0; size < textLength; size++)
	{
		if(!isLowercaseLetter(text[size]) && !isNumber(text[size]))
			return false;
	}

	return true;
}

bool isValidPassword(std::string text)
{
	toLowerCaseString(text);

	uint32_t textLength = text.length();
	for(uint32_t size = 0; size < textLength; size++)
	{
		if(!isLowercaseLetter(text[size]) && !isNumber(text[size]) && !isPasswordCharacter(text[size]))
			return false;
	}

	return true;
}

bool isValidName(std::string text, bool forceUppercaseOnFirstLetter/* = true*/)
{
	uint32_t textLength = text.length(), lenBeforeSpace = 1, lenBeforeQuote = 1, lenBeforeDash = 1, repeatedCharacter = 0;
	char lastChar = 32;
	if(forceUppercaseOnFirstLetter)
	{
		if(!isUppercaseLetter(text[0]))
			return false;
	}
	else if(!isLowercaseLetter(text[0]) && !isUppercaseLetter(text[0]))
		return false;

	for(uint32_t size = 1; size < textLength; size++)
	{
		if(text[size] != 32)
		{
			lenBeforeSpace++;

			if(text[size] != 39)
				lenBeforeQuote++;
			else
			{
				if(lenBeforeQuote <= 1 || size == textLength - 1 || text[size + 1] == 32)
					return false;

				lenBeforeQuote = 0;
			}

			if(text[size] != 45)
				lenBeforeDash++;
			else
			{
				if(lenBeforeDash <= 1 || size == textLength - 1 || text[size + 1] == 32)
					return false;

				lenBeforeDash = 0;
			}

			if(text[size] == lastChar)
			{
				repeatedCharacter++;
				if(repeatedCharacter > 2)
					return false;
			}
			else
				repeatedCharacter = 0;

			lastChar = text[size];
		}
		else
		{
			if(lenBeforeSpace <= 1 || size == textLength - 1 || text[size + 1] == 32)
				return false;

			lenBeforeSpace = lenBeforeQuote = lenBeforeDash = 0;
		}

		if(!(isLowercaseLetter(text[size]) || text[size] == 32 || text[size] == 39 || text[size] == 45
			|| (isUppercaseLetter(text[size]) && text[size - 1] == 32)))
			return false;
	}

	return true;
}

bool isNumbers(std::string text)
{
	uint32_t textLength = text.length();
	for(uint32_t size = 0; size < textLength; size++)
	{
		if(!isNumber(text[size]))
			return false;
	}

	return true;
}

bool checkText(std::string text, std::string str)
{
	trimString(text);
	return asLowerCaseString(text) == str;
}

std::string generateRecoveryKey(int32_t fieldCount, int32_t fieldLenght)
{
	std::stringstream key;
	int32_t i = 0, j = 0, lastNumber = 99, number = 0;

	char character = 0, lastCharacter = 0;
	bool madeNumber = false, madeCharacter = false;
	do
	{
		do
		{
			madeNumber = madeCharacter = false;
			if((bool)random_range(0, 1))
			{
				number = random_range(2, 9);
				if(number != lastNumber)
				{
					key << number;
					lastNumber = number;
					madeNumber = true;
				}
			}
			else
			{
				character = (char)random_range(65, 90);
				if(character != lastCharacter)
				{
					key << character;
					lastCharacter = character;
					madeCharacter = true;
				}
			}
		}
		while((!madeCharacter && !madeNumber) ? true : (++j && j < fieldLenght));
		lastCharacter = character = number = j = 0;

		lastNumber = 99;
		if(i < fieldCount - 1)
			key << "-";
	}
	while(++i && i < fieldCount);
	return key.str();
}

std::string trimString(std::string& str)
{
	str.erase(str.find_last_not_of(" ") + 1);
	return str.erase(0, str.find_first_not_of(" "));
}

std::string parseParams(tokenizer::iterator &it, tokenizer::iterator end)
{
	if(it == end)
		return "";

	std::string tmp = (*it);
	++it;
	if(tmp[0] == '"')
	{
		tmp.erase(0, 1);
		while(it != end && tmp[tmp.length() - 1] != '"')
		{
			tmp += " " + (*it);
			++it;
		}

		if(tmp.length() > 0 && tmp[tmp.length() - 1] == '"')
			tmp.erase(tmp.length() - 1);
	}

	return tmp;
}

std::string formatDate(time_t _time/* = 0*/)
{
	char buffer[21];
	if(!_time)
		_time = time(NULL);

	const tm* tms = localtime(&_time);
	if(tms)
		sprintf(buffer, "%02d/%02d/%04d %02d:%02d:%02d", tms->tm_mday, tms->tm_mon + 1, tms->tm_year + 1900, tms->tm_hour, tms->tm_min, tms->tm_sec);
	else
		sprintf(buffer, "UNIX Time: %d", (int32_t)_time);

	return buffer;
}

std::string formatDateShort(time_t _time, bool detailed/* = false*/)
{
	char buffer[21];
	if(!_time)
		_time = time(NULL);

	const tm* tms = localtime(&_time);
	if(tms)
	{
		std::string format = "%d %b %Y";
		if(detailed)
			format += " %H:%M:%S";

		strftime(buffer, 25, format.c_str(), tms);
	}
	else
		sprintf(buffer, "UNIX Time: %d", (int32_t)_time);

	return buffer;
}

std::string formatTime(int32_t hours, int32_t minutes)
{
	std::stringstream time;
	if(hours)
		time << hours << " " << (hours > 1 ? "hours" : "hour") << (minutes ? " and " : "");

	if(minutes)
		time << minutes << " " << (minutes > 1 ? "minutes" : "minute");

	return time.str();
}

std::string convertIPAddress(uint32_t ip)
{
	char buffer[17];
	sprintf(buffer, "%d.%d.%d.%d", ip & 0xFF, (ip >> 8) & 0xFF, (ip >> 16) & 0xFF, (ip >> 24));
	return buffer;
}

Skulls_t getSkull(std::string strValue)
{
	std::string tmpStrValue = asLowerCaseString(strValue);
	if(tmpStrValue == "black" || tmpStrValue == "5")
		return SKULL_BLACK;
	else if(tmpStrValue == "red" || tmpStrValue == "4")
		return SKULL_RED;
	else if(tmpStrValue == "white" || tmpStrValue == "3")
		return SKULL_WHITE;
	else if(tmpStrValue == "green" || tmpStrValue == "2")
		return SKULL_GREEN;
	else if(tmpStrValue == "yellow" || tmpStrValue == "1")
		return SKULL_YELLOW;

	return SKULL_NONE;
}

PartyShields_t getPartyShield(std::string strValue)
{
	std::string tmpStrValue = asLowerCaseString(strValue);
	if(tmpStrValue == "whitenoshareoff" || tmpStrValue == "10")
		return SHIELD_YELLOW_NOSHAREDEXP;
	else if(tmpStrValue == "blueshareoff" || tmpStrValue == "9")
		return SHIELD_BLUE_NOSHAREDEXP;
	else if(tmpStrValue == "yellowshareblink" || tmpStrValue == "8")
		return SHIELD_YELLOW_NOSHAREDEXP_BLINK;
	else if(tmpStrValue == "blueshareblink" || tmpStrValue == "7")
		return SHIELD_BLUE_NOSHAREDEXP_BLINK;
	else if(tmpStrValue == "yellowshareon" || tmpStrValue == "6")
		return SHIELD_YELLOW_SHAREDEXP;
	else if(tmpStrValue == "blueshareon" || tmpStrValue == "5")
		return SHIELD_BLUE_SHAREDEXP;
	else if(tmpStrValue == "yellow" || tmpStrValue == "4")
		return SHIELD_YELLOW;
	else if(tmpStrValue == "blue" || tmpStrValue == "3")
		return SHIELD_BLUE;
	else if(tmpStrValue == "whiteyellow" || tmpStrValue == "2")
		return SHIELD_WHITEYELLOW;
	else if(tmpStrValue == "whiteblue" || tmpStrValue == "1")
		return SHIELD_WHITEBLUE;

	return SHIELD_NONE;
}

Direction getDirection(std::string string)
{
	if(string == "north" || string == "n" || string == "0")
		return NORTH;
	else if(string == "east" || string == "e" || string == "1")
		return EAST;
	else if(string == "south" || string == "s" || string == "2")
		return SOUTH;
	else if(string == "west" || string == "w" || string == "3")
		return WEST;
	else if(string == "southwest" || string == "south west" || string == "south-west" || string == "sw" || string == "4")
		return SOUTHWEST;
	else if(string == "southeast" || string == "south east" || string == "south-east" || string == "se" || string == "5")
		return SOUTHEAST;
	else if(string == "northwest" || string == "north west" || string == "north-west" || string == "nw" || string == "6")
		return NORTHWEST;
	else if(string == "northeast" || string == "north east" || string == "north-east" || string == "ne" || string == "7")
		return NORTHEAST;

	return SOUTH;
}

Direction getDirectionTo(Position pos1, Position pos2, bool extended/* = true*/)
{
	Direction direction = NORTH;
	if(pos1.x > pos2.x)
	{
		direction = WEST;
		if(extended)
		{
			if(pos1.y > pos2.y)
				direction = NORTHWEST;
			else if(pos1.y < pos2.y)
				direction = SOUTHWEST;
		}
	}
	else if(pos1.x < pos2.x)
	{
		direction = EAST;
		if(extended)
		{
			if(pos1.y > pos2.y)
				direction = NORTHEAST;
			else if(pos1.y < pos2.y)
				direction = SOUTHEAST;
		}
	}
	else
	{
		if(pos1.y > pos2.y)
			direction = NORTH;
		else if(pos1.y < pos2.y)
			direction = SOUTH;
	}

	return direction;
}

Direction getReverseDirection(Direction dir)
{
	switch(dir)
	{
		case NORTH:
			return SOUTH;
		case SOUTH:
			return NORTH;
		case WEST:
			return EAST;
		case EAST:
			return WEST;
		case SOUTHWEST:
			return NORTHEAST;
		case NORTHWEST:
			return SOUTHEAST;
		case NORTHEAST:
			return SOUTHWEST;
		case SOUTHEAST:
			return NORTHWEST;
	}

	return SOUTH;
}

Position getNextPosition(Direction direction, Position pos)
{
	switch(direction)
	{
		case NORTH:
			pos.y--;
			break;
		case SOUTH:
			pos.y++;
			break;
		case WEST:
			pos.x--;
			break;
		case EAST:
			pos.x++;
			break;
		case SOUTHWEST:
			pos.x--;
			pos.y++;
			break;
		case NORTHWEST:
			pos.x--;
			pos.y--;
			break;
		case SOUTHEAST:
			pos.x++;
			pos.y++;
			break;
		case NORTHEAST:
			pos.x++;
			pos.y--;
			break;
	}

	return pos;
}

struct AmmoTypeNames
{
	const char* name;
	Ammo_t ammoType;
};

struct MagicEffectNames
{
	const char* name;
	MagicEffect_t magicEffect;
};

struct ShootTypeNames
{
	const char* name;
	ShootEffect_t shootType;
};

struct CombatTypeNames
{
	const char* name;
	CombatType_t combatType;
};

struct AmmoActionNames
{
	const char* name;
	AmmoAction_t ammoAction;
};

struct FluidTypeNames
{
	const char* name;
	FluidTypes_t fluidType;
};

struct SkillIdNames
{
	const char* name;
	skills_t skillId;
};

MagicEffectNames magicEffectNames[] =
{
	{"redspark",		MAGIC_EFFECT_DRAW_BLOOD},
	{"bluebubble",		MAGIC_EFFECT_LOSE_ENERGY},
	{"poff",		MAGIC_EFFECT_POFF},
	{"yellowspark",		MAGIC_EFFECT_BLOCKHIT},
	{"explosionarea",	MAGIC_EFFECT_EXPLOSION_AREA},
	{"explosion",		MAGIC_EFFECT_EXPLOSION_DAMAGE},
	{"firearea",		MAGIC_EFFECT_FIRE_AREA},
	{"yellowbubble",	MAGIC_EFFECT_YELLOW_RINGS},
	{"greenbubble",		MAGIC_EFFECT_POISON_RINGS},
	{"blackspark",		MAGIC_EFFECT_HIT_AREA},
	{"teleport",		MAGIC_EFFECT_TELEPORT},
	{"energy",		MAGIC_EFFECT_ENERGY_DAMAGE},
	{"blueshimmer",		MAGIC_EFFECT_WRAPS_BLUE},
	{"redshimmer",		MAGIC_EFFECT_WRAPS_RED},
	{"greenshimmer",	MAGIC_EFFECT_WRAPS_GREEN},
	{"fire",		MAGIC_EFFECT_HITBY_FIRE},
	{"greenspark",		MAGIC_EFFECT_POISON},
	{"mortarea",		MAGIC_EFFECT_MORT_AREA},
	{"greennote",		MAGIC_EFFECT_SOUND_GREEN},
	{"rednote",		MAGIC_EFFECT_SOUND_RED},
	{"poison",		MAGIC_EFFECT_POISON_AREA},
	{"yellownote",		MAGIC_EFFECT_SOUND_YELLOW},
	{"purplenote",		MAGIC_EFFECT_SOUND_PURPLE},
	{"bluenote",		MAGIC_EFFECT_SOUND_BLUE},
	{"whitenote",		MAGIC_EFFECT_SOUND_WHITE},
	{"bubbles",		MAGIC_EFFECT_BUBBLES},
	{"dice",		MAGIC_EFFECT_CRAPS},
	{"giftwraps",		MAGIC_EFFECT_GIFT_WRAPS},
	{"yellowfirework",	MAGIC_EFFECT_FIREWORK_YELLOW},
	{"redfirework",		MAGIC_EFFECT_FIREWORK_RED},
	{"bluefirework",	MAGIC_EFFECT_FIREWORK_BLUE},
	{"stun",		MAGIC_EFFECT_STUN},
	{"sleep",		MAGIC_EFFECT_SLEEP},
	{"watercreature",	MAGIC_EFFECT_WATERCREATURE},
	{"groundshaker",	MAGIC_EFFECT_GROUNDSHAKER},
	{"hearts",		MAGIC_EFFECT_HEARTS},
	{"fireattack",		MAGIC_EFFECT_FIREATTACK},
	{"energyarea",		MAGIC_EFFECT_ENERGY_AREA},
	{"smallclouds",		MAGIC_EFFECT_SMALLCLOUDS},
	{"holydamage",		MAGIC_EFFECT_HOLYDAMAGE},
	{"bigclouds",		MAGIC_EFFECT_BIGCLOUDS},
	{"icearea",		MAGIC_EFFECT_ICEAREA},
	{"icetornado",		MAGIC_EFFECT_ICETORNADO},
	{"iceattack",		MAGIC_EFFECT_ICEATTACK},
	{"stones",		MAGIC_EFFECT_STONES},
	{"smallplants",		MAGIC_EFFECT_SMALLPLANTS},
	{"carniphila",		MAGIC_EFFECT_CARNIPHILA},
	{"purpleenergy",	MAGIC_EFFECT_PURPLEENERGY},
	{"yellowenergy",	MAGIC_EFFECT_YELLOWENERGY},
	{"holyarea",		MAGIC_EFFECT_HOLYAREA},
	{"bigplants",		MAGIC_EFFECT_BIGPLANTS},
	{"cake",		MAGIC_EFFECT_CAKE},
	{"giantice",		MAGIC_EFFECT_GIANTICE},
	{"watersplash",		MAGIC_EFFECT_WATERSPLASH},
	{"plantattack",		MAGIC_EFFECT_PLANTATTACK},
	{"tutorialarrow",	MAGIC_EFFECT_TUTORIALARROW},
	{"tutorialsquare",	MAGIC_EFFECT_TUTORIALSQUARE},
	{"mirrorhorizontal",	MAGIC_EFFECT_MIRRORHORIZONTAL},
	{"mirrorvertical",	MAGIC_EFFECT_MIRRORVERTICAL},
	{"skullhorizontal",	MAGIC_EFFECT_SKULLHORIZONTAL},
	{"skullvertical",	MAGIC_EFFECT_SKULLVERTICAL},
	{"assassin",		MAGIC_EFFECT_ASSASSIN},
	{"stepshorizontal",	MAGIC_EFFECT_STEPSHORIZONTAL},
	{"bloodysteps",		MAGIC_EFFECT_BLOODYSTEPS},
	{"stepsvertical",	MAGIC_EFFECT_STEPSVERTICAL},
	{"yalaharighost",	MAGIC_EFFECT_YALAHARIGHOST},
	{"bats",		MAGIC_EFFECT_BATS},
	{"smoke",		MAGIC_EFFECT_SMOKE},
	{"insects",		MAGIC_EFFECT_INSECTS}
};

ShootTypeNames shootTypeNames[] =
{
	{"spear",		SHOOT_EFFECT_SPEAR},
	{"bolt",		SHOOT_EFFECT_BOLT},
	{"arrow",		SHOOT_EFFECT_ARROW},
	{"fire",		SHOOT_EFFECT_FIRE},
	{"energy",		SHOOT_EFFECT_ENERGY},
	{"poisonarrow",		SHOOT_EFFECT_POISONARROW},
	{"burstarrow",		SHOOT_EFFECT_BURSTARROW},
	{"throwingstar",	SHOOT_EFFECT_THROWINGSTAR},
	{"throwingknife",	SHOOT_EFFECT_THROWINGKNIFE},
	{"smallstone",		SHOOT_EFFECT_SMALLSTONE},
	{"death",		SHOOT_EFFECT_DEATH},
	{"largerock",		SHOOT_EFFECT_LARGEROCK},
	{"snowball",		SHOOT_EFFECT_SNOWBALL},
	{"powerbolt",		SHOOT_EFFECT_POWERBOLT},
	{"poison",		SHOOT_EFFECT_POISONFIELD},
	{"infernalbolt",	SHOOT_EFFECT_INFERNALBOLT},
	{"huntingspear",	SHOOT_EFFECT_HUNTINGSPEAR},
	{"enchantedspear",	SHOOT_EFFECT_ENCHANTEDSPEAR},
	{"redstar",		SHOOT_EFFECT_REDSTAR},
	{"greenstar",		SHOOT_EFFECT_GREENSTAR},
	{"royalspear",		SHOOT_EFFECT_ROYALSPEAR},
	{"sniperarrow",		SHOOT_EFFECT_SNIPERARROW},
	{"onyxarrow",		SHOOT_EFFECT_ONYXARROW},
	{"piercingbolt",	SHOOT_EFFECT_PIERCINGBOLT},
	{"whirlwindsword",	SHOOT_EFFECT_WHIRLWINDSWORD},
	{"whirlwindaxe",	SHOOT_EFFECT_WHIRLWINDAXE},
	{"whirlwindclub",	SHOOT_EFFECT_WHIRLWINDCLUB},
	{"etherealspear",	SHOOT_EFFECT_ETHEREALSPEAR},
	{"ice",			SHOOT_EFFECT_ICE},
	{"earth",		SHOOT_EFFECT_EARTH},
	{"holy",		SHOOT_EFFECT_HOLY},
	{"suddendeath",		SHOOT_EFFECT_SUDDENDEATH},
	{"flasharrow",		SHOOT_EFFECT_FLASHARROW},
	{"flammingarrow",	SHOOT_EFFECT_FLAMMINGARROW},
	{"flamingarrow",	SHOOT_EFFECT_FLAMMINGARROW},
	{"shiverarrow",		SHOOT_EFFECT_SHIVERARROW},
	{"energyball",		SHOOT_EFFECT_ENERGYBALL},
	{"smallice",		SHOOT_EFFECT_SMALLICE},
	{"smallholy",		SHOOT_EFFECT_SMALLHOLY},
	{"smallearth",		SHOOT_EFFECT_SMALLEARTH},
	{"eartharrow",		SHOOT_EFFECT_EARTHARROW},
	{"explosion",		SHOOT_EFFECT_EXPLOSION},
	{"cake",		SHOOT_EFFECT_CAKE}
};

CombatTypeNames combatTypeNames[] =
{
	{"physical",		COMBAT_PHYSICALDAMAGE},
	{"energy",		COMBAT_ENERGYDAMAGE},
	{"earth",		COMBAT_EARTHDAMAGE},
	{"fire",		COMBAT_FIREDAMAGE},
	{"undefined",		COMBAT_UNDEFINEDDAMAGE},
	{"lifedrain",		COMBAT_LIFEDRAIN},
	{"life drain",		COMBAT_LIFEDRAIN},
	{"manadrain",		COMBAT_MANADRAIN},
	{"mana drain",		COMBAT_MANADRAIN},
	{"healing",		COMBAT_HEALING},
	{"drown",		COMBAT_DROWNDAMAGE},
	{"ice",			COMBAT_ICEDAMAGE},
	{"holy",		COMBAT_HOLYDAMAGE},
	{"death",		COMBAT_DEATHDAMAGE}
};

AmmoTypeNames ammoTypeNames[] =
{
	{"spear",		AMMO_SPEAR},
	{"arrow",		AMMO_ARROW},
	{"poisonarrow",		AMMO_ARROW},
	{"burstarrow",		AMMO_ARROW},
	{"bolt",		AMMO_BOLT},
	{"powerbolt",		AMMO_BOLT},
	{"smallstone",		AMMO_STONE},
	{"largerock",		AMMO_STONE},
	{"throwingstar",	AMMO_THROWINGSTAR},
	{"throwingknife",	AMMO_THROWINGKNIFE},
	{"snowball",		AMMO_SNOWBALL},
	{"huntingspear",	AMMO_SPEAR},
	{"royalspear",		AMMO_SPEAR},
	{"enchantedspear",	AMMO_SPEAR},
	{"sniperarrow",		AMMO_ARROW},
	{"onyxarrow",		AMMO_ARROW},
	{"piercingbolt",	AMMO_BOLT},
	{"infernalbolt",	AMMO_BOLT},
	{"flasharrow",		AMMO_ARROW},
	{"flammingarrow",	AMMO_ARROW},
	{"flamingarrow",	AMMO_ARROW},
	{"shiverarrow",		AMMO_ARROW},
	{"eartharrow",		AMMO_ARROW},
	{"etherealspear",	AMMO_SPEAR}
};

AmmoActionNames ammoActionNames[] =
{
	{"move",		AMMOACTION_MOVE},
	{"moveback",		AMMOACTION_MOVEBACK},
	{"move back",		AMMOACTION_MOVEBACK},
	{"removecharge",	AMMOACTION_REMOVECHARGE},
	{"remove charge",	AMMOACTION_REMOVECHARGE},
	{"removecount",		AMMOACTION_REMOVECOUNT},
	{"remove count",	AMMOACTION_REMOVECOUNT}
};

FluidTypeNames fluidTypeNames[] =
{
	{"none",		FLUID_NONE},
	{"water",		FLUID_WATER},
	{"blood",		FLUID_BLOOD},
	{"beer",		FLUID_BEER},
	{"slime",		FLUID_SLIME},
	{"lemonade",		FLUID_LEMONADE},
	{"milk",		FLUID_MILK},
	{"mana",		FLUID_MANA},
	{"life",		FLUID_LIFE},
	{"oil",			FLUID_OIL},
	{"urine",		FLUID_URINE},
	{"coconutmilk",		FLUID_COCONUTMILK},
	{"coconut milk",	FLUID_COCONUTMILK},
	{"wine",		FLUID_WINE},
	{"mud",			FLUID_MUD},
	{"fruitjuice",		FLUID_FRUITJUICE},
	{"fruit juice",		FLUID_FRUITJUICE},
	{"lava",		FLUID_LAVA},
	{"rum",			FLUID_RUM},
	{"swamp",		FLUID_SWAMP}
};

SkillIdNames skillIdNames[] =
{
	{"fist",		SKILL_FIST},
	{"club",		SKILL_CLUB},
	{"sword",		SKILL_SWORD},
	{"axe",			SKILL_AXE},
	{"distance",		SKILL_DIST},
	{"dist",		SKILL_DIST},
	{"shielding",		SKILL_SHIELD},
	{"shield",		SKILL_SHIELD},
	{"fishing",		SKILL_FISH},
	{"fish",		SKILL_FISH},
	{"mineworker",		SKILL_MINE},
	{"level",		SKILL__LEVEL},
	{"magiclevel",		SKILL__MAGLEVEL},
	{"magic level",		SKILL__MAGLEVEL}
};

MagicEffect_t getMagicEffect(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(magicEffectNames) / sizeof(MagicEffectNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), magicEffectNames[i].name))
			return magicEffectNames[i].magicEffect;
	}

	return MAGIC_EFFECT_UNKNOWN;
}

ShootEffect_t getShootType(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(shootTypeNames) / sizeof(ShootTypeNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), shootTypeNames[i].name))
			return shootTypeNames[i].shootType;
	}

	return SHOOT_EFFECT_UNKNOWN;
}

CombatType_t getCombatType(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(combatTypeNames) / sizeof(CombatTypeNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), combatTypeNames[i].name))
			return combatTypeNames[i].combatType;
	}

	return COMBAT_NONE;
}

Ammo_t getAmmoType(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(ammoTypeNames) / sizeof(AmmoTypeNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), ammoTypeNames[i].name))
			return ammoTypeNames[i].ammoType;
	}

	return AMMO_NONE;
}

AmmoAction_t getAmmoAction(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(ammoActionNames) / sizeof(AmmoActionNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), ammoActionNames[i].name))
			return ammoActionNames[i].ammoAction;
	}

	return AMMOACTION_NONE;
}

FluidTypes_t getFluidType(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(fluidTypeNames) / sizeof(FluidTypeNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), fluidTypeNames[i].name))
			return fluidTypeNames[i].fluidType;
	}

	return FLUID_NONE;
}

skills_t getSkillId(const std::string& strValue)
{
	for(uint32_t i = 0; i < sizeof(skillIdNames) / sizeof(SkillIdNames); ++i)
	{
		if(!strcasecmp(strValue.c_str(), skillIdNames[i].name))
			return skillIdNames[i].skillId;
	}

	return SKILL_FIST;
}

std::string getCombatName(CombatType_t combatType)
{
	switch(combatType)
	{
		case COMBAT_PHYSICALDAMAGE:
			return "physical";
		case COMBAT_ENERGYDAMAGE:
			return "energy";
		case COMBAT_EARTHDAMAGE:
			return "earth";
		case COMBAT_FIREDAMAGE:
			return "fire";
		case COMBAT_UNDEFINEDDAMAGE:
			return "undefined";
		case COMBAT_LIFEDRAIN:
			return "life drain";
		case COMBAT_MANADRAIN:
			return "mana drain";
		case COMBAT_HEALING:
			return "healing";
		case COMBAT_DROWNDAMAGE:
			return "drown";
		case COMBAT_ICEDAMAGE:
			return "ice";
		case COMBAT_HOLYDAMAGE:
			return "holy";
		case COMBAT_DEATHDAMAGE:
			return "death";
		default:
			break;
	}

	return "unknown";
}

std::string getSkillName(uint16_t skillId, bool suffix/* = true*/)
{
	switch(skillId)
	{
		case SKILL_FIST:
		{
			std::string tmp = "fist";
			if(suffix)
				tmp += " fighting";

			return tmp;
		}
		case SKILL_CLUB:
		{
			std::string tmp = "club";
			if(suffix)
				tmp += " fighting";

			return tmp;
		}
		case SKILL_SWORD:
		{
			std::string tmp = "sword";
			if(suffix)
				tmp += " fighting";

			return tmp;
		}
		case SKILL_AXE:
		{
			std::string tmp = "axe";
			if(suffix)
				tmp += " fighting";

			return tmp;
		}
		case SKILL_DIST:
		{
			std::string tmp = "distance";
			if(suffix)
				tmp += " fighting";

			return tmp;
		}
		case SKILL_SHIELD:
			return "shielding";
		case SKILL_FISH:
			return "fishing";
		case SKILL_MINE:
			return "mineworker";
		case SKILL__MAGLEVEL:
			return "magic level";
		case SKILL__LEVEL:
			return "level";
		default:
			break;
	}

	return "unknown";
}

std::string getReason(int32_t reasonId)
{
	switch(reasonId)
	{
		case 0:
			return "Offensive Name";
		case 1:
			return "Invalid Name Format";
		case 2:
			return "Unsuitable Name";
		case 3:
			return "Name Inciting Rule Violation";
		case 4:
			return "Offensive Statement";
		case 5:
			return "Spamming";
		case 6:
			return "Illegal Advertising";
		case 7:
			return "Off-Topic Public Statement";
		case 8:
			return "Non-English Public Statement";
		case 9:
			return "Inciting Rule Violation";
		case 10:
			return "Bug Abuse";
		case 11:
			return "Game Weakness Abuse";
		case 12:
			return "Using Unofficial Software to Play";
		case 13:
			return "Hacking";
		case 14:
			return "Multi-Clienting";
		case 15:
			return "Account Trading or Sharing";
		case 16:
			return "Threatening Gamemaster";
		case 17:
			return "Pretending to Have Influence on Rule Enforcement";
		case 18:
			return "False Report to Gamemaster";
		case 19:
			return "Destructive Behaviour";
		case 20:
			return "Excessive Unjustified Player Killing";
		default:
			break;
	}

	return "Unknown Reason";
}

std::string getAction(ViolationAction_t actionId, bool ipBanishment)
{
	std::string action = "Unknown";
	switch(actionId)
	{
		case ACTION_NOTATION:
			action = "Notation";
			break;
		case ACTION_NAMEREPORT:
			action = "Name Report";
			break;
		case ACTION_BANISHMENT:
			action = "Banishment";
			break;
		case ACTION_BANREPORT:
			action = "Name Report + Banishment";
			break;
		case ACTION_BANFINAL:
			action = "Banishment + Final Warning";
			break;
		case ACTION_BANREPORTFINAL:
			action = "Name Report + Banishment + Final Warning";
			break;
		case ACTION_STATEMENT:
			action = "Statement Report";
			break;
		//internal use
		case ACTION_DELETION:
			action = "Deletion";
			break;
		case ACTION_NAMELOCK:
			action = "Name Lock";
			break;
		case ACTION_BANLOCK:
			action = "Name Lock + Banishment";
			break;
		case ACTION_BANLOCKFINAL:
			action = "Name Lock + Banishment + Final Warning";
			break;
		default:
			break;
	}

	if(ipBanishment)
		action += " + IP Banishment";

	return action;
}

std::string parseVocationString(StringVec vocStringVec)
{
	std::string str = "";
	if(!vocStringVec.empty())
	{
		for(StringVec::iterator it = vocStringVec.begin(); it != vocStringVec.end(); ++it)
		{
			if((*it) != vocStringVec.front())
			{
				if((*it) != vocStringVec.back())
					str += ", ";
				else
					str += " and ";
			}

			str += (*it);
			str += "s";
		}
	}

	return str;
}

bool parseVocationNode(xmlNodePtr vocationNode, VocationMap& vocationMap, StringVec& vocStringVec, std::string& errorStr)
{
	if(xmlStrcmp(vocationNode->name,(const xmlChar*)"vocation"))
		return true;

	int32_t vocationId = -1;
	std::string strValue, tmpStrValue;
	if(readXMLString(vocationNode, "name", strValue))
	{
		vocationId = Vocations::getInstance()->getVocationId(strValue);
		if(vocationId != -1)
		{
			vocationMap[vocationId] = true;
			int32_t promotedVocation = Vocations::getInstance()->getPromotedVocation(vocationId);
			if(promotedVocation != -1)
				vocationMap[promotedVocation] = true;
		}
		else
		{
			errorStr = "Wrong vocation name: " + strValue;
			return false;
		}
	}
	else if(readXMLString(vocationNode, "id", strValue))
	{
		IntegerVec intVector;
		if(!parseIntegerVec(strValue, intVector))
		{
			errorStr = "Invalid vocation id - '" + strValue + "'";
			return false;
		}

		size_t size = intVector.size();
		for(size_t i = 0; i < size; ++i)
		{
			Vocation* vocation = Vocations::getInstance()->getVocation(intVector[i]);
			if(vocation && vocation->getName() != "")
			{
				vocationId = vocation->getId();
				strValue = vocation->getName();

				vocationMap[vocationId] = true;
				int32_t promotedVocation = Vocations::getInstance()->getPromotedVocation(vocationId);
				if(promotedVocation != -1)
					vocationMap[promotedVocation] = true;
			}
			else
			{
				std::stringstream ss;
				ss << "Wrong vocation id: " << intVector[i];

				errorStr = ss.str();
				return false;
			}
		}
	}

	if(vocationId != -1 && (!readXMLString(vocationNode, "showInDescription", tmpStrValue) || booleanString(tmpStrValue)))
		vocStringVec.push_back(asLowerCaseString(strValue));

	return true;
}

bool parseIntegerVec(std::string str, IntegerVec& intVector)
{
	StringVec strVector = explodeString(str, ";");
	IntegerVec tmpIntVector;
	for(StringVec::iterator it = strVector.begin(); it != strVector.end(); ++it)
	{
		tmpIntVector = vectorAtoi(explodeString((*it), "-"));
		if(!tmpIntVector[0] && it->substr(0, 1) != "0")
			continue;

		intVector.push_back(tmpIntVector[0]);
		if(tmpIntVector.size() > 1)
		{
			while(tmpIntVector[0] < tmpIntVector[1])
				intVector.push_back(++tmpIntVector[0]);
		}
	}

	return true;
}

bool fileExists(const char* filename)
{
	FILE* f = fopen(filename, "rb");
	if(!f)
		return false;

	fclose(f);
	return true;
}

uint32_t adlerChecksum(uint8_t *data, size_t length)
{
	if(length > NETWORKMESSAGE_MAXSIZE || length < 0)
		return 0;

	const uint16_t adler = 65521;
	uint32_t a = 1, b = 0;
	while(length > 0)
	{
		size_t tmp = length > 5552 ? 5552 : length;
		length -= tmp;
		do
		{
			a += *data++;
			b += a;
		}
		while(--tmp);

		a %= adler;
		b %= adler;
	}

	return (b << 16) | a;
}

std::string getFilePath(FileType_t filetype, std::string filename)
{
	#ifdef __FILESYSTEM_HIERARCHY_STANDARD__
	std::string path = "/usr/share/tfs/";
	#endif
	std::string path = g_config.getString(ConfigManager::DATA_DIRECTORY);
	switch(filetype)
	{
		case FILE_TYPE_OTHER:
			path += filename;
			break;
		case FILE_TYPE_XML:
			path += "XML/" + filename;
			break;
		case FILE_TYPE_LOG:
			#ifndef __FILESYSTEM_HIERARCHY_STANDARD__
			path += "logs/" + filename;
			#else
			path = "/var/log/tfs/" + filename;
			#endif
			break;
		case FILE_TYPE_MOD:
		{
			#ifndef __FILESYSTEM_HIERARCHY_STANDARD__
			path = "mods/" + filename;
			#else
			path = "/etc/tfs/mods/" + filename;
			#endif
			break;
		}
		case FILE_TYPE_CONFIG:
		{
			#if defined(__FILESYSTEM_HIERARCHY_STANDARD__) && defined(__HOMEDIR_CONF__)
			if(fileExists("~/.tfs/" + filename))
				path = "~/.tfs/" + filename;
			else
				path = "/etc/tfs/" + filename;

			#elif defined(__FILESYSTEM_HIERARCHY_STANDARD__)
			path = "/etc/tfs/" + filename;
			#else
			path = filename;
			#endif
			break;
		}
		default:
			std::cout << "ERROR: Wrong file type!" << std::endl;
			break;
	}
	return path;
}
 
vocation.CPP:
Code:
////////////////////////////////////////////////////////////////////////
// OpenTibia - an opensource roleplaying game
////////////////////////////////////////////////////////////////////////
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.
////////////////////////////////////////////////////////////////////////
#include "otpch.h"
#include <iostream>

#include <libxml/xmlmemory.h>
#include <libxml/parser.h>

#include "vocation.h"
#include "tools.h"

Vocation Vocations::defVoc = Vocation();

void Vocations::clear()
{
	for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it)
		delete it->second;

	vocationsMap.clear();
}

bool Vocations::reload()
{
	clear();
	return loadFromXml();
}

bool Vocations::parseVocationNode(xmlNodePtr p)
{
	std::string strValue;
	int32_t intValue;
	float floatValue;
	if(xmlStrcmp(p->name, (const xmlChar*)"vocation"))
		return false;

	if(!readXMLInteger(p, "id", intValue))
	{
		std::cout << "[Error - Vocations::parseVocationNode] Missing vocation id." << std::endl;
		return false;
	}

	Vocation* voc = new Vocation(intValue);
	if(readXMLString(p, "name", strValue))
		voc->setName(strValue);

	if(readXMLString(p, "description", strValue))
		voc->setDescription(strValue);

	if(readXMLString(p, "needpremium", strValue))
		voc->setNeedPremium(booleanString(strValue));

	if(readXMLInteger(p, "gaincap", intValue) || readXMLInteger(p, "gaincapacity", intValue))
		voc->setGainCap(intValue);

	if(readXMLInteger(p, "gainhp", intValue) || readXMLInteger(p, "gainhealth", intValue))
		voc->setGain(GAIN_HEALTH, intValue);

	if(readXMLInteger(p, "gainmana", intValue))
		voc->setGain(GAIN_MANA, intValue);

	if(readXMLInteger(p, "gainhpticks", intValue) || readXMLInteger(p, "gainhealthticks", intValue))
		voc->setGainTicks(GAIN_HEALTH, intValue);

	if(readXMLInteger(p, "gainhpamount", intValue) || readXMLInteger(p, "gainhealthamount", intValue))
		voc->setGainAmount(GAIN_HEALTH, intValue);

	if(readXMLInteger(p, "gainmanaticks", intValue))
		voc->setGainTicks(GAIN_MANA, intValue);

	if(readXMLInteger(p, "gainmanaamount", intValue))
		voc->setGainAmount(GAIN_MANA, intValue);

	if(readXMLFloat(p, "manamultiplier", floatValue))
		voc->setMultiplier(MULTIPLIER_MANA, floatValue);

	if(readXMLInteger(p, "attackspeed", intValue))
		voc->setAttackSpeed(intValue);

	if(readXMLInteger(p, "basespeed", intValue))
		voc->setBaseSpeed(intValue);

	if(readXMLInteger(p, "soulmax", intValue))
		voc->setGain(GAIN_SOUL, intValue);

	if(readXMLInteger(p, "gainsoulamount", intValue))
		voc->setGainAmount(GAIN_SOUL, intValue);

	if(readXMLInteger(p, "gainsoulticks", intValue))
		voc->setGainTicks(GAIN_SOUL, intValue);

	if(readXMLString(p, "attackable", strValue))
		voc->setAttackable(booleanString(strValue));

	if(readXMLInteger(p, "fromvoc", intValue) || readXMLInteger(p, "fromvocation", intValue))
		voc->setFromVocation(intValue);

	if(readXMLInteger(p, "lessloss", intValue))
		voc->setLessLoss(intValue);

	for(xmlNodePtr configNode = p->children; configNode; configNode = configNode->next)
	{
		if(!xmlStrcmp(configNode->name, (const xmlChar*)"skill"))
		{
			if(readXMLFloat(configNode, "fist", floatValue))
				voc->setSkillMultiplier(SKILL_FIST, floatValue);

			if(readXMLInteger(configNode, "fistBase", intValue))
				voc->setSkillBase(SKILL_FIST, intValue);

			if(readXMLFloat(configNode, "club", floatValue))
				voc->setSkillMultiplier(SKILL_CLUB, floatValue);

			if(readXMLInteger(configNode, "clubBase", intValue))
				voc->setSkillBase(SKILL_CLUB, intValue);

			if(readXMLFloat(configNode, "mineworker", floatValue) || readXMLFloat(configNode, "mine", floatValue))
				voc->setSkillMultiplier(SKILL_MINE, floatValue);

			if(readXMLInteger(configNode, "mineworkerBase", intValue) || readXMLInteger(configNode, "mineBase", intValue))
				voc->setSkillBase(SKILL_MINE, intValue);

			if(readXMLFloat(configNode, "axe", floatValue))
				voc->setSkillMultiplier(SKILL_AXE, floatValue);

			if(readXMLInteger(configNode, "axeBase", intValue))
				voc->setSkillBase(SKILL_AXE, intValue);

			if(readXMLFloat(configNode, "sword", floatValue))
				voc->setSkillMultiplier(SKILL_SWORD, floatValue);

			if(readXMLInteger(configNode, "swordBase", intValue))
				voc->setSkillBase(SKILL_SWORD, intValue);

			if(readXMLFloat(configNode, "distance", floatValue) || readXMLFloat(configNode, "dist", floatValue))
				voc->setSkillMultiplier(SKILL_DIST, floatValue);

			if(readXMLInteger(configNode, "distanceBase", intValue) || readXMLInteger(configNode, "distBase", intValue))
				voc->setSkillBase(SKILL_DIST, intValue);

			if(readXMLFloat(configNode, "shielding", floatValue) || readXMLFloat(configNode, "shield", floatValue))
				voc->setSkillMultiplier(SKILL_SHIELD, floatValue);

			if(readXMLInteger(configNode, "shieldingBase", intValue) || readXMLInteger(configNode, "shieldBase", intValue))
				voc->setSkillBase(SKILL_SHIELD, intValue);

			if(readXMLFloat(configNode, "fishing", floatValue) || readXMLFloat(configNode, "fish", floatValue))
				voc->setSkillMultiplier(SKILL_FISH, floatValue);

			if(readXMLInteger(configNode, "fishingBase", intValue) || readXMLInteger(configNode, "fishBase", intValue))
				voc->setSkillBase(SKILL_FISH, intValue);

			if(readXMLFloat(configNode, "experience", floatValue) || readXMLFloat(configNode, "exp", floatValue))
				voc->setSkillMultiplier(SKILL__LEVEL, floatValue);

			if(readXMLInteger(configNode, "id", intValue))
			{
				skills_t skill = (skills_t)intValue;
				if(intValue < SKILL_FIRST || intValue >= SKILL__LAST)
				{
					std::cout << "[Error - Vocations::parseVocationNode] No valid skill id (" << intValue << ")." << std::endl;
					continue;
				}

				if(readXMLInteger(configNode, "base", intValue))
					voc->setSkillBase(skill, intValue);

				if(readXMLFloat(configNode, "multiplier", floatValue))
					voc->setSkillMultiplier(skill, floatValue);
			}
		}
		else if(!xmlStrcmp(configNode->name, (const xmlChar*)"formula"))
		{
			if(readXMLFloat(configNode, "meleeDamage", floatValue))
				voc->setMultiplier(MULTIPLIER_MELEE, floatValue);

			if(readXMLFloat(configNode, "distDamage", floatValue) || readXMLFloat(configNode, "distanceDamage", floatValue))
				voc->setMultiplier(MULTIPLIER_DISTANCE, floatValue);

			if(readXMLFloat(configNode, "wandDamage", floatValue) || readXMLFloat(configNode, "rodDamage", floatValue))
				voc->setMultiplier(MULTIPLIER_WAND, floatValue);

			if(readXMLFloat(configNode, "magDamage", floatValue) || readXMLFloat(configNode, "magicDamage", floatValue))
				voc->setMultiplier(MULTIPLIER_MAGIC, floatValue);

			if(readXMLFloat(configNode, "magHealingDamage", floatValue) || readXMLFloat(configNode, "magicHealingDamage", floatValue))
				voc->setMultiplier(MULTIPLIER_HEALING, floatValue);

			if(readXMLFloat(configNode, "defense", floatValue))
				voc->setMultiplier(MULTIPLIER_DEFENSE, floatValue);

			if(readXMLFloat(configNode, "magDefense", floatValue) || readXMLFloat(configNode, "magicDefense", floatValue))
				voc->setMultiplier(MULTIPLIER_MAGICDEFENSE, floatValue);

			if(readXMLFloat(configNode, "armor", floatValue))
				voc->setMultiplier(MULTIPLIER_ARMOR, floatValue);
		}
		else if(!xmlStrcmp(configNode->name, (const xmlChar*)"absorb"))
		{
			if(readXMLInteger(configNode, "percentAll", intValue))
			{
				for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
					voc->increaseAbsorb((CombatType_t)i, intValue);
			}

			if(readXMLInteger(configNode, "percentElements", intValue))
			{
				voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "percentMagic", intValue))
			{
				voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue);
				voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "percentEnergy", intValue))
				voc->increaseAbsorb(COMBAT_ENERGYDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentFire", intValue))
				voc->increaseAbsorb(COMBAT_FIREDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue))
				voc->increaseAbsorb(COMBAT_EARTHDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentIce", intValue))
				voc->increaseAbsorb(COMBAT_ICEDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentHoly", intValue))
				voc->increaseAbsorb(COMBAT_HOLYDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentDeath", intValue))
				voc->increaseAbsorb(COMBAT_DEATHDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentLifeDrain", intValue))
				voc->increaseAbsorb(COMBAT_LIFEDRAIN, intValue);

			if(readXMLInteger(configNode, "percentManaDrain", intValue))
				voc->increaseAbsorb(COMBAT_MANADRAIN, intValue);

			if(readXMLInteger(configNode, "percentDrown", intValue))
				voc->increaseAbsorb(COMBAT_DROWNDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentPhysical", intValue))
				voc->increaseAbsorb(COMBAT_PHYSICALDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentHealing", intValue))
				voc->increaseAbsorb(COMBAT_HEALING, intValue);

			if(readXMLInteger(configNode, "percentUndefined", intValue))
				voc->increaseAbsorb(COMBAT_UNDEFINEDDAMAGE, intValue);
		}
		else if(!xmlStrcmp(configNode->name, (const xmlChar*)"reflect"))
		{
			if(readXMLInteger(configNode, "percentAll", intValue))
			{
				for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
					voc->increaseReflect(REFLECT_PERCENT, (CombatType_t)i, intValue);
			}

			if(readXMLInteger(configNode, "percentElements", intValue))
			{
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "percentMagic", intValue))
			{
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "percentEnergy", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ENERGYDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentFire", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_FIREDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentPoison", intValue) || readXMLInteger(configNode, "percentEarth", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_EARTHDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentIce", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_ICEDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentHoly", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_HOLYDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentDeath", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_DEATHDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentLifeDrain", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_LIFEDRAIN, intValue);

			if(readXMLInteger(configNode, "percentManaDrain", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_MANADRAIN, intValue);

			if(readXMLInteger(configNode, "percentDrown", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_DROWNDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentPhysical", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_PHYSICALDAMAGE, intValue);

			if(readXMLInteger(configNode, "percentHealing", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_HEALING, intValue);

			if(readXMLInteger(configNode, "percentUndefined", intValue))
				voc->increaseReflect(REFLECT_PERCENT, COMBAT_UNDEFINEDDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceAll", intValue))
			{
				for(int32_t i = COMBAT_FIRST; i <= COMBAT_LAST; i++)
					voc->increaseReflect(REFLECT_CHANCE, (CombatType_t)i, intValue);
			}

			if(readXMLInteger(configNode, "chanceElements", intValue))
			{
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "chanceMagic", intValue))
			{
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue);
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue);
			}

			if(readXMLInteger(configNode, "chanceEnergy", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ENERGYDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceFire", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_FIREDAMAGE, intValue);

			if(readXMLInteger(configNode, "chancePoison", intValue) || readXMLInteger(configNode, "chanceEarth", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_EARTHDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceIce", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_ICEDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceHoly", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_HOLYDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceDeath", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_DEATHDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceLifeDrain", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_LIFEDRAIN, intValue);

			if(readXMLInteger(configNode, "chanceManaDrain", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_MANADRAIN, intValue);

			if(readXMLInteger(configNode, "chanceDrown", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_DROWNDAMAGE, intValue);

			if(readXMLInteger(configNode, "chancePhysical", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_PHYSICALDAMAGE, intValue);

			if(readXMLInteger(configNode, "chanceHealing", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_HEALING, intValue);

			if(readXMLInteger(configNode, "chanceUndefined", intValue))
				voc->increaseReflect(REFLECT_CHANCE, COMBAT_UNDEFINEDDAMAGE, intValue);
		}
	}

	vocationsMap[voc->getId()] = voc;
	return true;
}

bool Vocations::loadFromXml()
{
	xmlDocPtr doc = xmlParseFile(getFilePath(FILE_TYPE_XML,"classes.xml").c_str());
	if(!doc)
	{
		std::cout << "[Warning - Vocations::loadFromXml] Cannot load vocations file." << std::endl;
		std::cout << getLastXMLError() << std::endl;
		return false;
	}

	xmlNodePtr p, root = xmlDocGetRootElement(doc);
	if(xmlStrcmp(root->name,(const xmlChar*)"vocations"))
	{
		std::cout << "[Error - Vocations::loadFromXml] Malformed vocations file." << std::endl;
		xmlFreeDoc(doc);
		return false;
	}

	for(p = root->children; p; p = p->next)
		parseVocationNode(p);

	xmlFreeDoc(doc);
	return true;
}

Vocation* Vocations::getVocation(uint32_t vocId)
{
	VocationsMap::iterator it = vocationsMap.find(vocId);
	if(it != vocationsMap.end())
		return it->second;

	std::cout << "[Warning - Vocations::getVocation] Vocation " << vocId << " not found." << std::endl;
	return &Vocations::defVoc;
}

int32_t Vocations::getVocationId(const std::string& name)
{
	for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it)
	{
		if(!strcasecmp(it->second->getName().c_str(), name.c_str()))
			return it->first;
	}

	return -1;
}

int32_t Vocations::getPromotedVocation(uint32_t vocationId)
{
	for(VocationsMap::iterator it = vocationsMap.begin(); it != vocationsMap.end(); ++it)
	{
		if(it->second->getFromVocation() == vocationId && it->first != vocationId)
			return it->first;
	}

	return -1;
}

Vocation::~Vocation()
{
	cacheMana.clear();
	for(int32_t i = SKILL_FIRST; i < SKILL_LAST; ++i)
		cacheSkill[i].clear();
}

void Vocation::reset()
{
	memset(absorb, 0, sizeof(absorb));
	memset(reflect[REFLECT_PERCENT], 0, sizeof(reflect[REFLECT_PERCENT]));
	memset(reflect[REFLECT_CHANCE], 0, sizeof(reflect[REFLECT_CHANCE]));

	needPremium = false;
	attackable = true;
	lessLoss = fromVocation = 0;
	gain[GAIN_SOUL] = 100;
	gainTicks[GAIN_SOUL] = 120;
	baseSpeed = 220;
	attackSpeed = 1500;
	name = description = "";

	gainAmount[GAIN_HEALTH] = gainAmount[GAIN_MANA] = gainAmount[GAIN_SOUL] = 1;
	gain[GAIN_HEALTH] = gain[GAIN_MANA] = capGain = 5;
	gainTicks[GAIN_HEALTH] = gainTicks[GAIN_MANA] = 6;

	skillBase[SKILL_SHIELD] = 100;
	skillBase[SKILL_DIST] = 30;
	skillBase[SKILL_FISH] = 20;
	for(int32_t i = SKILL_FIST; i < SKILL_DIST; i++)
		skillBase[i] = 50;

	skillMultipliers[SKILL_FIST] = 1.5f;
	skillMultipliers[SKILL_FISH] = 1.1f;
	skillMultipliers[SKILL__LEVEL] = 1.0f;
	for(int32_t i = SKILL_CLUB; i < SKILL_FISH; i++)
		skillMultipliers[i] = 2.0f;

	formulaMultipliers[MULTIPLIER_MANA] = 4.0f;
	for(int32_t i = MULTIPLIER_FIRST; i < MULTIPLIER_LAST; i++)
		formulaMultipliers[i] = 1.0f;
}

int16_t Vocation::getReflect(CombatType_t combat) const
{
	if(reflect[REFLECT_CHANCE][combat] < random_range(0, 100))
		return reflect[REFLECT_PERCENT][combat];

	return 0;
}

uint32_t Vocation::getReqSkillTries(int32_t skill, int32_t level)
{
	if(skill < SKILL_FIRST || skill > SKILL_LAST)
		return 0;

	cacheMap& skillMap = cacheSkill[skill];
	cacheMap::iterator it = skillMap.find(level);
	if(it != cacheSkill[skill].end())
		return it->second;

	skillMap[level] = (uint32_t)(skillBase[skill] * std::pow(skillMultipliers[skill], (level - 11)));
	return skillMap[level];
}

uint64_t Vocation::getReqMana(uint32_t magLevel)
{
	cacheMap::iterator it = cacheMana.find(magLevel);
	if(it != cacheMana.end())
		return it->second;

	cacheMana[magLevel] = (uint64_t)(1600 * std::pow(formulaMultipliers[MULTIPLIER_MANA], (float)(magLevel - 1)));
	return cacheMana[magLevel];
}
 
Could be more easy if you post the parts that handle with skills in general and your custom skill, so it will be more easy to see your error.

P.S: maybe the problem is in the database schema? I don't know how OpenTibia takes care of player skills, but maybe you need to change also 'player_skills'? Don't really know.
 
Back
Top