Update to how bonuses are calculated in chance to hit code to be

consistent across all relevant effects (treating avoidance
and hit chance bonuses equally).
Rule ArcheryHitPenalty will now calc correctly (Was doing basically nothing)
New field npc_types 'Avoidance' (add avoidance bonus to npc)
Rules for setting min / max chance to hit
This commit is contained in:
KayenEQ 2014-07-23 21:24:21 -04:00
parent 152a7410b6
commit 4f07be2343
11 changed files with 80 additions and 48 deletions

View File

@ -44,6 +44,10 @@ Param1: Max Ranged distance (default: 250)
Param2: Percent Chance to Hit modifier
Param3: Percent Total Damage modifier
Kayen: Updated to Chance to Hit code with how bonuses are applied to be consistent for all effects.
Added field to npc_types 'Avoidance' which will modify chance to avoid melee
Added rules to set max and min chance to hit from melee/ranged (Default 95% / 5%)
Required SQL: utils/sql/git/required/2014_07_10_npc_spells.sql
Optional SQL: utils/sql/git/optional/2014_07_10_AICastingRules.sql

View File

@ -358,6 +358,8 @@ RULE_REAL ( Combat, HitBonusPerLevel, 1.2) //You gain this % of hit for every le
RULE_REAL ( Combat, WeaponSkillFalloff, 0.33) //For every weapon skill point that's not maxed you lose this % of hit
RULE_REAL ( Combat, ArcheryHitPenalty, 0.25) //Archery has a hit penalty to try to help balance it with the plethora of long term +hit modifiers for it
RULE_REAL ( Combat, AgiHitFactor, 0.01)
RULE_REAL ( Combat, MinChancetoHit, 5.0) //Minimum % chance to hit with regular melee/ranged
RULE_REAL ( Combat, MaxChancetoHit, 95.0) //Maximum % chance to hit with regular melee/ranged
RULE_INT ( Combat, MinRangedAttackDist, 25) //Minimum Distance to use Ranged Attacks
RULE_BOOL ( Combat, ArcheryBonusRequiresStationary, true) //does the 2x archery bonus chance require a stationary npc
RULE_REAL ( Combat, ArcheryBaseDamageBonus, 1) // % Modifier to Base Archery Damage (.5 = 50% base damage, 1 = 100%, 2 = 200%)

View File

@ -9,4 +9,7 @@ INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VAL
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_PursueDetrimentalChance','90','Chance while chasing target to cast a detrimental spell.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleNoSpellMinRecast','500','AI spell recast time(MS) check when no spell is cast while idle. (min time in random)');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleNoSpellMaxRecast','2000','AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleBeneficialChance','100','Chance while idle to do a beneficial spell on self or others.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:AI_IdleBeneficialChance','100','Chance while idle to do a beneficial spell on self or others.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MinChancetoHit','5','Minimum % chance to hit with regular melee/ranged.');
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Combat:MaxChancetoHit','95','Maximum % chance to hit with regular melee/ranged.');

View File

@ -1,6 +1,7 @@
-- npc_types
ALTER TABLE `npc_types` ADD `ammo_idfile` varchar( 30 ) NOT NULL DEFAULT 'IT10' AFTER `d_meele_texture2`;
ALTER TABLE `npc_types` ADD `ranged_type` tinyint( 4 ) UNSIGNED NOT NULL DEFAULT '7' AFTER `sec_melee_type`;
ALTER TABLE `npc_types` ADD `Avoidance` mediumint(9) UNSIGNED NOT NULL DEFAULT '0' AFTER `Accuracy`;
-- npc spells
ALTER TABLE `npc_spells` ADD `range_proc` smallint(5) NOT NULL DEFAULT '-1';

View File

