Split Mob::SetAttackTimer into Client and NPC methods

This was the easiest way to NPCs delay settings to be sane in the database.
The functions are cleaner since there is no specific logic to change
behavior depending on if they're a client or not.
This commit is contained in:
Michael Cook (mackal) 2014-09-09 22:13:30 -04:00
parent 2fa31799f6
commit fa1e33783a
6 changed files with 172 additions and 162 deletions

View File

@ -4803,3 +4803,145 @@ void Mob::CommonBreakInvisible()
hidden = false;
improved_hidden = false;
}
void Mob::SetAttackTimer()
{
attack_timer.SetAtTrigger(4000, true);
}
void Client::SetAttackTimer()
{
float PermaHaste = GetPermaHaste() * 100.0f;
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
const Item_Struct *PrimaryWeapon = nullptr;
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if (i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
const Item_Struct *ItemToUse = nullptr;
//find our item
ItemInst *ci = GetInv().GetItem(i);
if (ci)
ItemToUse = ci->GetItem();
//special offhand stuff
if (i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if (PrimaryWeapon != nullptr) {
if (PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
attack_dw_timer.Disable();
continue;
}
}
//see if we have a valid weapon
if (ItemToUse != nullptr) {
//check type and damage/delay
if (ItemToUse->ItemClass != ItemClassCommon
|| ItemToUse->Damage == 0
|| ItemToUse->Delay == 0) {
//no weapon
ItemToUse = nullptr;
}
// Check to see if skill is valid
else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) &&
(ItemToUse->ItemType != ItemTypeMartial) &&
(ItemToUse->ItemType != ItemType2HPiercing)) {
//no weapon
ItemToUse = nullptr;
}
}
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
int speed = 0;
//if we have no weapon..
if (ItemToUse == nullptr) {
//above checks ensure ranged weapons do not fall into here
// Work out if we're a monk
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
speed = static_cast<int>((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste);
else
speed = static_cast<int>((36 * (100 + DelayMod) / 100) * PermaHaste);
} else {
//we have a weapon, use its delay
// Convert weapon delay to timer resolution (milliseconds)
//delay * 100
speed = static_cast<int>((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste);
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) {
float quiver_haste = GetQuiverHaste();
if (quiver_haste > 0)
speed *= quiver_haste;
}
}
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
if (i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
void NPC::SetAttackTimer()
{
float PermaHaste = GetPermaHaste();
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer *TimerToUse = nullptr;
for (int i = MainRange; i <= MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if (i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
//special offhand stuff
if (i == MainSecondary) {
//NPCs get it for free at 13
if(GetLevel() < 13) {
attack_dw_timer.Disable();
continue;
}
}
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
// Technically NPCs should do some logic for weapons, but the effect is minimal
// What they do is take the lower of their set delay and the weapon's
// ex. Mob's delay set to 20, weapon set to 19, delay 19
// Mob's delay set to 20, weapon set to 21, delay 20
int speed = static_cast<int>((36 * (100 + DelayMod) / 100) * (100.0f + attack_speed) * PermaHaste);
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
}
}

View File

@ -8346,3 +8346,19 @@ void Client::ShowNumHits()
return;
}
float Client::GetQuiverHaste()
{
float quiver_haste = 0;
for (int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++) {
const ItemInst *pi = GetInv().GetItem(r);
if (!pi)
continue;
if (pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver) {
float temp_wr = (pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv));
quiver_haste = std::max(temp_wr, quiver_haste);
}
}
if (quiver_haste > 0)
quiver_haste = 1.0f / (1.0f + static_cast<float>(quiver_haste) / 100.0f);
return quiver_haste;
}

View File

@ -222,6 +222,8 @@ public:
virtual Group* GetGroup() { return entity_list.GetGroupByClient(this); }
virtual inline bool IsBerserk() { return berserk; }
virtual int32 GetMeleeMitDmg(Mob *attacker, int32 damage, int32 minhit, float mit_rating, float atk_rating);
virtual void SetAttackTimer();
float GetQuiverHaste();
void AI_Init();
void AI_Start(uint32 iMoveDelay = 0);
@ -1194,9 +1196,9 @@ public:
int32 mod_client_xp(int32 in_exp, NPC *npc);
uint32 mod_client_xp_for_level(uint32 xp, uint16 check_level);
int mod_client_haste_cap(int cap);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
int mod_consume(Item_Struct *item, ItemUseTypes type, int change);
int mod_food_value(const Item_Struct *item, int change);
int mod_drink_value(const Item_Struct *item, int change);
void SetEngagedRaidTarget(bool value) { EngagedRaidTarget = value; }
bool GetEngagedRaidTarget() const { return EngagedRaidTarget; }

View File

@ -1939,157 +1939,6 @@ void Mob::Kill() {
Death(this, 0, SPELL_UNKNOWN, SkillHandtoHand);
}
void Mob::SetAttackTimer() {
float PermaHaste;
if (GetHaste())
PermaHaste = 1 / (1 + (float)GetHaste() / 100);
else
PermaHaste = 1.0f;
//default value for attack timer in case they have
//an invalid weapon equipped:
attack_timer.SetAtTrigger(4000, true);
Timer* TimerToUse = nullptr;
const Item_Struct* PrimaryWeapon = nullptr;
for (int i=MainRange; i<=MainSecondary; i++) {
//pick a timer
if (i == MainPrimary)
TimerToUse = &attack_timer;
else if (i == MainRange)
TimerToUse = &ranged_timer;
else if(i == MainSecondary)
TimerToUse = &attack_dw_timer;
else //invalid slot (hands will always hit this)
continue;
const Item_Struct* ItemToUse = nullptr;
//find our item
if (IsClient()) {
ItemInst* ci = CastToClient()->GetInv().GetItem(i);
if (ci)
ItemToUse = ci->GetItem();
} else if(IsNPC())
{
//The code before here was fundementally flawed because equipment[]
//isn't the same as PC inventory and also:
//NPCs don't use weapon speed to dictate how fast they hit anyway.
ItemToUse = nullptr;
}
//special offhand stuff
if(i == MainSecondary) {
//if we have a 2H weapon in our main hand, no dual
if(PrimaryWeapon != nullptr) {
if( PrimaryWeapon->ItemClass == ItemClassCommon
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
attack_dw_timer.Disable();
continue;
}
}
//clients must have the skill to use it...
if(IsClient()) {
//if we cant dual wield, skip it
if (!CanThisClassDualWield()) {
attack_dw_timer.Disable();
continue;
}
} else {
//NPCs get it for free at 13
if(GetLevel() < 13) {
attack_dw_timer.Disable();
continue;
}
}
}
//see if we have a valid weapon
if(ItemToUse != nullptr) {
//check type and damage/delay
if(ItemToUse->ItemClass != ItemClassCommon
|| ItemToUse->Damage == 0
|| ItemToUse->Delay == 0) {
//no weapon
ItemToUse = nullptr;
}
// Check to see if skill is valid
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
//no weapon
ItemToUse = nullptr;
}
}
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
if (DelayMod < -99)
DelayMod = -99;
//if we have no weapon..
if (ItemToUse == nullptr) {
//above checks ensure ranged weapons do not fall into here
// Work out if we're a monk
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
//we are a monk, use special delay
int speed = (int)( (GetMonkHandToHandDelay()*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
// 1200 seemed too much, with delay 10 weapons available
if(speed < RuleI(Combat, MinHastedDelay)) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, delay based on level or epic
} else {
//not a monk... using fist, regular delay
int speed = (int)((36 *(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay) && IsClient()) //lower bound
speed = RuleI(Combat, MinHastedDelay);
TimerToUse->SetAtTrigger(speed, true); // Hand to hand, non-monk 2/36
}
} else {
//we have a weapon, use its delay
// Convert weapon delay to timer resolution (milliseconds)
//delay * 100
int speed = (int)((ItemToUse->Delay*(100+DelayMod)/100)*(100.0f+attack_speed)*PermaHaste);
if(speed < RuleI(Combat, MinHastedDelay))
speed = RuleI(Combat, MinHastedDelay);
if(ItemToUse && (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing))
{
if(IsClient())
{
float max_quiver = 0;
for(int r = EmuConstants::GENERAL_BEGIN; r <= EmuConstants::GENERAL_END; r++)
{
const ItemInst *pi = CastToClient()->GetInv().GetItem(r);
if(!pi)
continue;
if(pi->IsType(ItemClassContainer) && pi->GetItem()->BagType == BagTypeQuiver)
{
float temp_wr = ( pi->GetItem()->BagWR / RuleI(Combat, QuiverWRHasteDiv) );
if(temp_wr > max_quiver)
{
max_quiver = temp_wr;
}
}
}
if(max_quiver > 0)
{
float quiver_haste = 1 / (1 + max_quiver / 100);
speed *= quiver_haste;
}
}
}
TimerToUse->SetAtTrigger(speed, true);
}
if(i == MainPrimary)
PrimaryWeapon = ItemToUse;
}
}
bool Mob::CanThisClassDualWield(void) const {
if(!IsClient()) {
return(GetSkill(SkillDualWield) > 0);

View File

@ -684,6 +684,7 @@ public:
inline bool GetInvul(void) { return invulnerable; }
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
virtual int GetHaste();
inline float GetPermaHaste() { return GetHaste() ? 1.0f / (1.0f + static_cast<float>(GetHaste()) / 100.0f) : 1.0f; }
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
uint16 GetDamageTable(SkillUseTypes skillinuse);

View File

@ -134,7 +134,6 @@ public:
void CalcNPCRegen();
void CalcNPCDamage();
int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr);
int32 GetActSpellHealing(uint16 spell_id, int32 value, Mob* target = nullptr);
inline void SetSpellFocusDMG(int32 NewSpellFocusDMG) {SpellFocusDMG = NewSpellFocusDMG;}
@ -158,6 +157,7 @@ public:
virtual void InitializeBuffSlots();
virtual void UninitializeBuffSlots();
virtual void SetAttackTimer();
virtual void RangedAttack(Mob* other);
virtual void ThrowingAttack(Mob* other) { }
int32 GetNumberOfAttacks() const { return attack_count; }
@ -388,16 +388,16 @@ public:
inline void SetHealScale(float amt) { healscale = amt; }
inline float GetHealScale() { return healscale; }
uint32 GetSpawnKillCount();
int GetScore();
void SetMerchantProbability(uint8 amt) { probability = amt; }
uint32 GetSpawnKillCount();
int GetScore();
void SetMerchantProbability(uint8 amt) { probability = amt; }
uint8 GetMerchantProbability() { return probability; }
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
void mod_prespawn(Spawn2 *sp);
int mod_npc_damage(int damage, SkillUseTypes skillinuse, int hand, const Item_Struct* weapon, Mob* other);
void mod_npc_killed_merit(Mob* c);
void mod_npc_killed(Mob* oos);
void AISpellsList(Client *c);
void AISpellsList(Client *c);
bool IsRaidTarget() const { return raid_target; };
protected: