Jump to content
  • 2

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


funtwocrasher

Question


  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

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

19 answers to this question

Recommended Posts

  • 0

  • Group:  Members
  • Topic Count:  0
  • Topics Per Day:  0
  • Content Count:  90
  • Reputation:   34
  • Joined:  10/01/18
  • Last Seen:  

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

  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

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 at 10secs intervals.
			val3 = tick / tick_time;
			tick = INFINITE_TICK; // Duration sent to the client should be infinite
			break;
		case SC_PARRYING:
		    val2 = 20 + val1*3; // Block Chance
			break;

		case SC_WINDWALK:
			val2 = (val1+1)/2; // Flee bonus is 1/1/2/2/3/3/4/4/5/5
			break;

		case SC_JOINTBEAT:
			if( val2&BREAK_NECK )
				sc_start2(src,bl,SC_BLEEDING,100,val1,val3,skill_get_time2(scdb->skill_id,val1));
			break;

		case SC_BERSERK:
			if( val3 == SC__BLOODYLUST )
				sc_start(src,bl,(sc_type)val3,100,val1,tick);
			else
				sc_start4(src,bl, SC_ENDURE, 100,10,0,0,1, tick);
			// HP healing is performing after the calc_status call.
			// Val2 holds HP penalty
			if (!val4) val4 = skill_get_time2(scdb->skill_id,val1);
			if (!val4) val4 = 10000; // Val4 holds damage interval
			val3 = tick/val4; // val3 holds skill duration
			tick_time = val4; // [GodLesZ] tick time
			break;

		case SC_GOSPEL:
			if(val4 == BCT_SELF) {	// Self effect
				val2 = tick/10000;
				tick_time = 10000; // [GodLesZ] tick time
				status_change_clear_buffs(bl, SCCB_BUFFS|SCCB_DEBUFFS|SCCB_CHEM_PROTECT); // Remove buffs/debuffs
			}
			break;

		case SC_MARIONETTE:
		{
			int stat;

			val3 = 0;
			val4 = 0;
			stat = ( sd ? sd->status.str : status_get_base_status(bl)->str ) / 2; val3 |= cap_value(stat,0,0xFF)<<16;
			stat = ( sd ? sd->status.agi : status_get_base_status(bl)->agi ) / 2; val3 |= cap_value(stat,0,0xFF)<<8;
			stat = ( sd ? sd->status.vit : status_get_base_status(bl)->vit ) / 2; val3 |= cap_value(stat,0,0xFF);
			stat = ( sd ? sd->status.int_: status_get_base_status(bl)->int_) / 2; val4 |= cap_value(stat,0,0xFF)<<16;
			stat = ( sd ? sd->status.dex : status_get_base_status(bl)->dex ) / 2; val4 |= cap_value(stat,0,0xFF)<<8;
			stat = ( sd ? sd->status.luk : status_get_base_status(bl)->luk ) / 2; val4 |= cap_value(stat,0,0xFF);
			break;
		}
		case SC_MARIONETTE2:
		{
			int stat,max_stat;
			// Fetch caster information
			struct block_list *pbl = map_id2bl(val1);
			struct status_change *psc = pbl?status_get_sc(pbl):NULL;
			struct status_change_entry *psce = psc?psc->data[SC_MARIONETTE]:NULL;
			// Fetch target's stats
			struct status_data* status2 = status_get_status_data(bl); // Battle status

			if (!psce)
				return 0;

			val3 = 0;
			val4 = 0;
			max_stat = battle_config.max_parameter; // Cap to 99 (default)
			stat = (psce->val3 >>16)&0xFF; stat = min(stat, max_stat - status2->str ); val3 |= cap_value(stat,0,0xFF)<<16;
			stat = (psce->val3 >> 8)&0xFF; stat = min(stat, max_stat - status2->agi ); val3 |= cap_value(stat,0,0xFF)<<8;
			stat = (psce->val3 >> 0)&0xFF; stat = min(stat, max_stat - status2->vit ); val3 |= cap_value(stat,0,0xFF);
			stat = (psce->val4 >>16)&0xFF; stat = min(stat, max_stat - status2->int_); val4 |= cap_value(stat,0,0xFF)<<16;
			stat = (psce->val4 >> 8)&0xFF; stat = min(stat, max_stat - status2->dex ); val4 |= cap_value(stat,0,0xFF)<<8;
			stat = (psce->val4 >> 0)&0xFF; stat = min(stat, max_stat - status2->luk ); val4 |= cap_value(stat,0,0xFF);
			break;
		}
		case SC_SPIRIT:
			//1st Transcendent Spirit works similar to Marionette Control
			if(sd && val2 == SL_HIGH) {
				int stat,max_stat;
				struct status_data *status2 = status_get_base_status(bl);

				val3 = 0;
				val4 = 0;
				max_stat = (status_get_lv(bl)-10<50)?status_get_lv(bl)-10:50;
				stat = max(0, max_stat - status2->str); val3 |= cap_value(stat,0,0xFF)<<16;
				stat = max(0, max_stat - status2->agi ); val3 |= cap_value(stat,0,0xFF)<<8;
				stat = max(0, max_stat - status2->vit ); val3 |= cap_value(stat,0,0xFF);
				stat = max(0, max_stat - status2->int_); val4 |= cap_value(stat,0,0xFF)<<16;
				stat = max(0, max_stat - status2->dex ); val4 |= cap_value(stat,0,0xFF)<<8;
				stat = max(0, max_stat - status2->luk ); val4 |= cap_value(stat,0,0xFF);
			}
			break;

		case SC_REJECTSWORD:
			val2 = 15*val1; // Reflect chance
			val3 = 3; // Reflections
			tick = INFINITE_TICK;
			break;

		case SC_MEMORIZE:
			val2 = 5; // Memorized casts.
			tick = INFINITE_TICK;
			break;

#ifndef RENEWAL
		case SC_GRAVITATION:
			val2 = 50*val1; // aspd reduction
			break;
#endif

		case SC_REGENERATION:
			if (val1 == 1)
				val2 = 2;
			else
				val2 = val1; // HP Regerenation rate: 200% 200% 300%
			val3 = val1; // SP Regeneration Rate: 100% 200% 300%
			// If val4 comes set, this blocks regen rather than increase it.
			break;

		case SC_DEVOTION:
		{
			struct block_list *d_bl;
			struct status_change *d_sc;

			if( (d_bl = map_id2bl(val1)) && (d_sc = status_get_sc(d_bl)) && d_sc->count ) { // Inherits Status From Source
				const enum sc_type types[] = { SC_AUTOGUARD, SC_DEFENDER, SC_REFLECTSHIELD, SC_ENDURE };
				int i = (map_flag_gvg2(bl->m) || map_getmapflag(bl->m, MF_BATTLEGROUND))?2:3;
				while( i >= 0 ) {
					enum sc_type type2 = types[i];
					if( d_sc->data[type2] )
						status_change_start(d_bl, bl, type2, 10000, d_sc->data[type2]->val1, 0, 0, (type2 == SC_REFLECTSHIELD ? 1 : 0), skill_get_time(status_db.getSkill(type2),d_sc->data[type2]->val1), (type2 == SC_DEFENDER) ? SCSTART_NOAVOID : SCSTART_NOAVOID|SCSTART_NOICON);
					i--;
				}
			}
			break;
		}

		case SC_COMA: // Coma. Sends a char to 1HP. If val2, do not zap sp
			status_zap(bl, status->hp-1, val2?0:status->sp);
			return 1;
			break;
		case SC_CLOSECONFINE2:
		{
			struct block_list *src2 = val2?map_id2bl(val2):NULL;
			struct status_change *sc2 = src2?status_get_sc(src2):NULL;
			struct status_change_entry *sce2 = sc2?sc2->data[SC_CLOSECONFINE]:NULL;

			if (src2 && sc2) {
				if (!sce2) // Start lock on caster.
					sc_start4(src2,src2,SC_CLOSECONFINE,100,val1,1,0,0,tick+1000);
				else { // Increase count of locked enemies and refresh time.
					(sce2->val2)++;
					delete_timer(sce2->timer, status_change_timer);
					sce2->timer = add_timer(gettick()+tick+1000, status_change_timer, src2->id, SC_CLOSECONFINE);
				}
			} else // Status failed.
				return 0;
		}
			break;
		case SC_KAITE:
			val2 = 1+val1/5; // Number of bounces: 1 + skill_lv/5
			break;
		case SC_KAUPE:
			switch (val1) {
				case 3: // 33*3 + 1 -> 100%
					val2++;
				case 1:
				case 2: // 33, 66%
					val2 += 33*val1;
					val3 = 1; // Dodge 1 attack total.
					break;
				default: // Custom. For high level mob usage, higher level means more blocks. [Skotlex]
					val2 = 100;
					val3 = val1-2;
					break;
			}
			break;

		case SC_COMBO:
		{
			// val1: Skill ID
			// val2: When given, target (for autotargetting skills)
			// val3: When set, this combo time should NOT delay attack/movement
			// val3: If set to 2 this combo will delay ONLY attack
			// val3: TK: Last used kick
			// val4: TK: Combo time
			struct unit_data *ud = unit_bl2ud(bl);
			if ( ud && (!val3 || val3 == 2) ) {
				tick += 300 * battle_config.combo_delay_rate/100;
				ud->attackabletime = gettick()+tick;
				if( !val3 )
					unit_set_walkdelay(bl, gettick(), tick, 1);
			}
			val3 = 0;
			val4 = tick;
			break;
		}
		case SC_EARTHSCROLL:
			val2 = 11-val1; // Chance to consume: 11-skill_lv%
			break;
		case SC_RUN:
		{
			//Store time at which you started running.
			t_tick currenttick = gettick();
			// Note: this int64 value is stored in two separate int32 variables (FIXME)
			val3 = (int)(currenttick & 0x00000000ffffffffLL);
			val4 = (int)((currenttick & 0xffffffff00000000LL) >> 32);
			tick = INFINITE_TICK;
			break;
		}
		case SC_KAAHI:
			val2 = 200*val1; // HP heal
			val3 = 5*val1; // SP cost
			break;
		case SC_BLESSING:
			if ((!undead_flag && status->race!=RC_DEMON) || bl->type == BL_PC)
				val2 = val1;
			else
				val2 = 0; // 0 -> Half stat.
			break;
		case SC_TRICKDEAD:
			if (vd) vd->dead_sit = 1;
			tick = INFINITE_TICK;
			break;
		case SC_CONCENTRATE:
			val2 = 2 + val1;
			if (sd) { // Store the card-bonus data that should not count in the %
				val3 = sd->indexed_bonus.param_bonus[1]; // Agi
				val4 = sd->indexed_bonus.param_bonus[4]; // Dex
			} else
				val3 = val4 = 0;
			break;
		case SC_MAXOVERTHRUST:
			val2 = 20*val1; // Power increase
			break;
		case SC_OVERTHRUST:
		case SC_ADRENALINE2:
		case SC_ADRENALINE:
		case SC_WEAPONPERFECTION:
			{
				struct map_session_data * s_sd = BL_CAST(BL_PC, src);
				if (type == SC_OVERTHRUST) {
					// val2 holds if it was casted on self, or is bonus received from others
#ifdef RENEWAL
						val3 = (val2) ? 5 * val1 : (val1 > 4) ? 15 : (val1 > 2) ? 10 : 5; // Power increase
#else
						val3 = (val2) ? 5 * val1 : 5; // Power increase
#endif
				}
				else if (type == SC_ADRENALINE2 || type == SC_ADRENALINE) {
					val3 = (val2) ? 300 : 200; // Aspd increase
				}
				if (s_sd && pc_checkskill(s_sd, BS_HILTBINDING) > 0)
					tick += tick / 10; //If caster has Hilt Binding, duration increases by 10%
			}
			break;
		case SC_CONCENTRATION:
#ifdef RENEWAL
			val2 = 5 + val1 * 2; // Batk/Watk Increase
			val4 = 5 + val1 * 2; // Def reduction
#else
			val2 = 5*val1; // Batk/Watk Increase
			val4 = 5*val1; // Def reduction
#endif
			val3 = 10*val1; // Hit Increase
			sc_start(src, bl, SC_ENDURE, 100, 1, tick); // Level 1 Endure effect
			break;
		case SC_ANGELUS:
			val2 = 5*val1; // def increase
			break;
		case SC_IMPOSITIO:
			val2 = 5*val1; // WATK/MATK increase
			break;
		case SC_MELTDOWN:
			val2 = 100*val1; // Chance to break weapon
			val3 = 70*val1; // Change to break armor
			break;
		case SC_TRUESIGHT:
			val2 = 10*val1; // Critical increase
			val3 = 3*val1; // Hit increase
			break;
		case SC_SUN_COMFORT:
			val2 = (status_get_lv(bl) + status->dex + status->luk)/2; // def increase
			break;
		case SC_MOON_COMFORT:
			val2 = (status_get_lv(bl) + status->dex + status->luk)/10; // flee increase
			break;
		case SC_STAR_COMFORT:
			val2 = (status_get_lv(bl) + status->dex + status->luk); // Aspd increase
			break;
		case SC_QUAGMIRE:
			val2 = (sd?5:10)*val1; // Agi/Dex decrease.
			break;

		// gs_something1 [Vicious]
		case SC_GATLINGFEVER:
			val2 = 20*val1; // Aspd increase
			val3 = 20+10*val1; // Atk increase
			val4 = 5*val1; // Flee decrease
			break;

		case SC_FLING:
			if (bl->type == BL_PC)
				val2 = 0; // No armor reduction to players.
			else
				val2 = 5*val1; // Def reduction
			val3 = 5*val1; // Def2 reduction
			break;
		case SC_PROVOKE:
			val2 = 2+3*val1; // Atk increase
			val3 = 5+5*val1; // Def reduction.
			// val4 signals autoprovoke.
			break;
		case SC_AVOID:
			// val2 = 10*val1; // Speed change rate.
			break;
		case SC_DEFENCE:
#ifdef RENEWAL
			val2 = 5 + (val1 * 5); // Vit bonus
#else
			val2 = 2*val1; // Def bonus
#endif
			break;
		case SC_BLOODLUST:
			val2 = 20+10*val1; // Atk rate change.
			val3 = 3*val1; // Leech chance
			val4 = 20; // Leech percent
			break;
		case SC_FLEET:
			val2 = 30*val1; // Aspd change
			val3 = 5+5*val1; // bAtk/wAtk rate change
			break;
		case SC_MINDBREAKER:
			val2 = 20*val1; // matk increase.
			val3 = 12*val1; // mdef2 reduction.
			break;
		case SC_JAILED:
			// Val1 is duration in minutes. Use INT_MAX to specify 'unlimited' time.
			tick = val1>0?1000:250;
			if (sd) {
				if (sd->mapindex != val2) {
					int pos =  (bl->x&0xFFFF)|(bl->y<<16), // Current Coordinates
					map_idx =  sd->mapindex; // Current Map
					// 1. Place in Jail (val2 -> Jail Map, val3 -> x, val4 -> y
					pc_setpos(sd,(unsigned short)val2,val3,val4, CLR_TELEPORT);
					// 2. Set restore point (val3 -> return map, val4 return coords
					val3 = map_idx;
					val4 = pos;
				} else if (!val3 || val3 == sd->mapindex) { // Use save point.
					val3 = sd->status.save_point.map;
					val4 = (sd->status.save_point.x&0xFFFF)
						|(sd->status.save_point.y<<16);
				}
			}
			break;
		case SC_UTSUSEMI:
			val2=(val1+1)/2; // Number of hits blocked
			val3=skill_get_blewcount(NJ_UTSUSEMI, val1); // knockback value.
			break;
		case SC_BUNSINJYUTSU:
			val2=(val1+1)/2; // Number of hits blocked
			break;
		case SC_CHANGE:
			val2= 30*val1; // Vit increase
			val3= 20*val1; // Int increase
			break;
		case SC_SWOO:
			if(status_has_mode(status,MD_STATUSIMMUNE))
				tick /= 5; // !TODO: Reduce skill's duration. But for how long?
			break;
		case SC_ARMOR:
			// NPC_DEFENDER:
			val2 = 80; // Damage reduction
			// Attack requirements to be blocked:
			val3 = BF_LONG; // Range
			val4 = BF_WEAPON|BF_MISC; // Type
			break;
		case SC_ENCHANTARMS:
			// end previous enchants
			skill_enchant_elemental_end(bl,type);
			// Make sure the received element is valid.
			if (val1 >= ELE_ALL)
				val1 = val1%ELE_ALL;
			else if (val1 < 0)
				val1 = rnd()%ELE_ALL;
			break;
		case SC_CRITICALWOUND:
			val2 = 20*val1; // Heal effectiveness decrease
			break;
		case SC_MAGICMIRROR:
			// Level 1 ~ 5 & 6 ~ 10 has different duration
			// Level 6 ~ 10 use effect of level 1 ~ 5
			val1 = 1 + ((val1-1)%5);
		case SC_SLOWCAST:
			val2 = 20*val1; // Magic reflection/cast rate
			break;

		case SC_ARMORCHANGE:
			if (val2 == NPC_ANTIMAGIC) { // Boost mdef
				val2 =-20;
				val3 = 20;
			} else { // Boost def
				val2 = 20;
				val3 =-20;
			}
			// Level 1 ~ 5 & 6 ~ 10 has different duration
			// Level 6 ~ 10 use effect of level 1 ~ 5
			val1 = 1 + ((val1-1)%5);
			val2 *= val1; // 20% per level
			val3 *= val1;
			break;
		case SC_EXPBOOST:
		case SC_JEXPBOOST:
		case SC_JP_EVENT04:
			if (val1 < 0)
				val1 = 0;
			break;
		case SC_INCFLEE2:
		case SC_INCCRI:
			val2 = val1*10; // Actual boost (since 100% = 1000)
			break;
		case SC_SUFFRAGIUM:
#ifdef RENEWAL
			val2 = 5 + val1 * 5; // Speed cast decrease
#else
			val2 = 15 * val1; // Speed cast decrease
#endif
			break;
		case SC_INCHEALRATE:
			if (val1 < 1)
				val1 = 1;
			break;
		case SC_DOUBLECAST:
			val2 = 30+10*val1; // Trigger rate
			break;
		case SC_KAIZEL:
			val2 = 10*val1; // % of life to be revived with
			break;
		// case SC_ARMOR_ELEMENT_WATER:
		// case SC_ARMOR_ELEMENT_EARTH:
		// case SC_ARMOR_ELEMENT_FIRE:
		// case SC_ARMOR_ELEMENT_WIND:
		// case SC_ARMOR_RESIST:
			// Mod your resistance against elements:
			// val1 = water | val2 = earth | val3 = fire | val4 = wind
			// break;
		// case ????:
			// Place here SCs that have no SCB_* data, no skill associated, no ICON
			// associated, and yet are not wrong/unknown. [Skotlex]
			// break;

		case SC_MERC_FLEEUP:
		case SC_MERC_ATKUP:
		case SC_MERC_HITUP:
			val2 = 15 * val1;
			break;
		case SC_MERC_HPUP:
		case SC_MERC_SPUP:
			val2 = 5 * val1;
			break;
		case SC_REBIRTH:
			val2 = 20*val1; // % of life to be revived with
			break;

		case SC_MANU_DEF:
		case SC_MANU_ATK:
		case SC_MANU_MATK:
			val2 = 1; // Manuk group
			break;
		case SC_SPL_DEF:
		case SC_SPL_ATK:
		case SC_SPL_MATK:
			val2 = 2; // Splendide group
			break;

		/* General */
		case SC_FEAR:
			status_change_start(src,bl,SC_ANKLE,10000,val1,0,0,0,2000,SCSTART_NOAVOID|SCSTART_NOTICKDEF|SCSTART_NORATEDEF);
			break;

		/* Rune Knight */
		case SC_DEATHBOUND:
			val2 = 500 + 100 * val1;
			break;
		case SC_STONEHARDSKIN:
			if (!status_charge(bl, status->hp / 5, 0)) // 20% of HP
				return 0;
			if (sd)
				val1 = sd->status.job_level * pc_checkskill(sd, RK_RUNEMASTERY) / 4; // DEF/MDEF Increase
			break;
		case SC_REFRESH:
			status_heal(bl, status_get_max_hp(bl) * 25 / 100, 0, 1);
			status_change_clear_buffs(bl, SCCB_REFRESH);
			break;
		case SC_MILLENNIUMSHIELD:
			{
				int8 chance = rnd()%100;

				val2 = ((chance < 20) ? 4 : (chance < 50) ? 3 : 2); // Shield count
				val3 = 1000; // Shield HP
				clif_millenniumshield(bl, val2);
			}
 			break;
		case SC_ABUNDANCE:
			val4 = tick / 10000;
			tick_time = 10000; // [GodLesZ] tick time
			break;
		case SC_GIANTGROWTH:
			val2 = 30; // Damage success rate and STR increase
			break;
		case SC_LUXANIMA:
			val2 = 15; // Storm Blast success %
			val3 = 30; // Damage/HP/SP % increase
			break;

		/* Arch Bishop */
		case SC_RENOVATIO:
			val4 = tick / 5000;
			tick_time = 5000;
			break;
		case SC_SECRAMENT:
			val2 = 10 * val1;
			break;
		case SC_VENOMIMPRESS:
			val2 = 10 * val1;
			break;
		case SC_WEAPONBLOCKING:
			val2 = 10 + 2 * val1; // Chance
			val4 = tick / 5000;
			tick_time = 5000; // [GodLesZ] tick time
			break;
		case SC_OBLIVIONCURSE:
			if (val3 == 0)
				break;
			val4 = tick / 3000;
			tick_time = 3000; // [GodLesZ] tick time
			break;
		case SC_CLOAKINGEXCEED:
			val2 = (val1 + 1) / 2; // Hits
			val3 = (val1 - 1) * 10; // Walk speed
			if (bl->type == BL_PC)
				val4 |= battle_config.pc_cloak_check_type&7;
			else
				val4 |= battle_config.monster_cloak_check_type&7;
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_HALLUCINATIONWALK:
		case SC_NPC_HALLUCINATIONWALK:
			val2 = 50 * val1; // Evasion rate of physical attacks. Flee
			val3 = 10 * val1; // Evasion rate of magical attacks.
			break;
		case SC_MARSHOFABYSS:
			if( bl->type == BL_PC )
				val2 = 3 * val1; // AGI and DEX Reduction
			else // BL_MOB
				val2 = 6 * val1; // AGI and DEX Reduction
			val3 = 10 * val1; // Movement Speed Reduction
			break;
		case SC_FREEZE_SP:
			// val2 = sp drain per 10 seconds
			tick_time = 10000; // [GodLesZ] tick time
			break;
		case SC_SPHERE_1:
		case SC_SPHERE_2:
		case SC_SPHERE_3:
		case SC_SPHERE_4:
		case SC_SPHERE_5:
			if( !sd )
				return 0;	// Should only work on players.
			val4 = tick / 1000;
			if( val4 < 1 )
				val4 = 1;
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_SHAPESHIFT:
			switch( val1 ) {
				case 1: val2 = ELE_FIRE; break;
				case 2: val2 = ELE_EARTH; break;
				case 3: val2 = ELE_WIND; break;
				case 4: val2 = ELE_WATER; break;
			}
			break;
		case SC_ELECTRICSHOCKER:
		case SC_CRYSTALIZE:
			val4 = tick / 1000;
			if( val4 < 1 )
				val4 = 1;
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_MEIKYOUSISUI:
			val2 = val1 * 2; // % HP each sec
			val3 = val1; // % SP each sec
			val4 = tick / 1000;
			if( val4 < 1 )
				val4 = 1;
			tick_time = 1000;
			break;
		case SC_CAMOUFLAGE:
			val4 = tick/1000;
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_WUGDASH:
		{
			//Store time at which you started running.
			t_tick currenttick = gettick();
			// Note: this int64 value is stored in two separate int32 variables (FIXME)
			val3 = (int)(currenttick&0x00000000ffffffffLL);
			val4 = (int)((currenttick&0xffffffff00000000LL)>>32);
			tick = INFINITE_TICK;
			break;
		}
		case SC__SHADOWFORM:
			{
				struct map_session_data * s_sd = map_id2sd(val2);
				if( s_sd )
					s_sd->shadowform_id = bl->id;
				val4 = tick / 1000;
				tick_time = 1000; // [GodLesZ] tick time
			}
			break;
		case SC__STRIPACCESSORY:
			if (!sd)
				val2 = 20;
			break;
		case SC__INVISIBILITY:
			val2 = 50 - 10 * val1; // ASPD
			val3 = 20 * val1; // CRITICAL
			val4 = tick / 1000;
			tick = INFINITE_TICK; // Duration sent to the client should be infinite
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC__ENERVATION:
			val2 = 20 + 10 * val1; // ATK Reduction
			if (sd) {
				pc_delspiritball(sd,sd->spiritball,0);
				pc_delspiritcharm(sd,sd->spiritcharm,sd->spiritcharm_type);
			}
			break;
		case SC__GROOMY:
			val2 = 20 + 10 * val1; // ASPD
			val3 = 20 * val1; // HIT
			if( sd ) { // Removes Animals
				if( pc_isriding(sd) ) pc_setriding(sd, 0);
				if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
				if( pc_iswug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUG);
				if( pc_isridingwug(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_WUGRIDER);
				if( pc_isfalcon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_FALCON);
				if( sd->status.pet_id > 0 ) pet_return_egg(sd, sd->pd);
				if( hom_is_active(sd->hd) ) hom_vaporize(sd, HOM_ST_ACTIVE);
				//if( sd->md ) mercenary_delete(sd->md,3); // Are Mercenaries removed? [aleos]
			}
			break;
		case SC__LAZINESS:
			val2 = 10 + 10 * val1; // Cast Increase
			val3 = 10 * val1; // Flee Reduction
			break;
		case SC__UNLUCKY:
		{
			sc_type rand_eff; 
			switch(rnd() % 3) {
				case 1: rand_eff = SC_BLIND; break;
				case 2: rand_eff = SC_SILENCE; break;
				default: rand_eff = SC_POISON; break;
			}
			val2 = 10 * val1; // Crit and Flee2 Reduction
			status_change_start(src,bl,rand_eff,10000,val1,0,(rand_eff == SC_POISON ? src->id : 0),0,tick,SCSTART_NOTICKDEF|SCSTART_NORATEDEF);
			break;
		}
		case SC__WEAKNESS:
			val2 = 10 * val1;
			// Bypasses coating protection and MADO
			sc_start(src,bl,SC_STRIPWEAPON,100,val1,tick);
			sc_start(src,bl,SC_STRIPSHIELD,100,val1,tick);
			break;
		case SC_GN_CARTBOOST:
			if( val1 < 3 )
				val2 = 50;
			else if( val1 > 2 && val1 < 5 )
				val2 = 75;
			else
				val2 = 100;
			break;
		case SC_PROPERTYWALK:
			val3 = 0;
			break;
		case SC_STRIKING:
			// val2 = watk bonus already calc
			val3 = 6 - val1;// spcost = 6 - level (lvl1:5 ... lvl 5: 1)
			val4 = tick / 1000;
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_WARMER:
			val4 = tick / 3000;
			tick = INFINITE_TICK; // Duration sent to the client should be infinite
			tick_time = 3000;
			break;
		case SC_HELLS_PLANT:
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_SWINGDANCE:
			val3 = 3 * val1 + val2; // Walk speed and aspd reduction.
			break;
		case SC_SYMPHONYOFLOVER:
			val3 = 2 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // MDEF Increase
			break;
		case SC_MOONLITSERENADE: // MATK Increase
		case SC_RUSHWINDMILL: // ATK Increase
			val3 = 4 + val1 * 3 + val2 + (sd?sd->status.job_level:50) / 5;
			break;
		case SC_ECHOSONG:
			val3 = 6 * val1 + val2 + (sd?sd->status.job_level:50) / 4; // DEF Increase
			break;
		case SC_HARMONIZE:
			val2 = 5 + 5 * val1;
			break;
		case SC_VOICEOFSIREN:
			val4 = tick / 2000;
			tick_time = 2000; // [GodLesZ] tick time
			break;
		case SC_DEEPSLEEP:
			val4 = tick / 2000;
			tick_time = 2000; // [GodLesZ] tick time
			break;
		case SC_SIRCLEOFNATURE:
			val2 = 50 * val1; // HP recovery rate
			break;
		case SC_SONGOFMANA:
			status_heal(bl, 0, status->max_sp * (val1 <= 2 ? 10 : val1 <= 4 ? 15 : 20) / 100, 1);
			val3 = 50 * val1;
			break;
		case SC_SATURDAYNIGHTFEVER:
			if (!val4) val4 = skill_get_time2(scdb->skill_id,val1);
			if (!val4) val4 = 3000;
			val3 = tick/val4;
			tick_time = val4; // [GodLesZ] tick time
			break;
		case SC_GLOOMYDAY:
			val2 = 20 + 5 * val1; // Flee reduction.
			val3 = 15 + 5 * val1; // ASPD reduction.
			if( sd && rnd()%100 < val1 ) { // (Skill Lv) %
				val4 = 1; // Reduce walk speed by half.
				if( pc_isriding(sd) ) pc_setriding(sd, 0);
				if( pc_isridingdragon(sd) ) pc_setoption(sd, sd->sc.option&~OPTION_DRAGON);
			}
			break;
		case SC_GLOOMYDAY_SK:
			// Random number between [15 ~ (Voice Lesson Skill Level x 5) + (Skill Level x 10)] %.
			val2 = 15 + rnd()%( (sd?pc_checkskill(sd, WM_LESSON)*5:0) + val1*10 );
			break;
		case SC_SITDOWN_FORCE:
		case SC_BANANA_BOMB_SITDOWN:
			if( sd && !pc_issit(sd) ) {
				pc_setsit(sd);
				skill_sit(sd, true);
				clif_sitting(bl);
			}
			break;
		case SC_DANCEWITHWUG:
			val3 = 5 * val1; // ASPD Increase
			val4 = 20 + 10 * val1; // Fixed Cast Time Reduction
			break;
		case SC_LERADSDEW:
			val3 = 2 + 3 * val1 + min(3 * val2, 25); // MaxHP Increase
			break;
		case SC_MELODYOFSINK:
			val2 = 10 * val1; // INT Reduction.
			val3 = 2 + 2 * val1; // MaxSP reduction
			break;
		case SC_BEYONDOFWARCRY:
			val2 = 10 + 10 * val1; // STR Reduction
			val3 = 4 * val1; // MaxHP Reduction
			break;
		case SC_UNLIMITEDHUMMINGVOICE:
			val3 = 4 * val1 + min(3 * val2, 15); // !TODO: What's the Lesson bonus?
			break;
		case SC_REFLECTDAMAGE:
			val2 = 10 * val1; // Reflect reduction amount
			val3 = val1*5 + 25; // Number of reflects
			val4 = tick/1000; // Number of SP cycles (duration)
			tick_time = 1000; // [GodLesZ] tick time
			break;
		case SC_FORCEOFVANGUARD:
			val2 = 8 + 12 * val1; // Chance
			val3 = 5 + 2 * val1; // Max rage counters
			tick = INFINITE_TICK; // Endless duration in the client
			tick_time = 10000; // [GodLesZ] tick time
			break;
		case SC_EXEEDBREAK:
			val2 = 150 * val1;
			if (sd) { // Players
				short index = sd->equip_index[EQI_HAND_R];

				if (index >= 0 && sd->inventory_data[index] && sd->inventory_data[index]->type == IT_WEAPON)
					val2 += 15 * sd->status.job_level + sd->inventory_data[index]->weight / 10 * sd->inventory_data[index]->weapon_level * status_get_lv(bl) / 100;
			} else // Monster
				val2 += 750;
			break;
		case SC_PRESTIGE:
			val2 = (status->int_ + status->luk) * val1 / 20 * status_get_lv(bl) / 200 + val1;	// Chance to evade magic damage.
			val3 = ((val1 * 15) + (10 * (sd?pc_checkskill(sd,CR_DEFENDER):skill_get_max(CR_DEFENDER)))) * status_get_lv(bl) / 100; // Defence added
			break;
		case SC_SHIELDSPELL_HP:
			val2 = 3; // 3% HP every 3 seconds
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_SHIELDSPELL_SP:
			val2 = 3; // 3% SP every 5 seconds
			tick_time = status_get_sc_interval(type);
			val4 = tick - tick_time; // Remaining time
			break;
		case SC_SHIELDSPELL_ATK:
			val2 = 150; // WATK/MATK bonus
			break;
		case SC_BANDING:
			val2 = (sd ? skill_banding_count(sd) : 1);
			tick_time = 5000; // [GodLesZ] tick time
			break;
		case SC_MAGNETICFIELD:
			tick_time = 1000; // [GodLesZ] tick time
			val4 = tick / tick_time;
			break;
		case SC_INSPIRATION:
			val2 = 40 * val1; // ATK/MATK
			val3 = 6 * val1; //All stat bonus
			val4 = tick / 5000;
			tick_time = 5000; // [GodLesZ] tick time
			status_change_clear_buffs(bl, SCCB_DEBUFFS); // Remove debuffs
			break;
		case SC_CRESCENTELBOW:
			val2 = (sd?sd->status.job_level:50) / 2 + (50 + 5 * val1);
			break;
		case SC_LIGHTNINGWALK: // [(Job Level / 2) + (40 + 5 * Skill Level)] %
			val1 = (sd?sd->status.job_level:2)/2 + 40 + 5 * val1;
			break;
		case SC_GT_ENERGYGAIN:
			val2 = 10 + 5 * val1; // Sphere gain chance.
			break;
		case SC_GT_CHANGE:
			// Take note there is no def increase as skill desc says. [malufett]
			val2 = val1 * 8; // ATK increase
			val3 = status->agi * val1 / 60; // ASPD increase: [(Target AGI x Skill Level) / 60] %
			break;
		case SC_GT_REVITALIZE:
			// Take note there is no vit, aspd, speed increase as skill desc says. [malufett]
			val2 = 2 * val1; // MaxHP: [(Skill Level * 2)]%
			val3 = val1 * 30 + 50; // Natural HP recovery increase: [(Skill Level x 30) + 50] %
			// The stat def is not shown in the status window and it is processed differently
			val4 = val1 * 20; // STAT DEF increase
			break;
		case SC_PYROTECHNIC_OPTION:
			val2 = 60; // Eatk Renewal (Atk2)
			break;
		case SC_HEATER_OPTION:
			val2 = 120; // Eatk Renewal (Atk2)
			val3 = ELE_FIRE; // Change into fire element.
			break;
		case SC_TROPIC_OPTION:
			val2 = 180; // Eatk Renewal (Atk2)
			val3 = MG_FIREBOLT;
			break;
		case SC_AQUAPLAY_OPTION:
			val2 = 40;
			break;
		case SC_COOLER_OPTION:
			val2 = 80;
			val3 = ELE_WATER; // Change into water element.
			break;
		case SC_CHILLY_AIR_OPTION:
			val2 = 120; // Matk. Renewal (Matk1)
			val3 = MG_COLDBOLT;
			break;
		case SC_WIND_STEP_OPTION:
			val2 = 50; // % Increase speed and flee.
			break;
		case SC_BLAST_OPTION:
			val2 = 20;
			val3 = ELE_WIND; // Change into wind element.
			break;
		case SC_WILD_STORM_OPTION:
			val2 = MG_LIGHTNINGBOLT;
			break;
		case SC_PETROLOGY_OPTION:
			val2 = 5; //HP Rate bonus
			val3 = 50;
			break;
		case SC_SOLID_SKIN_OPTION:
			val2 = 33; //% Increase DEF
			break;
		case SC_CURSED_SOIL_OPTION:
			val2 = 10; //HP rate bonus
			val3 = ELE_EARTH; // Change into earth element.
			break;
		case SC_UPHEAVAL_OPTION:
			val2 = 15; //HP rate bonus
			val3 = WZ_EARTHSPIKE;
			break;
		case SC_CIRCLE_OF_FIRE_OPTION:
			val2 = 300;
			break;
		case SC_WATER_SCREEN_OPTION:
			tick_time = 10000;
			break;
		case SC_FIRE_CLOAK_OPTION:
		case SC_WATER_DROP_OPTION:
		case SC_WIND_CURTAIN_OPTION:
		case SC_STONE_SHIELD_OPTION:
			val2 = 100; // Elemental modifier.
			break;
		case SC_TROPIC:
		case SC_CHILLY_AIR:
		case SC_WILD_STORM:
		case SC_UPHEAVAL:
			val2 += 10;
		case SC_HEATER:
		case SC_COOLER:
		case SC_BLAST:
		case SC_CURSED_SOIL:
			val2 += 10;
		case SC_PYROTECHNIC:
		case SC_AQUAPLAY:
		case SC_GUST:
		case SC_PETROLOGY:
			val2 += 5;
			val3 += 9000;
		case SC_CIRCLE_OF_FIRE:
		case SC_FIRE_CLOAK:
		case SC_WATER_DROP:
		case SC_WATER_SCREEN:
		case SC_WIND_CURTAIN:
		case SC_WIND_STEP:
		case SC_STONE_SHIELD:
		case SC_SOLID_SKIN:
			val2 += 5;
			val3 += 1000;
			tick_time = val3; // [GodLesZ] tick time
			break;
		case SC_WATER_BARRIER:
			val2 = 30; // Reductions. Atk2 and Flee1
			break;
		case SC_ZEPHYR:
			val2 = 25; // Flee.
			break;
		case SC_TIDAL_WEAPON:
			val2 = 20; // Increase Elemental's attack.
			break;
		case SC_ROCK_CRUSHER:
		case SC_ROCK_CRUSHER_ATK:
		case SC_POWER_OF_GAIA:
			val2 = 33; //Def rate bonus/Speed rate reduction
			val3 = 20; //HP rate bonus
			break;
		case SC_TEARGAS:
			val2 = status_get_max_hp(bl) * 5 / 100; // Drain 5% HP
			val4 = tick / 2000;
			tick_time = 2000;
			break;
		case SC_TEARGAS_SOB:
			val4 = tick / 3000;
			tick_time = 3000;
			break;
		case SC_STOMACHACHE:
			val2 = 8; // SP consume.
			val4 = tick / 10000;
			tick_time = 10000; // [GodLesZ] tick time
			break;
		case SC_PROMOTE_HEALTH_RESERCH:
			//val1: 1 = Regular Potion, 2 = Thrown Potion
			//val2: 1 = Small Potion, 2 = Medium Potion, 3 = Large Potion
			//val3: MaxHP Increase By Fixed Amount
			if (val1 == 1) // If potion was normally used, take the user's BaseLv
				val3 = 1000 * val2 - 500 + status_get_lv(bl) * 10 / 3;
			else if (val1 == 2) // If potion was thrown at someone, take the thrower's BaseLv
				val3 = 1000 * val2 - 500 + status_get_lv(src) * 10 / 3;
			if (val3 <= 0) // Prevents a negeative value from happening
				val3 = 0;
			break;
		case SC_ENERGY_DRINK_RESERCH:
			//val1: 1 = Regular Potion, 2 = Thrown Potion
			//val2: 1 = Small Potion, 2 = Medium Potion, 3 = Large Potion
			//val3: MaxSP Increase By Percentage Amount
			if (val1 == 1) // If potion was normally used, take the user's BaseLv
				val3 = status_get_lv(bl) / 10 + 5 * val2 - 10;
			else if (val1 == 2) // If potion was thrown at someone, take the thrower's BaseLv
				val3 = status_get_lv(src) / 10 + 5 * val2 - 10;
			if (val3 <= 0) // Prevents a negeative value from happening
				val3 = 0;
			break;
		case SC_KYOUGAKU:
			val2 = 2*val1 + rnd()%val1;
			clif_status_change(bl,EFST_ACTIVE_MONSTER_TRANSFORM,1,0,1002,0,0);
			break;
		case SC_KAGEMUSYA:
			val2 = 20; // Damage increase bonus
			val3 = val1 * 2;
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_ZANGETSU:
			if( status_get_hp(bl) % 2 == 0 )
				val2 = (status_get_lv(bl) / 3) + (20 * val1); //+Watk
			else
				val2 -= (status_get_lv(bl) / 3) + (30 * val1); //-Watk

			if( status_get_sp(bl) % 2 == 0 )
				val3 = (status_get_lv(bl) / 3) + (20 * val1); //+Matk
			else
				val3 -= (status_get_lv(bl) / 3) + (30 * val1); //-Matk
			break;
		case SC_GENSOU:
			{
				int hp = status_get_hp(bl), lv = 5;
				short per = 100 / (status_get_max_hp(bl) / hp);

				if( per <= 15 )
					lv = 1;
				else if( per <= 30 )
					lv = 2;
				else if( per <= 50 )
					lv = 3;
				else if( per <= 75 )
					lv = 4;
				if( hp % 2 == 0)
					status_heal(bl, hp * (6-lv) * 4 / 100, status_get_sp(bl) * (6-lv) * 3 / 100, 1);
				else
					status_zap(bl, hp * (lv*4) / 100, status_get_sp(bl) * (lv*3) / 100);
			}
			break;
		case SC_ANGRIFFS_MODUS:
			val2 = 50 + 20 * val1; // atk bonus
			val3 = 25 + 10 * val1; // Flee reduction.
			val4 = tick/1000; // hp/sp reduction timer
			tick_time = 1000;
			break;
		case SC_GOLDENE_FERSE:
			val2 = 10 + 10*val1; // flee bonus
			val3 = 6 + 4 * val1; // Aspd Bonus
			val4 = 2 + 2 * val1; // Chance of holy attack
			break;
		case SC_STONE_WALL:
			val2 = 100 * val1; // DEF bonus
			val3 = 30 * val1; // MDEF bonus
			break;
		case SC_OVERED_BOOST:
			val2 = 400 + 40 * val1; // flee bonus
			val3 = 180 + 2 * val1; // aspd bonus
			val4 = 50; // def reduc %
			break;
		case SC_GRANITIC_ARMOR:
			val2 = 2*val1; // dmg reduction
			val3 = 6*val1; // dmg taken on status end (6%:12%:18%:24%:30%)
			val4 = 5*val1; // unknow formula
			break;
		case SC_MAGMA_FLOW:
			val2 = 3*val1; // Activation chance
			break;
		case SC_PYROCLASTIC:
			val2 += 100 + 10*val1; // atk bonus // !TODO: Confirm formula
			break;
		case SC_PARALYSIS: // [Lighta] need real info
			val2 = 2*val1; // def reduction
			val3 = 500*val1; // varcast augmentation
			break;
		case SC_LIGHT_OF_REGENE: // Yommy leak need confirm
			val2 = 20 * val1; // hp reco on death %
			break;
		case SC_PAIN_KILLER: // Yommy leak need confirm
			val2 = min((( 200 * val1 ) * status_get_lv(src)) / 150, 1000); // dmg reduction linear. upto a maximum of 1000 [iRO Wiki]
			if(sc->data[SC_PARALYSIS])
				sc_start(src,bl, SC_ENDURE, 100, val1, tick); // Start endure for same duration
			break;
		case SC_STYLE_CHANGE:
			tick = INFINITE_TICK; // Infinite duration
			break;
		case SC_CBC:
			val3 = 10; // Drain sp % dmg
			val4 = tick/1000; // dmg each sec
			tick = 1000;
			break;
		case SC_EQC:
			val2 = 5 * val1; // def % reduc
			val3 = 2 * val1; // HP drain %
			break;
		case SC_ASH:
			val2 = 0; // hit % reduc
			val3 = 0; // def % reduc
			val4 = 0; // atk flee & reduc
			if (!status_bl_has_mode(bl,MD_STATUSIMMUNE)) {
				val2 = 50;
				if (status_get_race(bl) == RC_PLANT) // plant type
					val3 = 50;
				if (status_get_element(bl) == ELE_WATER) // defense water type
					val4 = 50;
			}
			break;
		case SC_FULL_THROTTLE:
			val2 = ( val1 == 1 ? 6 : 6 - val1 );
			val3 = 20; //+% AllStats
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_REBOUND:
			tick_time = 2000;
			val4 = tick / tick_time;
			clif_emotion(bl, ET_SWEAT);
			break;
		case SC_KINGS_GRACE:
			val2 = 3 + val1; //HP Recover rate
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_TELEKINESIS_INTENSE:
			val2 = 10 * val1; // sp consum / casttime reduc %
			val3 = 40 * val1; // magic dmg bonus
			break;
		case SC_OFFERTORIUM:
			val2 = 30 * val1; // heal power bonus
			val3 = 100 + 20 * val1; // sp cost inc
			break;
		case SC_FRIGG_SONG:
			val2 = 5 * val1; // maxhp bonus
			val3 = 80 + 20 * val1; // healing
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_FLASHCOMBO:
			val2 = 20 * val1 + 20; // atk bonus
			break;
		case SC_DARKCROW:
			val2 = 30 * val1; // ATK bonus
			break;
		case SC_UNLIMIT:
			val2 = 50 * val1;
			break;
		case SC_MONSTER_TRANSFORM:
		case SC_ACTIVE_MONSTER_TRANSFORM:
			if( !mobdb_checkid(val1) )
				val1 = MOBID_PORING; // Default poring
			break;
#ifndef RENEWAL
		case SC_APPLEIDUN:
		{
			struct map_session_data * s_sd = BL_CAST(BL_PC, src);

			val2 = (5 + 2 * val1) + (status_get_vit(src) / 10); //HP Rate: (5 + 2 * skill_lv) + (vit/10) + (BA_MUSICALLESSON level)
			if (s_sd)
				val2 += pc_checkskill(s_sd, BA_MUSICALLESSON) / 2;
			break;
		}
#endif
		case SC_EPICLESIS:
			val2 = 5 * val1; //HP rate bonus
			break;
		case SC_ILLUSIONDOPING:
			val2 = 50; // -Hit
			break;

		case SC_OVERHEAT:
		case SC_OVERHEAT_LIMITPOINT:
		case SC_STEALTHFIELD:
			tick_time = tick;
			tick = INFINITE_TICK;
			break;
		case SC_STEALTHFIELD_MASTER:
			tick_time = val3 = 2000 + 1000 * val1;
			val4 = tick / tick_time;
			break;
		case SC_VACUUM_EXTREME:
			// Suck target at n second, only if the n second is lower than the duration
			// Does not suck targets on no-knockback maps
			if (val4 < tick && unit_blown_immune(bl, 0x9) == UB_KNOCKABLE) {
				tick_time = val4;
				val4 = tick - tick_time;
			} else
				val4 = 0;
			break;
		case SC_FIRE_INSIGNIA:
		case SC_WATER_INSIGNIA:
		case SC_WIND_INSIGNIA:
		case SC_EARTH_INSIGNIA:
			tick_time = 5000;
			val4 = tick / tick_time;
			break;
		case SC_NEUTRALBARRIER:
			val2 = 10 + val1 * 5; // Def/Mdef
			tick = INFINITE_TICK;
			break;
		case SC_MAGIC_POISON:
			val2 = 50; // Attribute Reduction
			break;

		/* Rebellion */
		case SC_B_TRAP:
			val2 = src->id;
			val3 = val1 * 25; // -movespeed TODO: Figure out movespeed rate
			break;
		case SC_C_MARKER:
		case SC_BURNT:
			// val1 = skill_lv
			// val2 = src_id
			val3 = 10; // -10 flee
			//Start timer to send mark on mini map
			val4 = tick/1000;
			tick_time = 1000; // Sends every 1 seconds
			break;
		case SC_H_MINE:
			val2 = src->id;
			break;
		case SC_HEAT_BARREL:
			{
				uint8 n = 10;
				if (sd)
					n = (uint8)sd->spiritball_old;

				//kRO Update 2016-05-25
				val2 = n * 5; // -fixed casttime
				val3 = (6 + val1 * 2) * n; // ATK
				val4 = 25 + val1 * 5; // -hit
			}
			break;
		case SC_P_ALTER:
			{
				uint8 n = 10;
				if (sd)
					n = (uint8)sd->spiritball_old;
				val2 = 10 * n; // +atk
				val3 = (status->max_hp * (val1 * 5) / 100); // Barrier HP
			}
			break;
		case SC_E_CHAIN:
			val2 = 10;
			if (sd)
				val2 = sd->spiritball_old;
			break;
		case SC_ANTI_M_BLAST:
			val2 = val1 * 10;
			break;
		case SC_CATNIPPOWDER:
			val2 = 50; // WATK%, MATK%
			val3 = 25 * val1; // Move speed reduction
			if (bl->type == BL_PC && pc_checkskill(sd, SU_SPIRITOFLAND))
				val4 = status_get_lv(src) / 12;
			break;
		case SC_BITESCAR: {
				const struct status_data *b_status = status_get_base_status(src); // Base Status

				val2 = (status_get_max_hp(bl) * (val1 + (b_status->dex / 25))) / status_get_max_hp(bl); // MHP% damage
				tick_time = 1000;
				val4 = tick / tick_time;
			}
			break;
		case SC_ARCLOUSEDASH:
			val2 = 15 + 5 * val1; // AGI
			val3 = 25; // Move speed increase
			if (sd && (sd->class_&MAPID_BASEMASK) == MAPID_SUMMONER)
				val4 = 10; // Ranged ATK increase
			break;
		case SC_SHRIMP:
			val2 = 10; // BATK%, MATK%
			break;
		case SC_FRESHSHRIMP: {
				int min = 0, max = 0;

#ifdef RENEWAL
				min = status_base_matk_min(src, status, status_get_lv(src));
				max = status_base_matk_max(src, status, status_get_lv(src));
				if (status->rhw.matk > 0) {
					int wMatk, variance;

					wMatk = status->rhw.matk;
					variance = wMatk * status->rhw.wlv / 10;
					min += wMatk - variance;
					max += wMatk + variance;
				}
#endif

				if (sd && sd->right_weapon.overrefine > 0) {
					min++;
					max += sd->right_weapon.overrefine - 1;
				}

				val2 += min + 178; // Heal
				if (max > min)
					val2 += rnd() % (max - min); // Heal

				if (sd) {
					if (pc_checkskill(sd, SU_POWEROFSEA) > 0) {
						val2 += val2 * 10 / 100;
						if (pc_checkskill_summoner(sd, SUMMONER_POWER_SEA) >= 20)
							val2 += val2 * 20 / 100;
					}
					if (pc_checkskill(sd, SU_SPIRITOFSEA) > 0)
						val2 *= 2; // Doubles HP
				}
				tick_time = 10000 - ((val1 - 1) * 1000);
				val4 = tick / tick_time;
			}
			break;
		case SC_TUNAPARTY:
			val2 = (status->max_hp * (val1 * 10) / 100); // Max HP% to absorb
			if (sd && pc_checkskill(sd, SU_SPIRITOFSEA))
				val2 <<= 1; // Double the shield life
			break;
		case SC_HISS:
			val2 = 50; // Perfect Dodge
			sc_start(src, bl, SC_DORAM_WALKSPEED, 100, 50, skill_get_time2(SU_HISS, val1));
			break;
		case SC_GROOMING:
			val2 = 100; // Flee
			break;
		case SC_CHATTERING:
			val2 = 100; // eATK, eMATK
			sc_start(src, bl, SC_DORAM_WALKSPEED, 100, 50, skill_get_time2(SU_CHATTERING, val1));
			break;
		case SC_SWORDCLAN:
		case SC_ARCWANDCLAN:
		case SC_GOLDENMACECLAN:
		case SC_CROSSBOWCLAN:
		case SC_JUMPINGCLAN:
			tick = INFINITE_TICK;
			status_change_start(src,bl,SC_CLAN_INFO,10000,0,val2,0,0,INFINITE_TICK,flag);
			break;
		case SC_DORAM_BUF_01:
		case SC_DORAM_BUF_02:
			tick_time = 10000; // every 10 seconds
			if( (val4 = tick/tick_time) < 1 )
				val4 = 1;
			break;

		case SC_GLASTHEIM_ATK:
			val1 = 100; // Undead/Demon MDEF ignore rate
			break;
		case SC_GLASTHEIM_HEAL:
			val1 = 100; // Heal Power rate bonus
			val2 = 50; // Received heal rate bonus
			break;
		case SC_GLASTHEIM_HIDDEN:
			val1 = 90; // Damage rate reduction bonus
			break;
		case SC_GLASTHEIM_STATE:
			val1 = 20; // All-stat bonus
			break;
		case SC_GLASTHEIM_ITEMDEF:
			val1 = 200; // DEF bonus
			val2 = 50; // MDEF bonus
			break;
		case SC_GLASTHEIM_HPSP:
			val1 = 10000; // HP bonus
			val2 = 1000; // SP bonus
			break;
		case SC_ANCILLA:
			val1 = 15; // Heal Power rate bonus
			val2 = 30; // SP Recovery rate bonus
			break;
		case SC_HELPANGEL:
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_EMERGENCY_MOVE:
			val2 = 25; // Movement speed increase
			break;

		case SC_SUNSTANCE:
			val2 = 2 + val1; // ATK Increase
			tick = INFINITE_TICK;
			break;
		case SC_LUNARSTANCE:
			val2 = 2 + val1; // MaxHP Increase
			tick = INFINITE_TICK;
			break;
		case SC_STARSTANCE:
			val2 = 4 + 2 * val1; // ASPD Increase
			tick = INFINITE_TICK;
			break;
		case SC_DIMENSION1:
		case SC_DIMENSION2:
			if (sd)
				pc_addspiritball(sd, skill_get_time2(SJ_BOOKOFDIMENSION, 1), 2);
			break;
		case SC_UNIVERSESTANCE:
			val2 = 2 + val1; // All Stats Increase
			tick = INFINITE_TICK;
			break;
		case SC_NEWMOON:
			val2 = 7; // Number of Regular Attacks Until Reveal
			tick_time = 1000;
			val4 = tick / tick_time;
			break;
		case SC_FALLINGSTAR:
			val2 = 8 + 2 * (1 + val1) / 2; // Autocast Chance
			if (val1 >= 7)
				val2 += 1; // Make it 15% at level 7.
			break;
		case SC_CREATINGSTAR:
			tick_time = 500;
			val4 = tick / tick_time;
			break;
		case SC_LIGHTOFSUN:
		case SC_LIGHTOFMOON:
		case SC_LIGHTOFSTAR:
			val2 = 5 * val1; // Skill Damage Increase.
			break;
		case SC_SOULGOLEM:
			val2 = 60 * val1; // DEF Increase
			val3 = 15 + 5 * val1; // MDEF Increase
			break;
		case SC_SOULSHADOW:
			val2 = (1 + val1) / 2; // ASPD Increase
			val3 = 10 + 2 * val1; // CRIT Increase
			break;
		case SC_SOULFALCON:
			val2 = 10 * val1; // WATK Increase
			val3 = 10; // HIT Increase
			if (val1 >= 3)
				val3 += 3;
			else if (val1 >= 5)
				val3 += 5;
			break;
		case SC_SOULFAIRY:
			val2 = 10 * val1; // MATK Increase
			val3 = 5; // Variable Cast Time Reduction
			if (val1 >= 3)
				val3 += 2;
			else if (val1 >= 5)
				val3 += 5;
			break;
		case SC_SOULUNITY:
			tick_time = 3000;
			val4 = tick / tick_time;
			break;
		case SC_SOULDIVISION:
			val2 = 10 * val1; // Skill Aftercast Increase
			break;
		case SC_SOULREAPER:
			val2 = 10 + 5 * val1; // Chance of Getting A Soul Sphere.
			break;
		case SC_SOULCOLLECT:
			val2 = 5 + 3 * val2; // Max Soul Sphere's.
			val3 = tick > 0 ? tick : 60000;
			tick_time = tick;
			tick = INFINITE_TICK;
			break;
		case SC_SP_SHA:
			val2 = 50; // Move speed reduction
			break;
		case SC_SERVANTWEAPON:
			if( sd ){
				// Generate 5 servants on start
				pc_addservantball( *sd, MAX_SERVANTBALL );
			}
			tick_time = skill_get_time2(DK_SERVANTWEAPON,val1); // Servant Regen Interval
			if (tick_time < 500)
				tick_time = 500; // Avoid being brought down to 0.
			val4 = tick - tick_time; // Remaining Time
			break;
		case SC_VIGOR: {
				uint8 hp_loss[10] = { 15, 14, 12, 11, 9, 8, 6, 5, 3, 2 };

				val2 = hp_loss[val1- 1];
			}
			break;
		case SC_POWERFUL_FAITH:
			val2 = 5 + 5 * val1;// ATK Increase
			val3 = 5 + 2 * val1;// PAtk Increase
			break;
		case SC_FIRM_FAITH:
			val2 = 2 * val1;// MaxHP Increase
			val3 = 8 * val1;// Res Increase
			break;
		case SC_SINCERE_FAITH:
			val2 = (1 + val1) / 2;// ASPD Increase
			val3 = 4 * val1;// Perfect Hit Increase
			break;
		case SC_GUARD_STANCE:
			val2 = 50 + 50 * val1;// DEF Increase
			val3 = 50 * val1;// ATK Decrease
			tick = INFINITE_TICK;
			break;
		case SC_GUARDIAN_S:
			val2 = status->max_hp * (50 * val1) / 100;// Barrier HP
			break;
		case SC_REBOUND_S:
			val2 = 10 * val1;// Reduced Damage From Devotion
			if (val2 > 99)
				val2 = 99;// Lets not let it reduce above 99.
			break;
		case SC_ATTACK_STANCE:
			val2 = 40 * val1;// DEF Decrease
			val3 = 5 + 5 * val1;// ATK Increase
			tick = INFINITE_TICK;
			break;
		case SC_HOLY_S:
			val2 = 5 + 2 * val1;// Damage Reduction / Holy Damage Increase
			break;
		case SC_MEDIALE:
			val2 = 2 * val1;// Heal Rate
			val4 = tick / 2000;
			tick_time = 2000;
			break;
		case SC_A_VITA:
		case SC_A_TELUM:
			val2 = 5 * val1;// Res/MRes Pierce Percentage
			break;
		case SC_PRE_ACIES:
			val2 = 2 * val1;// CRate Increase
			break;
		case SC_COMPETENTIA:
			val2 = 10 * val1;// PAtk/SMatk Increase - Unconfirmed if this is official formula but its 50 at Lv 5. [Rytech]
			break;
		case SC_RELIGIO:
		case SC_BENEDICTUM:
			val2 = 2 * val1;// Trait Stats Increase
			break;
		case SC_DANCING_KNIFE:
			val4 = tick / 300;
			tick_time = 300;
			break;
		case SC_POTENT_VENOM:
			val2 = 3 * val1;// Res Pierce Percentage
			break;
		case SC_SHADOW_WEAPON:
			val2 = val1;// Success Chance of Shadow Scar
			break;
		case SC_A_MACHINE:
			val4 = tick / 1000;
			tick_time = 1000;
			break;
		case SC_D_MACHINE:
			val2 = 200 + 50 * val1;// DEF Increase
			val3 = 20 * val1;// Res Increase
			break;
		case SC_SHADOW_STRIP:
			if (!sd)// Res/MRes on mobs only.
				val2 = 25;// Need official reduction amount.
			break;
		case SC_ABYSSFORCEWEAPON:
			if( sd ){
				// Generate 5 abyss spheres on start.
				pc_addabyssball( *sd, MAX_ABYSSBALL );
			}
			tick_time = skill_get_time2(ABC_FROM_THE_ABYSS, val1);// Abyss Regen Interval
			if (tick_time < 500)
				tick_time = 500;// Avoid being brought down to 0.
			val4 = tick - tick_time;// Remaining Time
			break;
		case SC_ABYSS_SLAYER:
			val2 = 10 + 2 * val1;// PAtk/SMatk Increase
			val3 = 100 + 20 * val1;// Hit Increase
			break;
		case SC_WINDSIGN:
			val2 = 8 + 6 * val1;// Chance to gain AP on attack.
			if (val1 == 5)// Its 40% on level 5.
				val2 += 2;
			break;
		case SC_CALAMITYGALE:// Unlimit runs along with this.
			sc_start(bl, bl, SC_UNLIMIT, 100, 5, skill_get_time(RA_UNLIMIT, 5));
			break;
		case SC_GEF_NOCTURN:// MRes Reduction. Official formula unknown.
		case SC_AIN_RHAPSODY:// Res Reduction. Official formula unknown.
			val2 = 10 * val1;// Res/MRes Decrease
			if (val3&2)// Bonus if partner is found in party.
				val2 *= 2;
			break;
		case SC_MUSICAL_INTERLUDE:
			val2 = 5 + 5 * val1;// Res Increase
			if (val3&2)// Bonus if partner is found in party.
				val2 *= 2;
			break;
		case SC_JAWAII_SERENADE:
			val2 = 3 * val1;// SMatk Increase
			if (val3 & 2)// Bonus if partner is found in party.
				val2 *= 2;
			break;
		case SC_PRON_MARCH:
			val2 = 3 * val1;// PAtk Increase
			if (val3 & 2)// Bonus if partner is found in party.
				val2 *= 2;
			break;
		case SC_SPELL_ENCHANTING:
			val2 = 4 * val1;// SMatk Increase
			break;
		case SC_FLAMETECHNIC:
		case SC_FLAMEARMOR:
		case SC_COLD_FORCE:
		case SC_CRYSTAL_ARMOR:
		case SC_GRACE_BREEZE:
		case SC_EYES_OF_STORM:
		case SC_EARTH_CARE:
		case SC_STRONG_PROTECTION:
		case SC_DEEP_POISONING:
		case SC_POISON_SHIELD:
			val2 += 10;
			val3 += 10000;
			tick_time = val3;
			break;
		case SC_FLAMETECHNIC_OPTION:
			val3 = ELE_FIRE;
			break;
		case SC_COLD_FORCE_OPTION:
			val3 = ELE_WATER;
			break;
		case SC_GRACE_BREEZE_OPTION:
			val3 = ELE_WIND;
			break;
		case SC_EARTH_CARE_OPTION:
			val3 = ELE_EARTH;
			break;
		case SC_DEEP_POISONING_OPTION:
			val3 = ELE_POISON;
			break;

		default:
			if (calc_flag == SCB_NONE && scdb->skill_id == 0 && scdb->icon == EFST_BLANK) {
				// Status change with no calc, no icon, and no skill associated...?
				ShowError("status_change_start: Unknown SC %d\n", type);
				return 0;
			}
	} else // Special considerations when loading SC data.
		switch( type ) {
			case SC_WEDDING:
			case SC_XMAS:
			case SC_SUMMER:
			case SC_HANBOK:
			case SC_OKTOBERFEST:
			case SC_DRESSUP:
				if( !vd )
					break;
				clif_changelook(bl,LOOK_BASE,vd->class_);
				clif_changelook(bl,LOOK_WEAPON,0);
				clif_changelook(bl,LOOK_SHIELD,0);
				clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
				clif_changelook(bl,LOOK_BODY2,0);
				break;
			case SC_STONE:
				if (val3 > 0)
					break; //Incubation time still active
				//Fall through
			case SC_POISON:
			case SC_DPOISON:
			case SC_BLEEDING:
			case SC_BURNING:
			case SC_TOXIN:
				tick_time = tick;
				tick = tick_time + max(val4, 0);
				break;
			case SC_DEATHHURT:
				if (val3 == 1)
					break;
				tick_time = tick;
				tick = tick_time + max(val4, 0);
			case SC_MAGICMUSHROOM:
			case SC_PYREXIA:
			case SC_LEECHESEND:
				if (val3 == 0)
					break;
				tick_time = tick;
				tick = tick_time + max(val4, 0);
				break;
			case SC_SWORDCLAN:
			case SC_ARCWANDCLAN:
			case SC_GOLDENMACECLAN:
			case SC_CROSSBOWCLAN:
			case SC_JUMPINGCLAN:
			case SC_CLAN_INFO:
				// If the player still has a clan status, but was removed from his clan
				if( sd && sd->status.clan_id == 0 ){
					return 0;
				}
				break;
			case SC_SERVANTWEAPON:
			case SC_ABYSSFORCEWEAPON:
				tick_time = tick;
				tick = tick_time + max(val4, 0);
				break;
		}

	if (sd && current_equip_combo_pos > 0 && tick == INFINITE_TICK) {
		ShowWarning("sc_start: Item combo of item #%u contains an INFINITE_TICK duration. Skipping bonus.\n", sd->inventory_data[pc_checkequip(sd, current_equip_combo_pos)]->nameid);
		return 0;
	}

	/* [Ind] */
	if (scdb->flag[SCF_DISPLAYPC] || scdb->flag[SCF_DISPLAYNPC]) {
		int dval1 = 0, dval2 = 0, dval3 = 0;

		switch (type) {
			case SC_ALL_RIDING:
				dval1 = 1;
				break;
			case SC_CLAN_INFO:
				dval1 = val1;
				dval2 = val2;
				dval3 = val3;
				break;
			default: /* All others: just copy val1 */
				dval1 = val1;
				break;
		}
		status_display_add(bl,type,dval1,dval2,dval3);
	}

	//SC that force player to stand if is sitting
	if (scdb->flag[SCF_SETSTAND] && sd && pc_issit(sd))
		pc_setstand(sd, true);

	//SC that make stop attacking [LuzZza]
	if (scdb->flag[SCF_STOPATTACKING])
		unit_stop_attack(bl);

	//SC that make stop walking
	if (scdb->flag[SCF_STOPWALKING]) {
		switch (type) {
			case SC_ANKLE:
				if (battle_config.skill_trap_type || !map_flag_gvg(bl->m))
					unit_stop_walking(bl, 1);
				break;
			case SC__MANHOLE:
				if (bl->type == BL_PC || !unit_blown_immune(bl,0x1))
					unit_stop_walking(bl,1);
				break;
			case SC_VACUUM_EXTREME:
				if (bl->type != BL_PC && unit_blown_immune(bl, 0x1) == UB_KNOCKABLE) {
					unit_stop_walking(bl,1);
					unit_stop_attack(bl);
				}
				break;
			case SC_WHITEIMPRISON:
			case SC_DEEPSLEEP:
			case SC_CRYSTALIZE:
			case SC_FREEZE:
			case SC_STUN:
			case SC_GRAVITYCONTROL:
				if (sc->data[SC_DANCING])
					unit_stop_walking(bl, 1);
				break;
			default:
				if (!unit_blown_immune(bl,0x1))
					unit_stop_walking(bl,1);
				break;
		}
	}

	//SC that make stop casting
	if (battle_config.sc_castcancel&bl->type && scdb->flag[SCF_STOPCASTING])
		unit_skillcastcancel(bl,0);

	sc->opt1 = scdb->opt1;
	sc->opt2 |= scdb->opt2;
	sc->opt3 |= scdb->opt3;
	sc->option |= scdb->look;

	std::bitset<SCF_MAX> opt_flag = scdb->flag;
	uint16 disable_opt_flag = false;

	switch(type) {
		case SC_STONE: 
			if (val3 > 0)
				sc->opt1 = OPT1_STONEWAIT;
			else
				sc->opt1 = OPT1_STONE;
			break;
		case SC_DANCING:
			if ((val1&0xFFFF) != CG_MOONLIT)
				sc->opt3 |= OPT3_MOONLIT;
			break;
		case SC_INCATKRATE:
			// Simulate Explosion Spirits effect for NPC_POWERUP [Skotlex]
			if (bl->type != BL_MOB) {
				disable_opt_flag = true;
				break;
			}
			break;
	}

	// On Aegis, when turning on a status change, first goes the option packet, then the sc packet.
	if (!disable_opt_flag && (opt_flag[SCF_SENDOPTION] || opt_flag[SCF_ONTOUCH] || opt_flag[SCF_UNITMOVE] || opt_flag[SCF_NONPLAYER] || opt_flag[SCF_SENDLOOK])) {
		clif_changeoption(bl);
		if(sd && opt_flag[SCF_SENDLOOK]) {
			clif_changelook(bl,LOOK_BASE,vd->class_);
			clif_changelook(bl,LOOK_WEAPON,0);
			clif_changelook(bl,LOOK_SHIELD,0);
			clif_changelook(bl,LOOK_CLOTHES_COLOR,vd->cloth_color);
		}
	}

	if (calc_flag&SCB_DYE) { // Reset DYE color
		if (vd && vd->cloth_color) {
			val4 = vd->cloth_color;
			clif_changelook(bl,LOOK_CLOTHES_COLOR,0);
		}
		calc_flag&=~SCB_DYE;
	}

	/*if (calc_flag&SCB_BODY)// Might be needed in the future. [Rytech]
	{	//Reset body style
		if (vd && vd->body_style)
		{
			val4 = vd->body_style;
			clif_changelook(bl,LOOK_BODY2,0);
		}
		calc_flag&=~SCB_BODY;
	}*/

	if (!(flag&SCSTART_NOICON) && !(flag&SCSTART_LOADED && scdb->flag[SCF_DISPLAYPC] || scdb->flag[SCF_DISPLAYNPC])) {
		int status_icon = scdb->icon;

#if PACKETVER < 20151104
		if (status_icon == EFST_WEAPONPROPERTY)
			status_icon = EFST_ATTACK_PROPERTY_NOTHING + val1; // Assign status icon for older clients
#endif

		clif_status_change(bl, status_icon, 1, tick, scdb->flag[SCF_SENDVAL1] ? val1 : 1, scdb->flag[SCF_SENDVAL2] ? val2 : 0, scdb->flag[SCF_SENDVAL3] ? val3 : 0);
	}

	// Used as temporary storage for scs with interval ticks, so that the actual duration is sent to the client first.
	if( tick_time )
		tick = tick_time;

	// Don't trust the previous sce assignment, in case the SC ended somewhere between there and here.
	if((sce=sc->data[type])) { // reuse old sc
		if( sce->timer != INVALID_TIMER )
			delete_timer(sce->timer, status_change_timer);
		sc_isnew = false;
	} else { // New sc
		++(sc->count);
		sce = sc->data[type] = ers_alloc(sc_data_ers, struct status_change_entry);
	}
	sce->val1 = val1;
	sce->val2 = val2;
	sce->val3 = val3;
	sce->val4 = val4;
	if (tick >= 0)
		sce->timer = add_timer(gettick() + tick, status_change_timer, bl->id, type);
	else
		sce->timer = INVALID_TIMER; // Infinite duration

	if (calc_flag) {
		if (sd) {
			switch(type) {
				// Statuses that adjust HP/SP and heal after starting
				case SC_BERSERK:
				case SC_MERC_HPUP:
				case SC_MERC_SPUP:
					status_calc_bl_(bl, static_cast<e_scb_flag>(calc_flag), SCO_FORCE);
					break;
				default:
					status_calc_bl(bl, calc_flag);
					break;
			}
		} else
			status_calc_bl(bl, calc_flag);
	}

	// Non-zero
	// Calc state for SC_STONE when OPT1_STONE in the timer
	if (sc_isnew && scdb->state.any() && type != SC_STONE)
		status_calc_state(bl, sc, scdb->state, true);

	if (sd && sd->pd)
		pet_sc_check(sd, type); // Skotlex: Pet Status Effect Healing

	// 1st thing to execute when loading status
	switch (type) {
		case SC_BERSERK:
			if (!(sce->val2)) { // Don't heal if already set
				status_heal(bl, status->max_hp, 0, 1); // Do not use percent_heal as this healing must override BERSERK's block.
				status_set_sp(bl, 0, 0); // Damage all SP
			}
			sce->val2 = 5 * status->max_hp / 100;
			break;
		case SC_RUN:
			{
				struct unit_data *ud = unit_bl2ud(bl);

				if( ud )
					ud->state.running = unit_run(bl, NULL, SC_RUN);
			}
			break;
		case SC_BOSSMAPINFO:
			if (sd)
				clif_bossmapinfo(sd, map_id2boss(sce->val1), BOSS_INFO_ALIVE_WITHMSG); // First Message
			break;
		case SC_FULL_THROTTLE:
		case SC_MERC_HPUP:
			status_percent_heal(bl, 100, 0); // Recover Full HP
			break;
		case SC_MERC_SPUP:
			status_percent_heal(bl, 0, 100); // Recover Full SP
			break;
		case SC_WUGDASH:
			{
				struct unit_data *ud = unit_bl2ud(bl);

				if( ud )
					ud->state.running = unit_run(bl, sd, SC_WUGDASH);
			}
			break;
		case SC_COMBO:
			switch(sce->val1) {
			case TK_STORMKICK:
				skill_combo_toggle_inf(bl, TK_JUMPKICK, 0);
				clif_skill_nodamage(bl,bl,TK_READYSTORM,1,1);
				break;
			case TK_DOWNKICK:
				skill_combo_toggle_inf(bl, TK_JUMPKICK, 0);
				clif_skill_nodamage(bl,bl,TK_READYDOWN,1,1);
				break;
			case TK_TURNKICK:
				skill_combo_toggle_inf(bl, TK_JUMPKICK, 0);
				clif_skill_nodamage(bl,bl,TK_READYTURN,1,1);
				break;
			case TK_COUNTER:
				skill_combo_toggle_inf(bl, TK_JUMPKICK, 0);
				clif_skill_nodamage(bl,bl,TK_READYCOUNTER,1,1);
				break;
			default: // Rest just toggle inf to enable autotarget
				skill_combo_toggle_inf(bl,sce->val1,INF_SELF_SKILL);
				break;
			}
			break;
		case SC_C_MARKER:
			//Send mini-map, don't wait for first timer triggered
			if (src->type == BL_PC && (sd = map_id2sd(src->id)))
				clif_crimson_marker(sd, bl, false);
			break;
		case SC_ITEMSCRIPT: // Shows Buff Icons
			if (sd)
				clif_status_change(bl, (efst_type)val2, 1, tick, 0, 0, 0);
			break;
		case SC_GVG_GIANT:
		case SC_GVG_GOLEM:
		case SC_GVG_STUN:
		case SC_GVG_STONE:
		case SC_GVG_FREEZ:
		case SC_GVG_SLEEP:
		case SC_GVG_CURSE:
		case SC_GVG_SILENCE:
		case SC_GVG_BLIND:
			if (val1 || val2)
				status_zap(bl, val1 ? val1 : 0, val2 ? val2 : 0);
			break;
	}

	if( opt_flag[SCF_ONTOUCH] && sd && !sd->npc_ontouch_.empty() )
		npc_touchnext_areanpc(sd,false); // Run OnTouch_ on next char in range

	return 1;
}

