diff --git a/zone/attack.cpp b/zone/attack.cpp index c08382838..8547ca3c6 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -1871,7 +1871,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool damage = (max_dmg+eleBane); } - damage = mod_npc_damage(damage, skillinuse, Hand, &weapon_inst, other); + damage = mod_npc_damage(damage, skillinuse, Hand, weapon, other); int32 hate = damage; if(IsPet()) @@ -2115,10 +2115,13 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski Group *kg = entity_list.GetGroupByClient(give_exp_client); Raid *kr = entity_list.GetRaidByClient(give_exp_client); + int32 finalxp = EXP_FORMULA; + finalxp = give_exp_client->mod_client_xp(finalxp, this); + if(kr) { if(!IsLdonTreasure) { - kr->SplitExp((EXP_FORMULA), this); + kr->SplitExp((finalxp), this); if(killerMob && (kr->IsRaidMember(killerMob->GetName()) || kr->IsRaidMember(killerMob->GetUltimateOwner()->GetName()))) killerMob->TrySpellOnKill(killed_level,spell); } @@ -2159,7 +2162,7 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski else if (give_exp_client->IsGrouped() && kg != nullptr) { if(!IsLdonTreasure) { - kg->SplitExp((EXP_FORMULA), this); + kg->SplitExp((finalxp), this); if(killerMob && (kg->IsGroupMember(killerMob->GetName()) || kg->IsGroupMember(killerMob->GetUltimateOwner()->GetName()))) killerMob->TrySpellOnKill(killed_level,spell); } @@ -2208,7 +2211,7 @@ void NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillType attack_ski if(GetOwner() && GetOwner()->IsClient()){ } else { - give_exp_client->AddEXP((EXP_FORMULA), conlevel); // Pyro: Comment this if NPC death crashes zone + give_exp_client->AddEXP((finalxp), conlevel); // Pyro: Comment this if NPC death crashes zone if(killerMob && (killerMob->GetID() == give_exp_client->GetID() || killerMob->GetUltimateOwner()->GetID() == give_exp_client->GetID())) killerMob->TrySpellOnKill(killed_level,spell); } diff --git a/zone/client.h b/zone/client.h index 52d298b61..3be422a2b 100644 --- a/zone/client.h +++ b/zone/client.h @@ -1112,8 +1112,8 @@ public: int16 GetActCHA() { return( min(GetMaxCHA(), GetCHA()) ); } void LoadAccountFlags(); void SetAccountFlag(std::string flag, std::string val); - std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillType); - int mod_client_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other); + std::string GetAccountFlag(std::string flag); + int mod_client_damage(int damage, SkillType skillinuse, int hand, const ItemInst* weapon, Mob* other); bool mod_client_message(char* message, uint8 chan_num); bool mod_can_increase_skill(SkillType skillid, Mob* against_who); int16 mod_increase_skill_chance(int16 chance, Mob* against_who); @@ -1129,6 +1129,7 @@ public: void mod_client_death_npc(Mob* killerMob); void mod_client_death_duel(Mob* killerMob); void mod_client_death_env(); + int32 mod_client_xp(int32 in_exp, NPC *npc); protected: friend class Mob; diff --git a/zone/mob.h b/zone/mob.h index b300eb9b6..d83f2aa90 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -805,8 +805,8 @@ public: int32 mod_monk_special_damage(int32 ndamage, SkillType skill_type); int32 mod_backstab_damage(int32 ndamage); int mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon); - uint32 mod_archery_bonus_damage(uint32 MaxDmg); - int32 mod_archery_damage(int32 TotalDmg, bool hasbonus); + uint32 mod_archery_bonus_damage(uint32 MaxDmg, const ItemInst* RangeWeapon); + int32 mod_archery_damage(int32 TotalDmg, bool hasbonus, const ItemInst* RangeWeapon); uint16 mod_throwing_damage(uint16 MaxDmg); int32 mod_cast_time(int32 cast_time); int mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id); diff --git a/zone/mod_functions.cpp b/zone/mod_functions.cpp index af228ed9e..a6088ee02 100644 --- a/zone/mod_functions.cpp +++ b/zone/mod_functions.cpp @@ -23,33 +23,504 @@ extern WorldServer worldserver; using namespace std; +#define DW_STATBASE 70 + void Zone::mod_init() { return; } void Zone::mod_repop() { return; } -void NPC::mod_prespawn(Spawn2 *sp) { return; } -int NPC::mod_npc_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); } +void NPC::mod_prespawn(Spawn2 *sp) { + //The spawn has to have 1 kill to qualify + if(sp->GetKillCount() < 1) { return; } + //Ignore existing bosses + if(lastname[0] == '[') { return; } + //5% chance to elevate + if(MakeRandomInt(0, 100) > 5) { return; } + + //Let everything else happen in perl. Our job here is to set the last name + int npcscore = GetScore(); + std::string bosstag = "<"; + int lvs = (int)(npcscore/10); + for(int x = 0; x < 10; x++) + { + if(x < lvs) { bosstag += "+"; } + else { bosstag += "="; } + } + bosstag += ">"; + strn0cpy(lastname, bosstag.c_str(), sizeof(lastname)); + + TempName("DYNBOSS"); +} + +//Base damage from NPC::Attack +int NPC::mod_npc_damage(int damage, SkillType skillinuse, int hand, const Item_Struct* weapon, Mob* other) { + float chbonus; + int lbonus; + +// if(!IsPet() || !GetOwner()) { return(damage); } +// if(!GetOwner()->IsClient()) { return(damage); } + + //Client pet power bonuses + if(GetOwner() && GetOwner()->IsClient()) + { + chbonus = (float)((float)GetOwner()->CastToClient()->GetActCHA() - DW_STATBASE) / 100; + chbonus += 1; + + if(GetOwner()->GetLevel() > 50) + { + lbonus = GetOwner()->GetLevel() - 50; + damage += lbonus * (20 * lbonus); + } + + if(weapon) + { + damage += (int)ceil(((float)weapon->Damage / (float)weapon->Delay) * (float)GetLevel()); + } + + damage = (int)ceil( (float)damage * chbonus ); + return(damage); + } + + //Regular NPC damage - test for debuffs that currently do nothing. + //str, sta, agi, dex, AC, ATK + return(damage); +} + void NPC::mod_npc_killed_merit(Mob* c) { return; } void NPC::mod_npc_killed(Mob* oos) { return; } -int Client::mod_client_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other) { return(damage); } +//Base damage from Client::Attack - can cover myriad skill types +int Client::mod_client_damage(int damage, SkillType skillinuse, int hand, const ItemInst* weapon, Mob* other) { + float dmult = 1.1f; + float cdex = (float)GetActDEX() - DW_STATBASE; + float cstr = (float)GetActSTR() - DW_STATBASE; + float cagi = (float)GetActAGI() - DW_STATBASE; + float csta = (float)GetActSTA() - DW_STATBASE; + + float cmax = ((float)GetLevel() * 2) + 150; + if(GetLevel() > 49) { cmax = 10000; } + + if(cdex > cmax) { cdex = cmax; } + if(cstr > cmax) { cstr = cmax; } + if(cagi > cmax) { cagi = cmax; } + if(csta > cmax) { csta = cmax; } + + switch(skillinuse) + { + case ARCHERY: + dmult += cdex / 600; + dmult += csta / 600; + + if(GetClass() == RANGER) + { + dmult += cdex / 1000; + } + break; + + case KICK: + case HAND_TO_HAND: + case BACKSTAB: + case _1H_BLUNT: + case _1H_SLASHING: + case PIERCING: + case THROWING: + if(GetClass() == MONK || GetClass() == BEASTLORD) + { + if(GetRace() == BARBARIAN || GetRace() == TROLL || GetRace() == OGRE) + { + dmult += cstr / 320; + dmult += csta / 320; + dmult += cdex / 150; + //2.325 + } + else + { + dmult += cdex / 400; + dmult += csta / 200; + dmult += cstr / 200; + //2.25 + } + } + else + { + dmult += cdex / 400; + dmult += cstr / 200; + dmult += csta / 200; + //2.25 + } + break; + + case BASH: + if(GetClass() == WARRIOR) + { + dmult += csta / 100; + dmult += cstr / 100; + } + else + { + dmult += csta / 150; + dmult += cstr / 150; + } + break; + + case _2H_SLASHING: + case _2H_BLUNT: + case FRENZY: + if(GetClass() == BERSERKER) + { + dmult += csta / 100; + dmult += cstr / 100; + } + else + { + dmult += csta / 150; + dmult += cstr / 150; + } + break; + + default: + dmult += cstr / 200; + dmult += csta / 200; + break; + } + + if(GetLevel() > 50) + { + float lbonus = GetLevel() - 50; + dmult += lbonus * 0.2; + } + + int final = (int)((float)damage * dmult); + + if(skillinuse == ARCHERY) + { + final += (GetSkill(ARCHERY) + (GetLevel() * 2)) / 2; + if(weapon) //We should always have a weapon here + { + final = (int)((float)final * (float)(0.6 + ((float)weapon->GetItem()->Delay / 100))); + } + } + + return(final); +} + + +//message is char[4096], don't screw it up. bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); } //Potentially dangerous string handling here -bool Client::mod_can_increase_skill(SkillType skillid, Mob* against_who) { return(false); } -int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { return(chance); } -int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { return(max_percent); } -int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); } -int Client::mod_client_haste(int h) { return(h); } + +//Skillup override. When this is called the regular skillup check has failed. Return false to proceed with default behavior. +//This will NOT allow a client to increase skill past a cap. +bool Client::mod_can_increase_skill(SkillType skillid, Mob* against_who) { + //Let people skillup on golem training dummies. + if(against_who->GetRace() == 405) { return(true); } + return(false); +} + +//chance of general skill increase, rolled against 0-99 where higher chance is better. +int16 Client::mod_increase_skill_chance(int16 chance, Mob* against_who) { + float cint = (float)GetActINT() - DW_STATBASE; + float cwis = (float)GetActWIS() - DW_STATBASE; + + float bonus = (cint + (cwis / 2)) / 10; + + if(bonus < 0) { bonus = 0; } + + return( (int)((float)chance * bonus) ); +} + +//Max percent of health you can bind wound starting with default value for class, item, and AA bonuses +int Client::mod_bindwound_percent(int max_percent, Mob* bindmob) { + return(max_percent + 20); +} + +//Final bind HP value after bonuses +int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { + if(GetSkill(BIND_WOUND) > 200) + { + bindhps += GetSkill(BIND_WOUND) * 2; + } + else + { + bindhps += GetSkill(BIND_WOUND); + } + + if(GetLevel() > 50) + { + float bonus = ((GetLevel() - 50) / 10) + 1; + bindhps += (int)( (float)bindhps * bonus ); + } + + return(bindhps); +} + +//Client haste as calculated by default formulas - In percent from 0-100 +int Client::mod_client_haste(int h) { + float agibase = (float)GetActAGI() - DW_STATBASE; + float agibonus = 0; + + if(agibase > 0) + { + agibonus = agibase; + + float l1, l2, l3; + + switch(GetClass()) + { + case ROGUE: + case MONK: + l1 = 1.5; + l2 = 2.0; + l3 = 2.5; + break; + + default: + l1 = 1.0; + l2 = 1.5; + l3 = 2.0; + } + + if(GetLevel() > 19) { agibonus = agibase * l1; } + if(GetLevel() > 39) { agibonus = agibase * l2; } + if(GetLevel() > 59) { agibonus = agibase * l3; } + } + + h += (int)(agibonus / 10); + return(h); +} + void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; } bool Client::mod_saylink(const std::string&, bool silentsaylink) { return(true); } -int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { return(act_power); } -float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { return(chance); } -float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); } -int32 Client::mod_tribute_item_value(int32 pts) { return(pts); } + +//Client pet power as calculated by default formulas and bonuses +int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { + act_power += (int)(((float)GetActCHA() - DW_STATBASE) /2 ) + GetLevel(); + return(act_power); +} + +//Chance to combine rolled against a random 0-99 where higher is better. +float Client::mod_tradeskill_chance(float chance, DBTradeskillRecipe_Struct *spec) { + chance += 10; + return(chance); +} + +//Chance to skillup rolled against a random 0-99 where higher is better. +float Client::mod_tradeskill_skillup(float chance_stage2) { + float cint = (float)CastToClient()->GetActINT() - DW_STATBASE; + float cwis = (float)CastToClient()->GetActWIS() - DW_STATBASE; + + float bonus = (cint + (cwis / 2)) / 10; + + if(bonus < 0) { bonus = 0; } + return(chance_stage2 + bonus); +} + +//Tribute value override +int32 Client::mod_tribute_item_value(int32 pts) { + return(0); +} + +//Death reporting void Client::mod_client_death_npc(Mob* killerMob) { return; } void Client::mod_client_death_duel(Mob* killerMob) { return; } void Client::mod_client_death_env() { return; } -int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { return(effect_value); } -float Mob::mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker) { return(chancetohit); } +//Calculated xp before consider modifier +int32 Client::mod_client_xp(int32 in_xp, NPC *npc) { + float xpmult = 2.0f; + + if(GetLevel() > 5) + { + xpmult = 1.5f; + } + if(GetLevel() > 10) + { + xpmult = 1.25f; + } + if(GetLevel() > 15) + { + xpmult = 1.0f; + } + + return( (int32)((float)in_xp * xpmult) ); +} + +//effect_vallue - Spell effect value as calculated by default formulas. You will want to ignore effects that don't lend themselves to scaling - pet ID's, gate coords, etc. +int Mob::mod_effect_value(int effect_value, uint16 spell_id, int effect_type, Mob* caster) { + if(IsClient()) { return(effect_value); } + if(!caster) { return(effect_value); } + if(!caster->IsClient()) { return(effect_value); } + + float mult = 1.0f; + float spbonus = 0.0f; + float spadd = 0.0f; + + if(caster->GetClass() == BARD) + { + spbonus = (float)(caster->CastToClient()->GetActCHA() - DW_STATBASE) / 150; + spbonus += (float)(caster->CastToClient()->GetActINT() - DW_STATBASE) / 150; + } + else if(caster->GetCasterClass() == 'W') + { + spbonus = (float)(caster->CastToClient()->GetActWIS() - DW_STATBASE) / 400; + spbonus += (float)(caster->CastToClient()->GetActSTA() - DW_STATBASE) / 400; + } + else if(caster->GetCasterClass() == 'I') + { + spbonus = (float)(caster->CastToClient()->GetActINT() - DW_STATBASE) / 400; + spbonus += (float)(caster->CastToClient()->GetActDEX() - DW_STATBASE) / 400; + } + else + { + //No proc/click bonuses for non casters + return(effect_value); + } + + //Add a fixed value to help things like bard songs, dots, and other low effect value spells + spadd = (mult + spbonus) * ((float)caster->GetLevel() / 10); + if(caster->GetLevel() > 50) + { + float lbonus = caster->GetLevel() - 50; + spadd += spadd * (lbonus / 2); + } + + switch(effect_type) + { + case SE_CriticalHitChance: + case SE_SpellCritChance: + case SE_CrippBlowChance: + case SE_AvoidMeleeChance: + case SE_RiposteChance: + case SE_DodgeChance: + case SE_ParryChance: + case SE_DualWieldChance: + case SE_DoubleAttackChance: + case SE_MovementSpeed: + case SE_AttackSpeed4: + case SE_AttackSpeed3: + case SE_AttackSpeed2: + case SE_AttackSpeed: + if(caster->GetLevel() < 30) { spbonus = spbonus / 2; } + if(caster->GetLevel() >= 30 && caster->GetLevel() < 46) { spbonus = spbonus / 1.5; } + mult += spbonus; + //No fixed bonus on these types + spadd = 0; + break; + + case SE_CurrentHPOnce: + case SE_CurrentHP: + if(caster->GetLevel() < 30) { spbonus = spbonus / 2; } + if(caster->GetLevel() >= 30 && caster->GetLevel() < 46) { spbonus = spbonus / 1.5; } + mult += spbonus; + break; + + case SE_DamageShield: + case SE_Stun: + case SE_ArmorClass: + case SE_ATK: + case SE_STR: + case SE_DEX: + case SE_AGI: + case SE_STA: + case SE_INT: + case SE_WIS: + case SE_CHA: + case SE_CurrentMana: + case SE_Lull: + case SE_AddFaction: + case SE_Stamina: + case SE_ChangeFrenzyRad: + case SE_DiseaseCounter: + case SE_PoisonCounter: + case SE_ResistFire: + case SE_ResistCold: + case SE_ResistPoison: + case SE_ResistDisease: + case SE_ResistMagic: + case SE_Rune: + case SE_TotalHP: + case SE_TossUp: + case SE_ManaPool: + case SE_HealOverTime: + case SE_CastingLevel: + case SE_Hunger: + case SE_CurseCounter: + case SE_HealRate: + case SE_ImprovedDamage: + case SE_ImprovedHeal: + case SE_SpellResistReduction: + case SE_IncreaseSpellHaste: + case SE_IncreaseSpellDuration: + case SE_IncreaseRange: + case SE_AllStats: + case SE_MeleeLifetap: + case SE_AllInstrumentMod: + case SE_ResistSpellChance: + case SE_ResistFearChance: + case SE_HitChance: + case SE_DamageModifier: + case SE_MinDamageModifier: + case SE_IncreaseBlockChance: + case SE_CurrentEndurance: + case SE_EndurancePool: + case SE_CurrentEnduranceOnce: + case SE_MaxHPChange: + case SE_Accuracy: + case SE_BardAEDot: + case SE_CurrentManaOnce: + case SE_FactionMod: + case SE_CorruptionCounter: + case SE_ResistCorruption: + mult += spbonus; + break; + + default: + return(effect_value); + break; + } + + //Shroud of the bear + if(caster->FindBuff(5045)) + { + spadd = spadd * -1; + mult = 1.0f; + } + + if(effect_value > 0) { effect_value += (int)ceil(spadd); } + else { effect_value -= (int)ceil(spadd); } + + effect_value *= mult; + + return( (int)(ceil(effect_value)) ); +} + +//chancetohit - 0 to 100 percent - set over 1000 for a guaranteed hit +float Mob::mod_hit_chance(float chancetohit, SkillType skillinuse, Mob* attacker) { + if(!IsClient()) + { + //Factor NPC debuffs + return(chancetohit); + } + + int divisor; + + switch(GetClass()) + { + case MONK: + case ROGUE: + divisor = 8; + divisor -= (GetLevel() / 100) * 2; + break; + + default: + divisor = 8; + } + + float hitmod = ((float)CastToClient()->GetActDEX() - DW_STATBASE) / divisor; + hitmod += ((float)CastToClient()->GetActAGI() - DW_STATBASE) / divisor; + if(hitmod < 0) { hitmod = -5; } + + return(chancetohit + hitmod); +} + float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); } float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); } float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); } @@ -57,16 +528,266 @@ float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechan float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); } float Mob::mod_mitigation_rating(float mitigation_rating, Mob* attacker) { return(mitigation_rating); } float Mob::mod_attack_rating(float attack_rating, Mob* defender) { return(attack_rating); } -int32 Mob::mod_kick_damage(int32 dmg) { return(dmg); } -int32 Mob::mod_bash_damage(int32 dmg) { return(dmg); } -int32 Mob::mod_frenzy_damage(int32 dmg) { return(dmg); } -int32 Mob::mod_monk_special_damage(int32 ndamage, SkillType skill_type) { return(ndamage); } -int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); } + +//Kick damage after all other bonuses are applied +int32 Mob::mod_kick_damage(int32 dmg) { + if(!IsClient()) { return(dmg); } + + ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_FEET); + if(item) + { + dmg += item->GetItem()->AC; + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + { + ItemInst *aug = item->GetAugment(i); + if(aug) + { + dmg += aug->GetItem()->AC; + } + } + } + dmg += GetLevel() / 4; + dmg = CastToClient()->mod_client_damage(dmg, KICK, 0, nullptr, nullptr); + + return(dmg); +} + +//Slam and bash damage after all other bonuses are applied +int32 Mob::mod_bash_damage(int32 dmg) { + if(!IsClient()) { return(dmg); } + + float lmult = (((float)GetLevel() * 2 ) / 100) + 1; + bool foundshield = false; + + ItemInst *item = CastToClient()->GetInv().GetItem(SLOT_SECONDARY); + if(item) + { + if(item->GetItem()->ItemType == ItemTypeShield) + { + foundshield = true; + dmg += item->GetItem()->AC * lmult; + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + { + ItemInst *aug = item->GetAugment(i); + if(aug) + { + dmg += aug->GetItem()->AC; + } + } + } + } + + if(!foundshield) //This is from a slam + { + item = CastToClient()->GetInv().GetItem(SLOT_SHOULDER); + if(item) + { + if(item->GetItem()->ItemType == ItemTypeArmor) + { + dmg += item->GetItem()->AC * lmult; + for(int i = 0; i < MAX_AUGMENT_SLOTS; ++i) + { + ItemInst *aug = item->GetAugment(i); + if(aug) + { + dmg += aug->GetItem()->AC; + } + } + } + } + } + + dmg += GetLevel() / 4; + dmg = CastToClient()->mod_client_damage(dmg, BASH, 0, nullptr, nullptr); + + return(dmg); +} + +//Frenzy damage after all other bonuses are applied +int32 Mob::mod_frenzy_damage(int32 dmg) { + if(!IsClient()) { return(dmg); } + + dmg = CastToClient()->mod_client_damage(dmg, FRENZY, 0, nullptr, nullptr); + return(dmg); +} + +//Special attack damage after all other bonuses are applied. +int32 Mob::mod_monk_special_damage(int32 ndamage, SkillType skill_type) { + if(!IsClient()) { return(ndamage); } + + ndamage = CastToClient()->mod_client_damage(ndamage, KICK, 0, nullptr, nullptr); + return(ndamage); +} + +//ndamage - Backstab damage as calculated by default formulas +int32 Mob::mod_backstab_damage(int32 ndamage) { + if(!IsClient()) { return(ndamage); } + + float cdex = (float)CastToClient()->GetActDEX() - DW_STATBASE; + float cstr = (float)CastToClient()->GetActSTR() - DW_STATBASE; + float cagi = (float)CastToClient()->GetActAGI() - DW_STATBASE; + float bsm = 1.0f; + + bsm += (cdex / 200) + ((float)GetLevel() / 100); + bsm += cstr / 300; + bsm += cagi / 300; + + if(GetLevel() > 50) + { + float lbonus = GetLevel() - 50; + bsm += (lbonus * 0.1); + } + + return( (int32)((float)ndamage * bsm) ); +} + +//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true. Base is Combat:ArcheryBonusChance int Mob::mod_archery_bonus_chance(int bonuschance, const ItemInst* RangeWeapon) { return(bonuschance); } -uint32 Mob::mod_archery_bonus_damage(uint32 MaxDmg) { return(MaxDmg); } -int32 Mob::mod_archery_damage(int32 TotalDmg, bool hasbonus) { return(TotalDmg); } -uint16 Mob::mod_throwing_damage(uint16 MaxDmg) { return(MaxDmg); } -int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); } -int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); } -int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { return(2); } -int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) { return(resist_chance + level_mod + resist_modifier + target_resist); } + +//Archery bonus damage +uint32 Mob::mod_archery_bonus_damage(uint32 MaxDmg, const ItemInst* RangeWeapon) { return(MaxDmg); } + +//Final archery damage including bonus if it was applied. +int32 Mob::mod_archery_damage(int32 TotalDmg, bool hasbonus, const ItemInst* RangeWeapon) { + if(!IsClient()) { return(TotalDmg); } + + TotalDmg += (GetLevel() * 2); + TotalDmg = CastToClient()->mod_client_damage(TotalDmg, ARCHERY, 0, RangeWeapon, nullptr); + + return(TotalDmg); +} + +//Thrown weapon damage after all other calcs +uint16 Mob::mod_throwing_damage(uint16 MaxDmg) { + if(!IsClient()) { return(MaxDmg); } + + MaxDmg = CastToClient()->mod_client_damage(MaxDmg, THROWING, 0, nullptr, nullptr); + return(MaxDmg); +} + +int32 Mob::mod_cast_time(int32 cast_time) { + if(!IsClient()) { return(cast_time); } + + float ctmod = 1; + float cdex = (float)CastToClient()->GetActDEX() - DW_STATBASE; + float ccast = 0; + + if(GetCasterClass() == 'W') + { + ccast = (float)CastToClient()->GetActWIS() - DW_STATBASE; + } + if(GetCasterClass() == 'I') + { + ccast = (float)CastToClient()->GetActINT() - DW_STATBASE; + } + if(ccast < 0.0001) { return(cast_time); } + + ctmod += cdex / 185; + ctmod += ccast / 185; + + if(GetLevel() > 50) + { + float lbonus = GetLevel() - 50; + ctmod += (lbonus * 0.1); + } + + return( (int32)((float)cast_time / ctmod) ); +} + +//res - Default buff duration formula +int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { + if(!IsClient()) { return(res); } + + float cmult = 1; + + if(GetClass() == BARD) + { + cmult = (float)(CastToClient()->GetActCHA() - DW_STATBASE) / 200; + } + else if(GetCasterClass() == 'W') + { + cmult = (float)(CastToClient()->GetActWIS() - DW_STATBASE) / 200; + } + else if(GetCasterClass() == 'I') + { + cmult = (float)(CastToClient()->GetActINT() - DW_STATBASE) / 200; + } + + cmult += (float)(CastToClient()->GetActSTA() - DW_STATBASE) / 400; + + if(cmult < 1) { cmult = 1; } + + return( (int)((float)res * cmult) ); +} + +//Spell stack override - If this returns anything < 2, it will ignore all other stacking rules. +// See spells.cpp: Mob::CheckStackConflict +// 0 - No conflict +// 1 - Overwrite, spellid1 is replaced by spellid2 +// -1 - Blocked, spellid2 will not land +// 2 - Default stacking behavior +int Mob::mod_spell_stack(uint16 spellid1, int caster_level1, Mob* caster1, uint16 spellid2, int caster_level2, Mob* caster2) { + + if(IsEffectInSpell(spellid1, SE_Illusion) && IsEffectInSpell(spellid2, SE_Illusion)) + { + return(1); + } + return(2); +} + +//Sum of various resists rolled against a value of 200. +int Mob::mod_spell_resist(int resist_chance, int level_mod, int resist_modifier, int target_resist, uint8 resist_type, uint16 spell_id, Mob* caster) { + int final = resist_chance + level_mod + resist_modifier + target_resist; + + int temp_level_diff = GetLevel() - caster->GetLevel(); + if(temp_level_diff > 15 && caster->GetLevel() < 46) + { + if(caster->IsClient()) + { + if(caster->CastToClient()->GetAAXP() < 100) + { + return(0); + } + } + } + + if(final > 185) { final = 185; } // Cap resist so it's always possible to land a spell (unless we hit the client level diff max). + if(!IsClient()) { return(final); } + + float resistmod = 1.0f; + + //Make charisma a part of all resists + resistmod += ((float)CastToClient()->GetActCHA() - DW_STATBASE) / 20; + + //The other half is the casting stat + if(GetClass() == BARD) + { + resistmod += ((float)CastToClient()->GetActCHA() - DW_STATBASE) / 20; + } + else if(GetCasterClass() == 'W') + { + resistmod += ((float)CastToClient()->GetActWIS() - DW_STATBASE) / 20; + } + else if(GetCasterClass() == 'I') + { + resistmod += ((float)CastToClient()->GetActINT() - DW_STATBASE) / 20; + } + + final += resistmod; + + if(caster->GetLevel() > 50) + { + final -= (int)( (caster->GetLevel() - 50) * 20 ); + } + + //Let the client be highly resistant to their own AoE + if( + (spells[spell_id].targettype == ST_AECaster || spells[spell_id].targettype == ST_AETarget) && + caster->CastToClient()->CharacterID() == CastToClient()->CharacterID() + ) + { + final = 185; + } + + return(final); +} diff --git a/zone/npc.h b/zone/npc.h index cd9b2baaa..d33dd4a3c 100644 --- a/zone/npc.h +++ b/zone/npc.h @@ -457,7 +457,7 @@ public: uint32 GetSpawnKillCount(); int GetScore(); void mod_prespawn(Spawn2 *sp); - int mod_npc_damage(int damage, SkillType skillinuse, int hand, ItemInst* weapon, Mob* other); + int mod_npc_damage(int damage, SkillType skillinuse, int hand, const Item_Struct* weapon, Mob* other); void mod_npc_killed_merit(Mob* c); void mod_npc_killed(Mob* oos); diff --git a/zone/special_attacks.cpp b/zone/special_attacks.cpp index 4950e3b35..29e86cdd4 100644 --- a/zone/special_attacks.cpp +++ b/zone/special_attacks.cpp @@ -904,7 +904,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item { MaxDmg *= (float)2; hate *= (float)2; - MaxDmg = mod_archery_bonus_damage(MaxDmg); + MaxDmg = mod_archery_bonus_damage(MaxDmg, RangeWeapon); mlog(COMBAT__RANGED, "Ranger. Double damage success roll, doubling damage to %d", MaxDmg); Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE); @@ -937,7 +937,7 @@ void Mob::DoArcheryAttackDmg(Mob* other, const ItemInst* RangeWeapon, const Item TotalDmg += other->GetAdditionalDamage(this, 0, true, ARCHERY); TotalDmg += (itembonuses.HeroicDEX / 10) + (TotalDmg * other->GetSkillDmgTaken(ARCHERY) / 100) + GetSkillDmgAmt(ARCHERY); - TotalDmg = mod_archery_damage(TotalDmg, dobonus); + TotalDmg = mod_archery_damage(TotalDmg, dobonus, RangeWeapon); TryCriticalHit(other, ARCHERY, TotalDmg); other->AddToHateList(this, hate, 0, false);