@ -202,7 +202,8 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
if (chance_mod >= 10000)
return true;
float bonus;
float avoidanceBonus = 0;
float hitBonus = 0;
////////////////////////////////////////////////////////
// To hit calcs go here
@ -214,6 +215,7 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
//Calculate the level difference
mlog(COMBAT__TOHIT, "Chance to hit before level diff calc %.2f", chancetohit);
double level_difference = attacker_level - defender_level;
double range = defender->GetLevel();
range = ((range / 4) + 3);
@ -268,37 +270,32 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
mlog(COMBAT__TOHIT, "Applied item melee skill bonus %d, yeilding %.2f", attacker->spellbonuses.MeleeSkillCheck, chancetohit);
}
//subtract off avoidance by the defender. (Live AA - Combat Agility)
bonus = defender->spellbonuses.AvoidMeleeChance + defender->itembonuses.AvoidMeleeChance + (defender->aabonuses.AvoidMeleeChance * 10);
//Avoidance Bonuses on defender decreases baseline hit chance by percent.
avoidanceBonus = defender->spellbonuses.AvoidMeleeChanceEffect +
defender->itembonuses.AvoidMeleeChanceEffect +
defender->aabonuses.AvoidMeleeChanceEffect +
(defender->itembonuses.AvoidMeleeChance / 10.0f); //Item Mod 'Avoidence'
//AA Live - Elemental Agility
if (IsPet()) {
Mob *owner = defender->GetOwner();
if (!owner)return false;
bonus += (owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance)*10;
}
Mob *owner = nullptr;
if (defender->IsPet())
owner = defender->GetOwner();
else if ((defender->IsNPC() && defender->CastToNPC()->GetSwarmOwner()))
owner = entity_list.GetMobID(defender->CastToNPC()->GetSwarmOwner());
if (owner)
avoidanceBonus += owner->aabonuses.PetAvoidance + owner->spellbonuses.PetAvoidance + owner->itembonuses.PetAvoidance;
if(bonus) {
chancetohit -= ((bonus * chancetohit) / 1000);
mlog(COMBAT__TOHIT, "Applied avoidance chance %.2f/10, yeilding %.2f", bonus, chancetohit);
}
if(attacker->IsNPC())
chancetohit += (chancetohit * attacker->CastToNPC()->GetAccuracyRating() / 1000);
mlog(COMBAT__TOHIT, "Chance to hit after accuracy rating calc %.2f", chancetohit);
float hitBonus = 0;
/*
Kayen: Unknown if the HitChance and Accuracy effect's should modify 'chancetohit'
cumulatively or successively. For now all hitBonuses are cumulative.
*/
if(defender->IsNPC())
avoidanceBonus += (defender->CastToNPC()->GetAvoidanceRating() / 10.0f); //Modifier from database
//Hit Chance Bonuses on attacker increases baseline hit chance by percent.
hitBonus += attacker->itembonuses.HitChanceEffect[skillinuse] +
attacker->spellbonuses.HitChanceEffect[skillinuse]+
attacker->aabonuses.HitChanceEffect[skillinuse]+
attacker->itembonuses.HitChanceEffect[HIGHEST_SKILL+1] +
attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1];
attacker->spellbonuses.HitChanceEffect[HIGHEST_SKILL+1] +
attacker->aabonuses.HitChanceEffect[HIGHEST_SKILL+1];
//Accuracy = Spell Effect , HitChance = 'Accuracy' from Item Effect
//Only AA derived accuracy can be skill limited. ie (Precision of the Pathfinder, Dead Aim)
@ -306,26 +303,31 @@ bool Mob::CheckHitChance(Mob* other, SkillUseTypes skillinuse, int Hand, int16 c
attacker->spellbonuses.Accuracy[HIGHEST_SKILL+1] +
attacker->aabonuses.Accuracy[HIGHEST_SKILL+1] +
attacker->aabonuses.Accuracy[skillinuse] +
attacker->itembonuses.HitChance) / 15.0f;
attacker->itembonuses.HitChance) / 15.0f; //Item Mod 'Accuracy'
hitBonus += chance_mod; //Modifier applied from casted/disc skill attacks.
chancetohit += ((chancetohit * hitBonus) / 100.0f);
if(attacker->IsNPC())
hitBonus += (attacker->CastToNPC()->GetAccuracyRating() / 10.0f); //Modifier from database
if(skillinuse == SkillArchery)
chancetohit -= (chancetohit * RuleR(Combat, ArcheryHitPenalty)) / 100.0f;
hitBonus -= hitBonus*RuleR(Combat, ArcheryHitPenalty);
//Calculate final chance to hit
chancetohit += ((chancetohit * (hitBonus - avoidanceBonus)) / 100.0f);
mlog(COMBAT__TOHIT, "Chance to hit %.2f after accuracy calc %.2f and avoidance calc %.2f", chancetohit, hitBonus, avoidanceBonus);
chancetohit = mod_hit_chance(chancetohit, skillinuse, attacker);
// Chance to hit; Max 95%, Min 30%
// Chance to hit; Max 95%, Min 5% DEFAULTS
if(chancetohit > 1000 || chancetohit < -1000) {
//if chance to hit is crazy high, that means a discipline is in use, and let it stay there
}
else if(chancetohit > 95) {
chancetohit = 95;
else if(chancetohit > RuleR(Combat,MaxChancetoHit)) {
chancetohit = RuleR(Combat,MaxChancetoHit);
}
else if(chancetohit < 5) {
chancetohit = 5;
else if(chancetohit < RuleR(Combat,MinChancetoHit)) {
chancetohit = RuleR(Combat,MinChancetoHit);
}
//I dont know the best way to handle a garunteed hit discipline being used

View File

@ -879,7 +879,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->PetMaxHP += base1;
break;
case SE_AvoidMeleeChance:
newbon->AvoidMeleeChance += base1;
newbon->AvoidMeleeChanceEffect += base1;
break;
case SE_CombatStability:
newbon->CombatStability += base1;
@ -989,6 +989,14 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
newbon->CrippBlowChance += base1;
break;
case SE_HitChance:
{
if(base2 == -1)
newbon->HitChanceEffect[HIGHEST_SKILL+1] += base1;
else
newbon->HitChanceEffect[base2] += base1;
}
case SE_ProcOnKillShot:
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3)
{
@ -1833,16 +1841,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
case SE_AvoidMeleeChance:
{
//multiplier is to be compatible with item effects, watching for overflow too
effect_value = effect_value<3000? effect_value * 10 : 30000;
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
newbon->AvoidMeleeChance += effect_value;
newbon->AvoidMeleeChanceEffect += effect_value;
else if((effect_value < 0) && (newbon->AvoidMeleeChance > effect_value))
newbon->AvoidMeleeChance = effect_value;
else if((effect_value < 0) && (newbon->AvoidMeleeChanceEffect > effect_value))
newbon->AvoidMeleeChanceEffect = effect_value;
else if(newbon->AvoidMeleeChance < effect_value)
newbon->AvoidMeleeChance = effect_value;
else if(newbon->AvoidMeleeChanceEffect < effect_value)
newbon->AvoidMeleeChanceEffect = effect_value;
break;
}
@ -3610,9 +3616,9 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
break;
case SE_AvoidMeleeChance:
spellbonuses.AvoidMeleeChance = effect_value;
aabonuses.AvoidMeleeChance = effect_value;
itembonuses.AvoidMeleeChance = effect_value;
spellbonuses.AvoidMeleeChanceEffect = effect_value;
aabonuses.AvoidMeleeChanceEffect = effect_value;
itembonuses.AvoidMeleeChanceEffect = effect_value;
break;
case SE_RiposteChance:

View File

@ -275,7 +275,8 @@ struct StatBonuses {
int16 CriticalHealOverTime; //i
int16 CriticalDoTChance; //i
int16 CrippBlowChance; //
int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance
int16 AvoidMeleeChance; //AvoidMeleeChance/10 == % chance i = Avoidance (item mod)
int16 AvoidMeleeChanceEffect; //AvoidMeleeChance Spell Effect
int16 RiposteChance; //i
int16 DodgeChance; //i
int16 ParryChance; //i

View File

@ -194,6 +194,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
}
accuracy_rating = d->accuracy_rating;
avoidance_rating = d->avoidance_rating;
ATK = d->ATK;
CalcMaxMana();
@ -1927,6 +1928,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
return;
}
if(id == "avoidance")
{
avoidance_rating = atoi(val.c_str());
return;
}
if(id == "trackable")
{
trackable = atoi(val.c_str());

View File

@ -333,6 +333,8 @@ public:
int32 GetAccuracyRating() const { return (accuracy_rating); }
void SetAccuracyRating(int32 d) { accuracy_rating = d;}
int32 GetAvoidanceRating() const { return (avoidance_rating); }
void SetAvoidanceRating(int32 d) { avoidance_rating = d;}
int32 GetRawAC() const { return AC; }
void ModifyNPCStat(const char *identifier, const char *newValue);
@ -441,6 +443,7 @@ protected:
uint32 max_dmg;
uint32 min_dmg;
int32 accuracy_rating;
int32 avoidance_rating;
int16 attack_count;
uint32 npc_mana;
float spellscale;

View File

@ -1105,6 +1105,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
"npc_types.see_improved_hide,"
"npc_types.ATK,"
"npc_types.Accuracy,"
"npc_types.Avoidance,"
"npc_types.slow_mitigation,"
"npc_types.maxlevel,"
"npc_types.scalerate,"
@ -1290,6 +1291,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true;
tmpNPCType->ATK = atoi(row[r++]);
tmpNPCType->accuracy_rating = atoi(row[r++]);
tmpNPCType->avoidance_rating = atoi(row[r++]);
tmpNPCType->slow_mitigation = atoi(row[r++]);
tmpNPCType->maxlevel = atoi(row[r++]);
tmpNPCType->scalerate = atoi(row[r++]);

View File

@ -112,6 +112,7 @@ struct NPCType
uint8 mount_color; //only used by horse class
float attack_speed; //%+- on attack delay of the mob.
int accuracy_rating; //10 = 1% accuracy
int avoidance_rating; //10 = 1% avoidance
bool findable; //can be found with find command
bool trackable;
int16 slow_mitigation;