eqemu-server/zone/mod_functions.cpp
mmcgarvey 58d5983ef1
[Skills] Configurable Exponential Decay Formula for Skill Up (#1887)
* [Skills] Exponential Decay Skill Up Formula

Added an exponential decay skill up formula option.
The current, linear, formula results in negative chances to skill up, which
have been mitigated via a multiplier and minimum of 1%

* [Skills]Configurable Exponential Decay Formala for Skill Up

What this fixes:
The existing formula for determining whether or not to skill up could result
in negative chances, and made an assumption around the number 252.
This would ultimately result in an override that would set the chance to 1.

My fix:
I created 2 new rules:
Character:SkillUpMaximumChancePercentage
Character:SkillUpMinimumChancePercentage

I changed the forumla to:
chance = ((max - min + skill_modification) * (.99^skill)) + min

This results in an exponential decay that starts at skill-modified maximum
and approaches minimum.

I decided that max-min+skill_modification should never be less than min
I also decided to continue to apply the Character:SkillUpModifier rule
post-calculation.  I do not really think this is necessary anymore, given
this new formula, but we can discuss removing it.
I chose 25 and 2 as default maximum and minimum based on feel.

Related method signature fix:

Client::mod_increase_skill_chance was changed to return a double and
accept a double as an input for chance.  This matches the actual data types
provided while calling the method and eliminates some type coersion and
resultant truncation.  Right now, this method doesn't do anything, but in the
future we could implement skill-specific training dummies that accelerate
skill ups.  I deduce that this is the purpose of this method call.

* [Skills]Configurable Exponential Decay Formula for Skill Up

What this fixes:
The existing formula for determining whether or not to skill up could result
in negative chances, and made an assumption around the number 252.
This would ultimately result in an override that would set the chance to 1.

My fix:
I created 2 new rules:
Character:SkillUpMaximumChancePercentage
Character:SkillUpMinimumChancePercentage

I changed the forumla to:
chance = ((max - min + skill_modification) * (.99^skill)) + min

This results in an exponential decay that starts at skill-modified maximum
and approaches minimum.

I decided that max-min+skill_modification should never be less than min
I also decided to continue to apply the Character:SkillUpModifier rule
post-calculation.  I do not really think this is necessary anymore, given
this new formula, but we can discuss removing it.
I chose 25 and 2 as default maximum and minimum based on feel.

Related method signature fix:

Client::mod_increase_skill_chance was changed to return a double and
accept a double as an input for chance.  This matches the actual data types
provided while calling the method and eliminates some type coersion and
resultant truncation.  Right now, this method doesn't do anything, but in the
future we could implement skill-specific training dummies that accelerate
skill ups.  I deduce that this is the purpose of this method call.

* fixup! [Skills]Configurable Exponential Decay Formula for Skill Up

* fixup! [Skills]Configurable Exponential Decay Formula for Skill Up
2022-01-29 20:01:58 -06:00

190 lines
8.5 KiB
C++

#include "client.h"
#include "entity.h"
#include "mob.h"
#include "npc.h"
#include "worldserver.h"
#include "zone.h"
class Spawn2;
struct Consider_Struct;
struct DBTradeskillRecipe_Struct;
namespace EQ
{
class ItemInstance;
}
extern EntityList entity_list;
extern Zone* zone;
extern WorldServer worldserver;
//All functions that modify a value are passed the value as it was computed by default formulas and bonuses. In most cases this should be the final value that will be used.
//These are called when a zone boots or is repopped
void Zone::mod_init() { return; }
void Zone::mod_repop() { return; }
//Pre-spawn hook called from the NPC object to be spawned
void NPC::mod_prespawn(Spawn2 *sp) { return; }
//Base damage from NPC::Attack
int NPC::mod_npc_damage(int damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemData* weapon, Mob* other) { return(damage); }
//Mob c has been given credit for a kill. This is called after the regular EVENT_KILLED_MERIT event.
void NPC::mod_npc_killed_merit(Mob* c) { return; }
//Mob oos has been given credit for a kill. This is called after the regular EVENT_DEATH event.
void NPC::mod_npc_killed(Mob* oos) { return; }
//Base damage from Client::Attack - can cover myriad skill types
int Client::mod_client_damage(int damage, EQ::skills::SkillType skillinuse, int hand, const EQ::ItemInstance* weapon, Mob* other) { return(damage); }
//message is char[4096], don't screw it up. Return true for normal behavior, false to return immediately.
// Channels:
// 0 - Guild Chat
// 2 - Group Chat
// 3 - Shout
// 4 - Auction
// 5 - Out of Character
// 6 - Broadcast
// 7 - Tell
// 8 - Say
// 11 - GMSay
// 15 - Raid Chat
// 20 - UCS Relay for UF client and later
// 22 - Emotes for UF and later
bool Client::mod_client_message(char* message, uint8 chan_num) { return(true); }
//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(EQ::skills::SkillType skillid, Mob* against_who) { return(false); }
//chance of general skill increase, rolled against 0-99 where higher chance is better.
double Client::mod_increase_skill_chance(double chance, Mob* against_who) { return(chance); }
//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); }
//Final bind HP value after bonuses
int Client::mod_bindwound_hp(int bindhps, Mob* bindmob) { return(bindhps); }
//Client haste as calculated by default formulas - In percent from 0-100
int Client::mod_client_haste(int h) { return(h); }
//Haste cap override
int Client::mod_client_haste_cap(int cap) { return(cap); }
//This is called when a client cons a mob
void Client::mod_consider(Mob* tmob, Consider_Struct* con) { return; }
//Return true to continue with normal behavior, false returns in the parent function
bool Client::mod_saylink(const std::string& response, bool silentsaylink) { return(true); }
//Client pet power as calculated by default formulas and bonuses
int16 Client::mod_pet_power(int16 act_power, uint16 spell_id) { 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) { return(chance); }
//Chance to skillup rolled against a random 0-99 where higher is better.
float Client::mod_tradeskill_skillup(float chance_stage2) { return(chance_stage2); }
//Tribute value override
int32 Client::mod_tribute_item_value(int32 pts, const EQ::ItemInstance* item) { return(pts); }
//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; }
//Calculated xp before consider modifier, called whenever a client gets XP for killing a mob.
int32 Client::mod_client_xp(int32 in_xp, NPC *npc) { return(in_xp); }
//Client XP formula. Changes here will cause clients to change level after gaining or losing xp.
//Either modify this before your server goes live, or be prepared to write a quest script that fixes levels.
//To adjust how much XP is given per kill, use mod_client_xp
uint32 Client::mod_client_xp_for_level(uint32 xp, uint16 check_level) { return(xp); }
//Food and drink values as computed by consume requests. Return < 0 to abort the request.
int Client::mod_food_value(const EQ::ItemData *item, int change) { return(change); }
int Client::mod_drink_value(const EQ::ItemData *item, int change) { return(change); }
//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, uint16 caster_id) { return(effect_value); }
//chancetohit - 0 to 100 percent - set over 1000 for a guaranteed hit
float Mob::mod_hit_chance(float chancetohit, EQ::skills::SkillType skillinuse, Mob* attacker) { return(chancetohit); }
//Final riposte chance
float Mob::mod_riposte_chance(float ripostechance, Mob* attacker) { return(ripostechance); }
//Final block chance
float Mob::mod_block_chance(float blockchance, Mob* attacker) { return(blockchance); }
//Final parry chance
float Mob::mod_parry_chance(float parrychance, Mob* attacker) { return(parrychance); }
//Final dodge chance
float Mob::mod_dodge_chance(float dodgechance, Mob* attacker) { return(dodgechance); }
//Monk AC Bonus weight cap. Defined in Combat:MonkACBonusWeight
//Usually 15, a monk under this weight threshold gets an AC bonus
float Mob::mod_monk_weight(float monkweight, Mob* attacker) { return(monkweight); }
//Mitigation rating is compared to incoming attack rating. Higher is better.
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); }
//Kick damage after all other bonuses are applied
int32 Mob::mod_kick_damage(int32 dmg) { return(dmg); }
//Slam and bash damage after all other bonuses are applied
int32 Mob::mod_bash_damage(int32 dmg) { return(dmg); }
//Frenzy damage after all other bonuses are applied
int32 Mob::mod_frenzy_damage(int32 dmg) { return(dmg); }
//Special attack damage after all other bonuses are applied.
int32 Mob::mod_monk_special_damage(int32 ndamage, EQ::skills::SkillType skill_type) { return(ndamage); }
//ndamage - Backstab damage as calculated by default formulas
int32 Mob::mod_backstab_damage(int32 ndamage) { return(ndamage); }
//Chance for 50+ archery bonus damage if Combat:UseArcheryBonusRoll is true.
int Mob::mod_archery_bonus_chance(int bonuschance, const EQ::ItemInstance* RangeWeapon) { return(bonuschance); }
//Archery bonus damage
uint32 Mob::mod_archery_bonus_damage(uint32 MaxDmg, const EQ::ItemInstance* RangeWeapon) { return(MaxDmg); }
//Final archery damage including bonus if it was applied.
int32 Mob::mod_archery_damage(int32 TotalDmg, bool hasbonus, const EQ::ItemInstance* RangeWeapon) { return(TotalDmg); }
//Thrown weapon damage after all other calcs
uint16 Mob::mod_throwing_damage(uint16 MaxDmg) { return(MaxDmg); }
//Spell cast time in milliseconds - will not sync with client cast time bar, but does work.
int32 Mob::mod_cast_time(int32 cast_time) { return(cast_time); }
//res - Default buff duration formula
int Mob::mod_buff_duration(int res, Mob* caster, Mob* target, uint16 spell_id) { return(res); }
//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) { 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) {
return(resist_chance);
}
//Spell is cast by this on spelltar, called from spellontarget after the event_cast_on NPC event
void Mob::mod_spell_cast(uint16 spell_id, Mob* spelltar, bool reflect, bool use_resist_adjust, int16 resist_adjust, bool isproc) { return; }
//At this point all applicable aggro checks have succeeded. Attacker should aggro unless we return false.
bool Mob::mod_will_aggro(Mob *attacker, Mob *on) { return(true); }