/**
 * End all statuses except those listed
 * TODO: May be useful for dispel instead resetting a list there
 * @param src: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
 * @param type: Changes behaviour of the function
 * 	0: PC killed -> Place here statuses that do not dispel on death.
 * 	1: If for some reason status_change_end decides to still keep the status when quitting.
 * 	2: Do clif_changeoption()
 * 	3: Do not remove some permanent/time-independent effects
 * @return 1: Success 0: Fail
 */
int status_change_clear(struct block_list* bl, int type)
{
	struct status_change* sc;

	sc = status_get_sc(bl);

	if (!sc)
		return 0;

	// Cleaning all extras vars
	sc->comet_x = 0;
	sc->comet_y = 0;
#ifndef RENEWAL
	sc->sg_counter = 0;
#endif

	if (!sc->count)
		return 0;

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

		if (!sc->data[status])
			continue;
		if (type == 0) { // Type 0: PC killed
			if (it.second->flag[SCF_NOREMOVEONDEAD]) {
				switch (status) {
					case SC_ELEMENTALCHANGE: // Only when its Holy or Dark that it doesn't dispell on death
						if (sc->data[status]->val2 != ELE_HOLY && sc->data[status]->val2 != ELE_DARK)
							break;
					default:
						continue;
				}
			}
		}

		if (type == 3 && it.second->flag[SCF_NOCLEARBUFF])
			continue;

		status_change_end(bl, status, INVALID_TIMER);
		if( type == 1 && sc->data[status] ) { // If for some reason status_change_end decides to still keep the status when quitting. [Skotlex]
			(sc->count)--;
			if (sc->data[status]->timer != INVALID_TIMER)
				delete_timer(sc->data[status]->timer, status_change_timer);
			ers_free(sc_data_ers, sc->data[status]);
			sc->data[status] = NULL;
		}
	}

	sc->opt1 = 0;
	sc->opt2 = 0;
	sc->opt3 = 0;

	if( type == 0 || type == 2 )
		clif_changeoption(bl);

	return 1;
}

