public WorldObjectStatus(WorldObject obj) { Object = obj; Rhw = new WeaponAtk(); Lhw = new WeaponAtk(); //Serial = new DatabaseID(obj.ID, obj.Type); }
/// <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; }
/// <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; */ }