Jump to content
  • 0
funtwocrasher

VIP STATUS ICON ERROR (status_change_start: Invalid status change (1500)!)

Question

Hi! I added a custom status icon (VIP Users). But I have trouble implementing them. This is what I did.

1. src/map/status.cpp
Added 
case SC_VIPSTATE:
After
case SC_JEXPBOOST:

2. src/map/status.hpp
Added
SC_VIPSTATE = 1500,
Before
SC_MAX,

3. src/map/status.hpp
Added
EFST_VIPSTATE = 1500,
Before
EFST_MAX,

4. src/map/script_constants.hpp

Added the following on their respective lines.
export_constant(SC_VIPSTATE);
export_deprecated_constant2("SI_VIPSTATE",1500);
export_constant(EFST_VIPSTATE);

5. efstids.lub

EFST_VIPSTATE = 1500,

 stateiconimginfo.lub
[EFST_IDs.EFST_VIPSTATE] = "vip.tga",

 stateiconinfo.lub

StateIconList[EFST_IDs.EFST_VIPSTATE] = {
    haveTimeLimit = 1,
    posTimeLimitStr = 2,
    descript = {
        { "VIP MEMBER", COLOR_TITLE_BUFF },
        { "Exp Bonus 10%" },
        { "Job Exp Bonus 10%" },
        { "Drop Rate Bonus 10%" },
        { "Additional 300 Storage Slot" },
        { "Can use command" },
        { "@autoattack" },
        { "@autotrade" },
        { "@autoloot" },
        { "Can get Fairy Buff for +5 All stats" }
    }
    
}


6. Iteminfo.yml

 Script: |
      vip_time(1440); sc_start SC_VIPSTATE,-1,0;






I did all these things but i still receive an error and nothing works,
image.png.e4fdbca5d1f2939a832aa51f1e27cd9e.png

If anyone can help me, I would really appreciate it. Thank you very much!

Edited by funtwocrasher
  • Upvote 1
Link to comment
Share on other sites

10 answers to this question

Recommended Posts

  • 0

Try this one:
export_deprecated_constant2("SI_VIPSTATE",1500); => Not Necessary

s
rc/map/status.cpp

StatusIconChangeTable[SC_VIPSTATE] = EFST_VIPSTATE;

StatusChangeFlagTable[SC_VIPSTATE] |= SCB_NONE;

 

Link to comment
Share on other sites

  • 0
3 hours ago, Royr said:

src/map/status.cpp

StatusIconChangeTable[SC_VIPSTATE] = EFST_VIPSTATE;

StatusChangeFlagTable[SC_VIPSTATE] |= SCB_NONE;

 

 

This option is not on latest rAthena. This is the status.cpp of the latest rAthena.
 

// Copyright (c) rAthena Dev Teams - Licensed under GNU GPL
// For more information, see LICENCE in the main folder

#include "status.hpp"

#include <functional>
#include <math.h>
#include <stdlib.h>
#include <string>
#include <yaml-cpp/yaml.h>

#include "../common/cbasetypes.hpp"
#include "../common/ers.hpp"
#include "../common/malloc.hpp"
#include "../common/nullpo.hpp"
#include "../common/random.hpp"
#include "../common/showmsg.hpp"
#include "../common/strlib.hpp"
#include "../common/timer.hpp"
#include "../common/utilities.hpp"
#include "../common/utils.hpp"

#include "battle.hpp"
#include "battleground.hpp"
#include "clif.hpp"
#include "elemental.hpp"
#include "guild.hpp"
#include "homunculus.hpp"
#include "itemdb.hpp"
#include "map.hpp"
#include "mercenary.hpp"
#include "mob.hpp"
#include "npc.hpp"
#include "path.hpp"
#include "pc.hpp"
#include "pc_groups.hpp"
#include "pet.hpp"
#include "script.hpp"

using namespace rathena;

// Regen related flags.
enum e_regen {
	RGN_NONE = 0x00,
	RGN_HP   = 0x01,
	RGN_SP   = 0x02,
	RGN_SHP  = 0x04,
	RGN_SSP  = 0x08,
};

static struct eri *sc_data_ers; /// For sc_data entries
static struct status_data dummy_status;

short current_equip_item_index; /// Contains inventory index of an equipped item. To pass it into the EQUP_SCRIPT [Lupus]
unsigned int current_equip_combo_pos; /// For combo items we need to save the position of all involved items here
int current_equip_card_id; /// To prevent card-stacking (from jA) [Skotlex]
// We need it for new cards 15 Feb 2005, to check if the combo cards are insrerted into the CURRENT weapon only to avoid cards exploits
short current_equip_opt_index; /// Contains random option index of an equipped item. [Secret]

uint16 SCDisabled[SC_MAX]; ///< List of disabled SC on map zones. [Cydh]

static unsigned short status_calc_str(struct block_list *,struct status_change *,int);
static unsigned short status_calc_agi(struct block_list *,struct status_change *,int);
static unsigned short status_calc_vit(struct block_list *,struct status_change *,int);
static unsigned short status_calc_int(struct block_list *,struct status_change *,int);
static unsigned short status_calc_dex(struct block_list *,struct status_change *,int);
static unsigned short status_calc_luk(struct block_list *,struct status_change *,int);
static unsigned short status_calc_pow(struct block_list *, struct status_change *, int);
static unsigned short status_calc_sta(struct block_list *, struct status_change *, int);
static unsigned short status_calc_wis(struct block_list *, struct status_change *, int);
static unsigned short status_calc_spl(struct block_list *, struct status_change *, int);
static unsigned short status_calc_con(struct block_list *, struct status_change *, int);
static unsigned short status_calc_crt(struct block_list *, struct status_change *, int);
static unsigned short status_calc_batk(struct block_list *,struct status_change *,int);
static unsigned short status_calc_watk(struct block_list *,struct status_change *,int);
static unsigned short status_calc_matk(struct block_list *,struct status_change *,int);
static signed short status_calc_hit(struct block_list *,struct status_change *,int);
static signed short status_calc_critical(struct block_list *,struct status_change *,int);
static signed short status_calc_flee(struct block_list *,struct status_change *,int);
static signed short status_calc_flee2(struct block_list *,struct status_change *,int);
static defType status_calc_def(struct block_list *bl, struct status_change *sc, int);
static signed short status_calc_def2(struct block_list *,struct status_change *,int);
static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int);
static signed short status_calc_mdef2(struct block_list *,struct status_change *,int);
static unsigned short status_calc_speed(struct block_list *,struct status_change *,int);
static short status_calc_aspd_rate(struct block_list *,struct status_change *,int);
static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion);
#ifdef RENEWAL_ASPD
static short status_calc_aspd(struct block_list *bl, struct status_change *sc, bool fixed);
#endif
static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int);
static signed short status_calc_patk(struct block_list *, struct status_change *, int);
static signed short status_calc_smatk(struct block_list *, struct status_change *, int);
static signed short status_calc_res(struct block_list *, struct status_change *, int);
static signed short status_calc_mres(struct block_list *, struct status_change *, int);
static signed short status_calc_hplus(struct block_list *, struct status_change *, int);
static signed short status_calc_crate(struct block_list *, struct status_change *, int);
static unsigned int status_calc_maxhp(struct block_list *bl, uint64 maxhp);
static unsigned int status_calc_maxsp(struct block_list *bl, uint64 maxsp);
static unsigned int status_calc_maxap(struct block_list *bl, uint64 maxap);
static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element);
static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv);
static int status_calc_mode(struct block_list *bl, struct status_change *sc, int mode);
#ifdef RENEWAL
static unsigned short status_calc_ematk(struct block_list *,struct status_change *,int);
#endif
static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type);
static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type);
static int status_get_apbonus(struct block_list *bl, enum e_status_bonus type);
static unsigned int status_calc_maxhpsp_pc(struct map_session_data* sd, unsigned int stat, bool isHP);
static unsigned int status_calc_maxap_pc(struct map_session_data* sd);
static int status_get_sc_interval(enum sc_type type);

static bool status_change_isDisabledOnMap_(sc_type type, bool mapIsVS, bool mapIsPVP, bool mapIsGVG, bool mapIsBG, unsigned int mapZone, bool mapIsTE);
#define status_change_isDisabledOnMap(type, m) ( status_change_isDisabledOnMap_((type), mapdata_flag_vs2((m)), m->flag[MF_PVP] != 0, mapdata_flag_gvg2_no_te((m)), m->flag[MF_BATTLEGROUND] != 0, (m->zone << 3) != 0, mapdata_flag_gvg2_te((m))) )

const std::string RefineDatabase::getDefaultLocation(){
	return std::string( db_path ) + "/refine.yml";
}

uint64 RefineDatabase::parseBodyNode( const YAML::Node& node ){
	std::string group_name;

	if( !this->asString( node, "Group", group_name ) ){
		return 0;
	}

	std::string group_name_constant = "REFINE_TYPE_" + group_name;
	int64 constant;

	if( !script_get_constant( group_name_constant.c_str(), &constant ) ){
		this->invalidWarning(node["Group"], "Unknown refine group %s, skipping.\n", group_name.c_str() );
		return 0;
	}

	uint16 group_id = static_cast<uint16>( constant );

	std::shared_ptr<s_refine_info> info = this->find( group_id );
	bool exists = info != nullptr;

	if( !exists ){
		info = std::make_shared<s_refine_info>();
	}

	if( this->nodeExists( node, "Levels" ) ){
		for( const YAML::Node& levelNode : node["Levels"] ){
			uint16 level;

			if( !this->asUInt16( levelNode, "Level", level ) ){
				return 0;
			}

			std::shared_ptr<s_refine_levels_info> levels_info = util::umap_find( info->levels, level );
			bool levels_exists = levels_info != nullptr;

			if( !levels_exists ){
				levels_info = std::make_shared<s_refine_levels_info>();
				levels_info->level = level;
			}

			if( this->nodeExists( levelNode, "RefineLevels" ) ){
				for( const YAML::Node& refineLevelNode : levelNode["RefineLevels"] ){
					uint16 refine_level;

					if( !this->asUInt16( refineLevelNode, "Level", refine_level ) ){
						return 0;
					}

					if( refine_level == 0 || refine_level > MAX_REFINE ){
						this->invalidWarning( refineLevelNode["Level"], "Refine level %hu is invalid, skipping.\n", refine_level );
						return 0;
					}

					// Database is 1 based, code is 0 based
					refine_level -= 1;

					std::shared_ptr<s_refine_level_info> level_info = util::umap_find( levels_info->levels, refine_level );
					bool level_exists = level_info != nullptr;

					if( !level_exists ){
						level_info = std::make_shared<s_refine_level_info>();
						level_info->level = refine_level;
					}

					if( this->nodeExists( refineLevelNode, "Bonus" ) ){
						uint32 bonus;

						if( !this->asUInt32( refineLevelNode, "Bonus", bonus ) ){
							return 0;
						}

						level_info->bonus = bonus;
					}else{
						if( !level_exists ){
							level_info->bonus = 0;
						}
					}

					if( this->nodeExists( refineLevelNode, "RandomBonus" ) ){
						uint32 bonus;

						if( !this->asUInt32( refineLevelNode, "RandomBonus", bonus ) ){
							return 0;
						}

						level_info->randombonus_max = bonus;
					}else{
						if( !level_exists ){
							level_info->randombonus_max = 0;
						}
					}

					if( this->nodeExists( refineLevelNode, "BlacksmithBlessingAmount" ) ){
						uint16 amount;

						if( !this->asUInt16( refineLevelNode, "BlacksmithBlessingAmount", amount ) ){
							return 0;
						}

						if( amount > MAX_AMOUNT ){
							this->invalidWarning( refineLevelNode["BlacksmithBlessingAmount"], "Blacksmith Blessing amount %hu too high, capping to MAX_AMOUNT.\n", amount );
							amount = MAX_AMOUNT;
						}

						level_info->blessing_amount = amount;
					}else{
						if( !level_exists ){
							level_info->blessing_amount = 0;
						}
					}

					if( this->nodeExists( refineLevelNode, "Chances" ) ){
						for( const YAML::Node& chanceNode : refineLevelNode["Chances"] ){
							std::string cost_name;

							if( !this->asString( chanceNode, "Type", cost_name ) ){
								return 0;
							}

							std::string cost_name_constant = "REFINE_COST_" + cost_name;

							if( !script_get_constant( cost_name_constant.c_str(), &constant ) ){
								this->invalidWarning( chanceNode["Type"], "Unknown refine cost type %s, skipping.\n", cost_name.c_str() );
								return 0;
							}

							if( constant >= REFINE_COST_MAX ){
								this->invalidWarning( chanceNode["Type"], "Refine cost type %s is unsupported, skipping.\n", cost_name.c_str() );
								return 0;
							}

							uint16 index = (uint16)constant;

							std::shared_ptr<s_refine_cost> cost = util::umap_find( level_info->costs, index );
							bool cost_exists = cost != nullptr;

							if( !cost_exists ){
								cost = std::make_shared<s_refine_cost>();
								cost->index = index;
							}

							if( this->nodeExists( chanceNode, "Rate" ) ){
								uint16 rate;

								if( !this->asUInt16Rate( chanceNode, "Rate", rate ) ){
									return 0;
								}

								cost->chance = rate;
							}else{
								if( !cost_exists ){
									cost->chance = 0;
								}
							}

							if( this->nodeExists( chanceNode, "Price" ) ){
								uint32 price;

								if( !this->asUInt32( chanceNode, "Price", price ) ){
									return 0;
								}

								if( price > MAX_ZENY ){
									this->invalidWarning( chanceNode["Price"], "Price is above MAX_ZENY, capping...\n" );
									price = MAX_ZENY;
								}

								cost->zeny = price;
							}else{
								if( !cost_exists ){
									cost->zeny = 0;
								}
							}

							if( this->nodeExists( chanceNode, "Material" ) ){
								std::string item_name;

								if( !this->asString( chanceNode, "Material", item_name ) ){
									return 0;
								}

								std::shared_ptr<item_data> id = item_db.search_aegisname( item_name.c_str() );

								if( id == nullptr ){
									this->invalidWarning( chanceNode["Material"], "Unknown refine material %s, skipping.\n", item_name.c_str() );
									return 0;
								}

								cost->nameid = id->nameid;
							}else{
								if( !cost_exists ){
									cost->nameid = 0;
								}
							}

							if( this->nodeExists( chanceNode, "BreakingRate" ) ){
								uint16 breaking_rate;

								if( !this->asUInt16Rate( chanceNode, "BreakingRate", breaking_rate ) ){
									return 0;
								}

								cost->breaking_rate = breaking_rate;
							}else{
								if( !cost_exists ){
									cost->breaking_rate = 0;
								}
							}

							if( this->nodeExists( chanceNode, "DowngradeAmount" ) ){
								uint16 downgrade_amount;

								if( !this->asUInt16( chanceNode, "DowngradeAmount", downgrade_amount ) ){
									return 0;
								}

								if( downgrade_amount > MAX_REFINE ){
									this->invalidWarning( chanceNode["DowngradeAmount"], "Downgrade amount %hu is invalid, skipping.\n", downgrade_amount );
									return 0;
								}

								cost->downgrade_amount = downgrade_amount;
							}else{
								if( !cost_exists ){
									cost->downgrade_amount = 0;
								}
							}

							if( !cost_exists ){
								level_info->costs[index] = cost;
							}
						}
					}

					if( !level_exists ){
						levels_info->levels[refine_level] = level_info;
					}
				}
			}

			if( !levels_exists ){
				info->levels[level] = levels_info;
			}
		}
	}

	if( !exists ){
		this->put( group_id, info );
	}

	return 1;
}

std::shared_ptr<s_refine_level_info> RefineDatabase::findLevelInfoSub( const struct item_data& data, struct item& item, uint16 refine ){
	// Check if the item can be refined
	if( data.flag.no_refine ){
		return nullptr;
	}

	// Cap the refine level
	if( refine > MAX_REFINE ){
		refine = MAX_REFINE;
	}

	e_refine_type type;
	uint16 level;

	if( !this->calculate_refine_info( data, type, level ) ){
		return nullptr;
	}

	std::shared_ptr<s_refine_info> info = this->find( type );

	if( info == nullptr ){
		return nullptr;
	}

	std::shared_ptr<s_refine_levels_info> levels_info = util::umap_find( info->levels, level );

	if( levels_info == nullptr ){
		return nullptr;
	}

	return util::umap_find( levels_info->levels, refine );
}

std::shared_ptr<s_refine_level_info> RefineDatabase::findLevelInfo( const struct item_data& data, struct item& item ){
	// Check the current refine level
	if( item.refine >= MAX_REFINE ){
		return nullptr;
	}

	return this->findLevelInfoSub( data, item, item.refine );
}

std::shared_ptr<s_refine_level_info> RefineDatabase::findCurrentLevelInfo( const struct item_data& data, struct item& item ){
	if( item.refine > 0 ){
		return this->findLevelInfoSub( data, item, item.refine - 1 );
	}else{
		return nullptr;
	}
}

bool RefineDatabase::calculate_refine_info( const struct item_data& data, e_refine_type& refine_type, uint16& level ){
	if( data.type == IT_WEAPON ){
		refine_type = REFINE_TYPE_WEAPON;
		level = data.weapon_level;

		return true;
	}else if( data.type == IT_ARMOR ){
		refine_type = REFINE_TYPE_ARMOR;
		level = data.armor_level;

		return true;
	}else if( data.type == IT_SHADOWGEAR ){
		if( data.equip == EQP_SHADOW_WEAPON ){
			refine_type = REFINE_TYPE_SHADOW_WEAPON;
		}else{
			refine_type = REFINE_TYPE_SHADOW_ARMOR;
		}
		level = 1;

		return true;
	}else{
		return false;
	}
}

RefineDatabase refine_db;

const std::string SizeFixDatabase::getDefaultLocation() {
	return std::string(db_path) + "/size_fix.yml";
}

/**
 * Reads and parses an entry from size_fix.
 * @param node: YAML node containing the entry.
 * @return count of successfully parsed rows
 */
uint64 SizeFixDatabase::parseBodyNode(const YAML::Node &node) {
	std::string weapon_name;

	if (!this->asString(node, "Weapon", weapon_name))
		return 0;

	std::string weapon_name_constant = "W_" + weapon_name;
	int64 constant;

	if (!script_get_constant(weapon_name_constant.c_str(), &constant)) {
		this->invalidWarning(node["Weapon"], "Size Fix unknown weapon %s, skipping.\n", weapon_name.c_str());
		return 0;
	}

	if (constant < W_FIST || constant > W_2HSTAFF) {
		this->invalidWarning(node["Weapon"], "Size Fix weapon %s is an invalid weapon, skipping.\n", weapon_name.c_str());
		return 0;
	}

	int weapon_id = static_cast<int>(constant);
	std::shared_ptr<s_sizefix_db> size = this->find(weapon_id);
	bool exists = size != nullptr;

	if (!exists)
		size = std::make_shared<s_sizefix_db>();

	if (this->nodeExists(node, "Small")) {
		uint16 small;

		if (!this->asUInt16(node, "Small", small))
			return 0;

		if (small > 100) {
			this->invalidWarning(node["Small"], "Small Size Fix %d for %s is out of bounds, defaulting to 100.\n", small, weapon_name.c_str());
			small = 100;
		}

		size->small = small;
	} else {
		if (!exists)
			size->small = 100;
	}

	if (this->nodeExists(node, "Medium")) {
		uint16 medium;

		if (!this->asUInt16(node, "Medium", medium))
			return 0;

		if (medium > 100) {
			this->invalidWarning(node["Medium"], "Medium Size Fix %d for %s is out of bounds, defaulting to 100.\n", medium, weapon_name.c_str());
			medium = 100;
		}

		size->medium = medium;
	} else {
		if (!exists)
			size->medium = 100;
	}

	if (this->nodeExists(node, "Large")) {
		uint16 large;

		if (!this->asUInt16(node, "Large", large))
			return 0;

		if (large > 100) {
			this->invalidWarning(node["Large"], "Large Size Fix %d for %s is out of bounds, defaulting to 100.\n", large, weapon_name.c_str());
			large = 100;
		}

		size->large = large;
	} else {
		if (!exists)
			size->large = 100;
	}

	if (!exists)
		this->put(weapon_id, size);

	return 1;
}

SizeFixDatabase size_fix_db;

/**
 * Get icon ID of SC
 * @param type: SC type
 * @return EFST ID
 **/
efst_type StatusDatabase::getIcon(sc_type type) {
	std::shared_ptr<s_status_change_db> status = status_db.find(type);

	return status ? status->icon : EFST_BLANK;
}

/**
 * Get flag of SC (SCB value) for status_calc_ flag
 * @param type: SC type
 * @return cal_flag: Calc value 
 **/
uint64 StatusDatabase::getCalcFlag(sc_type type) {
	std::shared_ptr<s_status_change_db> status = status_db.find(type);

	return status ? status->calc_flag : SCB_NONE;
}

/**
 * Get SC's END list
 * @param sc: SC type
 * @return End list
 **/
std::vector<sc_type> StatusDatabase::getEnd(sc_type type) {
	std::shared_ptr<s_status_change_db> status = status_db.find(type);

	return status ? status->end : std::vector<sc_type> {};
}

/**
 * Get BL type to display proper effect
 * @param efst: EFST type
 * @return BL types
 **/
uint16 status_efst_get_bl_type(enum efst_type efst) {
	if (efst <= EFST_BLANK || efst >= EFST_MAX)
		return BL_PC;
	return status_db.StatusRelevantBLTypes[efst];
}

/**
 * Returns the FIRST skill (in order of definition in initChangeTables) to use a given status change.
 * Utilized for various duration lookups. Use with caution!
 * @param sc The status to look up
 * @return A skill associated with the status
 **/
uint16 StatusDatabase::getSkill(sc_type type) {
	std::shared_ptr<s_status_change_db> status = status_db.find(type);

	return status ? status->skill_id : 0;
}

/**
 * Returns if a status change flag is active or not for a SC.
 * @param sc: Status changes active on target
 * @param flag: Flag to check for
 * @return True if flag is set or false otherwise
 */
bool StatusDatabase::hasSCF(status_change *sc, e_status_change_flag flag) {
	if (sc == nullptr || sc->count == 0 || flag == SCF_NONE)
		return false;

	for (const auto &status_it : *this) {
		std::shared_ptr<s_status_change_db> status = status_it.second;

		if (sc->data[status->type] && status->flag[flag])
			return true;
	}

	return false;
}

/**
 * Removes statuses from skills that aren't part of the new class skill tree.
 * @param sd: Player data
 */
void StatusDatabase::changeSkillTree(map_session_data *sd, int32 class_) {
	if (sd == nullptr)
		return;

	std::shared_ptr<s_skill_tree> tree = skill_tree_db.find(class_ > 0 ? class_ : sd->status.class_);

	if (tree == nullptr)
		return;

	for (const auto &it : tree->skills) {
		uint16 skill_id = it.first;
		sc_type sc = skill_get_sc(skill_id);

		if (sc > SC_COMMON_MAX && sd->sc.data[sc])
			status_change_end(&sd->bl, sc, INVALID_TIMER);
	}
}

/**
 * Validates if type is in SC ranges
 * @param type: SC type
 * @return True if type is in range, false otherwise
 */
bool StatusDatabase::validateStatus(sc_type type) {
	if (type > SC_NONE && type < SC_MAX)
		return true;

	return false;
}

/**
 * Removes a status based on the provided flag(s).
 * @param bl: Target to remove status from
 * @param flag: List of flags to check for removing
 */
void StatusDatabase::removeByStatusFlag(block_list *bl, std::vector<e_status_change_flag> flag) {
	if (bl == nullptr || flag.empty())
		return;

	status_change *sc = status_get_sc(bl);

	if (sc == nullptr || sc->count == 0)
		return;

	for (const auto &status_it : *this) {
		std::shared_ptr<s_status_change_db> status = status_it.second;
		sc_type type = status->type;

		if (sc->data[type]) {
			for (const auto &flag_it : flag) {
				if (status->flag[flag_it])
					status_change_end(bl, type, INVALID_TIMER);
			}
		}
	}
}

/** Creates dummy status */
static void initDummyData(void) {
	memset(&dummy_status, 0, sizeof(dummy_status));
	dummy_status.hp =
	dummy_status.max_hp =
	dummy_status.max_sp =
	dummy_status.str =
	dummy_status.agi =
	dummy_status.vit =
	dummy_status.int_ =
	dummy_status.dex =
	dummy_status.luk =
	dummy_status.hit = 1;
	dummy_status.speed = 2000;
	dummy_status.adelay = 4000;
	dummy_status.amotion = 2000;
	dummy_status.dmotion = 2000;
	dummy_status.ele_lv = 1; // Min elemental level.
	dummy_status.mode = MD_CANMOVE;
}

/**
 * For copying a status_data structure from b to a, without overwriting current Hp, Sp, and Ap.
 * @param a: Status data structure to copy from
 * @param b: Status data structure to copy to
 */
static inline void status_cpy(struct status_data* a, const struct status_data* b)
{
	memcpy((void*)&a->max_hp, (const void*)&b->max_hp, sizeof(struct status_data)-(sizeof(a->hp)+sizeof(a->sp)+sizeof(a->ap)));
}

/**
 * Sets HP to a given value
 * Will always succeed (overrides heal impediment statuses) but can't kill an object
 * @param bl: Object whose HP will be set [PC|MOB|HOM|MER|ELEM|NPC]
 * @param hp: What the HP is to be set as
 * @param flag: Used in case final value is higher than current
 *		Use 2 to display healing effect
 * @return heal or zapped HP if valid
 */
int status_set_hp(struct block_list *bl, unsigned int hp, int flag)
{
	struct status_data *status;
	if (hp < 1)
		return 0;
	status = status_get_status_data(bl);
	if (status == &dummy_status)
		return 0;

	if (hp > status->max_hp)
		hp = status->max_hp;
	if (hp == status->hp)
		return 0;
	if (hp > status->hp)
		return status_heal(bl, hp - status->hp, 0, 1|flag);
	return status_zap(bl, status->hp - hp, 0);
}

/**
 * Sets Max HP to a given value
 * @param bl: Object whose Max HP will be set [PC|MOB|HOM|MER|ELEM|NPC]
 * @param maxhp: What the Max HP is to be set as
 * @param flag: Used in case final value is higher than current
 *		Use 2 to display healing effect
 * @return heal or zapped HP if valid
 */
int status_set_maxhp(struct block_list *bl, unsigned int maxhp, int flag)
{
	struct status_data *status;
	int64 heal;

	if (maxhp < 1)
		return 0;
	status = status_get_status_data(bl);
	if (status == &dummy_status)
		return 0;

	if (maxhp == status->max_hp)
		return 0;

	heal = maxhp - status->max_hp;
	status->max_hp = maxhp;

	if (heal > 0)
		status_heal(bl, heal, 0, 1|flag);
	else
		status_zap(bl, -heal, 0);

	return maxhp;
}

/**
 * Sets SP to a given value
 * @param bl: Object whose SP will be set [PC|HOM|MER|ELEM]
 * @param sp: What the SP is to be set as
 * @param flag: Used in case final value is higher than current
 *		Use 2 to display healing effect		
 * @return heal or zapped SP if valid
 */
int status_set_sp(struct block_list *bl, unsigned int sp, int flag)
{
	struct status_data *status;

	status = status_get_status_data(bl);
	if (status == &dummy_status)
		return 0;

	if (sp > status->max_sp)
		sp = status->max_sp;
	if (sp == status->sp)
		return 0;
	if (sp > status->sp)
		return status_heal(bl, 0, sp - status->sp, 1|flag);
	return status_zap(bl, 0, status->sp - sp);
}

/**
 * Sets Max SP to a given value
 * @param bl: Object whose Max SP will be set [PC|HOM|MER|ELEM]
 * @param maxsp: What the Max SP is to be set as
 * @param flag: Used in case final value is higher than current
 *		Use 2 to display healing effect
 * @return heal or zapped HP if valid
 */
int status_set_maxsp(struct block_list *bl, unsigned int maxsp, int flag)
{
	struct status_data *status;
	if (maxsp < 1)
		return 0;
	status = status_get_status_data(bl);
	if (status == &dummy_status)
		return 0;

	if (maxsp == status->max_sp)
		return 0;
	if (maxsp > status->max_sp)
		status_heal(bl, maxsp - status->max_sp, 0, 1|flag);
	else
		status_zap(bl, status->max_sp - maxsp, 0);

	status->max_sp = maxsp;
	return maxsp;
}

/**
* Sets AP to a given value
* @param bl: Object whose AP will be set [PC|HOM|MER|ELEM]
* @param ap: What the AP is to be set as
* @param flag: Used in case final value is higher than current
*		Use 2 to display healing effect
* @return heal or zapped AP if valid
*/
int status_set_ap(struct block_list *bl, unsigned int ap, int flag)
{
	status_data *status = status_get_status_data(bl);

	if (status == &dummy_status)
		return 0;

	if (ap > status->max_ap)
		ap = status->max_ap;
	if (ap == status->ap)
		return 0;
	if (ap > status->ap)
		return status_heal(bl, 0, 0, ap - status->ap, 1 | flag);
	return status_zap(bl, 0, 0, status->ap - ap);
}

/**
* Sets Max AP to a given value
* @param bl: Object whose Max AP will be set [PC|HOM|MER|ELEM]
* @param maxap: What the Max AP is to be set as
* @param flag: Used in case final value is higher than current
*		Use 2 to display healing effect
* @return heal or zapped AP if valid
*/
int status_set_maxap(struct block_list *bl, unsigned int maxap, int flag)
{
	if (maxap < 1)
		return 0;

	status_data *status = status_get_status_data(bl);

	if (status == &dummy_status)
		return 0;

	if (maxap == status->max_ap)
		return 0;
	if (maxap > status->max_ap)
		status_heal(bl, 0, 0, maxap - status->max_ap, 1 | flag);
	else
		status_zap(bl, 0, 0, status->max_ap - maxap);

	status->max_ap = maxap;
	return maxap;
}

/**
 * Takes HP/SP from an Object
 * @param bl: Object who will have HP/SP taken [PC|MOB|HOM|MER|ELEM]
 * @param hp: How much HP to charge
 * @param sp: How much SP to charge	
 * @return hp+sp through status_damage()
 * Note: HP/SP are integer values, not percentages. Values should be
 *	 calculated either within function call or before
 */
int64 status_charge(struct block_list* bl, int64 hp, int64 sp)
{
	if(!(bl->type&BL_CONSUME))
		return (int)hp+sp; // Assume all was charged so there are no 'not enough' fails.
	return status_damage(NULL, bl, hp, sp, 0, 3, 0);
}

/**
 * Inflicts damage on the target with the according walkdelay.
 * @param src: Source object giving damage [PC|MOB|PET|HOM|MER|ELEM]
 * @param target: Target of the damage
 * @param dhp: How much damage to HP
 * @param dsp: How much damage to SP
 * @param dap: How much damage to AP
 * @param walkdelay: Amount of time before object can walk again
 * @param flag: Damage flag decides various options
 *		flag&1: Passive damage - Does not trigger cancelling status changes
 *		flag&2: Fail if there is not enough to subtract
 *		flag&4: Mob does not give EXP/Loot if killed
 *		flag&8: Used to damage SP of a dead character
 * @return hp+sp+ap
 * Note: HP/SP/AP are integer values, not percentages. Values should be
 *	 calculated either within function call or before
 */
int status_damage(struct block_list *src,struct block_list *target,int64 dhp, int64 dsp, int64 dap, t_tick walkdelay, int flag, uint16 skill_id)
{
	struct status_data *status;
	struct status_change *sc;
	int hp = (int)cap_value(dhp,INT_MIN,INT_MAX);
	int sp = (int)cap_value(dsp,INT_MIN,INT_MAX);
	int ap = (int)cap_value(dap,INT_MIN,INT_MAX);

	nullpo_ret(target);

	if(sp && !(target->type&BL_CONSUME))
		sp = 0; // Not a valid SP target.

	if (ap && !(target->type&BL_CONSUME))
		ap = 0; // Not a valid AP target.

	if (hp < 0) { // Assume absorbed damage.
		status_heal(target, -hp, 0, 1);
		hp = 0;
	}

	if (sp < 0) {
		status_heal(target, 0, -sp, 1);
		sp = 0;
	}

	if (ap < 0) {
		status_heal(target, 0, 0, -ap, 1);
		ap = 0;
	}

	if (target->type == BL_SKILL) {
		if (!src || src->type&battle_config.can_damage_skill)
			return (int)skill_unit_ondamaged((struct skill_unit *)target, hp);
		return 0;
	}

	status = status_get_status_data(target);
	if(!status || status == &dummy_status )
		return 0;

	if ((unsigned int)hp >= status->hp) {
		if (flag&2) return 0;
		hp = status->hp;
	}

	if ((unsigned int)sp > status->sp) {
		if (flag&2) return 0;
		sp = status->sp;
	}

	if ((unsigned int)ap > status->ap) {
		if (flag & 2) return 0;
		ap = status->ap;
	}

	if (!hp && !sp && !ap)
		return 0;

	if( !status->hp )
		flag |= 8;

	sc = status_get_sc(target);
	if( hp && battle_config.invincible_nodamage && src && sc && sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
		hp = 1;

	if( hp && !(flag&1) ) {
		if( sc ) {
			struct status_change_entry *sce;

			for (const auto &it : status_db) {
				sc_type type = static_cast<sc_type>(it.first);

				if (it.second->flag[SCF_REMOVEONDAMAGED]) {
					if (type != SC_STONE || (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE))
						status_change_end(target, type, INVALID_TIMER);
				}
			}
			if ((sce=sc->data[SC_ENDURE]) && !sce->val4) {
				/** [Skotlex]
				* Endure count is only reduced by non-players on non-gvg maps.
				* val4 signals infinite endure.
				**/
				if (src && src->type != BL_PC && !map_flag_gvg2(target->m) && !map_getmapflag(target->m, MF_BATTLEGROUND) && --(sce->val2) <= 0)
					status_change_end(target, SC_ENDURE, INVALID_TIMER);
			}
#ifndef RENEWAL
			if ((sce=sc->data[SC_GRAVITATION]) && sce->val3 == BCT_SELF) {
				std::shared_ptr<s_skill_unit_group> sg = skill_id2group(sce->val4);

				if (sg) {
					skill_delunitgroup(sg);
					sce->val4 = 0;
					status_change_end(target, SC_GRAVITATION, INVALID_TIMER);
				}
			}
#endif
			if(sc->data[SC_DANCING] && (unsigned int)hp > status->max_hp>>2)
				status_change_end(target, SC_DANCING, INVALID_TIMER);
			if(sc->data[SC_CLOAKINGEXCEED] && --(sc->data[SC_CLOAKINGEXCEED]->val2) <= 0)
				status_change_end(target, SC_CLOAKINGEXCEED, INVALID_TIMER);
			if(sc->data[SC_KAGEMUSYA] && --(sc->data[SC_KAGEMUSYA]->val3) <= 0)
				status_change_end(target, SC_KAGEMUSYA, INVALID_TIMER);
		}

		if (target->type == BL_PC)
			pc_bonus_script_clear(BL_CAST(BL_PC,target),BSF_REM_ON_DAMAGED);
		unit_skillcastcancel(target, 2);
	}

	status->hp-= hp;
	status->sp-= sp;
	status->ap-= ap;

	if (sc && hp && status->hp) {
		if (sc->data[SC_AUTOBERSERK] &&
			(!sc->data[SC_PROVOKE] || !sc->data[SC_PROVOKE]->val4) &&
			status->hp < status->max_hp>>2)
			sc_start4(src,target,SC_PROVOKE,100,10,0,0,1,0);
		if (sc->data[SC_BERSERK] && status->hp <= 100)
			status_change_end(target, SC_BERSERK, INVALID_TIMER);
		if( sc->data[SC_RAISINGDRAGON] && status->hp <= 1000 )
			status_change_end(target, SC_RAISINGDRAGON, INVALID_TIMER);
		if (sc->data[SC_SATURDAYNIGHTFEVER] && status->hp <= 100)
			status_change_end(target, SC_SATURDAYNIGHTFEVER, INVALID_TIMER);
	}

	switch (target->type) {
		case BL_PC:  pc_damage((TBL_PC*)target,src,hp,sp,ap); break;
		case BL_MOB: mob_damage((TBL_MOB*)target, src, hp); break;
		case BL_HOM: hom_damage((TBL_HOM*)target); break;
		case BL_MER: mercenary_heal((TBL_MER*)target,hp,sp); break;
		case BL_ELEM: elemental_heal((TBL_ELEM*)target,hp,sp); break;
	}

	if( src && target->type == BL_PC && ((TBL_PC*)target)->disguise ) { // Stop walking when attacked in disguise to prevent walk-delay bug
		unit_stop_walking( target, 1 );
	}

	if( status->hp || (flag&8) ) { // Still lives or has been dead before this damage.
		if (walkdelay)
			unit_set_walkdelay(target, gettick(), walkdelay, 0);
		return (int)(hp+sp+ap);
	}

	status->hp = 0;
	/** [Skotlex]
	* NOTE: These dead functions should return:
	* 0: Death cancelled, auto-revived.
	* Non-zero: Standard death. Clear status, cancel move/attack, etc
	* &2: Remove object from map.
	* &4: Delete object from memory. (One time spawn mobs)
	**/
	switch (target->type) {
		case BL_PC:  flag = pc_dead((TBL_PC*)target,src); break;
		case BL_MOB: flag = mob_dead((TBL_MOB*)target, src, flag&4?3:0); break;
		case BL_HOM: flag = hom_dead((TBL_HOM*)target); break;
		case BL_MER: flag = mercenary_dead((TBL_MER*)target); break;
		case BL_ELEM: flag = elemental_dead((TBL_ELEM*)target); break;
		default:	// Unhandled case, do nothing to object.
			flag = 0;
			break;
	}

	if(!flag) // Death cancelled.
		return (int)(hp+sp+ap);

	// Normal death
	if (battle_config.clear_unit_ondeath &&
		battle_config.clear_unit_ondeath&target->type)
		skill_clear_unitgroup(target);

	if(target->type&BL_REGEN) { // Reset regen ticks.
		struct regen_data *regen = status_get_regen_data(target);
		if (regen) {
			memset(&regen->tick, 0, sizeof(regen->tick));
			if (regen->sregen)
				memset(&regen->sregen->tick, 0, sizeof(regen->sregen->tick));
			if (regen->ssregen)
				memset(&regen->ssregen->tick, 0, sizeof(regen->ssregen->tick));
		}
	}

	if( sc && sc->data[SC_KAIZEL] && !map_flag_gvg2(target->m) ) { // flag&8 = disable Kaizel
		int time = skill_get_time2(SL_KAIZEL,sc->data[SC_KAIZEL]->val1);
		// Look for Osiris Card's bonus effect on the character and revive 100% or revive normally
		if ( target->type == BL_PC && BL_CAST(BL_PC,target)->special_state.restart_full_recover )
			status_revive(target, 100, 100);
		else
			status_revive(target, sc->data[SC_KAIZEL]->val2, 0);
		status_change_clear(target,0);
		clif_skill_nodamage(target,target,ALL_RESURRECTION,1,1);
		sc_start(src,target,SC_KYRIE,100,10,time);

		if( target->type == BL_MOB )
			((TBL_MOB*)target)->state.rebirth = 1;

		return (int)(hp+sp+ap);
	}

	// Disable Ultimate Sacrifice on GVG maps
	if (sc && sc->data[SC_ULTIMATE_S] && !map_flag_gvg2(target->m)) {
		status_revive(target, 100, 100);
		status_change_clear(target, 0);
		clif_skill_nodamage(target, target, ALL_RESURRECTION, 1, 1);

		if (target->type == BL_MOB)
			((TBL_MOB*)target)->state.rebirth = 1;

		return (int)(hp+sp+ap);
	}

	if (target->type == BL_MOB && sc && sc->data[SC_REBIRTH] && !((TBL_MOB*) target)->state.rebirth) { // Ensure the monster has not already rebirthed before doing so.
		status_revive(target, sc->data[SC_REBIRTH]->val2, 0);
		status_change_clear(target,0);
		((TBL_MOB*)target)->state.rebirth = 1;

		return (int)(hp+sp+ap);
	}

	status_change_clear(target,0);

	if(flag&4) // Delete from memory. (also invokes map removal code)
		unit_free(target,CLR_DEAD);
	else if(flag&2) // remove from map
		unit_remove_map(target,CLR_DEAD);
	else { // Some death states that would normally be handled by unit_remove_map
		unit_stop_attack(target);
		unit_stop_walking(target,1);
		unit_skillcastcancel(target,0);
		clif_clearunit_area(target,CLR_DEAD);
		skill_unit_move(target,gettick(),4);
		skill_cleartimerskill(target);
	}

	// Always run NPC scripts for players last
	//FIXME those ain't always run if a player die if he was resurrect meanwhile
	//cf SC_REBIRTH, SC_KAIZEL, pc_dead...
	if(target->type == BL_PC) {
		TBL_PC *sd = BL_CAST(BL_PC,target);
		if( sd->bg_id ) {
			std::shared_ptr<s_battleground_data> bg = util::umap_find(bg_team_db, sd->bg_id);

			if( bg && !(bg->die_event.empty()) )
				npc_event(sd, bg->die_event.c_str(), 0);
		}

		npc_script_event(sd,NPCE_DIE);
	}

	return (int)(hp+sp+ap);
}

/**
 * Heals an object
 * @param bl: Object to heal [PC|MOB|HOM|MER|ELEM]
 * @param hhp: How much HP to heal
 * @param hsp: How much SP to heal
 * @param hap: How much AP to heal
 * @param flag:	Whether it's Forced(&1), gives HP/SP/AP(&2) heal effect,
 *      or gives HP(&4) heal effect with 0 heal
 *		Forced healing overrides heal impedement statuses (Berserk)
 * @return hp+sp+ap
 */
int status_heal(struct block_list *bl,int64 hhp,int64 hsp, int64 hap, int flag)
{
	struct status_data *status;
	struct status_change *sc;
	int hp = (int)cap_value(hhp,INT_MIN,INT_MAX);
	int sp = (int)cap_value(hsp,INT_MIN,INT_MAX);
	int ap = (int)cap_value(hap,INT_MIN,INT_MAX);

	status = status_get_status_data(bl);

	if (status == &dummy_status || !status->hp)
		return 0;

	sc = status_get_sc(bl);
	if (sc && !sc->count)
		sc = NULL;

	if (hp < 0) {
		if (hp == INT_MIN) // -INT_MIN == INT_MIN in some architectures!
			hp++;
		status_damage(NULL, bl, -hp, 0, 0, 1, 0);
		hp = 0;
	}

	if(hp) {
		if( sc && (sc->data[SC_BERSERK]) ) {
			if( flag&1 )
				flag &= ~2;
			else
				hp = 0;
		}

		if((unsigned int)hp > status->max_hp - status->hp)
			hp = status->max_hp - status->hp;
	}

	if(sp < 0) {
		if (sp == INT_MIN)
			sp++;
		status_damage(NULL, bl, 0, -sp, 0, 1, 0);
		sp = 0;
	}

	if(sp) {
		if((unsigned int)sp > status->max_sp - status->sp)
			sp = status->max_sp - status->sp;
	}

	if (ap < 0) {
		if (ap == INT_MIN)
			ap++;
		status_damage(nullptr, bl, 0, 0, -ap, 0, 1, 0);
		ap = 0;
	}

	if (ap) {
		if ((unsigned int)ap > status->max_ap - status->ap)
			ap = status->max_ap - status->ap;
	}

	if(!ap && !sp && !hp && !(flag&4))
		return 0;

	status->hp += hp;
	status->sp += sp;
	status->ap += ap;

	if(hp && sc &&
		sc->data[SC_AUTOBERSERK] &&
		sc->data[SC_PROVOKE] &&
		sc->data[SC_PROVOKE]->val4==1 &&
		status->hp>=status->max_hp>>2
	)	// End auto berserk.
		status_change_end(bl, SC_PROVOKE, INVALID_TIMER);

	// Send HP update to client
	switch(bl->type) {
		case BL_PC:  pc_heal((TBL_PC*)bl,hp,sp,ap,flag); break;
		case BL_MOB: mob_heal((TBL_MOB*)bl,hp); break;
		case BL_HOM: hom_heal((TBL_HOM*)bl); break;
		case BL_MER: mercenary_heal((TBL_MER*)bl,hp,sp); break;
		case BL_ELEM: elemental_heal((TBL_ELEM*)bl,hp,sp); break;
	}

	return (int)hp+sp+ap;
}

/**
 * Applies percentage based damage to a unit.
 * If a mob is killed this way and there is no src, no EXP/Drops will be awarded.
 * @param src: Object initiating HP/SP/AP modification [PC|MOB|PET|HOM|MER|ELEM]
 * @param target: Object to modify HP/SP/AP
 * @param hp_rate: Percentage of HP to modify. If > 0:percent is of current HP, if < 0:percent is of max HP
 * @param sp_rate: Percentage of SP to modify. If > 0:percent is of current SP, if < 0:percent is of max SP
 * @param ap_rate: Percentage of AP to modify. If > 0:percent is of current AP, if < 0:percent is of max AP
 * @param flag: \n
 *		0: Heal target \n 
 *		1: Use status_damage \n 
 *		2: Use status_damage and make sure target must not die from subtraction
 * @return hp+sp+ap through status_heal()
 */
int status_percent_change(struct block_list *src, struct block_list *target, int8 hp_rate, int8 sp_rate, int8 ap_rate, uint8 flag)
{
	struct status_data *status;
	unsigned int hp = 0, sp = 0, ap = 0;

	status = status_get_status_data(target);


	// It's safe now [MarkZD]
	if (hp_rate > 99)
		hp = status->hp;
	else if (hp_rate > 0)
		hp = apply_rate(status->hp, hp_rate);
	else if (hp_rate < -99)
		hp = status->max_hp;
	else if (hp_rate < 0)
		hp = (apply_rate(status->max_hp, -hp_rate));
	if (hp_rate && !hp)
		hp = 1;

	if (flag == 2 && hp >= status->hp)
		hp = status->hp-1; // Must not kill target.

	if (sp_rate > 99)
		sp = status->sp;
	else if (sp_rate > 0)
		sp = apply_rate(status->sp, sp_rate);
	else if (sp_rate < -99)
		sp = status->max_sp;
	else if (sp_rate < 0)
		sp = (apply_rate(status->max_sp, -sp_rate));
	if (sp_rate && !sp)
		sp = 1;

	if (ap_rate > 99)
		ap = status->ap;
	else if (ap_rate > 0)
		ap = apply_rate(status->ap, ap_rate);
	else if (ap_rate < -99)
		ap = status->max_ap;
	else if (ap_rate < 0)
		ap = (apply_rate(status->max_ap, -ap_rate));
	if (ap_rate && !ap)
		ap = 1;

	// Ugly check in case damage dealt is too much for the received args of
	// status_heal / status_damage. [Skotlex]
	if (hp > INT_MAX) {
		hp -= INT_MAX;
		if (flag)
			status_damage(src, target, INT_MAX, 0, 0, (!src||src==target?5:1), 0);
		else
			status_heal(target, INT_MAX, 0, 0);
	}
	if (sp > INT_MAX) {
		sp -= INT_MAX;
		if (flag)
			status_damage(src, target, 0, INT_MAX, 0, (!src||src==target?5:1), 0);
		else
			status_heal(target, 0, INT_MAX, 0);
	}
	if (ap > INT_MAX) {
		ap -= INT_MAX;
		if (flag)
			status_damage(src, target, 0, 0, INT_MAX, 0, (!src || src == target ? 5 : 1), 0);
		else
			status_heal(target, 0, 0, INT_MAX, 0);
	}
	if (flag)
		return status_damage(src, target, hp, sp, ap, 0, (!src||src==target?5:1), 0);
	return status_heal(target, hp, sp, ap, 0);
}

/**
 * Revives a unit
 * @param bl: Object to revive [PC|MOB|HOM]
 * @param per_hp: Percentage of HP to revive with
 * @param per_sp: Percentage of SP to revive with
 * @param per_ap: Percentage of AP to revive with
 * @return Successful (1) or Invalid target (0)
 */
int status_revive(struct block_list *bl, unsigned char per_hp, unsigned char per_sp, unsigned char per_ap)
{
	struct status_data *status;
	unsigned int hp, sp, ap;
	if (!status_isdead(bl)) return 0;

	status = status_get_status_data(bl);
	if (status == &dummy_status)
		return 0; // Invalid target.

	hp = (int64)status->max_hp * per_hp/100;
	sp = (int64)status->max_sp * per_sp/100;
	ap = (int64)status->max_ap * per_ap/100;

	if(hp > status->max_hp - status->hp)
		hp = status->max_hp - status->hp;
	else if (per_hp && !hp)
		hp = 1;

	if(sp > status->max_sp - status->sp)
		sp = status->max_sp - status->sp;
	else if (per_sp && !sp)
		sp = 1;

	if (ap > status->max_ap - status->ap)
		ap = status->max_ap - status->ap;
	else if (per_ap && !ap)
		ap = 1;

	status->hp += hp;
	status->sp += sp;
	status->ap += ap;

	if (bl->prev) // Animation only if character is already on a map.
		clif_resurrection(bl, 1);
	switch (bl->type) {
		case BL_PC:  pc_revive((TBL_PC*)bl, hp, sp, ap); break;
		case BL_MOB: mob_revive((TBL_MOB*)bl, hp); break;
		case BL_HOM: hom_revive((TBL_HOM*)bl, hp, sp); break;
	}
	return 1;
}

/**
 * Checks whether the src can use the skill on the target,
 * taking into account status/option of both source/target
 * @param src:	Object using skill on target [PC|MOB|PET|HOM|MER|ELEM]
		src MAY be NULL to indicate we shouldn't check it, this is a ground-based skill attack
 * @param target: Object being targeted by src [PC|MOB|HOM|MER|ELEM]
		 target MAY be NULL, which checks if src can cast skill_id on the ground
 * @param skill_id: Skill ID being used on target
 * @param flag:	0 - Trying to use skill on target
 *		1 - Cast bar is done
 *		2 - Skill already pulled off, check is due to ground-based skills or splash-damage ones
 * @return src can use skill (1) or cannot use skill (0)
 * @author [Skotlex]
 */
bool status_check_skilluse(struct block_list *src, struct block_list *target, uint16 skill_id, int flag) {
	struct status_data *status;
	struct status_change *sc = NULL, *tsc;
	int hide_flag;

	status = src ? status_get_status_data(src) : &dummy_status;

	if (src && src->type != BL_PC && status_isdead(src))
		return false;

	if (!skill_id) { // Normal attack checks.
		if (sc && sc->cant.attack)
			return false;
		// This mode is only needed for melee attacking.
		if (!status_has_mode(status,MD_CANATTACK))
			return false;
		// Dead state is not checked for skills as some skills can be used
		// on dead characters, said checks are left to skill.cpp [Skotlex]
		if (target && status_isdead(target))
			return false;
	}

	switch( skill_id ) {
#ifndef RENEWAL
		case PA_PRESSURE:
			if( flag && target ) {
				// Gloria Avoids pretty much everything....
				tsc = status_get_sc(target);
				if(tsc && tsc->option&OPTION_HIDE)
					return false;
			}
			break;
#endif
		case GN_WALLOFTHORN:
			if( target && status_isdead(target) )
				return false;
			break;
		case AL_TELEPORT:
		case ALL_ODINS_POWER:
			// Should fail when used on top of Land Protector [Skotlex]
			if (src && map_getcell(src->m, src->x, src->y, CELL_CHKLANDPROTECTOR)
				&& !status_has_mode(status,MD_STATUSIMMUNE)
				&& (src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id))
				return false;
			break;
		case SC_MANHOLE:
			// Skill is disabled against special racial grouped monsters(GvG and Battleground)
			if (target && ( util::vector_exists(status_get_race2(target), RC2_GVG) || util::vector_exists(status_get_race2(target), RC2_BATTLEFIELD) ) )
				return false;
		default:
			break;
	}

	if ( src )
		sc = status_get_sc(src);

	if( sc && sc->count ) {
		if (sc->data[SC_ALL_RIDING])
			return false; //You can't use skills while in the new mounts (The client doesn't let you, this is to make cheat-safe)

		if (flag == 1 && !status_has_mode(status,MD_STATUSIMMUNE) && ( // Applies to after cast completion only and doesn't apply to Boss monsters.
			(sc->data[SC_ASH] && rnd()%2) || // Volcanic Ash has a 50% chance of causing skills to fail.
			(sc->data[SC_KYOMU] && rnd()%100 < 25) // Kyomu has a 25% chance of causing skills fail.
		)) {
			if (src->type == BL_PC)
				clif_skill_fail((TBL_PC*)src,skill_id,USESKILL_FAIL_LEVEL,0);
			return false;
		}

		if (sc->cant.cast && skill_id != RK_REFRESH && skill_id != SU_GROOMING && skill_id != SR_GENTLETOUCH_CURE) { // Stuned/Frozen/etc
			if (flag != 1) // Can't cast, casted stuff can't damage.
				return false;
			if (skill_get_casttype(skill_id) == CAST_DAMAGE)
				return false; // Damage spells stop casting.
		}

		if (
			(sc->data[SC_TRICKDEAD] && skill_id != NV_TRICKDEAD)
			|| (sc->data[SC_AUTOCOUNTER] && !flag && skill_id)
			|| (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF && skill_id != PA_GOSPEL)
			|| (sc->data[SC_SUHIDE] && skill_id != SU_HIDE)
		)
			return false;

		if (sc->data[SC_WINKCHARM] && target && !flag) { // Prevents skill usage
			if (unit_bl2ud(src) && (unit_bl2ud(src))->walktimer == INVALID_TIMER)
				unit_walktobl(src, map_id2bl(sc->data[SC_WINKCHARM]->val2), 3, 1);
			clif_emotion(src, ET_THROB);
			return false;
		}

		if (sc->data[SC_BLADESTOP]) {
			switch (sc->data[SC_BLADESTOP]->val1) {
				case 5: if (skill_id == MO_EXTREMITYFIST) break;
				case 4: if (skill_id == MO_CHAINCOMBO) break;
				case 3: if (skill_id == MO_INVESTIGATE) break;
				case 2: if (skill_id == MO_FINGEROFFENSIVE) break;
				default: return false;
			}
		}

		if (sc->data[SC_DANCING] && flag!=2) {
			std::shared_ptr<s_skill_db> skill = skill_db.find(skill_id);

			if (!skill)
				return false;

			if (src->type == BL_PC && ((skill_id >= WA_SWING_DANCE && skill_id <= WM_UNLIMITED_HUMMING_VOICE ) ||
				skill_id == WM_FRIGG_SONG))
			{ // Lvl 5 Lesson or higher allow you use 3rd job skills while dancing.
				if( pc_checkskill((TBL_PC*)src,WM_LESSON) < 5 )
					return false;
#ifndef RENEWAL
			} else if(sc->data[SC_LONGING]) { // Allow everything except dancing/re-dancing. [Skotlex]
				if (skill_id == BD_ENCORE || skill->inf2[INF2_ISSONG] || skill->inf2[INF2_ISENSEMBLE])
					return false;
#endif
			} else if(!skill->inf2[INF2_ALLOWWHENPERFORMING]) // Skills that can be used in dancing state
				return false;
#ifndef RENEWAL
			if ((sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE && skill_id == BD_ADAPTATION)
				return false; // Can't amp out of Wand of Hermode :/ [Skotlex]
#endif
		}

		if (skill_id && // Do not block item-casted skills.
			(src->type != BL_PC || ((TBL_PC*)src)->skillitem != skill_id)
		) {	// Skills blocked through status changes...
			if (!flag && ( // Blocked only from using the skill (stuff like autospell may still go through
				sc->cant.cast ||
#ifndef RENEWAL
				(sc->data[SC_BASILICA] && (sc->data[SC_BASILICA]->val4 != src->id || skill_id != HP_BASILICA)) || // Only Basilica caster that can cast, and only Basilica to cancel it
#endif
				(sc->data[SC_MARIONETTE] && skill_id != CG_MARIONETTE) || // Only skill you can use is marionette again to cancel it
				(sc->data[SC_MARIONETTE2] && skill_id == CG_MARIONETTE) || // Cannot use marionette if you are being buffed by another
				(sc->data[SC_ANKLE] && skill_block_check(src, SC_ANKLE, skill_id)) ||
				(sc->data[SC_STASIS] && skill_block_check(src, SC_STASIS, skill_id)) ||
				(sc->data[SC_BITE] && skill_block_check(src, SC_BITE, skill_id)) ||
				(sc->data[SC_NOVAEXPLOSING] && skill_block_check(src, SC_NOVAEXPLOSING, skill_id)) ||
				(sc->data[SC_GRAVITYCONTROL] && skill_block_check(src, SC_GRAVITYCONTROL, skill_id)) ||
				(sc->data[SC_KAGEHUMI] && skill_block_check(src, SC_KAGEHUMI, skill_id))
#ifdef RENEWAL
				|| (sc->data[SC_ENSEMBLEFATIGUE] && skill_id != CG_SPECIALSINGER)
#endif
			))
				return false;

			// Skill blocking.
			if (
				(sc->data[SC_VOLCANO] && skill_id == WZ_ICEWALL) ||
#ifndef RENEWAL
				(sc->data[SC_ROKISWEIL] && skill_id != BD_ADAPTATION) ||
#endif
				(sc->data[SC_HERMODE] && skill_get_inf(skill_id) & INF_SUPPORT_SKILL) ||
				(sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOSKILL)
			)
				return false;
		}

		if (sc->option) {
			if ((sc->option&OPTION_HIDE) && src->type == BL_PC && (skill_id == 0 || !skill_get_inf2(skill_id, INF2_ALLOWWHENHIDDEN))) {
				// Non players can use all skills while hidden.
				return false;
			}
			if (sc->option&OPTION_CHASEWALK && skill_id != ST_CHASEWALK)
				return false;
		}
	}

	if (target == NULL || target == src) // No further checking needed.
		return true;

	tsc = status_get_sc(target);

	if (tsc && tsc->count) {
		/**
		* Attacks in invincible are capped to 1 damage and handled in battle.cpp.
		* Allow spell break and eske for sealed shrine GDB when in INVINCIBLE state.
		**/
		if( tsc->data[SC_INVINCIBLE] && !tsc->data[SC_INVINCIBLEOFF] && skill_id && !(skill_id&(SA_SPELLBREAKER|SL_SKE)) )
			return false;
		if(!skill_id && tsc->data[SC_TRICKDEAD])
			return false;
		if((skill_id == WZ_STORMGUST || skill_id == WZ_FROSTNOVA || skill_id == NJ_HYOUSYOURAKU || skill_id == NPC_STORMGUST2)
			&& tsc->data[SC_FREEZE])
			return false;
		if(skill_id == PR_LEXAETERNA && (tsc->data[SC_FREEZE] || (tsc->data[SC_STONE] && tsc->opt1 == OPT1_STONE)))
			return false;
		if (tsc->data[SC__MANHOLE] && !skill_get_inf2(skill_id, INF2_TARGETMANHOLE))
			return false;
	}

	// If targetting, cloak+hide protect you, otherwise only hiding does.
	hide_flag = flag?OPTION_HIDE:(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK);

 	// Skill that can hit hidden target
	if( skill_get_inf2(skill_id, INF2_TARGETHIDDEN) )
		hide_flag &= ~OPTION_HIDE;

	switch( target->type ) {
		case BL_PC: {
				struct map_session_data *tsd = (TBL_PC*)target;
				bool is_boss = (src && status_get_class_(src) == CLASS_BOSS);
				bool is_detect = status_has_mode(status,MD_DETECTOR);

				if (pc_isinvisible(tsd))
					return false;
				if (tsc) {
					if ((tsc->option&hide_flag) && !is_boss && (tsd->special_state.perfect_hiding || !is_detect))
						return false;
					if ((tsc->data[SC_CLOAKINGEXCEED] || tsc->data[SC_NEWMOON]) && !is_boss && (tsd->special_state.perfect_hiding || is_detect))
						return false; // Works against insect and demon but not against bosses
					if (tsc->data[SC__FEINTBOMB] && (is_boss || is_detect))
						return false; // Works against all
					if ((tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD] || tsc->data[SC_SUHIDE]) && !(is_boss || is_detect) && (!skill_id || (!flag && src)))
						return false; // Insect, demon, and boss can detect
				}
			}
			break;
		case BL_ITEM: // Allow targetting of items to pick'em up (or in the case of mobs, to loot them).
			// !TODO: Would be nice if this could be used to judge whether the player can or not pick up the item it targets. [Skotlex]
			if (status_has_mode(status,MD_LOOTER))
				return true;
			return false;
		case BL_HOM:
		case BL_MER:
		case BL_ELEM:
			if( target->type == BL_HOM && skill_id && battle_config.hom_setting&HOMSET_NO_SUPPORT_SKILL && skill_get_inf(skill_id)&INF_SUPPORT_SKILL && battle_get_master(target) != src )
				return false; // Can't use support skills on Homunculus (only Master/Self)
			if( target->type == BL_MER && (skill_id == PR_ASPERSIO || (skill_id >= SA_FLAMELAUNCHER && skill_id <= SA_SEISMICWEAPON)) && battle_get_master(target) != src )
				return false; // Can't use Weapon endow skills on Mercenary (only Master)
			if( skill_id == AM_POTIONPITCHER && ( target->type == BL_MER || target->type == BL_ELEM) )
				return false; // Can't use Potion Pitcher on Mercenaries
			if (tsc && tsc->data[SC_ELEMENTAL_VEIL] && !(src && status_get_class_(src) == CLASS_BOSS) && !status_has_mode(status, MD_DETECTOR))
				return false;
		default:
			// Check for chase-walk/hiding/cloaking opponents.
			if( tsc ) {
				if( tsc->option&hide_flag && !status_has_mode(status,MD_DETECTOR))
					return false;
			}
	}
	return true;
}

/**
 * Checks whether the src can see the target
 * @param src:	Object using skill on target [PC|MOB|PET|HOM|MER|ELEM]
 * @param target: Object being targeted by src [PC|MOB|HOM|MER|ELEM]
 * @return src can see (1) or target is invisible (0)
 * @author [Skotlex]
 */
int status_check_visibility(struct block_list *src, struct block_list *target)
{
	int view_range;
	struct status_data* status = status_get_status_data(src);
	struct status_change* tsc = status_get_sc(target);
	switch (src->type) {
		case BL_MOB:
			view_range = ((TBL_MOB*)src)->min_chase;
			break;
		case BL_PET:
			view_range = ((TBL_PET*)src)->db->range2;
			break;
		default:
			view_range = AREA_SIZE;
	}

	if (src->m != target->m || !check_distance_bl(src, target, view_range))
		return 0;

	if ( src->type == BL_NPC) // NPCs don't care for the rest
		return 1;

	if (tsc) {
		bool is_boss = (status_get_class_(src) == CLASS_BOSS);
		bool is_detector = status_has_mode(status,MD_DETECTOR);

		switch (target->type) {	// Check for chase-walk/hiding/cloaking opponents.
			case BL_PC: {
					struct map_session_data *tsd = (TBL_PC*)target;

					if (((tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD] || tsc->data[SC_SUHIDE]) && !is_boss && (tsd->special_state.perfect_hiding || !is_detector))
						return 0;
					if ((tsc->data[SC_CLOAKINGEXCEED] || tsc->data[SC_NEWMOON]) && !is_boss && ((tsd && tsd->special_state.perfect_hiding) || is_detector))
						return 0;
					if (tsc->data[SC__FEINTBOMB] && !is_boss && !is_detector)
						return 0;
				}
				break;
			case BL_ELEM:
				if (tsc->data[SC_ELEMENTAL_VEIL] && !is_boss && !is_detector)
					return 0;
				break;
			default:
				if (((tsc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK)) || tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_STEALTHFIELD] || tsc->data[SC_SUHIDE]) && !is_boss && !is_detector)
					return 0;
		}
	}

	return 1;
}

/**
 * Base ASPD value taken from the job tables
 * @param sd: Player object
 * @param status: Player status
 * @return base amotion after single/dual weapon and shield adjustments [RENEWAL]
 *	  base amotion after single/dual weapon and stats adjustments [PRE-RENEWAL]
 */
int status_base_amotion_pc(struct map_session_data* sd, struct status_data* status)
{
	std::shared_ptr<s_job_info> job = job_db.find(sd->status.class_);

	if (job == nullptr)
		return 2000;

	int amotion;
#ifdef RENEWAL_ASPD
	int16 skill_lv, val = 0;
	float temp_aspd = 0;

	amotion = job->aspd_base[sd->weapontype1]; // Single weapon
	if (sd->status.shield)
		amotion += job->aspd_base[MAX_WEAPON_TYPE];
	else if (sd->weapontype2 != W_FIST && sd->equip_index[EQI_HAND_R] != sd->equip_index[EQI_HAND_L])
		amotion += job->aspd_base[sd->weapontype2] / 4; // Dual-wield

	switch(sd->status.weapon) {
		case W_BOW:
		case W_MUSICAL:
		case W_WHIP:
		case W_REVOLVER:
		case W_RIFLE:
		case W_GATLING:
		case W_SHOTGUN:
		case W_GRENADE:
			temp_aspd = status->dex * status->dex / 7.0f + status->agi * status->agi * 0.5f;
			break;
		default:
			temp_aspd = status->dex * status->dex / 5.0f + status->agi * status->agi * 0.5f;
			break;
	}
	temp_aspd = (float)(sqrt(temp_aspd) * 0.25f) + 0xc4;
	if ((skill_lv = pc_checkskill(sd,SA_ADVANCEDBOOK)) > 0 && sd->status.weapon == W_BOOK)
		val += (skill_lv - 1) / 2 + 1;
	if ((skill_lv = pc_checkskill(sd, SG_DEVIL)) > 0 && ((sd->class_&MAPID_THIRDMASK) == MAPID_STAR_EMPEROR || pc_is_maxjoblv(sd)))
		val += 1 + skill_lv;
	if ((skill_lv = pc_checkskill(sd,GS_SINGLEACTION)) > 0 && (sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
		val += ((skill_lv + 1) / 2);
	if ((skill_lv = pc_checkskill(sd, RG_PLAGIARISM)) > 0)
		val += skill_lv;
	if (pc_isriding(sd))
		val -= 50 - 10 * pc_checkskill(sd, KN_CAVALIERMASTERY);
	else if (pc_isridingdragon(sd))
		val -= 25 - 5 * pc_checkskill(sd, RK_DRAGONTRAINING);
	amotion = ((int)(temp_aspd + ((float)(status_calc_aspd(&sd->bl, &sd->sc, true) + val) * status->agi / 200)) - min(amotion, 200));
#else
	// Angra Manyu disregards aspd_base and similar
	if (pc_checkequip2(sd, ITEMID_ANGRA_MANYU, EQI_ACC_L, EQI_MAX))
		return 0;

	// Base weapon delay
	amotion = (sd->status.weapon < MAX_WEAPON_TYPE)
	 ? (job->aspd_base[sd->status.weapon]) // Single weapon
	 : (job->aspd_base[sd->weapontype1] + job->aspd_base[sd->weapontype2]) * 7 / 10; // Dual-wield

	// Percentual delay reduction from stats
	amotion -= amotion * (4 * status->agi + status->dex) / 1000;

	// Raw delay adjustment from bAspd bonus
	amotion += sd->bonus.aspd_add;
#endif

 	return amotion;
}

/**
 * Base attack value calculated for units
 * @param bl: Object to get attack for [BL_CHAR|BL_NPC]
 * @param status: Object status
 * @return base attack
 */
unsigned short status_base_atk(const struct block_list *bl, const struct status_data *status, int level)
{
	int flag = 0, str, dex, dstr;

#ifdef RENEWAL
	if (!(bl->type&battle_config.enable_baseatk_renewal))
		return 0;
#else
	if (!(bl->type&battle_config.enable_baseatk))
		return 0;
#endif

	if (bl->type == BL_PC)
	switch(((TBL_PC*)bl)->status.weapon) {
		case W_BOW:
		case W_MUSICAL:
		case W_WHIP:
		case W_REVOLVER:
		case W_RIFLE:
		case W_GATLING:
		case W_SHOTGUN:
		case W_GRENADE:
			flag = 1;
	}
	if (flag) {
#ifdef RENEWAL
		dstr =
#endif
		str = status->dex;
		dex = status->str;
	} else {
#ifdef RENEWAL
		dstr =
#endif
		str = status->str;
		dex = status->dex;
	}

	/** [Skotlex]
	* Normally only players have base-atk, but homunc have a different batk
	* equation, hinting that perhaps non-players should use this for batk.
	**/
	switch (bl->type) {
		case BL_HOM:
#ifdef RENEWAL
			str = 2 * level + status_get_homstr(bl);
#else
			dstr = str / 10;
			str += dstr*dstr;
#endif
			break;
		case BL_PC:
#ifdef RENEWAL
			str = (dstr * 10 + dex * 10 / 5 + status->luk * 10 / 3 + level * 10 / 4) / 10 + 5 * status->pow;
#else
			dstr = str / 10;
			str += dstr*dstr;
			str += dex / 5 + status->luk / 5;
#endif
			break;
		default:// Others
#ifdef RENEWAL
			str = dstr + level;
#else
			dstr = str / 10;
			str += dstr*dstr;
			str += dex / 5 + status->luk / 5;
#endif
			break;
	}

	return cap_value(str, 0, USHRT_MAX);
}

#ifdef RENEWAL
/**
 * Weapon attack value calculated for Players
 * @param wa: Weapon attack
 * @param status: Player status
 * @return weapon attack
 */
unsigned int status_weapon_atk(weapon_atk &wa)
{
	return wa.atk + wa.atk2;
}
#endif

#ifndef RENEWAL
unsigned short status_base_matk_min(const struct status_data* status) { return status->int_ + (status->int_ / 7) * (status->int_ / 7); }
unsigned short status_base_matk_max(const struct status_data* status) { return status->int_ + (status->int_ / 5) * (status->int_ / 5); }
#else
/*
* Calculates minimum attack variance 80% from db's ATK1 for non BL_PC
* status->batk (base attack) will be added in battle_calc_base_damage
*/
unsigned short status_base_atk_min(struct block_list *bl, const struct status_data* status, int level)
{
	switch (bl->type) {
		case BL_PET:
		case BL_MOB:
		case BL_MER:
		case BL_ELEM:
			return status->rhw.atk * 80 / 100;
		case BL_HOM:
			return (status_get_homstr(bl) + status_get_homdex(bl)) / 5;
		default:
			return status->rhw.atk;
	}
}

/*
* Calculates maximum attack variance 120% from db's ATK1 for non BL_PC
* status->batk (base attack) will be added in battle_calc_base_damage
*/
unsigned short status_base_atk_max(struct block_list *bl, const struct status_data* status, int level)
{
	switch (bl->type) {
		case BL_PET:
		case BL_MOB:
		case BL_MER:
		case BL_ELEM:
			return status->rhw.atk * 120 / 100;
		case BL_HOM:
			return (status_get_homluk(bl) + status_get_homstr(bl) + status_get_homdex(bl)) / 3;
		default:
			return status->rhw.atk2;
	}
}

/*
* Calculates minimum magic attack
*/
unsigned short status_base_matk_min(struct block_list *bl, const struct status_data* status, int level)
{
	switch (bl->type) {
		case BL_PET:
		case BL_MOB:
		case BL_MER:
		case BL_ELEM:
			return status->int_ + level + status->rhw.matk * 70 / 100;
		case BL_HOM:
			return status_get_homint(bl) + level + (status_get_homint(bl) + status_get_homdex(bl)) / 5;
		case BL_PC:
		default:
			return status->int_ + (status->int_ / 2) + (status->dex / 5) + (status->luk / 3) + (level / 4) + 5 * status->spl;
	}
}

/*
* Calculates maximum magic attack
*/
unsigned short status_base_matk_max(struct block_list *bl, const struct status_data* status, int level)
{
	switch (bl->type) {
		case BL_PET:
		case BL_MOB:
		case BL_MER:
		case BL_ELEM:
			return status->int_ + level + status->rhw.matk * 130 / 100;
		case BL_HOM:
			return status_get_homint(bl) + level + (status_get_homluk(bl) + status_get_homint(bl) + status_get_homdex(bl)) / 3;
		case BL_PC:
		default:
			return status->int_ + (status->int_ / 2) + (status->dex / 5) + (status->luk / 3) + (level / 4) + 5 * status->spl;
	}
}
#endif

/**
 * Fills in the misc data that can be calculated from the other status info (except for level)
 * @param bl: Object to calculate status on [PC|MOB|PET|HOM|MERC|ELEM]
 * @param status: Player status
 */
void status_calc_misc(struct block_list *bl, struct status_data *status, int level)
{
	int stat;

	// Non players get the value set, players need to stack with previous bonuses.
	if( bl->type != BL_PC )
		status->batk =
		status->hit = status->flee =
		status->def2 = status->mdef2 =
		status->cri = status->flee2 =
		status->patk = status->smatk =
		status->hplus = status->crate =
		status->res = status->mres = 0;

#ifdef RENEWAL // Renewal formulas
	if (bl->type == BL_HOM) {
		// Def2
		stat = status_get_homvit(bl) + status_get_homagi(bl) / 2;
		status->def2 = cap_value(stat, 0, SHRT_MAX);
		// Mdef2
		stat = (status_get_homvit(bl) + status_get_homint(bl)) / 2;
		status->mdef2 = cap_value(stat, 0, SHRT_MAX);
		// Def
		stat = status->def;
		stat += status_get_homvit(bl) + level / 2;
		status->def = cap_value(stat, 0, SHRT_MAX);
		// Mdef
		stat = (int)(((float)status_get_homvit(bl) + level) / 4 + (float)status_get_homint(bl) / 2);
		status->mdef = cap_value(stat, 0, SHRT_MAX);
		// Hit
		stat = level + status->dex + 150;
		status->hit = cap_value(stat, 1, SHRT_MAX);
		// Flee
		stat = level + status_get_homagi(bl);
		status->flee = cap_value(stat, 1, SHRT_MAX);
	} else {
		// Hit
		stat = status->hit;
		stat += level + status->dex + (bl->type == BL_PC ? status->luk / 3 + 175 : 150); //base level + ( every 1 dex = +1 hit ) + (every 3 luk = +1 hit) + 175
		stat += 2 * status->con;
		status->hit = cap_value(stat, 1, SHRT_MAX);
		// Flee
		stat = status->flee;
		stat += level + status->agi + (bl->type == BL_MER ? 0 : bl->type == BL_PC ? status->luk / 5 : 0) + 100; //base level + ( every 1 agi = +1 flee ) + (every 5 luk = +1 flee) + 100
		stat += 2 * status->con;
		status->flee = cap_value(stat, 1, SHRT_MAX);
		// Def2
		if (bl->type == BL_MER)
			stat = (int)(status->vit + ((float)level / 10) + ((float)status->vit / 5));
		else {
			stat = status->def2;
			stat += (int)(((float)level + status->vit) / 2 + (bl->type == BL_PC ? ((float)status->agi / 5) : 0)); //base level + (every 2 vit = +1 def) + (every 5 agi = +1 def)
		}
		status->def2 = cap_value(stat, 0, SHRT_MAX);
		// Mdef2
		if (bl->type == BL_MER)
			stat = (int)(((float)level / 10) + ((float)status->int_ / 5));
		else {
			stat = status->mdef2;
			stat += (int)(bl->type == BL_PC ? (status->int_ + ((float)level / 4) + ((float)(status->dex + status->vit) / 5)) : ((float)(status->int_ + level) / 4)); //(every 4 base level = +1 mdef) + (every 1 int = +1 mdef) + (every 5 dex = +1 mdef) + (every 5 vit = +1 mdef)
		}
		status->mdef2 = cap_value(stat, 0, SHRT_MAX);
		// PAtk
		stat = status->patk;
		stat += status->pow / 3 + status->con / 5;
		status->patk = cap_value(stat, 0, SHRT_MAX);
		// SMatk
		stat = status->smatk;
		stat += status->spl / 3 + status->con / 5;
		status->smatk = cap_value(stat, 0, SHRT_MAX);
		// Res
		stat = status->res;
		stat += status->sta + status->sta / 3 * 5;
		status->res = cap_value(stat, 0, SHRT_MAX);
		// Mres
		stat = status->mres;
		stat += status->wis + status->wis / 3 * 5;
		status->mres = cap_value(stat, 0, SHRT_MAX);
		// HPlus
		stat = status->hplus;
		stat += status->crt;
		status->hplus = cap_value(stat, 0, SHRT_MAX);
		// CRate
		stat = status->crate;
		stat += status->crt / 3;
		status->crate = cap_value(stat, 0, SHRT_MAX);
	}

	// ATK
	if (bl->type != BL_PC) {
		status->rhw.atk2 = status_base_atk_max(bl, status, level);
		status->rhw.atk = status_base_atk_min(bl, status, level);
	}

	// MAtk
	status->matk_min = status_base_matk_min(bl, status, level);
	status->matk_max = status_base_matk_max(bl, status, level);
#else
	// Matk
	status->matk_min = status_base_matk_min(status);
	status->matk_max = status_base_matk_max(status);
	// Hit
	stat = status->hit;
	stat += level + status->dex;
	status->hit = cap_value(stat, 1, SHRT_MAX);
	// Flee
	stat = status->flee;
	stat += level + status->agi;
	status->flee = cap_value(stat, 1, SHRT_MAX);
	// Def2
	stat = status->def2;
	stat += status->vit;
	status->def2 = cap_value(stat, 0, SHRT_MAX);
	// Mdef2
	stat = status->mdef2;
	stat += status->int_ + (status->vit>>1);
	status->mdef2 = cap_value(stat, 0, SHRT_MAX);
#endif

	//Critical
	if( bl->type&battle_config.enable_critical ) {
		stat = status->cri;
		stat += 10 + (status->luk*10/3); // (every 1 luk = +0.3 critical)
		status->cri = cap_value(stat, 1, SHRT_MAX);
	} else
		status->cri = 0;

	if (bl->type&battle_config.enable_perfect_flee) {
		stat = status->flee2;
		stat += status->luk + 10; // (every 10 luk = +1 perfect flee)
		status->flee2 = cap_value(stat, 0, SHRT_MAX);
	} else
		status->flee2 = 0;

	if (status->batk) {
		int temp = status->batk + status_base_atk(bl, status, level);
		status->batk = cap_value(temp, 0, USHRT_MAX);
	} else
		status->batk = status_base_atk(bl, status, level);

	if (status->cri) {
		switch (bl->type) {
			case BL_MOB:
				if(battle_config.mob_critical_rate != 100)
					status->cri = cap_value(status->cri*battle_config.mob_critical_rate/100,1,SHRT_MAX);
				if(!status->cri && battle_config.mob_critical_rate)
					status->cri = 10;
				break;
			case BL_PC:
				// Players don't have a critical adjustment setting as of yet.
				break;
			default:
				if(battle_config.critical_rate != 100)
					status->cri = cap_value(status->cri*battle_config.critical_rate/100,1,SHRT_MAX);
				if (!status->cri && battle_config.critical_rate)
					status->cri = 10;
		}
	}

	if(bl->type&BL_REGEN)
		status_calc_regen(bl, status, status_get_regen_data(bl));
}

/**
 * Calculates the initial status for the given mob
 * @param md: Mob object
 * @param opt: Whether or not it is the first calculation
		This will only be false when a mob levels up (Regular and WoE Guardians)
 * @return 1 for calculated special statuses or 0 for none
 * @author [Skotlex]
 */
int status_calc_mob_(struct mob_data* md, uint8 opt)
{
	struct status_data *status;
	struct block_list *mbl = NULL;
	int flag=0;

	if (opt&SCO_FIRST) { // Set basic level on respawn.
		if (md->level > 0 && md->level <= MAX_LEVEL && md->level != md->db->lv)
			;
		else
			md->level = md->db->lv;
	}

	// Check if we need custom base-status
	if (battle_config.mobs_level_up && md->level > md->db->lv)
		flag|=1;

	if (md->special_state.size)
		flag|=2;

	if (md->guardian_data && md->guardian_data->guardup_lv)
		flag|=4;
	if (md->mob_id == MOBID_EMPERIUM)
		flag|=4;

	if (battle_config.slaves_inherit_speed && md->master_id)
		flag|=8;

	if (md->master_id && md->special_state.ai>AI_ATTACK)
		flag|=16;

	if (md->master_id && battle_config.slaves_inherit_mode)
		flag |= 32;

	if (!flag) { // No special status required.
		if (md->base_status) {
			aFree(md->base_status);
			md->base_status = NULL;
		}
		if (opt&SCO_FIRST)
			memcpy(&md->status, &md->db->status, sizeof(struct status_data));
		return 0;
	}
	if (!md->base_status)
		md->base_status = (struct status_data*)aCalloc(1, sizeof(struct status_data));

	status = md->base_status;
	memcpy(status, &md->db->status, sizeof(struct status_data));

	if (flag&(8|16))
		mbl = map_id2bl(md->master_id);

	if (flag&8 && mbl) {
		struct status_data *mstatus = status_get_base_status(mbl);

		if (mstatus &&
			battle_config.slaves_inherit_speed&(status_has_mode(mstatus,MD_CANMOVE)?1:2))
			status->speed = mstatus->speed;
		if( status->speed < 2 ) // Minimum for the unit to function properly
			status->speed = 2;
	}

	if (flag&32)
		status_calc_slave_mode(md, map_id2md(md->master_id));

	if (flag&1) { // Increase from mobs leveling up [Valaris]
		int diff = md->level - md->db->lv;

		status->str += diff;
		status->agi += diff;
		status->vit += diff;
		status->int_ += diff;
		status->dex += diff;
		status->luk += diff;
		status->max_hp += diff * status->vit;
		status->max_sp += diff * status->int_;
		status->hp = status->max_hp;
		status->sp = status->max_sp;
		status->speed -= cap_value(diff, 0, status->speed - 10);
	}

	if (flag&2 && battle_config.mob_size_influence) { // Change for sized monsters [Valaris]
		if (md->special_state.size == SZ_MEDIUM) {
			status->max_hp >>= 1;
			status->max_sp >>= 1;
			if (!status->max_hp) status->max_hp = 1;
			if (!status->max_sp) status->max_sp = 1;
			status->hp = status->max_hp;
			status->sp = status->max_sp;
			status->str >>= 1;
			status->agi >>= 1;
			status->vit >>= 1;
			status->int_ >>= 1;
			status->dex >>= 1;
			status->luk >>= 1;
			if (!status->str) status->str = 1;
			if (!status->agi) status->agi = 1;
			if (!status->vit) status->vit = 1;
			if (!status->int_) status->int_ = 1;
			if (!status->dex) status->dex = 1;
			if (!status->luk) status->luk = 1;
		} else if (md->special_state.size == SZ_BIG) {
			status->max_hp <<= 1;
			status->max_sp <<= 1;
			status->hp = status->max_hp;
			status->sp = status->max_sp;
			status->str <<= 1;
			status->agi <<= 1;
			status->vit <<= 1;
			status->int_ <<= 1;
			status->dex <<= 1;
			status->luk <<= 1;
		}
	}

	status_calc_misc(&md->bl, status, md->level);

	if(flag&4) { // Strengthen Guardians - custom value +10% / lv
		struct map_data *mapdata = map_getmapdata(md->bl.m);
		std::shared_ptr<guild_castle> gc = castle_db.mapname2gc(mapdata->name);

		if (gc == nullptr)
			ShowError("status_calc_mob: No castle set at map %s\n", mapdata->name);
		else if(gc->castle_id < 24 || md->mob_id == MOBID_EMPERIUM) {
#ifdef RENEWAL
			status->max_hp += 50 * (gc->defense / 5);
#else
			status->max_hp += 1000 * gc->defense;
#endif
			status->hp = status->max_hp;
			status->def += (gc->defense+2)/3;
			status->mdef += (gc->defense+2)/3;
		}
		if(md->mob_id != MOBID_EMPERIUM) {
			status->max_hp += 1000 * gc->defense;
			status->hp = status->max_hp;
			status->batk += 2 * md->guardian_data->guardup_lv + 8;
			status->rhw.atk += 2 * md->guardian_data->guardup_lv + 8;
			status->rhw.atk2 += 2 * md->guardian_data->guardup_lv + 8;
			status->aspd_rate -= 2 * md->guardian_data->guardup_lv + 3;
		}
	}

	if (flag&16 && mbl) { // Max HP setting from Summon Flora/marine Sphere
		struct unit_data *ud = unit_bl2ud(mbl);
		// Remove special AI when this is used by regular mobs.
		if (mbl->type == BL_MOB && !((TBL_MOB*)mbl)->special_state.ai)
			md->special_state.ai = AI_NONE;
		if (ud) { 
			// Different levels of HP according to skill level
			if(!ud->skill_id) // !FIXME: We lost the unit data for magic decoy in somewhere before this
				ud->skill_id = ((TBL_PC*)mbl)->menuskill_id;
			switch(ud->skill_id) {
				case AM_SPHEREMINE:
					status->max_hp = 2000 + 400*ud->skill_lv;
					break;
				case KO_ZANZOU:
					status->max_hp = 3000 + 3000 * ud->skill_lv;
					break;
				case AM_CANNIBALIZE:
					status->max_hp = 1500 + 200*ud->skill_lv + 10*status_get_lv(mbl);
					status->mode = static_cast<e_mode>(status->mode|MD_CANATTACK|MD_AGGRESSIVE);
					break;
				case MH_SUMMON_LEGION:
				{
					int homblvl = status_get_lv(mbl);

					status->max_hp = 10 * (100 * (ud->skill_lv + 2) + homblvl);
					status->batk = 100 * (ud->skill_lv+5) / 2;
					status->def = 10 * (100 * (ud->skill_lv+2) + homblvl);
					// status->aspd_rate = 10 * (2 * (20 - ud->skill_lv) - homblvl/10);
					// status->aspd_rate = max(100,status->aspd_rate);
					break;
				}
				case NC_SILVERSNIPER:
				{
					struct status_data *mstatus = status_get_status_data(mbl);
					if(!mstatus)
						break;
					status->max_hp = (1000 * ud->skill_lv) + (mstatus->hp / 3) + (status_get_lv(mbl) * 12);
					status->batk = 200 * ud->skill_lv;
					break;
				}
				case NC_MAGICDECOY:
				{
					struct status_data *mstatus = status_get_status_data(mbl);
					if(!mstatus)
						break;
					status->max_hp = (1000 * ((TBL_PC*)mbl)->menuskill_val) + (mstatus->sp * 4) + (status_get_lv(mbl) * 12);
					status->matk_min = status->matk_max = 250 + 50*((TBL_PC*)mbl)->menuskill_val;
					break;
				}
				case MT_SUMMON_ABR_BATTLE_WARIOR:
				case MT_SUMMON_ABR_DUAL_CANNON:
				case MT_SUMMON_ABR_MOTHER_NET:
				case MT_SUMMON_ABR_INFINITY: {
						map_session_data *msd = BL_CAST(BL_PC, mbl);
						status_data *mstatus = status_get_status_data(mbl);

						if (msd == nullptr || mstatus == nullptr)
							break;

						uint8 abr_mastery = pc_checkskill(msd, MT_ABR_M);

						// Custom formulas for ABR's.
						// Its unknown how the summoner's stats affects the ABR's stats.
						// I decided to do something similar to elementals for now until I know.
						// Also added hit increase from ABR-Mastery for balance reasons. [Rytech]
						status->max_hp = (5000 + 2000 * abr_mastery) * mstatus->vit / 100;
						status->rhw.atk = (2 * mstatus->batk + 500 + 200 * abr_mastery) * 70 / 100;
						status->rhw.atk2 = 2 * mstatus->batk + 500 + 200 * abr_mastery;
						status->def = mstatus->def + 20 * abr_mastery;
						status->mdef = mstatus->mdef + 4 * abr_mastery;
						status->hit = mstatus->hit + 5 * abr_mastery / 2;
						status->flee = mstatus->flee + 10 * abr_mastery;
						status->speed = mstatus->speed;

						// The Infinity ABR appears to have a much higher attack then other
						// ABR's and im guessing has a much higher MaxHP due to it being a AP
						// costing summon. [Rytech]
						if (ud->skill_id == MT_SUMMON_ABR_INFINITY) {
							status->max_hp += 20000;
							status->rhw.atk += 1400; // 70% of 2000
							status->rhw.atk2 += 2000;
						}
					}
					break;
				case BO_WOODENWARRIOR:
				case BO_WOODEN_FAIRY:
				case BO_CREEPER:
				case BO_HELLTREE: {
						map_session_data *msd = BL_CAST(BL_PC, mbl);
						status_data *mstatus = status_get_status_data(mbl);

						if (msd == nullptr || mstatus == nullptr)
							break;

						uint8 bionic_mastery = pc_checkskill(msd, BO_BIONICS_M);

						// Custom formulas for bionic's.
						// Its unknown how the summoner's stats affects the bionic's stats.
						// I decided to do something similar to elementals for now until I know.
						// Also added hit increase from Bionic-Mastery for balance reasons. [Rytech]
						status->max_hp = (5000 + 2000 * bionic_mastery) * mstatus->vit / 100;
						//status->max_sp = (50 + 20 * bionic_mastery) * mstatus->int_ / 100;// Wait what??? Bionic Mastery increases MaxSP? They have SP???
						status->rhw.atk = (2 * mstatus->batk + 200 * bionic_mastery) * 70 / 100;
						status->rhw.atk2 = 2 * mstatus->batk + 200 * bionic_mastery;
						status->def = mstatus->def + 20 * bionic_mastery;
						status->mdef = mstatus->mdef + 4 * bionic_mastery;
						status->hit = mstatus->hit + 5 * bionic_mastery / 2;
						status->flee = mstatus->flee + 10 * bionic_mastery;
						status->speed = mstatus->speed;

						// The Hell Tree bionic appears to have a much higher attack then other
						// bionic's and im guessing has a much higher MaxHP due to it being a AP
						// costing summon. [Rytech]
						if (ud->skill_id == BO_HELLTREE) {
							status->max_hp += 20000;
							status->rhw.atk += 1400; // 70% of 2000
							status->rhw.atk2 += 2000;
						}
					}
					break;
			}
			status->hp = status->max_hp;
		}
	}

	if (opt&SCO_FIRST) // Initial battle status
		memcpy(&md->status, status, sizeof(struct status_data));

	return 1;
}

/**
 * Calculates the stats of the given pet
 * @param pd: Pet object
 * @param opt: Whether or not it is the first calculation
		This will only be false when a pet levels up
 * @return 1
 * @author [Skotlex]
 */
void status_calc_pet_(struct pet_data *pd, uint8 opt)
{
	nullpo_retv(pd);

	if (opt&SCO_FIRST) {
		memcpy(&pd->status, &pd->db->status, sizeof(struct status_data));
		pd->status.mode = MD_CANMOVE; // Pets discard all modes, except walking
		pd->status.class_ = CLASS_NORMAL;
		pd->status.speed = pd->get_pet_walk_speed();

		if(battle_config.pet_attack_support || battle_config.pet_damage_support) {
			// Attack support requires the pet to be able to attack
			pd->status.mode = static_cast<e_mode>(pd->status.mode|MD_CANATTACK);
		}
	}

	if (battle_config.pet_lv_rate && pd->master) {
		struct map_session_data *sd = pd->master;
		int lv;

		lv =sd->status.base_level*battle_config.pet_lv_rate/100;
		if (lv < 0)
			lv = 1;
		if (lv != pd->pet.level || opt&SCO_FIRST) {
			struct status_data *bstat = &pd->db->status, *status = &pd->status;

			pd->pet.level = lv;
			if (!(opt&SCO_FIRST)) // Lv Up animation
				clif_misceffect(&pd->bl, 0);
			status->rhw.atk = (bstat->rhw.atk*lv)/pd->db->lv;
			status->rhw.atk2 = (bstat->rhw.atk2*lv)/pd->db->lv;
			status->str = (bstat->str*lv)/pd->db->lv;
			status->agi = (bstat->agi*lv)/pd->db->lv;
			status->vit = (bstat->vit*lv)/pd->db->lv;
			status->int_ = (bstat->int_*lv)/pd->db->lv;
			status->dex = (bstat->dex*lv)/pd->db->lv;
			status->luk = (bstat->luk*lv)/pd->db->lv;

			status->rhw.atk = cap_value(status->rhw.atk, 1, battle_config.pet_max_atk1);
			status->rhw.atk2 = cap_value(status->rhw.atk2, 2, battle_config.pet_max_atk2);
			status->str = cap_value(status->str,1,battle_config.pet_max_stats);
			status->agi = cap_value(status->agi,1,battle_config.pet_max_stats);
			status->vit = cap_value(status->vit,1,battle_config.pet_max_stats);
			status->int_= cap_value(status->int_,1,battle_config.pet_max_stats);
			status->dex = cap_value(status->dex,1,battle_config.pet_max_stats);
			status->luk = cap_value(status->luk,1,battle_config.pet_max_stats);

			status_calc_misc(&pd->bl, &pd->status, lv);

			if (!(opt&SCO_FIRST)) // Not done the first time because the pet is not visible yet
				clif_send_petstatus(sd);
		}
	} else if (opt&SCO_FIRST) {
		status_calc_misc(&pd->bl, &pd->status, pd->db->lv);
		if (!battle_config.pet_lv_rate && pd->pet.level != pd->db->lv)
			pd->pet.level = pd->db->lv;
	}

	// Support rate modifier (1000 = 100%)
	pd->rate_fix = min(1000 * (pd->pet.intimate - battle_config.pet_support_min_friendly) / (1000 - battle_config.pet_support_min_friendly) + 500, USHRT_MAX);
	pd->rate_fix = min(apply_rate(pd->rate_fix, battle_config.pet_support_rate), USHRT_MAX);
}

/**
 * Get HP bonus modifiers
 * @param bl: block_list that will be checked
 * @param type: type of e_status_bonus (STATUS_BONUS_FIX or STATUS_BONUS_RATE)
 * @return bonus: total bonus for HP
 * @author [Cydh]
 */
static int status_get_hpbonus(struct block_list *bl, enum e_status_bonus type) {
	int bonus = 0;

	if (type == STATUS_BONUS_FIX) {
		struct status_change *sc = status_get_sc(bl);

		//Only for BL_PC
		if (bl->type == BL_PC) {
			struct map_session_data *sd = map_id2sd(bl->id);
			uint16 skill_lv;

			bonus += sd->bonus.hp;
			if ((skill_lv = pc_checkskill(sd,CR_TRUST)) > 0)
				bonus += skill_lv * 200;
			if (pc_checkskill(sd,SU_SPRITEMABLE) > 0)
				bonus += 1000;
			if (pc_checkskill(sd, SU_POWEROFSEA) > 0) {
				bonus += 1000;
				if (pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20)
					bonus += 3000;
			}
			if ((skill_lv = pc_checkskill(sd, NV_BREAKTHROUGH)) > 0)
				bonus += 350 * skill_lv + (skill_lv > 4 ? 250 : 0);
			if ((skill_lv = pc_checkskill(sd, NV_TRANSCENDENCE)) > 0)
				bonus += 350 * skill_lv + (skill_lv > 4 ? 250 : 0);
#ifndef HP_SP_TABLES
			if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 99)
				bonus += 2000; // Supernovice lvl99 hp bonus.
			if ((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && sd->status.base_level >= 150)
				bonus += 2000; // Supernovice lvl150 hp bonus.
#endif
		}

		//Bonus by SC
		if (sc) {
			if(sc->data[SC_INCMHP])
				bonus += sc->data[SC_INCMHP]->val1;
			if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
				bonus += 500;
			if (sc->data[SC_PROMOTE_HEALTH_RESERCH])
				bonus += sc->data[SC_PROMOTE_HEALTH_RESERCH]->val3;
			if(sc->data[SC_SOLID_SKIN_OPTION])
				bonus += 2000;
			if(sc->data[SC_MARIONETTE])
				bonus -= 1000;
			if(sc->data[SC_SWORDCLAN])
				bonus += 30;
			if(sc->data[SC_ARCWANDCLAN])
				bonus += 30;
			if(sc->data[SC_GOLDENMACECLAN])
				bonus += 30;
			if(sc->data[SC_CROSSBOWCLAN])
				bonus += 30;
			if(sc->data[SC_GLASTHEIM_HPSP])
				bonus += sc->data[SC_GLASTHEIM_HPSP]->val1;
#ifdef RENEWAL
			if (sc->data[SC_ANGELUS])
				bonus += sc->data[SC_ANGELUS]->val1 * 50;
#endif
		}
	} else if (type == STATUS_BONUS_RATE) {
		struct status_change *sc = status_get_sc(bl);

		//Bonus by SC
		if (sc) {
			//Increasing
			if(sc->data[SC_INCMHPRATE])
				bonus += sc->data[SC_INCMHPRATE]->val1;
			if(sc->data[SC_APPLEIDUN])
				bonus += sc->data[SC_APPLEIDUN]->val2;
			if(sc->data[SC_DELUGE])
				bonus += sc->data[SC_DELUGE]->val2;
			if(sc->data[SC_BERSERK])
				bonus += 200; //+200%
			if(sc->data[SC_MERC_HPUP])
				bonus += sc->data[SC_MERC_HPUP]->val2;
			if(sc->data[SC_EPICLESIS])
				bonus += sc->data[SC_EPICLESIS]->val2;
			if(sc->data[SC_FRIGG_SONG])
				bonus += sc->data[SC_FRIGG_SONG]->val2;
			if(sc->data[SC_LERADSDEW])
				bonus += sc->data[SC_LERADSDEW]->val3;
			if(sc->data[SC_FORCEOFVANGUARD])
				bonus += (3 * sc->data[SC_FORCEOFVANGUARD]->val1);
			if(sc->data[SC_INSPIRATION])
				bonus += (4 * sc->data[SC_INSPIRATION]->val1);
			if(sc->data[SC_RAISINGDRAGON])
				bonus += sc->data[SC_RAISINGDRAGON]->val1;
			if(sc->data[SC_GT_REVITALIZE])
				bonus += sc->data[SC_GT_REVITALIZE]->val2;
			if(sc->data[SC_ANGRIFFS_MODUS])
				bonus += (5 * sc->data[SC_ANGRIFFS_MODUS]->val1);
			if(sc->data[SC_PETROLOGY_OPTION])
				bonus += sc->data[SC_PETROLOGY_OPTION]->val2;
			if(sc->data[SC_POWER_OF_GAIA])
				bonus += sc->data[SC_POWER_OF_GAIA]->val3;
			if(sc->data[SC_CURSED_SOIL_OPTION])
				bonus += sc->data[SC_CURSED_SOIL_OPTION]->val2;
			if(sc->data[SC_UPHEAVAL_OPTION])
				bonus += sc->data[SC_UPHEAVAL_OPTION]->val2;
			if(sc->data[SC_LAUDAAGNUS])
				bonus += 2 + (sc->data[SC_LAUDAAGNUS]->val1 * 2);
#ifdef RENEWAL
			if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_HPRATE)
				bonus += 30;
#endif
			if(sc->data[SC_LUNARSTANCE])
				bonus += sc->data[SC_LUNARSTANCE]->val2;
			if (sc->data[SC_LUXANIMA])
				bonus += sc->data[SC_LUXANIMA]->val3;
			if (sc->data[SC_MTF_MHP])
				bonus += sc->data[SC_MTF_MHP]->val1;
			if (sc->data[SC_FIRM_FAITH])
				bonus += sc->data[SC_FIRM_FAITH]->val2;

			//Decreasing
			if (sc->data[SC_VENOMBLEED] && sc->data[SC_VENOMBLEED]->val3 == 1)
				bonus -= 15;
			if(sc->data[SC_BEYONDOFWARCRY])
				bonus -= sc->data[SC_BEYONDOFWARCRY]->val3;
			if(sc->data[SC__WEAKNESS])
				bonus -= sc->data[SC__WEAKNESS]->val2;
			if(sc->data[SC_EQC])
				bonus -= sc->data[SC_EQC]->val3;
			if(sc->data[SC_PACKING_ENVELOPE3])
				bonus += sc->data[SC_PACKING_ENVELOPE3]->val1;
		}
		// Max rate reduce is -100%
		bonus = cap_value(bonus,-100,INT_MAX);
	}

	return min(bonus,INT_MAX);
}

/**
* HP bonus rate from equipment
*/
static int status_get_hpbonus_equip(TBL_PC *sd) {
	int bonus = 0;

	bonus += sd->hprate;

	return bonus -= 100; //Default hprate is 100, so it should be add 0%
}

/**
* HP bonus rate from usable items
*/
static int status_get_hpbonus_item(block_list *bl) {
	int bonus = 0;

	struct status_change *sc = status_get_sc(bl);

	//Bonus by SC
	if (sc) {
		if (sc->data[SC_INCREASE_MAXHP])
			bonus += sc->data[SC_INCREASE_MAXHP]->val1;
		if (sc->data[SC_MUSTLE_M])
			bonus += sc->data[SC_MUSTLE_M]->val1;

		if (sc->data[SC_MYSTERIOUS_POWDER])
			bonus -= sc->data[SC_MYSTERIOUS_POWDER]->val1;
	}

	// Max rate reduce is -100%
	return cap_value(bonus, -100, INT_MAX);
}

/**
 * Get SP bonus modifiers
 * @param bl: block_list that will be checked
 * @param type: type of e_status_bonus (STATUS_BONUS_FIX or STATUS_BONUS_RATE)
 * @return bonus: total bonus for SP
 * @author [Cydh]
 */
static int status_get_spbonus(struct block_list *bl, enum e_status_bonus type) {
	int bonus = 0;

	if (type == STATUS_BONUS_FIX) {
		struct status_change *sc = status_get_sc(bl);

		//Only for BL_PC
		if (bl->type == BL_PC) {
			struct map_session_data *sd = map_id2sd(bl->id);
			uint16 skill_lv;

			bonus += sd->bonus.sp;
			if ((skill_lv = pc_checkskill(sd,SL_KAINA)) > 0)
				bonus += 30 * skill_lv;
			if ((skill_lv = pc_checkskill(sd,RA_RESEARCHTRAP)) > 0)
				bonus += 200 + 20 * skill_lv;
			if ((skill_lv = pc_checkskill(sd,WM_LESSON)) > 0)
				bonus += 30 * skill_lv;
			if (pc_checkskill(sd,SU_SPRITEMABLE) > 0)
				bonus += 100;
			if (pc_checkskill(sd, SU_POWEROFSEA) > 0) {
				bonus += 100;
				if (pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20)
					bonus += 300;
			}
			if ((skill_lv = pc_checkskill(sd, NV_BREAKTHROUGH)) > 0)
				bonus += 30 * skill_lv + (skill_lv > 4 ? 50 : 0);
			if ((skill_lv = pc_checkskill(sd, NV_TRANSCENDENCE)) > 0)
				bonus += 30 * skill_lv + (skill_lv > 4 ? 50 : 0);
		}

		//Bonus by SC
		if (sc) {
			if(sc->data[SC_INCMSP])
				bonus += sc->data[SC_INCMSP]->val1;
			if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
				bonus += 50;
			if(sc->data[SC_SWORDCLAN])
				bonus += 10;
			if(sc->data[SC_ARCWANDCLAN])
				bonus += 10;
			if(sc->data[SC_GOLDENMACECLAN])
				bonus += 10;
			if(sc->data[SC_CROSSBOWCLAN])
				bonus += 10;
			if(sc->data[SC_GLASTHEIM_HPSP])
				bonus += sc->data[SC_GLASTHEIM_HPSP]->val2;
		}
	} else if (type == STATUS_BONUS_RATE) {
		struct status_change *sc = status_get_sc(bl);

		//Only for BL_PC
		if (bl->type == BL_PC) {
			struct map_session_data *sd = map_id2sd(bl->id);
			uint8 i;

			if((i = pc_checkskill(sd,HP_MEDITATIO)) > 0)
				bonus += i;
			if((i = pc_checkskill(sd,HW_SOULDRAIN)) > 0)
				bonus += 2 * i;
#ifdef RENEWAL
			if ((i = pc_checkskill(sd, (sd->status.sex ? BA_MUSICALLESSON : DC_DANCINGLESSON))) > 0)
				bonus += i;
			if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_SPRATE)
				bonus += 30;
#endif
		}

		//Bonus by SC
		if (sc) {
			//Increasing
			if(sc->data[SC_INCMSPRATE])
				bonus += sc->data[SC_INCMSPRATE]->val1;
			if(sc->data[SC_RAISINGDRAGON])
				bonus += sc->data[SC_RAISINGDRAGON]->val1;
			if(sc->data[SC_SERVICE4U])
				bonus += sc->data[SC_SERVICE4U]->val2;
			if(sc->data[SC_MERC_SPUP])
				bonus += sc->data[SC_MERC_SPUP]->val2;
			if (sc->data[SC_LUXANIMA])
				bonus += sc->data[SC_LUXANIMA]->val3;
			if (sc->data[SC_MTF_MSP])
				bonus += sc->data[SC_MTF_MSP]->val1;
			if(sc->data[SC_PACKING_ENVELOPE4])
				bonus += sc->data[SC_PACKING_ENVELOPE4]->val1;

			//Decreasing
			if (sc->data[SC_MELODYOFSINK])
				bonus -= sc->data[SC_MELODYOFSINK]->val3;
		}
		// Max rate reduce is -100%
		bonus = cap_value(bonus,-100,INT_MAX);
	}

	return min(bonus,INT_MAX);
}

/**
* SP bonus rate from equipment
*/
static int status_get_spbonus_equip(TBL_PC *sd) {
	int bonus = 0;

	bonus += sd->sprate;

	return bonus -= 100; //Default sprate is 100, so it should be add 0%
}

/**
* SP bonus rate from usable items
*/
static int status_get_spbonus_item(block_list *bl) {
	int bonus = 0;

	struct status_change *sc = status_get_sc(bl);

	//Bonus by SC
	if (sc) {
		if (sc->data[SC_INCREASE_MAXSP])
			bonus += sc->data[SC_INCREASE_MAXSP]->val1;
		if (sc->data[SC_LIFE_FORCE_F])
			bonus += sc->data[SC_LIFE_FORCE_F]->val1;
		if (sc->data[SC_VITATA_500])
			bonus += sc->data[SC_VITATA_500]->val2;
		if (sc->data[SC_ENERGY_DRINK_RESERCH])
			bonus += sc->data[SC_ENERGY_DRINK_RESERCH]->val3;
	}

	// Max rate reduce is -100%
	return cap_value(bonus, -100, INT_MAX);
}

/**
 * Get AP bonus modifiers
 * @param bl: block_list that will be checked
 * @param type: type of e_status_bonus (STATUS_BONUS_FIX or STATUS_BONUS_RATE)
 * @return bonus: total bonus for AP
 */
static int status_get_apbonus(struct block_list *bl, enum e_status_bonus type) {
	int bonus = 0;

	if (type == STATUS_BONUS_FIX) {
		struct status_change *sc = status_get_sc(bl);

		//Only for BL_PC
		if (bl->type == BL_PC) {
			struct map_session_data *sd = map_id2sd(bl->id);
			//uint16 skill_lv;

			bonus += sd->bonus.ap;
			//if ((skill_lv = pc_checkskill(sd, NV_BASIC)) > 0)
			//	bonus += 100 * skill_lv;
		}

		//Bonus by SC
		if (sc) {
			//if (sc->data[SC_NONE])
			//	bonus += sc->data[SC_NONE]->val1;
		}
	} else if (type == STATUS_BONUS_RATE) {
		struct status_change *sc = status_get_sc(bl);

		//Only for BL_PC
		if (bl->type == BL_PC) {
			struct map_session_data *sd = map_id2sd(bl->id);
			//uint8 i;

			//if ((i = pc_checkskill(sd, NV_BASIC)) > 0)
			//	bonus += 100 * i;
		}

		//Bonus by SC
		if (sc) {
			//if (sc->data[SC_NONE])
			//	bonus += sc->data[SC_NONE]->val1;
		}
		// Max rate reduce is -100%
		bonus = cap_value(bonus, -100, INT_MAX);
	}

	return min(bonus, INT_MAX);
}

/**
 * AP bonus rate from equipment
 * @param sd: Player data
 * @return AP rate
 */
static int status_get_apbonus_equip(TBL_PC *sd) {
	int bonus = 0;

	bonus += sd->aprate;

	return bonus -= 100; //Default aprate is 100, so it should be add 0%
}

/**
 * AP bonus rate from usable items
 * @param bl: Object to check against
 * @return AP bonus
 */
static int status_get_apbonus_item(block_list *bl) {
	int bonus = 0;

	struct status_change *sc = status_get_sc(bl);

	//Bonus by SC
	if (sc) {
		//if (sc->data[SC_NONE])
		//	bonus += sc->data[SC_NONE]->val1;
	}

	// Max rate reduce is -100%
	return cap_value(bonus, -100, INT_MAX);
}

/**
 * Get final MaxHP or MaxSP for player. References: http://irowiki.org/wiki/Max_HP and http://irowiki.org/wiki/Max_SP
 * The calculation needs base_level, base_status/battle_status (vit or int), additive modifier, and multiplicative modifier
 * @param sd Player
 * @param stat Vit/Int of player as param modifier
 * @param isHP true - calculates Max HP, false - calculated Max SP
 * @return max The max value of HP or SP
 */
static unsigned int status_calc_maxhpsp_pc(struct map_session_data* sd, unsigned int stat, bool isHP) {
	nullpo_ret(sd);

	double dmax = 0;
	uint32 level = umax(sd->status.base_level,1);
	std::shared_ptr<s_job_info> job = job_db.find(pc_mapid2jobid(sd->class_, sd->status.sex));

	if (job == nullptr)
		return 1;

	if (isHP) { //Calculates MaxHP
		double equip_bonus = 0, item_bonus = 0;
		dmax = job->base_hp[level-1] * (1 + (umax(stat,1) * 0.01)) * ((sd->class_&JOBL_UPPER)?1.25:(pc_is_taekwon_ranker(sd))?3:1);
		dmax += status_get_hpbonus(&sd->bl,STATUS_BONUS_FIX);
		equip_bonus = (dmax * status_get_hpbonus_equip(sd) / 100);
		item_bonus = (dmax * status_get_hpbonus_item(&sd->bl) / 100);
		dmax += equip_bonus + item_bonus;
		dmax += (int64)(dmax * status_get_hpbonus(&sd->bl,STATUS_BONUS_RATE) / 100); //Aegis accuracy
	}
	else { //Calculates MaxSP
		double equip_bonus = 0, item_bonus = 0;
		dmax = job->base_sp[level-1] * (1 + (umax(stat,1) * 0.01)) * ((sd->class_&JOBL_UPPER)?1.25:(pc_is_taekwon_ranker(sd))?3:1);
		dmax += status_get_spbonus(&sd->bl,STATUS_BONUS_FIX);
		equip_bonus = (dmax * status_get_spbonus_equip(sd) / 100);
		item_bonus = (dmax * status_get_spbonus_item(&sd->bl) / 100);
		dmax += equip_bonus + item_bonus;
		dmax += (int64)(dmax * status_get_spbonus(&sd->bl,STATUS_BONUS_RATE) / 100); //Aegis accuracy
	}

	//Make sure it's not negative before casting to unsigned int
	if(dmax < 1) dmax = 1;

	return cap_value((unsigned int)dmax,1,UINT_MAX);
}

/**
 * Get final MaxAP for player.
 * @param sd: Player data
 * @return AP amount
 */
static unsigned int status_calc_maxap_pc(struct map_session_data* sd) {
	double dmax = 0, equip_bonus = 0, item_bonus = 0;

	nullpo_ret(sd);

	dmax = (sd->class_&JOBL_FOURTH) ? 200 : 0;
	dmax += status_get_apbonus(&sd->bl, STATUS_BONUS_FIX);
	equip_bonus = (dmax * status_get_apbonus_equip(sd) / 100);
	item_bonus = (dmax * status_get_apbonus_item(&sd->bl) / 100);
	dmax += equip_bonus + item_bonus;
	dmax += (int64)(dmax * status_get_apbonus(&sd->bl, STATUS_BONUS_RATE) / 100);// Aegis accuracy

	//Make sure it's not negative before casting to unsigned int
	if (dmax < 0) dmax = 0;

	return cap_value((unsigned int)dmax, 0, UINT_MAX);
}

/**
 * Calculates player's weight
 * @param sd: Player object
 * @param flag: Calculation type
 *   CALCWT_ITEM - Item weight
 *   CALCWT_MAXBONUS - Skill/Status/Configuration max weight bonus
 * @return false - failed, true - success
 */
bool status_calc_weight(struct map_session_data *sd, enum e_status_calc_weight_opt flag)
{
	int b_weight, b_max_weight, skill, i;
	struct status_change *sc;

	nullpo_retr(false, sd);

	sc = &sd->sc;
	b_max_weight = sd->max_weight; // Store max weight for later comparison
	b_weight = sd->weight; // Store current weight for later comparison
	sd->max_weight = job_db.get_maxWeight(pc_mapid2jobid(sd->class_, sd->status.sex)) + sd->status.str * 300; // Recalculate max weight

	if (flag&CALCWT_ITEM) {
		sd->weight = 0; // Reset current weight

		for(i = 0; i < MAX_INVENTORY; i++) {
			if (!sd->inventory.u.items_inventory[i].nameid || sd->inventory_data[i] == NULL)
				continue;
			sd->weight += sd->inventory_data[i]->weight * sd->inventory.u.items_inventory[i].amount;
		}
	}

	if (flag&CALCWT_MAXBONUS) {
		// Skill/Status bonus weight increases
		sd->max_weight += sd->add_max_weight; // From bAddMaxWeight
		if ((skill = pc_checkskill(sd, MC_INCCARRY)) > 0)
			sd->max_weight += 2000 * skill;
		if (pc_isriding(sd) && pc_checkskill(sd, KN_RIDING) > 0)
			sd->max_weight += 10000;
		else if (pc_isridingdragon(sd))
			sd->max_weight += 5000 + 2000 * pc_checkskill(sd, RK_DRAGONTRAINING);
		if (sc->data[SC_KNOWLEDGE])
			sd->max_weight += sd->max_weight * sc->data[SC_KNOWLEDGE]->val1 / 10;
		if ((skill = pc_checkskill(sd, ALL_INCCARRY)) > 0)
			sd->max_weight += 2000 * skill;
		if (pc_ismadogear(sd))
			sd->max_weight += 15000;
	}

	// Update the client if the new weight calculations don't match
	if (b_weight != sd->weight)
		clif_updatestatus(sd, SP_WEIGHT);
	if (b_max_weight != sd->max_weight) {
		clif_updatestatus(sd, SP_MAXWEIGHT);
		pc_updateweightstatus(sd);
	}

	return true;
}

/**
 * Calculates player's cart weight
 * @param sd: Player object
 * @param flag: Calculation type
 *   CALCWT_ITEM - Cart item weight
 *   CALCWT_MAXBONUS - Skill/Status/Configuration max weight bonus
 *   CALCWT_CARTSTATE - Whether to check for cart state
 * @return false - failed, true - success
 */
bool status_calc_cart_weight(struct map_session_data *sd, enum e_status_calc_weight_opt flag)
{
	int b_cart_weight_max, i;

	nullpo_retr(false, sd);

	if (!pc_iscarton(sd) && !(flag&CALCWT_CARTSTATE))
		return false;

	b_cart_weight_max = sd->cart_weight_max; // Store cart max weight for later comparison
	sd->cart_weight_max = battle_config.max_cart_weight; // Recalculate max weight

	if (flag&CALCWT_ITEM) {
		sd->cart_weight = 0; // Reset current cart weight
		sd->cart_num = 0; // Reset current cart item count

		for(i = 0; i < MAX_CART; i++) {
			if (!sd->cart.u.items_cart[i].nameid)
				continue;
			sd->cart_weight += itemdb_weight(sd->cart.u.items_cart[i].nameid) * sd->cart.u.items_cart[i].amount; // Recalculate current cart weight
			sd->cart_num++; // Recalculate current cart item count
		}
	}

	// Skill bonus max weight increases
	if (flag&CALCWT_MAXBONUS)
		sd->cart_weight_max += (pc_checkskill(sd, GN_REMODELING_CART) * 5000);

	// Update the client if the new weight calculations don't match
	if (b_cart_weight_max != sd->cart_weight_max)
		clif_updatestatus(sd, SP_CARTINFO);

	return true;
}

/**
 * Calculates player data from scratch without counting SC adjustments
 * Should be invoked whenever players raise stats, learn passive skills or change equipment
 * @param sd: Player object
 * @param opt: Whether it is first calc (login) or not
 * @return (-1) for too many recursive calls, (1) recursive call, (0) success
 */
int status_calc_pc_sub(struct map_session_data* sd, uint8 opt)
{
	static int calculating = 0; ///< Check for recursive call preemption. [Skotlex]
	struct status_data *base_status; ///< Pointer to the player's base status
	const struct status_change *sc = &sd->sc;
	struct s_skill b_skill[MAX_SKILL]; ///< Previous skill tree
	int i, skill, refinedef = 0;
	short index = -1;

	if (++calculating > 10) // Too many recursive calls!
		return -1;

	// Remember player-specific values that are currently being shown to the client (for refresh purposes)
	memcpy(b_skill, &sd->status.skill, sizeof(b_skill));

	pc_calc_skilltree(sd);	// SkillTree calculation

	if (opt&SCO_FIRST) {
		// Load Hp/SP from char-received data.
		sd->battle_status.hp = sd->status.hp;
		sd->battle_status.sp = sd->status.sp;
		if (battle_config.keep_ap_on_logout == 1)
			sd->battle_status.ap = sd->status.ap;
		sd->regen.sregen = &sd->sregen;
		sd->regen.ssregen = &sd->ssregen;
	}

	base_status = &sd->base_status;
	// These are not zeroed. [zzo]
	sd->hprate = 100;
	sd->sprate = 100;
	sd->aprate = 100;
	sd->castrate = 100;
	sd->dsprate = 100;
	sd->hprecov_rate = 100;
	sd->sprecov_rate = 100;
	sd->matk_rate = 100;
	sd->critical_rate = sd->hit_rate = sd->flee_rate = sd->flee2_rate = 100;
	sd->def_rate = sd->def2_rate = sd->mdef_rate = sd->mdef2_rate = 100;
	sd->patk_rate = sd->smatk_rate = 100;
	sd->res_rate = sd->mres_rate = 100;
	sd->hplus_rate = sd->crate_rate = 100;
	sd->regen.state.block = 0;
	sd->add_max_weight = 0;

	sd->indexed_bonus = {};

	memset (&sd->right_weapon.overrefine, 0, sizeof(sd->right_weapon) - sizeof(sd->right_weapon.atkmods));
	memset (&sd->left_weapon.overrefine, 0, sizeof(sd->left_weapon) - sizeof(sd->left_weapon.atkmods));

	if (sd->special_state.intravision)
		clif_status_load(&sd->bl, EFST_CLAIRVOYANCE, 0);

	if (sd->special_state.no_walk_delay)
		clif_status_load(&sd->bl, EFST_ENDURE, 0);

	memset(&sd->special_state,0,sizeof(sd->special_state));

	if (pc_isvip(sd)) // Magic Stone requirement avoidance for VIP.
		sd->special_state.no_gemstone = battle_config.vip_gemstone;

	if (!sd->state.permanent_speed) {
		memset(&base_status->max_hp, 0, sizeof(struct status_data)-(sizeof(base_status->hp)+sizeof(base_status->sp)+sizeof(base_status->ap)));
		base_status->speed = DEFAULT_WALK_SPEED;
	} else {
		int pSpeed = base_status->speed;

		memset(&base_status->max_hp, 0, sizeof(struct status_data)-(sizeof(base_status->hp)+sizeof(base_status->sp)+sizeof(base_status->ap)));
		base_status->speed = pSpeed;
	}

	// !FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
	// Give them all modes except these (useful for clones)
	base_status->mode = static_cast<e_mode>(MD_MASK&~(MD_STATUSIMMUNE|MD_IGNOREMELEE|MD_IGNOREMAGIC|MD_IGNORERANGED|MD_IGNOREMISC|MD_DETECTOR|MD_ANGRY|MD_TARGETWEAK));

	base_status->size = (sd->class_&JOBL_BABY) ? SZ_SMALL : (((sd->class_&MAPID_BASEMASK) == MAPID_SUMMONER) ? battle_config.summoner_size : SZ_MEDIUM);
	if (battle_config.character_size && pc_isriding(sd)) { // [Lupus]
		if (sd->class_&JOBL_BABY) {
			if (battle_config.character_size&SZ_BIG)
				base_status->size++;
		} else
		if(battle_config.character_size&SZ_MEDIUM)
			base_status->size++;
	}
	base_status->aspd_rate = 1000;
	base_status->ele_lv = 1;
	base_status->race = ((sd->class_&MAPID_BASEMASK) == MAPID_SUMMONER) ? battle_config.summoner_race : RC_PLAYER_HUMAN;
	base_status->class_ = CLASS_NORMAL;

	sd->autospell.clear();
	sd->autospell2.clear();
	sd->autospell3.clear();
	sd->addeff.clear();
	sd->addeff_atked.clear();
	sd->addeff_onskill.clear();
	sd->skillatk.clear();
	sd->skillusesprate.clear();
	sd->skillusesp.clear();
	sd->skillheal.clear();
	sd->skillheal2.clear();
	sd->skillblown.clear();
	sd->skillcastrate.clear();
	sd->skillfixcastrate.clear();
	sd->subskill.clear();
	sd->skillcooldown.clear();
	sd->skillfixcast.clear();
	sd->skillvarcast.clear();
	sd->add_def.clear();
	sd->add_mdef.clear();
	sd->add_mdmg.clear();
	sd->reseff.clear();
	sd->itemgrouphealrate.clear();
	sd->add_drop.clear();
	sd->itemhealrate.clear();
	sd->subele2.clear();
	sd->subrace3.clear();
	sd->skilldelay.clear();
	sd->sp_vanish.clear();
	sd->hp_vanish.clear();
	sd->itemsphealrate.clear();
	sd->itemgroupsphealrate.clear();

	// Zero up structures...
	memset(&sd->hp_loss, 0, sizeof(sd->hp_loss)
		+ sizeof(sd->sp_loss)
		+ sizeof(sd->hp_regen)
		+ sizeof(sd->sp_regen)
		+ sizeof(sd->percent_hp_regen)
		+ sizeof(sd->percent_sp_regen)
		+ sizeof(sd->def_set_race)
		+ sizeof(sd->mdef_set_race)
		+ sizeof(sd->norecover_state_race)
		+ sizeof(sd->hp_vanish_race)
		+ sizeof(sd->sp_vanish_race)
	);

	memset(&sd->bonus, 0, sizeof(sd->bonus));

	// Autobonus
	pc_delautobonus(*sd, sd->autobonus, true);
	pc_delautobonus(*sd, sd->autobonus2, true);
	pc_delautobonus(*sd, sd->autobonus3, true);

	// Parse equipment
	for (i = 0; i < EQI_MAX; i++) {
		current_equip_item_index = index = sd->equip_index[i]; // We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
		current_equip_combo_pos = 0;
		if (index < 0)
			continue;
		if (i == EQI_AMMO)
			continue;
		if (pc_is_same_equip_index((enum equip_index)i, sd->equip_index, index))
			continue;
		if (!sd->inventory_data[index])
			continue;

		base_status->def += sd->inventory_data[index]->def;

		// Items may be equipped, their effects however are nullified.
		if (opt&SCO_FIRST && sd->inventory_data[index]->equip_script && (pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT)
			|| !itemdb_isNoEquip(sd->inventory_data[index],sd->bl.m))) { // Execute equip-script on login
			run_script(sd->inventory_data[index]->equip_script,0,sd->bl.id,0);
			if (!calculating)
				return 1;
		}

		// Sanitize the refine level in case someone decreased the value inbetween
		if (sd->inventory.u.items_inventory[index].refine > MAX_REFINE)
			sd->inventory.u.items_inventory[index].refine = MAX_REFINE;

		std::shared_ptr<s_refine_level_info> info = refine_db.findCurrentLevelInfo( *sd->inventory_data[index], sd->inventory.u.items_inventory[index] );

		if (sd->inventory_data[index]->type == IT_WEAPON) {
			int wlv = sd->inventory_data[index]->weapon_level;
			struct weapon_data *wd;
			struct weapon_atk *wa;

			if(wlv >= MAX_WEAPON_LEVEL)
				wlv = MAX_WEAPON_LEVEL;

			if(i == EQI_HAND_L && sd->inventory.u.items_inventory[index].equip == EQP_HAND_L) {
				wd = &sd->left_weapon; // Left-hand weapon
				wa = &base_status->lhw;
			} else {
				wd = &sd->right_weapon;
				wa = &base_status->rhw;
			}
			wa->atk += sd->inventory_data[index]->atk;
			if( info != nullptr ){
				wa->atk2 += info->bonus / 100;

#ifdef RENEWAL
				// TODO: additional grade bonus

				if( wlv == 5 ){
					base_status->patk += sd->inventory.u.items_inventory[index].refine * 2;
					base_status->smatk += sd->inventory.u.items_inventory[index].refine * 2;
				}
#endif
			}
#ifdef RENEWAL
			if (sd->bonus.weapon_atk_rate)
				wa->atk += wa->atk * sd->bonus.weapon_atk_rate / 100;
			wa->matk += sd->inventory_data[index]->matk;
			wa->wlv = wlv;
			// Renewal magic attack refine bonus
			if( info != nullptr && sd->weapontype1 != W_BOW ){
				wa->matk += info->bonus / 100;

				// TODO: additional grade bonus
			}
#endif
			// Overrefine bonus.
			if( info != nullptr ){
				wd->overrefine = info->randombonus_max / 100;
			}

			wa->range += sd->inventory_data[index]->range;
			if(sd->inventory_data[index]->script && (pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT) || !itemdb_isNoEquip(sd->inventory_data[index],sd->bl.m))) {
				if (wd == &sd->left_weapon) {
					sd->state.lr_flag = 1;
					run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
					sd->state.lr_flag = 0;
				} else
					run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
				if (!calculating) // Abort, run_script retriggered this. [Skotlex]
					return 1;
			}
#ifdef RENEWAL
			if (sd->bonus.weapon_matk_rate)
				wa->matk += wa->matk * sd->bonus.weapon_matk_rate / 100;
#endif
			if(sd->inventory.u.items_inventory[index].card[0] == CARD0_FORGE) { // Forged weapon
				wd->star += (sd->inventory.u.items_inventory[index].card[1]>>8);
				if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg
				if(pc_famerank(MakeDWord(sd->inventory.u.items_inventory[index].card[2],sd->inventory.u.items_inventory[index].card[3]) ,MAPID_BLACKSMITH))
					wd->star += 10;
				if (!wa->ele) // Do not overwrite element from previous bonuses.
					wa->ele = (sd->inventory.u.items_inventory[index].card[1]&0x0f);
			}
		} else if(sd->inventory_data[index]->type == IT_ARMOR) {
			if( info != nullptr ){
				refinedef += info->bonus;

#ifdef RENEWAL
				if( sd->inventory_data[index]->armor_level == 2 ){
					base_status->res += sd->inventory.u.items_inventory[index].refine * 2;
					base_status->mres += sd->inventory.u.items_inventory[index].refine * 2;
				}
#endif
			}

			if(sd->inventory_data[index]->script && (pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT) || !itemdb_isNoEquip(sd->inventory_data[index],sd->bl.m))) {
				if( i == EQI_HAND_L ) // Shield
					sd->state.lr_flag = 3;
				run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
				if( i == EQI_HAND_L ) // Shield
					sd->state.lr_flag = 0;
				if (!calculating) // Abort, run_script retriggered this. [Skotlex]
					return 1;
			}
		} else if( sd->inventory_data[index]->type == IT_SHADOWGEAR ) { // Shadow System
			if (sd->inventory_data[index]->script && (pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT) || !itemdb_isNoEquip(sd->inventory_data[index],sd->bl.m))) {
				run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
				if( !calculating )
					return 1;
			}
		}
	}

	if(sd->equip_index[EQI_AMMO] >= 0) {
		index = sd->equip_index[EQI_AMMO];
		if(sd->inventory_data[index]) { // Arrows
			sd->bonus.arrow_atk += sd->inventory_data[index]->atk;
			sd->state.lr_flag = 2;
			if( !itemdb_group.item_exists(IG_THROWABLE, sd->inventory_data[index]->nameid) ) // Don't run scripts on throwable items
				run_script(sd->inventory_data[index]->script,0,sd->bl.id,0);
			sd->state.lr_flag = 0;
			if (!calculating) // Abort, run_script retriggered status_calc_pc. [Skotlex]
				return 1;
		}
	}

	// Process and check item combos
	if (!sd->combos.empty()) {
		for (const auto &combo : sd->combos) {
			std::shared_ptr<s_item_combo> item_combo;

			current_equip_item_index = -1;
			current_equip_combo_pos = combo->pos;

			if (combo->bonus == nullptr || !(item_combo = itemdb_combo.find(combo->id)))
				continue;

			bool no_run = false;
			size_t j = 0;

			// Check combo items
			while (j < item_combo->nameid.size()) {
				item_data *id = itemdb_exists(item_combo->nameid[j]);

				// Don't run the script if at least one of combo's pair has restriction
				if (id && !pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT) && itemdb_isNoEquip(id, sd->bl.m)) {
					no_run = true;
					break;
				}

				j++;
			}

			if (no_run)
				continue;

			run_script(combo->bonus, 0, sd->bl.id, 0);

			if (!calculating) // Abort, run_script retriggered this
				return 1;
		}
	}

	// Store equipment script bonuses
	memcpy(sd->indexed_bonus.param_equip,sd->indexed_bonus.param_bonus,sizeof(sd->indexed_bonus.param_equip));
	memset(sd->indexed_bonus.param_bonus, 0, sizeof(sd->indexed_bonus.param_bonus));

	base_status->def += (refinedef+50)/100;

	// Parse Cards
	for (i = 0; i < EQI_MAX; i++) {
		current_equip_item_index = index = sd->equip_index[i]; // We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
		current_equip_combo_pos = 0;
		if (index < 0)
			continue;
		if (i == EQI_AMMO)
			continue;
		if (pc_is_same_equip_index((enum equip_index)i, sd->equip_index, index))
			continue;

		if (sd->inventory_data[index]) {
			int j;
			struct item_data *data;

			// Card script execution.
			if (itemdb_isspecial(sd->inventory.u.items_inventory[index].card[0]))
				continue;
			for (j = 0; j < MAX_SLOTS; j++) { // Uses MAX_SLOTS to support Soul Bound system [Inkfish]
				int c = sd->inventory.u.items_inventory[index].card[j];
				current_equip_card_id= c;
				if(!c)
					continue;
				data = itemdb_exists(c);
				if(!data)
					continue;
				if (opt&SCO_FIRST && data->equip_script && (pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT) || !itemdb_isNoEquip(data,sd->bl.m))) {// Execute equip-script on login
					run_script(data->equip_script,0,sd->bl.id,0);
					if (!calculating)
						return 1;
				}
				if(!data->script)
					continue;
				if(!pc_has_permission(sd,PC_PERM_USE_ALL_EQUIPMENT) && itemdb_isNoEquip(data,sd->bl.m)) // Card restriction checks.
					continue;
				if(i == EQI_HAND_L && sd->inventory.u.items_inventory[index].equip == EQP_HAND_L) { // Left hand status.
					sd->state.lr_flag = 1;
					run_script(data->script,0,sd->bl.id,0);
					sd->state.lr_flag = 0;
				} else
					run_script(data->script,0,sd->bl.id,0);
				if (!calculating) // Abort, run_script his function. [Skotlex]
					return 1;
			}
		}
	}
	current_equip_card_id = 0; // Clear stored card ID [Secret]

	// Parse random options
	for (i = 0; i < EQI_MAX; i++) {
		current_equip_item_index = index = sd->equip_index[i];
		current_equip_combo_pos = 0;
		current_equip_opt_index = -1;

		if (index < 0)
			continue;
		if (i == EQI_AMMO)
			continue;
		if (pc_is_same_equip_index((enum equip_index)i, sd->equip_index, index))
			continue;
		
		if (sd->inventory_data[index]) {
			for (uint8 j = 0; j < MAX_ITEM_RDM_OPT; j++) {
				short opt_id = sd->inventory.u.items_inventory[index].option[j].id;

				if (!opt_id)
					continue;
				current_equip_opt_index = j;

				std::shared_ptr<s_random_opt_data> data = random_option_db.find(opt_id);

				if (!data || !data->script)
					continue;
				if (!pc_has_permission(sd, PC_PERM_USE_ALL_EQUIPMENT) && itemdb_isNoEquip(sd->inventory_data[index], sd->bl.m))
					continue;
				if (i == EQI_HAND_L && sd->inventory.u.items_inventory[index].equip == EQP_HAND_L) { // Left hand status.
					sd->state.lr_flag = 1;
					run_script(data->script, 0, sd->bl.id, 0);
					sd->state.lr_flag = 0;
				}
				else
					run_script(data->script, 0, sd->bl.id, 0);
				if (!calculating)
					return 1;
			}
		}
		current_equip_opt_index = -1;
	}

	if (sc->count && sc->data[SC_ITEMSCRIPT]) {
		struct item_data *data = itemdb_exists(sc->data[SC_ITEMSCRIPT]->val1);
		if (data && data->script)
			run_script(data->script, 0, sd->bl.id, 0);
	}

	pc_bonus_script(sd);

	if( sd->pd ) { // Pet Bonus
		struct pet_data *pd = sd->pd;
		std::shared_ptr<s_pet_db> pet_db_ptr = pd->get_pet_db();

		if (pet_db_ptr != nullptr && pet_db_ptr->pet_bonus_script)
			run_script(pet_db_ptr->pet_bonus_script,0,sd->bl.id,0);
		if (pet_db_ptr != nullptr && pd->pet.intimate > 0 && (!battle_config.pet_equip_required || pd->pet.equip > 0) && pd->state.skillbonus == 1 && pd->bonus)
			pc_bonus(sd,pd->bonus->type, pd->bonus->val);
	}

	// param_bonus now holds card bonuses.
	if(base_status->rhw.range < 1) base_status->rhw.range = 1;
	if(base_status->lhw.range < 1) base_status->lhw.range = 1;
	if(base_status->rhw.range < base_status->lhw.range)
		base_status->rhw.range = base_status->lhw.range;

	sd->bonus.double_rate += sd->bonus.double_add_rate;
	sd->bonus.perfect_hit += sd->bonus.perfect_hit_add;
	sd->bonus.splash_range += sd->bonus.splash_add_range;

	// Damage modifiers from weapon type
	std::shared_ptr<s_sizefix_db> right_weapon = size_fix_db.find(sd->weapontype1);
	std::shared_ptr<s_sizefix_db> left_weapon = size_fix_db.find(sd->weapontype2);

	sd->right_weapon.atkmods[SZ_SMALL] = right_weapon->small;
	sd->right_weapon.atkmods[SZ_MEDIUM] = right_weapon->medium;
	sd->right_weapon.atkmods[SZ_BIG] = right_weapon->large;
	sd->left_weapon.atkmods[SZ_SMALL] = left_weapon->small;
	sd->left_weapon.atkmods[SZ_MEDIUM] = left_weapon->medium;
	sd->left_weapon.atkmods[SZ_BIG] = left_weapon->large;

	if((pc_isriding(sd) || pc_isridingdragon(sd)) &&
		(sd->status.weapon==W_1HSPEAR || sd->status.weapon==W_2HSPEAR))
	{	// When Riding with spear, damage modifier to mid-class becomes
		// same as versus large size.
		sd->right_weapon.atkmods[SZ_MEDIUM] = sd->right_weapon.atkmods[SZ_BIG];
		sd->left_weapon.atkmods[SZ_MEDIUM] = sd->left_weapon.atkmods[SZ_BIG];
	}

// ----- STATS CALCULATION -----

	// Job bonuses
	std::shared_ptr<s_job_info> job_info = job_db.find( pc_mapid2jobid( sd->class_, sd->status.sex ) );

	if( job_info != nullptr ){
		const auto& bonus = job_info->job_bonus[sd->status.job_level-1];

		base_status->str += bonus[PARAM_STR];
		base_status->agi += bonus[PARAM_AGI];
		base_status->vit += bonus[PARAM_VIT];
		base_status->int_ += bonus[PARAM_INT];
		base_status->dex += bonus[PARAM_DEX];
		base_status->luk += bonus[PARAM_LUK];
		base_status->pow += bonus[PARAM_POW];
		base_status->sta += bonus[PARAM_STA];
		base_status->wis += bonus[PARAM_WIS];
		base_status->spl += bonus[PARAM_SPL];
		base_status->con += bonus[PARAM_CON];
		base_status->crt += bonus[PARAM_CRT];
	}

	// If a Super Novice has never died and is at least joblv 70, he gets all stats +10
	if(((sd->class_&MAPID_UPPERMASK) == MAPID_SUPER_NOVICE && (sd->status.job_level >= 70  || sd->class_&JOBL_THIRD)) && sd->die_counter == 0) {
		base_status->str += 10;
		base_status->agi += 10;
		base_status->vit += 10;
		base_status->int_+= 10;
		base_status->dex += 10;
		base_status->luk += 10;
	}

	// Absolute modifiers from passive skills
	if(pc_checkskill(sd,BS_HILTBINDING)>0)
		base_status->str++;
	if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0)
		base_status->int_ += (skill+1)/2; // +1 INT / 2 lv
	if((skill=pc_checkskill(sd,AC_OWL))>0)
		base_status->dex += skill;
	if((skill = pc_checkskill(sd,RA_RESEARCHTRAP))>0)
		base_status->int_ += skill;
	if (pc_checkskill(sd, SU_POWEROFLAND) > 0)
		base_status->int_ += 20;

	// Bonuses from cards and equipment as well as base stat, remember to avoid overflows.
	i = base_status->str + sd->status.str + sd->indexed_bonus.param_bonus[0] + sd->indexed_bonus.param_equip[0];
	base_status->str = cap_value(i,0,USHRT_MAX);
	i = base_status->agi + sd->status.agi + sd->indexed_bonus.param_bonus[1] + sd->indexed_bonus.param_equip[1];
	base_status->agi = cap_value(i,0,USHRT_MAX);
	i = base_status->vit + sd->status.vit + sd->indexed_bonus.param_bonus[2] + sd->indexed_bonus.param_equip[2];
	base_status->vit = cap_value(i,0,USHRT_MAX);
	i = base_status->int_+ sd->status.int_+ sd->indexed_bonus.param_bonus[3] + sd->indexed_bonus.param_equip[3];
	base_status->int_ = cap_value(i,0,USHRT_MAX);
	i = base_status->dex + sd->status.dex + sd->indexed_bonus.param_bonus[4] + sd->indexed_bonus.param_equip[4];
	base_status->dex = cap_value(i,0,USHRT_MAX);
	i = base_status->luk + sd->status.luk + sd->indexed_bonus.param_bonus[5] + sd->indexed_bonus.param_equip[5];
	base_status->luk = cap_value(i,0,USHRT_MAX);
	i = base_status->pow + sd->status.pow + sd->indexed_bonus.param_bonus[PARAM_POW] + sd->indexed_bonus.param_equip[PARAM_POW];
	base_status->pow = cap_value(i, 0, USHRT_MAX);
	i = base_status->sta + sd->status.sta + sd->indexed_bonus.param_bonus[PARAM_STA] + sd->indexed_bonus.param_equip[PARAM_STA];
	base_status->sta = cap_value(i, 0, USHRT_MAX);
	i = base_status->wis + sd->status.wis + sd->indexed_bonus.param_bonus[PARAM_WIS] + sd->indexed_bonus.param_equip[PARAM_WIS];
	base_status->wis = cap_value(i, 0, USHRT_MAX);
	i = base_status->spl + sd->status.spl + sd->indexed_bonus.param_bonus[PARAM_SPL] + sd->indexed_bonus.param_equip[PARAM_SPL];
	base_status->spl = cap_value(i, 0, USHRT_MAX);
	i = base_status->con + sd->status.con + sd->indexed_bonus.param_bonus[PARAM_CON] + sd->indexed_bonus.param_equip[PARAM_CON];
	base_status->con = cap_value(i, 0, USHRT_MAX);
	i = base_status->crt + sd->status.crt + sd->indexed_bonus.param_bonus[PARAM_CRT] + sd->indexed_bonus.param_equip[PARAM_CRT];
	base_status->crt = cap_value(i, 0, USHRT_MAX);

	if (sd->special_state.no_walk_delay) {
		if (sc->data[SC_ENDURE]) {
			if (sc->data[SC_ENDURE]->val4)
				sc->data[SC_ENDURE]->val4 = 0;
			status_change_end(&sd->bl, SC_ENDURE, INVALID_TIMER);
		}
		clif_status_load(&sd->bl, EFST_ENDURE, 1);
		base_status->mdef++;
	}

// ------ ATTACK CALCULATION ------

	// Base batk value is set in status_calc_misc
#ifndef RENEWAL
	// !FIXME: Weapon-type bonus (Why is the weapon_atk bonus applied to base attack?)
	if (sd->status.weapon < MAX_WEAPON_TYPE && sd->indexed_bonus.weapon_atk[sd->status.weapon])
		base_status->batk += sd->indexed_bonus.weapon_atk[sd->status.weapon];
	// Absolute modifiers from passive skills
	if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
		base_status->batk += 4;
#else
	base_status->watk = status_weapon_atk(base_status->rhw);
	base_status->watk2 = status_weapon_atk(base_status->lhw);
	base_status->eatk = sd->bonus.eatk;
#endif

// ----- HP MAX CALCULATION -----
	base_status->max_hp = sd->status.max_hp = status_calc_maxhpsp_pc(sd,base_status->vit,true);

	if(battle_config.hp_rate != 100)
		base_status->max_hp = (unsigned int)(battle_config.hp_rate * (base_status->max_hp/100.));

	if (sd->status.base_level < 100)
		base_status->max_hp = cap_value(base_status->max_hp,1,(unsigned int)battle_config.max_hp_lv99);
	else if (sd->status.base_level < 151)
		base_status->max_hp = cap_value(base_status->max_hp,1,(unsigned int)battle_config.max_hp_lv150);
	else
		base_status->max_hp = cap_value(base_status->max_hp,1,(unsigned int)battle_config.max_hp);

// ----- SP MAX CALCULATION -----
	base_status->max_sp = sd->status.max_sp = status_calc_maxhpsp_pc(sd,base_status->int_,false);

	if(battle_config.sp_rate != 100)
		base_status->max_sp = (unsigned int)(battle_config.sp_rate * (base_status->max_sp/100.));

	base_status->max_sp = cap_value(base_status->max_sp,1,(unsigned int)battle_config.max_sp);

// ----- AP MAX CALCULATION -----
	base_status->max_ap = sd->status.max_ap = status_calc_maxap_pc(sd);

	if (battle_config.ap_rate != 100)
		base_status->max_ap = (unsigned int)(battle_config.ap_rate * (base_status->max_ap / 100.));

	base_status->max_ap = cap_value(base_status->max_ap, 0, (unsigned int)battle_config.max_ap);

// ----- RESPAWN HP/SP/AP -----

	// Calc respawn hp and store it on base_status
	if (sd->special_state.restart_full_recover) {
		base_status->hp = base_status->max_hp;
		base_status->sp = base_status->max_sp;
	} else {
		if((sd->class_&MAPID_BASEMASK) == MAPID_NOVICE && !(sd->class_&JOBL_2)
			&& battle_config.restart_hp_rate < 50)
			base_status->hp = base_status->max_hp>>1;
		else
			base_status->hp = (int64)base_status->max_hp * battle_config.restart_hp_rate/100;
		if(!base_status->hp)
			base_status->hp = 1;

		base_status->sp = (int64)base_status->max_sp * battle_config.restart_sp_rate /100;

		if( !base_status->sp ) // The minimum for the respawn setting is SP:1
			base_status->sp = 1;

		base_status->ap = (int64)base_status->max_ap * battle_config.restart_ap_rate / 100;
	}

// ----- MISC CALCULATION -----
	status_calc_misc(&sd->bl, base_status, sd->status.base_level);

	// Equipment modifiers for misc settings
	if(sd->matk_rate < 0)
		sd->matk_rate = 0;

	if(sd->matk_rate != 100) {
		base_status->matk_max = base_status->matk_max * sd->matk_rate/100;
		base_status->matk_min = base_status->matk_min * sd->matk_rate/100;
	}

	if(sd->hit_rate < 0)
		sd->hit_rate = 0;
	if(sd->hit_rate != 100)
		base_status->hit = base_status->hit * sd->hit_rate/100;

	if(sd->flee_rate < 0)
		sd->flee_rate = 0;
	if(sd->flee_rate != 100)
		base_status->flee = base_status->flee * sd->flee_rate/100;

	if(sd->def2_rate < 0)
		sd->def2_rate = 0;
	if(sd->def2_rate != 100)
		base_status->def2 = base_status->def2 * sd->def2_rate/100;

	if(sd->mdef2_rate < 0)
		sd->mdef2_rate = 0;
	if(sd->mdef2_rate != 100)
		base_status->mdef2 = base_status->mdef2 * sd->mdef2_rate/100;

	if(sd->critical_rate < 0)
		sd->critical_rate = 0;
	if(sd->critical_rate != 100)
		base_status->cri = cap_value(base_status->cri * sd->critical_rate/100,SHRT_MIN,SHRT_MAX);
	if (pc_checkskill(sd, SU_POWEROFLIFE) > 0)
		base_status->cri += 200;

	if(sd->flee2_rate < 0)
		sd->flee2_rate = 0;
	if(sd->flee2_rate != 100)
		base_status->flee2 = base_status->flee2 * sd->flee2_rate/100;

	if (sd->patk_rate < 0)
		sd->patk_rate = 0;
	if (sd->patk_rate != 100)
		base_status->patk = base_status->patk * sd->patk_rate / 100;

	if (sd->smatk_rate < 0)
		sd->smatk_rate = 0;
	if (sd->smatk_rate != 100)
		base_status->smatk = base_status->smatk * sd->smatk_rate / 100;

	if (sd->res_rate < 0)
		sd->res_rate = 0;
	if (sd->res_rate != 100)
		base_status->res = base_status->res * sd->res_rate / 100;

	if (sd->mres_rate < 0)
		sd->mres_rate = 0;
	if (sd->mres_rate != 100)
		base_status->mres = base_status->mres * sd->mres_rate / 100;

	if (sd->hplus_rate < 0)
		sd->hplus_rate = 0;
	if (sd->hplus_rate != 100)
		base_status->hplus = base_status->hplus * sd->hplus_rate / 100;

	if (sd->crate_rate < 0)
		sd->crate_rate = 0;
	if (sd->crate_rate != 100)
		base_status->crate = base_status->crate * sd->crate_rate / 100;

// ----- HIT CALCULATION -----

	// Absolute modifiers from passive skills
#ifndef RENEWAL
	if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
		base_status->hit += skill*2;
#endif
	if((skill=pc_checkskill(sd,AC_VULTURE))>0) {
#ifndef RENEWAL
		base_status->hit += skill;
#endif
		if(sd->status.weapon == W_BOW)
			base_status->rhw.range += skill;
	}
	if(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE) {
		if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0)
			base_status->hit += 2*skill;
		if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) {
			base_status->hit += skill;
			base_status->rhw.range += skill;
		}
	}
	if((sd->status.weapon == W_1HAXE || sd->status.weapon == W_2HAXE) && (skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0)
		base_status->hit += skill * 3;
	if((sd->status.weapon == W_MACE || sd->status.weapon == W_2HMACE) && (skill = pc_checkskill(sd,NC_TRAININGAXE)) > 0)
		base_status->hit += skill * 2;
	if (pc_checkskill(sd, SU_POWEROFLIFE) > 0)
		base_status->hit += 20;
	if ((skill = pc_checkskill_imperial_guard(sd, 2)) > 0)// IG_SPEAR_SWORD_M
		base_status->hit += skill * 3;

	if ((skill = pc_checkskill(sd, SU_SOULATTACK)) > 0)
		base_status->rhw.range += skill_get_range2(&sd->bl, SU_SOULATTACK, skill, true);

// ----- FLEE CALCULATION -----

	// Absolute modifiers from passive skills
	if((skill=pc_checkskill(sd,TF_MISS))>0)
		base_status->flee += skill*(sd->class_&JOBL_2 && (sd->class_&MAPID_BASEMASK) == MAPID_THIEF? 4 : 3);
	if((skill=pc_checkskill(sd,MO_DODGE))>0)
		base_status->flee += (skill*3)>>1;
	if (pc_checkskill(sd, SU_POWEROFLIFE) > 0)
		base_status->flee += 20;
	if ((skill = pc_checkskill(sd, SHC_SHADOW_SENSE)) > 0)
		base_status->flee += skill * 10;

// ----- CRITICAL CALCULATION -----

#ifdef RENEWAL
	if ((skill = pc_checkskill(sd, DC_DANCINGLESSON)) > 0)
		base_status->cri += skill * 10;
	if ((skill = pc_checkskill(sd, PR_MACEMASTERY)) > 0 && (sd->status.weapon == W_MACE || sd->status.weapon == W_2HMACE))
		base_status->cri += skill * 10;
#endif
	if ((skill = pc_checkskill(sd, SHC_SHADOW_SENSE)) > 0)
	{
		if (sd->status.weapon == W_DAGGER || sd->status.weapon == W_DOUBLE_DD || 
			sd->status.weapon == W_DOUBLE_DS || sd->status.weapon == W_DOUBLE_DA)
			base_status->cri += 100 + skill * 40;
		else if (sd->status.weapon == W_KATAR)
			base_status->cri += 50 + skill * 20;
	}

// ----- P.Atk/S.Matk CALCULATION -----
	if ((skill = pc_checkskill(sd, TR_STAGE_MANNER)) > 0 && (sd->status.weapon == W_BOW || sd->status.weapon == W_MUSICAL || sd->status.weapon == W_WHIP)) {
		base_status->patk += skill * 3;
		base_status->smatk += skill * 3;
	}

// ----- PHYSICAL RESISTANCE CALCULATION -----
	if ((skill = pc_checkskill_imperial_guard(sd, 1)) > 0)// IG_SHIELD_MASTERY
		base_status->res += skill * 3;

// ----- EQUIPMENT-DEF CALCULATION -----

	// Apply relative modifiers from equipment
	if(sd->def_rate < 0)
		sd->def_rate = 0;
	if(sd->def_rate != 100) {
		i = base_status->def * sd->def_rate/100;
		base_status->def = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
	}

	if(pc_ismadogear(sd) && pc_checkskill(sd, NC_MAINFRAME) > 0)
		base_status->def += 20 + (pc_checkskill(sd, NC_MAINFRAME) * 20);

#ifndef RENEWAL
	if (!battle_config.weapon_defense_type && base_status->def > battle_config.max_def) {
		base_status->def2 += battle_config.over_def_bonus*(base_status->def -battle_config.max_def);
		base_status->def = (unsigned char)battle_config.max_def;
	}
#endif

// ----- EQUIPMENT-MDEF CALCULATION -----

	// Apply relative modifiers from equipment
	if(sd->mdef_rate < 0)
		sd->mdef_rate = 0;
	if(sd->mdef_rate != 100) {
		i =  base_status->mdef * sd->mdef_rate/100;
		base_status->mdef = cap_value(i, DEFTYPE_MIN, DEFTYPE_MAX);
	}

#ifndef RENEWAL
	if (!battle_config.magic_defense_type && base_status->mdef > battle_config.max_def) {
		base_status->mdef2 += battle_config.over_def_bonus*(base_status->mdef -battle_config.max_def);
		base_status->mdef = (signed char)battle_config.max_def;
	}
#endif

// ----- ASPD CALCULATION -----

	/// Unlike other stats, ASPD rate modifiers from skills/SCs/items/etc are first all added together, then the final modifier is applied

	// Basic ASPD value
	i = status_base_amotion_pc(sd,base_status);
	base_status->amotion = cap_value(i,pc_maxaspd(sd),2000);

	// Relative modifiers from passive skills
	// Renewal modifiers are handled in status_base_amotion_pc
#ifndef RENEWAL_ASPD
	if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && sd->status.weapon == W_BOOK)
		base_status->aspd_rate -= 5*skill;
	if ((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && ((sd->class_&MAPID_THIRDMASK) == MAPID_STAR_EMPEROR || pc_is_maxjoblv(sd)))
		base_status->aspd_rate -= 30*skill;
	if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
		(sd->status.weapon >= W_REVOLVER && sd->status.weapon <= W_GRENADE))
		base_status->aspd_rate -= ((skill+1)/2) * 10;
	if(pc_isriding(sd))
		base_status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);
	else if(pc_isridingdragon(sd))
		base_status->aspd_rate += 250-50*pc_checkskill(sd,RK_DRAGONTRAINING);
#endif
	base_status->adelay = 2*base_status->amotion;


// ----- DMOTION -----

	i =  800-base_status->agi*4;
	base_status->dmotion = cap_value(i, 400, 800);
	if(battle_config.pc_damage_delay_rate != 100)
		base_status->dmotion = base_status->dmotion*battle_config.pc_damage_delay_rate/100;

// ----- MISC CALCULATIONS -----

	// Weight
	status_calc_weight(sd, CALCWT_MAXBONUS);
	status_calc_cart_weight(sd, CALCWT_MAXBONUS);

	if (pc_checkskill(sd, SM_MOVINGRECOVERY) > 0 || pc_ismadogear(sd))
		sd->regen.state.walk = 1;
	else
		sd->regen.state.walk = 0;

	// Skill SP cost
	if((skill=pc_checkskill(sd,HP_MANARECHARGE))>0 )
		sd->dsprate -= 4*skill;

	if(sc->data[SC_SERVICE4U])
		sd->dsprate -= sc->data[SC_SERVICE4U]->val3;

	if(sc->data[SC_SPCOST_RATE])
		sd->dsprate -= sc->data[SC_SPCOST_RATE]->val1;

	// Underflow protections.
	if(sd->dsprate < 0)
		sd->dsprate = 0;
	if(sd->castrate < 0)
		sd->castrate = 0;
	if(sd->hprecov_rate < 0)
		sd->hprecov_rate = 0;
	if(sd->sprecov_rate < 0)
		sd->sprecov_rate = 0;

	// Anti-element and anti-race
	if((skill=pc_checkskill(sd,CR_TRUST))>0)
		sd->indexed_bonus.subele[ELE_HOLY] += skill*5;
	if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
		sd->indexed_bonus.subele[ELE_NEUTRAL] += skill;
		sd->indexed_bonus.subele[ELE_FIRE] += skill*5;
	}
	if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0) {
#ifdef RENEWAL
		skill = skill * 2;
#else
		skill = skill * 4;
#endif
		sd->right_weapon.addrace[RC_DRAGON]+=skill;
		sd->left_weapon.addrace[RC_DRAGON]+=skill;
		sd->indexed_bonus.magic_addrace[RC_DRAGON]+=skill;
		sd->indexed_bonus.subrace[RC_DRAGON]+=skill;
	}
	if ((skill = pc_checkskill(sd, AB_EUCHARISTICA)) > 0) {
		sd->right_weapon.addrace[RC_DEMON] += skill;
		sd->right_weapon.addele[ELE_DARK] += skill;
		sd->left_weapon.addrace[RC_DEMON] += skill;
		sd->left_weapon.addele[ELE_DARK] += skill;
		sd->indexed_bonus.magic_addrace[RC_DEMON] += skill;
		sd->indexed_bonus.magic_addele[ELE_DARK] += skill;
		sd->indexed_bonus.subrace[RC_DEMON] += skill;
		sd->indexed_bonus.subele[ELE_DARK] += skill;
	}
	if ((skill = pc_checkskill(sd, DK_TWOHANDDEF)) > 0 && (sd->status.weapon == W_2HSWORD || sd->status.weapon == W_2HSPEAR || sd->status.weapon == W_2HAXE)) {
		uint8 defense_bonus[SZ_MAX][10] = {
			{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, // SZ_SMALL
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_MEDIUM
			{ 3, 5, 7, 9, 10, 12, 13, 15, 16, 18 }, // SZ_BIG
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // SZ_ALL
		};

		for( uint8 size = SZ_SMALL; size < SZ_MAX; size++ ){
			sd->indexed_bonus.weapon_subsize[size] += defense_bonus[size][skill - 1];
		}
	}
	if ((skill = pc_checkskill(sd, IQ_WILL_OF_FAITH)) > 0 && sd->status.weapon == W_KNUCKLE) {
		uint8 race_atk[10] = { 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
		uint8 race_def[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

		sd->right_weapon.addrace[RC_UNDEAD] += race_atk[skill - 1];
		sd->right_weapon.addrace[RC_DEMON] += race_atk[skill - 1];
		sd->left_weapon.addrace[RC_UNDEAD] += race_atk[skill - 1];
		sd->left_weapon.addrace[RC_DEMON] += race_atk[skill - 1];
		sd->indexed_bonus.subrace[RC_UNDEAD] += race_def[skill - 1];
		sd->indexed_bonus.subrace[RC_DEMON] += race_def[skill - 1];
	}
	if ((skill = pc_checkskill(sd, CD_MACE_BOOK_M)) > 0 && (sd->status.weapon == W_MACE || sd->status.weapon == W_2HMACE || sd->status.weapon == W_BOOK)) {
		uint8 attack_bonus[SZ_MAX][10] = {
			{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, // SZ_SMALL
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_MEDIUM
			{ 3, 5, 7, 9, 10, 12, 13, 15, 16, 18 }, // SZ_BIG
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // SZ_ALL
		};

		for( uint8 size = SZ_SMALL; size < SZ_MAX; size++ ){
			sd->right_weapon.addsize[size] += attack_bonus[size][skill - 1];
			sd->left_weapon.addsize[size] += attack_bonus[size][skill - 1];
		}
	}
	if ((skill = pc_checkskill(sd, CD_FIDUS_ANIMUS)) > 0) {
		uint8 holy_matk[10] = { 1, 3, 4, 6, 7, 9, 10, 12, 13, 15 };

		sd->indexed_bonus.magic_atk_ele[ELE_HOLY] += holy_matk[skill - 1];
	}
	if ((skill = pc_checkskill(sd, MT_TWOAXEDEF)) > 0 && sd->status.weapon == W_2HAXE) {
		uint8 defense_bonus[SZ_MAX][10] = {
			{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, // SZ_SMALL
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_MEDIUM
			{ 3, 5, 7, 9, 10, 12, 13, 15, 16, 18 }, // SZ_BIG
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // SZ_ALL
		};

		for( uint8 size = SZ_SMALL; size < SZ_MAX; size++ ){
				sd->indexed_bonus.weapon_subsize[size] += defense_bonus[size][skill - 1];
		}
	}
	if ((skill = pc_checkskill(sd, ABC_DAGGER_AND_BOW_M)) > 0 && (sd->status.weapon == W_DAGGER || sd->status.weapon == W_BOW || sd->status.weapon == W_DOUBLE_DD || sd->status.weapon == W_DOUBLE_DS || sd->status.weapon == W_DOUBLE_DA)) {
		uint8 attack_bonus[SZ_MAX][10] = {
			{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, // SZ_SMALL
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_MEDIUM
			{ 2, 4, 6, 8, 10, 12, 14, 16, 18, 20 }, // SZ_BIG
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // SZ_ALL
		};

		for( uint8 size = SZ_SMALL; size < SZ_MAX; size++ ){
			sd->right_weapon.addsize[size] += attack_bonus[size][skill - 1];
			sd->left_weapon.addsize[size] += attack_bonus[size][skill - 1];
		}
	}
	if ((skill = pc_checkskill(sd, ABC_MAGIC_SWORD_M)) > 0 && (sd->status.weapon == W_DAGGER || sd->status.weapon == W_1HSWORD || sd->status.weapon == W_DOUBLE_DD || sd->status.weapon == W_DOUBLE_SS || sd->status.weapon == W_DOUBLE_DS || sd->status.weapon == W_DOUBLE_DA || sd->status.weapon == W_DOUBLE_SA)) {
		uint8 attack_bonus[SZ_MAX][10] = {
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_SMALL
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_MEDIUM
			{ 2, 3, 5, 6, 8, 9, 11, 12, 14, 15 }, // SZ_BIG
			{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } // SZ_ALL
		};

		for( uint8 size = SZ_SMALL; size < SZ_MAX; size++ ){
			sd->indexed_bonus.magic_addsize[size] += attack_bonus[size][skill - 1];
		}
	}
	if ((skill = pc_checkskill(sd, EM_MAGIC_BOOK_M)) > 0 && sd->status.weapon == W_BOOK) {
		sd->indexed_bonus.magic_atk_ele[ELE_WATER] += skill;
		sd->indexed_bonus.magic_atk_ele[ELE_EARTH] += skill;
		sd->indexed_bonus.magic_atk_ele[ELE_FIRE] += skill;
		sd->indexed_bonus.magic_atk_ele[ELE_WIND] += skill;
		sd->indexed_bonus.magic_atk_ele[ELE_POISON] += skill;
	}

	if(sc->count) {
		if(sc->data[SC_CONCENTRATE]) { // Update the card-bonus data
			sc->data[SC_CONCENTRATE]->val3 = sd->indexed_bonus.param_bonus[1]; // Agi
			sc->data[SC_CONCENTRATE]->val4 = sd->indexed_bonus.param_bonus[4]; // Dex
		}
		if(sc->data[SC_SIEGFRIED]) {
			i = sc->data[SC_SIEGFRIED]->val2;
			sd->indexed_bonus.subele[ELE_WATER] += i;
			sd->indexed_bonus.subele[ELE_EARTH] += i;
			sd->indexed_bonus.subele[ELE_FIRE] += i;
			sd->indexed_bonus.subele[ELE_WIND] += i;
#ifndef RENEWAL
			sd->indexed_bonus.subele[ELE_POISON] += i;
			sd->indexed_bonus.subele[ELE_HOLY] += i;
			sd->indexed_bonus.subele[ELE_DARK] += i;
			sd->indexed_bonus.subele[ELE_GHOST] += i;
			sd->indexed_bonus.subele[ELE_UNDEAD] += i;
#endif
		}
#ifdef RENEWAL
		if (sc->data[SC_BASILICA]) {
			i = sc->data[SC_BASILICA]->val1 * 5;
			sd->right_weapon.addele[ELE_DARK] += i;
			sd->right_weapon.addele[ELE_UNDEAD] += i;
			sd->left_weapon.addele[ELE_DARK] += i;
			sd->left_weapon.addele[ELE_UNDEAD] += i;
			sd->indexed_bonus.magic_atk_ele[ELE_HOLY] += sc->data[SC_BASILICA]->val1 * 3;
		}
		if (sc->data[SC_FIREWEAPON])
			sd->indexed_bonus.magic_atk_ele[ELE_FIRE] += sc->data[SC_FIREWEAPON]->val1;
		if (sc->data[SC_WINDWEAPON])
			sd->indexed_bonus.magic_atk_ele[ELE_WIND] += sc->data[SC_WINDWEAPON]->val1;
		if (sc->data[SC_WATERWEAPON])
			sd->indexed_bonus.magic_atk_ele[ELE_WATER] += sc->data[SC_WATERWEAPON]->val1;
		if (sc->data[SC_EARTHWEAPON])
			sd->indexed_bonus.magic_atk_ele[ELE_EARTH] += sc->data[SC_EARTHWEAPON]->val1;
#endif
		if(sc->data[SC_PROVIDENCE]) {
			sd->indexed_bonus.subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
			sd->indexed_bonus.subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
		}
        if (sc->data[SC_GEFFEN_MAGIC1]) {
            sd->right_weapon.addrace[RC_PLAYER_HUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1;
            sd->right_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1;
            sd->left_weapon.addrace[RC_PLAYER_HUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1;
            sd->left_weapon.addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC1]->val1;
        }
        if (sc->data[SC_GEFFEN_MAGIC2]) {
            sd->indexed_bonus.magic_addrace[RC_PLAYER_HUMAN] += sc->data[SC_GEFFEN_MAGIC2]->val1;
            sd->indexed_bonus.magic_addrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC2]->val1;
        }
        if(sc->data[SC_GEFFEN_MAGIC3]) {
            sd->indexed_bonus.subrace[RC_PLAYER_HUMAN] += sc->data[SC_GEFFEN_MAGIC3]->val1;
            sd->indexed_bonus.subrace[RC_DEMIHUMAN] += sc->data[SC_GEFFEN_MAGIC3]->val1;
        }
		if(sc->data[SC_ARMOR_ELEMENT_WATER]) {	// This status change should grant card-type elemental resist.
			sd->indexed_bonus.subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT_WATER]->val1;
			sd->indexed_bonus.subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT_WATER]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT_WATER]->val3;
			sd->indexed_bonus.subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT_WATER]->val4;
		}
		if(sc->data[SC_ARMOR_ELEMENT_EARTH]) {	// This status change should grant card-type elemental resist.
			sd->indexed_bonus.subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT_EARTH]->val1;
			sd->indexed_bonus.subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT_EARTH]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT_EARTH]->val3;
			sd->indexed_bonus.subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT_EARTH]->val4;
		}
		if(sc->data[SC_ARMOR_ELEMENT_FIRE]) {	// This status change should grant card-type elemental resist.
			sd->indexed_bonus.subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT_FIRE]->val1;
			sd->indexed_bonus.subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT_FIRE]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT_FIRE]->val3;
			sd->indexed_bonus.subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT_FIRE]->val4;
		}
		if(sc->data[SC_ARMOR_ELEMENT_WIND]) {	// This status change should grant card-type elemental resist.
			sd->indexed_bonus.subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT_WIND]->val1;
			sd->indexed_bonus.subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT_WIND]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT_WIND]->val3;
			sd->indexed_bonus.subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT_WIND]->val4;
		}
		if(sc->data[SC_ARMOR_RESIST]) { // Undead Scroll
			sd->indexed_bonus.subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
			sd->indexed_bonus.subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
			sd->indexed_bonus.subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
		}
		if( sc->data[SC_FIRE_CLOAK_OPTION] ) {
			i = sc->data[SC_FIRE_CLOAK_OPTION]->val2;
			sd->indexed_bonus.subele[ELE_FIRE] += i;
			sd->indexed_bonus.subele[ELE_WATER] -= i;
		}
		if( sc->data[SC_WATER_DROP_OPTION] ) {
			i = sc->data[SC_WATER_DROP_OPTION]->val2;
			sd->indexed_bonus.subele[ELE_WATER] += i;
			sd->indexed_bonus.subele[ELE_WIND] -= i;
		}
		if( sc->data[SC_WIND_CURTAIN_OPTION] ) {
			i = sc->data[SC_WIND_CURTAIN_OPTION]->val2;
			sd->indexed_bonus.subele[ELE_WIND] += i;
			sd->indexed_bonus.subele[ELE_EARTH] -= i;
		}
		if( sc->data[SC_STONE_SHIELD_OPTION] ) {
			i = sc->data[SC_STONE_SHIELD_OPTION]->val2;
			sd->indexed_bonus.subele[ELE_EARTH] += i;
			sd->indexed_bonus.subele[ELE_FIRE] -= i;
		}
		if (sc->data[SC_MTF_MLEATKED] )
			sd->indexed_bonus.subele[ELE_NEUTRAL] += sc->data[SC_MTF_MLEATKED]->val3;
		if (sc->data[SC_MTF_CRIDAMAGE])
			sd->bonus.crit_atk_rate += sc->data[SC_MTF_CRIDAMAGE]->val1;
		if (sc->data[SC_GLASTHEIM_ATK]) {
			sd->indexed_bonus.ignore_mdef_by_race[RC_UNDEAD] += sc->data[SC_GLASTHEIM_ATK]->val1;
			sd->indexed_bonus.ignore_mdef_by_race[RC_DEMON] += sc->data[SC_GLASTHEIM_ATK]->val1;
		}
		if (sc->data[SC_LAUDARAMUS])
			sd->bonus.crit_atk_rate += 5 * sc->data[SC_LAUDARAMUS]->val1;
#ifdef RENEWAL
		if (sc->data[SC_FORTUNE])
			sd->bonus.crit_atk_rate += 2 * sc->data[SC_FORTUNE]->val1;
#endif
		if (sc->data[SC_SYMPHONYOFLOVER]) {
			sd->indexed_bonus.subele[ELE_GHOST] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3;
			sd->indexed_bonus.subele[ELE_HOLY] += sc->data[SC_SYMPHONYOFLOVER]->val1 * 3;
		}
		if (sc->data[SC_PYREXIA] && sc->data[SC_PYREXIA]->val3 == 0)
			sd->bonus.crit_atk_rate += sc->data[SC_PYREXIA]->val2;
		if (sc->data[SC_LUXANIMA]) {
			pc_bonus2(sd, SP_ADDSIZE, SZ_ALL, sc->data[SC_LUXANIMA]->val3);
			sd->bonus.crit_atk_rate += sc->data[SC_LUXANIMA]->val3;
			sd->bonus.short_attack_atk_rate += sc->data[SC_LUXANIMA]->val3;
			sd->bonus.long_attack_atk_rate += sc->data[SC_LUXANIMA]->val3;
		}
		if (sc->data[SC_STRIKING])
			sd->bonus.perfect_hit += 20 + 10 * pc_checkskill(sd, SO_STRIKING);
		if (sc->data[SC_VIGOR]) {
			// Skill desc says increases physical damage. Supposed to affect damage from base ATK right???
			// Because this is only boosting the ATK from the equipped weapon and not from base ATK. [Rytech]
			sd->right_weapon.addrace[RC_DEMIHUMAN] += 50;
			sd->left_weapon.addrace[RC_ANGEL] += 50;
		}
		if (sc->data[SC_DEADLY_DEFEASANCE])
			sd->special_state.no_magic_damage = 0;
		if (sc->data[SC_CLIMAX_DES_HU])
			sd->indexed_bonus.magic_atk_ele[ELE_WIND] += 30;
		if (sc->data[SC_CLIMAX_EARTH])
			sd->indexed_bonus.subele[ELE_EARTH] -= 100;
		if (sc->data[SC_CLIMAX_BLOOM])
			sd->indexed_bonus.subele[ELE_FIRE] -= 100;
		if (sc->data[SC_CLIMAX_CRYIMP]) {
			sd->indexed_bonus.subele[ELE_WATER] += 30;
			sd->indexed_bonus.magic_atk_ele[ELE_WATER] += 30;
		}
		if (sc->data[SC_SINCERE_FAITH])
			sd->bonus.perfect_hit += sc->data[SC_SINCERE_FAITH]->val3;
		if (sc->data[SC_HOLY_S]) {
			sd->indexed_bonus.subele[ELE_DARK] += sc->data[SC_HOLY_S]->val2;
			sd->indexed_bonus.subele[ELE_UNDEAD] += sc->data[SC_HOLY_S]->val2;
			sd->indexed_bonus.magic_atk_ele[ELE_HOLY] += sc->data[SC_HOLY_S]->val2;
		}
		if (sc->data[SC_SUMMON_ELEMENTAL_ARDOR])
			sd->indexed_bonus.magic_atk_ele[ELE_FIRE] += 10;
		if (sc->data[SC_SUMMON_ELEMENTAL_DILUVIO])
			sd->indexed_bonus.magic_atk_ele[ELE_WATER] += 10;
		if (sc->data[SC_SUMMON_ELEMENTAL_PROCELLA])
			sd->indexed_bonus.magic_atk_ele[ELE_WIND] += 10;
		if (sc->data[SC_SUMMON_ELEMENTAL_TERREMOTUS])
			sd->indexed_bonus.magic_atk_ele[ELE_EARTH] += 10;
		if (sc->data[SC_SUMMON_ELEMENTAL_SERPENS])
			sd->indexed_bonus.magic_atk_ele[ELE_POISON] += 10;
		if (sc->data[SC_FLAMEARMOR_OPTION]) {
			sd->indexed_bonus.subele[ELE_FIRE] += 100;
			sd->indexed_bonus.subele[ELE_WATER] -= 30;
		}
		if (sc->data[SC_CRYSTAL_ARMOR_OPTION]) {
			sd->indexed_bonus.subele[ELE_WATER] += 100;
			sd->indexed_bonus.subele[ELE_WIND] -= 30;
		}
		if (sc->data[SC_EYES_OF_STORM_OPTION]) {
			sd->indexed_bonus.subele[ELE_WIND] += 100;
			sd->indexed_bonus.subele[ELE_EARTH] -= 30;
		}
		if (sc->data[SC_STRONG_PROTECTION_OPTION]) {
			sd->indexed_bonus.subele[ELE_EARTH] += 100;
			sd->indexed_bonus.subele[ELE_FIRE] -= 30;
		}
		if (sc->data[SC_POISON_SHIELD_OPTION]) {
			sd->indexed_bonus.subele[ELE_POISON] += 100;
			sd->indexed_bonus.subele[ELE_HOLY] -= 30;
		}
	}
	status_cpy(&sd->battle_status, base_status);

// ----- CLIENT-SIDE REFRESH -----
	if(!sd->bl.prev) {
		// Will update on LoadEndAck
		calculating = 0;
		return 0;
	}
	if(memcmp(b_skill,sd->status.skill,sizeof(sd->status.skill)))
		clif_skillinfoblock(sd);

	// If the skill is learned, the status is infinite.
	if (pc_checkskill(sd, SU_SPRITEMABLE) > 0 && !sd->sc.data[SC_SPRITEMABLE])
		sc_start(&sd->bl, &sd->bl, SC_SPRITEMABLE, 100, 1, INFINITE_TICK);
	if (pc_checkskill(sd, SU_SOULATTACK) > 0 && !sd->sc.data[SC_SOULATTACK])
		sc_start(&sd->bl, &sd->bl, SC_SOULATTACK, 100, 1, INFINITE_TICK);

	calculating = 0;

	return 0;
}

/// Intermediate function since C++ does not have a try-finally syntax
int status_calc_pc_( struct map_session_data* sd, uint8 opt ){
	// Save the old script the player was attached to
	struct script_state* previous_st = sd->st;

	// Store the return value of the original function
	int ret = status_calc_pc_sub( sd, opt );

	// If an old script is present
	if( previous_st ){
		// Reattach the player to it, so that the limitations of that script kick back in
		script_attach_state( previous_st );
	}

	// Return the original return value
	return ret;
}

/**
 * Calculates Mercenary data
 * @param md: Mercenary object
 * @param opt: Whether it is first calc or not (0 on level up or status)
 * @return 0
 */
int status_calc_mercenary_(s_mercenary_data *md, uint8 opt)
{
	struct status_data *status = &md->base_status;
	s_mercenary *merc = &md->mercenary;

	if (opt&SCO_FIRST) {
		memcpy(status, &md->db->status, sizeof(struct status_data));
		status->class_ = CLASS_NORMAL;
		status->mode = static_cast<e_mode>(MD_CANMOVE|MD_CANATTACK);
		status->hp = status->max_hp;
		status->sp = status->max_sp;
		md->battle_status.hp = merc->hp;
		md->battle_status.sp = merc->sp;
		if (md->master)
			status->speed = status_get_speed(&md->master->bl);
	}

	status_calc_misc(&md->bl, status, md->db->lv);
	status_cpy(&md->battle_status, status);

	return 0;
}

/**
 * Calculates Homunculus data
 * @param hd: Homunculus object
 * @param opt: Whether it is first calc or not (0 on level up or status)
 * @return 1
 */
int status_calc_homunculus_(struct homun_data *hd, uint8 opt)
{
	struct status_data *status = &hd->base_status;
	struct s_homunculus *hom = &hd->homunculus;
	int skill_lv;
	int amotion;

	status->str = hom->str / 10;
	status->agi = hom->agi / 10;
	status->vit = hom->vit / 10;
	status->dex = hom->dex / 10;
	status->int_ = hom->int_ / 10;
	status->luk = hom->luk / 10;

	APPLY_HOMUN_LEVEL_STATWEIGHT();

	if (opt&SCO_FIRST) {
		const struct s_homunculus_db *db = hd->homunculusDB;
		status->def_ele = db->element;
		status->ele_lv = 1;
		status->race = db->race;
		status->class_ = CLASS_NORMAL;
		status->size = (hom->class_ == db->evo_class) ? db->evo_size : db->base_size;
		status->rhw.range = 1 + status->size;
		status->mode = static_cast<e_mode>(MD_CANMOVE|MD_CANATTACK);
		status->speed = DEFAULT_WALK_SPEED;
		if (battle_config.hom_setting&HOMSET_COPY_SPEED && hd->master)
			status->speed = status_get_speed(&hd->master->bl);

		status->hp = 1;
		status->sp = 1;
	}

	status->aspd_rate = 1000;

#ifdef RENEWAL
	amotion = hd->homunculusDB->baseASPD;
	amotion = amotion - amotion * (status->dex + hom->dex_value) / 1000 - (status->agi + hom->agi_value) * amotion / 250;
	status->def = status->mdef = 0;
#else
	skill_lv = hom->level / 10 + status->vit / 5;
	status->def = cap_value(skill_lv, 0, 99);

	skill_lv = hom->level / 10 + status->int_ / 5;
	status->mdef = cap_value(skill_lv, 0, 99);

	amotion = (1000 - 4 * status->agi - status->dex) * hd->homunculusDB->baseASPD / 1000;
#endif

	status->amotion = cap_value(amotion, battle_config.max_aspd, 2000);
	status->adelay = status->amotion; //It seems adelay = amotion for Homunculus.

	status->max_hp = hom->max_hp;
	status->max_sp = hom->max_sp;

	hom_calc_skilltree(hd, 0);

	if((skill_lv = hom_checkskill(hd, HAMI_SKIN)) > 0)
		status->def += skill_lv * 4;

	if((skill_lv = hom_checkskill(hd, HVAN_INSTRUCT)) > 0) {
		status->int_ += 1 + skill_lv / 2 + skill_lv / 4 + skill_lv / 5;
		status->str += 1 + skill_lv / 3 + skill_lv / 3 + skill_lv / 4;
	}

	if((skill_lv = hom_checkskill(hd, HAMI_SKIN)) > 0)
		status->max_hp += skill_lv * 2 * status->max_hp / 100;

	if((skill_lv = hom_checkskill(hd, HLIF_BRAIN)) > 0)
		status->max_sp += (1 + skill_lv / 2 - skill_lv / 4 + skill_lv / 5) * status->max_sp / 100;

	if (opt&SCO_FIRST) {
		hd->battle_status.hp = hom->hp;
		hd->battle_status.sp = hom->sp;
		if(hom->class_ == 6052) // Eleanor
			sc_start(&hd->bl,&hd->bl, SC_STYLE_CHANGE, 100, MH_MD_FIGHTING, INFINITE_TICK);
	}

#ifndef RENEWAL
	status->rhw.atk = status->dex;
	status->rhw.atk2 = status->str + hom->level;
#endif

	status_calc_misc(&hd->bl, status, hom->level);

	status_cpy(&hd->battle_status, status);
	return 1;
}

/**
 * Calculates Elemental data
 * @param ed: Elemental object
 * @param opt: Whether it is first calc or not (0 on status change)
 * @return 0
 */
int status_calc_elemental_(s_elemental_data *ed, uint8 opt)
{
	struct status_data *status = &ed->base_status;
	s_elemental *ele = &ed->elemental;
	struct map_session_data *sd = ed->master;

	if( !sd )
		return 0;

	if (opt&SCO_FIRST) {
		memcpy(status, &ed->db->status, sizeof(struct status_data));
		if( !ele->mode )
			status->mode = EL_MODE_PASSIVE;
		else
			status->mode = ele->mode;

		status->class_ = CLASS_NORMAL;
		status_calc_misc(&ed->bl, status, 0);

		status->max_hp = ele->max_hp;
		status->max_sp = ele->max_sp;
		status->hp = ele->hp;
		status->sp = ele->sp;
		status->rhw.atk = ele->atk;
		status->rhw.atk2 = ele->atk2;
		status->matk_min += ele->matk;
		status->def += ele->def;
		status->mdef += ele->mdef;
		status->flee = ele->flee;
		status->hit = ele->hit;

		if (ed->master)
			status->speed = status_get_speed(&ed->master->bl);

		memcpy(&ed->battle_status,status,sizeof(struct status_data));
	} else {
		status_calc_misc(&ed->bl, status, 0);
		status_cpy(&ed->battle_status, status);
	}

	return 0;
}

/**
 * Calculates NPC data
 * @param nd: NPC object
 * @param opt: Whether it is first calc or not (what?)
 * @return 0
 */
int status_calc_npc_(struct npc_data *nd, uint8 opt)
{
	struct status_data *status = &nd->status;

	if (!nd)
		return 0;

	if (opt&SCO_FIRST) {
		status->hp = 1;
		status->sp = 1;
		status->max_hp = 1;
		status->max_sp = 1;

		status->def_ele = ELE_NEUTRAL;
		status->ele_lv = 1;
		status->race = RC_DEMIHUMAN;
		status->class_ = CLASS_NORMAL;
		status->size = nd->size;
		status->rhw.range = 1 + status->size;
		status->mode = static_cast<e_mode>(MD_CANMOVE|MD_CANATTACK);
		status->speed = nd->speed;
	}

	status->str = nd->stat_point + nd->params.str;
	status->agi = nd->stat_point + nd->params.agi;
	status->vit = nd->stat_point + nd->params.vit;
	status->int_= nd->stat_point + nd->params.int_;
	status->dex = nd->stat_point + nd->params.dex;
	status->luk = nd->stat_point + nd->params.luk;

	status_calc_misc(&nd->bl, status, nd->level);
	status_cpy(&nd->status, status);

	return 0;
}

/**
 * Calculates regeneration values
 * Applies passive skill regeneration additions
 * @param bl: Object to calculate regen for [PC|HOM|MER|ELEM]
 * @param status: Object's status
 * @param regen: Object's base regeneration data
 */
void status_calc_regen(struct block_list *bl, struct status_data *status, struct regen_data *regen)
{
	struct map_session_data *sd;
	struct status_change *sc;
	int val, skill, reg_flag;

	if( !(bl->type&BL_REGEN) || !regen )
		return;

	sd = BL_CAST(BL_PC,bl);
	sc = status_get_sc(bl);

	val = 1 + (status->vit/5) + (status->max_hp/200);

	if( sd && sd->hprecov_rate != 100 )
		val = val*sd->hprecov_rate/100;

	reg_flag = bl->type == BL_PC ? 0 : 1;

	regen->hp = cap_value(val, reg_flag, SHRT_MAX);

	val = 1 + (status->int_/6) + (status->max_sp/100);
	if( status->int_ >= 120 )
		val += ((status->int_-120)>>1) + 4;

	if( sd && sd->sprecov_rate != 100 )
		val = val*sd->sprecov_rate/100;

	regen->sp = cap_value(val, reg_flag, SHRT_MAX);

	if( sd ) {
		struct regen_data_sub *sregen;
		if( (skill=pc_checkskill(sd,HP_MEDITATIO)) > 0 ) {
			val = regen->sp*(100+3*skill)/100;
			regen->sp = cap_value(val, 1, SHRT_MAX);
		}
		// Only players have skill/sitting skill regen for now.
		sregen = regen->sregen;

		val = 0;
		if( (skill=pc_checkskill(sd,SM_RECOVERY)) > 0 )
			val += skill*5 + skill*status->max_hp/500;

		if (sc && sc->count) {
			if (sc->data[SC_INCREASE_MAXHP])
				val += val * sc->data[SC_INCREASE_MAXHP]->val2 / 100;
		}

		sregen->hp = cap_value(val, 0, SHRT_MAX);

		val = 0;
		if( (skill=pc_checkskill(sd,MG_SRECOVERY)) > 0 )
			val += skill*3 + skill*status->max_sp/500;
		if( (skill=pc_checkskill(sd,NJ_NINPOU)) > 0 )
			val += skill*3 + skill*status->max_sp/500;
		if( (skill=pc_checkskill(sd,WM_LESSON)) > 0 )
			val += 3 + 3 * skill;

		if (sc && sc->count) {
			if (sc->data[SC_ANCILLA])
				val += sc->data[SC_ANCILLA]->val2 / 100;
			if (sc->data[SC_INCREASE_MAXSP])
				val += val * sc->data[SC_INCREASE_MAXSP]->val2 / 100;
		}

		sregen->sp = cap_value(val, 0, SHRT_MAX);

		// Skill-related recovery (only when sit)
		sregen = regen->ssregen;

		val = 0;
		if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
			val += skill*4 + skill*status->max_hp/500;

		if( (skill=pc_checkskill(sd,TK_HPTIME)) > 0 && sd->state.rest )
			val += skill*30 + skill*status->max_hp/500;
		sregen->hp = cap_value(val, 0, SHRT_MAX);

		val = 0;
		if( (skill=pc_checkskill(sd,TK_SPTIME)) > 0 && sd->state.rest ) {
			val += skill*3 + skill*status->max_sp/500;
			if ((skill=pc_checkskill(sd,SL_KAINA)) > 0) // Power up Enjoyable Rest
				val += (30+10*skill)*val/100;
		}
		if( (skill=pc_checkskill(sd,MO_SPIRITSRECOVERY)) > 0 )
			val += skill*2 + skill*status->max_sp/500;
		sregen->sp = cap_value(val, 0, SHRT_MAX);
	}

	if( bl->type == BL_HOM ) {
		struct homun_data *hd = (TBL_HOM*)bl;
		if( (skill = hom_checkskill(hd,HAMI_SKIN)) > 0 ) {
			val = regen->hp*(100+5*skill)/100;
			regen->hp = cap_value(val, 1, SHRT_MAX);
		}
		if( (skill = hom_checkskill(hd,HLIF_BRAIN)) > 0 ) {
			val = regen->sp*(100+3*skill)/100;
			regen->sp = cap_value(val, 1, SHRT_MAX);
		}
	} else if( bl->type == BL_MER ) {
		val = (status->max_hp * status->vit / 10000 + 1) * 6;
		regen->hp = cap_value(val, 1, SHRT_MAX);

		val = (status->max_sp * (status->int_ + 10) / 750) + 1;
		regen->sp = cap_value(val, 1, SHRT_MAX);
	} else if( bl->type == BL_ELEM ) {
		val = (status->max_hp * status->vit / 10000 + 1) * 6;
		regen->hp = cap_value(val, 1, SHRT_MAX);

		val = (status->max_sp * (status->int_ + 10) / 750) + 1;
		regen->sp = cap_value(val, 1, SHRT_MAX);
	}
}

/**
 * Calculates SC (Status Changes) regeneration values
 * @param bl: Object to calculate regen for [PC|HOM|MER|ELEM]
 * @param regen: Object's base regeneration data
 * @param sc: Object's status change data
 */
void status_calc_regen_rate(struct block_list *bl, struct regen_data *regen, struct status_change *sc)
{
	if (!(bl->type&BL_REGEN) || !regen)
		return;

	regen->flag = RGN_HP|RGN_SP;
	if(regen->sregen) {
		if (regen->sregen->hp)
			regen->flag |= RGN_SHP;

		if (regen->sregen->sp)
			regen->flag |= RGN_SSP;
		regen->sregen->rate.hp = regen->sregen->rate.sp = 100;
	}
	if (regen->ssregen) {
		if (regen->ssregen->hp)
			regen->flag |= RGN_SHP;

		if (regen->ssregen->sp)
			regen->flag |= RGN_SSP;
		regen->ssregen->rate.hp = regen->ssregen->rate.sp = 100;
	}
	regen->rate.hp = regen->rate.sp = 100;

	if (!sc || !sc->count)
		return;

	// No HP or SP regen
	if ((sc->data[SC_POISON] && !sc->data[SC_SLOWPOISON])
		|| (sc->data[SC_DPOISON] && !sc->data[SC_SLOWPOISON])
		|| sc->data[SC_BERSERK]
		|| sc->data[SC_TRICKDEAD]
		|| sc->data[SC_BLEEDING]
		|| (sc->data[SC_MAGICMUSHROOM] && sc->data[SC_MAGICMUSHROOM]->val3 == 1)
		|| sc->data[SC_SATURDAYNIGHTFEVER]
		|| sc->data[SC_REBOUND])
		regen->flag = RGN_NONE;

	// No natural SP regen
	if (sc->data[SC_DANCING] ||
#ifdef RENEWAL
		sc->data[SC_MAXIMIZEPOWER] ||
#endif
#ifndef RENEWAL
		(bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
		(sc->data[SC_EXTREMITYFIST] || sc->data[SC_EXPLOSIONSPIRITS]) && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)) ||
#else
		(bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
		sc->data[SC_EXTREMITYFIST] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK)) ||
#endif
		(sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1))
		regen->flag &= ~RGN_SP;

	if (sc->data[SC_TENSIONRELAX]) {
		if (sc->data[SC_WEIGHT50] || sc->data[SC_WEIGHT90])
			regen->state.overweight = 0; // 1x HP regen
		else {
			regen->rate.hp += 200;
			if (regen->sregen)
				regen->sregen->rate.hp += 200;
		}
	}

	if (sc->data[SC_MAGNIFICAT])
		regen->rate.sp += 100;

	if (sc->data[SC_REGENERATION]) {
		const struct status_change_entry *sce = sc->data[SC_REGENERATION];
		if (!sce->val4) {
			regen->rate.hp += (sce->val2*100);
			regen->rate.sp += (sce->val3*100);
		} else
			regen->flag &= ~sce->val4; // Remove regen as specified by val4
	}
	if(sc->data[SC_GT_REVITALIZE]) {
		regen->hp += cap_value(regen->hp * sc->data[SC_GT_REVITALIZE]->val3/100, 1, SHRT_MAX);
		regen->state.walk = 1;
	}
	if (sc->data[SC_EXTRACT_WHITE_POTION_Z])
		regen->hp += cap_value(regen->hp * sc->data[SC_EXTRACT_WHITE_POTION_Z]->val1 / 100, 1, SHRT_MAX);
	if (sc->data[SC_VITATA_500])
		regen->sp += cap_value(regen->sp * sc->data[SC_VITATA_500]->val1 / 100, 1, SHRT_MAX);
	if (bl->type == BL_ELEM) { // Recovery bonus only applies to the Elementals.
		int ele_class = status_get_class(bl);

		switch (ele_class) {
		case ELEMENTALID_AGNI_S:
		case ELEMENTALID_AGNI_M:
		case ELEMENTALID_AGNI_L:
		case ELEMENTALID_ARDOR:
			if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 1)
				regen->rate.hp += 100;
			break;
		case ELEMENTALID_AQUA_S:
		case ELEMENTALID_AQUA_M:
		case ELEMENTALID_AQUA_L:
		case ELEMENTALID_DILUVIO:
			if (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 1)
				regen->rate.hp += 100;
			break;
		case ELEMENTALID_VENTUS_S:
		case ELEMENTALID_VENTUS_M:
		case ELEMENTALID_VENTUS_L:
		case ELEMENTALID_PROCELLA:
			if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 1)
				regen->rate.hp += 100;
			break;
		case ELEMENTALID_TERA_S:
		case ELEMENTALID_TERA_M:
		case ELEMENTALID_TERA_L:
		case ELEMENTALID_TERREMOTUS:
		case ELEMENTALID_SERPENS:
			if (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 1)
				regen->rate.hp += 100;
			break;
		}
	}

	if (sc->data[SC_CATNIPPOWDER]) {
		regen->rate.hp *= 2;
		regen->rate.sp *= 2;
	}
	if (sc->data[SC_SHRIMPBLESSING])
		regen->rate.sp += 50;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN]) {
		if (sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_HPREGEN)
			regen->rate.hp += 100;
		else if (sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_SPREGEN)
			regen->rate.sp += 100;
	}
#endif
	if (sc->data[SC_SIRCLEOFNATURE])
		regen->rate.hp += sc->data[SC_SIRCLEOFNATURE]->val2;
	if (sc->data[SC_SONGOFMANA])
		regen->rate.sp += sc->data[SC_SONGOFMANA]->val3;
}

/**
 * Applies a state to a unit - See [StatusChangeStateTable]
 * @param bl: Object to change state on [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change data
 * @param flag: Which state to apply to bl
 * @param start: (1) start state, (0) remove state
 */
void status_calc_state( struct block_list *bl, struct status_change *sc, std::bitset<SCS_MAX> flag, bool start )
{

	/// No sc at all, we can zero without any extra weight over our conciousness
	if( !sc->count ) {
		sc->cant = {};
		return;
	}

	// Can't move
	if( flag[SCS_NOMOVE] ) {
		if( !flag[SCS_NOMOVECOND] )
			sc->cant.move += (start ? 1 : ((sc->cant.move) ? -1 : 0));
		else if(
				     (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)	// cannot move while gospel is in effect
#ifndef RENEWAL
				  || (sc->data[SC_BASILICA] && sc->data[SC_BASILICA]->val4 == bl->id) // Basilica caster cannot move
				  || (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF)
#endif
				  || (sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 < 3)
				  || (sc->data[SC_MAGNETICFIELD] && sc->data[SC_MAGNETICFIELD]->val2 != bl->id)
				  || (sc->data[SC_FEAR] && sc->data[SC_FEAR]->val2 > 0)
				  || (sc->data[SC_SPIDERWEB] && sc->data[SC_SPIDERWEB]->val1)
				  || (sc->data[SC_HIDING] && (bl->type != BL_PC || (pc_checkskill(BL_CAST(BL_PC,bl),RG_TUNNELDRIVE) <= 0)))
				  || (sc->data[SC_DANCING] && sc->data[SC_DANCING]->val4 && (
#ifndef RENEWAL
						!sc->data[SC_LONGING] ||
#endif
						(sc->data[SC_DANCING]->val1&0xFFFF) == CG_MOONLIT ||
						(sc->data[SC_DANCING]->val1&0xFFFF) == CG_HERMODE
						))
				  || (sc->data[SC_CRYSTALIZE] && bl->type != BL_MOB)
				  || (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
 				 )
				 sc->cant.move += (start ? 1 : ((sc->cant.move) ? -1 : 0));
	}

	// Can't use skills
	if( flag[SCS_NOCAST] ) {
		if( !flag[SCS_NOCASTCOND] )
			sc->cant.cast += (start ? 1 : ((sc->cant.cast) ? -1 : 0));
		else if (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 1)
			sc->cant.cast += (start ? 1 : ((sc->cant.cast) ? -1 : 0));
	}

	// Can't chat
	if( flag[SCS_NOCHAT] ) {
		if( !flag[SCS_NOCHATCOND] )
			sc->cant.chat += (start ? 1 : ((sc->cant.chat) ? -1 : 0));
		else if(sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOCHAT)
			sc->cant.chat += (start ? 1 : ((sc->cant.chat) ? -1 : 0));
	}

	// Can't attack
	if( flag[SCS_NOATTACK] ) {
		if( !flag[SCS_NOATTACKCOND] )
			sc->cant.attack += (start ? 1 : ((sc->cant.attack) ? -1 : 0));
		/*else if( )
			sc->cant.attack += ( start ? 1 : ((sc->cant.attack)? -1:0) );*/
	}

	// Can't warp
	if (flag[SCS_NOWARP]) {
		if (!flag[SCS_NOWARPCOND])
			sc->cant.warp += (start ? 1 : ((sc->cant.warp) ? -1 : 0));
		/*else if (sc->data[])
			sc->cant.warp += ( start ? 1 : ((sc->cant.warp)? -1:0) );*/
	}

	// Player-only states
	if( bl->type == BL_PC ) {
		// Can't pick-up items
		if( flag[SCS_NOPICKITEM] ) {
			if( !flag[SCS_NOPICKITEMCOND] )
				sc->cant.pickup += (start ? 1 : ((sc->cant.pickup) ? -1 : 0));
			else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) )
				sc->cant.pickup += (start ? 1 : ((sc->cant.pickup) ? -1 : 0));
		}

		// Can't drop items
		if( flag[SCS_NODROPITEM] ) {
			if( !flag[SCS_NODROPITEMCOND] )
				sc->cant.drop += (start ? 1 : ((sc->cant.drop) ? -1 : 0));
			else if( (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) )
				sc->cant.drop += (start ? 1 : ((sc->cant.drop) ? -1 : 0));
		}

		// Can't equip item
		if( flag[SCS_NOEQUIPITEM] ) {
			if( !flag[SCS_NOEQUIPITEMCOND] )
				sc->cant.equip += (start ? 1 : ((sc->cant.equip) ? -1 : 0));
			/*else if(  )
				sc->cant.equip += ( start ? 1 : ((sc->cant.equip)? -1:0) );*/
		}

		// Can't unequip item
		if( flag[SCS_NOUNEQUIPITEM]) {
			if( !flag[SCS_NOUNEQUIPITEMCOND] )
				sc->cant.unequip += (start ? 1 : ((sc->cant.unequip) ? -1 : 0));
			/*else if(  )
				sc->cant.unequip += ( start ? 1 : ((sc->cant.unequip)? -1:0) );*/
		}

		// Can't consume item
		if( flag[SCS_NOCONSUMEITEM]) {
			if( !flag[SCS_NOCONSUMEITEMCOND] )
				sc->cant.consume += (start ? 1 : ((sc->cant.consume) ? -1 : 0));
			else if( (sc->data[SC_GRAVITATION] && sc->data[SC_GRAVITATION]->val3 == BCT_SELF) ||
				 (sc->data[SC_NOCHAT] && sc->data[SC_NOCHAT]->val1&MANNER_NOITEM) )
				sc->cant.consume += (start ? 1 : ((sc->cant.consume) ? -1 : 0));
		}

		// Can't lose exp
		if (flag[SCS_NODEATHPENALTY]) {
			if (!flag[SCS_NODEATHPENALTYCOND])
				sc->cant.deathpenalty += (start ? 1 : ((sc->cant.deathpenalty) ? -1 : 0));
			/*else if (sc->data[])
				sc->cant.deathpenalty += ( start ? 1 : ((sc->cant.deathpenalty)? -1:0) );*/
		}

		// Can't sit/stand/talk to NPC
		if (flag[SCS_NOINTERACT]) {
			if (!flag[SCS_NOINTERACTCOND])
				sc->cant.interact += (start ? 1 : ((sc->cant.interact) ? -1 : 0));
			/*else if (sc->data[])
				sc->cant.interact += ( start ? 1 : ((sc->cant.interact)? -1:0) );*/
		}
	}

	return;
}

/**
 * Recalculates parts of an objects status according to specified flags
 * See [set_sc] [add_sc]
 * @param bl: Object whose status has changed [PC|MOB|HOM|MER|ELEM]
 * @param flag: Which status has changed on bl
 */
void status_calc_bl_main(struct block_list *bl, uint64 flag)
{
	const struct status_data *b_status = status_get_base_status(bl); // Base Status
	struct status_data *status = status_get_status_data(bl); // Battle Status
	struct status_change *sc = status_get_sc(bl);
	TBL_PC *sd = BL_CAST(BL_PC,bl);
	int temp;

	if (!b_status || !status)
		return;

	/** [Playtester]
	* This needs to be done even if there is currently no status change active, because
	* we need to update the speed on the client when the last status change ends.
	**/
	if(flag&SCB_SPEED) {
		struct unit_data *ud = unit_bl2ud(bl);
		/** [Skotlex]
		* Re-walk to adjust speed (we do not check if walktimer != INVALID_TIMER
		* because if you step on something while walking, the moment this
		* piece of code triggers the walk-timer is set on INVALID_TIMER)
		**/
		if (ud)
			ud->state.change_walk_target = ud->state.speed_changed = 1;
	}

	if(flag&SCB_STR) {
		status->str = status_calc_str(bl, sc, b_status->str);
		flag|=SCB_BATK;
		if( bl->type&BL_HOM )
			flag |= SCB_WATK;
	}

	if(flag&SCB_AGI) {
		status->agi = status_calc_agi(bl, sc, b_status->agi);
		flag|=SCB_FLEE
#ifdef RENEWAL
			|SCB_DEF2
#endif
			;
		if( bl->type&(BL_PC|BL_HOM) )
			flag |= SCB_ASPD|SCB_DSPD;
	}

	if(flag&SCB_VIT) {
		status->vit = status_calc_vit(bl, sc, b_status->vit);
		flag|=SCB_DEF2|SCB_MDEF2;
		if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
			flag |= SCB_MAXHP;
		if( bl->type&BL_HOM )
			flag |= SCB_DEF;
	}

	if(flag&SCB_INT) {
		status->int_ = status_calc_int(bl, sc, b_status->int_);
		flag|=SCB_MATK|SCB_MDEF2;
		if( bl->type&(BL_PC|BL_HOM|BL_MER|BL_ELEM) )
			flag |= SCB_MAXSP;
		if( bl->type&BL_HOM )
			flag |= SCB_MDEF;
	}

	if(flag&SCB_DEX) {
		status->dex = status_calc_dex(bl, sc, b_status->dex);
		flag|=SCB_BATK|SCB_HIT
#ifdef RENEWAL
			|SCB_MATK|SCB_MDEF2
#endif
			;
		if( bl->type&(BL_PC|BL_HOM) )
			flag |= SCB_ASPD;
		if( bl->type&BL_HOM )
			flag |= SCB_WATK;
	}

	if(flag&SCB_LUK) {
		status->luk = status_calc_luk(bl, sc, b_status->luk);
		flag|=SCB_BATK|SCB_CRI|SCB_FLEE2
#ifdef RENEWAL
			|SCB_MATK|SCB_HIT|SCB_FLEE
#endif
			;
	}

#ifdef RENEWAL
	if (flag&SCB_POW) {
		status->pow = status_calc_pow(bl, sc, b_status->pow);
		flag |= SCB_BATK|SCB_PATK;
	}

	if (flag&SCB_STA) {
		status->sta = status_calc_sta(bl, sc, b_status->sta);
		flag |= SCB_RES;
	}

	if (flag&SCB_WIS) {
		status->wis = status_calc_wis(bl, sc, b_status->wis);
		flag |= SCB_MRES;
	}

	if (flag&SCB_SPL) {
		status->spl = status_calc_spl(bl, sc, b_status->spl);
		flag |= SCB_MATK|SCB_SMATK;
	}

	if (flag&SCB_CON) {
		status->con = status_calc_con(bl, sc, b_status->con);
		flag |= SCB_HIT|SCB_FLEE|SCB_PATK|SCB_SMATK;
	}

	if (flag&SCB_CRT) {
		status->crt = status_calc_crt(bl, sc, b_status->crt);
		flag |= SCB_HPLUS|SCB_CRATE;
	}
#endif

	if(flag&SCB_BATK && b_status->batk) {
		int lv = status_get_lv(bl);
		status->batk = status_base_atk(bl, status, lv);
		temp = b_status->batk - status_base_atk(bl, b_status, lv);
		if (temp) {
			temp += status->batk;
			status->batk = cap_value(temp, 0, USHRT_MAX);
		}
		status->batk = status_calc_batk(bl, sc, status->batk);
	}

	if(flag&SCB_WATK) {
#ifndef RENEWAL
		status->rhw.atk = status_calc_watk(bl, sc, b_status->rhw.atk);
		if (!sd) // Should not affect weapon refine bonus
			status->rhw.atk2 = status_calc_watk(bl, sc, b_status->rhw.atk2);

		if (sd && sd->bonus.weapon_atk_rate)
			status->rhw.atk += status->rhw.atk * sd->bonus.weapon_atk_rate / 100;
		if(b_status->lhw.atk) {
			if (sd) {
				sd->state.lr_flag = 1;
				status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
				sd->state.lr_flag = 0;
			} else {
				status->lhw.atk = status_calc_watk(bl, sc, b_status->lhw.atk);
				status->lhw.atk2= status_calc_watk(bl, sc, b_status->lhw.atk2);
			}
		}
#else
		if(!b_status->watk) { // We only have left-hand weapon
			status->watk = 0;
			status->watk2 = status_calc_watk(bl, sc, b_status->watk2);
		}
		else status->watk = status_calc_watk(bl, sc, b_status->watk);
#endif
	}

	if(flag&SCB_HIT) {
		if (status->dex == b_status->dex
#ifdef RENEWAL
			&& status->luk == b_status->luk && status->con == b_status->con
#endif
			)
			status->hit = status_calc_hit(bl, sc, b_status->hit);
		else
			status->hit = status_calc_hit(bl, sc, b_status->hit + (status->dex - b_status->dex)
#ifdef RENEWAL
			 + (status->luk/3 - b_status->luk/3) + 2 * (status->con - b_status->con)
#endif
			 );
	}

	if(flag&SCB_FLEE) {
		if (status->agi == b_status->agi
#ifdef RENEWAL
			&& status->luk == b_status->luk && status->con == b_status->con
#endif
			)
			status->flee = status_calc_flee(bl, sc, b_status->flee);
		else
			status->flee = status_calc_flee(bl, sc, b_status->flee +(status->agi - b_status->agi)
#ifdef RENEWAL
			+ (status->luk/5 - b_status->luk/5) + 2 * (status->con - b_status->con)
#endif
			);
	}

	if(flag&SCB_DEF) {
		status->def = status_calc_def(bl, sc, b_status->def);

		if( bl->type&BL_HOM )
			status->def += (status->vit/5 - b_status->vit/5);
	}

	if(flag&SCB_DEF2) {
		if (status->vit == b_status->vit
#ifdef RENEWAL
			&& status->agi == b_status->agi
#endif
			)
			status->def2 = status_calc_def2(bl, sc, b_status->def2);
		else
			status->def2 = status_calc_def2(bl, sc, b_status->def2
#ifdef RENEWAL
			+ (int)( ((float)status->vit/2 - (float)b_status->vit/2) + ((float)status->agi/5 - (float)b_status->agi/5) )
#else
			+ (status->vit - b_status->vit)
#endif
		);
	}

	if(flag&SCB_MDEF) {
		status->mdef = status_calc_mdef(bl, sc, b_status->mdef);

		if( bl->type&BL_HOM )
			status->mdef += (status->int_/5 - b_status->int_/5);
	}

	if(flag&SCB_MDEF2) {
		if (status->int_ == b_status->int_ && status->vit == b_status->vit
#ifdef RENEWAL
			&& status->dex == b_status->dex
#endif
			)
			status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2);
		else
			status->mdef2 = status_calc_mdef2(bl, sc, b_status->mdef2 +(status->int_ - b_status->int_)
#ifdef RENEWAL
			+ (int)( ((float)status->dex/5 - (float)b_status->dex/5) + ((float)status->vit/5 - (float)b_status->vit/5) )
#else
			+ ((status->vit - b_status->vit)>>1)
#endif
			);
	}

	if(flag&SCB_SPEED) {
		status->speed = status_calc_speed(bl, sc, b_status->speed);

		if( bl->type&BL_PC && !(sd && sd->state.permanent_speed) && status->speed < battle_config.max_walk_speed )
			status->speed = battle_config.max_walk_speed;

		if( bl->type&BL_PET && ((TBL_PET*)bl)->master)
			status->speed = status_get_speed(&((TBL_PET*)bl)->master->bl);
		if( bl->type&BL_HOM && battle_config.hom_setting&HOMSET_COPY_SPEED && ((TBL_HOM*)bl)->master)
			status->speed = status_get_speed(&((TBL_HOM*)bl)->master->bl);
		if( bl->type&BL_MER && ((TBL_MER*)bl)->master)
			status->speed = status_get_speed(&((TBL_MER*)bl)->master->bl);
		if( bl->type&BL_ELEM && ((TBL_ELEM*)bl)->master)
			status->speed = status_get_speed(&((TBL_ELEM*)bl)->master->bl);
	}

	if(flag&SCB_CRI && b_status->cri) {
		if (status->luk == b_status->luk)
			status->cri = status_calc_critical(bl, sc, b_status->cri);
		else
			status->cri = status_calc_critical(bl, sc, b_status->cri + 3*(status->luk - b_status->luk));

		/// After status_calc_critical so the bonus is applied despite if you have or not a sc bugreport:5240
		if (sd) {
			if (sd->status.weapon == W_KATAR)
				status->cri <<= 1;
		}
	}

	if(flag&SCB_FLEE2 && b_status->flee2) {
		if (status->luk == b_status->luk)
			status->flee2 = status_calc_flee2(bl, sc, b_status->flee2);
		else
			status->flee2 = status_calc_flee2(bl, sc, b_status->flee2 +(status->luk - b_status->luk));
	}

	if(flag&SCB_ATK_ELE) {
		status->rhw.ele = status_calc_attack_element(bl, sc, b_status->rhw.ele);
		if (sd) sd->state.lr_flag = 1;
		status->lhw.ele = status_calc_attack_element(bl, sc, b_status->lhw.ele);
		if (sd) sd->state.lr_flag = 0;
	}

	if(flag&SCB_DEF_ELE) {
		status->def_ele = status_calc_element(bl, sc, b_status->def_ele);
		status->ele_lv = status_calc_element_lv(bl, sc, b_status->ele_lv);
	}

	if(flag&SCB_MODE) {
		status->mode = status_calc_mode(bl, sc, b_status->mode);

		if (status_has_mode(status, MD_STATUSIMMUNE|MD_SKILLIMMUNE))
			status->class_ = CLASS_BATTLEFIELD;
		else if (status_has_mode(status, MD_STATUSIMMUNE|MD_KNOCKBACKIMMUNE|MD_DETECTOR))
			status->class_ = CLASS_BOSS;
		else if (status_has_mode(status, MD_STATUSIMMUNE))
			status->class_ = CLASS_GUARDIAN;
		else
			status->class_ = CLASS_NORMAL;

		// Since mode changed, reset their state.
		if (!status_has_mode(status,MD_CANATTACK))
			unit_stop_attack(bl);
		if (!status_has_mode(status,MD_CANMOVE))
			unit_stop_walking(bl,1);
	}

	/**
	* No status changes alter these yet.
	* if(flag&SCB_SIZE)
	* if(flag&SCB_RACE)
	* if(flag&SCB_RANGE)
	**/

	if(flag&SCB_MAXHP) {
		if( bl->type&BL_PC ) {
			status->max_hp = status_calc_maxhpsp_pc(sd,status->vit,true);

			if(battle_config.hp_rate != 100)
				status->max_hp = (unsigned int)(battle_config.hp_rate * (status->max_hp/100.));

			if (sd->status.base_level < 100)
				status->max_hp = umin(status->max_hp,(unsigned int)battle_config.max_hp_lv99);
			else if (sd->status.base_level < 151)
				status->max_hp = umin(status->max_hp,(unsigned int)battle_config.max_hp_lv150);
			else
				status->max_hp = umin(status->max_hp,(unsigned int)battle_config.max_hp);
		}
		else
			status->max_hp = status_calc_maxhp(bl, b_status->max_hp);

		if( status->hp > status->max_hp ) { // !FIXME: Should perhaps a status_zap should be issued?
			status->hp = status->max_hp;
			if( sd ) clif_updatestatus(sd,SP_HP);
		}
	}

	if(flag&SCB_MAXSP) {
		if( bl->type&BL_PC ) {
			status->max_sp = status_calc_maxhpsp_pc(sd,status->int_,false);

			if(battle_config.sp_rate != 100)
				status->max_sp = (unsigned int)(battle_config.sp_rate * (status->max_sp/100.));

			status->max_sp = umin(status->max_sp,(unsigned int)battle_config.max_sp);
		}
		else
			status->max_sp = status_calc_maxsp(bl, b_status->max_sp);

		if( status->sp > status->max_sp ) {
			status->sp = status->max_sp;
			if( sd ) clif_updatestatus(sd,SP_SP);
		}
	}

	if(flag&SCB_MATK) {
#ifndef RENEWAL
		status->matk_min = status_base_matk_min(status) + (sd?sd->bonus.ematk:0);
		status->matk_max = status_base_matk_max(status) + (sd?sd->bonus.ematk:0);
#else
		/**
		 * RE MATK Formula (from irowiki:http:// irowiki.org/wiki/MATK)
		 * MATK = (sMATK + wMATK + eMATK) * Multiplicative Modifiers
		 **/
		int lv = status_get_lv(bl);
		status->matk_min = status_base_matk_min(bl, status, lv);
		status->matk_max = status_base_matk_max(bl, status, lv);

		switch( bl->type ) {
			case BL_PC: {
				int wMatk = 0;
				int variance = 0;

				// Any +MATK you get from skills and cards, including cards in weapon, is added here.
				if (sd) {
					uint16 skill_lv;

					if (sd->bonus.ematk > 0)
						status->matk_min += sd->bonus.ematk;
					if (pc_checkskill(sd, SU_POWEROFLAND) > 0 && pc_checkskill_summoner(sd, SUMMONER_POWER_LAND) >= 20)
						status->matk_min += status->matk_min * 20 / 100;
					if ((skill_lv = pc_checkskill(sd, NV_TRANSCENDENCE)) > 0)
						status->matk_min += 15 * skill_lv + (skill_lv > 4 ? 25 : 0);
				}

				status->matk_min = status_calc_ematk(bl, sc, status->matk_min);
				status->matk_max = status->matk_min;

				// This is the only portion in MATK that varies depending on the weapon level and refinement rate.
				if (b_status->lhw.matk) {
					if (sd) {
						//sd->state.lr_flag = 1; //?? why was that set here
						status->lhw.matk = b_status->lhw.matk;
						sd->state.lr_flag = 0;
					} else {
						status->lhw.matk = b_status->lhw.matk;
					}
				}

				if (b_status->rhw.matk) {
					status->rhw.matk = b_status->rhw.matk;
				}

				if (status->rhw.matk) {
					wMatk += status->rhw.matk;
					variance += wMatk * status->rhw.wlv / 10;
				}

				if (status->lhw.matk) {
					wMatk += status->lhw.matk;
					variance += status->lhw.matk * status->lhw.wlv / 10;
				}

				status->matk_min += wMatk - variance;
				status->matk_max += wMatk + variance;
				}
				break;
		}
#endif

		if (bl->type&BL_PC && sd->matk_rate != 100) {
			status->matk_max = status->matk_max * sd->matk_rate/100;
			status->matk_min = status->matk_min * sd->matk_rate/100;
		}

		if ((bl->type&BL_HOM && battle_config.hom_setting&HOMSET_SAME_MATK)  /// Hom Min Matk is always the same as Max Matk
				|| (sc && sc->data[SC_RECOGNIZEDSPELL]))
			status->matk_min = status->matk_max;

#ifdef RENEWAL
		if( sd && sd->right_weapon.overrefine > 0) {
			status->matk_min++;
			status->matk_max += sd->right_weapon.overrefine - 1;
		}
#endif

		status->matk_max = status_calc_matk(bl, sc, status->matk_max);
		status->matk_min = status_calc_matk(bl, sc, status->matk_min);
	}

	if(flag&SCB_ASPD) {
		int amotion;

		if ( bl->type&BL_HOM ) {
#ifdef RENEWAL_ASPD
			amotion = ((TBL_HOM*)bl)->homunculusDB->baseASPD;
			amotion = amotion - amotion * status_get_homdex(bl) / 1000 - status_get_homagi(bl) * amotion / 250;
			amotion = (amotion * status_calc_aspd(bl, sc, true) + status_calc_aspd(bl, sc, false)) / - 100 + amotion;
#else
			amotion = (1000 - 4 * status->agi - status->dex) * ((TBL_HOM*)bl)->homunculusDB->baseASPD / 1000;

			amotion = status_calc_aspd_rate(bl, sc, amotion);
			amotion = amotion * status->aspd_rate / 1000;
#endif

			amotion = status_calc_fix_aspd(bl, sc, amotion);
			status->amotion = cap_value(amotion, battle_config.max_aspd, 2000);

			status->adelay = status->amotion;
		} else if ( bl->type&BL_PC ) {
			uint16 skill_lv;

			amotion = status_base_amotion_pc(sd,status);
#ifndef RENEWAL_ASPD
			status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
#endif
			// Absolute ASPD % modifiers
			amotion = amotion * status->aspd_rate / 1000;
			if (sd->ud.skilltimer != INVALID_TIMER && (skill_lv = pc_checkskill(sd, SA_FREECAST)) > 0)
#ifdef RENEWAL_ASPD
				amotion = amotion * 5 * (skill_lv + 10) / 100;
#else
				amotion += (2000 - amotion) * ( 55 - 5 * ( skill_lv + 1 ) ) / 100; //Increases amotion to reduce ASPD to the corresponding absolute percentage for each level (overriding other adjustments)
#endif

#ifdef RENEWAL_ASPD
			// RE ASPD % modifier
			amotion += (max(0xc3 - amotion, 2) * (status->aspd_rate2 + status_calc_aspd(bl, sc, false))) / 100;
			amotion = 10 * (200 - amotion);

			amotion += sd->bonus.aspd_add;
#endif
			amotion = status_calc_fix_aspd(bl, sc, amotion);
			status->amotion = cap_value(amotion,pc_maxaspd(sd),2000);

			status->adelay = 2 * status->amotion;
		} else { // Mercenary and mobs
			amotion = b_status->amotion;
			status->aspd_rate = status_calc_aspd_rate(bl, sc, b_status->aspd_rate);
			amotion = amotion*status->aspd_rate/1000;

			amotion = status_calc_fix_aspd(bl, sc, amotion);
			status->amotion = cap_value(amotion, battle_config.monster_max_aspd, 2000);

			temp = b_status->adelay*status->aspd_rate/1000;
			status->adelay = cap_value(temp, battle_config.monster_max_aspd*2, 4000);
		}
	}

	if(flag&SCB_DSPD) {
		int dmotion;
		if( bl->type&BL_PC ) {
			if (b_status->agi == status->agi)
				status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
			else {
				dmotion = 800-status->agi*4;
				status->dmotion = cap_value(dmotion, 400, 800);
				if(battle_config.pc_damage_delay_rate != 100)
					status->dmotion = status->dmotion*battle_config.pc_damage_delay_rate/100;
				// It's safe to ignore b_status->dmotion since no bonus affects it.
				status->dmotion = status_calc_dmotion(bl, sc, status->dmotion);
			}
		} else if( bl->type&BL_HOM ) {
			dmotion = 800-status->agi*4;
			status->dmotion = cap_value(dmotion, 400, 800);
			status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
		} else { // Mercenary and mobs
			status->dmotion = status_calc_dmotion(bl, sc, b_status->dmotion);
		}
	}

#ifdef RENEWAL
	if (flag&SCB_PATK) {
		if (status->pow == b_status->pow && status->con == b_status->con)
			status->patk = status_calc_patk(bl, sc, b_status->patk);
		else
			status->patk = status_calc_patk(bl, sc, b_status->patk + (status->pow - b_status->pow) / 3 + (status->con - b_status->con) / 5);
	}

	if (flag&SCB_SMATK) {
		if (status->spl == b_status->spl && status->con == b_status->con)
			status->smatk = status_calc_smatk(bl, sc, b_status->smatk);
		else
			status->smatk = status_calc_smatk(bl, sc, b_status->smatk) + (status->spl - b_status->spl) / 3 + (status->con - b_status->con) / 5;
	}

	if (flag&SCB_RES) {
		if (status->sta == b_status->sta)
			status->res = status_calc_res(bl, sc, b_status->res);
		else
			status->res = status_calc_res(bl, sc, b_status->res + (status->sta - b_status->sta) + (status->sta - b_status->sta) / 3 * 5);
	}

	if (flag&SCB_MRES) {
		if (status->wis == b_status->wis)
			status->mres = status_calc_mres(bl, sc, b_status->mres);
		else
			status->mres = status_calc_mres(bl, sc, b_status->mres + (status->wis - b_status->wis) + (status->wis - b_status->wis) / 3 * 5);
	}

	if (flag&SCB_HPLUS) {
		if (status->crt == b_status->crt)
			status->hplus = status_calc_hplus(bl, sc, b_status->hplus);
		else
			status->hplus = status_calc_hplus(bl, sc, b_status->hplus + (status->crt - b_status->crt));
	}

	if (flag&SCB_CRATE) {
		if (status->crt == b_status->crt)
			status->crate = status_calc_crate(bl, sc, b_status->crate);
		else
			status->crate = status_calc_crate(bl, sc, b_status->crate + (status->crt - b_status->crt) / 3);
	}

	if (flag&SCB_MAXAP) {
		if (bl->type&BL_PC) {
			status->max_ap = status_calc_maxap_pc(sd);

			if (battle_config.ap_rate != 100)
				status->max_ap = (unsigned int)(battle_config.ap_rate * (status->max_ap / 100.));

			status->max_ap = umin(status->max_ap, (unsigned int)battle_config.max_ap);
		} else
			status->max_ap = status_calc_maxap(bl, b_status->max_ap);

		if (status->ap > status->max_ap) {
			status->ap = status->max_ap;
			if (sd) clif_updatestatus(sd, SP_AP);
		}
	}
#endif

	if(flag&(SCB_VIT|SCB_MAXHP|SCB_INT|SCB_MAXSP) && bl->type&BL_REGEN)
		status_calc_regen(bl, status, status_get_regen_data(bl));

	if(flag&SCB_REGEN && bl->type&BL_REGEN)
		status_calc_regen_rate(bl, status_get_regen_data(bl), sc);
}

/**
 * Recalculates parts of an objects status according to specified flags
 * Also sends updates to the client when necessary
 * See [set_sc] [add_sc]
 * @param bl: Object whose status has changed [PC|MOB|HOM|MER|ELEM]
 * @param flag: Which status has changed on bl
 * @param opt: If true, will cause status_calc_* functions to run their base status initialization code
 */
void status_calc_bl_(struct block_list* bl, uint64 flag, uint8 opt)
{
	struct status_data b_status; // Previous battle status
	struct status_data* status; // Pointer to current battle status

	if (bl->type == BL_PC) {
		struct map_session_data *sd = BL_CAST(BL_PC, bl);

		if (sd->delayed_damage != 0) {
			if (opt&SCO_FORCE)
				sd->state.hold_recalc = false; // Clear and move on
			else {
				sd->state.hold_recalc = true; // Flag and stop
				return;
			}
		}
	}

	// Remember previous values
	status = status_get_status_data(bl);
	memcpy(&b_status, status, sizeof(struct status_data));

	if( flag&SCB_BASE ) { // Calculate the object's base status too
		switch( bl->type ) {
		case BL_PC:  status_calc_pc_(BL_CAST(BL_PC,bl), opt);          break;
		case BL_MOB: status_calc_mob_(BL_CAST(BL_MOB,bl), opt);        break;
		case BL_PET: status_calc_pet_(BL_CAST(BL_PET,bl), opt);        break;
		case BL_HOM: status_calc_homunculus_(BL_CAST(BL_HOM,bl), opt); break;
		case BL_MER: status_calc_mercenary_(BL_CAST(BL_MER,bl), opt);  break;
		case BL_ELEM: status_calc_elemental_(BL_CAST(BL_ELEM,bl), opt);  break;
		case BL_NPC: status_calc_npc_(BL_CAST(BL_NPC,bl), opt); break;
		}
	}

	if( bl->type == BL_PET )
		return; // Pets are not affected by statuses

	if (opt&SCO_FIRST && bl->type == BL_MOB)
		return; // Assume there will be no statuses active

	status_calc_bl_main(bl, flag);

	if (opt&SCO_FIRST && bl->type == BL_HOM)
		return; // Client update handled by caller

	// Compare against new values and send client updates
	if( bl->type == BL_PC ) {
		TBL_PC* sd = BL_CAST(BL_PC, bl);

		if(b_status.str != status->str)
			clif_updatestatus(sd,SP_STR);
		if(b_status.agi != status->agi)
			clif_updatestatus(sd,SP_AGI);
		if(b_status.vit != status->vit)
			clif_updatestatus(sd,SP_VIT);
		if(b_status.int_ != status->int_)
			clif_updatestatus(sd,SP_INT);
		if(b_status.dex != status->dex)
			clif_updatestatus(sd,SP_DEX);
		if(b_status.luk != status->luk)
			clif_updatestatus(sd,SP_LUK);
		if(b_status.hit != status->hit)
			clif_updatestatus(sd,SP_HIT);
		if(b_status.flee != status->flee)
			clif_updatestatus(sd,SP_FLEE1);
		if(b_status.amotion != status->amotion)
			clif_updatestatus(sd,SP_ASPD);
		if(b_status.speed != status->speed)
			clif_updatestatus(sd,SP_SPEED);

		if(b_status.batk != status->batk
#ifndef RENEWAL
			|| b_status.rhw.atk != status->rhw.atk || b_status.lhw.atk != status->lhw.atk
#endif
			)
			clif_updatestatus(sd,SP_ATK1);

		if(b_status.def != status->def) {
			clif_updatestatus(sd,SP_DEF1);
#ifdef RENEWAL
			clif_updatestatus(sd,SP_DEF2);
#endif
		}

		if(
#ifdef RENEWAL
			b_status.watk != status->watk || b_status.watk2 != status->watk2 || b_status.eatk != status->eatk
#else
			b_status.rhw.atk2 != status->rhw.atk2 || b_status.lhw.atk2 != status->lhw.atk2
#endif
			)
			clif_updatestatus(sd,SP_ATK2);

		if(b_status.def2 != status->def2) {
			clif_updatestatus(sd,SP_DEF2);
#ifdef RENEWAL
			clif_updatestatus(sd,SP_DEF1);
#endif
		}
		if(b_status.flee2 != status->flee2)
			clif_updatestatus(sd,SP_FLEE2);
		if(b_status.cri != status->cri)
			clif_updatestatus(sd,SP_CRITICAL);
#ifndef RENEWAL
		if(b_status.matk_max != status->matk_max)
			clif_updatestatus(sd,SP_MATK1);
		if(b_status.matk_min != status->matk_min)
			clif_updatestatus(sd,SP_MATK2);
#else
		if(b_status.matk_max != status->matk_max || b_status.matk_min != status->matk_min) {
			clif_updatestatus(sd,SP_MATK2);
			clif_updatestatus(sd,SP_MATK1);
		}
#endif
		if(b_status.mdef != status->mdef) {
			clif_updatestatus(sd,SP_MDEF1);
#ifdef RENEWAL
			clif_updatestatus(sd,SP_MDEF2);
#endif
		}
		if(b_status.mdef2 != status->mdef2) {
			clif_updatestatus(sd,SP_MDEF2);
#ifdef RENEWAL
			clif_updatestatus(sd,SP_MDEF1);
#endif
		}
		if(b_status.rhw.range != status->rhw.range)
			clif_updatestatus(sd,SP_ATTACKRANGE);
		if(b_status.max_hp != status->max_hp)
			clif_updatestatus(sd,SP_MAXHP);
		if(b_status.max_sp != status->max_sp)
			clif_updatestatus(sd,SP_MAXSP);
		if(b_status.hp != status->hp)
			clif_updatestatus(sd,SP_HP);
		if(b_status.sp != status->sp)
			clif_updatestatus(sd,SP_SP);
#ifdef RENEWAL
		if (b_status.pow != status->pow)
			clif_updatestatus(sd,SP_POW);
		if (b_status.sta != status->sta)
			clif_updatestatus(sd,SP_STA);
		if (b_status.wis != status->wis)
			clif_updatestatus(sd,SP_WIS);
		if (b_status.spl != status->spl)
			clif_updatestatus(sd,SP_SPL);
		if (b_status.con != status->con)
			clif_updatestatus(sd,SP_CON);
		if (b_status.crt != status->crt)
			clif_updatestatus(sd,SP_CRT);
		if (b_status.patk != status->patk)
			clif_updatestatus(sd, SP_PATK);
		if (b_status.smatk != status->smatk)
			clif_updatestatus(sd, SP_SMATK);
		if (b_status.res != status->res)
			clif_updatestatus(sd, SP_RES);
		if (b_status.mres != status->mres)
			clif_updatestatus(sd, SP_MRES);
		if (b_status.hplus != status->hplus)
			clif_updatestatus(sd, SP_HPLUS);
		if (b_status.crate != status->crate)
			clif_updatestatus(sd, SP_CRATE);
		if (b_status.max_ap != status->max_ap)
			clif_updatestatus(sd, SP_MAXAP);
		if (b_status.ap != status->ap)
			clif_updatestatus(sd, SP_AP);
#endif
	} else if( bl->type == BL_HOM ) {
		TBL_HOM* hd = BL_CAST(BL_HOM, bl);

		if( hd->master && memcmp(&b_status, status, sizeof(struct status_data)) != 0 )
			clif_hominfo(hd->master,hd,0);
	} else if( bl->type == BL_MER ) {
		TBL_MER* md = BL_CAST(BL_MER, bl);

		if (!md->master)
			return;

		if( b_status.rhw.atk != status->rhw.atk || b_status.rhw.atk2 != status->rhw.atk2 )
			clif_mercenary_updatestatus(md->master, SP_ATK1);
		if( b_status.matk_max != status->matk_max )
			clif_mercenary_updatestatus(md->master, SP_MATK1);
		if( b_status.hit != status->hit )
			clif_mercenary_updatestatus(md->master, SP_HIT);
		if( b_status.cri != status->cri )
			clif_mercenary_updatestatus(md->master, SP_CRITICAL);
		if( b_status.def != status->def )
			clif_mercenary_updatestatus(md->master, SP_DEF1);
		if( b_status.mdef != status->mdef )
			clif_mercenary_updatestatus(md->master, SP_MDEF1);
		if( b_status.flee != status->flee )
			clif_mercenary_updatestatus(md->master, SP_MERCFLEE);
		if( b_status.amotion != status->amotion )
			clif_mercenary_updatestatus(md->master, SP_ASPD);
		if( b_status.max_hp != status->max_hp )
			clif_mercenary_updatestatus(md->master, SP_MAXHP);
		if( b_status.max_sp != status->max_sp )
			clif_mercenary_updatestatus(md->master, SP_MAXSP);
		if( b_status.hp != status->hp )
			clif_mercenary_updatestatus(md->master, SP_HP);
		if( b_status.sp != status->sp )
			clif_mercenary_updatestatus(md->master, SP_SP);
	} else if( bl->type == BL_ELEM ) {
		TBL_ELEM* ed = BL_CAST(BL_ELEM, bl);

		if (!ed->master)
			return;

		if( b_status.max_hp != status->max_hp )
			clif_elemental_updatestatus(ed->master, SP_MAXHP);
		if( b_status.max_sp != status->max_sp )
			clif_elemental_updatestatus(ed->master, SP_MAXSP);
		if( b_status.hp != status->hp )
			clif_elemental_updatestatus(ed->master, SP_HP);
		if( b_status.sp != status->sp )
			clif_mercenary_updatestatus(ed->master, SP_SP);
	}
}

/**
 * Adds strength modifications based on status changes
 * @param bl: Object to change str [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param str: Initial str
 * @return modified str with cap_value(str,0,USHRT_MAX)
 */
static unsigned short status_calc_str(struct block_list *bl, struct status_change *sc, int str)
{
	if(!sc || !sc->count)
		return cap_value(str,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		str -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(str,0,USHRT_MAX);
	}
	if(sc->data[SC_INCALLSTATUS])
		str += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_CHASEWALK2])
		str += sc->data[SC_CHASEWALK2]->val1;
	if(sc->data[SC_INCSTR])
		str += sc->data[SC_INCSTR]->val1;
	if(sc->data[SC_STRFOOD])
		str += sc->data[SC_STRFOOD]->val1;
	if(sc->data[SC_FOOD_STR_CASH])
		str += sc->data[SC_FOOD_STR_CASH]->val1;
	if(sc->data[SC_BATTLEORDERS])
		str += 5;
	if(sc->data[SC_LEADERSHIP])
		str += sc->data[SC_LEADERSHIP]->val1;
	if(sc->data[SC_LOUD])
		str += 4;
	if(sc->data[SC_TRUESIGHT])
		str += 5;
	if(sc->data[SC_SPURT])
		str += 10;
	if(sc->data[SC_NEN])
		str += sc->data[SC_NEN]->val1;
	if(sc->data[SC_BLESSING]) {
		if(sc->data[SC_BLESSING]->val2)
			str += sc->data[SC_BLESSING]->val2;
		else
			str >>= 1;
	}
	if(sc->data[SC_MARIONETTE])
		str -= ((sc->data[SC_MARIONETTE]->val3)>>16)&0xFF;
	if(sc->data[SC_MARIONETTE2])
		str += ((sc->data[SC_MARIONETTE2]->val3)>>16)&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		str += ((sc->data[SC_SPIRIT]->val3)>>16)&0xFF;
	if(sc->data[SC_GIANTGROWTH])
		str += sc->data[SC_GIANTGROWTH]->val2;
	if(sc->data[SC_BEYONDOFWARCRY])
		str -= sc->data[SC_BEYONDOFWARCRY]->val2;
	if(sc->data[SC_SAVAGE_STEAK])
		str += sc->data[SC_SAVAGE_STEAK]->val1;
	if(sc->data[SC_INSPIRATION])
		str += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_2011RWC_SCROLL])
		str += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC_STOMACHACHE])
		str -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		str -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_SWORDCLAN])
		str += 1;
	if(sc->data[SC_JUMPINGCLAN])
		str += 1;
	if(sc->data[SC_FULL_THROTTLE])
		str += str * sc->data[SC_FULL_THROTTLE]->val3 / 100;
	if(sc->data[SC_CHEERUP])
		str += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		str += sc->data[SC_GLASTHEIM_STATE]->val1;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		str += 15;
#endif
	if (sc->data[SC_UNIVERSESTANCE])
		str += sc->data[SC_UNIVERSESTANCE]->val2;

	return (unsigned short)cap_value(str,0,USHRT_MAX);
}

/**
 * Adds agility modifications based on status changes
 * @param bl: Object to change agi [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param agi: Initial agi
 * @return modified agi with cap_value(agi,0,USHRT_MAX)
 */
static unsigned short status_calc_agi(struct block_list *bl, struct status_change *sc, int agi)
{
	if(!sc || !sc->count)
		return cap_value(agi,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		agi -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(agi,0,USHRT_MAX);
	}
	if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
		agi += (agi-sc->data[SC_CONCENTRATE]->val3)*sc->data[SC_CONCENTRATE]->val2/100;
	if(sc->data[SC_INCALLSTATUS])
		agi += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_INCAGI])
		agi += sc->data[SC_INCAGI]->val1;
	if(sc->data[SC_AGIFOOD])
		agi += sc->data[SC_AGIFOOD]->val1;
	if(sc->data[SC_FOOD_AGI_CASH])
		agi += sc->data[SC_FOOD_AGI_CASH]->val1;
	if(sc->data[SC_SOULCOLD])
		agi += sc->data[SC_SOULCOLD]->val1;
	if(sc->data[SC_TRUESIGHT])
		agi += 5;
	if(sc->data[SC_INCREASEAGI])
		agi += sc->data[SC_INCREASEAGI]->val2;
	if(sc->data[SC_INCREASING])
		agi += 4; // Added based on skill updates [Reddozen]
	if(sc->data[SC_2011RWC_SCROLL])
		agi += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC_DECREASEAGI])
		agi -= sc->data[SC_DECREASEAGI]->val2;
	if(sc->data[SC_QUAGMIRE])
		agi -= sc->data[SC_QUAGMIRE]->val2;
	if(sc->data[SC_SUITON] && sc->data[SC_SUITON]->val3)
		agi -= sc->data[SC_SUITON]->val2;
	if(sc->data[SC_MARIONETTE])
		agi -= ((sc->data[SC_MARIONETTE]->val3)>>8)&0xFF;
	if(sc->data[SC_MARIONETTE2])
		agi += ((sc->data[SC_MARIONETTE2]->val3)>>8)&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		agi += ((sc->data[SC_SPIRIT]->val3)>>8)&0xFF;
	if(sc->data[SC_ADORAMUS])
		agi -= sc->data[SC_ADORAMUS]->val2;
	if(sc->data[SC_MARSHOFABYSS])
		agi -= agi * sc->data[SC_MARSHOFABYSS]->val2 / 100;
	if(sc->data[SC_DROCERA_HERB_STEAMED])
		agi += sc->data[SC_DROCERA_HERB_STEAMED]->val1;
	if(sc->data[SC_INSPIRATION])
		agi += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_STOMACHACHE])
		agi -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		agi -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_CROSSBOWCLAN])
		agi += 1;
	if(sc->data[SC_JUMPINGCLAN])
		agi += 1;
	if(sc->data[SC_FULL_THROTTLE])
		agi += agi * sc->data[SC_FULL_THROTTLE]->val3 / 100;
	if (sc->data[SC_ARCLOUSEDASH])
		agi += sc->data[SC_ARCLOUSEDASH]->val2;
	if(sc->data[SC_CHEERUP])
		agi += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		agi += sc->data[SC_GLASTHEIM_STATE]->val1;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		agi += 15;
#endif
	if (sc->data[SC_UNIVERSESTANCE])
		agi += sc->data[SC_UNIVERSESTANCE]->val2;

	return (unsigned short)cap_value(agi,0,USHRT_MAX);
}

/**
 * Adds vitality modifications based on status changes
 * @param bl: Object to change vit [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param vit: Initial vit
 * @return modified vit with cap_value(vit,0,USHRT_MAX)
 */
static unsigned short status_calc_vit(struct block_list *bl, struct status_change *sc, int vit)
{
	if(!sc || !sc->count)
		return cap_value(vit,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		vit -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(vit,0,USHRT_MAX);
	}
	if(sc->data[SC_INCALLSTATUS])
		vit += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_INCVIT])
		vit += sc->data[SC_INCVIT]->val1;
	if(sc->data[SC_VITFOOD])
		vit += sc->data[SC_VITFOOD]->val1;
	if(sc->data[SC_FOOD_VIT_CASH])
		vit += sc->data[SC_FOOD_VIT_CASH]->val1;
	if(sc->data[SC_CHANGE])
		vit += sc->data[SC_CHANGE]->val2;
	if(sc->data[SC_GLORYWOUNDS])
		vit += sc->data[SC_GLORYWOUNDS]->val1;
	if(sc->data[SC_TRUESIGHT])
		vit += 5;
	if(sc->data[SC_MARIONETTE])
		vit -= sc->data[SC_MARIONETTE]->val3&0xFF;
	if(sc->data[SC_MARIONETTE2])
		vit += sc->data[SC_MARIONETTE2]->val3&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		vit += sc->data[SC_SPIRIT]->val3&0xFF;
	if(sc->data[SC_MINOR_BBQ])
		vit += sc->data[SC_MINOR_BBQ]->val1;
	if(sc->data[SC_INSPIRATION])
		vit += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_2011RWC_SCROLL])
		vit += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC_STOMACHACHE])
		vit -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		vit -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_SWORDCLAN])
		vit += 1;
	if(sc->data[SC_JUMPINGCLAN])
		vit += 1;
	if(sc->data[SC_STRIPARMOR] && bl->type != BL_PC)
		vit -= vit * sc->data[SC_STRIPARMOR]->val2/100;
	if(sc->data[SC_FULL_THROTTLE])
		vit += vit * sc->data[SC_FULL_THROTTLE]->val3 / 100;
#ifdef RENEWAL
	if(sc->data[SC_DEFENCE])
		vit += sc->data[SC_DEFENCE]->val2;
#endif
	if(sc->data[SC_CHEERUP])
		vit += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		vit += sc->data[SC_GLASTHEIM_STATE]->val1;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		vit += 15;
#endif
	if (sc->data[SC_UNIVERSESTANCE])
		vit += sc->data[SC_UNIVERSESTANCE]->val2;

	return (unsigned short)cap_value(vit,0,USHRT_MAX);
}

/**
 * Adds intelligence modifications based on status changes
 * @param bl: Object to change int [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param int_: Initial int
 * @return modified int with cap_value(int_,0,USHRT_MAX)
 */
static unsigned short status_calc_int(struct block_list *bl, struct status_change *sc, int int_)
{
	if(!sc || !sc->count)
		return cap_value(int_,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		int_ -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(int_,0,USHRT_MAX);
	}
	if(sc->data[SC_INCALLSTATUS])
		int_ += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_INCINT])
		int_ += sc->data[SC_INCINT]->val1;
	if(sc->data[SC_INTFOOD])
		int_ += sc->data[SC_INTFOOD]->val1;
	if(sc->data[SC_FOOD_INT_CASH])
		int_ += sc->data[SC_FOOD_INT_CASH]->val1;
	if(sc->data[SC_CHANGE])
		int_ += sc->data[SC_CHANGE]->val3;
	if(sc->data[SC_BATTLEORDERS])
		int_ += 5;
	if(sc->data[SC_TRUESIGHT])
		int_ += 5;
	if(sc->data[SC_BLESSING]) {
		if (sc->data[SC_BLESSING]->val2)
			int_ += sc->data[SC_BLESSING]->val2;
		else
			int_ >>= 1;
	}
	if(sc->data[SC_NEN])
		int_ += sc->data[SC_NEN]->val1;
	if(sc->data[SC_MARIONETTE])
		int_ -= ((sc->data[SC_MARIONETTE]->val4)>>16)&0xFF;
	if(sc->data[SC_2011RWC_SCROLL])
		int_ += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC_MARIONETTE2])
		int_ += ((sc->data[SC_MARIONETTE2]->val4)>>16)&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		int_ += ((sc->data[SC_SPIRIT]->val4)>>16)&0xFF;
	if(sc->data[SC_INSPIRATION])
		int_ += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_MELODYOFSINK])
		int_ -= sc->data[SC_MELODYOFSINK]->val2;
	if(sc->data[SC_MANDRAGORA])
		int_ -= 4 * sc->data[SC_MANDRAGORA]->val1;
	if(sc->data[SC_COCKTAIL_WARG_BLOOD])
		int_ += sc->data[SC_COCKTAIL_WARG_BLOOD]->val1;
	if(sc->data[SC_STOMACHACHE])
		int_ -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		int_ -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_ARCWANDCLAN])
		int_ += 1;
	if(sc->data[SC_GOLDENMACECLAN])
		int_ += 1;
	if(sc->data[SC_JUMPINGCLAN])
		int_ += 1;
	if(sc->data[SC_FULL_THROTTLE])
		int_ += int_ * sc->data[SC_FULL_THROTTLE]->val3 / 100;
	if(sc->data[SC_CHEERUP])
		int_ += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		int_ += sc->data[SC_GLASTHEIM_STATE]->val1;
	if (sc->data[SC_UNIVERSESTANCE])
		int_ += sc->data[SC_UNIVERSESTANCE]->val2;

	if(bl->type != BL_PC) {
		if(sc->data[SC_STRIPHELM])
			int_ -= int_ * sc->data[SC_STRIPHELM]->val2/100;
		if(sc->data[SC__STRIPACCESSORY])
			int_ -= int_ * sc->data[SC__STRIPACCESSORY]->val2 / 100;
	}
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		int_ += 15;
#endif

	return (unsigned short)cap_value(int_,0,USHRT_MAX);
}

/**
 * Adds dexterity modifications based on status changes
 * @param bl: Object to change dex [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param dex: Initial dex
 * @return modified dex with cap_value(dex,0,USHRT_MAX)
 */
static unsigned short status_calc_dex(struct block_list *bl, struct status_change *sc, int dex)
{
	if(!sc || !sc->count)
		return cap_value(dex,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		dex -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(dex,0,USHRT_MAX);
	}
	if(sc->data[SC_CONCENTRATE] && !sc->data[SC_QUAGMIRE])
		dex += (dex-sc->data[SC_CONCENTRATE]->val4)*sc->data[SC_CONCENTRATE]->val2/100;
	if(sc->data[SC_INCALLSTATUS])
		dex += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_INCDEX])
		dex += sc->data[SC_INCDEX]->val1;
	if(sc->data[SC_DEXFOOD])
		dex += sc->data[SC_DEXFOOD]->val1;
	if(sc->data[SC_FOOD_DEX_CASH])
		dex += sc->data[SC_FOOD_DEX_CASH]->val1;
	if(sc->data[SC_BATTLEORDERS])
		dex += 5;
	if(sc->data[SC_HAWKEYES])
		dex += sc->data[SC_HAWKEYES]->val1;
	if(sc->data[SC_TRUESIGHT])
		dex += 5;
	if(sc->data[SC_QUAGMIRE])
		dex -= sc->data[SC_QUAGMIRE]->val2;
	if(sc->data[SC_BLESSING]) {
		if (sc->data[SC_BLESSING]->val2)
			dex += sc->data[SC_BLESSING]->val2;
		else
			dex >>= 1;
	}
	if(sc->data[SC_INCREASING])
		dex += 4; // Added based on skill updates [Reddozen]
	if(sc->data[SC_MARIONETTE])
		dex -= ((sc->data[SC_MARIONETTE]->val4)>>8)&0xFF;
	if(sc->data[SC_2011RWC_SCROLL])
		dex += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC_MARIONETTE2])
		dex += ((sc->data[SC_MARIONETTE2]->val4)>>8)&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		dex += ((sc->data[SC_SPIRIT]->val4)>>8)&0xFF;
	if(sc->data[SC_SIROMA_ICE_TEA])
		dex += sc->data[SC_SIROMA_ICE_TEA]->val1;
	if(sc->data[SC_INSPIRATION])
		dex += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_STOMACHACHE])
		dex -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		dex -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_ARCWANDCLAN])
		dex += 1;
	if(sc->data[SC_CROSSBOWCLAN])
		dex += 1;
	if(sc->data[SC_JUMPINGCLAN])
		dex += 1;
	if(sc->data[SC__STRIPACCESSORY] && bl->type != BL_PC)
		dex -= dex * sc->data[SC__STRIPACCESSORY]->val2 / 100;
	if(sc->data[SC_MARSHOFABYSS])
		dex -= dex * sc->data[SC_MARSHOFABYSS]->val2 / 100;
	if(sc->data[SC_FULL_THROTTLE])
		dex += dex * sc->data[SC_FULL_THROTTLE]->val3 / 100;
	if(sc->data[SC_CHEERUP])
		dex += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		dex += sc->data[SC_GLASTHEIM_STATE]->val1;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		dex += 15;
#endif
	if (sc->data[SC_UNIVERSESTANCE])
		dex += sc->data[SC_UNIVERSESTANCE]->val2;

	return (unsigned short)cap_value(dex,0,USHRT_MAX);
}

/**
 * Adds luck modifications based on status changes
 * @param bl: Object to change luk [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param luk: Initial luk
 * @return modified luk with cap_value(luk,0,USHRT_MAX)
 */
static unsigned short status_calc_luk(struct block_list *bl, struct status_change *sc, int luk)
{
	if(!sc || !sc->count)
		return cap_value(luk,0,USHRT_MAX);

	if(sc->data[SC_HARMONIZE]) {
		luk -= sc->data[SC_HARMONIZE]->val2;
		return (unsigned short)cap_value(luk,0,USHRT_MAX);
	}
	if(sc->data[SC_CURSE])
		return 0;
	if(sc->data[SC_INCALLSTATUS])
		luk += sc->data[SC_INCALLSTATUS]->val1;
	if(sc->data[SC_INCLUK])
		luk += sc->data[SC_INCLUK]->val1;
	if(sc->data[SC_LUKFOOD])
		luk += sc->data[SC_LUKFOOD]->val1;
	if(sc->data[SC_FOOD_LUK_CASH])
		luk += sc->data[SC_FOOD_LUK_CASH]->val1;
	if(sc->data[SC_TRUESIGHT])
		luk += 5;
	if(sc->data[SC_GLORIA])
		luk += 30;
	if(sc->data[SC_MARIONETTE])
		luk -= sc->data[SC_MARIONETTE]->val4&0xFF;
	if(sc->data[SC_MARIONETTE2])
		luk += sc->data[SC_MARIONETTE2]->val4&0xFF;
	if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
		luk += sc->data[SC_SPIRIT]->val4&0xFF;
	if(sc->data[SC_PUTTI_TAILS_NOODLES])
		luk += sc->data[SC_PUTTI_TAILS_NOODLES]->val1;
	if(sc->data[SC_INSPIRATION])
		luk += sc->data[SC_INSPIRATION]->val3;
	if(sc->data[SC_STOMACHACHE])
		luk -= sc->data[SC_STOMACHACHE]->val1;
	if(sc->data[SC_KYOUGAKU])
		luk -= sc->data[SC_KYOUGAKU]->val2;
	if(sc->data[SC_2011RWC_SCROLL])
		luk += sc->data[SC_2011RWC_SCROLL]->val1;
	if(sc->data[SC__STRIPACCESSORY] && bl->type != BL_PC)
		luk -= luk * sc->data[SC__STRIPACCESSORY]->val2 / 100;
	if(sc->data[SC_BANANA_BOMB])
		luk -= 75;
	if(sc->data[SC_GOLDENMACECLAN])
		luk += 1;
	if(sc->data[SC_JUMPINGCLAN])
		luk += 1;
	if(sc->data[SC_FULL_THROTTLE])
		luk += luk * sc->data[SC_FULL_THROTTLE]->val3 / 100;
	if(sc->data[SC_CHEERUP])
		luk += 3;
	if(sc->data[SC_GLASTHEIM_STATE])
		luk += sc->data[SC_GLASTHEIM_STATE]->val1;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ALLSTAT)
		luk += 15;
#endif
	if (sc->data[SC_UNIVERSESTANCE])
		luk += sc->data[SC_UNIVERSESTANCE]->val2;

	return (unsigned short)cap_value(luk,0,USHRT_MAX);
}

/**
* Adds power modifications based on status changes
* @param bl: Object to change pow [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param pow: Initial pow
* @return modified pow with cap_value(pow,0,USHRT_MAX)
*/
static unsigned short status_calc_pow(struct block_list *bl, struct status_change *sc, int pow)
{
	if (!sc || !sc->count)
		return cap_value(pow, 0, USHRT_MAX);

	if (sc->data[SC_BENEDICTUM])
		pow += sc->data[SC_BENEDICTUM]->val2;

	return (unsigned short)cap_value(pow, 0, USHRT_MAX);
}

/**
* Adds stamina modifications based on status changes
* @param bl: Object to change sta [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param sta: Initial sta
* @return modified sta with cap_value(sta,0,USHRT_MAX)
*/
static unsigned short status_calc_sta(struct block_list *bl, struct status_change *sc, int sta)
{
	if (!sc || !sc->count)
		return cap_value(sta, 0, USHRT_MAX);

	if (sc->data[SC_RELIGIO])
		sta += sc->data[SC_RELIGIO]->val2;

	return (unsigned short)cap_value(sta, 0, USHRT_MAX);
}

/**
* Adds wisdom modifications based on status changes
* @param bl: Object to change wis [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param wis: Initial wis
* @return modified wis with cap_value(wis,0,USHRT_MAX)
*/
static unsigned short status_calc_wis(struct block_list *bl, struct status_change *sc, int wis)
{
	if (!sc || !sc->count)
		return cap_value(wis, 0, USHRT_MAX);

	if (sc->data[SC_RELIGIO])
		wis += sc->data[SC_RELIGIO]->val2;

	return (unsigned short)cap_value(wis, 0, USHRT_MAX);
}

/**
* Adds spell modifications based on status changes
* @param bl: Object to change spl [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param spl: Initial spl
* @return modified spl with cap_value(spl,0,USHRT_MAX)
*/
static unsigned short status_calc_spl(struct block_list *bl, struct status_change *sc, int spl)
{
	if (!sc || !sc->count)
		return cap_value(spl, 0, USHRT_MAX);

	if (sc->data[SC_RELIGIO])
		spl += sc->data[SC_RELIGIO]->val2;

	return (unsigned short)cap_value(spl, 0, USHRT_MAX);
}

/**
* Adds concentration modifications based on status changes
* @param bl: Object to change con [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param con: Initial con
* @return modified con with cap_value(con,0,USHRT_MAX)
*/
static unsigned short status_calc_con(struct block_list *bl, struct status_change *sc, int con)
{
	if (!sc || !sc->count)
		return cap_value(con, 0, USHRT_MAX);

	if (sc->data[SC_BENEDICTUM])
		con += sc->data[SC_BENEDICTUM]->val2;

	return (unsigned short)cap_value(con, 0, USHRT_MAX);
}

/**
* Adds creative modifications based on status changes
* @param bl: Object to change crt [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param crt: Initial crt
* @return modified crt with cap_value(crt,0,USHRT_MAX)
*/
static unsigned short status_calc_crt(struct block_list *bl, struct status_change *sc, int crt)
{
	if (!sc || !sc->count)
		return cap_value(crt, 0, USHRT_MAX);

	if (sc->data[SC_BENEDICTUM])
		crt += sc->data[SC_BENEDICTUM]->val2;

	return (unsigned short)cap_value(crt, 0, USHRT_MAX);
}

/**
 * Adds base attack modifications based on status changes
 * @param bl: Object to change batk [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param batk: Initial batk
 * @return modified batk with cap_value(batk,0,USHRT_MAX)
 */
static unsigned short status_calc_batk(struct block_list *bl, struct status_change *sc, int batk)
{
	if(!sc || !sc->count)
		return cap_value(batk,0,USHRT_MAX);

	if(sc->data[SC_ATKPOTION])
		batk += sc->data[SC_ATKPOTION]->val1;
	if(sc->data[SC_BATKFOOD])
		batk += sc->data[SC_BATKFOOD]->val1;
#ifndef RENEWAL
	if(sc->data[SC_GATLINGFEVER])
		batk += sc->data[SC_GATLINGFEVER]->val3;
	if(sc->data[SC_MADNESSCANCEL])
		batk += 100;
#endif
	if(sc->data[SC_FULL_SWING_K])
		batk += sc->data[SC_FULL_SWING_K]->val1;
	if(sc->data[SC_ASH])
		batk -= batk * sc->data[SC_ASH]->val4 / 100;
	if(bl->type == BL_HOM && sc->data[SC_PYROCLASTIC])
		batk += sc->data[SC_PYROCLASTIC]->val2;
	if (sc->data[SC_ANGRIFFS_MODUS])
		batk += sc->data[SC_ANGRIFFS_MODUS]->val2;
	if(sc->data[SC_2011RWC_SCROLL])
		batk += 30;
	if(sc->data[SC_INCATKRATE])
		batk += batk * sc->data[SC_INCATKRATE]->val1/100;
	if(sc->data[SC_PROVOKE])
		batk += batk * sc->data[SC_PROVOKE]->val2/100;
#ifndef RENEWAL
	if(sc->data[SC_CONCENTRATION])
		batk += batk * sc->data[SC_CONCENTRATION]->val2/100;
#endif
	if(sc->data[SC_SKE])
		batk += batk * 3;
	if(sc->data[SC_BLOODLUST])
		batk += batk * sc->data[SC_BLOODLUST]->val2/100;
	if(sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST)
		batk -= batk * 25/100;
	if(sc->data[SC_CURSE])
		batk -= batk * 25/100;
	/* Curse shouldn't effect on this? <- Curse OR Bleeding??
	if(sc->data[SC_BLEEDING])
		batk -= batk * 25 / 100; */
	if(sc->data[SC_FLEET])
		batk += batk * sc->data[SC_FLEET]->val3/100;
	if(sc->data[SC__ENERVATION])
		batk -= batk * sc->data[SC__ENERVATION]->val2 / 100;
	if( sc->data[SC_ZANGETSU] )
		batk += sc->data[SC_ZANGETSU]->val2;
	if(sc->data[SC_QUEST_BUFF1])
		batk += sc->data[SC_QUEST_BUFF1]->val1;
	if(sc->data[SC_QUEST_BUFF2])
		batk += sc->data[SC_QUEST_BUFF2]->val1;
	if(sc->data[SC_QUEST_BUFF3])
		batk += sc->data[SC_QUEST_BUFF3]->val1;
	if (sc->data[SC_SHRIMP])
		batk += batk * sc->data[SC_SHRIMP]->val2 / 100;
#ifdef RENEWAL
	if (sc->data[SC_LOUD])
		batk += 30;
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ATKRATE)
		batk += batk * 20 / 100;
#endif
	if (sc->data[SC_SUNSTANCE])
		batk += batk * sc->data[SC_SUNSTANCE]->val2 / 100;

	return (unsigned short)cap_value(batk,0,USHRT_MAX);
}

/**
 * Adds weapon attack modifications based on status changes
 * @param bl: Object to change watk [PC]
 * @param sc: Object's status change information
 * @param watk: Initial watk
 * @return modified watk with cap_value(watk,0,USHRT_MAX)
 */
static unsigned short status_calc_watk(struct block_list *bl, struct status_change *sc, int watk)
{
	if(!sc || !sc->count)
		return cap_value(watk,0,USHRT_MAX);

#ifndef RENEWAL
	if(sc->data[SC_DRUMBATTLE])
		watk += sc->data[SC_DRUMBATTLE]->val2;
#endif
	if (sc->data[SC_IMPOSITIO])
		watk += sc->data[SC_IMPOSITIO]->val2;
	if(sc->data[SC_WATKFOOD])
		watk += sc->data[SC_WATKFOOD]->val1;
	if(sc->data[SC_VOLCANO])
		watk += sc->data[SC_VOLCANO]->val2;
	if(sc->data[SC_MERC_ATKUP])
		watk += sc->data[SC_MERC_ATKUP]->val2;
	if(sc->data[SC_WATER_BARRIER])
		watk -= sc->data[SC_WATER_BARRIER]->val2;
#ifndef RENEWAL
	if(sc->data[SC_NIBELUNGEN]) {
		if (bl->type != BL_PC)
			watk += sc->data[SC_NIBELUNGEN]->val2;
		else {
			TBL_PC *sd = (TBL_PC*)bl;
			short index = sd->equip_index[sd->state.lr_flag?EQI_HAND_L:EQI_HAND_R];

			if(index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON && sd->inventory_data[index]->weapon_level == 4)
				watk += sc->data[SC_NIBELUNGEN]->val2;
		}
	}
	if(sc->data[SC_CONCENTRATION])
		watk += watk * sc->data[SC_CONCENTRATION]->val2 / 100;
#endif
	if(sc->data[SC_INCATKRATE])
		watk += watk * sc->data[SC_INCATKRATE]->val1/100;
	if(sc->data[SC_PROVOKE])
		watk += watk * sc->data[SC_PROVOKE]->val2/100;
	if(sc->data[SC_SKE])
		watk += watk * 3;
	if(sc->data[SC_FLEET])
		watk += watk * sc->data[SC_FLEET]->val3/100;
	if(sc->data[SC_CURSE])
		watk -= watk * 25/100;
	if(sc->data[SC_STRIPWEAPON] && bl->type != BL_PC)
		watk -= watk * sc->data[SC_STRIPWEAPON]->val2/100;
	if(sc->data[SC_FIGHTINGSPIRIT])
		watk += sc->data[SC_FIGHTINGSPIRIT]->val1;
	if (sc->data[SC_SHIELDSPELL_ATK])
		watk += sc->data[SC_SHIELDSPELL_ATK]->val2;
	if(sc->data[SC_INSPIRATION])
		watk += sc->data[SC_INSPIRATION]->val2;
	if(sc->data[SC_GT_CHANGE])
		watk += sc->data[SC_GT_CHANGE]->val2;
	if(sc->data[SC__ENERVATION])
		watk -= watk * sc->data[SC__ENERVATION]->val2 / 100;
	if(sc->data[SC_STRIKING])
		watk += sc->data[SC_STRIKING]->val2;
	if(sc->data[SC_RUSHWINDMILL])
		watk += sc->data[SC_RUSHWINDMILL]->val3;
	if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
		watk += 50;
	if((sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2)
	   || (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2)
	   || (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
	   || (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2))
		watk += watk * 10 / 100;
	if(sc->data[SC_PYROTECHNIC_OPTION])
		watk += sc->data[SC_PYROTECHNIC_OPTION]->val2;
	if(sc->data[SC_HEATER_OPTION])
		watk += sc->data[SC_HEATER_OPTION]->val2;
	if(sc->data[SC_TROPIC_OPTION])
		watk += sc->data[SC_TROPIC_OPTION]->val2;
	if( sc && sc->data[SC_TIDAL_WEAPON] )
		watk += watk * sc->data[SC_TIDAL_WEAPON]->val2 / 100;
	if(bl->type == BL_PC && sc->data[SC_PYROCLASTIC])
		watk += sc->data[SC_PYROCLASTIC]->val2;
	if(sc->data[SC_ANGRIFFS_MODUS])
		watk += watk * sc->data[SC_ANGRIFFS_MODUS]->val2/100;
	if(sc->data[SC_ODINS_POWER])
		watk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1;
	if (sc->data[SC_FLASHCOMBO])
		watk += sc->data[SC_FLASHCOMBO]->val2;
	if (sc->data[SC_CATNIPPOWDER])
		watk -= watk * sc->data[SC_CATNIPPOWDER]->val2 / 100;
	if (sc->data[SC_CHATTERING])
		watk += sc->data[SC_CHATTERING]->val2;
	if (sc->data[SC_SUNSTANCE])
		watk += watk * sc->data[SC_SUNSTANCE]->val2 / 100;
	if (sc->data[SC_SOULFALCON])
		watk += sc->data[SC_SOULFALCON]->val2;
	if (sc->data[SC_PACKING_ENVELOPE1])
		watk += sc->data[SC_PACKING_ENVELOPE1]->val1;
	if (sc->data[SC_POWERFUL_FAITH])
		watk += sc->data[SC_POWERFUL_FAITH]->val2;
	if (sc->data[SC_GUARD_STANCE])
		watk -= sc->data[SC_GUARD_STANCE]->val3;
	if (sc->data[SC_ATTACK_STANCE])
		watk += sc->data[SC_ATTACK_STANCE]->val3;

	return (unsigned short)cap_value(watk,0,USHRT_MAX);
}

#ifdef RENEWAL
/**
 * Adds equip magic attack modifications based on status changes [RENEWAL]
 * @param bl: Object to change matk [PC]
 * @param sc: Object's status change information
 * @param matk: Initial matk
 * @return modified matk with cap_value(matk,0,USHRT_MAX)
 */
static unsigned short status_calc_ematk(struct block_list *bl, struct status_change *sc, int matk)
{
	if (!sc || !sc->count)
		return cap_value(matk,0,USHRT_MAX);

	if (sc->data[SC_IMPOSITIO])
		matk += sc->data[SC_IMPOSITIO]->val2;
	if (sc->data[SC_MATKPOTION])
		matk += sc->data[SC_MATKPOTION]->val1;
	if (sc->data[SC_MATKFOOD])
		matk += sc->data[SC_MATKFOOD]->val1;
	if(sc->data[SC_MANA_PLUS])
		matk += sc->data[SC_MANA_PLUS]->val1;
	if(sc->data[SC_COOLER_OPTION])
		matk += sc->data[SC_COOLER_OPTION]->val2;
	if(sc->data[SC_AQUAPLAY_OPTION])
		matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
	if(sc->data[SC_CHILLY_AIR_OPTION])
		matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
	if(sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
		matk += 50;
	if(sc->data[SC_ODINS_POWER])
		matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; // 70 lvl1, 100lvl2
	if(sc->data[SC_MOONLITSERENADE])
		matk += sc->data[SC_MOONLITSERENADE]->val3;
	if(sc->data[SC_IZAYOI])
		matk += 25 * sc->data[SC_IZAYOI]->val1;
	if(sc->data[SC_ZANGETSU])
		matk += sc->data[SC_ZANGETSU]->val3;
	if(sc->data[SC_QUEST_BUFF1])
		matk += sc->data[SC_QUEST_BUFF1]->val1;
	if(sc->data[SC_QUEST_BUFF2])
		matk += sc->data[SC_QUEST_BUFF2]->val1;
	if(sc->data[SC_QUEST_BUFF3])
		matk += sc->data[SC_QUEST_BUFF3]->val1;
	if(sc->data[SC_MTF_MATK2])
		matk += sc->data[SC_MTF_MATK2]->val1;
	if(sc->data[SC_2011RWC_SCROLL])
		matk += 30;
	if (sc->data[SC_CATNIPPOWDER])
		matk -= matk * sc->data[SC_CATNIPPOWDER]->val2 / 100;
	if (sc->data[SC_CHATTERING])
		matk += sc->data[SC_CHATTERING]->val2;
	if (sc->data[SC_DORAM_MATK])
		matk += sc->data[SC_DORAM_MATK]->val1;
	if (sc->data[SC_SOULFAIRY])
		matk += sc->data[SC_SOULFAIRY]->val2;
	if (sc->data[SC__AUTOSHADOWSPELL])
		matk += sc->data[SC__AUTOSHADOWSPELL]->val4 * 5;
	if (sc->data[SC_INSPIRATION])
		matk += sc->data[SC_INSPIRATION]->val2;
	if (sc->data[SC_PACKING_ENVELOPE2])
		matk += sc->data[SC_PACKING_ENVELOPE2]->val1;

	return (unsigned short)cap_value(matk,0,USHRT_MAX);
}
#endif

/**
 * Adds magic attack modifications based on status changes
 * @param bl: Object to change matk [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param matk: Initial matk
 * @return modified matk with cap_value(matk,0,USHRT_MAX)
 */
static unsigned short status_calc_matk(struct block_list *bl, struct status_change *sc, int matk)
{
	if(!sc || !sc->count)
		return cap_value(matk,0,USHRT_MAX);
#ifndef RENEWAL
	/// Take note fixed value first before % modifiers [PRE-RENEWAL]
	if (sc->data[SC_MATKPOTION])
		matk += sc->data[SC_MATKPOTION]->val1;
	if (sc->data[SC_MATKFOOD])
		matk += sc->data[SC_MATKFOOD]->val1;
	if (sc->data[SC_MANA_PLUS])
		matk += sc->data[SC_MANA_PLUS]->val1;
	if (sc->data[SC_AQUAPLAY_OPTION])
		matk += sc->data[SC_AQUAPLAY_OPTION]->val2;
	if (sc->data[SC_CHILLY_AIR_OPTION])
		matk += sc->data[SC_CHILLY_AIR_OPTION]->val2;
	if (sc->data[SC_COOLER_OPTION])
		matk += sc->data[SC_COOLER_OPTION]->val2;
	if (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 3)
		matk += 50;
	if (sc->data[SC_ODINS_POWER])
		matk += 40 + 30 * sc->data[SC_ODINS_POWER]->val1; // 70 lvl1, 100lvl2
	if (sc->data[SC_IZAYOI])
		matk += 25 * sc->data[SC_IZAYOI]->val1;
	if (sc->data[SC_MTF_MATK2])
		matk += sc->data[SC_MTF_MATK2]->val1;
	if (sc->data[SC_2011RWC_SCROLL])
		matk += 30;
#endif
	if (sc->data[SC_ZANGETSU])
		matk += sc->data[SC_ZANGETSU]->val3;
	if (sc->data[SC_QUEST_BUFF1])
		matk += sc->data[SC_QUEST_BUFF1]->val1;
	if (sc->data[SC_QUEST_BUFF2])
		matk += sc->data[SC_QUEST_BUFF2]->val1;
	if (sc->data[SC_QUEST_BUFF3])
		matk += sc->data[SC_QUEST_BUFF3]->val1;
	if (sc->data[SC_MAGICPOWER]
#ifndef RENEWAL
		&& sc->data[SC_MAGICPOWER]->val4
#endif
		)
		matk += matk * sc->data[SC_MAGICPOWER]->val3/100;
	if (sc->data[SC_MINDBREAKER])
		matk += matk * sc->data[SC_MINDBREAKER]->val2/100;
	if (sc->data[SC_INCMATKRATE])
		matk += matk * sc->data[SC_INCMATKRATE]->val1/100;
	if (sc->data[SC_MOONLITSERENADE])
		matk += sc->data[SC_MOONLITSERENADE]->val3/100;
	if (sc->data[SC_MTF_MATK])
		matk += matk * sc->data[SC_MTF_MATK]->val1 / 100;
	if(sc->data[SC_2011RWC_SCROLL])
		matk += 30;
	if (sc->data[SC_SHRIMP])
		matk += matk * sc->data[SC_SHRIMP]->val2 / 100;
	if (sc->data[SC_VOLCANO])
		matk += sc->data[SC_VOLCANO]->val2;
#ifdef RENEWAL
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_MATKRATE)
		matk += matk * 20 / 100;
#endif
	if (sc->data[SC_SHIELDSPELL_ATK])
		matk += sc->data[SC_SHIELDSPELL_ATK]->val2;
	if (sc->data[SC_CLIMAX_DES_HU])
		matk += 100;

	return (unsigned short)cap_value(matk,0,USHRT_MAX);
}

/**
 * Adds critical modifications based on status changes
 * @param bl: Object to change critical [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param critical: Initial critical
 * @return modified critical with cap_value(critical,10,USHRT_MAX)
 */
static signed short status_calc_critical(struct block_list *bl, struct status_change *sc, int critical)
{
	if(!sc || !sc->count)
		return cap_value(critical,10,SHRT_MAX);

	if (sc->data[SC_INCCRI])
		critical += sc->data[SC_INCCRI]->val2;
	if (sc->data[SC_EP16_2_BUFF_SC])
		critical += 300;// crit +30
	if (sc->data[SC_CRIFOOD])
		critical += sc->data[SC_CRIFOOD]->val1;
	if (sc->data[SC_EXPLOSIONSPIRITS])
		critical += sc->data[SC_EXPLOSIONSPIRITS]->val2;
	if (sc->data[SC_FORTUNE])
		critical += sc->data[SC_FORTUNE]->val2;
	if (sc->data[SC_TRUESIGHT])
		critical += sc->data[SC_TRUESIGHT]->val2;
	if (sc->data[SC_CLOAKING])
		critical += critical;
#ifdef RENEWAL
	if (sc->data[SC_SPEARQUICKEN])
		critical += 3*sc->data[SC_SPEARQUICKEN]->val1*10;
	if (sc->data[SC_TWOHANDQUICKEN])
		critical += (2 + sc->data[SC_TWOHANDQUICKEN]->val1) * 10;
#endif
	if (sc->data[SC__INVISIBILITY])
		critical += sc->data[SC__INVISIBILITY]->val3 * 10;
	if (sc->data[SC__UNLUCKY])
		critical -= sc->data[SC__UNLUCKY]->val2;
	if (sc->data[SC_SOULSHADOW])
		critical += 10 * sc->data[SC_SOULSHADOW]->val3;
	if(sc->data[SC_BEYONDOFWARCRY])
		critical += sc->data[SC_BEYONDOFWARCRY]->val3;
	if (sc->data[SC_MTF_HITFLEE])
		critical += sc->data[SC_MTF_HITFLEE]->val1;
	if (sc->data[SC_PACKING_ENVELOPE9])
		critical += sc->data[SC_PACKING_ENVELOPE9]->val1 * 10;

	return (short)cap_value(critical,10,SHRT_MAX);
}

/**
 * Adds hit modifications based on status changes
 * @param bl: Object to change hit [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param hit: Initial hit
 * @return modified hit with cap_value(hit,1,USHRT_MAX)
 */
static signed short status_calc_hit(struct block_list *bl, struct status_change *sc, int hit)
{
	if(!sc || !sc->count)
		return cap_value(hit,1,SHRT_MAX);

	if(sc->data[SC_INCHIT])
		hit += sc->data[SC_INCHIT]->val1;
	if(sc->data[SC_HITFOOD])
		hit += sc->data[SC_HITFOOD]->val1;
	if(sc->data[SC_TRUESIGHT])
		hit += sc->data[SC_TRUESIGHT]->val3;
	if(sc->data[SC_HUMMING])
		hit += sc->data[SC_HUMMING]->val2;
	if(sc->data[SC_CONCENTRATION])
		hit += sc->data[SC_CONCENTRATION]->val3;
	if(sc->data[SC_INSPIRATION])
		hit += 12 * sc->data[SC_INSPIRATION]->val1;
	if(sc->data[SC_ADJUSTMENT])
		hit -= 30;
	if(sc->data[SC_INCREASING])
		hit += 20; // RockmanEXE; changed based on updated [Reddozen]
	if(sc->data[SC_MERC_HITUP])
		hit += sc->data[SC_MERC_HITUP]->val2;
	if(sc->data[SC_MTF_HITFLEE])
		hit += sc->data[SC_MTF_HITFLEE]->val1;
	if(sc->data[SC_INCHITRATE])
		hit += hit * sc->data[SC_INCHITRATE]->val1/100;
	if(sc->data[SC_BLIND])
		hit -= hit * 25/100;
	if(sc->data[SC_HEAT_BARREL])
		hit -= sc->data[SC_HEAT_BARREL]->val4;
	if(sc->data[SC__GROOMY])
		hit -= hit * sc->data[SC__GROOMY]->val3 / 100;
	if(sc->data[SC_FEAR])
		hit -= hit * 20 / 100;
	if (sc->data[SC_ASH])
		hit -= hit * sc->data[SC_ASH]->val2 / 100;
	if (sc->data[SC_TEARGAS])
		hit -= hit * 50 / 100;
	if(sc->data[SC_ILLUSIONDOPING])
		hit -= sc->data[SC_ILLUSIONDOPING]->val2;
	if (sc->data[SC_MTF_ASPD])
		hit += sc->data[SC_MTF_ASPD]->val2;
#ifdef RENEWAL
	if (sc->data[SC_BLESSING])
		hit += sc->data[SC_BLESSING]->val1 * 2;
	if (sc->data[SC_TWOHANDQUICKEN])
		hit += sc->data[SC_TWOHANDQUICKEN]->val1 * 2;
	if (sc->data[SC_ADRENALINE])
		hit += sc->data[SC_ADRENALINE]->val1 * 3 + 5;
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_HIT)
		hit += 50;
#endif
	if (sc->data[SC_SOULFALCON])
		hit += sc->data[SC_SOULFALCON]->val3;
	if (sc->data[SC_SATURDAYNIGHTFEVER])
		hit -= 50 + 50 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
	if (sc->data[SC_PACKING_ENVELOPE10])
		hit += sc->data[SC_PACKING_ENVELOPE10]->val1;
	if (sc->data[SC_ABYSS_SLAYER])
		hit += sc->data[SC_ABYSS_SLAYER]->val3;

	return (short)cap_value(hit,1,SHRT_MAX);
}

/**
 * Adds flee modifications based on status changes
 * @param bl: Object to change flee [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param flee: Initial flee
 * @return modified flee with cap_value(flee,1,USHRT_MAX)
 */
static signed short status_calc_flee(struct block_list *bl, struct status_change *sc, int flee)
{
	if( bl->type == BL_PC ) {
		struct map_data *mapdata = map_getmapdata(bl->m);

		if( mapdata_flag_gvg(mapdata) )
			flee -= flee * battle_config.gvg_flee_penalty/100;
		else if( mapdata->flag[MF_BATTLEGROUND] )
			flee -= flee * battle_config.bg_flee_penalty/100;
	}

	if(!sc || !sc->count)
		return cap_value(flee,1,SHRT_MAX);
	if (sc->data[SC_POISON_MIST])
		return 0;
	if(sc->data[SC_OVERED_BOOST]) //Should be final and unmodifiable by any means
		return sc->data[SC_OVERED_BOOST]->val2;

	// Fixed value
	if(sc->data[SC_INCFLEE])
		flee += sc->data[SC_INCFLEE]->val1;
	if(sc->data[SC_FLEEFOOD])
		flee += sc->data[SC_FLEEFOOD]->val1;
	if(sc->data[SC_WHISTLE])
		flee += sc->data[SC_WHISTLE]->val2;
	if(sc->data[SC_WINDWALK])
		flee += sc->data[SC_WINDWALK]->val2;
	if(sc->data[SC_VIOLENTGALE])
		flee += sc->data[SC_VIOLENTGALE]->val2;
	if(sc->data[SC_MOON_COMFORT]) // SG skill [Komurka]
		flee += sc->data[SC_MOON_COMFORT]->val2;
	if(sc->data[SC_CLOSECONFINE])
		flee += 10;
	if (sc->data[SC_ANGRIFFS_MODUS])
		flee -= sc->data[SC_ANGRIFFS_MODUS]->val3;
	if(sc->data[SC_ADJUSTMENT])
		flee += 30;
	if(sc->data[SC_SPEED])
		flee += 10 + sc->data[SC_SPEED]->val1 * 10;
	if(sc->data[SC_GATLINGFEVER])
		flee -= sc->data[SC_GATLINGFEVER]->val4;
	if(sc->data[SC_PARTYFLEE])
		flee += sc->data[SC_PARTYFLEE]->val1 * 10;
	if(sc->data[SC_MERC_FLEEUP])
		flee += sc->data[SC_MERC_FLEEUP]->val2;
	if( sc->data[SC_HALLUCINATIONWALK] )
		flee += sc->data[SC_HALLUCINATIONWALK]->val2;
	if( sc->data[SC_NPC_HALLUCINATIONWALK] )
		flee += sc->data[SC_NPC_HALLUCINATIONWALK]->val2;
	if(sc->data[SC_MTF_HITFLEE])
		flee += sc->data[SC_MTF_HITFLEE]->val2;
	if( sc->data[SC_WATER_BARRIER] )
		flee -= sc->data[SC_WATER_BARRIER]->val2;
	if( sc->data[SC_C_MARKER] )
		flee -= sc->data[SC_C_MARKER]->val3;
#ifdef RENEWAL
	if( sc->data[SC_SPEARQUICKEN] )
		flee += 2 * sc->data[SC_SPEARQUICKEN]->val1;
	if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_FLEE)
		flee += 50;
#endif

	// Rate value
	if(sc->data[SC_INCFLEERATE])
		flee += flee * sc->data[SC_INCFLEERATE]->val1/100;
	if(sc->data[SC_SPIDERWEB] || sc->data[SC_WIDEWEB])
		flee -= flee * 50/100;
	if(sc->data[SC_BERSERK])
		flee -= flee * 50/100;
	if(sc->data[SC_BLIND])
		flee -= flee * 25/100;
	if(sc->data[SC_FEAR])
		flee -= flee * 20 / 100;
	if(sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1)
		flee -= flee * 10 / 100;
	if(sc->data[SC_INFRAREDSCAN])
		flee -= flee * 30 / 100;
	if( sc->data[SC__LAZINESS] )
		flee -= flee * sc->data[SC__LAZINESS]->val3 / 100;
	if( sc->data[SC_GLOOMYDAY] )
		flee -= flee * sc->data[SC_GLOOMYDAY]->val2 / 100;
	if( sc->data[SC_SATURDAYNIGHTFEVER] )
		flee -= 20 + 30 * sc->data[SC_SATURDAYNIGHTFEVER]->val1;
	if( sc->data[SC_WIND_STEP_OPTION] )
		flee += flee * sc->data[SC_WIND_STEP_OPTION]->val2 / 100;
	if( sc->data[SC_TINDER_BREAKER] || sc->data[SC_TINDER_BREAKER2] )
		flee -= flee * 50 / 100;
	if( sc->data[SC_ZEPHYR] )
		flee += sc->data[SC_ZEPHYR]->val2;
	if(sc->data[SC_ASH])
		flee -= flee * sc->data[SC_ASH]->val4 / 100;
	if (sc->data[SC_GOLDENE_FERSE])
		flee += flee * sc->data[SC_GOLDENE_FERSE]->val2 / 100;
	if (sc->data[SC_SMOKEPOWDER])
		flee += flee * 20 / 100;
	if (sc->data[SC_TEARGAS])
		flee -= flee * 50 / 100;
	//if( sc->data[SC_C_MARKER] )
	//	flee -= (flee * sc->data[SC_C_MARKER]->val3) / 100;
	if (sc->data[SC_GROOMING])
		flee += sc->data[SC_GROOMING]->val2;
	if (sc->data[SC_PACKING_ENVELOPE5])
		flee += sc->data[SC_PACKING_ENVELOPE5]->val1;

	return (short)cap_value(flee,1,SHRT_MAX);
}

/**
 * Adds perfect flee modifications based on status changes
 * @param bl: Object to change flee2 [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param flee2: Initial flee2
 * @return modified flee2 with cap_value(flee2,10,USHRT_MAX)
 */
static signed short status_calc_flee2(struct block_list *bl, struct status_change *sc, int flee2)
{
	if(!sc || !sc->count)
		return cap_value(flee2,10,SHRT_MAX);

	if(sc->data[SC_INCFLEE2])
		flee2 += sc->data[SC_INCFLEE2]->val2;
	if(sc->data[SC_WHISTLE])
		flee2 += sc->data[SC_WHISTLE]->val3*10;
	if(sc->data[SC__UNLUCKY])
		flee2 -= flee2 * sc->data[SC__UNLUCKY]->val2 / 100;
	if (sc->data[SC_HISS])
		flee2 += sc->data[SC_HISS]->val2*10;
	if (sc->data[SC_DORAM_FLEE2])
		flee2 += sc->data[SC_DORAM_FLEE2]->val1;

	return (short)cap_value(flee2,10,SHRT_MAX);
}

/**
 * Adds defense (left-side) modifications based on status changes
 * @param bl: Object to change def [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param def: Initial def
 * @return modified def with cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX)
 */
static defType status_calc_def(struct block_list *bl, struct status_change *sc, int def)
{
	if(!sc || !sc->count)
		return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);

	if(sc->data[SC_BERSERK])
		return 0;
	if(sc->data[SC_BARRIER])
		return 100;
	if(sc->data[SC_KEEPING])
		return 90;
#ifndef RENEWAL /// Steel Body does not provide 90 DEF in [RENEWAL]
	if(sc->data[SC_STEELBODY])
		return 90;
#endif
	if (sc->data[SC_NYANGGRASS]) {
		if (bl->type == BL_PC)
			return 0;
		else
			return def >>= 1;
	}
	if(sc->data[SC_DEFSET])
		return sc->data[SC_DEFSET]->val1;

	if(sc->data[SC_DRUMBATTLE])
		def += sc->data[SC_DRUMBATTLE]->val3;
#ifdef RENEWAL
	if (sc->data[SC_ASSUMPTIO])
		def += sc->data[SC_ASSUMPTIO]->val1 * 50;
#else
	if(sc->data[SC_DEFENCE])
		def += sc->data[SC_DEFENCE]->val2;
#endif
	if(sc->data[SC_INCDEFRATE])
		def += def * sc->data[SC_INCDEFRATE]->val1/100;
	if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2)
		def += 50;
	if(sc->data[SC_ODINS_POWER])
		def -= 20 * sc->data[SC_ODINS_POWER]->val1;
	if( sc->data[SC_ANGRIFFS_MODUS] )
		def -= 20 + 10 * sc->data[SC_ANGRIFFS_MODUS]->val1;
	if(sc->data[SC_STONEHARDSKIN])
		def += sc->data[SC_STONEHARDSKIN]->val1;
	if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
		def >>=1;
	if(sc->data[SC_FREEZE])
		def >>=1;
	if(sc->data[SC_SIGNUMCRUCIS])
		def -= def * sc->data[SC_SIGNUMCRUCIS]->val2/100;
	if(sc->data[SC_CONCENTRATION])
		def -= def * sc->data[SC_CONCENTRATION]->val4/100;
	if(sc->data[SC_SKE])
		def >>=1;
	if(sc->data[SC_PROVOKE] && bl->type != BL_PC) // Provoke doesn't alter player defense->
		def -= def * sc->data[SC_PROVOKE]->val3/100;
	if(sc->data[SC_STRIPSHIELD] && bl->type != BL_PC) // Player doesn't have def reduction only equip removed
		def -= def * sc->data[SC_STRIPSHIELD]->val2/100;
	if (sc->data[SC_FLING])
		def -= def * (sc->data[SC_FLING]->val2)/100;
	if( sc->data[SC_FREEZING] )
		def -= def * (bl->type == BL_PC ? 30 : 10) / 100;
	if( sc->data[SC_ANALYZE] )
		def -= def * (14 * sc->data[SC_ANALYZE]->val1) / 100;
	if( sc->data[SC_NEUTRALBARRIER] )
		def += def * sc->data[SC_NEUTRALBARRIER]->val2 / 100;
	if( sc->data[SC_PRESTIGE] )
		def += sc->data[SC_PRESTIGE]->val3;
	if( sc->data[SC_BANDING] && sc->data[SC_BANDING]->val2 > 1 )
		def += 6 * sc->data[SC_BANDING]->val1;
	if( sc->data[SC_ECHOSONG] )
		def += sc->data[SC_ECHOSONG]->val3;
	if( sc->data[SC_CAMOUFLAGE] )
		def -= def * 5 * sc->data[SC_CAMOUFLAGE]->val3 / 100;
	if( sc->data[SC_SOLID_SKIN_OPTION] )
		def += def * sc->data[SC_SOLID_SKIN_OPTION]->val2 / 100;
	if( sc->data[SC_ROCK_CRUSHER] )
		def -= def * sc->data[SC_ROCK_CRUSHER]->val2 / 100;
	if( sc->data[SC_POWER_OF_GAIA] )
		def += def * sc->data[SC_POWER_OF_GAIA]->val2 / 100;
	if(sc->data[SC_ASH])
		def -= def * sc->data[SC_ASH]->val3/100;
	if( sc->data[SC_OVERED_BOOST] && bl->type == BL_HOM )
		def -= def * sc->data[SC_OVERED_BOOST]->val4 / 100;
	if(sc->data[SC_GLASTHEIM_ITEMDEF])
		def += sc->data[SC_GLASTHEIM_ITEMDEF]->val1;
	if (sc->data[SC_SOULGOLEM])
		def += sc->data[SC_SOULGOLEM]->val2;
	if (sc->data[SC_STONE_WALL])
		def += sc->data[SC_STONE_WALL]->val2;
	if( sc->data[SC_PACKING_ENVELOPE7] )
		def += sc->data[SC_PACKING_ENVELOPE7]->val1;
	if (sc->data[SC_D_MACHINE])
		def += sc->data[SC_D_MACHINE]->val2;
	if (sc->data[SC_CLIMAX_CRYIMP])
		def += 300;
	if (sc->data[SC_GUARD_STANCE])
		def += sc->data[SC_GUARD_STANCE]->val2;
	if (sc->data[SC_ATTACK_STANCE])
		def -= sc->data[SC_ATTACK_STANCE]->val2;

	return (defType)cap_value(def,DEFTYPE_MIN,DEFTYPE_MAX);
}

/**
 * Adds defense (right-side) modifications based on status changes
 * @param bl: Object to change def2 [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param def2: Initial def2
 * @return modified def2 with cap_value(def2,SHRT_MIN,SHRT_MAX)
 */
static signed short status_calc_def2(struct block_list *bl, struct status_change *sc, int def2)
{
	if(!sc || !sc->count)
#ifdef RENEWAL
		return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
#else
		return (short)cap_value(def2,1,SHRT_MAX);
#endif

	if(sc->data[SC_BERSERK])
		return 0;
	if(sc->data[SC_ETERNALCHAOS])
		return 0;
	if(sc->data[SC_DEFSET])
		return sc->data[SC_DEFSET]->val1;

	if(sc->data[SC_SUN_COMFORT])
		def2 += sc->data[SC_SUN_COMFORT]->val2;
#ifdef RENEWAL
	if (sc->data[SC_SKA])
		def2 += 80;
#endif
	if(sc->data[SC_ANGELUS])
#ifdef RENEWAL /// The VIT stat bonus is boosted by angelus [RENEWAL]
		def2 += status_get_vit(bl) / 2 * sc->data[SC_ANGELUS]->val2/100;
#else
		def2 += def2 * sc->data[SC_ANGELUS]->val2/100;
	if(sc->data[SC_CONCENTRATION])
		def2 -= def2 * sc->data[SC_CONCENTRATION]->val4/100;
#endif
	if(sc->data[SC_POISON])
		def2 -= def2 * 25/100;
	if(sc->data[SC_DPOISON])
		def2 -= def2 * 25/100;
	if(sc->data[SC_SKE])
		def2 -= def2 * 50/100;
	if(sc->data[SC_PROVOKE])
		def2 -= def2 * sc->data[SC_PROVOKE]->val3/100;
	if(sc->data[SC_JOINTBEAT])
		def2 -= def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_SHOULDER ? 50 : 0 ) / 100
			  + def2 * ( sc->data[SC_JOINTBEAT]->val2&BREAK_WAIST ? 25 : 0 ) / 100;
	if(sc->data[SC_FLING])
		def2 -= def2 * (sc->data[SC_FLING]->val3)/100;
	if(sc->data[SC_ANALYZE])
		def2 -= def2 * (14 * sc->data[SC_ANALYZE]->val1) / 100;
	if(sc->data[SC_ASH])
		def2 -= def2 * sc->data[SC_ASH]->val3/100;
	if (sc->data[SC_PARALYSIS])
		def2 -= def2 * sc->data[SC_PARALYSIS]->val2 / 100;
	if(sc->data[SC_EQC])
		def2 -= def2 * sc->data[SC_EQC]->val2 / 100;
	if( sc->data[SC_CAMOUFLAGE] )
		def2 -= def2 * 5 * sc->data[SC_CAMOUFLAGE]->val3 / 100;

#ifdef RENEWAL
	return (short)cap_value(def2,SHRT_MIN,SHRT_MAX);
#else
	return (short)cap_value(def2,1,SHRT_MAX);
#endif
}

/**
 * Adds magic defense (left-side) modifications based on status changes
 * @param bl: Object to change mdef [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param mdef: Initial mdef
 * @return modified mdef with cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX)
 */
static defType status_calc_mdef(struct block_list *bl, struct status_change *sc, int mdef)
{
	if(!sc || !sc->count)
		return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);

	if(sc->data[SC_BERSERK])
		return 0;
	if(sc->data[SC_BARRIER])
		return 100;

#ifndef RENEWAL /// Steel Body does not provide 90 MDEF in [RENEWAL]
	if(sc->data[SC_STEELBODY])
		return 90;
#endif
	if (sc->data[SC_NYANGGRASS]) {
		if (bl->type == BL_PC)
			return 0;
		else
			return mdef >>= 1;
	}
	if(sc->data[SC_MDEFSET])
		return sc->data[SC_MDEFSET]->val1;

	if(sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 3)
		mdef += 50;
	if(sc->data[SC_ENDURE] && !sc->data[SC_ENDURE]->val3) // It has been confirmed that Eddga card grants 1 MDEF, not 0, not 10, but 1.
		mdef += (sc->data[SC_ENDURE]->val4 == 0) ? sc->data[SC_ENDURE]->val1 : 1;
	if(sc->data[SC_STONEHARDSKIN])
		mdef += sc->data[SC_STONEHARDSKIN]->val1;
	if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
		mdef += 25 * mdef / 100;
	if(sc->data[SC_FREEZE])
		mdef += 25 * mdef / 100;
	if(sc->data[SC_BURNING])
		mdef -= 25 * mdef / 100;
	if( sc->data[SC_NEUTRALBARRIER] )
		mdef += mdef * sc->data[SC_NEUTRALBARRIER]->val2 / 100;
	if(sc->data[SC_ANALYZE])
		mdef -= mdef * ( 14 * sc->data[SC_ANALYZE]->val1 ) / 100;
	if(sc->data[SC_SYMPHONYOFLOVER])
		mdef += mdef * sc->data[SC_SYMPHONYOFLOVER]->val3 / 100;
	if (sc->data[SC_ODINS_POWER])
		mdef -= 20 * sc->data[SC_ODINS_POWER]->val1;
	if(sc->data[SC_GLASTHEIM_ITEMDEF])
		mdef += sc->data[SC_GLASTHEIM_ITEMDEF]->val2;
	if (sc->data[SC_SOULGOLEM])
		mdef += sc->data[SC_SOULGOLEM]->val3;
	if (sc->data[SC_STONE_WALL])
		mdef += sc->data[SC_STONE_WALL]->val3;
	if (sc->data[SC_PACKING_ENVELOPE8])
		mdef += sc->data[SC_PACKING_ENVELOPE8]->val1;
	if (sc->data[SC_CLIMAX_CRYIMP])
		mdef += 100;

	return (defType)cap_value(mdef,DEFTYPE_MIN,DEFTYPE_MAX);
}

/**
 * Adds magic defense (right-side) modifications based on status changes
 * @param bl: Object to change mdef2 [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param mdef2: Initial mdef2
 * @return modified mdef2 with cap_value(mdef2,SHRT_MIN,SHRT_MAX)
 */
static signed short status_calc_mdef2(struct block_list *bl, struct status_change *sc, int mdef2)
{
	if(!sc || !sc->count)
#ifdef RENEWAL
		return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
#else
		return (short)cap_value(mdef2,1,SHRT_MAX);
#endif

	if(sc->data[SC_BERSERK])
		return 0;
	if(sc->data[SC_SKA])
		return 90;
	if(sc->data[SC_MDEFSET])
		return sc->data[SC_MDEFSET]->val1;

	if(sc->data[SC_MINDBREAKER])
		mdef2 -= mdef2 * sc->data[SC_MINDBREAKER]->val3/100;
	if(sc->data[SC_BURNING])
		mdef2 -= mdef2 * 25 / 100;
	if(sc->data[SC_ANALYZE])
		mdef2 -= mdef2 * (14 * sc->data[SC_ANALYZE]->val1) / 100;

#ifdef RENEWAL
	return (short)cap_value(mdef2,SHRT_MIN,SHRT_MAX);
#else
	return (short)cap_value(mdef2,1,SHRT_MAX);
#endif
}

/**
 * Adds speed modifications based on status changes
 * @param bl: Object to change speed [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param speed: Initial speed
 * @return modified speed with cap_value(speed,10,USHRT_MAX)
 */
static unsigned short status_calc_speed(struct block_list *bl, struct status_change *sc, int speed)
{
	TBL_PC* sd = BL_CAST(BL_PC, bl);
	int speed_rate = 100;

	if (sc == NULL || (sd && sd->state.permanent_speed))
		return (unsigned short)cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);

	if (sd && pc_ismadogear(sd)) { // Mado speed is not affected by other statuses
		int val = 0;

		if (pc_checkskill(sd, NC_MADOLICENCE) < 5)
			val = 50 - 10 * pc_checkskill(sd, NC_MADOLICENCE);
		else
			val -= 25;
		if (sc->data[SC_ACCELERATION])
			val -= 25;
		speed += speed * val / 100;

		return (unsigned short)cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
	}

	if( sd && sd->ud.skilltimer != INVALID_TIMER && (pc_checkskill(sd,SA_FREECAST) > 0 || sd->ud.skill_id == LG_EXEEDBREAK) ) {
		if( sd->ud.skill_id == LG_EXEEDBREAK )
			speed_rate = 160 - 10 * sd->ud.skill_lv;
		else
			speed_rate = 175 - 5 * pc_checkskill(sd,SA_FREECAST);
	} else {
		int val = 0;

		// GetMoveHasteValue2()
		if( sc->data[SC_FUSION] )
			val = 25;
		else if( sd ) {
			if( pc_isriding(sd) || sd->sc.option&OPTION_DRAGON )
				val = 25; // Same bonus
			else if( pc_isridingwug(sd) )
				val = 15 + 5 * pc_checkskill(sd, RA_WUGRIDER);
			else if( sc->data[SC_ALL_RIDING] )
				val = battle_config.rental_mount_speed_boost;
		}
		speed_rate -= val;

		// GetMoveSlowValue()
		if( sd && sc->data[SC_HIDING] && pc_checkskill(sd,RG_TUNNELDRIVE) > 0 )
			val = 120 - 6 * pc_checkskill(sd,RG_TUNNELDRIVE);
		else if( sd && sc->data[SC_CHASEWALK] && sc->data[SC_CHASEWALK]->val3 < 0 )
			val = sc->data[SC_CHASEWALK]->val3;
		else {
			val = 0;
			// Longing for Freedom/Special Singer cancels song/dance penalty
#ifdef RENEWAL
			if (sc->data[SC_ENSEMBLEFATIGUE])
				val = max(val, sc->data[SC_ENSEMBLEFATIGUE]->val2);
#else
			if( sc->data[SC_LONGING] )
				val = max( val, 50 - 10 * sc->data[SC_LONGING]->val1 );
#endif
			else
			if( sd && sc->data[SC_DANCING] )
				val = max( val, 500 - (40 + 10 * (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_BARDDANCER)) * pc_checkskill(sd,(sd->status.sex?BA_MUSICALLESSON:DC_DANCINGLESSON)) );

			if( sc->data[SC_DECREASEAGI] )
				val = max( val, 25 );
			if( sc->data[SC_QUAGMIRE] || sc->data[SC_HALLUCINATIONWALK_POSTDELAY] || (sc->data[SC_GLOOMYDAY] && sc->data[SC_GLOOMYDAY]->val4) )
				val = max( val, 50 );
			if( sc->data[SC_DONTFORGETME] )
				val = max( val, sc->data[SC_DONTFORGETME]->val3 );
			if( sc->data[SC_CURSE] )
				val = max( val, 300 );
			if( sc->data[SC_CHASEWALK] )
				val = max( val, sc->data[SC_CHASEWALK]->val3 );
			if( sc->data[SC_WEDDING] )
				val = max( val, 100 );
			if( sc->data[SC_JOINTBEAT] && sc->data[SC_JOINTBEAT]->val2&(BREAK_ANKLE|BREAK_KNEE) )
				val = max( val, (sc->data[SC_JOINTBEAT]->val2&BREAK_ANKLE ? 50 : 0) + (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE ? 30 : 0) );
			if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 0 )
				val = max( val, sc->data[SC_CLOAKING]->val1 < 3 ? 300 : 30 - 3 * sc->data[SC_CLOAKING]->val1 );
			if( sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY )
				val = max( val, 75 );
			if( sc->data[SC_SLOWDOWN] ) // Slow Potion
				val = max( val, sc->data[SC_SLOWDOWN]->val1 );
			if( sc->data[SC_GATLINGFEVER] )
				val = max( val, 100 );
			if( sc->data[SC_SUITON] )
				val = max( val, sc->data[SC_SUITON]->val3 );
			if( sc->data[SC_SWOO] )
				val = max( val, 300 );
			if( sc->data[SC_SKA] )
				val = max( val, 25 );
			if( sc->data[SC_FREEZING] )
				val = max( val, 30 );
			if( sc->data[SC_MARSHOFABYSS] )
				val = max( val, sc->data[SC_MARSHOFABYSS]->val3 );
			if( sc->data[SC_CAMOUFLAGE] && sc->data[SC_CAMOUFLAGE]->val1 > 2 )
				val = max( val, 25 * (5 - sc->data[SC_CAMOUFLAGE]->val1) );
			if( sc->data[SC_STEALTHFIELD] )
				val = max( val, 20 );
			if( sc->data[SC__LAZINESS] )
				val = max( val, 25 );
			if( sc->data[SC_ROCK_CRUSHER_ATK] )
				val = max( val, sc->data[SC_ROCK_CRUSHER_ATK]->val2 );
			if( sc->data[SC_POWER_OF_GAIA] )
				val = max( val, sc->data[SC_POWER_OF_GAIA]->val2 );
			if( sc->data[SC_MELON_BOMB] )
				val = max( val, sc->data[SC_MELON_BOMB]->val2 );
			if( sc->data[SC_REBOUND] )
				val = max( val, 25 );
			if( sc->data[SC_B_TRAP] )
				val = max( val, sc->data[SC_B_TRAP]->val3 );
			if (sc->data[SC_CATNIPPOWDER])
				val = max(val, sc->data[SC_CATNIPPOWDER]->val3);
			if (sc->data[SC_SP_SHA])
				val = max(val, sc->data[SC_SP_SHA]->val2);
			if (sc->data[SC_CREATINGSTAR])
				val = max(val, 90);

			if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate > 0 ) // Permanent item-based speedup
				val = max( val, sd->bonus.speed_rate + sd->bonus.speed_add_rate );
		}
		speed_rate += val;
		val = 0;

		if( sc->data[SC_MARSHOFABYSS] && speed_rate > 150 )
			speed_rate = 150;

		// GetMoveHasteValue1()
		if( sc->data[SC_SPEEDUP1] ) // !FIXME: used both by NPC_AGIUP and Speed Potion script
			val = max( val, sc->data[SC_SPEEDUP1]->val1 );
		if( sc->data[SC_INCREASEAGI] )
			val = max( val, 25 );
		if( sc->data[SC_WINDWALK] )
			val = max( val, 2 * sc->data[SC_WINDWALK]->val1 );
		if( sc->data[SC_CARTBOOST] )
			val = max( val, 20 );
		if( sd && (sd->class_&MAPID_UPPERMASK) == MAPID_ASSASSIN && pc_checkskill(sd,TF_MISS) > 0 )
			val = max( val, 1 * pc_checkskill(sd,TF_MISS) );
		if( sc->data[SC_CLOAKING] && (sc->data[SC_CLOAKING]->val4&1) == 1 )
			val = max( val, sc->data[SC_CLOAKING]->val1 >= 10 ? 25 : 3 * sc->data[SC_CLOAKING]->val1 - 3 );
		if( sc->data[SC_BERSERK] )
			val = max( val, 25 );
		if( sc->data[SC_RUN] )
			val = max( val, 55 );
		if( sc->data[SC_AVOID] )
			val = max( val, 10 * sc->data[SC_AVOID]->val1 );
		if( sc->data[SC_INVINCIBLE] && !sc->data[SC_INVINCIBLEOFF] )
			val = max( val, 75 );
		if( sc->data[SC_CLOAKINGEXCEED] )
			val = max( val, sc->data[SC_CLOAKINGEXCEED]->val3);
		if (sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 0)
			val = max(val, 50);
		if( sc->data[SC_HOVERING] )
			val = max( val, 10 );
		if( sc->data[SC_GN_CARTBOOST] )
			val = max( val, sc->data[SC_GN_CARTBOOST]->val2 );
		if( sc->data[SC_SWINGDANCE] )
			val = max( val, sc->data[SC_SWINGDANCE]->val3 );
		if( sc->data[SC_WIND_STEP_OPTION] )
			val = max( val, sc->data[SC_WIND_STEP_OPTION]->val2 );
		if( sc->data[SC_FULL_THROTTLE] )
			val = max( val, 25 );
		if (sc->data[SC_ARCLOUSEDASH])
			val = max(val, sc->data[SC_ARCLOUSEDASH]->val3);
		if( sc->data[SC_DORAM_WALKSPEED] )
			val = max(val, sc->data[SC_DORAM_WALKSPEED]->val1);
		if (sc->data[SC_RUSHWINDMILL])
			val = max(val, 25); // !TODO: Confirm bonus movement speed
		if (sc->data[SC_EMERGENCY_MOVE])
			val = max(val, sc->data[SC_EMERGENCY_MOVE]->val2);

		// !FIXME: official items use a single bonus for this [ultramage]
		if( sc->data[SC_SPEEDUP0] ) // Temporary item-based speedup
			val = max( val, sc->data[SC_SPEEDUP0]->val1 );
		if( sd && sd->bonus.speed_rate + sd->bonus.speed_add_rate < 0 ) // Permanent item-based speedup
			val = max( val, -(sd->bonus.speed_rate + sd->bonus.speed_add_rate) );

		speed_rate -= val;

		if( speed_rate < 40 )
			speed_rate = 40;
	}

	// GetSpeed()
	if( sd && pc_iscarton(sd) )
		speed += speed * (50 - 5 * pc_checkskill(sd,MC_PUSHCART)) / 100;
	if( sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1 )
		speed += speed * 50 / 100;
	if( speed_rate != 100 )
		speed = speed * speed_rate / 100;
	if( sc->data[SC_STEELBODY] )
		speed = 200;
	if( sc->data[SC_DEFENDER] )
		speed = max(speed, 200);
	if( sc->data[SC_WALKSPEED] && sc->data[SC_WALKSPEED]->val1 > 0 ) // ChangeSpeed
		speed = speed * 100 / sc->data[SC_WALKSPEED]->val1;

	return (unsigned short)cap_value(speed, MIN_WALK_SPEED, MAX_WALK_SPEED);
}

#ifdef RENEWAL_ASPD
/**
 * Renewal attack speed modifiers based on status changes
 * This function only affects RENEWAL players and comes after base calculation
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param fixed: True - fixed value [malufett]
 *               False - percentage value
 * @return modified aspd
 */
static short status_calc_aspd(struct block_list *bl, struct status_change *sc, bool fixed)
{
	int bonus = 0;

	if (!sc || !sc->count)
		return 0;

	if (fixed) {
		enum sc_type sc_val;

		if (!sc->data[SC_QUAGMIRE]) {
			// !TODO: How does Two-Hand Quicken, Adrenaline Rush, and Spear quick change? (+10%)
			if (bonus < 7 && (sc->data[SC_TWOHANDQUICKEN] || sc->data[SC_ONEHAND] || sc->data[SC_MERC_QUICKEN] || sc->data[SC_ADRENALINE] || sc->data[SC_SPEARQUICKEN]))
				bonus = 7;
			else if (bonus < 6 && sc->data[SC_ADRENALINE2])
				bonus = 6;
			else if (bonus < 5 && sc->data[SC_FLEET])
				bonus = 5;
		}

		if (sc->data[SC_ASSNCROS] && bonus < sc->data[SC_ASSNCROS]->val2) {
#ifdef RENEWAL
			bonus += sc->data[SC_ASSNCROS]->val2;
#else
			if (bl->type != BL_PC)
				bonus += sc->data[SC_ASSNCROS]->val2;
			else {
				switch(((TBL_PC*)bl)->status.weapon) {
					case W_BOW:
					case W_REVOLVER:
					case W_RIFLE:
					case W_GATLING:
					case W_SHOTGUN:
					case W_GRENADE:
						break;
					default:
						bonus += sc->data[SC_ASSNCROS]->val2;
						break;
				}
			}
#endif
		}

		if (bonus < 20 && sc->data[SC_MADNESSCANCEL])
			bonus = 20;
		else if (bonus < 15 && sc->data[SC_BERSERK])
			bonus = 15;

		if (sc->data[sc_val = SC_ASPDPOTION3] || sc->data[sc_val = SC_ASPDPOTION2] || sc->data[sc_val = SC_ASPDPOTION1] || sc->data[sc_val = SC_ASPDPOTION0])
			bonus += sc->data[sc_val]->val1;
		if (sc->data[SC_ATTHASTE_CASH])
			bonus += sc->data[SC_ATTHASTE_CASH]->val1;
	} else {
		if (sc->data[SC_DONTFORGETME])
			bonus -= sc->data[SC_DONTFORGETME]->val2 / 10;
#ifdef RENEWAL
		if (sc->data[SC_ENSEMBLEFATIGUE])
			bonus -= sc->data[SC_ENSEMBLEFATIGUE]->val2 / 10;
#else
		if (sc->data[SC_LONGING])
			bonus -= sc->data[SC_LONGING]->val2 / 10;
#endif
		if (sc->data[SC_STEELBODY])
			bonus -= 25;
		if (sc->data[SC_SKA])
			bonus -= 25;
		if (sc->data[SC_DEFENDER])
			bonus -= sc->data[SC_DEFENDER]->val4 / 10;
		if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY)
			bonus -= 75;
#ifndef RENEWAL
		if (sc->data[SC_GRAVITATION])
			bonus -= sc->data[SC_GRAVITATION]->val2 / 10; // Needs more info
#endif
		if (sc->data[SC_JOINTBEAT]) { // Needs more info
			if (sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST)
				bonus -= 25;
			if (sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE)
				bonus -= 10;
		}
		if (sc->data[SC_FREEZING])
			bonus -= 30;
		if (sc->data[SC_HALLUCINATIONWALK_POSTDELAY])
			bonus -= 50;
		if (sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1)
			bonus -= 10;
		if (sc->data[SC__BODYPAINT])
			bonus -= 5 * sc->data[SC__BODYPAINT]->val1;
		if (sc->data[SC__INVISIBILITY])
			bonus -= sc->data[SC__INVISIBILITY]->val2;
		if (sc->data[SC__GROOMY])
			bonus -= sc->data[SC__GROOMY]->val2;
		if (sc->data[SC_SWINGDANCE])
			bonus += sc->data[SC_SWINGDANCE]->val3;
		if (sc->data[SC_DANCEWITHWUG])
			bonus += sc->data[SC_DANCEWITHWUG]->val3;
		if (sc->data[SC_GLOOMYDAY])
			bonus -= sc->data[SC_GLOOMYDAY]->val3;
		if (sc->data[SC_GT_CHANGE])
			bonus += sc->data[SC_GT_CHANGE]->val3;
		if (sc->data[SC_MELON_BOMB])
			bonus -= sc->data[SC_MELON_BOMB]->val3;
		if (sc->data[SC_BOOST500])
			bonus += sc->data[SC_BOOST500]->val1;
		if (sc->data[SC_EXTRACT_SALAMINE_JUICE])
			bonus += sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1;
		if (sc->data[SC_GOLDENE_FERSE])
			bonus += sc->data[SC_GOLDENE_FERSE]->val3;
		if (sc->data[SC_INCASPDRATE])
			bonus += sc->data[SC_INCASPDRATE]->val1;
		if (sc->data[SC_GATLINGFEVER])
			bonus += sc->data[SC_GATLINGFEVER]->val1;
		if (sc->data[SC_STAR_COMFORT])
			bonus += 3 * sc->data[SC_STAR_COMFORT]->val1;
		if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
			bonus += 10;
		if (sc->data[SC_INCREASEAGI])
			bonus += sc->data[SC_INCREASEAGI]->val1;
		if (sc->data[SC_NIBELUNGEN] && sc->data[SC_NIBELUNGEN]->val2 == RINGNBL_ASPDRATE)
			bonus += 20;
		if (sc->data[SC_STARSTANCE])
			bonus += sc->data[SC_STARSTANCE]->val2;

		struct map_session_data* sd = BL_CAST(BL_PC, bl);
		uint8 skill_lv;

		if (sd && (skill_lv = pc_checkskill(sd, BA_MUSICALLESSON)) > 0)
			bonus += skill_lv;
	}

	return bonus;
}
#endif

/**
 * Modifies ASPD by a number, rather than a percentage (10 = 1 ASPD)
 * A subtraction reduces the delay, meaning an increase in ASPD
 * This comes after the percentage changes and is based on status changes
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param aspd: Object's current ASPD
 * @return modified aspd
 */
static short status_calc_fix_aspd(struct block_list *bl, struct status_change *sc, int aspd)
{
	if (!sc || !sc->count)
		return cap_value(aspd, 0, 2000);
	if (sc->data[SC_OVERED_BOOST])
		return cap_value(2000 - sc->data[SC_OVERED_BOOST]->val3 * 10, 0, 2000);

	if ((sc->data[SC_GUST_OPTION] || sc->data[SC_BLAST_OPTION] || sc->data[SC_WILD_STORM_OPTION]))
		aspd -= 50; // +5 ASPD
	if (sc->data[SC_FIGHTINGSPIRIT])
		aspd -= sc->data[SC_FIGHTINGSPIRIT]->val2;
	if (sc->data[SC_MTF_ASPD])
		aspd -= sc->data[SC_MTF_ASPD]->val1;
	if (sc->data[SC_MTF_ASPD2])
		aspd -= sc->data[SC_MTF_ASPD2]->val1;
	if (sc->data[SC_SOULSHADOW])
		aspd -= 10 * sc->data[SC_SOULSHADOW]->val2;
	if (sc->data[SC_HEAT_BARREL])
		aspd -= sc->data[SC_HEAT_BARREL]->val1 * 10;
	if (sc->data[SC_EP16_2_BUFF_SS])
		aspd -= 100; // +10 ASPD
	if (sc->data[SC_PACKING_ENVELOPE6])
		aspd -= sc->data[SC_PACKING_ENVELOPE6]->val1 * 10;
	if (sc->data[SC_SINCERE_FAITH])
		aspd -= 10 * sc->data[SC_SINCERE_FAITH]->val2;

	return cap_value(aspd, 0, 2000); // Will be recap for proper bl anyway
}

/**
 * Calculates an object's ASPD modifier based on status changes (alters amotion value)
 * Note: The scale of aspd_rate is 1000 = 100%
 * Note2: This only affects Homunculus, Mercenaries, and Pre-renewal Players
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param aspd_rate: Object's current ASPD
 * @return modified aspd_rate
 */
static short status_calc_aspd_rate(struct block_list *bl, struct status_change *sc, int aspd_rate)
{
	int i;

	if(!sc || !sc->count)
		return cap_value(aspd_rate,0,SHRT_MAX);

	if( !sc->data[SC_QUAGMIRE] ) {
		int max = 0;
		if(sc->data[SC_STAR_COMFORT])
			max = sc->data[SC_STAR_COMFORT]->val2;

		if(sc->data[SC_TWOHANDQUICKEN] &&
			max < sc->data[SC_TWOHANDQUICKEN]->val2)
			max = sc->data[SC_TWOHANDQUICKEN]->val2;

		if(sc->data[SC_ONEHAND] &&
			max < sc->data[SC_ONEHAND]->val2)
			max = sc->data[SC_ONEHAND]->val2;

		if(sc->data[SC_MERC_QUICKEN] &&
			max < sc->data[SC_MERC_QUICKEN]->val2)
			max = sc->data[SC_MERC_QUICKEN]->val2;

		if(sc->data[SC_ADRENALINE2] &&
			max < sc->data[SC_ADRENALINE2]->val3)
			max = sc->data[SC_ADRENALINE2]->val3;

		if(sc->data[SC_ADRENALINE] &&
			max < sc->data[SC_ADRENALINE]->val3)
			max = sc->data[SC_ADRENALINE]->val3;

		if(sc->data[SC_SPEARQUICKEN] &&
			max < sc->data[SC_SPEARQUICKEN]->val2)
			max = sc->data[SC_SPEARQUICKEN]->val2;

		if(sc->data[SC_GATLINGFEVER] &&
			max < sc->data[SC_GATLINGFEVER]->val2)
			max = sc->data[SC_GATLINGFEVER]->val2;

		if(sc->data[SC_FLEET] &&
			max < sc->data[SC_FLEET]->val2)
			max = sc->data[SC_FLEET]->val2;

		if(sc->data[SC_ASSNCROS] && max < sc->data[SC_ASSNCROS]->val2) {
			if (bl->type!=BL_PC)
				max = sc->data[SC_ASSNCROS]->val2;
			else
				switch(((TBL_PC*)bl)->status.weapon) {
					case W_BOW:
					case W_REVOLVER:
					case W_RIFLE:
					case W_GATLING:
					case W_SHOTGUN:
					case W_GRENADE:
						break;
					default:
						max = sc->data[SC_ASSNCROS]->val2;
			}
		}
		aspd_rate -= max;

		if(sc->data[SC_BERSERK])
			aspd_rate -= 300;
		else if(sc->data[SC_MADNESSCANCEL])
			aspd_rate -= 200;
	}

	if( sc->data[i=SC_ASPDPOTION3] ||
		sc->data[i=SC_ASPDPOTION2] ||
		sc->data[i=SC_ASPDPOTION1] ||
		sc->data[i=SC_ASPDPOTION0] )
		aspd_rate -= sc->data[i]->val2;

	if (sc->data[SC_ATTHASTE_CASH])
		aspd_rate -= sc->data[SC_ATTHASTE_CASH]->val2;

	if(sc->data[SC_DONTFORGETME])
		aspd_rate += sc->data[SC_DONTFORGETME]->val2;
#ifdef RENEWAL
	if (sc->data[SC_ENSEMBLEFATIGUE])
		aspd_rate += sc->data[SC_ENSEMBLEFATIGUE]->val2;
#else
	if(sc->data[SC_LONGING])
		aspd_rate += sc->data[SC_LONGING]->val2;
#endif
	if(sc->data[SC_STEELBODY])
		aspd_rate += 250;
	if(sc->data[SC_SKA])
		aspd_rate += 250;
	if(sc->data[SC_DEFENDER])
		aspd_rate += sc->data[SC_DEFENDER]->val4;
	if(sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_ENEMY)
		aspd_rate += 250;
#ifndef RENEWAL
	if(sc->data[SC_GRAVITATION])
		aspd_rate += sc->data[SC_GRAVITATION]->val2;
#endif
	if(sc->data[SC_JOINTBEAT]) {
		if( sc->data[SC_JOINTBEAT]->val2&BREAK_WRIST )
			aspd_rate += 250;
		if( sc->data[SC_JOINTBEAT]->val2&BREAK_KNEE )
			aspd_rate += 100;
	}
	if( sc->data[SC_FREEZING] )
		aspd_rate += 300;
	if( sc->data[SC_HALLUCINATIONWALK_POSTDELAY] )
		aspd_rate += 500;
	if( sc->data[SC_PARALYSE] && sc->data[SC_PARALYSE]->val3 == 1 )
		aspd_rate += 100;
	if( sc->data[SC__BODYPAINT] )
		aspd_rate +=  50 * sc->data[SC__BODYPAINT]->val1;
	if( sc->data[SC__INVISIBILITY] )
		aspd_rate += sc->data[SC__INVISIBILITY]->val2 * 10;
	if( sc->data[SC__GROOMY] )
		aspd_rate += sc->data[SC__GROOMY]->val2 * 10;
	if( sc->data[SC_SWINGDANCE] )
		aspd_rate -= sc->data[SC_SWINGDANCE]->val3 * 10;
	if( sc->data[SC_DANCEWITHWUG] )
		aspd_rate -= sc->data[SC_DANCEWITHWUG]->val3 * 10;
	if( sc->data[SC_GLOOMYDAY] )
		aspd_rate += sc->data[SC_GLOOMYDAY]->val3 * 10;
	if( sc->data[SC_GT_CHANGE] )
		aspd_rate -= sc->data[SC_GT_CHANGE]->val3 * 10;
	if( sc->data[SC_MELON_BOMB] )
		aspd_rate += sc->data[SC_MELON_BOMB]->val3 * 10;
	if( sc->data[SC_BOOST500] )
		aspd_rate -= sc->data[SC_BOOST500]->val1 *10;
	if( sc->data[SC_EXTRACT_SALAMINE_JUICE] )
		aspd_rate -= sc->data[SC_EXTRACT_SALAMINE_JUICE]->val1 * 10;
	if( sc->data[SC_INCASPDRATE] )
		aspd_rate -= sc->data[SC_INCASPDRATE]->val1 * 10;
	if( sc->data[SC_GOLDENE_FERSE])
		aspd_rate -= sc->data[SC_GOLDENE_FERSE]->val3 * 10;
	if (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2)
		aspd_rate -= 100;
	if (sc->data[SC_STARSTANCE])
		aspd_rate -= 10 * sc->data[SC_STARSTANCE]->val2;

	return (short)cap_value(aspd_rate,0,SHRT_MAX);
}

/**
 * Modifies the damage delay time based on status changes
 * The lower your delay, the quicker you can act after taking damage
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param dmotion: Object's current damage delay
 * @return modified delay rate
 */
static unsigned short status_calc_dmotion(struct block_list *bl, struct status_change *sc, int dmotion)
{
	if( !sc || !sc->count || map_flag_gvg2(bl->m) || map_getmapflag(bl->m, MF_BATTLEGROUND) )
		return cap_value(dmotion,0,USHRT_MAX);

	/// It has been confirmed on official servers that MvP mobs have no dmotion even without endure
	if( bl->type == BL_MOB && status_get_class_(bl) == CLASS_BOSS )
		return 0;
	if (bl->type == BL_PC && ((TBL_PC *)bl)->special_state.no_walk_delay)
		return 0;
	if( sc->data[SC_ENDURE] || sc->data[SC_RUN] || sc->data[SC_WUGDASH] )
		return 0;

	return (unsigned short)cap_value(dmotion,0,USHRT_MAX);
}

/**
* Adds power atk modifications based on status changes
* @param bl: Object to change patk [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param patk: Initial patk
* @return modified patk with cap_value(patk,0,USHRT_MAX)
*/
static signed short status_calc_patk(struct block_list *bl, struct status_change *sc, int patk)
{
	if (!sc || !sc->count)
		return cap_value(patk, 0, SHRT_MAX);

	if (sc->data[SC_POWERFUL_FAITH])
		patk += sc->data[SC_POWERFUL_FAITH]->val3;
	if (sc->data[SC_COMPETENTIA])
		patk += sc->data[SC_COMPETENTIA]->val2;
	if (sc->data[SC_ABYSS_SLAYER])
		patk += sc->data[SC_ABYSS_SLAYER]->val2;
	if (sc->data[SC_PRON_MARCH])
		patk += sc->data[SC_PRON_MARCH]->val2;

	return (short)cap_value(patk, 0, SHRT_MAX);
}

/**
* Adds spell matk modifications based on status changes
* @param bl: Object to change smatk [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param smatk: Initial smatk
* @return modified smatk with cap_value(smatk,0,USHRT_MAX)
*/
static signed short status_calc_smatk(struct block_list *bl, struct status_change *sc, int smatk)
{
	if (!sc || !sc->count)
		return cap_value(smatk, 0, SHRT_MAX);

	if (sc->data[SC_COMPETENTIA])
		smatk += sc->data[SC_COMPETENTIA]->val2;
	if (sc->data[SC_ABYSS_SLAYER])
		smatk += sc->data[SC_ABYSS_SLAYER]->val2;
	if (sc->data[SC_JAWAII_SERENADE])
		smatk += sc->data[SC_JAWAII_SERENADE]->val2;
	if (sc->data[SC_SPELL_ENCHANTING])
		smatk += sc->data[SC_SPELL_ENCHANTING]->val2;

	return (short)cap_value(smatk, 0, SHRT_MAX);
}

/**
* Adds resist modifications based on status changes
* @param bl: Object to change res [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param res: Initial res
* @return modified res with cap_value(res,0,USHRT_MAX)
*/
static signed short status_calc_res(struct block_list *bl, struct status_change *sc, int res)
{
	if (!sc || !sc->count)
		return cap_value(res, 0, SHRT_MAX);

	if (sc->data[SC_FIRM_FAITH])
		res += sc->data[SC_FIRM_FAITH]->val3;
	if (sc->data[SC_D_MACHINE])
		res += sc->data[SC_D_MACHINE]->val3;
	if (sc->data[SC_MUSICAL_INTERLUDE])
		res += sc->data[SC_MUSICAL_INTERLUDE]->val2;
	if (sc->data[SC_SHADOW_STRIP] && bl->type != BL_PC)
		res -= res * sc->data[SC_SHADOW_STRIP]->val2 / 100;
	if (sc->data[SC_AIN_RHAPSODY])
		res -= sc->data[SC_AIN_RHAPSODY]->val2;

	return (short)cap_value(res, 0, SHRT_MAX);
}

/**
* Adds magic resist modifications based on status changes
* @param bl: Object to change mres [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param mres: Initial mres
* @return modified mres with cap_value(mres,0,USHRT_MAX)
*/
static signed short status_calc_mres(struct block_list *bl, struct status_change *sc, int mres)
{
	if (!sc || !sc->count)
		return cap_value(mres, 0, SHRT_MAX);

	if (sc->data[SC_SHADOW_STRIP] && bl->type != BL_PC)
		mres -= mres * sc->data[SC_SHADOW_STRIP]->val2 / 100;
	if (sc->data[SC_GEF_NOCTURN])
		mres -= sc->data[SC_GEF_NOCTURN]->val2;

	return (short)cap_value(mres, 0, SHRT_MAX);
}

/**
* Adds heal plus modifications based on status changes
* @param bl: Object to change hplus [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param hplus: Initial hplus
* @return modified hplus with cap_value(hplus,0,USHRT_MAX)
*/
static signed short status_calc_hplus(struct block_list *bl, struct status_change *sc, int hplus)
{
	if (!sc || !sc->count)
		return cap_value(hplus, 0, SHRT_MAX);

	return (short)cap_value(hplus, 0, SHRT_MAX);
}

/**
* Adds critical damage rate modifications based on status changes
* @param bl: Object to change crate [PC|MOB|HOM|MER|ELEM]
* @param sc: Object's status change information
* @param crate: Initial crate
* @return modified crate with cap_value(crate,0,USHRT_MAX)
*/
static signed short status_calc_crate(struct block_list *bl, struct status_change *sc, int crate)
{
	if (!sc || !sc->count)
		return cap_value(crate, 0, SHRT_MAX);

	if (sc->data[SC_PRE_ACIES])
		crate += sc->data[SC_PRE_ACIES]->val2;

	return (short)cap_value(crate, 0, SHRT_MAX);
}

/**
 * Calculates a max HP based on status changes
 * Values can either be percentages or fixed, based on how equations are formulated
 * @param bl: Object's block_list data
 * @param maxhp: Object's current max HP
 * @return modified maxhp
 */
static unsigned int status_calc_maxhp(struct block_list *bl, uint64 maxhp)
{
	int rate = 100;

	maxhp += status_get_hpbonus(bl,STATUS_BONUS_FIX);

	if ((rate += status_get_hpbonus(bl,STATUS_BONUS_RATE)) != 100)
		maxhp = maxhp * rate / 100;

	return (unsigned int)cap_value(maxhp,1,UINT_MAX);
}

/**
 * Calculates a max SP based on status changes
 * Values can either be percentages or fixed, bas ed on how equations are formulated
 * @param bl: Object's block_list data
 * @param maxsp: Object's current max SP
 * @return modified maxsp
 */
static unsigned int status_calc_maxsp(struct block_list *bl, uint64 maxsp)
{
	int rate = 100;

	maxsp += status_get_spbonus(bl,STATUS_BONUS_FIX);
	
	if ((rate += status_get_spbonus(bl,STATUS_BONUS_RATE)) != 100)
		maxsp = maxsp * rate / 100;
	
	return (unsigned int)cap_value(maxsp,1,UINT_MAX);
}

/**
* Calculates a max AP based on status changes
* Values can either be percentages or fixed, bas ed on how equations are formulated
* @param bl: Object's block_list data
* @param maxap: Object's current max AP
* @return modified maxap
*/
static unsigned int status_calc_maxap(struct block_list *bl, uint64 maxap)
{
	int rate = 100;

	maxap += status_get_apbonus(bl, STATUS_BONUS_FIX);

	if ((rate += status_get_apbonus(bl, STATUS_BONUS_RATE)) != 100)
		maxap = maxap * rate / 100;

	return (unsigned int)cap_value(maxap, 0, UINT_MAX);
}

/**
 * Changes a player's element based on status changes
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param element: Object's current element
 * @return new element
 */
static unsigned char status_calc_element(struct block_list *bl, struct status_change *sc, int element)
{
	if(!sc || !sc->count)
		return cap_value(element, 0, UCHAR_MAX);

	if(sc->data[SC_FREEZE] || sc->data[SC_CRYSTAL_ARMOR_OPTION])
		return ELE_WATER;
	if((sc->data[SC_STONE] && sc->opt1 == OPT1_STONE) || sc->data[SC_STRONG_PROTECTION_OPTION])
		return ELE_EARTH;
	if(sc->data[SC_FLAMEARMOR_OPTION])
		return ELE_FIRE;
	if(sc->data[SC_EYES_OF_STORM_OPTION])
		return ELE_WIND;
	if(sc->data[SC_POISON_SHIELD_OPTION])
		return ELE_POISON;
	if(sc->data[SC_BENEDICTIO])
		return ELE_HOLY;
	if(sc->data[SC_CHANGEUNDEAD])
		return ELE_UNDEAD;
	if(sc->data[SC_ELEMENTALCHANGE])
		return sc->data[SC_ELEMENTALCHANGE]->val2;
	if(sc->data[SC_SHAPESHIFT])
		return sc->data[SC_SHAPESHIFT]->val2;

	return (unsigned char)cap_value(element,0,UCHAR_MAX);
}

/**
 * Changes a player's element level based on status changes
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param lv: Object's current element level
 * @return new element level
 */
static unsigned char status_calc_element_lv(struct block_list *bl, struct status_change *sc, int lv)
{
	if(!sc || !sc->count)
		return cap_value(lv, 1, 4);

	if(sc->data[SC_FREEZE])
		return 1;
	if(sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
		return 1;
	if(sc->data[SC_BENEDICTIO])
		return 1;
	if(sc->data[SC_CHANGEUNDEAD])
		return 1;
	if(sc->data[SC_ELEMENTALCHANGE])
		return sc->data[SC_ELEMENTALCHANGE]->val1;
	if(sc->data[SC_SHAPESHIFT])
		return 1;
	if(sc->data[SC__INVISIBILITY])
		return 1;
	if (sc->data[SC_FLAMEARMOR_OPTION] || sc->data[SC_CRYSTAL_ARMOR_OPTION] || sc->data[SC_EYES_OF_STORM_OPTION] || 
		sc->data[SC_STRONG_PROTECTION_OPTION] || sc->data[SC_POISON_SHIELD_OPTION])
		return 1;

	return (unsigned char)cap_value(lv,1,4);
}

/**
 * Changes a player's attack element based on status changes
 * @param bl: Object to change aspd [PC|MOB|HOM|MER|ELEM]
 * @param sc: Object's status change information
 * @param element: Object's current attack element
 * @return new attack element
 */
unsigned char status_calc_attack_element(struct block_list *bl, struct status_change *sc, int element)
{
	if(!sc || !sc->count)
		return cap_value(element, 0, UCHAR_MAX);
	if(sc->data[SC_ENCHANTARMS])
		return sc->data[SC_ENCHANTARMS]->val1;
	if(sc->data[SC_WATERWEAPON]
		|| (sc->data[SC_WATER_INSIGNIA] && sc->data[SC_WATER_INSIGNIA]->val1 == 2) )
		return ELE_WATER;
	if(sc->data[SC_EARTHWEAPON]
		|| (sc->data[SC_EARTH_INSIGNIA] && sc->data[SC_EARTH_INSIGNIA]->val1 == 2) )
		return ELE_EARTH;
	if(sc->data[SC_FIREWEAPON]
		|| (sc->data[SC_FIRE_INSIGNIA] && sc->data[SC_FIRE_INSIGNIA]->val1 == 2) )
		return ELE_FIRE;
	if(sc->data[SC_WINDWEAPON]
		|| (sc->data[SC_WIND_INSIGNIA] && sc->data[SC_WIND_INSIGNIA]->val1 == 2) )
		return ELE_WIND;
	if(sc->data[SC_ENCPOISON])
		return ELE_POISON;
	if(sc->data[SC_ASPERSIO])
		return ELE_HOLY;
	if(sc->data[SC_SHADOWWEAPON])
		return ELE_DARK;
	if(sc->data[SC_GHOSTWEAPON] || sc->data[SC__INVISIBILITY])
		return ELE_GHOST;
	if(sc->data[SC_TIDAL_WEAPON_OPTION] || sc->data[SC_TIDAL_WEAPON] )
		return ELE_WATER;
	return (unsigned char)cap_value(element,0,UCHAR_MAX);
}

/**
 * Changes the mode of an object
 * @param bl: Object whose mode to change [PC|MOB|PET|HOM|NPC]
 * @param sc: Object's status change data
 * @param mode: Original mode
 * @return mode with cap_value(mode, 0, INT_MAX)
 */
static int status_calc_mode(struct block_list *bl, struct status_change *sc, int mode)
{
	if(!sc || !sc->count)
		return cap_value(mode, MD_NONE,INT_MAX);
	if(sc->data[SC_MODECHANGE]) {
		if (sc->data[SC_MODECHANGE]->val2)
			mode = (mode&~MD_MASK)|sc->data[SC_MODECHANGE]->val2; // Set mode
		if (sc->data[SC_MODECHANGE]->val3)
			mode = mode|sc->data[SC_MODECHANGE]->val3; // Add mode
		if (sc->data[SC_MODECHANGE]->val4)
			mode = mode&~sc->data[SC_MODECHANGE]->val4; // Del mode
	}
	return cap_value(mode, MD_NONE, INT_MAX);
}

/**
 * Changes the mode of a slave mob
 * @param md: Slave mob whose mode to change
 * @param mmd: Master of slave mob
 */
void status_calc_slave_mode(struct mob_data *md, struct mob_data *mmd)
{
	switch (battle_config.slaves_inherit_mode) {
		case 1: //Always aggressive
			if (!status_has_mode(&md->status,MD_AGGRESSIVE))
				sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0);
			break;
		case 2: //Always passive
			if (status_has_mode(&md->status,MD_AGGRESSIVE))
				sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0);
			break;
		case 4: // Overwrite with slave mode
			sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, MD_CANMOVE|MD_NORANDOMWALK|MD_CANATTACK, 0, 0, 0);
			break;
		default: //Copy master
			if (status_has_mode(&mmd->status,MD_AGGRESSIVE))
				sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, MD_AGGRESSIVE, 0, 0);
			else
				sc_start4(NULL, &md->bl, SC_MODECHANGE, 100, 1, 0, 0, MD_AGGRESSIVE, 0);
			break;
	}
}

/**
 * Gets the name of the given bl
 * @param bl: Object whose name to get [PC|MOB|PET|HOM|NPC]
 * @return name or "Unknown" if any other bl->type than noted above
 */
const char* status_get_name(struct block_list *bl)
{
	nullpo_ret(bl);
	switch (bl->type) {
		case BL_PC:	return ((TBL_PC *)bl)->fakename[0] != '\0' ? ((TBL_PC*)bl)->fakename : ((TBL_PC*)bl)->status.name;
		case BL_MOB:	return ((TBL_MOB*)bl)->name;
		case BL_PET:	return ((TBL_PET*)bl)->pet.name;
		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.name;
		case BL_MER:	return ((TBL_MER *)bl)->db->name.c_str();	// They only have database names which are global, not specific to GID.
		case BL_NPC:	return ((TBL_NPC*)bl)->name;
		case BL_ELEM:	return ((TBL_ELEM *)bl)->db->name.c_str(); // They only have database names which are global, not specific to GID.
	}
	return "Unknown";
}

/**
 * Gets the class/sprite id of the given bl
 * @param bl: Object whose class to get [PC|MOB|PET|HOM|MER|NPC|ELEM]
 * @return class or 0 if any other bl->type than noted above
 */
int status_get_class(struct block_list *bl)
{
	nullpo_ret(bl);
	switch( bl->type ) {
		case BL_PC:	return ((TBL_PC*)bl)->status.class_;
		case BL_MOB:	return ((TBL_MOB*)bl)->vd->class_; // Class used on all code should be the view class of the mob.
		case BL_PET:	return ((TBL_PET*)bl)->pet.class_;
		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.class_;
		case BL_MER:	return ((TBL_MER*)bl)->mercenary.class_;
		case BL_NPC:	return ((TBL_NPC*)bl)->class_;
		case BL_ELEM:	return ((TBL_ELEM*)bl)->elemental.class_;
	}
	return 0;
}

/**
 * Gets the base level of the given bl
 * @param bl: Object whose base level to get [PC|MOB|PET|HOM|MER|NPC|ELEM]
 * @return base level or 1 if any other bl->type than noted above
 */
int status_get_lv(struct block_list *bl)
{
	nullpo_ret(bl);
	switch (bl->type) {
		case BL_PC:	return ((TBL_PC*)bl)->status.base_level;
		case BL_MOB:	return ((TBL_MOB*)bl)->level;
		case BL_PET:	return ((TBL_PET*)bl)->pet.level;
		case BL_HOM:	return ((TBL_HOM*)bl)->homunculus.level;
		case BL_MER:	return ((TBL_MER*)bl)->db->lv;
		case BL_ELEM:	return ((TBL_ELEM*)bl)->db->lv;
		case BL_NPC:	return ((TBL_NPC*)bl)->level;
	}
	return 1;
}

/**
 * Gets the regeneration info of the given bl
 * @param bl: Object whose regen info to get [PC|HOM|MER|ELEM]
 * @return regen data or NULL if any other bl->type than noted above
 */
struct regen_data *status_get_regen_data(struct block_list *bl)
{
	nullpo_retr(NULL, bl);
	switch (bl->type) {
		case BL_PC:	return &((TBL_PC*)bl)->regen;
		case BL_HOM:	return &((TBL_HOM*)bl)->regen;
		case BL_MER:	return &((TBL_MER*)bl)->regen;
		case BL_ELEM:	return &((TBL_ELEM*)bl)->regen;
		default:
			return NULL;
	}
}

/**
 * Gets the status data of the given bl
 * @param bl: Object whose status to get [PC|MOB|PET|HOM|MER|ELEM|NPC]
 * @return status or "dummy_status" if any other bl->type than noted above
 */
struct status_data *status_get_status_data(struct block_list *bl)
{
	nullpo_retr(&dummy_status, bl);

	switch (bl->type) {
		case BL_PC: 	return &((TBL_PC*)bl)->battle_status;
		case BL_MOB:	return &((TBL_MOB*)bl)->status;
		case BL_PET:	return &((TBL_PET*)bl)->status;
		case BL_HOM:	return &((TBL_HOM*)bl)->battle_status;
		case BL_MER:	return &((TBL_MER*)bl)->battle_status;
		case BL_ELEM:	return &((TBL_ELEM*)bl)->battle_status;
		case BL_NPC:	return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : &dummy_status);
		default:
			return &dummy_status;
	}
}

/**
 * Gets the base status data of the given bl
 * @param bl: Object whose status to get [PC|MOB|PET|HOM|MER|ELEM|NPC]
 * @return base_status or NULL if any other bl->type than noted above
 */
struct status_data *status_get_base_status(struct block_list *bl)
{
	nullpo_retr(NULL, bl);
	switch (bl->type) {
		case BL_PC:	return &((TBL_PC*)bl)->base_status;
		case BL_MOB:	return ((TBL_MOB*)bl)->base_status ? ((TBL_MOB*)bl)->base_status : &((TBL_MOB*)bl)->db->status;
		case BL_PET:	return &((TBL_PET*)bl)->db->status;
		case BL_HOM:	return &((TBL_HOM*)bl)->base_status;
		case BL_MER:	return &((TBL_MER*)bl)->base_status;
		case BL_ELEM:	return &((TBL_ELEM*)bl)->base_status;
		case BL_NPC:	return ((mobdb_checkid(((TBL_NPC*)bl)->class_) == 0) ? &((TBL_NPC*)bl)->status : NULL);
		default:
			return NULL;
	}
}

/**
 * Gets the defense of the given bl
 * @param bl: Object whose defense to get [PC|MOB|HOM|MER|ELEM]
 * @return defense with cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX)
 */
defType status_get_def(struct block_list *bl)
{
	struct unit_data *ud;
	struct status_data *status = status_get_status_data(bl);
	int def = status?status->def:0;
	ud = unit_bl2ud(bl);
	if (ud && ud->skilltimer != INVALID_TIMER)
		def -= def * skill_get_castdef(ud->skill_id)/100;

	return cap_value(def, DEFTYPE_MIN, DEFTYPE_MAX);
}

/**
 * Gets the walking speed of the given bl
 * @param bl: Object whose speed to get [PC|MOB|PET|HOM|MER|ELEM|NPC]
 * @return speed
 */
unsigned short status_get_speed(struct block_list *bl)
{
	if(bl->type==BL_NPC)// Only BL with speed data but no status_data [Skotlex]
		return ((struct npc_data *)bl)->speed;
	return status_get_status_data(bl)->speed;
}

/**
 * Gets the party ID of the given bl
 * @param bl: Object whose party ID to get [PC|MOB|PET|HOM|MER|SKILL|ELEM]
 * @return party ID
 */
int status_get_party_id(struct block_list *bl)
{
	nullpo_ret(bl);
	switch (bl->type) {
		case BL_PC:
			return ((TBL_PC*)bl)->status.party_id;
		case BL_PET:
			if (((TBL_PET*)bl)->master)
				return ((TBL_PET*)bl)->master->status.party_id;
			break;
		case BL_MOB: {
				struct mob_data *md=(TBL_MOB*)bl;
				if( md->master_id > 0 ) {
					struct map_session_data *msd;
					if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
						return msd->status.party_id;
					return -md->master_id;
				}
			}
			break;
		case BL_HOM:
			if (((TBL_HOM*)bl)->master)
				return ((TBL_HOM*)bl)->master->status.party_id;
			break;
		case BL_MER:
			if (((TBL_MER*)bl)->master)
				return ((TBL_MER*)bl)->master->status.party_id;
			break;
		case BL_SKILL:
			if (((TBL_SKILL*)bl)->group)
				return ((TBL_SKILL*)bl)->group->party_id;
			break;
		case BL_ELEM:
			if (((TBL_ELEM*)bl)->master)
				return ((TBL_ELEM*)bl)->master->status.party_id;
			break;
	}
	return 0;
}

/**
 * Gets the guild ID of the given bl
 * @param bl: Object whose guild ID to get [PC|MOB|PET|HOM|MER|SKILL|ELEM|NPC]
 * @return guild ID
 */
int status_get_guild_id(struct block_list *bl)
{
	nullpo_ret(bl);
	switch (bl->type) {
		case BL_PC:
			return ((TBL_PC*)bl)->status.guild_id;
		case BL_PET:
			if (((TBL_PET*)bl)->master)
				return ((TBL_PET*)bl)->master->status.guild_id;
			break;
		case BL_MOB:
			{
				struct map_session_data *msd;
				struct mob_data *md = (struct mob_data *)bl;
				if (md->guardian_data)	// Guardian's guild [Skotlex]
					return md->guardian_data->guild_id;
				if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
					return msd->status.guild_id; // Alchemist's mobs [Skotlex]
			}
			break;
		case BL_HOM:
			if (((TBL_HOM*)bl)->master)
				return ((TBL_HOM*)bl)->master->status.guild_id;
			break;
		case BL_MER:
			if (((TBL_MER*)bl)->master)
				return ((TBL_MER*)bl)->master->status.guild_id;
			break;
		case BL_NPC:
			if (((TBL_NPC*)bl)->subtype == NPCTYPE_SCRIPT)
				return ((TBL_NPC*)bl)->u.scr.guild_id;
			break;
		case BL_SKILL:
			if (((TBL_SKILL*)bl)->group)
				return ((TBL_SKILL*)bl)->group->guild_id;
			break;
		case BL_ELEM:
			if (((TBL_ELEM*)bl)->master)
				return ((TBL_ELEM*)bl)->master->status.guild_id;
			break;
	}
	return 0;
}

/**
 * Gets the guild emblem ID of the given bl
 * @param bl: Object whose emblem ID to get [PC|MOB|PET|HOM|MER|SKILL|ELEM|NPC]
 * @return guild emblem ID
 */
int status_get_emblem_id(struct block_list *bl)
{
	nullpo_ret(bl);
	switch (bl->type) {
		case BL_PC:
			return ((TBL_PC*)bl)->guild_emblem_id;
		case BL_PET:
			if (((TBL_PET*)bl)->master)
				return ((TBL_PET*)bl)->master->guild_emblem_id;
			break;
		case BL_MOB:
			{
				struct map_session_data *msd;
				struct mob_data *md = (struct mob_data *)bl;
				if (md->guardian_data)	// Guardian's guild [Skotlex]
					return md->guardian_data->emblem_id;
				if (md->special_state.ai && (msd = map_id2sd(md->master_id)) != NULL)
					return msd->guild_emblem_id; // Alchemist's mobs [Skotlex]
			}
			break;
		case BL_HOM:
			if (((TBL_HOM*)bl)->master)
				return ((TBL_HOM*)bl)->master->guild_emblem_id;
			break;
		case BL_MER:
			if (((TBL_MER*)bl)->master)
				return ((TBL_MER*)bl)->master->guild_emblem_id;
			break;
		case BL_NPC:
			if (((TBL_NPC*)bl)->subtype == NPCTYPE_SCRIPT && ((TBL_NPC*)bl)->u.scr.guild_id > 0) {
				struct guild *g = guild_search(((TBL_NPC*)bl)->u.scr.guild_id);
				if (g)
					return g->emblem_id;
			}
			break;
		case BL_ELEM:
			if (((TBL_ELEM*)bl)->master)
				return ((TBL_ELEM*)bl)->master->guild_emblem_id;
			break;
	}
	return 0;
}

/**
 * Gets the race2 of a mob or pet
 * @param bl: Object whose race2 to get [MOB|PET]
 * @return race2
 */
std::vector<e_race2> status_get_race2(struct block_list *bl)
{
	nullpo_retr(std::vector<e_race2>(),bl);

	if (bl->type == BL_MOB)
		return ((struct mob_data *)bl)->db->race2;
	if (bl->type == BL_PET)
		return ((struct pet_data *)bl)->db->race2;
	return std::vector<e_race2>();
}

/**
 * Checks if an object is dead 
 * @param bl: Object to check [PC|MOB|HOM|MER|ELEM]
 * @return 1: Is dead or 0: Is alive
 */
int status_isdead(struct block_list *bl)
{
	nullpo_ret(bl);
	return status_get_status_data(bl)->hp == 0;
}

/**
 * Checks if an object is immune to magic 
 * @param bl: Object to check [PC|MOB|HOM|MER|ELEM]
 * @return value of magic damage to be blocked
 */
int status_isimmune(struct block_list *bl)
{
	struct status_change *sc =status_get_sc(bl);

	if (sc) {
		if (sc->data[SC_HERMODE])
			return 100;

		if (sc->data[SC_DEADLY_DEFEASANCE])
			return 0;
	}

	if (bl->type == BL_PC &&
		((TBL_PC*)bl)->special_state.no_magic_damage >= battle_config.gtb_sc_immunity)
		return ((TBL_PC*)bl)->special_state.no_magic_damage;
	return 0;
}

/**
 * Get view data of an object 
 * @param bl: Object whose view data to get [PC|MOB|PET|HOM|MER|ELEM|NPC]
 * @return view data structure bl->vd
 */
struct view_data* status_get_viewdata(struct block_list *bl)
{
	nullpo_retr(NULL, bl);
	switch (bl->type) {
		case BL_PC:  return &((TBL_PC*)bl)->vd;
		case BL_MOB: return ((TBL_MOB*)bl)->vd;
		case BL_PET: return &((TBL_PET*)bl)->vd;
		case BL_NPC: return &((TBL_NPC*)bl)->vd;
		case BL_HOM: return ((TBL_HOM*)bl)->vd;
		case BL_MER: return ((TBL_MER*)bl)->vd;
		case BL_ELEM: return ((TBL_ELEM*)bl)->vd;
	}
	return NULL;
}

/**
 * Set view data of an object
 * This function deals with class, mount, and item views
 * SC views are set in clif_getareachar_unit() 
 * @param bl: Object whose view data to set [PC|MOB|PET|HOM|MER|ELEM|NPC]
 * @param class_: class of the object
 */
void status_set_viewdata(struct block_list *bl, int class_)
{
	struct view_data* vd;
	nullpo_retv(bl);
	if (mobdb_checkid(class_) || mob_is_clone(class_))
		vd = mob_get_viewdata(class_);
	else if (npcdb_checkid(class_))
		vd = npc_get_viewdata(class_);
	else if (homdb_checkid(class_))
		vd = hom_get_viewdata(class_);
	else if (mercenary_db.exists(class_))
		vd = mercenary_get_viewdata(class_);
	else if (elemental_db.exists(class_))
		vd = elemental_get_viewdata(class_);
	else
		vd = NULL;

	switch (bl->type) {
	case BL_PC:
		{
			TBL_PC* sd = (TBL_PC*)bl;
			if (pcdb_checkid(class_)) {
				if (sd->sc.option&OPTION_RIDING) {
					switch (class_) { // Adapt class to a Mounted one.
						case JOB_KNIGHT:
							class_ = JOB_KNIGHT2;
							break;
						case JOB_CRUSADER:
							class_ = JOB_CRUSADER2;
							break;
						case JOB_LORD_KNIGHT:
							class_ = JOB_LORD_KNIGHT2;
							break;
						case JOB_PALADIN:
							class_ = JOB_PALADIN2;
							break;
						case JOB_BABY_KNIGHT:
							class_ = JOB_BABY_KNIGHT2;
							break;
						case JOB_BABY_CRUSADER:
							class_ = JOB_BABY_CRUSADER2;
							break;
					}
				}
				sd->vd.class_ = class_;
				clif_get_weapon_view(sd, &sd->vd.weapon, &sd->vd.shield);
				sd->vd.head_top = sd->status.head_top;
				sd->vd.head_mid = sd->status.head_mid;
				sd->vd.head_bottom = sd->status.head_bottom;
				sd->vd.hair_style = cap_value(sd->status.hair, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
				sd->vd.hair_color = cap_value(sd->status.hair_color, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
				sd->vd.cloth_color = cap_value(sd->status.clothes_color, MIN_CLOTH_COLOR, MAX_CLOTH_COLOR);
				sd->vd.body_style = cap_value(sd->status.body, MIN_BODY_STYLE, MAX_BODY_STYLE);
				sd->vd.sex = sd->status.sex;

				if (sd->vd.cloth_color) {
					if(sd->sc.option&OPTION_WEDDING && battle_config.wedding_ignorepalette)
						sd->vd.cloth_color = 0;
					if(sd->sc.option&OPTION_XMAS && battle_config.xmas_ignorepalette)
						sd->vd.cloth_color = 0;
					if(sd->sc.option&(OPTION_SUMMER|OPTION_SUMMER2) && battle_config.summer_ignorepalette)
						sd->vd.cloth_color = 0;
					if(sd->sc.option&OPTION_HANBOK && battle_config.hanbok_ignorepalette)
						sd->vd.cloth_color = 0;
					if(sd->sc.option&OPTION_OKTOBERFEST && battle_config.oktoberfest_ignorepalette)
						sd->vd.cloth_color = 0;
				}
				if ( sd->vd.body_style && sd->sc.option&OPTION_COSTUME)
 					sd->vd.body_style = 0;
			} else if (vd)
				memcpy(&sd->vd, vd, sizeof(struct view_data));
			else
				ShowError("status_set_viewdata (PC): No view data for class %d\n", class_);
		}
	break;
	case BL_MOB:
		{
			TBL_MOB* md = (TBL_MOB*)bl;
			if (vd){
				mob_free_dynamic_viewdata( md );

				md->vd = vd;
			}else if( pcdb_checkid( class_ ) ){
				mob_set_dynamic_viewdata( md );

				md->vd->class_ = class_;
				md->vd->hair_style = cap_value(md->vd->hair_style, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
				md->vd->hair_color = cap_value(md->vd->hair_color, MIN_HAIR_COLOR, MAX_HAIR_COLOR);
			}else
				ShowError("status_set_viewdata (MOB): No view data for class %d\n", class_);
		}
	break;
	case BL_PET:
		{
			TBL_PET* pd = (TBL_PET*)bl;
			if (vd) {
				memcpy(&pd->vd, vd, sizeof(struct view_data));
				if (!pcdb_checkid(vd->class_)) {
					pd->vd.hair_style = battle_config.pet_hair_style;
					if(pd->pet.equip) {
						pd->vd.head_bottom = itemdb_viewid(pd->pet.equip);
						if (!pd->vd.head_bottom)
							pd->vd.head_bottom = pd->pet.equip;
					}
				}
			} else
				ShowError("status_set_viewdata (PET): No view data for class %d\n", class_);
		}
	break;
	case BL_NPC:
		{
			TBL_NPC* nd = (TBL_NPC*)bl;
			if (vd)
				memcpy(&nd->vd, vd, sizeof(struct view_data));
			else if (pcdb_checkid(class_)) {
				memset(&nd->vd, 0, sizeof(struct view_data));
				nd->vd.class_ = class_;
				nd->vd.hair_style = cap_value(nd->vd.hair_style, MIN_HAIR_STYLE, MAX_HAIR_STYLE);
			} else {
				ShowError("status_set_viewdata (NPC): Invalid view data %d\n", class_);
				if (bl->m >= 0)
					ShowDebug("Source (NPC): %s at %s (%d,%d)\n", nd->name, map_mapid2mapname(bl->m), bl->x, bl->y);
				else
					ShowDebug("Source (NPC): %s (invisible/not on a map)\n", nd->name);
				ShowDebug( "Source (NPC): %s is located in: %s\n", nd->name, nd->path );
			}
			break;
		}
	break;
	case BL_HOM:
		{
			struct homun_data *hd = (struct homun_data*)bl;
			if (vd)
				hd->vd = vd;
			else
				ShowError("status_set_viewdata (HOMUNCULUS): No view data for class %d\n", class_);
		}
		break;
	case BL_MER:
		{
			s_mercenary_data *md = (s_mercenary_data*)bl;
			if (vd)
				md->vd = vd;
			else
				ShowError("status_set_viewdata (MERCENARY): No view data for class %d\n", class_);
		}
		break;
	case BL_ELEM:
		{
			s_elemental_data *ed = (s_elemental_data*)bl;
			if (vd)
				ed->vd = vd;
			else
				ShowError("status_set_viewdata (ELEMENTAL): No view data for class %d\n", class_);
		}
		break;
	}
}

/**
 * Get status change data of an object
 * @param bl: Object whose sc data to get [PC|MOB|HOM|MER|ELEM|NPC]
 * @return status change data structure bl->sc
 */
struct status_change *status_get_sc(struct block_list *bl)
{
	if( bl )
	switch (bl->type) {
		case BL_PC:  return &((TBL_PC*)bl)->sc;
		case BL_MOB: return &((TBL_MOB*)bl)->sc;
		case BL_NPC: return &((TBL_NPC*)bl)->sc;
		case BL_HOM: return &((TBL_HOM*)bl)->sc;
		case BL_MER: return &((TBL_MER*)bl)->sc;
		case BL_ELEM: return &((TBL_ELEM*)bl)->sc;
	}
	return NULL;
}

/**
 * Initiate (memset) the status change data of an object
 * @param bl: Object whose sc data to memset [PC|MOB|HOM|MER|ELEM|NPC]
 */
void status_change_init(struct block_list *bl)
{
	struct status_change *sc = status_get_sc(bl);
	nullpo_retv(sc);
	memset(sc, 0, sizeof (struct status_change));
}

/*========================================== [Playtester]
* Returns the interval for status changes that iterate multiple times
* through the timer (e.g. those that deal damage in regular intervals)
* @param type: Status change (SC_*)
*------------------------------------------*/
static int status_get_sc_interval(enum sc_type type)
{
	switch (type) {
		case SC_POISON:
		case SC_LEECHESEND:
		case SC_DPOISON:
		case SC_DEATHHURT:
			return 1000;
		case SC_BURNING:
		case SC_PYREXIA:
			return 3000;
		case SC_MAGICMUSHROOM:
			return 4000;
		case SC_STONE:
			return 5000;
		case SC_BLEEDING:
		case SC_TOXIN:
			return 10000;
		case SC_HELLS_PLANT:
			return 333;
		case SC_SHIELDSPELL_HP:
			return 3000;
		case SC_SHIELDSPELL_SP:
			return 5000;
		default:
			break;
	}
	return 0;
}

/**
 * Applies SC defense to a given status change
 * This function also determines whether or not the status change will be applied
 * @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
 * @param bl: Target of the status change
 * @param type: Status change (SC_*)
 * @param rate: Initial percentage rate of affecting bl (0~10000)
 * @param tick: Initial duration that the status change affects bl
 * @param flag: Value which determines what parts to calculate. See e_status_change_start_flags
 * @return adjusted duration based on flag values
 */
t_tick status_get_sc_def(struct block_list *src, struct block_list *bl, enum sc_type type, int rate, t_tick tick, unsigned char flag)
{
	/// Resistance rate: 10000 = 100%
	/// Example:	50% (5000) -> sc_def = 5000 -> 25%;
	///				5000ms -> tick_def = 5000 -> 2500ms
	int sc_def = 0, tick_def = -1; // -1 = use sc_def
	/// Fixed resistance value (after rate calculation)
	/// Example:	25% (2500) -> sc_def2 = 2000 -> 5%;
	///				2500ms -> tick_def2=2000 -> 500ms
	int sc_def2 = 0, tick_def2 = 0;

	struct status_data *status, *status_src;
	struct status_change *sc;
	struct map_session_data *sd;

	nullpo_ret(bl);
	if (src == NULL)
		return tick?tick:1; // This should not happen in current implementation, but leave it anyway

	// Skills (magic type) that are blocked by Golden Thief Bug card or Wand of Hermod
	if (status_isimmune(bl)) {
		std::shared_ptr<s_skill_db> skill = skill_db.find(battle_getcurrentskill(src));

		if (skill == nullptr) // Check for ground-type skills using the status when a player moves through units
			skill = skill_db.find(status_db.getSkill(type));

		if (skill != nullptr && skill->skill_type == BF_MAGIC && // Basic magic skill
			!skill->inf2[INF2_IGNOREGTB] && // Specific skill to bypass
			((skill->inf == INF_ATTACK_SKILL || skill->inf == INF_GROUND_SKILL || skill->inf == INF_SUPPORT_SKILL) || // Target skills should get blocked even when cast on self
			 (skill->inf == INF_SELF_SKILL && src != bl))) // Self skills should get blocked on all targets except self
			return 0;
	}

	rate = cap_value(rate, 0, 10000);
	sd = BL_CAST(BL_PC,bl);
	status = status_get_status_data(bl);
	status_src = status_get_status_data(src);
	sc = status_get_sc(bl);
	if( sc && !sc->count )
		sc = NULL;

	switch (type) {
		case SC_POISON:
		case SC_DPOISON:
			sc_def = status->vit*100;
#ifndef RENEWAL
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
			if (sd) {
				// For players: 60000 - 450*vit - 100*luk
				tick_def = status->vit*75;
				tick_def2 = status->luk*100;
			} else {
				// For monsters: 30000 - 200*vit
				tick>>=1;
				tick_def = (status->vit*200)/3;
			}
#endif
			break;
		case SC_STUN:
			sc_def = status->vit*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
			tick_def2 = status->luk*10;
			break;
		case SC_SILENCE:
#ifndef RENEWAL
			sc_def = status->vit*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#else
			sc_def = status->int_*100;
			sc_def2 = (status->vit + status->luk) * 5 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#endif
			tick_def2 = status->luk*10;
			break;
		case SC_BLEEDING:
#ifndef RENEWAL
			sc_def = status->vit*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#else
			sc_def = status->agi*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#endif
			tick_def2 = status->luk*10;
			break;
		case SC_SLEEP:
#ifndef RENEWAL
			sc_def = status->int_*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#else
			sc_def = status->agi*100;
			sc_def2 = (status->int_ + status->luk) * 5 + status_get_lv(bl)*10 - status_get_lv(src)*10;
#endif
			tick_def2 = status->luk*10;
			break;
		case SC_STONE:
			sc_def = status->mdef*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
			tick_def = 0; // No duration reduction
			break;
		case SC_FREEZE:
			sc_def = status->mdef*100;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
			tick_def2 = status_src->luk*-10; // Caster can increase final duration with luk
			break;
		case SC_CURSE:
			// Special property: immunity when luk is zero
			if (status->luk == 0)
				return 0;
			sc_def = status->luk*100;
			sc_def2 = status->luk*10 - status_get_lv(src)*10; // Curse only has a level penalty and no resistance
			tick_def = status->vit*100;
			tick_def2 = status->luk*10;
			break;
		case SC_BLIND:
			sc_def = (status->vit + status->int_)*50;
			sc_def2 = status->luk*10 + status_get_lv(bl)*10 - status_get_lv(src)*10;
			tick_def2 = status->luk*10;
			break;
		case SC_CONFUSION:
			sc_def = (status->str + status->int_)*50;
			sc_def2 = status_get_lv(src)*10 - status_get_lv(bl)*10 - status->luk*10; // Reversed sc_def2
			tick_def2 = status->luk*10;
			break;
		case SC_DECREASEAGI:
			if (sd)
				tick >>= 1; // Half duration for players.
			sc_def2 = status->mdef*100;
			break;
		case SC_ANKLE:
			if(status_has_mode(status,MD_STATUSIMMUNE)) // Lasts 5 times less on bosses
				tick /= 5;
			sc_def = status->agi*50;
			break;
		case SC_JOINTBEAT:
			sc_def2 = 270 * status->str / 100; // 270 * STR / 100
			tick_def2 = (status->luk * 50 + status->agi * 200) / 2; // (50 * LUK / 100 + 20 * AGI / 100) / 2
			break;
		case SC_DEEPSLEEP:
			tick_def2 = status_get_base_status(bl)->int_ * 25 + status_get_lv(bl) * 50;
			break;
		case SC_NETHERWORLD:
			// Resistance: {(Target's Base Level / 50) + (Target's Job Level / 10)} seconds
			tick_def2 = status_get_lv(bl) * 20 + (sd ? sd->status.job_level : 1) * 100;
			break;
		case SC_MARSHOFABYSS:
			// 5 second (Fixed) + 25 second - {( INT + LUK ) / 20 second }
			tick_def2 = (status->int_ + status->luk)*50;
			break;
		case SC_STASIS:
			// 10 second (fixed) + { Stasis Skill level * 10 - (Target's VIT + DEX) / 20 }
			tick_def2 = (status->vit + status->dex) * 50;
			break;
		case SC_WHITEIMPRISON:
			if( tick == 5000 ) // 100% on caster
				break;
			if( bl->type == BL_PC )
				tick_def2 = status_get_lv(bl)*20 + status->vit*25 + status->agi*10;
			else
				tick_def2 = (status->vit + status->luk)*50;
			break;
		case SC_BURNING:
			tick_def2 = 75*status->luk + 125*status->agi;
			break;
		case SC_FREEZING:
			tick_def2 = (status->vit + status->dex)*50;
			break;
		case SC_OBLIVIONCURSE: // 100% - (100 - 0.8 x INT)
			sc_def = status->int_ * 80;
			sc_def = max(sc_def, 500); // minimum of 5% resist
			tick_def = 0;
			tick_def2 = (status->vit + status->luk) * 500;
			break;
		case SC_TOXIN:
		case SC_PARALYSE:
		case SC_VENOMBLEED:
		case SC_MAGICMUSHROOM:
		case SC_DEATHHURT:
		case SC_PYREXIA:
		case SC_LEECHESEND:
			tick_def2 = (status->vit + status->luk) * 500;
			break;
		case SC_BITE: // {(Base Success chance) - (Target's AGI / 4)}
			sc_def2 = status->agi*25;
			break;
		case SC_ELECTRICSHOCKER:
			tick_def2 = (status->vit + status->agi) * 70;
			break;
		case SC_CRYSTALIZE:
			tick_def2 = (sd ? sd->status.vit : status_get_base_status(bl)->vit) * 100;
			break;
		case SC_VACUUM_EXTREME:
			tick_def2 = (sd ? sd->status.str : status_get_base_status(bl)->str) * 50;
			break;
		case SC_KYOUGAKU:
			tick_def2 = 30*status->int_;
			break;
		case SC_PARALYSIS:
			tick_def2 = (status->vit + status->luk)*50;
			break;
		case SC_VOICEOFSIREN:
			// Resistance: {(Target's Base Level / 10) + (Target's Job Level / 5)} seconds
			tick_def2 = status_get_lv(bl) * 100 + (sd ? sd->status.job_level : 1) * 200;
			break;
		case SC_B_TRAP:
			tick_def = (sd ? sd->status.str : status_get_base_status(bl)->str) * 50; //! TODO: Figure out reduction formula
			break;
		case SC_NORECOVER_STATE:
			tick_def2 = status->luk * 100;
			break;
		default:
			// Effect that cannot be reduced? Likely a buff.
			if (!(rnd()%10000 < rate))
				return 0;
			return tick ? tick : 1;
	}

	if (sd) {
		if (battle_config.pc_sc_def_rate != 100) {
			sc_def = sc_def*battle_config.pc_sc_def_rate/100;
			sc_def2 = sc_def2*battle_config.pc_sc_def_rate/100;
		}

		sc_def = min(sc_def, battle_config.pc_max_sc_def*100);
		sc_def2 = min(sc_def2, battle_config.pc_max_sc_def*100);

		if (battle_config.pc_sc_def_rate != 100) {
			tick_def = tick_def*battle_config.pc_sc_def_rate/100;
			tick_def2 = tick_def2*battle_config.pc_sc_def_rate/100;
		}
	} else {
		if (battle_config.mob_sc_def_rate != 100) {
			sc_def = sc_def*battle_config.mob_sc_def_rate/100;
			sc_def2 = sc_def2*battle_config.mob_sc_def_rate/100;
		}

		sc_def = min(sc_def, battle_config.mob_max_sc_def*100);
		sc_def2 = min(sc_def2, battle_config.mob_max_sc_def*100);

		if (battle_config.mob_sc_def_rate != 100) {
			tick_def = tick_def*battle_config.mob_sc_def_rate/100;
			tick_def2 = tick_def2*battle_config.mob_sc_def_rate/100;
		}
	}

	if (sc) {
		if (sc->data[SC_SCRESIST])
			sc_def += sc->data[SC_SCRESIST]->val1*100; // Status resist
#ifdef RENEWAL
		else if (sc->data[SC_SIEGFRIED] && (type == SC_BLIND || type == SC_STONE || type == SC_FREEZE || type == SC_STUN || type == SC_CURSE || type == SC_SLEEP || type == SC_SILENCE))
			sc_def += sc->data[SC_SIEGFRIED]->val3 * 100; // Status resistance.
#else
		else if (sc->data[SC_SIEGFRIED])
			sc_def += sc->data[SC_SIEGFRIED]->val3*100; // Status resistance.
#endif
		else if (sc->data[SC_LEECHESEND] && sc->data[SC_LEECHESEND]->val3 == 0) {
			switch (type) {
				case SC_BLIND:
				case SC_STUN:
					return 0; // Immune
			}
		} else if (sc->data[SC_OBLIVIONCURSE] && sc->data[SC_OBLIVIONCURSE]->val3 == 0) {
			switch (type) {
				case SC_SILENCE:
				case SC_CURSE:
					return 0; // Immune
			}
		}
	}

	// When tick def not set, reduction is the same for both.
	if(tick_def == -1)
		tick_def = sc_def;

	// Natural resistance
	if (!(flag&SCSTART_NORATEDEF)) {
		rate -= rate*sc_def/10000;
		rate -= sc_def2;

		// Item resistance (only applies to rate%)
		if (sd) {
			for (const auto &it : sd->reseff) {
				if (it.id == type)
					rate -= rate * it.val / 10000;
			}
			if (sd->sc.data[SC_COMMONSC_RESIST] && SC_COMMON_MIN <= type && type <= SC_COMMON_MAX)
				rate -= rate*sd->sc.data[SC_COMMONSC_RESIST]->val1/100;
		}

		// Aegis accuracy
		if(rate > 0 && rate%10 != 0) rate += (10 - rate%10);
	}

	std::shared_ptr<s_status_change_db> scdb = status_db.find(type);

	// Cap minimum rate
	rate = max(rate, scdb->min_rate);
	// Cap minimum duration
	tick = i64max(tick, scdb->min_duration);

	if (rate < 10000 && (rate <= 0 || !(rnd()%10000 < rate)))
		return 0;

	// Duration cannot be reduced
	if (flag&SCSTART_NOTICKDEF)
		return i64max(tick, 1);

	tick -= tick*tick_def/10000;
	tick -= tick_def2;

	// Minimum durations
	switch (type) {
		case SC_ANKLE:
		case SC_MARSHOFABYSS:
			tick = i64max(tick, 5000); // Minimum duration 5s
			break;
		case SC_FREEZING:
			tick = i64max(tick, 6000); // Minimum duration 6s
			// NEED AEGIS CHECK: might need to be 10s (http://ro.gnjoy.com/news/notice/View.asp?seq=5352)
			break;
		case SC_BURNING:
		case SC_STASIS:
		case SC_VOICEOFSIREN:
			tick = i64max(tick, 10000); // Minimum duration 10s
			break;
		default:
			// Skills need to trigger even if the duration is reduced below 1ms
			tick = i64max(tick, 1);
			break;
	}

	return tick;
}

/**
 * Applies SC effect
 * @param bl: Source to apply effect
 * @param type: Status change (SC_*)
 * @param dval1~3: Depends on type of status change
 * Author: Ind
 */
void status_display_add(struct block_list *bl, enum sc_type type, int dval1, int dval2, int dval3) {
	struct eri *eri;
	struct sc_display_entry **sc_display;
	struct sc_display_entry ***sc_display_ptr;
	struct sc_display_entry *entry;
	int i;
	unsigned char sc_display_count;
	unsigned char *sc_display_count_ptr;

	nullpo_retv(bl);

	switch( bl->type ){
		case BL_PC: {
			struct map_session_data* sd = (struct map_session_data*)bl;

			sc_display_ptr = &sd->sc_display;
			sc_display_count_ptr = &sd->sc_display_count;
			eri = pc_sc_display_ers;
			}
			break;
		case BL_NPC: {
			struct npc_data* nd = (struct npc_data*)bl;

			sc_display_ptr = &nd->sc_display;
			sc_display_count_ptr = &nd->sc_display_count;
			eri = npc_sc_display_ers;
			}
			break;
		default:
			return;
	}

	sc_display = *sc_display_ptr;
	sc_display_count = *sc_display_count_ptr;

	ARR_FIND(0, sc_display_count, i, sc_display[i]->type == type);

	if( i != sc_display_count ) {
		sc_display[i]->val1 = dval1;
		sc_display[i]->val2 = dval2;
		sc_display[i]->val3 = dval3;
		return;
	}

	entry = ers_alloc(eri, struct sc_display_entry);

	entry->type = type;
	entry->val1 = dval1;
	entry->val2 = dval2;
	entry->val3 = dval3;

	RECREATE(sc_display, struct sc_display_entry *, ++sc_display_count);
	sc_display[sc_display_count - 1] = entry;
	*sc_display_ptr = sc_display;
	*sc_display_count_ptr = sc_display_count;
}

/**
 * Removes SC effect
 * @param bl: Source to remove effect
 * @param type: Status change (SC_*)
 * Author: Ind
 */
void status_display_remove(struct block_list *bl, enum sc_type type) {
	struct eri *eri;
	struct sc_display_entry **sc_display;
	struct sc_display_entry ***sc_display_ptr;
	int i;
	unsigned char sc_display_count;
	unsigned char *sc_display_count_ptr;

	nullpo_retv(bl);

	switch( bl->type ){
		case BL_PC: {
			struct map_session_data* sd = (struct map_session_data*)bl;

			sc_display_ptr = &sd->sc_display;
			sc_display_count_ptr = &sd->sc_display_count;
			eri = pc_sc_display_ers;
			}
			break;
		case BL_NPC: {
			struct npc_data* nd = (struct npc_data*)bl;

			sc_display_ptr = &nd->sc_display;
			sc_display_count_ptr = &nd->sc_display_count;
			eri = npc_sc_display_ers;
			}
			break;
		default:
			return;
	}

	sc_display = *sc_display_ptr;
	sc_display_count = *sc_display_count_ptr;

	ARR_FIND(0, sc_display_count, i, sc_display[i]->type == type);

	if( i != sc_display_count ) {
		int cursor;

		ers_free(eri, sc_display[i]);
		sc_display[i] = NULL;

		/* The all-mighty compact-o-matic */
		for( i = 0, cursor = 0; i < sc_display_count; i++ ) {
			if( sc_display[i] == NULL )
				continue;

			if( i != cursor )
				sc_display[cursor] = sc_display[i];

			cursor++;
		}

		if( !(sc_display_count = cursor) ) {
			aFree(sc_display);
			sc_display = NULL;
		}

		*sc_display_ptr = sc_display;
		*sc_display_count_ptr = sc_display_count;
	}
}

/**
 * Applies SC defense to a given status change
 * This function also determines whether or not the status change will be applied
 * @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
 * @param bl: Target of the status change (See: enum sc_type)
 * @param type: Status change (SC_*)
 * @param rate: Initial percentage rate of affecting bl (0~10000)
 * @param val1~4: Depends on type of status change
 * @param tick: Initial duration that the status change affects bl
 * @param flag: Value which determines what parts to calculate. See e_status_change_start_flags
 * @return adjusted duration based on flag values
 */
int status_change_start(struct block_list* src, struct block_list* bl,enum sc_type type,int rate,int val1,int val2,int val3,int val4,t_tick duration,unsigned char flag) {
	struct map_session_data *sd = NULL;
	struct status_change* sc;
	struct status_change_entry* sce;
	struct status_data *status;
	struct view_data *vd;
	int undead_flag, tick_time = 0;
	uint64 calc_flag;
	bool sc_isnew = true;
	std::shared_ptr<s_status_change_db> scdb = status_db.find(type);

	nullpo_ret(bl);
	sc = status_get_sc(bl);
	status = status_get_status_data(bl);

	if( !scdb ) {
		ShowError("status_change_start: Invalid status change (%d)!\n", type);
		return 0;
	}

	if( !sc )
		return 0; // Unable to receive status changes

	if( bl->type != BL_NPC && status_isdead(bl) && ( type != SC_NOCHAT && type != SC_JAILED ) ) // SC_NOCHAT and SC_JAILED should work even on dead characters
		return 0;

	if (status_change_isDisabledOnMap(type, map_getmapdata(bl->m)))
		return 0;

	if (sc->data[SC_GRAVITYCONTROL])
		return 0; // !TODO: Confirm what statuses/conditions (if not all) are blocked.

	// Uncomment to prevent status adding hp to gvg mob (like bloodylust=hp*3 etc...
//	if (bl->type == BL_MOB)
//		if (util::vector_exists(status_get_race2(bl), RC2_GVG) && status_sc2scb_flag(type)&SCB_MAXHP) return 0;

	// Fail if Madogear is active
	if (sc->option&OPTION_MADOGEAR && flag&SCSTART_NOAVOID && scdb->flag[SCF_FAILEDMADO])
		return 0;

	// Check for Boss resistances
	if(status->mode&MD_STATUSIMMUNE && !(flag&SCSTART_NOAVOID) && scdb->flag[SCF_BOSSRESIST])
		return 0;

	// Check for MVP resistance
	if(status->mode&MD_MVP && !(flag&SCSTART_NOAVOID) && scdb->flag[SCF_MVPRESIST])
		return 0;

	// Check failing SCs from list
	if (!scdb->fail.empty()) {
		for (const auto &it : scdb->fail) {
			if (it && sc->data[it])
				return 0;
		}
	}

	// Adjust tick according to status resistances
	if( !(flag&(SCSTART_NOAVOID|SCSTART_LOADED)) ) {
		duration = status_get_sc_def(src, bl, type, rate, duration, flag);
		if( !duration )
			return 0;
	}

	int tick = (int)duration;

	sd = BL_CAST(BL_PC, bl);
	vd = status_get_viewdata(bl);

	undead_flag = battle_check_undead(status->race,status->def_ele);
	// Check for immunities / sc fails
	switch (type) {
		case SC_VACUUM_EXTREME:
			if (sc && sc->data[SC_VACUUM_EXTREME_POSTDELAY] && sc->data[SC_VACUUM_EXTREME_POSTDELAY]->val2 == val2) // Ignore post delay from other vacuum (this will make stack effect enabled)
				return 0;
			break;
		case SC_STONE:
		case SC_FREEZE:
			// Undead are immune to Freeze/Stone
			if (undead_flag && !(flag&SCSTART_NOAVOID))
				return 0;
		case SC_ALL_RIDING:
			if( !sd || sc->option&(OPTION_RIDING|OPTION_DRAGON|OPTION_WUG|OPTION_MADOGEAR) )
				return 0;
			break;
		case SC_SIGNUMCRUCIS:
			// Only affects demons and undead element (but not players)
			if((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
				return 0;
			break;
		case SC_AETERNA:
			if( sc->data[SC_STONE] && sc->opt1 == OPT1_STONE )
				return 0;
			break;
		case SC_KYRIE:
		case SC_TUNAPARTY:
			if (bl->type == BL_MOB)
				return 0;
			break;
		case SC_ADRENALINE:
			if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE)))
				return 0;
			break;
		case SC_ADRENALINE2:
			if(sd && !pc_check_weapontype(sd,skill_get_weapontype(BS_ADRENALINE2)))
				return 0;
			break;
		case SC_CLOAKING:
			// Avoid cloaking with no wall and low skill level. [Skotlex]
			// Due to the cloaking card, we have to check the wall versus to known
			// skill level rather than the used one. [Skotlex]
			// if (sd && val1 < 3 && skill_check_cloaking(bl,NULL))
			if( sd && pc_checkskill(sd, AS_CLOAKING) < 3 && !skill_check_cloaking(bl,NULL) )
				return 0;
			break;
		case SC_MODECHANGE: {
				int32 mode;
				struct status_data *bstatus = status_get_base_status(bl);
				if (!bstatus) return 0;
				if (sc->data[type]) { // Pile up with previous values.
					if (!val2) val2 = sc->data[type]->val2;
					val3 |= sc->data[type]->val3;
					val4 |= sc->data[type]->val4;
				}
				mode = val2 ? ((val2&~MD_MASK) | val2) : bstatus->mode; // Base mode
				if (val4) mode = static_cast<e_mode>(mode&~val4); // Del mode
				if (val3) mode = static_cast<e_mode>(mode | val3); // Add mode
				if (mode == bstatus->mode) { // No change.
					if (sc->data[type]) // Abort previous status
						return status_change_end(bl, type, INVALID_TIMER);
					return 0;
				}
			}
			break;
		// Strip skills, need to divest something or it fails.
		case SC_STRIPWEAPON:
			if (val2 == 1)
				val2 = 0; // Brandish Spear/Bowling Bash effet. Do not take weapon off.
			else if (sd && !(flag&SCSTART_LOADED)) { // Apply sc anyway if loading saved sc_data
				short i;
				uint8 successFlag = 0;
				if(sd->bonus.unstripable_equip&EQP_WEAPON)
					return 0;
				i = sd->equip_index[EQI_HAND_L];
				if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
					successFlag|=1;
					pc_unequipitem(sd,i,3); // Left-hand weapon
				}
	
				i = sd->equip_index[EQI_HAND_R];
				if (i>=0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_WEAPON) {
					successFlag|=2;
					pc_unequipitem(sd,i,3);
				}
				if (!successFlag) return 0;
			}
			if (tick == 1) return 1; // Minimal duration: Only strip without causing the SC
			break;
		case SC_STRIPSHIELD:
			if( val2 == 1 ) val2 = 0; // GX effect. Do not take shield off..
			else
			if (sd && !(flag&SCSTART_LOADED)) {
				short i;
				if(sd->bonus.unstripable_equip&EQP_SHIELD)
					return 0;
				i = sd->equip_index[EQI_HAND_L];
				if ( i < 0 || !sd->inventory_data[i] || sd->inventory_data[i]->type != IT_ARMOR )
					return 0;
				pc_unequipitem(sd,i,3);
			}
			if (tick == 1) return 1; // Minimal duration: Only strip without causing the SC
			break;
		case SC_STRIPARMOR:
			if (sd && !(flag&SCSTART_LOADED)) {
				short i;
				if(sd->bonus.unstripable_equip&EQP_ARMOR)
					return 0;
				i = sd->equip_index[EQI_ARMOR];
				if ( i < 0 || !sd->inventory_data[i] )
					return 0;
				pc_unequipitem(sd,i,3);
			}
			if (tick == 1) return 1; // Minimal duration: Only strip without causing the SC
			break;
		case SC_STRIPHELM:
			if (sd && !(flag&SCSTART_LOADED)) {
				short i;
				if(sd->bonus.unstripable_equip&EQP_HELM)
					return 0;
				i = sd->equip_index[EQI_HEAD_TOP];
				if ( i < 0 || !sd->inventory_data[i] )
					return 0;
				pc_unequipitem(sd,i,3);
			}
			if (tick == 1) return 1; // Minimal duration: Only strip without causing the SC
			break;
		case SC_SHADOW_STRIP:
			if (sd && !(flag&SCSTART_LOADED)) {
				if (sd->bonus.unstripable_equip&EQP_SHADOW_GEAR)
					return 0;

				bool successFlag = false;

				for( int i = EQI_SHADOW_ARMOR; i <= EQI_SHADOW_ACC_L; i++ ){
					int index = sd->equip_index[i];

					if( index >= 0 && sd->inventory_data[index] != nullptr ){
						pc_unequipitem( sd, index, 3 );
						successFlag = true;
					}
				}

				if (!successFlag)
					return 0;
			}
			if (tick == 1)
				return 1;
			break;
		case SC_MERC_FLEEUP:
		case SC_MERC_ATKUP:
		case SC_MERC_HPUP:
		case SC_MERC_SPUP:
		case SC_MERC_HITUP:
			if( bl->type != BL_MER )
				return 0; // Stats only for Mercenaries
			break;
		case SC_STRFOOD:
			if (sc->data[SC_FOOD_STR_CASH] && sc->data[SC_FOOD_STR_CASH]->val1 > val1)
				return 0;
			break;
		case SC_AGIFOOD:
			if (sc->data[SC_FOOD_AGI_CASH] && sc->data[SC_FOOD_AGI_CASH]->val1 > val1)
				return 0;
			break;
		case SC_VITFOOD:
			if (sc->data[SC_FOOD_VIT_CASH] && sc->data[SC_FOOD_VIT_CASH]->val1 > val1)
				return 0;
			break;
		case SC_INTFOOD:
			if (sc->data[SC_FOOD_INT_CASH] && sc->data[SC_FOOD_INT_CASH]->val1 > val1)
				return 0;
			break;
		case SC_DEXFOOD:
			if (sc->data[SC_FOOD_DEX_CASH] && sc->data[SC_FOOD_DEX_CASH]->val1 > val1)
				return 0;
			break;
		case SC_LUKFOOD:
			if (sc->data[SC_FOOD_LUK_CASH] && sc->data[SC_FOOD_LUK_CASH]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_STR_CASH:
			if (sc->data[SC_STRFOOD] && sc->data[SC_STRFOOD]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_AGI_CASH:
			if (sc->data[SC_AGIFOOD] && sc->data[SC_AGIFOOD]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_VIT_CASH:
			if (sc->data[SC_VITFOOD] && sc->data[SC_VITFOOD]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_INT_CASH:
			if (sc->data[SC_INTFOOD] && sc->data[SC_INTFOOD]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_DEX_CASH:
			if (sc->data[SC_DEXFOOD] && sc->data[SC_DEXFOOD]->val1 > val1)
				return 0;
			break;
		case SC_FOOD_LUK_CASH:
			if (sc->data[SC_LUKFOOD] && sc->data[SC_LUKFOOD]->val1 > val1)
				return 0;
			break;
		case SC_CAMOUFLAGE:
			if( sd && pc_checkskill(sd, RA_CAMOUFLAGE) < 3 && !skill_check_camouflage(bl,NULL) )
				return 0;
			break;
		case SC__STRIPACCESSORY:
			if( sd ) {
				short i = -1;
				if( !(sd->bonus.unstripable_equip&EQP_ACC_L) ) {
					i = sd->equip_index[EQI_ACC_L];
					if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
						pc_unequipitem(sd,i,3); // Left-Accessory
				}
				if( !(sd->bonus.unstripable_equip&EQP_ACC_R) ) {
					i = sd->equip_index[EQI_ACC_R];
					if( i >= 0 && sd->inventory_data[i] && sd->inventory_data[i]->type == IT_ARMOR )
						pc_unequipitem(sd,i,3); // Right-Accessory
				}
				if( i < 0 )
					return 0;
			}
			if (tick == 1) return 1; // Minimal duration: Only strip without causing the SC
			break;
		case SC_C_MARKER:
			if (src == bl)
				return 0;
			else {
				struct status_change *tsc = status_get_sc(bl);
				// Failed if the target is already marked and the new marker that isn't same marker
				if (tsc && tsc->data[type] && tsc->data[type]->val2 != src->id)
					return 0;
			}
			break;
		case SC_MADNESSCANCEL:
			if (sc->data[type]) { // Toggle the status but still consume requirements.
				status_change_end(bl, type, INVALID_TIMER);
				return 0;
			}
			break;
		case SC_TOXIN:
		case SC_PARALYSE:
		case SC_VENOMBLEED:
		case SC_MAGICMUSHROOM:
		case SC_DEATHHURT:
		case SC_PYREXIA:
		case SC_OBLIVIONCURSE:
		case SC_LEECHESEND:
			if (val3 == 0) // Don't display icon on self
				flag |= SCSTART_NOICON;
			for (int32 i = SC_TOXIN; i <= SC_LEECHESEND; i++) {
				if (sc->data[i] && sc->data[i]->val3 == 1) // It doesn't stack or even renew on the target
					return 0;
				else if (sc->data[i] && sc->data[i]->val3 == 0)
					status_change_end(bl, static_cast<sc_type>(i), INVALID_TIMER); // End the bonus part on the caster
			}
			break;
	}

	// Before overlapping fail, one must check for status cured.
	switch (type) {
		case SC_BLESSING:
			// !TODO: Blessing and Agi up should do 1 damage against players on Undead Status, even on PvM
			// !but cannot be plagiarized (this requires aegis investigation on packets and official behavior) [Brainstorm]
			if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC) {
				status_change_end(bl, SC_CURSE, INVALID_TIMER);
				if (sc->data[SC_STONE] && sc->opt1 == OPT1_STONE)
					status_change_end(bl, SC_STONE, INVALID_TIMER);
				if (sc->data[SC_CURSE]) {
						status_change_end(bl, SC_CURSE, INVALID_TIMER);
						return 1; // End Curse and do not give stat boost
				}
			}
			if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
				status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
			break;
		case SC_INCREASEAGI:
			if(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_HIGH)
				status_change_end(bl, SC_SPIRIT, INVALID_TIMER);
			break;
		case SC_DELUGE:
			if (sc->data[SC_FOGWALL] && sc->data[SC_BLIND])
				status_change_end(bl, SC_BLIND, INVALID_TIMER);
			break;
		case SC_SILENCE:
			if (sc->data[SC_GOSPEL] && sc->data[SC_GOSPEL]->val4 == BCT_SELF)
				status_change_end(bl, SC_GOSPEL, INVALID_TIMER);
			break;
		case SC_IMPOSITIO:
			if (sc->data[SC_IMPOSITIO] && sc->data[SC_IMPOSITIO]->val1 > val1) //Replace higher level effect for lower.
				status_change_end(bl,SC_IMPOSITIO,INVALID_TIMER);
			break;
		case SC_ENDURE:
			if (sd && sd->special_state.no_walk_delay)
				return 1;
			break;
		case SC_MADOGEAR:
			status_db.removeByStatusFlag(bl, { SCF_MADOCANCEL });
			if (sd)
				pc_bonus_script_clear(sd, BSF_REM_ON_MADOGEAR);
			break;
		default:
			// If new SC has OPT1 while unit has OPT1, fail it!
			if (sc->opt1 && scdb->opt1)
				return 0;
			break;
	}

	std::vector<sc_type> list;

	if (type == SC_BERSERK && val3 == SC__BLOODYLUST) //There is some reasons that using SC_BERSERK first before SC__BLOODYLUST itself on Akinari's fix
		list = status_db.getEnd(SC__BLOODYLUST);
	else
		list = scdb->end;

	// End the SCs from the list
	if (!list.empty()) {
		bool isRemoving = false;

		for (const auto &it : list) {
			sc_type rem_sc = it;

			if (sc->data[rem_sc]) {
				switch (rem_sc) {
					case SC_BERSERK:
					case SC_SATURDAYNIGHTFEVER:
						sc->data[rem_sc]->val2 = 0; // Mark to not lose hp
					default:
						status_change_end(bl, rem_sc, INVALID_TIMER);
						isRemoving = true;
						break;
				}
			}
		}
		if (isRemoving && scdb->end_return)
			return 0;
	}

	// Check for overlapping fails
	if( (sce = sc->data[type]) ) {
		if (scdb->flag[SCF_OVERLAPFAIL])
			return 0;
		switch( type ) {
			case SC_MERC_FLEEUP:
			case SC_MERC_ATKUP:
			case SC_MERC_HPUP:
			case SC_MERC_SPUP:
			case SC_MERC_HITUP:
				if( sce->val1 > val1 )
					val1 = sce->val1;
				break;
			case SC_ADRENALINE:
			case SC_ADRENALINE2:
			case SC_WEAPONPERFECTION:
			case SC_OVERTHRUST:
				if (sce->val2 > val2)
					return 0;
				break;
			case SC_GOSPEL:
				 // Must not override a casting gospel char.
				if(sce->val4 == BCT_SELF)
					return 0;
				if(sce->val1 > val1)
					return 1;
				break;
			case SC_ENDURE:
				if(sce->val4 && !val4)
					return 1; // Don't let you override infinite endure.
				if(sce->val1 > val1)
					return 1;
				break;
			case SC_JAILED:
				// When a player is already jailed, do not edit the jail data.
				val2 = sce->val2;
				val3 = sce->val3;
				val4 = sce->val4;
				break;
			case SC_LERADSDEW:
				if (sc && sc->data[SC_BERSERK])
					return 0;
			case SC_SHAPESHIFT:
			case SC_PROPERTYWALK:
				break;
			case SC_LEADERSHIP:
			case SC_GLORYWOUNDS:
			case SC_SOULCOLD:
			case SC_HAWKEYES:
				if( sce->val4 && !val4 ) // You cannot override master guild aura
					return 0;
				break;
			case SC_JOINTBEAT:
				if (sc && sc->data[type]->val2 & BREAK_NECK)
					return 0; // BREAK_NECK cannot be stacked with new breaks until the status is over.
				val2 |= sce->val2; // Stackable ailments
			default:
				if (scdb->flag[SCF_OVERLAPIGNORELEVEL])
					break;
				if(sce->val1 > val1)
					return 1; // Return true to not mess up skill animations. [Skotlex]
		}
	}

	vd = status_get_viewdata(bl);
	calc_flag = scdb->calc_flag;
	if(!(flag&SCSTART_LOADED)) // &4 - Do not parse val settings when loading SCs
	switch(type)
	{
		/* Permanent effects */
		case SC_AETERNA:
		case SC_MODECHANGE:
		case SC_WEIGHT50:
		case SC_WEIGHT90:
		case SC_BROKENWEAPON:
		case SC_BROKENARMOR:
		case SC_READYSTORM:
		case SC_READYDOWN:
		case SC_READYCOUNTER:
		case SC_READYTURN:
		case SC_DODGE:
		case SC_PUSH_CART:
		case SC_SPRITEMABLE:
		case SC_CLAN_INFO:
		case SC_DAILYSENDMAILCNT:
		case SC_SOULATTACK:
			tick = INFINITE_TICK;
			break;

		case SC_KEEPING:
		case SC_BARRIER: {
			unit_data *ud = unit_bl2ud(bl);

			if (ud)
				ud->attackabletime = ud->canact_tick = ud->canmove_tick = gettick() + tick;
		}
			break;
		case SC_DECREASEAGI:
		case SC_INCREASEAGI:
		case SC_ADORAMUS:
			if (type == SC_ADORAMUS) {
				// 1000% base chance to blind, but still can be resisted
				sc_start(src, bl, SC_BLIND, 1000, val1, skill_get_time(scdb->skill_id, val1));
				if (sc->data[SC_ADORAMUS])
					return 0; //Adoramus can't refresh itself, but it can cause blind again
			}
			val2 = 2 + val1; // Agi change
			break;
		case SC_ENDURE:
			val2 = 7; // Hit-count [Celest]
			if( !(flag&SCSTART_NOAVOID) && (bl->type&(BL_PC|BL_MER)) && !map_flag_gvg2(bl->m) && !map_getmapflag(bl->m, MF_BATTLEGROUND) && !val4 ) {
				struct map_session_data *tsd;
				if( sd ) {
					int i;
					for( i = 0; i < MAX_DEVOTION; i++ ) {
						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
							status_change_start(src,&tsd->bl, type, 10000, val1, val2, val3, val4, tick, SCSTART_NOAVOID|SCSTART_NOICON);
					}
				}
				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
					status_change_start(src,&tsd->bl, type, 10000, val1, val2, val3, val4, tick, SCSTART_NOAVOID|SCSTART_NOICON);
			}
			if( val4 )
				tick = INFINITE_TICK;
			break;
		case SC_AUTOBERSERK:
			if (status->hp < status->max_hp>>2 &&
				(!sc->data[SC_PROVOKE] || sc->data[SC_PROVOKE]->val4==0))
					sc_start4(src,bl,SC_PROVOKE,100,10,0,0,1,60000);
			tick = INFINITE_TICK;
			break;
		case SC_SIGNUMCRUCIS:
			val2 = 10 + 4*val1; // Def reduction
			tick = INFINITE_TICK;
			clif_emotion(bl, ET_SWEAT);
			break;
		case SC_MAXIMIZEPOWER:
			tick_time = val2 = tick>0?tick:60000;
			tick = INFINITE_TICK; // Duration sent to the client should be infinite
			break;
		case SC_EDP:
			val2 = val1 + 2; // Chance to Poison enemies.
#ifndef RENEWAL
			val3 = 50*(val1+1); // Damage increase (+50 +50*lv%)
#endif
			if (sd) {
				uint16 poison_level = pc_checkskill(sd, GC_RESEARCHNEWPOISON);

				if (poison_level > 0) {
					tick += 30000; // Base of 30 seconds
					tick += poison_level * 15 * 1000; // Additional 15 seconds per level
				}
			}
			break;
		case SC_POISONREACT:
#ifdef RENEWAL
			val2=(val1 - ((val1-1) % 1 - 1)) / 2;
#else
			val2=(val1+1)/2 + val1/10; // Number of counters [Skotlex]
#endif
			val3=50; // + 5*val1; // Chance to counter. [Skotlex]
			break;
		case SC_MAGICROD:
			val2 = val1*20; // SP gained
			break;
		case SC_KYRIE:
			if( val4 ) { // Formulas for Praefatio
				val2 = (status->max_hp * (val1 * 2 + 10) / 100) + val4 * 2; //%Max HP to absorb
				val3 = 6 + val1; //Hits
			} else { // Formulas for Kyrie Eleison
				val2 = status->max_hp * (val1 * 2 + 10) / 100;
				val3 = (val1 / 2 + 5);
			}
			break;
		case SC_MAGICPOWER:
#ifdef RENEWAL
			val3 = 5 * val1; // Matk% increase
#else
			val2 = 1; // Lasts 1 invocation
			val3 = 10 * val1; // Matk% increase
			val4 = 0; // 0 = ready to be used, 1 = activated and running
#endif
			break;
		case SC_SACRIFICE:
			val2 = 5; // Lasts 5 hits
			tick = INFINITE_TICK;
			break;
		case SC_ENCPOISON:
			val2= 250+50*val1; // Poisoning Chance (2.5+0.5%) in 1/10000 rate
		case SC_ASPERSIO:
		case SC_FIREWEAPON:
		case SC_WATERWEAPON:
		case SC_WINDWEAPON:
		case SC_EARTHWEAPON:
		case SC_SHADOWWEAPON:
		case SC_GHOSTWEAPON:
			skill_enchant_elemental_end(bl,type);
			break;
		case SC_ELEMENTALCHANGE:
			// val1 : Element Lvl (if called by skill lvl 1, takes random value between 1 and 4)
			// val2 : Element (When no element, random one is picked)
			// val3 : 0 = called by skill 1 = called by script (fixed level)
			if( !val2 ) val2 = rnd()%ELE_ALL;

			if( val1 == 1 && val3 == 0 )
				val1 = 1 + rnd()%4;
			else if( val1 > 4 )
				val1 = 4; // Max Level
			val3 = 0; // Not need to keep this info.
			break;
		case SC_PROVIDENCE:
			val2 = val1*5; // Race/Ele resist
			break;
		case SC_REFLECTSHIELD:
			val2 = 10+val1*3; // %Dmg reflected
			// val4 used to mark if reflect shield is an inheritance bonus from Devotion
			if( !(flag&SCSTART_NOAVOID) && (bl->type&(BL_PC|BL_MER)) ) {
				struct map_session_data *tsd;
				if( sd ) {
					int i;
					for( i = 0; i < MAX_DEVOTION; i++ ) {
						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
							status_change_start(src,&tsd->bl, type, 10000, val1, val2, 0, 1, tick, SCSTART_NOAVOID|SCSTART_NOICON);
					}
				}
				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
					status_change_start(src,&tsd->bl, type, 10000, val1, val2, 0, 1, tick, SCSTART_NOAVOID|SCSTART_NOICON);
			}
			break;
		case SC_STRIPWEAPON:
			if (!sd) // Watk reduction
				val2 = 25;
			break;
		case SC_STRIPSHIELD:
			if (!sd) // Def reduction
				val2 = 15;
			break;
		case SC_STRIPARMOR:
			if (!sd) // Vit reduction
				val2 = 40;
			break;
		case SC_STRIPHELM:
			if (!sd) // Int reduction
				val2 = 40;
			break;
		case SC_AUTOSPELL:
			// Val1 Skill LV of Autospell
			// Val2 Skill ID to cast
			// Val3 Max Lv to cast
#ifdef RENEWAL
			val4 = val1 * 2; // Chance of casting
#else
			val4 = 5 + val1*2; // Chance of casting
#endif
			break;
		case SC_VOLCANO:
			{
				int8 enchant_eff[] = { 10, 14, 17, 19, 20 }; // Enchant addition
				uint8 i = max((val1-1)%5, 0);

#ifdef RENEWAL
				val2 = 5 + val1 * 5; // ATK/MATK increase
#else
				val2 = val1*10; // Watk increase
				if (status->def_ele != ELE_FIRE)
					val2 = 0;
#endif
				val3 = enchant_eff[i];
			}
			break;
		case SC_VIOLENTGALE:
			{
				int8 enchant_eff[] = { 10, 14, 17, 19, 20 }; // Enchant addition
				uint8 i = max((val1-1)%5, 0);

				val2 = val1*3; // Flee increase
#ifndef RENEWAL
				if (status->def_ele != ELE_WIND)
					val2 = 0;
#endif
				val3 = enchant_eff[i];
			}
			break;
		case SC_DELUGE:
			{
				int8 deluge_eff[]  = {  5,  9, 12, 14, 15 }; // HP addition rate n/100
				int8 enchant_eff[] = { 10, 14, 17, 19, 20 }; // Enchant addition
				uint8 i = max((val1-1)%5, 0);

				val2 = deluge_eff[i]; // HP increase
#ifndef RENEWAL
				if (status->def_ele != ELE_WATER)
					val2 = 0;
#endif
				val3 = enchant_eff[i];
			}
			break;
		case SC_SUITON:
			if (!val2 || (sd && (sd->class_&MAPID_BASEMASK) == MAPID_NINJA)) {
				// No penalties.
				val2 = 0; // Agi penalty
				val3 = 0; // Walk speed penalty
				break;
			}
			val3 = 50;
			val2 = 3*((val1+1)/3);
			if (val1 > 4) val2--;
			//Suiton is a special case, stop effect is forced and only happens when target enters it
			if (!unit_blown_immune(bl, 0x1))
				unit_stop_walking(bl, 9);
			break;
		case SC_ONEHAND:
		case SC_TWOHANDQUICKEN:
			val2 = 300;
			if (val1 > 10) // For boss casted skills [Skotlex]
				val2 += 20*(val1-10);
			break;
		case SC_MERC_QUICKEN:
			val2 = 300;
			break;
#ifndef RENEWAL_ASPD
		case SC_SPEARQUICKEN:
			val2 = 200+10*val1;
			break;
#endif
		case SC_DANCING:
			// val1 : Skill ID + LV
			// val2 : Skill Group of the Dance.
			// val3 : Brings the skill_lv (merged into val1 here)
			// val4 : Partner
			if (val1 == CG_MOONLIT)
				clif_status_change(bl,EFST_MOON,1,tick,0, 0, 0);
			val1|= (val3<<16);
			val3 = tick/1000; // Tick duration
			tick_time = 1000; // [GodLesZ] tick time
			break;
#ifndef RENEWAL
		case SC_LONGING:
			val2 = 500-100*val1; // Aspd penalty.
			break;
#else
		case SC_ENSEMBLEFATIGUE:
			val2 = 30; // Speed and ASPD penalty
			break;
		case SC_RICHMANKIM:
			val2 = 10 + 10 * val1; // Exp increase bonus
			break;
		case SC_DRUMBATTLE:
			val2 = 15 + val1 * 5; // Atk increase
			val3 = val1 * 15; // Def increase
			break;
		case SC_NIBELUNGEN:
			val2 = rnd() % RINGNBL_MAX; // See e_nibelungen_status
			break;
		case SC_SIEGFRIED:
			val2 = val1 * 3; // Elemental Resistance
			val3 = val1 * 5; // Status ailment resistance
			break;
		case SC_WHISTLE:
			val2 = 18 + 2 * val1; // Flee increase
			val3 = (val1 + 1) / 2; // Perfect dodge increase
			break;
		case SC_ASSNCROS:
			val2 = val1 < 10 ? val1 * 2 - 1 : 20; // ASPD increase
			break;
		case SC_POEMBRAGI:
			val2 = 2 * val1; // Cast time reduction
			val3 = 3 * val1; // After-cast delay reduction
			break;
		case SC_APPLEIDUN:
			val2 = val1 < 10 ? 9 + val1 : 20; // HP rate increase
			val3 = 2 * val1; // Potion recovery rate
			break;
		case SC_HUMMING:
			val2 = 4 * val1; // Hit increase
			break;
		case SC_DONTFORGETME:
			val2 = 1 + 30 * val1; // ASPD decrease
			val3 = 5 + 2 * val1; // Movement speed adjustment.
			break;
		case SC_FORTUNE:
			val2 = val1 * 10; // Critical increase
			break;
		case SC_SERVICE4U:
			val2 = val1 < 10 ? 9 + val1 : 20; // MaxSP percent increase
			val3 = 5 + val1; // SP cost reduction
			break;
#endif
		case SC_EXPLOSIONSPIRITS:
			val2 = 75 + 25*val1; // Cri bonus
			break;

		case SC_ASPDPOTION0:
		case SC_ASPDPOTION1:
		case SC_ASPDPOTION2:
		case SC_ASPDPOTION3:
			val2 = 50*(2+type-SC_ASPDPOTION0);
			break;

		case SC_ATTHASTE_CASH:
			val2 = 50*val1; // Just custom for pre-re
			break;

		case SC_NOCHAT:
			// A hardcoded interval of 60 seconds is expected, as the time that SC_NOCHAT uses is defined by
			// mmocharstatus.manner, each negative point results in 1 minute with this status activated.
			// This is done this way because the message that the client displays is hardcoded, and only
			// shows how many minutes are remaining. [Panikon]
			tick = 60000;
			val1 = battle_config.manner_system; // Mute filters.
			if (sd) {
				clif_changestatus(sd,SP_MANNER,sd->status.manner);
				clif_updatestatus(sd,SP_MANNER);
			}
			break;

		case SC_STONE:
			val3 = max(val3, 100); // Incubation time
			val4 = max(tick-val3, 100); // Petrify time
			tick = val3;
			calc_flag = SCB_NONE; // Actual status changes take effect on petrified state.
			break;

		case SC_DPOISON:
			// Lose 10/15% of your life as long as it doesn't brings life below 25%
			if (status->hp > status->max_hp>>2) {
				int diff = status->max_hp*(bl->type==BL_PC?10:15)/100;
				if (status->hp - diff < status->max_hp>>2)
					diff = status->hp - (status->max_hp>>2);
				status_zap(bl, diff, 0);
			}
			// Fall through
		case SC_POISON:
		case SC_BLEEDING:
		case SC_BURNING:
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_TOXIN:
			if (val3 == 1) // Target
				tick_time = status_get_sc_interval(type);
			else // Caster
				tick_time = 1000;
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_DEATHHURT:
			if (val3 == 1)
				break;
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
		case SC_LEECHESEND:
			if (val3 == 0)
				break;
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_PYREXIA:
			if (val3 == 1) { // Target
				// Causes blind for duration of pyrexia, unreducable and unavoidable, but can be healed with e.g. green potion
				status_change_start(src, bl, SC_BLIND, 10000, val1, 0, 0, 0, tick, SCSTART_NOAVOID | SCSTART_NOTICKDEF | SCSTART_NORATEDEF);
				tick_time = status_get_sc_interval(type);
				val4 = tick - tick_time; // Remaining time
			} else // Caster
				val2 = 15; // CRIT % and ATK % increase
			break;
		case SC_VENOMBLEED:
			if (val3 == 0) // Caster
				val2 = 30; // Reflect damage % reduction
			break;
		case SC_MAGICMUSHROOM:
			if (val3 == 1) { // Target
				tick_time = status_get_sc_interval(type);
				val4 = tick - tick_time; // Remaining time
			} else // Caster
				val2 = 10; // After-cast delay % reduction
			break;

		case SC_CONFUSION:
			if (!val4)
				clif_emotion(bl,ET_QUESTION);
			break;
		case SC_S_LIFEPOTION:
		case SC_L_LIFEPOTION:
			if( val1 == 0 ) return 0;
			// val1 = heal percent/amout
			// val2 = seconds between heals
			// val4 = total of heals
			if( val2 < 1 ) val2 = 1;
			if( (val4 = tick/(val2 * 1000)) < 1 )
				val4 = 1;
			tick_time = val2 * 1000; // [GodLesZ] tick time
			break;
		case SC_BOSSMAPINFO:
			if( sd != NULL ) {
				struct mob_data *boss_md = map_getmob_boss(bl->m); // Search for Boss on this Map

				if( boss_md == NULL ) { // No MVP on this map
					clif_bossmapinfo(sd, NULL, BOSS_INFO_NOT);
					return 0;
				}
				val1 = boss_md->bl.id;
				tick_time = 1000; // [GodLesZ] tick time
				val4 = tick / tick_time;
			}
			break;
		case SC_HIDING:
			val2 = tick/1000;
			tick_time = 1000; // [GodLesZ] tick time
			val3 = 0; // Unused, previously speed adjustment
			val4 = val1+3; // Seconds before SP substraction happen.
			break;
		case SC_CHASEWALK:
			val2 = tick>0?tick:10000; // Interval at which SP is drained.
			val3 = 35 - 5 * val1; // Speed adjustment.
			if (sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE)
				val3 -= 40;
			val4 = 10+val1*2; // SP cost.
			if (map_flag_gvg2(bl->m) || map_getmapflag(bl->m, MF_BATTLEGROUND)) val4 *= 5;
			break;
		case SC_CLOAKING:
			if (!sd) // Monsters should be able to walk with no penalties. [Skotlex]
				val1 = 10;
			tick_time = val2 = tick>0?tick:60000; // SP consumption rate.
			tick = INFINITE_TICK; // Duration sent to the client should be infinite
			val3 = 0; // Unused, previously walk speed adjustment
			// val4&1 signals the presence of a wall.
			// val4&2 makes cloak not end on normal attacks [Skotlex]
			// val4&4 makes cloak not end on using skills
			if (bl->type == BL_PC || (bl->type == BL_MOB && ((TBL_MOB*)bl)->special_state.clone) )	// Standard cloaking.
				val4 |= battle_config.pc_cloak_check_type&7;
			else
				val4 |= battle_config.monster_cloak_check_type&7;
			break;
		case SC_SIGHT:			/* splash status */
		case SC_RUWACH:
		case SC_SIGHTBLASTER:
			val3 = skill_get_splash(val2, val1); // Val2 should bring the skill-id.
			val2 = tick/20;
			tick_time = 20; // [GodLesZ] tick time
			break;

		case SC_AUTOGUARD:
			if( !(flag&SCSTART_NOAVOID) ) {
				struct map_session_data *tsd;
				int i;
				for( i = val2 = 0; i < val1; i++) {
					int t = 5-(i>>1);
					val2 += (t < 0)? 1:t;
				}

				if( bl->type&(BL_PC|BL_MER) ) {
					if( sd ) {
						for( i = 0; i < MAX_DEVOTION; i++ ) {
							if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) )
								status_change_start(src,&tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCSTART_NOAVOID|SCSTART_NOICON);
						}
					}
					else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag && (tsd = ((TBL_MER*)bl)->master) )
						status_change_start(src,&tsd->bl, type, 10000, val1, val2, 0, 0, tick, SCSTART_NOAVOID|SCSTART_NOICON);
				}
			}
			break;

		case SC_DEFENDER:
			if (!(flag&SCSTART_NOAVOID)) {
				val2 = 5 + 15*val1; // Damage reduction
				val3 = 0; // Unused, previously speed adjustment
				val4 = 250 - 50*val1; // Aspd adjustment

				if (sd) {
					struct map_session_data *tsd;
					int i;
					for (i = 0; i < MAX_DEVOTION; i++) { // See if there are devoted characters, and pass the status to them. [Skotlex]
						if (sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])))
							status_change_start(src,&tsd->bl,type,10000,val1,val2,val3,val4,tick,SCSTART_NOAVOID);
					}
				}
			}
			break;

		case SC_TENSIONRELAX:
			if (sd) {
				pc_setsit(sd);
				skill_sit(sd, true);
				clif_sitting(&sd->bl);
			}
			val2 = 12; // SP cost
			tick_time = 10000; // Decrease