示例#1
0
		public WorldObjectStatus(WorldObject obj) {
			Object = obj;
			Rhw = new WeaponAtk();
			Lhw = new WeaponAtk();
			//Serial = new DatabaseID(obj.ID, obj.Type);
		}
示例#2
0
		/// <summary>
		/// Clone-like copys all values
		/// </summary>
		/// <param name="status"></param>
		public void LoadFromStatus(WorldObjectStatus status, bool includeHP) {
			// TODO: confirm this.. i think this is the way how status_cpy() works
			if (includeHP == true) {
				HP = status.HP;
				SP = status.SP;
			}
			HPMax = status.HPMax;
			SPMax = status.SPMax;
			Str = status.Str;
			Agi = status.Agi;
			Vit = status.Vit;
			Int = status.Int;
			Dex = status.Dex;
			Luk = status.Luk;
			AtkBase = status.AtkBase;
			MagicAtkMin = status.MagicAtkMin;
			MagicAtkMax = status.MagicAtkMax;
			Speed = status.Speed;
			AMotion = status.AMotion;
			ADelay = status.ADelay;
			DMotion = status.DMotion;
			Mode = status.Mode;
			Hit = status.Hit;
			Flee = status.Flee;
			Flee2 = status.Flee2;
			Crit = status.Crit;
			Def2 = status.Def2;
			MDef2 = status.MDef2;
			AspdRate = status.AspdRate;
			DefEle = status.DefEle;
			EleLv = status.EleLv;
			Size = status.Size;
			Race = status.Race;
			Def = status.Def;
			MDef = status.MDef;
			Rhw = status.Rhw;
			Lhw = status.Lhw;
		}