/**
 * End a specific status after checking
 * @param bl: Source of the status change [PC|MOB|HOM|MER|ELEM|NPC]
 * @param type: Status change (SC_*)
 * @param tid: Timer
 * @param file: Used for dancing save
 * @param line: Used for dancing save
 * @return 1: Success 0: Fail
 */
int status_change_end_(struct block_list* bl, enum sc_type type, int tid, const char* file, int line)
{
	struct map_session_data *sd;
	struct status_change *sc;
	struct status_change_entry *sce;
	struct status_data *status;
	struct view_data *vd;
	e_scb_flag calc_flag = SCB_NONE;
	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(!sc || !(sce = sc->data[type]) || !scdb)
		return 0;

	sd = BL_CAST(BL_PC,bl);

	if (sce->timer != tid && tid != INVALID_TIMER)
		return 0;

	if (tid == INVALID_TIMER) {
		if (type == SC_ENDURE && sce->val4)
			// Do not end infinite endure.
			return 0;
		if (type == SC_SPIDERWEB) {
			//Delete the unit group first to expire found in the status change
			std::shared_ptr<s_skill_unit_group> group, group2;
			t_tick tick = gettick();
			int pos = 1;
			if (sce->val2)
				if (!(group = skill_id2group(sce->val2)))
					sce->val2 = 0;
			if (sce->val3) {
				if (!(group2 = skill_id2group(sce->val3)))
					sce->val3 = 0;
				else if (!group || ((group->limit - DIFF_TICK(tick, group->tick)) > (group2->limit - DIFF_TICK(tick, group2->tick)))) {
					group = group2;
					pos = 2;
				}
			}
			if (sce->val4) {
				if (!(group2 = skill_id2group(sce->val4)))
					sce->val4 = 0;
				else if (!group || ((group->limit - DIFF_TICK(tick, group->tick)) > (group2->limit - DIFF_TICK(tick, group2->tick)))) {
					group = group2;
					pos = 3;
				}
			}
			if (pos == 1)
				sce->val2 = 0;
			else if (pos == 2)
				sce->val3 = 0;
			else if (pos == 3)
				sce->val4 = 0;
			if (group)
				skill_delunitgroup(group);
			if (!status_isdead(bl) && (sce->val2 || sce->val3 || sce->val4))
				return 0; //Don't end the status change yet as there are still unit groups associated with it
		}
		if (sce->timer != INVALID_TIMER) // Could be a SC with infinite duration
			delete_timer(sce->timer,status_change_timer);
		if (sc->opt1)
			switch (type) {
				// "Ugly workaround"  [Skotlex]
				// delays status change ending so that a skill that sets opt1 fails to
				// trigger when it also removed one
				case SC_STONE:
					sce->val4 = -1; // Petrify time
				case SC_FREEZE:
				case SC_STUN:
				case SC_SLEEP:
					if (sce->val1) {
						// Removing the 'level' shouldn't affect anything in the code
						// since these SC are not affected by it, and it lets us know
						// if we have already delayed this attack or not.
						sce->val1 = 0;
						sce->timer = add_timer(gettick()+10, status_change_timer, bl->id, type);
						return 1;
					}
			}
	}

	(sc->count)--;

	if (scdb->state.any())
		status_calc_state(bl,sc,scdb->state,false);

	sc->data[type] = NULL;

	if (scdb->flag[SCF_DISPLAYPC] || scdb->flag[SCF_DISPLAYNPC])
		status_display_remove(bl,type);

	vd = status_get_viewdata(bl);
	calc_flag = static_cast<e_scb_flag>(scdb->calc_flag);

	switch(type) {
		case SC_KEEPING:
		case SC_BARRIER: {
			unit_data *ud = unit_bl2ud(bl);

			if (ud)
				ud->attackabletime = ud->canact_tick = ud->canmove_tick = gettick();
		}
			break;
		case SC_GRANITIC_ARMOR:
			{
				int damage = status->max_hp*sce->val3/100;
				if(status->hp < damage) // to not kill him
					damage = status->hp-1;
				status_damage(NULL,bl,damage,0,0,1,0);
			}
			break;
		case SC_RUN:
		{
			struct unit_data *ud = unit_bl2ud(bl);
			bool begin_spurt = true;
			// Note: this int64 value is stored in two separate int32 variables (FIXME)
			t_tick starttick  = (t_tick)sce->val3&0x00000000ffffffffLL;
			starttick |= ((t_tick)sce->val4<<32)&0xffffffff00000000LL;

			if (ud) {
				if(!ud->state.running)
					begin_spurt = false;
				ud->state.running = 0;
				if (ud->walktimer != INVALID_TIMER)
					unit_stop_walking(bl,1);
			}
			if (begin_spurt && sce->val1 >= 7 &&
				DIFF_TICK(gettick(), starttick) <= 1000 &&
				(!sd || (sd->weapontype1 == W_FIST && sd->weapontype2 == W_FIST))
			)
				sc_start(bl,bl,SC_SPURT,100,sce->val1,skill_get_time2(scdb->skill_id, sce->val1));
		}
		break;
		case SC_AUTOBERSERK:
			if (sc->data[SC_PROVOKE] && sc->data[SC_PROVOKE]->val4 == 1)
				status_change_end(bl, SC_PROVOKE, INVALID_TIMER);
			break;

		case SC_ENDURE:
		case SC_DEFENDER:
		case SC_REFLECTSHIELD:
		case SC_AUTOGUARD:
			{
				struct map_session_data *tsd;
				if( bl->type == BL_PC ) { // Clear Status from others
					int i;
					for( i = 0; i < MAX_DEVOTION; i++ ) {
						if( sd->devotion[i] && (tsd = map_id2sd(sd->devotion[i])) && tsd->sc.data[type] )
							status_change_end(&tsd->bl, type, INVALID_TIMER);
					}
				}
				else if( bl->type == BL_MER && ((TBL_MER*)bl)->devotion_flag ) { // Clear Status from Master
					tsd = ((TBL_MER*)bl)->master;
					if( tsd && tsd->sc.data[type] )
						status_change_end(&tsd->bl, type, INVALID_TIMER);
				}
			}
			break;
		case SC_DEVOTION:
			{
				struct block_list *d_bl = map_id2bl(sce->val1);
				if( d_bl ) {
					if( d_bl->type == BL_PC )
						((TBL_PC*)d_bl)->devotion[sce->val2] = 0;
					else if( d_bl->type == BL_MER )
						((TBL_MER*)d_bl)->devotion_flag = 0;
					clif_devotion(d_bl, NULL);
				}

				status_change_end(bl, SC_AUTOGUARD, INVALID_TIMER);
				status_change_end(bl, SC_DEFENDER, INVALID_TIMER);
				status_change_end(bl, SC_REFLECTSHIELD, INVALID_TIMER);
				status_change_end(bl, SC_ENDURE, INVALID_TIMER);
			}
			break;

		case SC_FLASHKICK: {
				map_session_data *tsd;

				if (!(tsd = map_id2sd(sce->val1)))
					break;

				tsd->stellar_mark[sce->val2] = 0;
			}
			break;

		case SC_SOULUNITY: {
				map_session_data *tsd;

				if (!(tsd = map_id2sd(sce->val2)))
					break;

				tsd->united_soul[sce->val3] = 0;
			}
			break;

		case SC_BLADESTOP:
			if(sce->val4) {
				int tid2 = sce->val4; //stop the status for the other guy of bladestop as well
				struct block_list *tbl = map_id2bl(tid2);
				struct status_change *tsc = status_get_sc(tbl);
				sce->val4 = 0;
				if(tbl && tsc && tsc->data[SC_BLADESTOP]) {
					tsc->data[SC_BLADESTOP]->val4 = 0;
					status_change_end(tbl, SC_BLADESTOP, INVALID_TIMER);
				}
				clif_bladestop(bl, tid2, 0);
			}
			break;
		case SC_DANCING:
			{
				struct map_session_data *dsd;
				struct status_change_entry *dsc;

				if(sce->val4 && sce->val4 != BCT_SELF && (dsd=map_id2sd(sce->val4))) { // End status on partner as well
					dsc = dsd->sc.data[SC_DANCING];
					if(dsc) {
						// This will prevent recursive loops.
						dsc->val2 = dsc->val4 = 0;
						status_change_end(&dsd->bl, SC_DANCING, INVALID_TIMER);
					}
				}

				if(sce->val2) { // Erase associated land skill
					std::shared_ptr<s_skill_unit_group> group = skill_id2group(sce->val2);

					sce->val2 = 0;
					if (group)
						skill_delunitgroup(group);
				}

				if((sce->val1&0xFFFF) == CG_MOONLIT)
					clif_status_change(bl,EFST_MOON,0,0,0,0,0);

#ifdef RENEWAL
				status_change_end(bl, SC_ENSEMBLEFATIGUE, INVALID_TIMER);
#else
				status_change_end(bl, SC_LONGING, INVALID_TIMER);
#endif
			}
			break;
		case SC_NOCHAT:
			if (sd && sd->status.manner < 0 && tid != INVALID_TIMER)
				sd->status.manner = 0;
			if (sd && tid == INVALID_TIMER) {
				clif_changestatus(sd,SP_MANNER,sd->status.manner);
				clif_updatestatus(sd,SP_MANNER);
			}
			break;
		case SC_SPLASHER:
		case SC_ROSEBLOSSOM:
			{
				struct block_list *src=map_id2bl(sce->val3);

				if(src && tid != INVALID_TIMER)
					skill_castend_damage_id(src, bl, sce->val2, sce->val1, gettick(), SD_LEVEL );
			}
			break;
		case SC_CLOSECONFINE2:{
			struct block_list *src = sce->val2?map_id2bl(sce->val2):NULL;
			struct status_change *sc2 = src?status_get_sc(src):NULL;
			if (src && sc2 && sc2->data[SC_CLOSECONFINE]) {
				// If status was already ended, do nothing.
				// Decrease count
				if (--(sc2->data[SC_CLOSECONFINE]->val1) <= 0) // No more holds, free him up.
					status_change_end(src, SC_CLOSECONFINE, INVALID_TIMER);
			}
		}
		case SC_CLOSECONFINE:
			if (sce->val2 > 0) {
				// Caster has been unlocked... nearby chars need to be unlocked.
				int range = 1
					+ skill_get_range2(bl, scdb->skill_id, sce->val1, true)
					+ skill_get_range2(bl, TF_BACKSLIDING, 1, true); // Since most people use this to escape the hold....
				map_foreachinallarea(status_change_timer_sub,
					bl->m, bl->x-range, bl->y-range, bl->x+range,bl->y+range,BL_CHAR,bl,sce,type,gettick());
			}
			break;
		case SC_COMBO:
			skill_combo_toggle_inf(bl,sce->val1,0);
			break;
		case SC_MARIONETTE:
		case SC_MARIONETTE2: // Marionette target
			if (sce->val1) { // Check for partner and end their marionette status as well
				enum sc_type type2 = (type == SC_MARIONETTE) ? SC_MARIONETTE2 : SC_MARIONETTE;
				struct block_list *pbl = map_id2bl(sce->val1);
				struct status_change* sc2 = pbl?status_get_sc(pbl):NULL;

				if (sc2 && sc2->data[type2]) {
					sc2->data[type2]->val1 = 0;
					status_change_end(pbl, type2, INVALID_TIMER);
				}
			}
			break;

		case SC_CONCENTRATION:
			if (sc->data[SC_ENDURE] && !sc->data[SC_ENDURE]->val4)
				status_change_end(bl, SC_ENDURE, INVALID_TIMER);
			break;
		case SC_BERSERK:
			if(status->hp > 200 && sc && sc->data[SC__BLOODYLUST]) {
				status_percent_heal(bl, 100, 0);
				status_change_end(bl, SC__BLOODYLUST, INVALID_TIMER);
			} else if (status->hp > 100 && sce->val2) // If val2 is removed, no HP penalty (dispelled?) [Skotlex]
				status_set_hp(bl, 100, 0);
			if(sc->data[SC_ENDURE] && sc->data[SC_ENDURE]->val4) {
				sc->data[SC_ENDURE]->val4 = 0;
				status_change_end(bl, SC_ENDURE, INVALID_TIMER);
			}
			sc_start4(bl, bl, SC_REGENERATION, 100, 10,0,0,(RGN_HP|RGN_SP), skill_get_time(LK_BERSERK, sce->val1));
			break;
		case SC_GOSPEL:
			if (sce->val3) { // Clear the group.
				std::shared_ptr<s_skill_unit_group> group = skill_id2group(sce->val3);

				sce->val3 = 0;
				if (group)
					skill_delunitgroup(group);
			}
			break;
#ifndef RENEWAL
		case SC_HERMODE:
			if(sce->val3 == BCT_SELF)
				skill_clear_unitgroup(bl);
			break;
		case SC_BASILICA: // Clear the skill area. [Skotlex]
				if (sce->val3 && sce->val4 == bl->id) {
					std::shared_ptr<s_skill_unit_group> group = skill_id2group(sce->val3);

					sce->val3 = 0;
					if (group)
						skill_delunitgroup(group);
				}
				break;
#endif
		case SC_TRICKDEAD:
			if (vd) vd->dead_sit = 0;
			break;
		case SC_WARM:
		case SC__MANHOLE:
		case SC_BANDING:
		case SC_LEADERSHIP:
		case SC_GLORYWOUNDS:
		case SC_SOULCOLD:
		case SC_HAWKEYES:
			if (sce->val4) { // Clear the group.
				std::shared_ptr<s_skill_unit_group> group = skill_id2group(sce->val4);

				sce->val4 = 0;
				if( group ) // Might have been cleared before status ended, e.g. land protector
					skill_delunitgroup(group);
			}
			break;
		case SC_JAILED:
			if(tid == INVALID_TIMER)
				break;
		  	// Natural expiration.
			if(sd && sd->mapindex == sce->val2)
				pc_setpos(sd,(unsigned short)sce->val3,sce->val4&0xFFFF, sce->val4>>16, CLR_TELEPORT);
			break; // Guess hes not in jail :P
		case SC_CHANGE:
			if (tid == INVALID_TIMER)
		 		break;
			// "lose almost all their HP and SP" on natural expiration.
			status_set_hp(bl, 10, 0);
			status_set_sp(bl, 10, 0);
			break;
		case SC_AUTOTRADE:
			if (tid == INVALID_TIMER)
				break;
			// Vending is not automatically closed for autovenders
			vending_closevending(sd);
			map_quit(sd);
			// Because map_quit calls status_change_end with tid -1
			// from here it's not neccesary to continue
			return 1;
			break;
		case SC_STOP:
			if( sce->val2 ) {
				struct block_list* tbl = map_id2bl(sce->val2);
				sce->val2 = 0;
				if( tbl && (sc = status_get_sc(tbl)) && sc->data[SC_STOP] && sc->data[SC_STOP]->val2 == bl->id )
					status_change_end(tbl, SC_STOP, INVALID_TIMER);
			}
			break;
		case SC_TENSIONRELAX:
			if (sc && (sc->data[SC_WEIGHT50] || sc->data[SC_WEIGHT90]))
				status_get_regen_data(bl)->state.overweight = 1; // Add the overweight flag back
			break;
		case SC_MONSTER_TRANSFORM:
		case SC_ACTIVE_MONSTER_TRANSFORM:
			if (sce->val2)
				status_change_end(bl, (sc_type)sce->val2, INVALID_TIMER);
			break;

		/* 3rd Stuff */
		case SC_MILLENNIUMSHIELD:
			clif_millenniumshield(bl, 0);
			break;
		case SC_HALLUCINATIONWALK:
			sc_start(bl,bl,SC_HALLUCINATIONWALK_POSTDELAY,100,sce->val1,skill_get_time2(GC_HALLUCINATIONWALK,sce->val1));
			break;
		case SC_WHITEIMPRISON:
			{
				struct block_list* src = map_id2bl(sce->val2);
				if( tid == -1 || !src)
					break; // Terminated by Damage
				status_fix_damage(src,bl,400*sce->val1,clif_damage(bl,bl,gettick(),0,0,400*sce->val1,0,DMG_NORMAL,0,false),WL_WHITEIMPRISON);
			}
			break;
		case SC_WUGDASH:
			{
				struct unit_data *ud = unit_bl2ud(bl);
				if (ud) {
					ud->state.running = 0;
					if (ud->walktimer != INVALID_TIMER)
						unit_stop_walking(bl,1);
				}
			}
			break;
		case SC__SHADOWFORM:
			{
				struct map_session_data *s_sd = map_id2sd(sce->val2);

				if (s_sd) s_sd->shadowform_id = 0;
			}
			break;
		case SC_SATURDAYNIGHTFEVER: // Sit down force of Saturday Night Fever has the duration of only 3 seconds.
			sc_start(bl, bl,SC_SITDOWN_FORCE,100,sce->val1,skill_get_time2(WM_SATURDAY_NIGHT_FEVER,sce->val1));
			break;
		case SC_NEUTRALBARRIER_MASTER:
		case SC_STEALTHFIELD_MASTER:
			if( sce->val2 ) {
				std::shared_ptr<s_skill_unit_group> group = skill_id2group(sce->val2);

				sce->val2 = 0;
				if( group ) // Might have been cleared before status ended, e.g. land protector
					skill_delunitgroup(group);
			}
			break;
		case SC_CURSEDCIRCLE_ATKER:
			if( sce->val2 ) // Used the default area size cause there is a chance the caster could knock back and can't clear the target.
				map_foreachinallrange(status_change_timer_sub, bl, AREA_SIZE + 3, BL_CHAR, bl, sce, SC_CURSEDCIRCLE_TARGET, gettick());
			break;
		case SC_RAISINGDRAGON:
			if( sd && !pc_isdead(sd) ) {
				int i = min(sd->spiritball,5);
				pc_delspiritball(sd, sd->spiritball, 0);
				status_change_end(bl, SC_EXPLOSIONSPIRITS, INVALID_TIMER);
				while( i > 0 ) {
					pc_addspiritball(sd, skill_get_time(MO_CALLSPIRITS, pc_checkskill(sd,MO_CALLSPIRITS)), 5);
					--i;
				}
			}
			break;
		case SC_CURSEDCIRCLE_TARGET:
			{
				struct block_list *src = map_id2bl(sce->val2);
				struct status_change *sc2 = status_get_sc(src);

				if( sc2 && sc2->data[SC_CURSEDCIRCLE_ATKER] && --(sc2->data[SC_CURSEDCIRCLE_ATKER]->val2) == 0 ) {
					clif_bladestop(bl, sce->val2, 0);
					status_change_end(src, SC_CURSEDCIRCLE_ATKER, INVALID_TIMER);
				}
			}
			break;
		case SC_TEARGAS:
			status_change_end(bl,SC_TEARGAS_SOB,INVALID_TIMER);
			break;
		case SC_SITDOWN_FORCE:
		case SC_BANANA_BOMB_SITDOWN:
			if( sd && pc_issit(sd) && pc_setstand(sd, false) )
				skill_sit(sd, false);
			break;
		case SC_KYOUGAKU:
			clif_status_load(bl, EFST_KYOUGAKU, 0); // Avoid client crash
			clif_status_load(bl, EFST_ACTIVE_MONSTER_TRANSFORM, 0);
			break;
		case SC_INTRAVISION:
			calc_flag = SCB_ALL; // Required for overlapping
			break;

		case SC_SUNSTANCE:
			status_change_end(bl, SC_LIGHTOFSUN, INVALID_TIMER);
			break;
		case SC_LUNARSTANCE:
			status_change_end(bl, SC_NEWMOON, INVALID_TIMER);
			status_change_end(bl, SC_LIGHTOFMOON, INVALID_TIMER);
			break;
		case SC_STARSTANCE:
			status_change_end(bl, SC_FALLINGSTAR, INVALID_TIMER);
			status_change_end(bl, SC_LIGHTOFSTAR, INVALID_TIMER);
			break;
		case SC_UNIVERSESTANCE:
			status_change_end(bl, SC_LIGHTOFSUN, INVALID_TIMER);
			status_change_end(bl, SC_NEWMOON, INVALID_TIMER);
			status_change_end(bl, SC_LIGHTOFMOON, INVALID_TIMER);
			status_change_end(bl, SC_FALLINGSTAR, INVALID_TIMER);
			status_change_end(bl, SC_LIGHTOFSTAR, INVALID_TIMER);
			status_change_end(bl, SC_DIMENSION, INVALID_TIMER);
			break;
		case SC_GRAVITYCONTROL:
			status_fix_damage(bl, bl, sce->val2, clif_damage(bl, bl, gettick(), 0, 0, sce->val2, 0, DMG_NORMAL, 0, false), 0);
			clif_specialeffect(bl, 223, AREA);
			clif_specialeffect(bl, 330, AREA);
			break;
			
		case SC_OVERED_BOOST:
			switch (bl->type) {
				case BL_HOM: {
						struct homun_data *hd = BL_CAST(BL_HOM,bl);

						if( hd )
							hd->homunculus.hunger = max(1,hd->homunculus.hunger - 50);
					}
					break;
				case BL_PC:
					status_zap(bl,0,status_get_max_sp(bl) / 2);
					break;
			}
			break;
		case SC_FULL_THROTTLE: {
				int sec = skill_get_time2(scdb->skill_id, sce->val1);

				clif_status_change(bl, EFST_DEC_AGI, 1, sec, 0, 0, 0);
				sc_start(bl, bl, SC_REBOUND, 100, sce->val1, sec);
			}
			break;
		case SC_REBOUND:
			clif_status_load(bl, EFST_DEC_AGI, 0);
			break;
		case SC_ITEMSCRIPT: // Removes Buff Icons
			if (sd)
				clif_status_load(bl, (enum efst_type)sce->val2, 0);
			break;
		case SC_C_MARKER:
			{
				// Remove mark data from caster
				struct map_session_data *caster = map_id2sd(sce->val2);
				uint8 i = 0;

				if (!caster)
					break;
				ARR_FIND(0,MAX_SKILL_CRIMSON_MARKER,i,caster->c_marker[i] == bl->id);
				if (i < MAX_SKILL_CRIMSON_MARKER) {
					caster->c_marker[i] = 0;
					clif_crimson_marker(caster, bl, true);
				}
			}
			break;
		case SC_H_MINE:
			{
				// Drop the material from target if expired
				struct item it;
				struct map_session_data *caster = NULL;

				if (sce->val3 || status_isdead(bl) || !(caster = map_id2sd(sce->val2)))
					break;

				std::shared_ptr<s_skill_db> skill = skill_db.find(RL_H_MINE);

				if (!itemdb_exists(skill->require.itemid[0]))
					break;
				memset(&it, 0, sizeof(it));
				it.nameid = skill->require.itemid[0];
				it.amount = max(skill->require.amount[0],1);
				it.identify = 1;
				map_addflooritem(&it, it.amount, bl->m,bl->x, bl->y, caster->status.char_id, 0, 0, 4, 0);
			}
			break;
		case SC_VACUUM_EXTREME:
			///< !CHECKME: Seems on official, there's delay before same target can be vacuumed in same area again [Cydh]
			sc_start2(bl, bl, SC_VACUUM_EXTREME_POSTDELAY, 100, sce->val1, sce->val2, skill_get_time2(SO_VACUUM_EXTREME,sce->val1));
			break;
		case SC_SWORDCLAN:
		case SC_ARCWANDCLAN:
		case SC_GOLDENMACECLAN:
		case SC_CROSSBOWCLAN:
		case SC_JUMPINGCLAN:
			status_change_end(bl,SC_CLAN_INFO,INVALID_TIMER);
			break;
		case SC_DIMENSION1:
		case SC_DIMENSION2:
			if (sd)
				pc_delspiritball(sd, 1, 0);
			break;
		case SC_SOULENERGY:
			if (sd)
				pc_delsoulball(sd, sd->soulball, false);
			break;
		case SC_MADOGEAR:
			status_db.removeByStatusFlag(bl, { SCF_MADOENDCANCEL });
			if (sd)
				pc_bonus_script_clear(sd, BSF_REM_ON_MADOGEAR);
			break;
		case SC_HOMUN_TIME:
			if (sd && hom_is_active(sd->hd))
				hom_vaporize(sd, HOM_ST_REST);
			break;
		case SC_SERVANT_SIGN: {
				map_session_data *tsd = map_id2sd(sce->val1);

				if( tsd != nullptr )
					tsd->servant_sign[sce->val2] = 0;
			}
			break;
		case SC_SOUNDBLEND: {
				block_list *src = map_id2bl(sce->val2);

				if (src && tid != INVALID_TIMER)
					skill_castend_damage_id(src, bl, TR_SOUNDBLEND, sce->val1, gettick(), SD_LEVEL|SD_ANIMATION);
			}
			break;
		case SC_SERVANTWEAPON:
			if( sd ){
				pc_delservantball( *sd, sd->servantball );
			}
			break;
		case SC_CHARGINGPIERCE:
			status_change_end(bl, SC_CHARGINGPIERCE_COUNT, INVALID_TIMER);
			break;
		case SC_ABYSSFORCEWEAPON:
			if( sd ){
				pc_delabyssball( *sd, sd->abyssball );
			}
			break;
	}

	// Reset the options as needed
	std::bitset<SCF_MAX> opt_flag = scdb->flag;
	bool disable_opt_flag = false;

	switch (type) {
		case SC_DANCING:
			if ((sce->val1&0xFFFF) == CG_MOONLIT)
				sc->opt3 &= ~OPT3_MOONLIT;
			break;
		case SC_INCATKRATE: // Simulated Explosion spirits effect.
			if (bl->type != BL_MOB) {
				disable_opt_flag = true;
				break;
			}
			break;
	}

	if (scdb->opt1)
		sc->opt1 = OPT1_NONE;

	if (scdb->opt2)
		sc->opt2 &= ~scdb->opt2;

	if (scdb->opt3)
		sc->opt3 &= ~scdb->opt3;

	if (scdb->look)
		sc->option &= ~scdb->look;

	if (calc_flag&SCB_DYE) { // Restore DYE color
		if (vd && !vd->cloth_color && sce->val4)
			clif_changelook(bl,LOOK_CLOTHES_COLOR,sce->val4);
		calc_flag = static_cast<e_scb_flag>(calc_flag&~SCB_DYE);
	}

	/*if (calc_flag&SCB_BODY)// Might be needed in the future. [Rytech]
	{	//Restore body style
		if (vd && !vd->body_style && sce->val4)
			clif_changelook(bl,LOOK_BODY2,sce->val4);
		calc_flag = static_cast<scb_flag>(calc_flag&~SCB_BODY);
	}*/

	// On Aegis, when turning off a status change, first goes the sc packet, then the option packet.
	int status_icon = scdb->icon;

#if PACKETVER < 20151104
	if (status_icon == EFST_WEAPONPROPERTY)
		status_icon = EFST_ATTACK_PROPERTY_NOTHING + sce->val1; // Assign status icon for older clients
#endif

	clif_status_change(bl,status_icon,0,0,0,0,0);

	if( opt_flag[SCF_NONPLAYER] ) // bugreport:681
		clif_changeoption2(bl);
	else if (!disable_opt_flag && (opt_flag[SCF_SENDOPTION] || opt_flag[SCF_ONTOUCH] || opt_flag[SCF_UNITMOVE] || opt_flag[SCF_NONPLAYER] || opt_flag[SCF_SENDLOOK])) {
		clif_changeoption(bl);
		if (sd && opt_flag[SCF_SENDLOOK]) {
			clif_changelook(bl,LOOK_BASE,sd->vd.class_);
			clif_get_weapon_view(sd,&sd->vd.weapon,&sd->vd.shield);
			clif_changelook(bl,LOOK_WEAPON,sd->vd.weapon);
			clif_changelook(bl,LOOK_SHIELD,sd->vd.shield);
			clif_changelook(bl,LOOK_CLOTHES_COLOR,cap_value(sd->status.clothes_color,0,battle_config.max_cloth_color));
			clif_changelook(bl,LOOK_BODY2,cap_value(sd->status.body,0,battle_config.max_body_style));
		}
	}
	if (calc_flag) {
#ifndef RENEWAL
		if (type == SC_MAGICPOWER) {
			//If Mystical Amplification ends, MATK is immediately recalculated
			status_calc_bl_(bl, calc_flag, SCO_FORCE);
		} else
#endif
			status_calc_bl(bl, calc_flag);
	}

	if(opt_flag[SCF_UNITMOVE]) // Out of hiding, invoke on place.
		skill_unit_move(bl,gettick(),1);

	if(opt_flag[SCF_ONTOUCH] && sd && !sd->state.warping && map_getcell(bl->m,bl->x,bl->y,CELL_CHKNPC))
		npc_touch_area_allnpc(sd,bl->m,bl->x,bl->y); // Trigger on-touch event.

	ers_free(sc_data_ers, sce);
	return 1;
}

