mirror of
https://github.com/EQEmu/Server.git
synced 2025-12-18 19:41:30 +00:00
Merge branch 'master' into loot
This commit is contained in:
commit
0cd320dd75
@ -1,5 +1,9 @@
|
|||||||
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
EQEMu Changelog (Started on Sept 24, 2003 15:50)
|
||||||
-------------------------------------------------------
|
-------------------------------------------------------
|
||||||
|
== 09/28/2014 ==
|
||||||
|
demonstar55: Add support for post June 18, 2014 Hundred Hands Effect spells (they changed the formula and stuff)
|
||||||
|
set Spells:Jun182014HundredHandsRevamp to true if you're using a spell file from June 18, 2014+
|
||||||
|
|
||||||
== 09/27/2014 ==
|
== 09/27/2014 ==
|
||||||
Kayen: Implemented perl function $mob->GetSpellStat(spell_id, identifier, slot);
|
Kayen: Implemented perl function $mob->GetSpellStat(spell_id, identifier, slot);
|
||||||
Note: identifier is the stat field in spells_new, slot is used for certain effects like effectid, base,base2, max ect.
|
Note: identifier is the stat field in spells_new, slot is used for certain effects like effectid, base,base2, max ect.
|
||||||
@ -7,6 +11,10 @@ Example $mob->GetSpellStat(121, "range"); //Returns spell range
|
|||||||
Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1
|
Example $mob->GetSpellStat(121, "effectid", 1); //Returns the the value of effectid1
|
||||||
This will allow you to pull almost all the data for any spell in quest files.
|
This will allow you to pull almost all the data for any spell in quest files.
|
||||||
demonstar55: Move the client's SetAttackTimer to the end of Client::CalcBonuses to keep the haste in sync
|
demonstar55: Move the client's SetAttackTimer to the end of Client::CalcBonuses to keep the haste in sync
|
||||||
|
demonstar55: Correct haste/slow "stacking" rules
|
||||||
|
demonstar55: Correct SE_AttackSpeed4 to respect unslowable
|
||||||
|
demonstar55: Make the haste be between 1-225 like the client (<100 = slow, >100 = haste) to ...
|
||||||
|
demonstar55: Correct Hundred Hands effect and use formula provided by devs
|
||||||
|
|
||||||
== 09/24/2014 ==
|
== 09/24/2014 ==
|
||||||
Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.)
|
Uleat: Re-ordered server opcodes and handlers to give them some predictability of location (I need this for the inventory re-enumeration.)
|
||||||
|
|||||||
@ -322,6 +322,7 @@ RULE_INT ( Spells, AI_IdleNoSpellMinRecast, 500) // AI spell recast time(MS) che
|
|||||||
RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)
|
RULE_INT ( Spells, AI_IdleNoSpellMaxRecast, 2000) // AI spell recast time(MS) check when no spell is cast while chasing target. (max time in random)
|
||||||
RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others.
|
RULE_INT ( Spells, AI_IdleBeneficialChance, 100) // Chance while idle to do a beneficial spell on self or others.
|
||||||
RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false)
|
RULE_BOOL ( Spells, SHDProcIDOffByOne, true) // pre June 2009 SHD spell procs were off by 1, they stopped doing this in June 2009 (so UF+ spell files need this false)
|
||||||
|
RULE_BOOL ( Spells, Jun182014HundredHandsRevamp, false) // this should be true for if you import a spell file newer than June 18, 2014
|
||||||
|
|
||||||
RULE_CATEGORY_END()
|
RULE_CATEGORY_END()
|
||||||
|
|
||||||
|
|||||||
@ -0,0 +1 @@
|
|||||||
|
INSERT INTO `rule_values` (`ruleset_id`, `rule_name`, `rule_value`, `notes`) VALUES (1, 'Spells:Jun182014HundredHandsRevamp', 'false', 'Set this to true if your spell file is from after June 18, 2014.');
|
||||||
@ -4807,6 +4807,26 @@ void Mob::CommonBreakInvisible()
|
|||||||
improved_hidden = false;
|
improved_hidden = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dev quotes:
|
||||||
|
* Old formula
|
||||||
|
* Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 100) * Original Delay)
|
||||||
|
* New formula
|
||||||
|
* Final delay = (Original Delay / (haste mod *.01f)) + ((Hundred Hands / 1000) * (Original Delay / (haste mod *.01f))
|
||||||
|
* Base Delay 20 25 30 37
|
||||||
|
* Haste 2.25 2.25 2.25 2.25
|
||||||
|
* HHE (old) -17 -17 -17 -17
|
||||||
|
* Final Delay 5.488888889 6.861111111 8.233333333 10.15444444
|
||||||
|
*
|
||||||
|
* Base Delay 20 25 30 37
|
||||||
|
* Haste 2.25 2.25 2.25 2.25
|
||||||
|
* HHE (new) -383 -383 -383 -383
|
||||||
|
* Final Delay 5.484444444 6.855555556 8.226666667 10.14622222
|
||||||
|
*
|
||||||
|
* Difference -0.004444444 -0.005555556 -0.006666667 -0.008222222
|
||||||
|
*
|
||||||
|
* These times are in 10th of a second
|
||||||
|
*/
|
||||||
|
|
||||||
void Mob::SetAttackTimer()
|
void Mob::SetAttackTimer()
|
||||||
{
|
{
|
||||||
attack_timer.SetAtTrigger(4000, true);
|
attack_timer.SetAtTrigger(4000, true);
|
||||||
@ -4814,7 +4834,7 @@ void Mob::SetAttackTimer()
|
|||||||
|
|
||||||
void Client::SetAttackTimer()
|
void Client::SetAttackTimer()
|
||||||
{
|
{
|
||||||
float PermaHaste = GetPermaHaste();
|
float haste_mod = GetHaste() * 0.01f;
|
||||||
|
|
||||||
//default value for attack timer in case they have
|
//default value for attack timer in case they have
|
||||||
//an invalid weapon equipped:
|
//an invalid weapon equipped:
|
||||||
@ -4879,28 +4899,30 @@ void Client::SetAttackTimer()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int16 DelayMod = std::max(itembonuses.HundredHands + spellbonuses.HundredHands, -99);
|
int hhe = itembonuses.HundredHands + spellbonuses.HundredHands;
|
||||||
int speed = 0;
|
int speed = 0;
|
||||||
|
int delay = 36;
|
||||||
|
float quiver_haste = 0.0f;
|
||||||
|
|
||||||
//if we have no weapon..
|
//if we have no weapon..
|
||||||
if (ItemToUse == nullptr) {
|
if (ItemToUse == nullptr) {
|
||||||
//above checks ensure ranged weapons do not fall into here
|
//above checks ensure ranged weapons do not fall into here
|
||||||
// Work out if we're a monk
|
// Work out if we're a monk
|
||||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
|
if (GetClass() == MONK || GetClass() == BEASTLORD)
|
||||||
speed = static_cast<int>((GetMonkHandToHandDelay() * (100 + DelayMod) / 100) * PermaHaste);
|
delay = GetMonkHandToHandDelay();
|
||||||
else
|
|
||||||
speed = static_cast<int>((36 * (100 + DelayMod) / 100) * PermaHaste);
|
|
||||||
} else {
|
} else {
|
||||||
//we have a weapon, use its delay
|
//we have a weapon, use its delay
|
||||||
// Convert weapon delay to timer resolution (milliseconds)
|
delay = ItemToUse->Delay;
|
||||||
//delay * 100
|
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing)
|
||||||
speed = static_cast<int>((ItemToUse->Delay * (100 + DelayMod) / 100) * PermaHaste);
|
quiver_haste = GetQuiverHaste();
|
||||||
if (ItemToUse->ItemType == ItemTypeBow || ItemToUse->ItemType == ItemTypeLargeThrowing) {
|
|
||||||
float quiver_haste = GetQuiverHaste();
|
|
||||||
if (quiver_haste > 0)
|
|
||||||
speed *= quiver_haste;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (RuleB(Spells, Jun182014HundredHandsRevamp))
|
||||||
|
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100);
|
||||||
|
else
|
||||||
|
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100);
|
||||||
|
// this is probably wrong
|
||||||
|
if (quiver_haste > 0)
|
||||||
|
speed *= quiver_haste;
|
||||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||||
|
|
||||||
if (i == MainPrimary)
|
if (i == MainPrimary)
|
||||||
@ -4910,13 +4932,24 @@ void Client::SetAttackTimer()
|
|||||||
|
|
||||||
void NPC::SetAttackTimer()
|
void NPC::SetAttackTimer()
|
||||||
{
|
{
|
||||||
float PermaHaste = GetPermaHaste();
|
float haste_mod = GetHaste() * 0.01f;
|
||||||
|
|
||||||
//default value for attack timer in case they have
|
//default value for attack timer in case they have
|
||||||
//an invalid weapon equipped:
|
//an invalid weapon equipped:
|
||||||
attack_timer.SetAtTrigger(4000, true);
|
attack_timer.SetAtTrigger(4000, true);
|
||||||
|
|
||||||
Timer *TimerToUse = nullptr;
|
Timer *TimerToUse = nullptr;
|
||||||
|
int hhe = itembonuses.HundredHands + spellbonuses.HundredHands;
|
||||||
|
|
||||||
|
// 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 = 0;
|
||||||
|
if (RuleB(Spells, Jun182014HundredHandsRevamp))
|
||||||
|
speed = static_cast<int>(((attack_delay / haste_mod) + ((hhe / 1000.0f) * (attack_delay / haste_mod))) * 100);
|
||||||
|
else
|
||||||
|
speed = static_cast<int>(((attack_delay / haste_mod) + ((hhe / 100.0f) * attack_delay)) * 100);
|
||||||
|
|
||||||
for (int i = MainRange; i <= MainSecondary; i++) {
|
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||||
//pick a timer
|
//pick a timer
|
||||||
@ -4938,13 +4971,6 @@ void NPC::SetAttackTimer()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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>((attack_delay * (100 + DelayMod) / 100) * PermaHaste);
|
|
||||||
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1546,6 +1546,11 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
|||||||
|
|
||||||
case SE_AttackSpeed4:
|
case SE_AttackSpeed4:
|
||||||
{
|
{
|
||||||
|
// These don't generate the IMMUNE_ATKSPEED message and the icon shows up
|
||||||
|
// but have no effect on the mobs attack speed
|
||||||
|
if (GetSpecialAbility(UNSLOWABLE))
|
||||||
|
break;
|
||||||
|
|
||||||
if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow)
|
if (effect_value < 0) //A few spells use negative values(Descriptions all indicate it should be a slow)
|
||||||
effect_value = effect_value * -1;
|
effect_value = effect_value * -1;
|
||||||
|
|
||||||
@ -2467,7 +2472,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case SE_ManaAbsorbPercentDamage:
|
case SE_ManaAbsorbPercentDamage:
|
||||||
{
|
{
|
||||||
if (newbon->ManaAbsorbPercentDamage[0] < effect_value){
|
if (newbon->ManaAbsorbPercentDamage[0] < effect_value){
|
||||||
@ -2488,7 +2493,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
|||||||
case SE_ShieldBlock:
|
case SE_ShieldBlock:
|
||||||
newbon->ShieldBlock += effect_value;
|
newbon->ShieldBlock += effect_value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case SE_ShieldEquipHateMod:
|
case SE_ShieldEquipHateMod:
|
||||||
newbon->ShieldEquipHateMod += effect_value;
|
newbon->ShieldEquipHateMod += effect_value;
|
||||||
break;
|
break;
|
||||||
@ -2502,6 +2507,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
|||||||
newbon->BlockBehind += effect_value;
|
newbon->BlockBehind += effect_value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SE_Blind:
|
||||||
|
newbon->IsBlind = true;
|
||||||
|
break;
|
||||||
|
|
||||||
case SE_Fear:
|
case SE_Fear:
|
||||||
newbon->IsFeared = true;
|
newbon->IsFeared = true;
|
||||||
break;
|
break;
|
||||||
@ -4081,6 +4090,10 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
|
|||||||
itembonuses.BlockBehind = effect_value;
|
itembonuses.BlockBehind = effect_value;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case SE_Blind:
|
||||||
|
spellbonuses.IsBlind = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case SE_Fear:
|
case SE_Fear:
|
||||||
spellbonuses.IsFeared = false;
|
spellbonuses.IsFeared = false;
|
||||||
break;
|
break;
|
||||||
|
|||||||
112
zone/bot.cpp
112
zone/bot.cpp
@ -8349,11 +8349,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
|||||||
if(!ca_time)
|
if(!ca_time)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float HasteModifier = 0;
|
float HasteModifier = GetHaste() * 0.01f;
|
||||||
if (GetHaste())
|
|
||||||
HasteModifier = 10000 / (100 + GetHaste());
|
|
||||||
else
|
|
||||||
HasteModifier = 100;
|
|
||||||
int32 dmg = 0;
|
int32 dmg = 0;
|
||||||
|
|
||||||
uint16 skill_to_use = -1;
|
uint16 skill_to_use = -1;
|
||||||
@ -8585,7 +8581,7 @@ void Bot::DoClassAttacks(Mob *target, bool IsRiposte) {
|
|||||||
TryBackstab(target,reuse);
|
TryBackstab(target,reuse);
|
||||||
}
|
}
|
||||||
|
|
||||||
classattack_timer.Start(reuse*HasteModifier/100);
|
classattack_timer.Start(reuse / HasteModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
|
bool Bot::TryHeadShot(Mob* defender, SkillUseTypes skillInUse) {
|
||||||
@ -8978,11 +8974,7 @@ int32 Bot::CalcMaxMana() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Bot::SetAttackTimer() {
|
void Bot::SetAttackTimer() {
|
||||||
float PermaHaste;
|
float haste_mod = GetHaste() * 0.01f;
|
||||||
if (GetHaste())
|
|
||||||
PermaHaste = 1 / (1 + (float)GetHaste()/100);
|
|
||||||
else
|
|
||||||
PermaHaste = 1.0f;
|
|
||||||
|
|
||||||
//default value for attack timer in case they have
|
//default value for attack timer in case they have
|
||||||
//an invalid weapon equipped:
|
//an invalid weapon equipped:
|
||||||
@ -8991,117 +8983,77 @@ void Bot::SetAttackTimer() {
|
|||||||
Timer* TimerToUse = nullptr;
|
Timer* TimerToUse = nullptr;
|
||||||
const Item_Struct* PrimaryWeapon = nullptr;
|
const Item_Struct* PrimaryWeapon = nullptr;
|
||||||
|
|
||||||
for (int i=MainRange; i<=MainSecondary; i++) {
|
for (int i = MainRange; i <= MainSecondary; i++) {
|
||||||
|
|
||||||
//pick a timer
|
//pick a timer
|
||||||
if (i == MainPrimary)
|
if (i == MainPrimary)
|
||||||
TimerToUse = &attack_timer;
|
TimerToUse = &attack_timer;
|
||||||
else if (i == MainRange)
|
else if (i == MainRange)
|
||||||
TimerToUse = &ranged_timer;
|
TimerToUse = &ranged_timer;
|
||||||
else if(i == MainSecondary)
|
else if (i == MainSecondary)
|
||||||
TimerToUse = &attack_dw_timer;
|
TimerToUse = &attack_dw_timer;
|
||||||
else //invalid slot (hands will always hit this)
|
else //invalid slot (hands will always hit this)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const Item_Struct* ItemToUse = nullptr;
|
const Item_Struct* ItemToUse = nullptr;
|
||||||
ItemInst* ci = GetBotItem(i);
|
ItemInst* ci = GetBotItem(i);
|
||||||
if(ci)
|
if (ci)
|
||||||
ItemToUse = ci->GetItem();
|
ItemToUse = ci->GetItem();
|
||||||
|
|
||||||
//special offhand stuff
|
//special offhand stuff
|
||||||
if(i == MainSecondary) {
|
if (i == MainSecondary) {
|
||||||
//if we have a 2H weapon in our main hand, no dual
|
//if we have a 2H weapon in our main hand, no dual
|
||||||
if(PrimaryWeapon != nullptr) {
|
if (PrimaryWeapon != nullptr) {
|
||||||
if( PrimaryWeapon->ItemClass == ItemClassCommon
|
if (PrimaryWeapon->ItemClass == ItemClassCommon
|
||||||
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
&& (PrimaryWeapon->ItemType == ItemType2HSlash
|
||||||
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
|| PrimaryWeapon->ItemType == ItemType2HBlunt
|
||||||
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
|| PrimaryWeapon->ItemType == ItemType2HPiercing)) {
|
||||||
attack_dw_timer.Disable();
|
attack_dw_timer.Disable();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//clients must have the skill to use it...
|
//clients must have the skill to use it...
|
||||||
if(!GetSkill(SkillDualWield)) {
|
if (!GetSkill(SkillDualWield)) {
|
||||||
attack_dw_timer.Disable();
|
attack_dw_timer.Disable();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//see if we have a valid weapon
|
//see if we have a valid weapon
|
||||||
if(ItemToUse != nullptr) {
|
if (ItemToUse != nullptr) {
|
||||||
//check type and damage/delay
|
//check type and damage/delay
|
||||||
if(ItemToUse->ItemClass != ItemClassCommon
|
if (ItemToUse->ItemClass != ItemClassCommon
|
||||||
|| ItemToUse->Damage == 0
|
|| ItemToUse->Damage == 0
|
||||||
|| ItemToUse->Delay == 0) {
|
|| ItemToUse->Delay == 0) {
|
||||||
//no weapon
|
//no weapon
|
||||||
ItemToUse = nullptr;
|
ItemToUse = nullptr;
|
||||||
}
|
}
|
||||||
// Check to see if skill is valid
|
// Check to see if skill is valid
|
||||||
else if((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
|
else if ((ItemToUse->ItemType > ItemTypeLargeThrowing) && (ItemToUse->ItemType != ItemTypeMartial) && (ItemToUse->ItemType != ItemType2HPiercing)) {
|
||||||
//no weapon
|
//no weapon
|
||||||
ItemToUse = nullptr;
|
ItemToUse = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int16 DelayMod = itembonuses.HundredHands + spellbonuses.HundredHands;
|
int hhe = itembonuses.HundredHands + spellbonuses.HundredHands;
|
||||||
if (DelayMod < -99)
|
int speed = 0;
|
||||||
DelayMod = -99;
|
int delay = 36;
|
||||||
|
|
||||||
//if we have no weapon..
|
//if we have no weapon..
|
||||||
if (ItemToUse == nullptr) {
|
if (ItemToUse == nullptr) {
|
||||||
//above checks ensure ranged weapons do not fall into here
|
//above checks ensure ranged weapons do not fall into here
|
||||||
// Work out if we're a monk
|
// Work out if we're a monk
|
||||||
if ((GetClass() == MONK) || (GetClass() == BEASTLORD)) {
|
if ((GetClass() == MONK) || (GetClass() == BEASTLORD))
|
||||||
//we are a monk, use special delay
|
delay = GetMonkHandToHandDelay();
|
||||||
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 {
|
} else {
|
||||||
//we have a weapon, use its delay
|
//we have a weapon, use its delay
|
||||||
// Convert weapon delay to timer resolution (milliseconds)
|
delay = ItemToUse->Delay;
|
||||||
//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 = SLOT_PERSONAL_BEGIN; r <= SLOT_PERSONAL_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 / 3);
|
|
||||||
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 (RuleB(Spells, Jun182014HundredHandsRevamp))
|
||||||
|
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 1000.0f) * (delay / haste_mod))) * 100);
|
||||||
|
else
|
||||||
|
speed = static_cast<int>(((delay / haste_mod) + ((hhe / 100.0f) * delay)) * 100);
|
||||||
|
TimerToUse->SetAtTrigger(std::max(RuleI(Combat, MinHastedDelay), speed), true);
|
||||||
|
|
||||||
if(i == MainPrimary)
|
if(i == MainPrimary)
|
||||||
PrimaryWeapon = ItemToUse;
|
PrimaryWeapon = ItemToUse;
|
||||||
|
|||||||
@ -1341,12 +1341,45 @@ int16 Client::CalcCHA() {
|
|||||||
return(CHA);
|
return(CHA);
|
||||||
}
|
}
|
||||||
|
|
||||||
int Client::CalcHaste() {
|
int Client::CalcHaste()
|
||||||
int h = spellbonuses.haste + spellbonuses.hastetype2;
|
{
|
||||||
|
/* Tests: (based on results in newer char window)
|
||||||
|
* 68 v1 + 46 item + 25 over + 35 inhib = 204%
|
||||||
|
* 46 item + 5 v2 + 25 over + 35 inhib = 65%
|
||||||
|
* 68 v1 + 46 item + 5 v2 + 25 over + 35 inhib = 209%
|
||||||
|
* 75% slow + 35 inhib = 25%
|
||||||
|
* 35 inhib = 65%
|
||||||
|
* 75% slow = 25%
|
||||||
|
* Conclusions:
|
||||||
|
* the bigger effect in slow v. inhib wins
|
||||||
|
* slow negates all other hastes
|
||||||
|
* inhib will only negate all other hastes if you don't have v1 (ex. VQ)
|
||||||
|
*/
|
||||||
|
// slow beats all! Besides a better inhibit
|
||||||
|
if (spellbonuses.haste < 0) {
|
||||||
|
if (-spellbonuses.haste <= spellbonuses.inhibitmelee)
|
||||||
|
Haste = 100 - spellbonuses.inhibitmelee;
|
||||||
|
else
|
||||||
|
Haste = 100 + spellbonuses.haste;
|
||||||
|
return Haste;
|
||||||
|
}
|
||||||
|
|
||||||
|
// No haste and inhibit, kills all other hastes
|
||||||
|
if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee) {
|
||||||
|
Haste = 100 - spellbonuses.inhibitmelee;
|
||||||
|
return Haste;
|
||||||
|
}
|
||||||
|
|
||||||
|
int h = 0;
|
||||||
int cap = 0;
|
int cap = 0;
|
||||||
int overhaste = 0;
|
|
||||||
int level = GetLevel();
|
int level = GetLevel();
|
||||||
|
|
||||||
|
// we know we have a haste spell and not slowed, no extra inhibit melee checks needed
|
||||||
|
if (spellbonuses.haste)
|
||||||
|
h += spellbonuses.haste - spellbonuses.inhibitmelee;
|
||||||
|
if (spellbonuses.hastetype2 && level > 49) // type 2 is capped at 10% and only available to 50+
|
||||||
|
h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2;
|
||||||
|
|
||||||
// 26+ no cap, 1-25 10
|
// 26+ no cap, 1-25 10
|
||||||
if (level > 25) // 26+
|
if (level > 25) // 26+
|
||||||
h += itembonuses.haste;
|
h += itembonuses.haste;
|
||||||
@ -1368,24 +1401,16 @@ int Client::CalcHaste() {
|
|||||||
|
|
||||||
// 51+ 25 (despite there being higher spells...), 1-50 10
|
// 51+ 25 (despite there being higher spells...), 1-50 10
|
||||||
if (level > 50) // 51+
|
if (level > 50) // 51+
|
||||||
overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
|
h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
|
||||||
else // 1-50
|
else // 1-50
|
||||||
overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
|
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
|
||||||
|
|
||||||
h += overhaste;
|
|
||||||
h += ExtraHaste; //GM granted haste.
|
h += ExtraHaste; //GM granted haste.
|
||||||
|
|
||||||
h = mod_client_haste(h);
|
h = mod_client_haste(h);
|
||||||
|
|
||||||
if (spellbonuses.inhibitmelee) {
|
Haste = 100 + h;
|
||||||
if (h >= 0)
|
return Haste;
|
||||||
h -= spellbonuses.inhibitmelee;
|
|
||||||
else
|
|
||||||
h -= ((100 + h) * spellbonuses.inhibitmelee / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
Haste = h;
|
|
||||||
return(Haste);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//The AA multipliers are set to be 5, but were 2 on WR
|
//The AA multipliers are set to be 5, but were 2 on WR
|
||||||
|
|||||||
@ -294,6 +294,7 @@ struct StatBonuses {
|
|||||||
int16 ResistFearChance; //i
|
int16 ResistFearChance; //i
|
||||||
bool Fearless; //i
|
bool Fearless; //i
|
||||||
bool IsFeared; //i
|
bool IsFeared; //i
|
||||||
|
bool IsBlind; //i
|
||||||
int16 StunResist; //i
|
int16 StunResist; //i
|
||||||
int16 MeleeSkillCheck; //i
|
int16 MeleeSkillCheck; //i
|
||||||
uint8 MeleeSkillCheckSkill;
|
uint8 MeleeSkillCheckSkill;
|
||||||
|
|||||||
@ -31,12 +31,10 @@
|
|||||||
#define snprintf _snprintf
|
#define snprintf _snprintf
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
extern Zone* zone;
|
extern Zone* zone;
|
||||||
|
|
||||||
#define FEAR_PATHING_DEBUG
|
#define FEAR_PATHING_DEBUG
|
||||||
|
|
||||||
|
|
||||||
//this is called whenever we are damaged to process possible fleeing
|
//this is called whenever we are damaged to process possible fleeing
|
||||||
void Mob::CheckFlee() {
|
void Mob::CheckFlee() {
|
||||||
//if were allready fleeing, dont need to check more...
|
//if were allready fleeing, dont need to check more...
|
||||||
@ -55,7 +53,7 @@ void Mob::CheckFlee() {
|
|||||||
float ratio = GetHPRatio();
|
float ratio = GetHPRatio();
|
||||||
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
||||||
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
||||||
|
|
||||||
if(ratio >= fleeratio)
|
if(ratio >= fleeratio)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@ -101,12 +99,13 @@ void Mob::CheckFlee() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mob::ProcessFlee()
|
||||||
void Mob::ProcessFlee() {
|
{
|
||||||
|
|
||||||
//Stop fleeing if effect is applied after they start to run.
|
//Stop fleeing if effect is applied after they start to run.
|
||||||
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
|
//When ImmuneToFlee effect fades it will turn fear back on and check if it can still flee.
|
||||||
if(flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) && !spellbonuses.IsFeared){
|
if (flee_mode && (GetSpecialAbility(IMMUNE_FLEEING) || spellbonuses.ImmuneToFlee) &&
|
||||||
|
!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
|
||||||
curfp = false;
|
curfp = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -114,40 +113,42 @@ void Mob::ProcessFlee() {
|
|||||||
//see if we are still dying, if so, do nothing
|
//see if we are still dying, if so, do nothing
|
||||||
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
float fleeratio = GetSpecialAbility(FLEE_PERCENT);
|
||||||
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
|
||||||
if(GetHPRatio() < fleeratio)
|
if (GetHPRatio() < fleeratio)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
//we are not dying anymore... see what we do next
|
//we are not dying anymore... see what we do next
|
||||||
|
|
||||||
flee_mode = false;
|
flee_mode = false;
|
||||||
|
|
||||||
//see if we are legitimately feared now
|
//see if we are legitimately feared or blind now
|
||||||
if(!spellbonuses.IsFeared) {
|
if (!spellbonuses.IsFeared && !spellbonuses.IsBlind) {
|
||||||
//not feared... were done...
|
//not feared or blind... were done...
|
||||||
curfp = false;
|
curfp = false;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float Mob::GetFearSpeed() {
|
float Mob::GetFearSpeed()
|
||||||
if(flee_mode) {
|
{
|
||||||
|
if (flee_mode) {
|
||||||
//we know ratio < FLEE_HP_RATIO
|
//we know ratio < FLEE_HP_RATIO
|
||||||
float speed = GetBaseRunspeed();
|
float speed = GetBaseRunspeed();
|
||||||
float ratio = GetHPRatio();
|
float ratio = GetHPRatio();
|
||||||
float multiplier = RuleR(Combat, FleeMultiplier);
|
float multiplier = RuleR(Combat, FleeMultiplier);
|
||||||
|
|
||||||
if(GetSnaredAmount() > 40)
|
if (GetSnaredAmount() > 40)
|
||||||
multiplier = multiplier / 6.0f;
|
multiplier = multiplier / 6.0f;
|
||||||
|
|
||||||
speed = speed * ratio * multiplier / 100;
|
speed = speed * ratio * multiplier / 100;
|
||||||
|
|
||||||
//NPC will eventually stop. Snares speeds this up.
|
//NPC will eventually stop. Snares speeds this up.
|
||||||
if(speed < 0.09)
|
if (speed < 0.09)
|
||||||
speed = 0.0001f;
|
speed = 0.0001f;
|
||||||
|
|
||||||
return(speed);
|
return speed;
|
||||||
}
|
}
|
||||||
return(GetRunspeed());
|
// fear and blind use their normal run speed
|
||||||
|
return GetRunspeed();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::CalculateNewFearpoint()
|
void Mob::CalculateNewFearpoint()
|
||||||
@ -209,17 +210,3 @@ void Mob::CalculateNewFearpoint()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -4619,13 +4619,7 @@ void Merc::DoClassAttacks(Mob *target) {
|
|||||||
if(!ca_time)
|
if(!ca_time)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float HasteModifier = 0;
|
float HasteModifier = GetHaste() * 0.01f;
|
||||||
if(GetHaste() > 0)
|
|
||||||
HasteModifier = 10000 / (100 + GetHaste());
|
|
||||||
else if(GetHaste() < 0)
|
|
||||||
HasteModifier = (100 - GetHaste());
|
|
||||||
else
|
|
||||||
HasteModifier = 100;
|
|
||||||
|
|
||||||
int level = GetLevel();
|
int level = GetLevel();
|
||||||
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
|
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
|
||||||
@ -4689,7 +4683,7 @@ void Merc::DoClassAttacks(Mob *target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classattack_timer.Start(reuse*HasteModifier/100);
|
classattack_timer.Start(reuse / HasteModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
bool Merc::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool IsFromSpell, ExtraAttackOptions *opts)
|
||||||
|
|||||||
37
zone/mob.cpp
37
zone/mob.cpp
@ -2735,12 +2735,29 @@ uint32 Mob::GetZoneID() const {
|
|||||||
return(zone->GetZoneID());
|
return(zone->GetZoneID());
|
||||||
}
|
}
|
||||||
|
|
||||||
int Mob::GetHaste() {
|
int Mob::GetHaste()
|
||||||
int h = spellbonuses.haste + spellbonuses.hastetype2;
|
{
|
||||||
|
// See notes in Client::CalcHaste
|
||||||
|
// Need to check if the effect of inhibit melee differs for NPCs
|
||||||
|
if (spellbonuses.haste < 0) {
|
||||||
|
if (-spellbonuses.haste <= spellbonuses.inhibitmelee)
|
||||||
|
return 100 - spellbonuses.inhibitmelee;
|
||||||
|
else
|
||||||
|
return 100 + spellbonuses.haste;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spellbonuses.haste == 0 && spellbonuses.inhibitmelee)
|
||||||
|
return 100 - spellbonuses.inhibitmelee;
|
||||||
|
|
||||||
|
int h = 0;
|
||||||
int cap = 0;
|
int cap = 0;
|
||||||
int overhaste = 0;
|
|
||||||
int level = GetLevel();
|
int level = GetLevel();
|
||||||
|
|
||||||
|
if (spellbonuses.haste)
|
||||||
|
h += spellbonuses.haste - spellbonuses.inhibitmelee;
|
||||||
|
if (spellbonuses.hastetype2 && level > 49)
|
||||||
|
h += spellbonuses.hastetype2 > 10 ? 10 : spellbonuses.hastetype2;
|
||||||
|
|
||||||
// 26+ no cap, 1-25 10
|
// 26+ no cap, 1-25 10
|
||||||
if (level > 25) // 26+
|
if (level > 25) // 26+
|
||||||
h += itembonuses.haste;
|
h += itembonuses.haste;
|
||||||
@ -2760,21 +2777,13 @@ int Mob::GetHaste() {
|
|||||||
|
|
||||||
// 51+ 25 (despite there being higher spells...), 1-50 10
|
// 51+ 25 (despite there being higher spells...), 1-50 10
|
||||||
if (level > 50) // 51+
|
if (level > 50) // 51+
|
||||||
overhaste = spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
|
h += spellbonuses.hastetype3 > 25 ? 25 : spellbonuses.hastetype3;
|
||||||
else // 1-50
|
else // 1-50
|
||||||
overhaste = spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
|
h += spellbonuses.hastetype3 > 10 ? 10 : spellbonuses.hastetype3;
|
||||||
|
|
||||||
h += overhaste;
|
|
||||||
h += ExtraHaste; //GM granted haste.
|
h += ExtraHaste; //GM granted haste.
|
||||||
|
|
||||||
if (spellbonuses.inhibitmelee) {
|
return 100 + h;
|
||||||
if (h >= 0)
|
|
||||||
h -= spellbonuses.inhibitmelee;
|
|
||||||
else
|
|
||||||
h -= ((100 + h) * spellbonuses.inhibitmelee / 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
return(h);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mob::SetTarget(Mob* mob) {
|
void Mob::SetTarget(Mob* mob) {
|
||||||
|
|||||||
@ -684,7 +684,6 @@ public:
|
|||||||
inline bool GetInvul(void) { return invulnerable; }
|
inline bool GetInvul(void) { return invulnerable; }
|
||||||
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
|
inline void SetExtraHaste(int Haste) { ExtraHaste = Haste; }
|
||||||
virtual int GetHaste();
|
virtual int GetHaste();
|
||||||
inline float GetPermaHaste() { return GetHaste() ? 100.0f / (1.0f + static_cast<float>(GetHaste()) / 100.0f) : 100.0f; }
|
|
||||||
|
|
||||||
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
|
uint8 GetWeaponDamageBonus(const Item_Struct* Weapon);
|
||||||
uint16 GetDamageTable(SkillUseTypes skillinuse);
|
uint16 GetDamageTable(SkillUseTypes skillinuse);
|
||||||
@ -769,6 +768,7 @@ public:
|
|||||||
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
|
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
|
||||||
void ProcessFlee();
|
void ProcessFlee();
|
||||||
void CheckFlee();
|
void CheckFlee();
|
||||||
|
inline bool IsBlind() { return spellbonuses.IsBlind; }
|
||||||
|
|
||||||
inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);}
|
inline bool CheckAggro(Mob* other) {return hate_list.IsOnHateList(other);}
|
||||||
float CalculateHeadingToTarget(float in_x, float in_y);
|
float CalculateHeadingToTarget(float in_x, float in_y);
|
||||||
|
|||||||
@ -1040,7 +1040,7 @@ void Mob::AI_Process() {
|
|||||||
//
|
//
|
||||||
if(RuleB(Combat, EnableFearPathing)){
|
if(RuleB(Combat, EnableFearPathing)){
|
||||||
if(curfp) {
|
if(curfp) {
|
||||||
if(IsRooted()) {
|
if(IsRooted() || (IsBlind() && CombatRange(hate_list.GetClosest(this)))) {
|
||||||
//make sure everybody knows were not moving, for appearance sake
|
//make sure everybody knows were not moving, for appearance sake
|
||||||
if(IsMoving())
|
if(IsMoving())
|
||||||
{
|
{
|
||||||
@ -1087,7 +1087,9 @@ void Mob::AI_Process() {
|
|||||||
|
|
||||||
if (engaged)
|
if (engaged)
|
||||||
{
|
{
|
||||||
if (IsRooted())
|
// we are prevented from getting here if we are blind and don't have a target in range
|
||||||
|
// from above, so no extra blind checks needed
|
||||||
|
if (IsRooted() || IsBlind())
|
||||||
SetTarget(hate_list.GetClosest(this));
|
SetTarget(hate_list.GetClosest(this));
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2672,45 +2672,47 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam
|
|||||||
// Query for an existing phrase and id in the saylink table
|
// Query for an existing phrase and id in the saylink table
|
||||||
std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string);
|
std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string);
|
||||||
auto results = database.QueryDatabase(query);
|
auto results = database.QueryDatabase(query);
|
||||||
if(results.Success())
|
if (results.Success()) {
|
||||||
{
|
if (results.RowCount() >= 1) {
|
||||||
if (results.RowCount() >= 1)
|
|
||||||
for (auto row = results.begin();row != results.end(); ++row)
|
for (auto row = results.begin();row != results.end(); ++row)
|
||||||
sayid = atoi(row[0]);
|
sayid = atoi(row[0]);
|
||||||
else // Add a new saylink entry to the database and query it again for the new sayid number
|
} else { // Add a new saylink entry to the database and query it again for the new sayid number
|
||||||
{
|
std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string);
|
||||||
query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string);
|
results = database.QueryDatabase(insert_query);
|
||||||
results = database.QueryDatabase(query);
|
if (!results.Success()) {
|
||||||
|
LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
|
||||||
if(!results.Success())
|
} else {
|
||||||
LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
|
results = database.QueryDatabase(query);
|
||||||
else if (results.RowCount() >= 1)
|
if (results.Success()) {
|
||||||
for(auto row = results.begin(); row != results.end(); ++row)
|
if (results.RowCount() >= 1)
|
||||||
sayid = atoi(row[0]);
|
for(auto row = results.begin(); row != results.end(); ++row)
|
||||||
|
sayid = atoi(row[0]);
|
||||||
|
} else {
|
||||||
|
LogFile->write(EQEMuLog::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
safe_delete_array(escaped_string);
|
safe_delete_array(escaped_string);
|
||||||
|
|
||||||
if(silent)
|
if (silent)
|
||||||
sayid = sayid + 750000;
|
sayid = sayid + 750000;
|
||||||
else
|
else
|
||||||
sayid = sayid + 500000;
|
sayid = sayid + 500000;
|
||||||
|
|
||||||
//Create the say link as an item link hash
|
//Create the say link as an item link hash
|
||||||
char linktext[250];
|
char linktext[250];
|
||||||
|
|
||||||
if(initiator)
|
if (initiator) {
|
||||||
{
|
|
||||||
if (initiator->GetClientVersion() >= EQClientRoF)
|
if (initiator->GetClientVersion() >= EQClientRoF)
|
||||||
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
|
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
|
||||||
else if (initiator->GetClientVersion() >= EQClientSoF)
|
else if (initiator->GetClientVersion() >= EQClientSoF)
|
||||||
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12);
|
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"00000000000000000000000000000000000000000000",LinkName,0x12);
|
||||||
else
|
else
|
||||||
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12);
|
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"000000000000000000000000000000000000000",LinkName,0x12);
|
||||||
}
|
} else { // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones.
|
||||||
else // If no initiator, create an RoF saylink, since older clients handle RoF ones better than RoF handles older ones.
|
|
||||||
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
|
sprintf(linktext,"%c%06X%s%s%c",0x12,sayid,"0000000000000000000000000000000000000000000000000",LinkName,0x12);
|
||||||
|
}
|
||||||
|
|
||||||
strcpy(Phrase,linktext);
|
strcpy(Phrase,linktext);
|
||||||
return Phrase;
|
return Phrase;
|
||||||
|
|||||||
@ -1412,11 +1412,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
|||||||
if(!ca_time)
|
if(!ca_time)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
float HasteModifier = 0;
|
float HasteModifier = GetHaste() * 0.01f;
|
||||||
if (GetHaste())
|
|
||||||
HasteModifier = 10000 / (100 + GetHaste());
|
|
||||||
else
|
|
||||||
HasteModifier = 100;
|
|
||||||
|
|
||||||
int level = GetLevel();
|
int level = GetLevel();
|
||||||
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
|
int reuse = TauntReuseTime * 1000; //make this very long since if they dont use it once, they prolly never will
|
||||||
@ -1568,7 +1564,7 @@ void NPC::DoClassAttacks(Mob *target) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classattack_timer.Start(reuse*HasteModifier/100);
|
classattack_timer.Start(reuse / HasteModifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
||||||
@ -1592,20 +1588,13 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
|||||||
}
|
}
|
||||||
|
|
||||||
int ReuseTime = 0;
|
int ReuseTime = 0;
|
||||||
int ClientHaste = GetHaste();
|
float HasteMod = GetHaste() * 0.01f;
|
||||||
int HasteMod = 0;
|
|
||||||
|
|
||||||
if(ClientHaste >= 0){
|
|
||||||
HasteMod = (10000/(100+ClientHaste)); //+100% haste = 2x as many attacks
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
HasteMod = (100-ClientHaste); //-100% haste = 1/2 as many attacks
|
|
||||||
}
|
|
||||||
int32 dmg = 0;
|
int32 dmg = 0;
|
||||||
|
|
||||||
uint16 skill_to_use = -1;
|
uint16 skill_to_use = -1;
|
||||||
|
|
||||||
if (skill == -1){
|
if (skill == -1){
|
||||||
switch(GetClass()){
|
switch(GetClass()){
|
||||||
case WARRIOR:
|
case WARRIOR:
|
||||||
case RANGER:
|
case RANGER:
|
||||||
@ -1677,8 +1666,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ReuseTime = BashReuseTime-1;
|
ReuseTime = (BashReuseTime - 1) / HasteMod;
|
||||||
ReuseTime = (ReuseTime*HasteMod)/100;
|
|
||||||
|
|
||||||
DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime);
|
DoSpecialAttackDamage(ca_target, SkillBash, dmg, 1,-1,ReuseTime);
|
||||||
|
|
||||||
@ -1705,8 +1693,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
|||||||
if (min_dmg > max_dmg)
|
if (min_dmg > max_dmg)
|
||||||
max_dmg = min_dmg;
|
max_dmg = min_dmg;
|
||||||
|
|
||||||
ReuseTime = FrenzyReuseTime-1;
|
ReuseTime = (FrenzyReuseTime - 1) / HasteMod;
|
||||||
ReuseTime = (ReuseTime*HasteMod)/100;
|
|
||||||
|
|
||||||
//Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit.
|
//Live parses show around 55% Triple 35% Double 10% Single, you will always get first hit.
|
||||||
while(AtkRounds > 0) {
|
while(AtkRounds > 0) {
|
||||||
@ -1781,7 +1768,7 @@ void Client::DoClassAttacks(Mob *ca_target, uint16 skill, bool IsRiposte)
|
|||||||
TryBackstab(ca_target,ReuseTime);
|
TryBackstab(ca_target,ReuseTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
ReuseTime = (ReuseTime*HasteMod)/100;
|
ReuseTime = ReuseTime / HasteMod;
|
||||||
if(ReuseTime > 0 && !IsRiposte){
|
if(ReuseTime > 0 && !IsRiposte){
|
||||||
p_timers.Start(pTimerCombatAbility, ReuseTime);
|
p_timers.Start(pTimerCombatAbility, ReuseTime);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1267,10 +1267,11 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
|||||||
#ifdef SPELL_EFFECT_SPAM
|
#ifdef SPELL_EFFECT_SPAM
|
||||||
snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value);
|
snprintf(effect_desc, _EDLEN, "Blind: %+i", effect_value);
|
||||||
#endif
|
#endif
|
||||||
if (spells[spell_id].base[i] == 1)
|
// this should catch the cures
|
||||||
|
if (BeneficialSpell(spell_id) && spells[spell_id].buffduration == 0)
|
||||||
BuffFadeByEffect(SE_Blind);
|
BuffFadeByEffect(SE_Blind);
|
||||||
// handled by client
|
else if (!IsClient())
|
||||||
// TODO: blind flag?
|
CalculateNewFearpoint();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3995,6 +3996,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case SE_Blind:
|
||||||
|
if (curfp && !FindType(SE_Fear))
|
||||||
|
curfp = false;
|
||||||
|
break;
|
||||||
|
|
||||||
case SE_Fear:
|
case SE_Fear:
|
||||||
{
|
{
|
||||||
if(RuleB(Combat, EnableFearPathing)){
|
if(RuleB(Combat, EnableFearPathing)){
|
||||||
|
|||||||
@ -433,8 +433,8 @@ void Zone::LoadTempMerchantData(){
|
|||||||
npcid = ml.npcid;
|
npcid = ml.npcid;
|
||||||
}
|
}
|
||||||
ml.slot = atoul(row[1]);
|
ml.slot = atoul(row[1]);
|
||||||
ml.item = atoul(row[2]);
|
ml.charges = atoul(row[2]);
|
||||||
ml.charges = atoul(row[3]);
|
ml.item = atoul(row[3]);
|
||||||
ml.origslot = ml.slot;
|
ml.origslot = ml.slot;
|
||||||
cur->second.push_back(ml);
|
cur->second.push_back(ml);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user