示例#3
0
		/// <summary>
		/// Calculates player data from scratch without counting SC adjustments.
		/// Should be invoked whenever players raise stats, learn passive skills or change equipment.
		/// </summary>
		/// <param name="first"></param>
		private void CalculateChar_(bool first) {
			// TODO: eAthena uses a static here.. check the recursion and convert it
			int calculating = 0; //Check for recursive call preemption
			Character character = (Object as Character);
			WorldObjectStatus status; // pointer to the player's base status
			WorldObjectStatusChangeList sc = character.StatusChange;
			CharacterSkillTree skills; // previous skill tree
			uint b_weight, b_max_weight; // previous weight
			int i, index;
			int skill, refinedef = 0;

			// Too many recursive calls!
			if (++calculating > 10) {
				return;
			}

			// remember player-specific values that are currently being shown to the client (for refresh purposes)
			skills = new CharacterSkillTree(character.Status.Skills);
			b_weight = character.Weight;
			b_max_weight = character.MaxWeight;

			character.CalculateSkillTree();

			character.MaxWeight = (uint)CharacterJobModifer.Modifer[character.Status.Class][ECharacterJobModifer.Weight];
			character.MaxWeight += (uint)(character.Status.Str * 300);

			if (first) {
				// Load Hp/SP from char-received data.
				// TODO: compare the damn datatype!
				character.BattleStatus.HP = (uint)character.Status.HP;
				character.BattleStatus.SP = (uint)character.Status.SP;
				// TODO: needs to be pointer, check this (changes on "character.Regen.SkillRegen" should affect "character.SRegen" too)
				character.Regen.SkillRegen = character.SRegen;
				character.Regen.SittingSkillRegen = character.SSRegen;
				character.Weight = 0;

				// Calc inventory weight
				for (i = 0; i < character.Status.Inventory.Count; i++) {
					var item = character.Status.Inventory[i];
					if (item.NameID > 0 && item.Amount > 0) {
						character.Weight += (uint)(item.Data.Weight * item.Amount);
					}
				}

				// Calc cart weight
				character.CartWeight = 0;
				character.CartNum = 0;
				for (i = 0; i < character.Status.Cart.Count; i++) {
					var item = character.Status.Cart[i];
					if (item.NameID > 0 && item.Amount > 0) {
						character.CartWeight += (int)(item.Data.Weight * item.Amount);
						character.CartNum++;
					}
				}

				// Set base values..
				// TODO: pointer here, check this
				status = character.BaseStatus;

				// these are not zeroed
				character.HPRate = 100;
				character.SPRate = 100;
				character.CastRate = 100;
				character.DelayRate = 100;
				character.DSPRate = 100;
				character.HPRecoverRate = 100;
				character.SPRecoverRate = 100;
				character.MatkRate = 100;
				character.CriticalRate = character.HitRate = character.FleeRate = character.Flee2Rate = 100;
				character.DefRate = character.Def2Rate = character.MdefRate = character.Mdef2Rate = 100;
				character.Regen.StateBlock = 0;

				// Lists and arrays are always initialized, just clear them
				Array.Clear(character.ParamBonus, 0, character.ParamBonus.Length);
				Array.Clear(character.ParamEquip, 0, character.ParamEquip.Length);
				Array.Clear(character.Subele, 0, character.Subele.Length);
				Array.Clear(character.Subrace, 0, character.Subrace.Length);
				Array.Clear(character.Subrace2, 0, character.Subrace2.Length);
				Array.Clear(character.Subsize, 0, character.Subsize.Length);
				Array.Clear(character.Reseff, 0, character.Reseff.Length);
				Array.Clear(character.WeaponComaEle, 0, character.WeaponComaEle.Length);
				Array.Clear(character.WeaponComaRace, 0, character.WeaponComaRace.Length);
				Array.Clear(character.WeaponAtk, 0, character.WeaponAtk.Length);
				Array.Clear(character.WeaponAtkRate, 0, character.WeaponAtkRate.Length);
				Array.Clear(character.ArrowAddEle, 0, character.ArrowAddEle.Length);
				Array.Clear(character.ArrowAddRace, 0, character.ArrowAddRace.Length);
				Array.Clear(character.ArrowAddSize, 0, character.ArrowAddSize.Length);
				Array.Clear(character.MagicAddEle, 0, character.MagicAddEle.Length);
				Array.Clear(character.MagicAddRace, 0, character.MagicAddRace.Length);
				Array.Clear(character.MagicAddSize, 0, character.MagicAddSize.Length);
				Array.Clear(character.CritAddRace, 0, character.CritAddRace.Length);
				Array.Clear(character.ExpAddRace, 0, character.ExpAddRace.Length);
				Array.Clear(character.IgnoreMdef, 0, character.IgnoreMdef.Length);
				Array.Clear(character.IgnoreDef, 0, character.IgnoreDef.Length);
				character.ItemGroupHealRate.Clear();
				Array.Clear(character.SpGainRace, 0, character.SpGainRace.Length);

				// TODO: clear them the same way
				//character.WeaponRight.OverRefine
				//memset (&character.right_weapon.overrefine, 0, sizeof(character.right_weapon) - sizeof(character.right_weapon.atkmods));
				//memset (&character.left_weapon.overrefine, 0, sizeof(character.left_weapon) - sizeof(character.left_weapon.atkmods));

				//Clear status change.
				if (character.SpecialState.intravision) {
					//clif_status_load(&character.bl, SI_INTRAVISION, 0);
				}

				// TODO: find a way .. 
				//memset(&character.special_state,0,sizeof(character.special_state));
				//memset(&status->max_hp, 0, sizeof(struct status_data)-(sizeof(status->hp)+sizeof(status->sp)));

				//FIXME: Most of these stuff should be calculated once, but how do I fix the memset above to do that? [Skotlex]
				status.Speed = (ushort)Global.DEFAULT_WALK_SPEED;
				// Give them all modes except these (useful for clones)
				status.Mode = EMonsterMode.Mask & ~(EMonsterMode.Boss | EMonsterMode.Plant | EMonsterMode.Detector | EMonsterMode.Angry | EMonsterMode.TargetWeak);
				status.Size = ((character.Class & EClass.JOBl_BABY) > 0 ? ESize.Tiny : ESize.Normal);

				if (Config.CharacterSize > 0 && character.IsRiding()) {
					if ((character.Class & EClass.JOBl_BABY) > 0) {
						if ((Config.CharacterSize & 2) > 0) {
							status.Size = (ESize)((int)status.Size + 1);
						}
					} else if ((Config.CharacterSize & 1) > 0) {
						status.Size = (ESize)((int)status.Size + 1);
					}
				}
				status.AspdRate = 1000;
				status.EleLv = 1;
				status.Race = ERace.Demihuman;

				// zero up structures...
				Array.Clear(character.AutoSpell, 0, character.AutoSpell.Length);
				Array.Clear(character.AutoSpell2, 0, character.AutoSpell2.Length);
				Array.Clear(character.AutoSpell3, 0, character.AutoSpell3.Length);
				character.AddEff.Clear();
				character.AddEff2.Clear();
				character.AddEff3.Clear();
				character.SkillAtk.Clear();
				character.SkillHeal.Clear();
				character.SkillHeal2.Clear();
				character.HPLoss.Clear();
				character.SPLoss.Clear();
				character.HPRegen.Clear();
				character.SPRegen.Clear();
				character.SkillBlown.Clear();
				character.SkillCast.Clear();
				character.AddDef.Clear();
				character.AddMDamage.Clear();
				character.AddDrop.Clear();
				character.ItemHealRate.Clear();
				character.SubEle2.Clear();


				// vars zeroing
				character.AtkRate = 0;
				character.ArrowAtk = 0;
				character.ArrowEle = 0;
				character.ArrowCri = 0;
				character.ArrowHit = 0;
				character.Nsshealhp = 0;
				character.Nsshealsp = 0;
				character.CriticalDef = 0;
				character.DoubleRate = 0;
				character.LongAttackAtkRate = 0;
				character.NearAttackDefRate = 0;
				character.LongAttackDefRate = 0;
				character.MagicDefRate = 0;
				character.MiscDefRate = 0;
				character.IgnoreMdefEle = 0;
				character.IgnoreMdefRace = 0;
				character.PerfectHit = 0;
				character.PerfectHitAdd = 0;
				character.GetZenyRate = 0;
				character.GetZenyNum = 0;
				character.DoubleAddRate = 0;
				character.ShortWeaponDamageReturn = 0;
				character.LongWeaponDamageReturn = 0;
				character.MagicDamageReturn = 0;
				character.RandomAttackIncreaseAdd = 0;
				character.RandomAttackIncreasePer = 0;
				character.BreakWeaponRate = 0;
				character.BreakArmorRate = 0;
				character.CritAtkRate = 0;
				character.Classchange = 0;
				character.SpeedRate = 0;
				character.SpeedAddRate = 0;
				character.AspdAdd = 0;
				character.SetitemHash = 0;
				character.SetitemHash2 = 0;
				character.Itemhealrate2 = 0;
				character.SplashRange = 0;
				character.SplashAddRange = 0;
				character.AddStealRate = 0;
				character.AddHealRate = 0;
				character.AddHeal2Rate = 0;
				character.HpGainValue = 0;
				character.SpGainValue = 0;
				character.MagicHpGainValue = 0;
				character.MagicSpGainValue = 0;
				character.SpVanishRate = 0;
				character.SpVanishPer = 0;
				character.Unbreakable = 0;
				character.UnbreakableEquip = 0;
				character.UnstripableEquip = 0;

				// Autobonus
				//pc_delautobonus(sd,character.autobonus,ARRAYLENGTH(character.autobonus),true);
				//pc_delautobonus(sd,character.autobonus2,ARRAYLENGTH(character.autobonus2),true);
				//pc_delautobonus(sd,character.autobonus3,ARRAYLENGTH(character.autobonus3),true);


				// Parse equipment
				int current_equip_item_index;
				for (i = 0; i < (int)EItemEquipIndex.Max - 1; i++) {
					EItemEquipIndex iEnum = (EItemEquipIndex)i;
					// We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) 
					current_equip_item_index = index = character.EquipIndex[i];
					if (index < 0) {
						continue;
					}
					if (iEnum == EItemEquipIndex.HandR && character.EquipIndex[(int)EItemEquipIndex.HandL] == index) {
						continue;
					}
					if (iEnum == EItemEquipIndex.HeadMid && character.EquipIndex[(int)EItemEquipIndex.HeadLow] == index) {
						continue;
					}
					if (iEnum == EItemEquipIndex.HeadTop && (character.EquipIndex[(int)EItemEquipIndex.HeadMid] == index || character.EquipIndex[(int)EItemEquipIndex.HeadLow] == index)) {
						continue;
					}
					if (character.Inventory.Count <= index) {
						continue;
					}

					status.Def += (byte)character.Inventory[index].Data.Def;

					// TODO: callbacks for item script
					/*
					if(first && character.Inventory[index].Data.EquipScript)
					{	//Execute equip-script on login
						run_script(character.inventory_data[index]->equip_script,0,character.bl.id,0);
						if (!calculating)
							return 1;
					}
					*/
					if (character.Inventory[index].Data.Type == EItemType.Weapon) {
						int r, wlv = (int)character.Inventory[index].Data.WeaponLevel;
						WeaponData wd;
						WeaponAtk wa;

						if (wlv >= Global.MAX_REFINE_BONUS)
							wlv = Global.MAX_REFINE_BONUS - 1;
						if (iEnum == EItemEquipIndex.HandL && character.Inventory[index].Equip == EItemEquipLocation.HandLeft) {
							wd = character.WeaponLeft; // Left-hand weapon
							wa = status.Lhw;
						} else {
							wd = character.WeaponRight;
							wa = status.Rhw;
						}
						if (wd == null) {
							wd = new WeaponData();
						}
						if (wa == null) {
							wa = new WeaponAtk();
						}

						wa.AtkMin += (ushort)character.Inventory[index].Data.Atk;
						// TODO: load refinebonus
						wa.AtkMax = (ushort)(r = character.Inventory[index].Refine); // *refinebonus[wlv][0];
						//if((r-=refinebonus[wlv][2])>0) //Overrefine bonus.
						//	wd->overrefine = r*refinebonus[wlv][1];

						// TODO: hier weiter machen!
						//		 Klammern } nur zum compile geadded (alle 3!)
					}
				}
			}
			/*
				wa->range += character.inventory_data[index]->range;
				if(character.inventory_data[index]->script) {
					if (wd == &character.left_weapon) {
						character.state.lr_flag = 1;
						run_script(character.inventory_data[index]->script,0,character.bl.id,0);
						character.state.lr_flag = 0;
					} else
						run_script(character.inventory_data[index]->script,0,character.bl.id,0);
					if (!calculating) //Abort, run_script retriggered this. [Skotlex]
						return 1;
				}

				if(character.status.inventory[index].card[0]==CARD0_FORGE)
				{	// Forged weapon
					wd->star += (character.status.inventory[index].card[1]>>8);
					if(wd->star >= 15) wd->star = 40; // 3 Star Crumbs now give +40 dmg
					if(pc_famerank(MakeDWord(character.status.inventory[index].card[2],character.status.inventory[index].card[3]) ,MAPID_BLACKSMITH))
						wd->star += 10;
				
					if (!wa->ele) //Do not overwrite element from previous bonuses.
						wa->ele = (character.status.inventory[index].card[1]&0x0f);
				}
			}
			else if(character.inventory_data[index]->type == IT_ARMOR) {
				refinedef += character.status.inventory[index].refine*refinebonus[0][0];
				if(character.inventory_data[index]->script) {
					run_script(character.inventory_data[index]->script,0,character.bl.id,0);
					if (!calculating) //Abort, run_script retriggered this. [Skotlex]
						return 1;
				}
			}
		}

		if(character.equip_index[EQI_AMMO] >= 0){
			index = character.equip_index[EQI_AMMO];
			if(character.inventory_data[index]){		// Arrows
				character.arrow_atk += character.inventory_data[index]->atk;
				character.state.lr_flag = 2;
				run_script(character.inventory_data[index]->script,0,character.bl.id,0);
				character.state.lr_flag = 0;
				if (!calculating) //Abort, run_script retriggered status_calc_pc. [Skotlex]
					return 1;
			}
		}

		//Store equipment script bonuses 
		memcpy(character.param_equip,character.param_bonus,sizeof(character.param_equip));
		memset(character.param_bonus, 0, sizeof(character.param_bonus));

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

		//Parse Cards
		for(i=0;i<EQI_MAX-1;i++) {
			current_equip_item_index = index = character.equip_index[i]; //We pass INDEX to current_equip_item_index - for EQUIP_SCRIPT (new cards solution) [Lupus]
			if(index < 0)
				continue;
			if(i == EQI_HAND_R && character.equip_index[EQI_HAND_L] == index)
				continue;
			if(i == EQI_HEAD_MID && character.equip_index[EQI_HEAD_LOW] == index)
				continue;
			if(i == EQI_HEAD_TOP && (character.equip_index[EQI_HEAD_MID] == index || character.equip_index[EQI_HEAD_LOW] == index))
				continue;

			if(character.inventory_data[index]) {
				int j,c;
				struct item_data *data;

				//Card script execution.
				if(itemdb_isspecial(character.status.inventory[index].card[0]))
					continue;
				for(j=0;j<MAX_SLOTS;j++){ // Uses MAX_SLOTS to support Soul Bound system [Inkfish]
					current_equip_card_id= c= character.status.inventory[index].card[j];
					if(!c)
						continue;
					data = itemdb_exists(c);
					if(!data)
						continue;
					if(first && data->equip_script)
					{	//Execute equip-script on login
						run_script(data->equip_script,0,character.bl.id,0);
						if (!calculating)
							return 1;
					}
					if(!data->script)
						continue;
					if(data->flag.no_equip) { //Card restriction checks.
						if(map[character.bl.m].flag.restricted && data->flag.no_equip&map[character.bl.m].zone)
							continue;
						if(map[character.bl.m].flag.pvp && data->flag.no_equip&1)
							continue;
						if(map_flag_gvg(character.bl.m) && data->flag.no_equip&2) 
							continue;
					}
					if(i == EQI_HAND_L && character.status.inventory[index].equip == EQP_HAND_L)
					{	//Left hand status.
						character.state.lr_flag = 1;
						run_script(data->script,0,character.bl.id,0);
						character.state.lr_flag = 0;
					} else
						run_script(data->script,0,character.bl.id,0);
					if (!calculating) //Abort, run_script his function. [Skotlex]
						return 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,character.bl.id,0);
		}

		if( character.pd )
		{ // Pet Bonus
			struct pet_data *pd = character.pd;
			if( pd && pd->petDB && pd->petDB->equip_script && pd->pet.intimate >= battle_config.pet_equip_min_friendly )
				run_script(pd->petDB->equip_script,0,character.bl.id,0);
			if( pd && 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(status->rhw.range < 1) status->rhw.range = 1;
		if(status->lhw.range < 1) status->lhw.range = 1;
		if(status->rhw.range < status->lhw.range)
			status->rhw.range = status->lhw.range;

		character.double_rate += character.double_add_rate;
		character.perfect_hit += character.perfect_hit_add;
		character.splash_range += character.splash_add_range;

		// Damage modifiers from weapon type
		character.right_weapon.atkmods[0] = atkmods[0][character.weapontype1];
		character.right_weapon.atkmods[1] = atkmods[1][character.weapontype1];
		character.right_weapon.atkmods[2] = atkmods[2][character.weapontype1];
		character.left_weapon.atkmods[0] = atkmods[0][character.weapontype2];
		character.left_weapon.atkmods[1] = atkmods[1][character.weapontype2];
		character.left_weapon.atkmods[2] = atkmods[2][character.weapontype2];

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

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

		// Job bonuses
		index = pc_class2idx(character.status.class_);
		for(i=0;i<(int)character.status.job_level && i<MAX_LEVEL;i++){
			if(!job_bonus[index][i])
				continue;
			switch(job_bonus[index][i]) {
				case 1: status->str++; break;
				case 2: status->agi++; break;
				case 3: status->vit++; break;
				case 4: status->int_++; break;
				case 5: status->dex++; break;
				case 6: status->luk++; break;
			}
		}

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

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

		// Bonuses from cards and equipment as well as base stat, remember to avoid overflows.
		i = status->str + character.status.str + character.param_bonus[0] + character.param_equip[0];
		status->str = cap_value(i,0,USHRT_MAX);
		i = status->agi + character.status.agi + character.param_bonus[1] + character.param_equip[1];
		status->agi = cap_value(i,0,USHRT_MAX);
		i = status->vit + character.status.vit + character.param_bonus[2] + character.param_equip[2];
		status->vit = cap_value(i,0,USHRT_MAX);
		i = status->int_+ character.status.int_+ character.param_bonus[3] + character.param_equip[3];
		status->int_ = cap_value(i,0,USHRT_MAX);
		i = status->dex + character.status.dex + character.param_bonus[4] + character.param_equip[4];
		status->dex = cap_value(i,0,USHRT_MAX);
		i = status->luk + character.status.luk + character.param_bonus[5] + character.param_equip[5];
		status->luk = cap_value(i,0,USHRT_MAX);

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

		// Base batk value is set on status_calc_misc
		// weapon-type bonus (FIXME: Why is the weapon_atk bonus applied to base attack?)
		if (character.status.weapon < MAX_WEAPON_TYPE && character.weapon_atk[character.status.weapon])
			status->batk += character.weapon_atk[character.status.weapon];
		// Absolute modifiers from passive skills
		if((skill=pc_checkskill(sd,BS_HILTBINDING))>0)
			status->batk += 4;

	// ----- HP MAX CALCULATION -----

		// Basic MaxHP value
		//We hold the standard Max HP here to make it faster to recalculate on vit changes.
		character.status.max_hp = status_base_pc_maxhp(sd,status);
		//This is done to handle underflows from negative Max HP bonuses
		i = character.status.max_hp + (int)status->max_hp;
		status->max_hp = cap_value(i, 0, INT_MAX);

		// Absolute modifiers from passive skills
		if((skill=pc_checkskill(sd,CR_TRUST))>0)
			status->max_hp += skill*200;

		// Apply relative modifiers from equipment
		if(character.hprate < 0)
			character.hprate = 0;
		if(character.hprate!=100)
			status->max_hp = status->max_hp * character.hprate/100;
		if(battle_config.hp_rate != 100)
			status->max_hp = status->max_hp * battle_config.hp_rate/100;

		if(status->max_hp > (unsigned int)battle_config.max_hp)
			status->max_hp = battle_config.max_hp;
		else if(!status->max_hp)
			status->max_hp = 1;

	// ----- SP MAX CALCULATION -----

		// Basic MaxSP value
		character.status.max_sp = status_base_pc_maxsp(sd,status);
		//This is done to handle underflows from negative Max SP bonuses
		i = character.status.max_sp + (int)status->max_sp;
		status->max_sp = cap_value(i, 0, INT_MAX);

		// Absolute modifiers from passive skills
		if((skill=pc_checkskill(sd,SL_KAINA))>0)
			status->max_sp += 30*skill;
		if((skill=pc_checkskill(sd,HP_MEDITATIO))>0)
			status->max_sp += status->max_sp * skill/100;
		if((skill=pc_checkskill(sd,HW_SOULDRAIN))>0)
			status->max_sp += status->max_sp * 2*skill/100;

		// Apply relative modifiers from equipment
		if(character.sprate < 0)
			character.sprate = 0;
		if(character.sprate!=100)
			status->max_sp = status->max_sp * character.sprate/100;
		if(battle_config.sp_rate != 100)
			status->max_sp = status->max_sp * battle_config.sp_rate/100;

		if(status->max_sp > (unsigned int)battle_config.max_sp)
			status->max_sp = battle_config.max_sp;
		else if(!status->max_sp)
			status->max_sp = 1;

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

			status->sp = status->max_sp * battle_config.restart_sp_rate /100;
		}

	// ----- MISC CALCULATION -----
		status_calc_misc(&character.bl, status, character.status.base_level);

		//Equipment modifiers for misc settings
		if(character.matk_rate < 0)
			character.matk_rate = 0;
		if(character.matk_rate != 100){
			status->matk_max = status->matk_max * character.matk_rate/100;
			status->matk_min = status->matk_min * character.matk_rate/100;
		}

		if(character.hit_rate < 0)
			character.hit_rate = 0;
		if(character.hit_rate != 100)
			status->hit = status->hit * character.hit_rate/100;

		if(character.flee_rate < 0)
			character.flee_rate = 0;
		if(character.flee_rate != 100)
			status->flee = status->flee * character.flee_rate/100;

		if(character.def2_rate < 0)
			character.def2_rate = 0;
		if(character.def2_rate != 100)
			status->def2 = status->def2 * character.def2_rate/100;

		if(character.mdef2_rate < 0)
			character.mdef2_rate = 0;
		if(character.mdef2_rate != 100)
			status->mdef2 = status->mdef2 * character.mdef2_rate/100;

		if(character.critical_rate < 0) 
			character.critical_rate = 0;
		if(character.critical_rate != 100)
			status->cri = status->cri * character.critical_rate/100;

		if(character.flee2_rate < 0)
			character.flee2_rate = 0;
		if(character.flee2_rate != 100)
			status->flee2 = status->flee2 * character.flee2_rate/100;

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

		// Absolute modifiers from passive skills
		if((skill=pc_checkskill(sd,BS_WEAPONRESEARCH))>0)
			status->hit += skill*2;
		if((skill=pc_checkskill(sd,AC_VULTURE))>0){
			status->hit += skill;
			if(character.status.weapon == W_BOW)
				status->rhw.range += skill;
		}
		if(character.status.weapon >= W_REVOLVER && character.status.weapon <= W_GRENADE)
		{
			if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0)
				status->hit += 2*skill;
			if((skill=pc_checkskill(sd,GS_SNAKEEYE))>0) {
				status->hit += skill;
				status->rhw.range += skill;
			}
		}

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

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

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

		// Apply relative modifiers from equipment
		if(character.def_rate < 0)
			character.def_rate = 0;
		if(character.def_rate != 100) {
			i =  status->def * character.def_rate/100;
			status->def = cap_value(i, CHAR_MIN, CHAR_MAX);
		}

		if (!battle_config.weapon_defense_type && status->def > battle_config.max_def)
		{
			status->def2 += battle_config.over_def_bonus*(status->def -battle_config.max_def);
			status->def = (unsigned char)battle_config.max_def;
		}

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

		// Apply relative modifiers from equipment
		if(character.mdef_rate < 0)
			character.mdef_rate = 0;
		if(character.mdef_rate != 100) {
			i =  status->mdef * character.mdef_rate/100;
			status->mdef = cap_value(i, CHAR_MIN, CHAR_MAX);
		}

		if (!battle_config.magic_defense_type && status->mdef > battle_config.max_def)
		{
			status->mdef2 += battle_config.over_def_bonus*(status->mdef -battle_config.max_def);
			status->mdef = (signed char)battle_config.max_def;
		}

	// ----- 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,status);
		status->amotion = cap_value(i,battle_config.max_aspd,2000);

		// Relative modifiers from passive skills
		if((skill=pc_checkskill(sd,SA_ADVANCEDBOOK))>0 && character.status.weapon == W_BOOK)
			status->aspd_rate -= 5*skill;
		if((skill = pc_checkskill(sd,SG_DEVIL)) > 0 && !pc_nextjobexp(sd))
			status->aspd_rate -= 30*skill;
		if((skill=pc_checkskill(sd,GS_SINGLEACTION))>0 &&
			(character.status.weapon >= W_REVOLVER && character.status.weapon <= W_GRENADE))
			status->aspd_rate -= ((skill+1)/2) * 10;
		if(pc_isriding(sd))
			status->aspd_rate += 500-100*pc_checkskill(sd,KN_CAVALIERMASTERY);

		status->adelay = 2*status->amotion;


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

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

		// Weight
		if((skill=pc_checkskill(sd,MC_INCCARRY))>0)
			character.max_weight += 2000*skill;
		if(pc_isriding(sd) && pc_checkskill(sd,KN_RIDING)>0)
			character.max_weight += 10000;
		if(sc->data[SC_KNOWLEDGE])
			character.max_weight += character.max_weight*sc->data[SC_KNOWLEDGE]->val1/10;
		if((skill=pc_checkskill(sd,ALL_INCCARRY))>0)
			character.max_weight += 2000*skill;

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

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

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

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

		//Underflow protections.
		if(character.dsprate < 0)
			character.dsprate = 0;
		if(character.castrate < 0)
			character.castrate = 0;
		if(character.delayrate < 0)
			character.delayrate = 0;
		if(character.hprecov_rate < 0)
			character.hprecov_rate = 0;
		if(character.sprecov_rate < 0)
			character.sprecov_rate = 0;

		// Anti-element and anti-race
		if((skill=pc_checkskill(sd,CR_TRUST))>0)
			character.subele[ELE_HOLY] += skill*5;
		if((skill=pc_checkskill(sd,BS_SKINTEMPER))>0) {
			character.subele[ELE_NEUTRAL] += skill;
			character.subele[ELE_FIRE] += skill*4;
		}
		if((skill=pc_checkskill(sd,SA_DRAGONOLOGY))>0 ){
			skill = skill*4;
			character.right_weapon.addrace[RC_DRAGON]+=skill;
			character.left_weapon.addrace[RC_DRAGON]+=skill;
			character.magic_addrace[RC_DRAGON]+=skill;
			character.subrace[RC_DRAGON]+=skill;
		}

		if(sc->count){
			if(sc->data[SC_CONCENTRATE])
			{	//Update the card-bonus data
				sc->data[SC_CONCENTRATE]->val3 = character.param_bonus[1]; //Agi
				sc->data[SC_CONCENTRATE]->val4 = character.param_bonus[4]; //Dex
			}
			if(sc->data[SC_SIEGFRIED]){
				i = sc->data[SC_SIEGFRIED]->val2;
				character.subele[ELE_WATER] += i;
				character.subele[ELE_EARTH] += i;
				character.subele[ELE_FIRE] += i;
				character.subele[ELE_WIND] += i;
				character.subele[ELE_POISON] += i;
				character.subele[ELE_HOLY] += i;
				character.subele[ELE_DARK] += i;
				character.subele[ELE_GHOST] += i;
				character.subele[ELE_UNDEAD] += i;
			}
			if(sc->data[SC_PROVIDENCE]){
				character.subele[ELE_HOLY] += sc->data[SC_PROVIDENCE]->val2;
				character.subrace[RC_DEMON] += sc->data[SC_PROVIDENCE]->val2;
			}
			if(sc->data[SC_ARMOR_ELEMENT])
			{	//This status change should grant card-type elemental resist.
				character.subele[ELE_WATER] += sc->data[SC_ARMOR_ELEMENT]->val1;
				character.subele[ELE_EARTH] += sc->data[SC_ARMOR_ELEMENT]->val2;
				character.subele[ELE_FIRE] += sc->data[SC_ARMOR_ELEMENT]->val3;
				character.subele[ELE_WIND] += sc->data[SC_ARMOR_ELEMENT]->val4;
			}
			if(sc->data[SC_ARMOR_RESIST])
			{ // Undead Scroll
				character.subele[ELE_WATER] += sc->data[SC_ARMOR_RESIST]->val1;
				character.subele[ELE_EARTH] += sc->data[SC_ARMOR_RESIST]->val2;
				character.subele[ELE_FIRE] += sc->data[SC_ARMOR_RESIST]->val3;
				character.subele[ELE_WIND] += sc->data[SC_ARMOR_RESIST]->val4;
			}
		}

		status_cpy(&character.battle_status, status);

	// ----- CLIENT-SIDE REFRESH -----
		if(!character.bl.prev) {
			//Will update on LoadEndAck
			calculating = 0;
			return 0;
		}
		if(memcmp(b_skill,character.status.skill,sizeof(character.status.skill)))
			clif_skillinfoblock(sd);
		if(b_weight != character.weight)
			clif_updatestatus(sd,SP_WEIGHT);
		if(b_max_weight != character.max_weight) {
			clif_updatestatus(sd,SP_MAXWEIGHT);
			pc_updateweightstatus(sd);
		}

		calculating = 0;
	*/
		}