/**
 * Resets timers for statuses
 * Used with reoccurring status effects, such as dropping SP every 5 seconds
 * @param tid: Timer ID
 * @param tick: How long before next call
 * @param id: ID of character
 * @param data: Information passed through the timer call
 * @return 1: Success 0: Fail
 */
TIMER_FUNC(status_change_timer){
	enum sc_type type = (sc_type)data;
	struct block_list *bl;
	struct map_session_data *sd;
	int interval = status_get_sc_interval(type);
	bool dounlock = false;

	bl = map_id2bl(id);
	if(!bl) {
		ShowDebug("status_change_timer: Null pointer id: %d data: %" PRIdPTR "\n", id, data);
		return 0;
	}

	struct status_change * const sc = status_get_sc(bl);
	struct status_data * const status = status_get_status_data(bl);
	if(!sc) {
		ShowDebug("status_change_timer: Null pointer id: %d data: %" PRIdPTR " bl-type: %d\n", id, data, bl->type);
		return 0;
	}
	
	struct status_change_entry * const sce = sc->data[type];
	if(!sce) {
		ShowDebug("status_change_timer: Null pointer id: %d data: %" PRIdPTR " bl-type: %d\n", id, data, bl->type);
		return 0;
	}
	if( sce->timer != tid ) {
		ShowError("status_change_timer: Mismatch for type %d: %d != %d (bl id %d)\n",type,tid,sce->timer, bl->id);
		return 0;
	}

	sd = BL_CAST(BL_PC, bl);

	std::function<void (t_tick)> sc_timer_next = [&sce, &bl, &data](t_tick t) {
		sce->timer = add_timer(t, status_change_timer, bl->id, data);
	};
	
	switch(type) {
	case SC_MAXIMIZEPOWER:
	case SC_CLOAKING:
		if(!status_charge(bl, 0, 1))
			break; // Not enough SP to continue.
		sc_timer_next(sce->val2+tick);
		return 0;

	case SC_CHASEWALK:
		if(!status_charge(bl, 0, sce->val4))
			break; // Not enough SP to continue.

		if (!sc->data[SC_CHASEWALK2]) {
			sc_start(bl,bl, SC_CHASEWALK2,100,1<<(sce->val1-1),
				(t_tick)(sc->data[SC_SPIRIT] && sc->data[SC_SPIRIT]->val2 == SL_ROGUE?10:1) // SL bonus -> x10 duration
				*skill_get_time2(status_db.getSkill(type),sce->val1));
		}
		sc_timer_next(sce->val2+tick);
		return 0;
	break;

	case SC_HIDING:
		if(--(sce->val2)>0) {

			if(sce->val2 % sce->val4 == 0 && !status_charge(bl, 0, 1))
				break; // Fail if it's time to substract SP and there isn't.

			sc_timer_next(1000+tick);
			return 0;
		}
	break;

	case SC_SIGHT:
	case SC_RUWACH:
	case SC_SIGHTBLASTER:
		if(type == SC_SIGHTBLASTER) {
			//Restore trap immunity
			if(sce->val4%2)
				sce->val4--;
			map_foreachinallrange( status_change_timer_sub, bl, sce->val3, BL_CHAR|BL_SKILL, bl, sce, type, tick);
		} else {
			map_foreachinallrange( status_change_timer_sub, bl, sce->val3, BL_CHAR, bl, sce, type, tick);
			skill_reveal_trap_inarea(bl, sce->val3, bl->x, bl->y);
		}

		if( --(sce->val2)>0 ) {
			sce->val4 += 20; // Use for Shadow Form 2 seconds checking.
			sc_timer_next(20+tick);
			return 0;
		}
		break;

	case SC_PROVOKE:
		if(sce->val4) { // Auto-provoke (it is ended in status_heal)
			sc_timer_next(1000*60+tick);
			return 0;
		}
		break;

	case SC_STONE:
		if (sc->opt1 == OPT1_STONEWAIT && sce->val4) {
			sce->val3 = 0; //Incubation time used up
			unit_stop_attack(bl);
			if (sc->data[SC_DANCING]) {
				unit_stop_walking(bl, 1);
				status_change_end(bl, SC_DANCING, INVALID_TIMER);
			}
			status_change_end(bl, SC_AETERNA, INVALID_TIMER);
			sc->opt1 = OPT1_STONE;

			std::shared_ptr<s_status_change_db> scdb = status_db.find(type);

			status_calc_state(bl,sc,scdb->state,1);
			clif_changeoption(bl);
			sc_timer_next(min(sce->val4, interval) + tick);
			sce->val4 -= interval; //Remaining time
			status_calc_bl(bl, scdb->calc_flag);
			return 0;
		}
		if (sce->val4 >= 0 && !(sce->val3) && status->hp > status->max_hp / 4) {
			status_percent_damage(NULL, bl, 1, 0, false);
		}
		break;

	case SC_POISON:
	case SC_DPOISON:
		if (sce->val4 >= 0 && !sc->data[SC_SLOWPOISON]) {
			unsigned int damage = 0;
			if (sd)
				damage = (type == SC_DPOISON) ? 2 + status->max_hp / 50 : 2 + status->max_hp * 3 / 200;
			else
				damage = (type == SC_DPOISON) ? 2 + status->max_hp / 100 : 2 + status->max_hp / 200;
			if (status->hp > umax(status->max_hp / 4, damage)) // Stop damaging after 25% HP left.
				status_zap(bl, damage, 0);
		}
		break;

	case SC_BLEEDING:
		if (sce->val4 >= 0) {
			int64 damage = rnd() % 600 + 200;
			if (!sd && damage >= status->hp)
				damage = status->hp - 1; // No deadly damage for monsters
			map_freeblock_lock();
			dounlock = true;
			status_zap(bl, damage, 0);
		}
		break;

	case SC_BURNING:
		if (sce->val4 >= 0) {
			int64 damage = 1000 + (3 * status->max_hp) / 100; // Deals fixed (1000 + 3%*MaxHP)
			map_freeblock_lock();
			dounlock = true;
			status_fix_damage(bl, bl, damage, clif_damage(bl, bl, tick, 0, 1, damage, 1, DMG_NORMAL, 0, false),0);
		}
		break;
		
	case SC_TOXIN:
		if (sce->val4 >= 0) { // Damage is every 10 seconds including 3%sp drain.
			if (sce->val3 == 1) { // Target
				map_freeblock_lock();
				dounlock = true;
				status_damage(bl, bl, 1, status->max_sp * 3 / 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 1, 1, DMG_NORMAL, 0, false), 0, 0);
			} else { // Caster
				interval = 1000; // Assign here since status_get_sc_internval() contains the target interval.

				if (status->sp < status->max_sp)
					status_heal(bl, 0, (int)status->max_sp * 1 / 100, 1);
			}
		}
		break;

	case SC_MAGICMUSHROOM:
		if (sce->val4 >= 0) {
			bool flag = 0;
			int64 damage = status->max_hp * 3 / 100;
			if (status->hp <= damage)
				damage = status->hp - 1; // Cannot Kill

			if (damage > 0) { // 3% Damage each 4 seconds
				map_freeblock_lock();
				status_zap(bl, damage, 0);
				flag = !sc->data[type]; // Killed? Should not
				map_freeblock_unlock();
			}

			if (!flag) { // Random Skill Cast
				if (magic_mushroom_db.size() > 0 && sd && !pc_issit(sd)) { // Can't cast if sit
					auto mushroom_spell = magic_mushroom_db.begin();

					std::advance(mushroom_spell, rnd() % magic_mushroom_db.size());

					uint16 mushroom_skill_id = mushroom_spell->second->skill_id;

					if (!skill_get_index(mushroom_skill_id))
						break;

					unit_stop_attack(bl);
					unit_skillcastcancel(bl, 1);

					switch (skill_get_casttype(mushroom_skill_id)) { // Magic Mushroom skills are buffs or area damage
					case CAST_GROUND:
						skill_castend_pos2(bl, bl->x, bl->y, mushroom_skill_id, 1, tick, 0);
						break;
					case CAST_NODAMAGE:
						skill_castend_nodamage_id(bl, bl, mushroom_skill_id, 1, tick, 0);
						break;
					case CAST_DAMAGE:
						skill_castend_damage_id(bl, bl, mushroom_skill_id, 1, tick, 0);
						break;
					}
				}
				clif_emotion(bl, ET_SMILE);
			}
		}
		break;
		
	case SC_PYREXIA:
		if (sce->val4 >= 0) {
			map_freeblock_lock();
			dounlock = true;
			status_fix_damage(bl, bl, 100, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, 100, 1, DMG_NORMAL, 0, false),0);
		}
		break;
		
	case SC_LEECHESEND:
		if (sce->val4 >= 0) {
			int64 damage = status->vit * (sce->val1 - 3) + (int)status->max_hp / 100; // {Target VIT x (New Poison Research Skill Level - 3)} + (Target HP/100)
			map_freeblock_lock();
			dounlock = true;
			status_fix_damage(bl, bl, damage, clif_damage(bl, bl, tick, status->amotion, status->dmotion + 500, damage, 1, DMG_NORMAL, 0, false),0);
			unit_skillcastcancel(bl, 2);
		}
		break;

	case SC_DEATHHURT:
		if (sce->val4 >= 0) {
			if (status->hp < status->max_hp)
				status_heal(bl, (int)status->max_hp * 1 / 100, 0, 1);
		}
		break;

	case SC_TENSIONRELAX:
		if(status->max_hp > status->hp && --(sce->val3) >= 0) {
			sc_timer_next(10000 + tick);
			return 0;
		}
		break;

	case SC_KNOWLEDGE:
		if (!sd) break;
		if(bl->m == sd->feel_map[0].m ||
			bl->m == sd->feel_map[1].m ||
			bl->m == sd->feel_map[2].m)
		{	// Timeout will be handled by pc_setpos
			sce->timer = INVALID_TIMER;
			return 0;
		}
		break;

	case SC_S_LIFEPOTION:
	case SC_L_LIFEPOTION:
		if( --(sce->val4) >= 0 ) {
			// val1 < 0 = per max% | val1 > 0 = exact amount
			int hp = 0;
			if( status->hp < status->max_hp )
				hp = (sce->val1 < 0) ? (int)(status->max_hp * -1 * sce->val1 / 100.) : sce->val1;
			status_heal(bl, hp, 0, 2);
			sc_timer_next((sce->val2 * 1000) + tick);
			return 0;
		}
		break;

	case SC_BOSSMAPINFO:
		if( sd && --(sce->val4) >= 0 ) {
			struct mob_data *boss_md = map_id2boss(sce->val1);

			if (boss_md) {
				if (sd->bl.m != boss_md->bl.m) // Not on same map anymore
					return 0;
				else if (boss_md->bl.prev != NULL) { // Boss is alive - Update X, Y on minimap
					sce->val2 = 0;
					clif_bossmapinfo(sd, boss_md, BOSS_INFO_ALIVE);
				} else if (boss_md->spawn_timer != INVALID_TIMER && !sce->val2) { // Boss is dead
					sce->val2 = 1;
					clif_bossmapinfo(sd, boss_md, BOSS_INFO_DEAD);
				}
			}
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_DANCING: // SP consumption by time of dancing skills
		{
			int s = 0;
			int sp = 1;
			if (--sce->val3 <= 0)
				break;
			switch(sce->val1&0xFFFF) {
#ifndef RENEWAL
				case BD_RICHMANKIM:
				case BD_DRUMBATTLEFIELD:
				case BD_RINGNIBELUNGEN:
				case BD_SIEGFRIED:
				case BA_DISSONANCE:
				case BA_ASSASSINCROSS:
				case DC_UGLYDANCE:
					s=3;
					break;
				case BD_LULLABY:
				case BD_ETERNALCHAOS:
				case BD_ROKISWEIL:
				case DC_FORTUNEKISS:
					s=4;
					break;
				case CG_HERMODE:
				case BD_INTOABYSS:
				case BA_WHISTLE:
				case DC_HUMMING:
				case BA_POEMBRAGI:
				case DC_SERVICEFORYOU:
					s=5;
					break;
				case BA_APPLEIDUN:
					s=6;
					break;
#endif
				case CG_MOONLIT:
					// Moonlit's cost is 4sp*skill_lv [Skotlex]
					sp= 4*(sce->val1>>16);
					// Upkeep is also every 10 secs.
#ifndef RENEWAL
				case DC_DONTFORGETME:
#endif
					s=10;
					break;
			}
			if( s != 0 && sce->val3 % s == 0 ) {
#ifndef RENEWAL
				if (sc->data[SC_LONGING])
					sp*= 3;
#endif
				if (!status_charge(bl, 0, sp))
					break;
			}
			sc_timer_next(1000+tick);
			return 0;
		}
		break;
	case SC_BERSERK:
		// 5% every 10 seconds [DracoRPG]
		if( --( sce->val3 ) > 0 && status_charge(bl, sce->val2, 0) && status->hp > 100 ) {
			sc_timer_next(sce->val4+tick);
			return 0;
		}
		break;

	case SC_NOCHAT:
		if(sd) {
			sd->status.manner++;
			clif_changestatus(sd,SP_MANNER,sd->status.manner);
			clif_updatestatus(sd,SP_MANNER);
			if (sd->status.manner < 0) { // Every 60 seconds your manner goes up by 1 until it gets back to 0.
				sc_timer_next(60000+tick);
				return 0;
			}
		}
		break;

	case SC_SPLASHER:
		// Custom Venom Splasher countdown timer
		// if (sce->val4 % 1000 == 0) {
		// 	char timer[10];
		// 	snprintf (timer, 10, "%d", sce->val4/1000);
		// 	clif_message(bl, timer);
		// }
		if((sce->val4 -= 500) > 0) {
			sc_timer_next(500 + tick);
			return 0;
		}
		break;

	case SC_MARIONETTE:
	case SC_MARIONETTE2:
		{
			struct block_list *pbl = map_id2bl(sce->val1);
			if( pbl && check_distance_bl(bl, pbl, 7) ) {
				sc_timer_next(1000 + tick);
				return 0;
			}
		}
		break;

	case SC_GOSPEL:
		if(sce->val4 == BCT_SELF && --(sce->val2) > 0) {
			int hp, sp;
			hp = (sce->val1 > 5) ? 45 : 30;
			sp = (sce->val1 > 5) ? 35 : 20;
			if(!status_charge(bl, hp, sp))
				break;
			sc_timer_next(10000+tick);
			return 0;
		}
		break;

	case SC_JAILED:
		if(sce->val1 == INT_MAX || --(sce->val1) > 0) {
			sc_timer_next(60000+tick);
			return 0;
		}
		break;

	case SC_BLIND:
		if(sc->data[SC_FOGWALL]) { // Blind lasts forever while you are standing on the fog.
			sc_timer_next(5000+tick);
			return 0;
		}
		break;
	case SC_ABUNDANCE:
		if(--(sce->val4) > 0) {
			status_heal(bl,0,60,0);
			sc_timer_next(10000+tick);
		}
		break;
		
	case SC_OBLIVIONCURSE:
		if( --(sce->val4) >= 0 ) {
			clif_emotion(bl,ET_QUESTION);
			sc_timer_next(3000 + tick);
			return 0;
		}
		break;

	case SC_WEAPONBLOCKING:
		if( --(sce->val4) >= 0 ) {
			if( !status_charge(bl,0,3) )
				break;
			sc_timer_next(5000+tick);
			return 0;
		}
		break;

	case SC_CLOAKINGEXCEED:
		if(!status_charge(bl,0,10-sce->val1))
			break;
		sc_timer_next(1000 + tick);
		return 0;

	case SC_RENOVATIO:
		if( --(sce->val4) >= 0 ) {
			int heal = status->max_hp * (sce->val1 + 4) / 100;
			if( sc && sc->data[SC_AKAITSUKI] && heal )
				heal = ~heal + 1;
			status_heal(bl, heal, 0, 3);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;

	case SC_SPHERE_1:
	case SC_SPHERE_2:
	case SC_SPHERE_3:
	case SC_SPHERE_4:
	case SC_SPHERE_5:
		if( --(sce->val4) >= 0 ) {
			if( !status_charge(bl, 0, 1) )
				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_FREEZE_SP:
		if( !status_charge(bl, 0, sce->val2) ) {
			int i;
			for(i = SC_SPELLBOOK1; i <= SC_MAXSPELLBOOK; i++) // Also remove stored spell as well.
				status_change_end(bl, (sc_type)i, INVALID_TIMER);
			break;
		}
		sc_timer_next(10000 + tick);
		return 0;

	case SC_ELECTRICSHOCKER:
		if( --(sce->val4) >= 0 ) {
			status_charge(bl, 0, 5 * sce->val1 * status->max_sp / 100);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_CAMOUFLAGE:
		if (!status_charge(bl, 0, 7 - sce->val1))
			break;
		if (--sce->val4 >= 0)
			sce->val3++;
		sc_timer_next(1000 + tick);
		return 0;

	case SC__REPRODUCE:
		if(!status_charge(bl, 0, 1))
			break;
		sc_timer_next(1000+tick);
		return 0;

	case SC__SHADOWFORM:
		if( --(sce->val4) >= 0 ) {
			if( !status_charge(bl, 0, 11 - sce->val1) )
				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC__INVISIBILITY:
		if( !status_charge(bl, 0, (12 - 2 * sce->val1) * status->max_sp / 100) ) // 6% - skill_lv.
			break;
		sc_timer_next(1000 + tick);
		return 0;

	case SC_STRIKING:
		if( --(sce->val4) >= 0 ) {
			if( !status_charge(bl,0, sce->val3 ) )
				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_WARMER: {
			int hp = 0;
			struct status_change *ssc = status_get_sc(map_id2bl(sce->val2));

			if (ssc && ssc->data[SC_HEATER_OPTION])
				hp = status->max_hp * 3 * sce->val1 / 100;
			else
				hp = status->max_hp * sce->val1 / 100;
			if (sc && sc->data[SC_AKAITSUKI] && hp)
				hp = ~hp + 1;
			if (status->hp != status->max_hp)
				status_heal(bl, hp, 0, 0);
			sc_timer_next(3000 + tick);
			return 0;
		}

	case SC_HELLS_PLANT:
		if( sce->val4 >= 0 ){
			skill_castend_damage_id( bl, bl, GN_HELLS_PLANT_ATK, sce->val1, tick, 0 );
		}
		break;

	case SC_VOICEOFSIREN:
		if( --(sce->val4) >= 0 ) {
			clif_emotion(bl,ET_THROB);
			sc_timer_next(2000 + tick);
			return 0;
		}
		break;

	case SC_DEEPSLEEP:
		if( --(sce->val4) >= 0 ) { // Recovers 3% HP/SP every 2 seconds.
			status_heal(bl, status->max_hp * 3 / 100, status->max_sp * 3 / 100, 2);
			sc_timer_next(2000 + tick);
			return 0;
		}
		break;

	case SC_SATURDAYNIGHTFEVER:
		// 1% HP/SP drain every val4 seconds [Jobbie]
		if( --(sce->val3) >= 0 ) {
			if( !status_charge(bl, status->hp / 100, status->sp / 100) )
				break;
			sc_timer_next(sce->val4+tick);
			return 0;
		}
		break;

	case SC_CRYSTALIZE:
		if( --(sce->val4) >= 0 ) { // Drains 2% of HP and 1% of SP every seconds.
			if (!status_charge(bl, status->max_hp * 2 / 100, status->max_sp / 100))
				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_FORCEOFVANGUARD:
		if( !status_charge(bl,0,24 - 4 * sce->val1) )
			break;
		sc_timer_next(10000 + tick);
		return 0;

	case SC_BANDING:
		if( status_charge(bl, 0, 7 - sce->val1) ) {
			sce->val2 = (sd ? skill_banding_count(sd) : 1);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;

	case SC_REFLECTDAMAGE:
		if( --(sce->val4) > 0 ) {
			if( !status_charge(bl,0,10) )
 				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_OVERHEAT_LIMITPOINT:
		if (--(sce->val1) >= 0) { // Cooling
			static std::vector<int16> limit = { 150, 200, 280, 360, 450 };
			uint16 skill_lv = (sd ? cap_value(pc_checkskill(sd, NC_MAINFRAME), 0, (uint16)(limit.size()-1)) : 0);

			if (sc && sc->data[SC_OVERHEAT])
				status_change_end(bl,SC_OVERHEAT,INVALID_TIMER);
			if (sce->val1 > limit[skill_lv])
				sc_start(bl, bl, SC_OVERHEAT, 100, sce->val1, 1000);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;

	case SC_OVERHEAT: {
			uint32 damage = status->max_hp / 100; // Suggestion 1% each second

			if (damage >= status->hp)
				damage = status->hp - 1; // Do not kill, just keep you with 1 hp minimum
			map_freeblock_lock();
			status_fix_damage(NULL, bl, damage, clif_damage(bl, bl, tick, 0, 0, damage, 0, DMG_NORMAL, 0, false),0);
			if (sc->data[type]) {
				sc_timer_next(1000 + tick);
			}
			map_freeblock_unlock();
			return 0;
		}
		break;

	case SC_MAGNETICFIELD:
		if (--(sce->val4) >= 0) {
			struct block_list *src = map_id2bl(sce->val2);

			if (!src || (src && (status_isdead(src) || src->m != bl->m)))
				break;
			map_freeblock_lock();
			if (!status_charge(bl, 0, 50))
				status_zap(bl, 0, status->sp);
			if (sc->data[type])
				sc_timer_next(1000 + tick);
			map_freeblock_unlock();
			return 0;
		}
		break;

	case SC_INSPIRATION:
		if(--(sce->val4) >= 0) {
			if (!status_charge(bl, status->max_hp * (35 - 5 * sce->val1) / 1000, status->max_sp * (45 - 5 * sce->val1) / 1000))
				break;

			sc_timer_next(5000+tick);
			return 0;
		}
		break;

	case SC_SHIELDSPELL_HP:
		if( sce->val4 >= 0 && status->hp < status->max_hp ){
			status_heal( bl, status->max_hp * sce->val2 / 100, 0, 1 );
		}
		break;

	case SC_SHIELDSPELL_SP:
		if( sce->val4 >= 0 && status->sp < status->max_sp ){
			status_heal( bl, 0, status->max_sp * sce->val2 / 100, 1 );
		}
		break;

	case SC_TROPIC:
	case SC_CHILLY_AIR:
	case SC_WILD_STORM:
	case SC_UPHEAVAL:
	case SC_HEATER:
	case SC_COOLER:
	case SC_BLAST:
	case SC_CURSED_SOIL:
	case SC_PYROTECHNIC:
	case SC_AQUAPLAY:
	case SC_GUST:
	case SC_PETROLOGY:
	case SC_CIRCLE_OF_FIRE:
	case SC_FIRE_CLOAK:
	case SC_WATER_DROP:
	case SC_WATER_SCREEN:
	case SC_WIND_CURTAIN:
	case SC_WIND_STEP:
	case SC_STONE_SHIELD:
	case SC_SOLID_SKIN:
	case SC_FLAMETECHNIC:
	case SC_FLAMEARMOR:
	case SC_COLD_FORCE:
	case SC_CRYSTAL_ARMOR:
	case SC_GRACE_BREEZE:
	case SC_EYES_OF_STORM:
	case SC_EARTH_CARE:
	case SC_STRONG_PROTECTION:
	case SC_DEEP_POISONING:
	case SC_POISON_SHIELD:
		if( !status_charge(bl,0,sce->val2) ) {
			struct block_list *s_bl = battle_get_master(bl);
			if (bl->type == BL_ELEM)
				elemental_change_mode(BL_CAST(BL_ELEM, bl), EL_MODE_PASSIVE);
			if( s_bl )
				status_change_end(s_bl,static_cast<sc_type>(type+1),INVALID_TIMER);
			status_change_end(bl,type,INVALID_TIMER);
			break;
		}
		sc_timer_next(sce->val3 + tick);
		return 0;

	case SC_WATER_SCREEN_OPTION:
		status_heal(bl,1000,0,2);
		sc_timer_next(10000 + tick);
		return 0;

	case SC_TEARGAS:
		if( --(sce->val4) >= 0 ) {
			struct block_list *src = map_id2bl(sce->val3);
			int damage = sce->val2;

			map_freeblock_lock();
			clif_damage(bl, bl, tick, 0, 0, damage, 1, DMG_MULTI_HIT_ENDURE, 0, false);
			status_damage(src, bl, damage,0, 0, 1, 0);
			if( sc->data[type] ) {
				sc_timer_next(2000 + tick);
			}
			map_freeblock_unlock();
			return 0;
		}
		break;
	case SC_TEARGAS_SOB:
		if( --(sce->val4) >= 0 ) {
			clif_emotion(bl, ET_CRY);
			sc_timer_next(3000 + tick);
			return 0;
		}
		break;
	case SC_STOMACHACHE:
		if( --(sce->val4) >= 0 ) {
			status_charge(bl,0,sce->val2);	// Reduce 8 every 10 seconds.
			if( sd && !pc_issit(sd) ) { // Force to sit every 10 seconds.
				pc_setsit(sd);
				skill_sit(sd, true);
				clif_sitting(bl);
			}
			sc_timer_next(10000 + tick);
			return 0;
		}
		break;
	case SC_LEADERSHIP:
	case SC_GLORYWOUNDS:
	case SC_SOULCOLD:
	case SC_HAWKEYES:
		// They only end by status_change_end
		sc_timer_next(600000 + tick);
		return 0;
	case SC_MEIKYOUSISUI:
		if( --(sce->val4) >= 0 ) {
			status_heal(bl, status->max_hp * sce->val2 / 100, status->max_sp * sce->val3 / 100, 0);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_KAGEMUSYA:
		if( --(sce->val4) >= 0 ) {
			if(!status_charge(bl, 0, 1)) break;
			sc_timer_next(1000+tick);
			return 0;
		}
		break;
	case SC_ANGRIFFS_MODUS:
		if(--(sce->val4) >= 0) { // Drain hp/sp
			if( !status_charge(bl,100,20) ) break;
			sc_timer_next(1000+tick);
			return 0;
		}
		break;
	case SC_CBC:
		if(--(sce->val4) >= 0) { // Drain hp/sp
			int hp=0;
			int sp = (status->max_sp * sce->val3) / 100;
			if(bl->type == BL_MOB) hp = sp*10;
			if( !status_charge(bl,hp,sp) )break;
			sc_timer_next(1000+tick);
			return 0;
		}
		break;
	case SC_FULL_THROTTLE:
		if( --(sce->val4) >= 0 ) {
			status_percent_damage(bl, bl, 0, sce->val2, false);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_REBOUND:
		if( --(sce->val4) >= 0 ) {
			clif_emotion(bl, ET_SWEAT);
			sc_timer_next(2000 + tick);
			return 0;
		}
		break;
	case SC_KINGS_GRACE:
		if( --(sce->val4) >= 0 ) {
			status_percent_heal(bl, sce->val2, 0);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_FRIGG_SONG:
		if( --(sce->val4) >= 0 ) {
			status_heal(bl, sce->val3, 0, 0);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_C_MARKER:
		if( --(sce->val4) >= 0 ) {
			TBL_PC *caster = map_id2sd(sce->val2);
			if (!caster || caster->bl.m != bl->m) //End the SC if caster isn't in same map
				break;
			sc_timer_next(1000 + tick);
			clif_crimson_marker(caster, bl, false);
			return 0;
		}
		break;
	case SC_STEALTHFIELD_MASTER:
		if (--(sce->val4) >= 0) {
			if (!status_charge(bl, 0, status->max_sp * 3 / 100))
				break;
			sc_timer_next(sce->val3 + tick);
			return 0;
		}
		break;
	case SC_VACUUM_EXTREME:
		if (sce->val4 > 0) {
			// Only slide targets to center if they are standing still
			if (unit_bl2ud(bl)->walktimer == INVALID_TIMER) {
				uint16 x = sce->val3 >> 16, y = sce->val3 & 0xFFFF;

				if (distance_xy(x, y, bl->x, bl->y) <= skill_get_unit_range(SO_VACUUM_EXTREME, sce->val1) && unit_movepos(bl, x, y, 0, false)) {
					clif_slide(bl, x, y);
					clif_fixpos(bl);
				}
			}
			sc_timer_next(tick + sce->val4);
			sce->val4 = 0;
		}
		break;
	case SC_FIRE_INSIGNIA:
		if (--(sce->val4) >= 0) {
			if (status->def_ele == ELE_FIRE)
				status_heal(bl, status->max_hp / 100, 0, 1);
			else if (status->def_ele == ELE_EARTH)
				status_zap(bl, status->max_hp / 100, 0);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;

	case SC_WATER_INSIGNIA:
		if (--(sce->val4) >= 0) {
			if (status->def_ele == ELE_WATER)
				status_heal(bl, status->max_hp / 100, 0, 1);
			else if (status->def_ele == ELE_FIRE)
				status_zap(bl, status->max_hp / 100, 0);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;

	case SC_WIND_INSIGNIA:
		if (--(sce->val4) >= 0) {
			if (status->def_ele == ELE_WIND)
				status_heal(bl, status->max_hp / 100, 0, 1);
			else if (status->def_ele == ELE_WATER)
				status_zap(bl, status->max_hp / 100, 0);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;

	case SC_EARTH_INSIGNIA:
		if (--(sce->val4) >= 0) {
			if (status->def_ele == ELE_EARTH)
				status_heal(bl, status->max_hp / 100, 0, 1);
			else if (status->def_ele == ELE_WIND)
				status_zap(bl, status->max_hp / 100, 0);
			sc_timer_next(5000 + tick);
			return 0;
		}
		break;
	case SC_BITESCAR:
		if (--(sce->val4) >= 0) {
			status_percent_damage(bl, bl, -(sce->val2), 0, 0);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_FRESHSHRIMP:
		if (--(sce->val4) >= 0) {
			status_heal(bl, sce->val2, 0, 3);
			sc_timer_next((10000 - ((sce->val1 - 1) * 1000)) + tick);
			return 0;
		}
		break;
	case SC_DORAM_BUF_01:
		if( sd && --(sce->val4) >= 0 ) {
			if( status->hp < status->max_hp )
				status_heal(bl, 10, 0, 2);
			sc_timer_next(10000 + tick);
			return 0;
		}
		break;
	case SC_DORAM_BUF_02:
		if( sd && --(sce->val4) >= 0 ) {
			if( status->sp < status->max_sp )
				status_heal(bl, 0, 5, 2);
			sc_timer_next(10000 + tick);
			return 0;
		}
		break;
	case SC_NEWMOON:
		if (--(sce->val4) >= 0) {
			if (!status_charge(bl, 0, 1))
				break;
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_CREATINGSTAR:
		if (--(sce->val4) >= 0) { // Needed to check who the caster is and what AoE is giving the status.
			struct block_list *star_caster = map_id2bl(sce->val2);
			struct skill_unit *star_aoe = (struct skill_unit *)map_id2bl(sce->val3);

			if (star_caster == nullptr || status_isdead(star_caster) || star_caster->m != bl->m || star_aoe == nullptr)
				break;

			sc_timer_next(500 + tick);

			// Attack after timer to prevent errors
			skill_attack(BF_WEAPON, star_caster, &star_aoe->bl, bl, SJ_BOOKOFCREATINGSTAR, sce->val1, tick, 0);
			return 0;
		}
		break;
	case SC_SOULUNITY:
		if (--(sce->val4) >= 0) { // Needed to check the caster's location for the range check.
			struct block_list *unity_src = map_id2bl(sce->val2);

			if (!unity_src || status_isdead(unity_src) || unity_src->m != bl->m || !check_distance_bl(bl, unity_src, 11))
				break;

			status_heal(bl, 150 * sce->val1, 0, 2);
			sc_timer_next(3000 + tick);
			return 0;
		}
		break;
	case SC_SOULCOLLECT:
		pc_addsoulball(sd, sce->val2);
		if (sd->soulball < sce->val2) {
			sc_timer_next(sce->val3 + tick);
			return 0;
		}
		break;
	case SC_HELPANGEL:
		if (--(sce->val4) >= 0) {
			status_heal(bl, 1000, 350, 2);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_BURNT:
		if( --(sce->val4) >= 0 ) {
			int damage = 2000;

			if( damage >= status->hp )
				damage = status->hp - 1;
			map_freeblock_lock();
			status_zap(bl,damage,0);
			if( sc->data[type] ) {
				sc_timer_next(1000 + tick);
			}
			map_freeblock_unlock();
			return 0;
		}
		break;
	case SC_MEDIALE:
		if (--(sce->val4) >= 0) {
			clif_specialeffect(bl, 1808, AREA);
			skill_castend_nodamage_id(bl, bl, CD_MEDIALE_VOTUM, sce->val1, tick, 1);
			sc_timer_next(2000 + tick);
			return 0;
		}
		break;

	case SC_DANCING_KNIFE:
		if (--(sce->val4) >= 0) {
			skill_castend_nodamage_id(bl, bl, SHC_DANCING_KNIFE, sce->val1, tick, 1);
			sc_timer_next(300 + tick);
			return 0;
		}
		break;

	case SC_A_MACHINE:
		if (--(sce->val4) >= 0) {
			skill_castend_nodamage_id(bl, bl, MT_A_MACHINE, sce->val1, tick, 1);
			sc_timer_next(1000 + tick);
			return 0;
		}
		break;
	case SC_SERVANTWEAPON:
		if (sce->val4 >= 0) {
			if( sd && sd->servantball < MAX_SERVANTBALL ){
				pc_addservantball( *sd, MAX_SERVANTBALL );
			}
			interval = max(500, skill_get_time2(DK_SERVANTWEAPON, sce->val1));
			map_freeblock_lock();
			dounlock = true;
		}
		break;
	case SC_ABYSSFORCEWEAPON:
		if (sce->val4 >= 0) {
			if( sd && sd->abyssball < MAX_ABYSSBALL ){
				pc_addabyssball( *sd );
			}
			interval = max(500, skill_get_time2(ABC_FROM_THE_ABYSS, sce->val1));
			map_freeblock_lock();
			dounlock = true;
		}
		break;
	}

	// If status has an interval and there is at least 100ms remaining time, wait for next interval
	if(interval > 0 && sc->data[type] && sce->val4 >= 100) {
		sc_timer_next(min(sce->val4,interval)+tick);
		sce->val4 -= interval;
		if (dounlock)
			map_freeblock_unlock();
		return 0;
	}

	if (dounlock)
		map_freeblock_unlock();

	// Default for all non-handled control paths is to end the status
	return status_change_end( bl,type,tid );
}

/**
 * For each iteration of repetitive status
 * @param bl: Object [PC|MOB|HOM|MER|ELEM]
 * @param ap: va_list arguments (src, sce, type, tick)
 */
int status_change_timer_sub(struct block_list* bl, va_list ap)
{
	struct status_change* tsc;

	struct block_list* src = va_arg(ap,struct block_list*);
	struct status_change_entry* sce = va_arg(ap,struct status_change_entry*);
	enum sc_type type = (sc_type)va_arg(ap,int); // gcc: enum args get promoted to int
	t_tick tick = va_arg(ap,t_tick);

	if (status_isdead(bl))
		return 0;

	tsc = status_get_sc(bl);

	switch( type ) {
	case SC_SIGHT: // Reveal hidden ennemy on 3*3 range
	case SC_CONCENTRATE:
		status_change_end(bl, SC_HIDING, INVALID_TIMER);
		status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
		status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
		status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
		status_change_end(bl, SC_NEWMOON, INVALID_TIMER);
		if (tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 > 0 && sce->val4%2000 == 0) && // For every 2 seconds do the checking
			rnd()%100 < 100 - tsc->data[SC__SHADOWFORM]->val1 * 10) // [100 - (Skill Level x 10)] %
				status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
		break;
	case SC_RUWACH: // Reveal hidden target and deal little dammages if enemy
		if (tsc && (tsc->data[SC_HIDING] || tsc->data[SC_CLOAKING] ||
				tsc->data[SC_CAMOUFLAGE] || tsc->data[SC_NEWMOON] || tsc->data[SC_CLOAKINGEXCEED])) {
			status_change_end(bl, SC_HIDING, INVALID_TIMER);
			status_change_end(bl, SC_CLOAKING, INVALID_TIMER);
			status_change_end(bl, SC_CAMOUFLAGE, INVALID_TIMER);
			status_change_end(bl, SC_CLOAKINGEXCEED, INVALID_TIMER);
			status_change_end(bl, SC_NEWMOON, INVALID_TIMER);
			if(battle_check_target( src, bl, BCT_ENEMY ) > 0)
				skill_attack(BF_MAGIC,src,src,bl,AL_RUWACH,1,tick,0);
		}
		if (tsc && tsc->data[SC__SHADOWFORM] && (sce && sce->val4 > 0 && sce->val4%2000 == 0) && // For every 2 seconds do the checking
			rnd()%100 < 100 - tsc->data[SC__SHADOWFORM]->val1 * 10 ) { // [100 - (Skill Level x 10)] %
				status_change_end(bl, SC__SHADOWFORM, INVALID_TIMER);
				if (battle_check_target(src, bl, BCT_ENEMY) > 0)
					skill_attack(BF_MAGIC, src, src, bl, status_db.getSkill(type), 1, tick, 0);
		}
		break;
	case SC_SIGHTBLASTER:
		if (battle_check_target( src, bl, BCT_ENEMY ) > 0 &&
			status_check_skilluse(src, bl, WZ_SIGHTBLASTER, 2))
		{
			if (sce) {
				struct skill_unit *su = NULL; 
				if(bl->type == BL_SKILL)
					su = (struct skill_unit *)bl;
				if (skill_attack(BF_MAGIC,src,src,bl,WZ_SIGHTBLASTER,sce->val1,tick,0x1000000)
					&& (!su || !su->group || !skill_get_inf2(su->group->skill_id, INF2_ISTRAP))) { // The hit is not counted if it's against a trap
					sce->val2 = 0; // This signals it to end.
				} else if((bl->type&BL_SKILL) && sce->val4%2 == 0) {
					//Remove trap immunity temporarily so it triggers if you still stand on it
					sce->val4++;
				}
			}
		}
		break;
	case SC_CLOSECONFINE:
		// Lock char has released the hold on everyone...
		if (tsc && tsc->data[SC_CLOSECONFINE2] && tsc->data[SC_CLOSECONFINE2]->val2 == src->id) {
			tsc->data[SC_CLOSECONFINE2]->val2 = 0;
			status_change_end(bl, SC_CLOSECONFINE2, INVALID_TIMER);
		}
		break;
	case SC_CURSEDCIRCLE_TARGET:
		if( tsc && tsc->data[SC_CURSEDCIRCLE_TARGET] && tsc->data[SC_CURSEDCIRCLE_TARGET]->val2 == src->id ) {
			clif_bladestop(bl, tsc->data[SC_CURSEDCIRCLE_TARGET]->val2, 0);
			status_change_end(bl, type, INVALID_TIMER);
		}
		break;
	}

	return 0;
}

/**
 * Clears buffs/debuffs on an object
 * @param bl: Object to clear [PC|MOB|HOM|MER|ELEM]
 * @param type: Type to remove
 *  SCCB_BUFFS: Clear Buffs
 *  SCCB_DEBUFFS: Clear Debuffs
 *  SCCB_REFRESH: Clear specific debuffs through RK_REFRESH
 *  SCCB_CHEM_PROTECT: Clear AM_CP_ARMOR/HELM/SHIELD/WEAPON
 *  SCCB_LUXANIMA: Bonus Script removed through RK_LUXANIMA
 */
void status_change_clear_buffs(struct block_list* bl, uint8 type)
{
	struct status_change *sc= status_get_sc(bl);

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

	//Clears buffs with specified flag and type
	for (const auto &it : status_db) {
		sc_type status = static_cast<sc_type>(it.first);
		e_scb_flag flag = static_cast<e_scb_flag>(it.second->calc_flag);

		if (!sc->data[status] || flag&SCF_NOCLEARBUFF) //Skip status with SCF_NOCLEARBUFF, no matter what
			continue;
		// &SCCB_LUXANIMA : Cleared by RK_LUXANIMA
		if (!(type&SCCB_LUXANIMA) && flag&SCF_REMOVEONLUXANIMA)
			continue;
		// &SCCB_CHEM_PROTECT : Clears AM_CP_ARMOR/HELP/SHIELD/WEAPON
		if (!(type&SCCB_CHEM_PROTECT) && flag&SCF_REMOVECHEMICALPROTECT)
			continue;
		// &SCCB_REFRESH : Cleared by RK_REFRESH
		if (!(type&SCCB_REFRESH) && flag&SCF_REMOVEONREFRESH)
			continue;
		// &SCCB_DEBUFFS : Clears debuffs
		if (!(type&SCCB_DEBUFFS) && flag&SCF_DEBUFF)
			continue;
		// &SCCB_BUFFS : Clears buffs
		if (!(type&SCCB_BUFFS) && !(flag&SCF_DEBUFF))
			continue;		
		if (status == SC_SATURDAYNIGHTFEVER || status == SC_BERSERK) // Mark to not lose HP
			sc->data[status]->val2 = 0;
		status_change_end(bl, status, INVALID_TIMER);
	}

	//Removes bonus_script
	if (bl->type == BL_PC) {
		uint32 i = 0;

		if (type&SCCB_BUFFS)    i |= BSF_REM_BUFF;
		if (type&SCCB_DEBUFFS)  i |= BSF_REM_DEBUFF;
		if (type&SCCB_REFRESH)  i |= BSF_REM_ON_REFRESH;
		if (type&SCCB_LUXANIMA) i |= BSF_REM_ON_LUXANIMA;
		pc_bonus_script_clear(BL_CAST(BL_PC,bl),i);
	}

	// Cleaning all extras vars
	sc->comet_x = 0;
	sc->comet_y = 0;
#ifndef RENEWAL
	sc->sg_counter = 0;
#endif

	return;
}

/**
 * Infect a user with status effects (SC_DEADLYINFECT)
 * @param src: Object initiating change on bl [PC|MOB|HOM|MER|ELEM]
 * @param bl: Object to change
 * @return 1: Success 0: Fail
 */
int status_change_spread(block_list *src, block_list *bl)
{
	if (src == nullptr || bl == nullptr)
		return 0;

	// Status Immunity resistance
	if (status_bl_has_mode(src, MD_STATUSIMMUNE) || status_bl_has_mode(bl, MD_STATUSIMMUNE))
		return 0;

	status_change *sc = status_get_sc(src);

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

	bool hasSpread = false;
	t_tick tick = gettick(), sc_tick;

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

		if (sc->data[type] && it.second->flag[SCF_SPREADEFFECT]) {
			if (sc->data[type]->timer != INVALID_TIMER) {
				timer = get_timer(sc->data[type]->timer);

				if (timer == nullptr || timer->func != status_change_timer || DIFF_TICK(timer->tick, tick) < 0)
					continue;

				int32 val4 = sc->data[type]->val4;

				sc_tick = DIFF_TICK(timer->tick, tick) + (val4 > 0 ? val4 : 0);
			} else
				sc_tick = INFINITE_TICK;

			status_change_start(src, bl, type, 10000, sc->data[type]->val1, sc->data[type]->val2, sc->data[type]->val3, sc->data[type]->val4, sc_tick, SCSTART_NOAVOID | SCSTART_NOTICKDEF | SCSTART_NORATEDEF);

			if (!hasSpread)
				hasSpread = true;
		}
	}

	return hasSpread;
}

/**
 * Applying natural heal bonuses (sit, skill, homun, etc...)
 * TODO: the va_list doesn't seem to be used, safe to remove?
 * @param bl: Object applying bonuses to [PC|HOM|MER|ELEM]
 * @param args: va_list arguments
 * @return which regeneration bonuses have been applied (flag)
 */
static t_tick natural_heal_prev_tick,natural_heal_diff_tick;
static int status_natural_heal(struct block_list* bl, va_list args)
{
	struct regen_data *regen;
	struct status_data *status;
	struct status_change *sc;
	struct unit_data *ud;
	struct view_data *vd = NULL;
	struct regen_data_sub *sregen;
	struct map_session_data *sd;
	int rate, multi = 1, flag;

	regen = status_get_regen_data(bl);
	if (!regen)
		return 0;
	status = status_get_status_data(bl);
	sc = status_get_sc(bl);
	if (sc && !sc->count)
		sc = NULL;
	sd = BL_CAST(BL_PC,bl);

	flag = regen->flag;
	if (flag&RGN_HP && (status->hp >= status->max_hp || regen->state.block&1))
		flag &= ~(RGN_HP|RGN_SHP);
	if (flag&RGN_SP && (status->sp >= status->max_sp || regen->state.block&2))
		flag &= ~(RGN_SP|RGN_SSP);

	if (flag && (
		status_isdead(bl) ||
		(sc && (sc->option&(OPTION_HIDE|OPTION_CLOAK|OPTION_CHASEWALK) || sc->data[SC__INVISIBILITY]))
	))
		flag = RGN_NONE;

	if (sd) {
		if (sd->hp_loss.value || sd->sp_loss.value)
			pc_bleeding(sd, natural_heal_diff_tick);
		if (sd->hp_regen.value || sd->sp_regen.value || sd->percent_hp_regen.value || sd->percent_sp_regen.value)
			pc_regen(sd, natural_heal_diff_tick);
	}

	if(flag&(RGN_SHP|RGN_SSP) && regen->ssregen &&
		(vd = status_get_viewdata(bl)) && vd->dead_sit == 2)
	{ // Apply sitting regen bonus.
		sregen = regen->ssregen;
		if(flag&(RGN_SHP)) { // Sitting HP regen
			rate = (int)(natural_heal_diff_tick * (sregen->rate.hp / 100.));
			if (regen->state.overweight)
				rate >>= 1; // Half as fast when overweight.
			sregen->tick.hp += rate;
			while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval) {
				sregen->tick.hp -= battle_config.natural_heal_skill_interval;
				if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp) { // Full
					flag &= ~(RGN_HP|RGN_SHP);
					break;
				}
			}
		}
		if(flag&(RGN_SSP)) { // Sitting SP regen
			rate = (int)(natural_heal_diff_tick * (sregen->rate.sp / 100.));
			if (regen->state.overweight)
				rate >>= 1; // Half as fast when overweight.
			sregen->tick.sp += rate;
			while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval) {
				sregen->tick.sp -= battle_config.natural_heal_skill_interval;
				if(status_heal(bl, 0, sregen->sp, 3) < sregen->sp) { // Full
					flag &= ~(RGN_SP|RGN_SSP);
					break;
				}
			}
		}
	}

	if (flag && regen->state.overweight)
		flag = RGN_NONE;

	ud = unit_bl2ud(bl);

	if (flag&(RGN_HP|RGN_SHP|RGN_SSP) && ud && ud->walktimer != INVALID_TIMER) {
		flag &= ~(RGN_SHP|RGN_SSP);
		if(!regen->state.walk)
			flag &= ~RGN_HP;
	}

	if (!flag)
		return 0;

	if (flag&(RGN_HP|RGN_SP)) {
		if(!vd)
			vd = status_get_viewdata(bl);
		if(vd && vd->dead_sit == 2)
			multi += 1; //This causes the interval to be halved
		if(regen->state.gc)
			multi += 1; //This causes the interval to be halved
	}

	// Natural Hp regen
	if (flag&RGN_HP) {
		rate = (int)(natural_heal_diff_tick * (regen->rate.hp/100. * multi));
		if (ud && ud->walktimer != INVALID_TIMER)
			rate /= 2;
		// Homun HP regen fix (they should regen as if they were sitting (twice as fast)
		if(bl->type == BL_HOM)
			rate *= 2;

		regen->tick.hp += rate;

		if(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval) {
			int val = 0;
			do {
				val += regen->hp;
				regen->tick.hp -= battle_config.natural_healhp_interval;
			} while(regen->tick.hp >= (unsigned int)battle_config.natural_healhp_interval);
			if (status_heal(bl, val, 0, 1) < val)
				flag &= ~RGN_SHP; // Full.
		}
	}

	// Natural SP regen
	if(flag&RGN_SP) {
		rate = (int)(natural_heal_diff_tick * (regen->rate.sp/100. * multi));
		// Homun SP regen fix (they should regen as if they were sitting (twice as fast)
		if(bl->type==BL_HOM)
			rate *= 2;
#ifdef RENEWAL
		if (bl->type == BL_PC && (((TBL_PC*)bl)->class_&MAPID_UPPERMASK) == MAPID_MONK &&
			sc && sc->data[SC_EXPLOSIONSPIRITS] && (!sc->data[SC_SPIRIT] || sc->data[SC_SPIRIT]->val2 != SL_MONK))
			rate /= 2; // Tick is doubled in Fury state
#endif
		regen->tick.sp += rate;

		if(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval) {
			int val = 0;
			do {
				val += regen->sp;
				regen->tick.sp -= battle_config.natural_healsp_interval;
			} while(regen->tick.sp >= (unsigned int)battle_config.natural_healsp_interval);
			if (status_heal(bl, 0, val, 1) < val)
				flag &= ~RGN_SSP; // full.
		}
	}

	if (!regen->sregen)
		return flag;

	// Skill regen
	sregen = regen->sregen;

	if(flag&RGN_SHP) { // Skill HP regen
		sregen->tick.hp += (int)(natural_heal_diff_tick * (sregen->rate.hp / 100.));

		while(sregen->tick.hp >= (unsigned int)battle_config.natural_heal_skill_interval) {
			sregen->tick.hp -= battle_config.natural_heal_skill_interval;
			if(status_heal(bl, sregen->hp, 0, 3) < sregen->hp)
				break; // Full
		}
	}
	if(flag&RGN_SSP) { // Skill SP regen
		sregen->tick.sp += (int)(natural_heal_diff_tick * (sregen->rate.sp /100.));
		while(sregen->tick.sp >= (unsigned int)battle_config.natural_heal_skill_interval) {
			int val = sregen->sp;
			if (sd && sd->state.doridori) {
				val *= 2;
				sd->state.doridori = 0;
				if ((rate = pc_checkskill(sd,TK_SPTIME)))
					sc_start(bl,bl,skill_get_sc(TK_SPTIME),
						100,rate,skill_get_time(TK_SPTIME, rate));
				if (
					(sd->class_&MAPID_UPPERMASK) == MAPID_STAR_GLADIATOR &&
					rnd()%10000 < battle_config.sg_angel_skill_ratio
				) { // Angel of the Sun/Moon/Star
					clif_feel_hate_reset(sd);
					pc_resethate(sd);
					pc_resetfeel(sd);
				}
			}
			sregen->tick.sp -= battle_config.natural_heal_skill_interval;
			if(status_heal(bl, 0, val, 3) < val)
				break; // Full
		}
	}
	return flag;
}

/**
 * Natural heal main timer
 * @param tid: Timer ID
 * @param tick: Current tick (time)
 * @param id: Object ID to heal
 * @param data: data pushed through timer function
 * @return 0
 */
static TIMER_FUNC(status_natural_heal_timer){
	natural_heal_diff_tick = DIFF_TICK(tick,natural_heal_prev_tick);
	map_foreachregen(status_natural_heal);
	natural_heal_prev_tick = tick;
	return 0;
}

/**
 * Check if status is disabled on a map
 * @param type: Status Change data
 * @param mapIsVS: If the map is a map_flag_vs type
 * @param mapisPVP: If the map is a PvP type
 * @param mapIsGVG: If the map is a map_flag_gvg type
 * @param mapIsBG: If the map is a Battleground type
 * @param mapZone: Map Zone type
 * @param mapIsTE: If the map us WOE TE
 * @return True - SC disabled on map; False - SC not disabled on map/Invalid SC
 */
static bool status_change_isDisabledOnMap_(sc_type type, bool mapIsVS, bool mapIsPVP, bool mapIsGVG, bool mapIsBG, unsigned int mapZone, bool mapIsTE)
{
	if (!status_db.validateStatus(type))
		return false;

	if ((!mapIsVS && SCDisabled[type]&1) ||
		(mapIsPVP && SCDisabled[type]&2) ||
		(mapIsGVG && SCDisabled[type]&4) ||
		(mapIsBG && SCDisabled[type]&8) ||
		(mapIsTE && SCDisabled[type]&16) ||
		(SCDisabled[type]&(mapZone)))
	{
		return true;
	}

	return false;
}

/**
 * Clear a status if it is disabled on a map
 * @param bl: Block list data
 * @param sc: Status Change data
 */
void status_change_clear_onChangeMap(struct block_list *bl, struct status_change *sc)
{
	nullpo_retv(bl);

	if (sc && sc->count) {
		struct map_data *mapdata = map_getmapdata(bl->m);
		bool mapIsVS = mapdata_flag_vs2(mapdata);
		bool mapIsPVP = mapdata->flag[MF_PVP] != 0;
		bool mapIsGVG = mapdata_flag_gvg2_no_te(mapdata);
		bool mapIsBG = mapdata->flag[MF_BATTLEGROUND] != 0;
		bool mapIsTE = mapdata_flag_gvg2_te(mapdata);

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

			if (!sc->data[type] || !SCDisabled[type])
				continue;

			if (status_change_isDisabledOnMap_(type, mapIsVS, mapIsPVP, mapIsGVG, mapIsBG, mapdata->zone, mapIsTE))
				status_change_end(bl, type, INVALID_TIMER);
		}
	}
}

/**
 * Read status_disabled.txt file
 * @param str: Fields passed from sv_readdb
 * @param columns: Columns passed from sv_readdb function call
 * @param current: Current row being read into SCDisabled array
 * @return True - Successfully stored, False - Invalid SC
 */
static bool status_readdb_status_disabled(char **str, int columns, int current)
{
	int64 type = SC_NONE;

	if (ISDIGIT(str[0][0]))
		type = atoi(str[0]);
	else {
		if (!script_get_constant(str[0],&type))
			type = SC_NONE;
	}

	if (type <= SC_NONE || type >= SC_MAX) {
		ShowError("status_readdb_status_disabled: Invalid SC with type %s.\n", str[0]);
		return false;
	}

	SCDisabled[type] = (unsigned int)atol(str[1]);
	return true;
}

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

/**
 * Reads and parses an entry from the attr_fix.
 * @param node: YAML node containing the entry.
 * @return count of successfully parsed rows
 */
uint64 AttributeDatabase::parseBodyNode(const YAML::Node &node) {
	uint16 level;

	if (!this->asUInt16(node, "Level", level))
		return 0;

	if (!CHK_ELEMENT_LEVEL(level)) {
		this->invalidWarning(node["Level"], "Invalid element level %hu.\n", level);
		return 0;
	}

	for (const auto &itatk : um_eleid2elename) {
		if (!this->nodeExists(node, itatk.first))
			continue;

		const YAML::Node &eleNode = node[itatk.first];

		for (const auto &itdef : um_eleid2elename) {
			if (!this->nodeExists(eleNode, itdef.first))
				continue;

			int16 val;

			if (!this->asInt16(eleNode, itdef.first, val))
				return 0;

			if (val < -100) {
				this->invalidWarning(eleNode[itdef.first], "%s %h is out of range %d~%d. Setting to -100.\n", itdef.first.c_str(), val, -100, 200);
				val = -100;
			}
			else if (val > 200) {
				this->invalidWarning(eleNode[itdef.first], "%s %h is out of range %d~%d. Setting to 200.\n", itdef.first.c_str(), val, -100, 200);
				val = 200;
			}

			this->attr_fix_table[level-1][itatk.second][itdef.second] = val;
		}
	}

	return 1;
}

AttributeDatabase elemental_attribute_db;

/**
 * Get attribute ratio
 * @param atk_ele Attack element enum e_element
 * @param def_ele Defense element enum e_element
 * @param level Element level 1 ~ MAX_ELE_LEVEL
 */
int16 AttributeDatabase::getAttribute(uint16 level, uint16 atk_ele, uint16 def_ele) {
	if (!CHK_ELEMENT(atk_ele) || !CHK_ELEMENT(def_ele) || !CHK_ELEMENT_LEVEL(level+1))
		return 100;

	return this->attr_fix_table[level][atk_ele][def_ele];
}

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

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

	if (!this->asString(node, "Status", status_name))
		return 0;

	std::string status_constant = "SC_" + status_name;
	int64 constant;

	if (!script_get_constant(status_constant.c_str(), &constant)) {
		this->invalidWarning(node["Status"], "Invalid Status %s.\n", status_name.c_str());
		return 0;
	}

	if (!this->validateStatus(static_cast<sc_type>(constant))) {
		this->invalidWarning(node["Status"], "Status %s is out of bounds.\n", status_name.c_str());
		return 0;
	}

	int status_id = static_cast<int32>(constant);
	std::shared_ptr<s_status_change_db> status = this->find(status_id);
	bool exists = status != nullptr;

	if (!exists) {
		status = std::make_shared<s_status_change_db>();
		status->type = static_cast<sc_type>(status_id);
	}

	if (this->nodeExists(node, "Icon")) {
		std::string icon_name;

		if (!this->asString(node, "Icon", icon_name))
			return 0;

		int64 constant;

		if (!script_get_constant(icon_name.c_str(), &constant)) {
			this->invalidWarning(node["Icon"], "Icon %s is invalid, defaulting to EFST_BLANK.\n", icon_name.c_str());
			constant = EFST_BLANK;
		}
		
		if (constant < EFST_BLANK || constant >= EFST_MAX) {
			this->invalidWarning(node["Icon"], "Icon %s is out of bounds, defaulting to EFST_BLANK.\n", icon_name.c_str());
			constant = EFST_BLANK;
		}

		status->icon = static_cast<efst_type>(constant);
	} else {
		if (!exists)
			status->icon = EFST_BLANK;
	}

	if (this->nodeExists(node, "DurationLookup")) {
		std::string skill_name;

		if (!this->asString(node, "DurationLookup", skill_name))
			return 0;

		uint16 skill_id = skill_name2id(skill_name.c_str());

		if (skill_id == 0)
			this->invalidWarning(node["DurationLookup"], "DurationLookup skill %s is invalid, defaulting to none.\n", skill_name.c_str());

		status->skill_id = skill_id;
	} else {
		if (!exists)
			status->skill_id = 0;
	}

	if (this->nodeExists(node, "States")) {
		const YAML::Node &stateNode = node["States"];

		for (const auto &it : stateNode) {
			std::string state = it.first.as<std::string>(), state_constant = "SCS_" + state;
			int64 constant;

			if (!script_get_constant(state_constant.c_str(), &constant)) {
				this->invalidWarning(stateNode, "State %s is invalid.\n", state.c_str());
				return 0;
			}

			if (constant < SCS_NONE || constant >= SCS_MAX) {
				this->invalidWarning(stateNode, "State %s is out of bounds.\n", state.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(stateNode, state, active))
				return 0;

			if (active)
				status->state.set(static_cast<e_scs_flag>(constant));
			else
				status->state.reset(static_cast<e_scs_flag>(constant));
		}
	} else {
		if (!exists)
			status->state = SCS_NONE;
	}

	if (this->nodeExists(node, "CalcFlags")) {
		const YAML::Node &flagNode = node["CalcFlags"];

		for (const auto &it : flagNode) {
			std::string flag = it.first.as<std::string>(), flag_constant = "SCB_" + flag;
			int64 constant;

			if (!script_get_constant(flag_constant.c_str(), &constant)) {
				this->invalidWarning(flagNode, "CalcFlag %s is invalid.\n", flag.c_str());
				return 0;
			}

			if (constant < SCB_NONE || constant >= SCB_MAX) {
				this->invalidWarning(flagNode, "CalcFlag %s is out of bounds.\n", flag.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(flagNode, flag, active))
				return 0;

			if (active)
				status->calc_flag |= static_cast<e_scb_flag>(constant);
			else
				status->calc_flag &= ~static_cast<e_scb_flag>(constant);
		}
	} else {
		if (!exists)
			status->calc_flag = SCB_NONE;
	}

	if (this->nodeExists(node, "Opt1")) {
		std::string opt;

		if (!this->asString(node, "Opt1", opt))
			return 0;

		std::string opt_constant = "OPT1_" + opt;
		int64 constant;

		if (!script_get_constant(opt_constant.c_str(), &constant)) {
			this->invalidWarning(node["Opt1"], "Opt1 %s is invalid.\n", opt.c_str());
			return 0;
		}

		if (constant < OPT1_NONE || constant >= OPT1_MAX) {
			this->invalidWarning(node["Opt1"], "Opt2 %s is out of bounds.\n", opt.c_str());
			return 0;
		}

		status->opt1 = static_cast<e_sc_opt1>(constant);
	} else {
		if (!exists)
			status->opt1 = OPT1_NONE;
	}

	if (this->nodeExists(node, "Opt2")) {
		const YAML::Node &optNode = node["Opt2"];

		for (const auto &it : optNode) {
			std::string opt = it.first.as<std::string>(), opt_constant = "OPT2_" + opt;
			int64 constant;

			if (!script_get_constant(opt_constant.c_str(), &constant)) {
				this->invalidWarning(optNode, "Opt2 %s is invalid.\n", opt.c_str());
				return 0;
			}

			if (constant < OPT2_NONE || constant >= OPT2_MAX) {
				this->invalidWarning(optNode, "Opt2 %s is out of bounds.\n", opt.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(optNode, opt, active))
				return 0;

			if (active)
				status->opt2 |= static_cast<e_sc_opt2>(constant);
			else
				status->opt2 &= ~static_cast<e_sc_opt2>(constant);
		}
	} else {
		if (!exists)
			status->opt2 = OPT2_NONE;
	}

	if (this->nodeExists(node, "Opt3")) {
		const YAML::Node &optNode = node["Opt3"];

		for (const auto &it : optNode) {
			std::string opt = it.first.as<std::string>(), opt_constant = "OPT3_" + opt;
			int64 constant;

			if (!script_get_constant(opt_constant.c_str(), &constant)) {
				this->invalidWarning(optNode, "Opt3 %s is invalid.\n", opt.c_str());
				return 0;
			}

			if (constant < OPT3_NORMAL || constant >= OPT3_MAX) {
				this->invalidWarning(optNode, "Opt3 %s is out of bounds.\n", opt.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(optNode, opt, active))
				return 0;

			if (active)
				status->opt3 |= static_cast<e_sc_opt3>(constant);
			else
				status->opt3 &= ~static_cast<e_sc_opt3>(constant);
		}
	} else {
		if (!exists)
			status->opt3 = OPT3_NORMAL;
	}

	if (this->nodeExists(node, "Options")) {
		const YAML::Node &optionNode = node["Options"];

		for (const auto &it : optionNode) {
			std::string option = it.first.as<std::string>(), option_constant = "OPTION_" + option;
			int64 constant;

			if (!script_get_constant(option_constant.c_str(), &constant)) {
				this->invalidWarning(optionNode, "Option %s is invalid.\n", option.c_str());
				return 0;
			}

			if (constant < OPTION_NOTHING || constant >= OPTION_MAX) {
				this->invalidWarning(optionNode, "Option %s is out of bounds.\n", option.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(optionNode, option, active))
				return 0;

			if (active)
				status->look |= static_cast<e_option>(constant);
			else
				status->look &= ~static_cast<e_option>(constant);
		}
	} else {
		if (!exists)
			status->look = OPTION_NOTHING;
	}

	if (this->nodeExists(node, "Flags")) {
		const YAML::Node &flagNode = node["Flags"];

		for (const auto &it : flagNode) {
			std::string flag = it.first.as<std::string>(), flag_constant = "SCF_" + flag;
			int64 constant;

			if (!script_get_constant(flag_constant.c_str(), &constant)) {
				this->invalidWarning(flagNode, "Flag %s is invalid.\n", flag.c_str());
				return 0;
			}

			if (constant < SCF_NONE || constant >= SCF_MAX) {
				this->invalidWarning(flagNode, "Flag %s is out of bounds.\n", flag.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(flagNode, flag, active))
				return 0;

			if (active)
				status->flag.set(static_cast<e_status_change_flag>(constant));
			else
				status->flag.reset(static_cast<e_status_change_flag>(constant));
		}
	}

	if (this->nodeExists(node, "MinRate")) {
		uint16 rate;

		if (!this->asUInt16(node, "MinRate", rate))
			return 0;

		status->min_rate = rate;
	} else {
		if (!exists)
			status->min_rate = 0;
	}
	
	if (this->nodeExists(node, "MinDuration")) {
		int64 duration;

		if (!this->asInt64(node, "MinDuration", duration))
			return 0;

		status->min_duration = static_cast<t_tick>(duration);
	} else {
		if (!exists)
			status->min_duration = 1;
	}

	if (this->nodeExists(node, "Fail")) {
		const YAML::Node &failNode = node["Fail"];

		for (const auto &it : failNode) {
			std::string fail = it.first.as<std::string>(), fail_constant = "SC_" + fail;
			int64 constant;

			if (!script_get_constant(fail_constant.c_str(), &constant)) {
				this->invalidWarning(failNode, "Fail status %s is invalid.\n", fail.c_str());
				return 0;
			}

			if (!this->validateStatus(static_cast<sc_type>(constant))) {
				this->invalidWarning(failNode, "Fail status %s is out of bounds.\n", fail.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(failNode, fail, active))
				return 0;

			if (active)
				status->fail.push_back(static_cast<sc_type>(constant));
			else
				util::vector_erase_if_exists(status->fail, static_cast<sc_type>(constant));
		}
	}

	if (this->nodeExists(node, "End")) {
		const YAML::Node &endNode = node["End"];

		for (const auto &it : endNode) {
			std::string end = it.first.as<std::string>(), end_constant = "SC_" + end;
			int64 constant;

			if (!script_get_constant(end_constant.c_str(), &constant)) {
				this->invalidWarning(endNode, "End status %s is invalid.\n", end.c_str());
				return 0;
			}

			if (!this->validateStatus(static_cast<sc_type>(constant))) {
				this->invalidWarning(endNode, "End status %s is out of bounds.\n", end.c_str());
				return 0;
			}

			bool active;

			if (!this->asBool(endNode, end, active))
				return 0;

			if (active)
				status->end.push_back(static_cast<sc_type>(constant));
			else
				util::vector_erase_if_exists(status->end, static_cast<sc_type>(constant));
		}
	}

	if (this->nodeExists(node, "EndReturn")) {
		bool end;

		if (!this->asBool(node, "EndReturn", end))
			return 0;

		status->end_return = end;
	} else {
		if (!exists)
			status->end_return = false;
	}

	if (!exists) {
		this->put(status_id, status);
	}

	return 1;
}

void StatusDatabase::loadingFinished(){
	std::fill( std::begin( this->StatusRelevantBLTypes ), std::end( this->StatusRelevantBLTypes ), BL_PC );

	for( auto& entry : *this ){
		auto& status = entry.second;

		if (status->type == SC_HALLUCINATION && !battle_config.display_hallucination) // Disable Hallucination.
			status->icon = EFST_BLANK;

		if( status->icon == EFST_BLANK ){
			continue;
		}else if( status->flag[SCF_BLEFFECT] ){
			this->StatusRelevantBLTypes[status->icon] |= BL_SCEFFECT;
		}else{
			this->StatusRelevantBLTypes[status->icon] = BL_PC;
		}
	}
}

StatusDatabase status_db;

/**
 * Sets defaults in tables and starts read db functions
 * sv_readdb reads the file, outputting the information line-by-line to
 * previous functions above, separating information by delimiter
 * DBs being read:
 *	attr_fix.yml: Attribute adjustment table for attacks
 *	size_fix.yml: Size adjustment table for weapons
 *	refine.yml: Refining data table
 * @return 0
 */
void status_readdb( bool reload ){
	int i;
	const char* dbsubpath[] = {
		"",
		"/" DBIMPORT,
		//add other path here
	};

	// read databases
	// path,filename,separator,mincol,maxcol,maxrow,func_parsor
	for(i=0; i<ARRAYLENGTH(dbsubpath); i++){
		size_t n1 = strlen(db_path)+strlen(dbsubpath[i])+1;
		size_t n2 = strlen(db_path)+strlen(DBPATH)+strlen(dbsubpath[i])+1;
		char* dbsubpath1 = (char*)aMalloc(n1+1);
		char* dbsubpath2 = (char*)aMalloc(n2+1);

		if(i==0) {
			safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]);
			safesnprintf(dbsubpath2,n2,"%s/%s%s",db_path,DBPATH,dbsubpath[i]);
		}
		else {
			safesnprintf(dbsubpath1,n1,"%s%s",db_path,dbsubpath[i]);
			safesnprintf(dbsubpath2,n1,"%s%s",db_path,dbsubpath[i]);
		}

		sv_readdb(dbsubpath1, "status_disabled.txt", ',', 2, 2, -1, &status_readdb_status_disabled, i > 0);

		aFree(dbsubpath1);
		aFree(dbsubpath2);
	}

	if( reload ){
		size_fix_db.reload();
		refine_db.reload();
		status_db.reload();
	}else{
		size_fix_db.load();
		refine_db.load();
		status_db.load();
	}
	elemental_attribute_db.load();
}

/**
 * Status db init and destroy.
 */
void do_init_status(void) {
	memset(SCDisabled, 0, sizeof(SCDisabled));

	add_timer_func_list(status_change_timer,"status_change_timer");
	add_timer_func_list(status_natural_heal_timer,"status_natural_heal_timer");
	initDummyData();
	status_readdb();
	natural_heal_prev_tick = gettick();
	sc_data_ers = ers_new(sizeof(struct status_change_entry),"status.cpp::sc_data_ers",ERS_OPT_NONE);
	add_timer_interval(natural_heal_prev_tick + NATURAL_HEAL_INTERVAL, status_natural_heal_timer, 0, 0, NATURAL_HEAL_INTERVAL);
}

/** Destroy status data */
void do_final_status(void) {
	ers_destroy(sc_data_ers);
	size_fix_db.clear();
	refine_db.clear();
	status_db.clear();
	elemental_attribute_db.clear();
}

 

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  123
  • Topics Per Day:  0.05
  • Content Count:  478
  • Reputation:   14
  • Joined:  11/30/17
  • Last Seen:  

status.cpp

Find all case SC_JEXPBOOST: and insert case SC_VIPSTATE:

except this part

case SC_EXPBOOST:

case SC_JEXPBOOST:

case SC_JP_EVENT04:

 

find StatusIconChangeTable[SC_JEXPBOOST

StatusIconChangeTable[SC_VIPSTATE] = EFST_VIPSTATE;

 

status.hpp

find SC_JEXPBOOST then

SC_VIPSTATE,

 

EFST_KILLING_AURA,

EFST_VIPSTATE = find the last number on efstids.lub +1,

script_constants.hpp

add below sc_jexpboost

export_constant[SC_VIPSTATE];

 

working fine with me as i write this

the only lacking now it has no timer

 

 

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

On 3/29/2022 at 6:37 PM, kalabasa said:

status.cpp

Find all case SC_JEXPBOOST: and insert case SC_VIPSTATE:

except this part

case SC_EXPBOOST:

case SC_JEXPBOOST:

case SC_JP_EVENT04:

 

find StatusIconChangeTable[SC_JEXPBOOST

StatusIconChangeTable[SC_VIPSTATE] = EFST_VIPSTATE;

 

status.hpp

find SC_JEXPBOOST then

SC_VIPSTATE,

 

EFST_KILLING_AURA,

EFST_VIPSTATE = find the last number on efstids.lub +1,

script_constants.hpp

add below sc_jexpboost

export_constant[SC_VIPSTATE];

 

working fine with me as i write this

the only lacking now it has no timer

 

 

Hi! What rAthena version are you using? I dont find any StatusIconChangeTable in my status.cpp

Mine is working but is has a timer, this is my item script, bonus_script "{ bonus2 bExpAddRace,RC_All,10; bonus2 bDropAddRace,RC_All,1; }",-1,1,0,EFST_VIPSTATE;

I want to use it as SC_VIPSTATE so that it has no timer on it. Please help.
 

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  123
  • Topics Per Day:  0.05
  • Content Count:  478
  • Reputation:   14
  • Joined:  11/30/17
  • Last Seen:  

On 4/1/2022 at 2:10 PM, funtwocrasher said:

Hi! What rAthena version are you using? I dont find any StatusIconChangeTable in my status.cpp

i download mine last 2020

On 4/1/2022 at 2:28 PM, funtwocrasher said:

Mine is working but is has a timer, this is my item script, bonus_script "{ bonus2 bExpAddRace,RC_All,10; bonus2 bDropAddRace,RC_All,1; }",-1,1,0,EFST_VIPSTATE;

I want to use it as SC_VIPSTATE so that it has no timer on it. Please help.
 

any idea how to show all icon in all characters created on the account

currently the only character that has icon is who use the item.

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

On 4/1/2022 at 2:45 PM, kalabasa said:

i download mine last 2020

Oh thats why. Mine is the latest version, downloaded last month.

 

On 4/1/2022 at 3:58 PM, kalabasa said:

any idea how to show all icon in all characters created on the account

currently the only character that has icon is who use the item.

Yeah you need to make a script file that will detect the gmlevel (5 = which is VIP) every time you login, and then you need to start SC_VIPSTATE on that script.

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  123
  • Topics Per Day:  0.05
  • Content Count:  478
  • Reputation:   14
  • Joined:  11/30/17
  • Last Seen:  

3 hours ago, funtwocrasher said:

Yeah you need to make a script file that will detect the gmlevel (5 = which is VIP) every time you login, and then you need to start SC_VIPSTATE on that script.

okay got it.

the only thing  now i can't get the time im using an NPC for that.

  • Upvote 1
  • Like 1
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

13 hours ago, kalabasa said:

okay got it.

the only thing  now i can't get the time im using an NPC for that.

Try this to get the time,

set .@Timer, vip_status(VIP_STATUS_EXPIRE);
            mes "Time left : "+ callfunc("Time2Str",.@Timer);
 

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  123
  • Topics Per Day:  0.05
  • Content Count:  478
  • Reputation:   14
  • Joined:  11/30/17
  • Last Seen:  

7 hours ago, funtwocrasher said:

Try this to get the time,

set .@Timer, vip_status(VIP_STATUS_EXPIRE);
            mes "Time left : "+ callfunc("Time2Str",.@Timer);
 

Yeah. i got this already. i mean the timer in the status icon

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  55
  • Topics Per Day:  0.01
  • Content Count:  137
  • Reputation:   3
  • Joined:  06/17/12
  • Last Seen:  

18 hours ago, kalabasa said:

Yeah. i got this already. i mean the timer in the status icon

Oh you need something like this on the item script,

bonus2 bExpAddRace,RC_All,10; bonus2 bDropAddRace,RC_All,1; }",(ADD TIME HERE IN SECONDS),1,0,EFST_VIPSTATE;

bonus2 bExpAddRace,RC_All,10; bonus2 bDropAddRace,RC_All,10; }",864001,0,EFST_VIPSTATE;  <--- 1 DAY timer

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  92
  • Topics Per Day:  0.02
  • Content Count:  354
  • Reputation:   22
  • Joined:  11/17/11
  • Last Seen:  

Managed to get this working as well as using it as sc_start sc_vip. now my issue is the timer disappears when it reaches a certain day amount. e.g

36hours(1day 12hrs)

No description available.

vs 24days

image.png.04ecc032e426bd753fa40bbbe942857a.png

vs 25days and above

image.png.1b61c8ad97af7bc6933f5dce3b46e0d7.png

Edited by Virtue
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  76
  • Topics Per Day:  0.02
  • Content Count:  461
  • Reputation:   61
  • Joined:  08/28/12
  • Last Seen:  

It may be that this duration for SC_Status is not supported.
I don't know if it is the source side that is the problem or maybe the SQL database.
The tick is set with bigint(20), but maybe it is not enough for this duration.

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.00
  • Content Count:  35
  • Reputation:   2
  • Joined:  02/15/13
  • Last Seen:  

On 4/1/2022 at 3:10 PM, funtwocrasher said:

Hi! What rAthena version are you using? I dont find any StatusIconChangeTable in my status.cpp

Mine is working but is has a timer, this is my item script, bonus_script "{ bonus2 bExpAddRace,RC_All,10; bonus2 bDropAddRace,RC_All,1; }",-1,1,0,EFST_VIPSTATE;

I want to use it as SC_VIPSTATE so that it has no timer on it. Please help.
 

 

On 3/29/2022 at 7:37 PM, kalabasa said:

status.cpp

Find all case SC_JEXPBOOST: and insert case SC_VIPSTATE:

except this part

case SC_EXPBOOST:

case SC_JEXPBOOST:

case SC_JP_EVENT04:

 

find StatusIconChangeTable[SC_JEXPBOOST

StatusIconChangeTable[SC_VIPSTATE] = EFST_VIPSTATE;

 

status.hpp

find SC_JEXPBOOST then

SC_VIPSTATE,

 

EFST_KILLING_AURA,

EFST_VIPSTATE = find the last number on efstids.lub +1,

script_constants.hpp

add below sc_jexpboost

export_constant[SC_VIPSTATE];

 

working fine with me as i write this

the only lacking now it has no timer

 

 

2023-02 server
I get a compilation error.

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  8
  • Topics Per Day:  0.00
  • Content Count:  35
  • Reputation:   2
  • Joined:  02/15/13
  • Last Seen:  

On 4/1/2022 at 3:45 PM, kalabasa said:

i download mine last 2020

any idea how to show all icon in all characters created on the account

currently the only character that has icon is who use the item.

MY 2023-02

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  13
  • Topics Per Day:  0.00
  • Content Count:  99
  • Reputation:   19
  • Joined:  05/01/12
  • Last Seen:  

Hi. Do we have updated guide for this? I'm having the same error with @funtwocrasher. I'm using the latest rathena and it seems this "StatusIconChangeTable" is nowhere to be found.

Edit: Nvm. I figured out my problem.

Edited by imat1
Resolved
  • Like 1
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  12
  • Topics Per Day:  0.03
  • Content Count:  44
  • Reputation:   2
  • Joined:  02/23/23
  • Last Seen:  

 

On 3/26/2022 at 10:56 PM, funtwocrasher said:

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!

Hi I'm sorry for reviving this topic, but may I know how did you resolve the problem about the 
Error: status_change_start: Invalid status change (1500)!? I tried to use different status change id like 1418, 976 and the error just don't disappear.  Some say that if this error exist this means that the status is still not exist. I followed every step you did on this thread but I can't seem to resolve the problem about the error. Can you help me, please?

Edited by playniks
I managed to resolve the problem. The problem I'm having is how to sync the timer accurately on statusicon. Still need help!!
  • Upvote 1
Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  3
  • Topics Per Day:  0.00
  • Content Count:  18
  • Reputation:   3
  • Joined:  11/30/16
  • Last Seen:  

On 7/2/2023 at 9:19 PM, imat1 said:

Hi. Do we have updated guide for this? I'm having the same error with @funtwocrasher. I'm using the latest rathena and it seems this "StatusIconChangeTable" is nowhere to be found.

Edit: Nvm. I figured out my problem.

Where did you find the StatusIconChangeTable?

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  30
  • Topics Per Day:  0.01
  • Content Count:  83
  • Reputation:   0
  • Joined:  01/30/12
  • Last Seen:  

Error: status_change_start: Invalid status change (1500)!? . Does anybody resolve this 😞

Link to comment
Share on other sites

  • 0

  • Group:  Members
  • Topic Count:  21
  • Topics Per Day:  0.02
  • Content Count:  121
  • Reputation:   0
  • Joined:  09/02/20
  • Last Seen:  

On 12/15/2022 at 11:48 PM, Virtue said:

Managed to get this working as well as using it as sc_start sc_vip. now my issue is the timer disappears when it reaches a certain day amount. e.g

36hours(1day 12hrs)

No description available.

vs 24days

image.png.04ecc032e426bd753fa40bbbe942857a.png

vs 25days and above

image.png.1b61c8ad97af7bc6933f5dce3b46e0d7.png

my icon didnt show up

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Answer this question...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...