Haynar's movement fixes.

Changes Speed from float to int. EQ client deals with int step locs better than it does floats according to Haynar's testing.

This also contains mob runspeed changes. I recommend you set runspeeds to start in the DB 1.25 for NPCs below 1.25 which will match player runspeeds almost equally. Existing DBs will need to be updated.

General Cleanup of MobAI functions. Mobs now change their heading on AIMovement timers if their targets' heading has changed since that time. This prevents players from being able to land backstabs inbetween mob swings.

Charmed/feared players now send the appropriate packet, there was a missing CastToClient() in spells that was missing.

Mob runspeed can no longer be snared to 0%, instead, 1% of their base runspeed is the maximum. Roots apply as roots instead of a modifier under this code.

There is going to be bugs with this code. It's better we push through it than revert it. Sanctuary has been running this for a good week and we've worked through the issues.

Misc updates:
Exported some variables to perl, including:

EVENT_ITE_CLICK_CAST:
EVENT_ITEM_CLICK:
spell_id - returns the spell_id of the click effect.
return value - cancels the cast.

EVENT_DROP_ITEM:
quantity - returns the # of items dropped in the packet. If the item has charges, charges are returned here instead.
itemname - name of the item being dropped
itemid - id of the item being droppped
spell_id - spell_id associated with the item's click effect.
slotid - the inventory slot id of the item being dropped.
return value - cancels the item from being dropped.

Added Perl function: CalcEXP. Calculates the experience you would gain for an NPC that cons a specific con value to you.

Fixed a bug where you would receive the group experience bonus and group experience messages for simply being in a group, regardless of the player being in the same zone as you.
This commit is contained in:
SecretsOTheP 2015-05-25 12:35:53 -04:00
parent aaca6fd2d9
commit 788959a5e2
22 changed files with 751 additions and 419 deletions

View File

@ -335,8 +335,8 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
} }
//FatherNitwit: New style haste, shields, and regens //FatherNitwit: New style haste, shields, and regens
if(newbon->haste < (int32)item->Haste) { if((int32)item->Haste > 0) {
newbon->haste = item->Haste; newbon->haste += item->Haste;
} }
if(item->Regen > 0) if(item->Regen > 0)
newbon->HPRegen += item->Regen; newbon->HPRegen += item->Regen;
@ -390,10 +390,10 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
newbon->HitChance += item->Accuracy; newbon->HitChance += item->Accuracy;
} }
if(item->CombatEffects > 0) { if(item->CombatEffects > 0) {
if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap)) if((newbon->MeleeDamage + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap))
newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap); newbon->MeleeDamage = RuleI(Character, ItemCombatEffectsCap);
else else
newbon->ProcChance += item->CombatEffects; newbon->MeleeDamage += item->CombatEffects;
} }
if(item->DotShielding > 0) { if(item->DotShielding > 0) {
if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap)) if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap))
@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
int buff_count = GetMaxTotalSlots(); int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) { for(i = 0; i < buff_count; i++) {
if(buffs[i].spellid != SPELL_UNKNOWN){ if(buffs[i].spellid != SPELL_UNKNOWN){
ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining, i, buffs[i].instrument_mod); ApplySpellsBonuses(buffs[i].spellid, buffs[i].casterlevel, newbon, buffs[i].casterid, 0, buffs[i].ticsremaining,i);
if (buffs[i].numhits > 0) if (buffs[i].numhits > 0)
Numhits(true); Numhits(true);
@ -1472,9 +1472,8 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells. if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
} }
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId, void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot,
uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod, bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{ {
int i, effect_value, base2, max, effectid; int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false; bool AdditiveWornBonus = false;
@ -1512,7 +1511,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
AdditiveWornBonus = true; AdditiveWornBonus = true;
effectid = spells[spell_id].effectid[i]; effectid = spells[spell_id].effectid[i];
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, instrument_mod, caster, ticsremaining); effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
base2 = spells[spell_id].base2[i]; base2 = spells[spell_id].base2[i];
max = spells[spell_id].max[i]; max = spells[spell_id].max[i];
} }
@ -1561,49 +1560,100 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_AttackSpeed: case SE_AttackSpeed:
{ {
if ((effect_value - 100) > 0) { // Haste
if (new_bonus->haste < 0) break; // Slowed - Don't apply haste if (AdditiveWornBonus) {
if ((effect_value - 100) > new_bonus->haste) { if ((effect_value - 100) > 0) { // Haste
new_bonus->haste = effect_value - 100; if (new_bonus->haste < 0) break; // Slowed - Don't apply haste
if ((effect_value - 100) > new_bonus->haste) {
new_bonus->haste += effect_value - 100;
}
}
else if ((effect_value - 100) < 0) { // Slow
int real_slow_value = (100 - effect_value) * -1;
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
if (real_slow_value < new_bonus->haste)
new_bonus->haste += real_slow_value;
} }
} }
else if ((effect_value - 100) < 0) { // Slow else
int real_slow_value = (100 - effect_value) * -1; {
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); if ((effect_value - 100) > 0) { // Haste
if (real_slow_value < new_bonus->haste) if (new_bonus->haste < 0) break; // Slowed - Don't apply haste
new_bonus->haste = real_slow_value; if ((effect_value - 100) > new_bonus->haste) {
new_bonus->haste = effect_value - 100;
}
}
else if ((effect_value - 100) < 0) { // Slow
int real_slow_value = (100 - effect_value) * -1;
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
if (real_slow_value < new_bonus->haste)
new_bonus->haste = real_slow_value;
}
} }
break; break;
} }
case SE_AttackSpeed2: case SE_AttackSpeed2:
{ {
if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap if (AdditiveWornBonus) {
if (new_bonus->hastetype2 < 0) break; //Slowed - Don't apply haste2 if ((effect_value - 100) > 0) { // Haste
if ((effect_value - 100) > new_bonus->hastetype2) { if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste
new_bonus->hastetype2 = effect_value - 100; if ((effect_value - 100) > new_bonus->hastetype2) {
new_bonus->hastetype2 += effect_value - 100;
}
}
else if ((effect_value - 100) < 0) { // Slow
int real_slow_value = (100 - effect_value) * -1;
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
if (real_slow_value < new_bonus->hastetype2)
new_bonus->hastetype2 += real_slow_value;
} }
} }
else if ((effect_value - 100) < 0) { // Slow else
int real_slow_value = (100 - effect_value) * -1; {
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100)); if ((effect_value - 100) > 0) { // Haste
if (real_slow_value < new_bonus->hastetype2) if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste
new_bonus->hastetype2 = real_slow_value; if ((effect_value - 100) > new_bonus->hastetype2) {
new_bonus->hastetype2 = effect_value - 100;
}
}
else if ((effect_value - 100) < 0) { // Slow
int real_slow_value = (100 - effect_value) * -1;
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
if (real_slow_value < new_bonus->hastetype2)
new_bonus->hastetype2 = real_slow_value;
}
} }
break; break;
} }
case SE_AttackSpeed3: case SE_AttackSpeed3:
{ {
if (effect_value < 0){ //Slow if (AdditiveWornBonus) {
effect_value -= ((effect_value * GetSlowMitigation()/100)); if (effect_value < 0){ //Slow
if (effect_value < new_bonus->hastetype3) effect_value -= ((effect_value * GetSlowMitigation()/100));
new_bonus->hastetype3 = effect_value; if (effect_value < new_bonus->hastetype3)
} new_bonus->hastetype3 += effect_value;
}
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
if (effect_value > new_bonus->hastetype3) { if (effect_value > new_bonus->hastetype3) {
new_bonus->hastetype3 = effect_value; new_bonus->hastetype3 += effect_value;
}
}
}
else
{
if (effect_value < 0){ //Slow
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value < new_bonus->hastetype3)
new_bonus->hastetype3 = effect_value;
}
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
if (effect_value > new_bonus->hastetype3) {
new_bonus->hastetype3 = effect_value;
}
} }
} }
break; break;
@ -1618,13 +1668,21 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
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;
if (AdditiveWornBonus) {
if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) { if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) {
effect_value -= ((effect_value * GetSlowMitigation()/100)); effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value > new_bonus->inhibitmelee) if (effect_value > new_bonus->inhibitmelee)
new_bonus->inhibitmelee = effect_value; new_bonus->inhibitmelee += effect_value;
}
}
else
{
if (effect_value > 0 && effect_value > new_bonus->inhibitmelee) {
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value > new_bonus->inhibitmelee)
new_bonus->inhibitmelee = effect_value;
}
} }
break; break;
} }
@ -3109,7 +3167,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon)
newbon->HitChance += cur->Accuracy; newbon->HitChance += cur->Accuracy;
} }
if(cur->CombatEffects > 0) { if(cur->CombatEffects > 0) {
newbon->ProcChance += cur->CombatEffects; newbon->MeleeDamage += cur->CombatEffects;
} }
if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type); ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type);

View File

@ -292,9 +292,7 @@ void Bot::ChangeBotArcherWeapons(bool isArcher) {
void Bot::Sit() { void Bot::Sit() {
if(IsMoving()) { if(IsMoving()) {
moved = false; moved = false;
// SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
tar_ndx = 0; tar_ndx = 0;
} }
@ -3448,11 +3446,10 @@ void Bot::AI_Process() {
if(IsMoving()) { if(IsMoving()) {
SetHeading(0); SetHeading(0);
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
SetCurrentSpeed(GetRunSpeed());
if(moved) { if(moved) {
moved = false; SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
} }
} }
@ -3495,11 +3492,10 @@ void Bot::AI_Process() {
if(IsMoving()) { if(IsMoving()) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
SetCurrentSpeed(0);
if(moved) { if(moved) {
moved = false; moved = false;
SendPosition(); SetCurrentSpeed(0);
SetMoving(false);
} }
} }
@ -3517,11 +3513,10 @@ void Bot::AI_Process() {
if(IsMoving()) { if(IsMoving()) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
SetCurrentSpeed(0);
if(moved) { if(moved) {
moved = false; moved = false;
SendPosition(); SetCurrentSpeed(0);
SetMoving(false);
} }
} }
@ -3740,7 +3735,7 @@ void Bot::AI_Process() {
if(follow) { if(follow) {
float dist = DistanceSquared(m_Position, follow->GetPosition()); float dist = DistanceSquared(m_Position, follow->GetPosition());
float speed = follow->GetRunspeed(); int speed = follow->GetRunspeed();
if(dist < GetFollowDistance() + 1000) if(dist < GetFollowDistance() + 1000)
speed = follow->GetWalkspeed(); speed = follow->GetWalkspeed();
@ -3757,9 +3752,8 @@ void Bot::AI_Process() {
{ {
if(moved) if(moved)
{ {
moved=false; moved = false;
SendPosition(); SetCurrentSpeed(0);
SetMoving(false);
} }
} }
} }
@ -3987,6 +3981,7 @@ void Bot::PetAIProcess() {
botPet->SetHeading(botPet->GetTarget()->GetHeading()); botPet->SetHeading(botPet->GetTarget()->GetHeading());
if(moved) { if(moved) {
moved=false; moved=false;
SetCurrentSpeed(0);
botPet->SendPosition(); botPet->SendPosition();
botPet->SetMoving(false); botPet->SetMoving(false);
} }
@ -4020,6 +4015,7 @@ void Bot::PetAIProcess() {
botPet->SetHeading(botPet->GetTarget()->GetHeading()); botPet->SetHeading(botPet->GetTarget()->GetHeading());
if(moved) { if(moved) {
moved=false; moved=false;
SetCurrentSpeed(0);
botPet->SendPosition(); botPet->SendPosition();
botPet->SetMoving(false); botPet->SetMoving(false);
} }

View File

@ -4991,7 +4991,7 @@ void Client::SetShadowStepExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000) if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{ {
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed(); int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{ {
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__, printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
@ -5048,7 +5048,7 @@ void Client::SetKnockBackExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000) if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{ {
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed(); int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{ {
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@ -5105,7 +5105,7 @@ void Client::SetPortExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000) if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{ {
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed(); int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{ {
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))

View File

@ -557,6 +557,7 @@ public:
void SendCrystalCounts(); void SendCrystalCounts();
void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false); void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
uint32 CalcEXP(uint8 conlevel = 0xFF);
void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false); void SetEXP(uint32 set_exp, uint32 set_aaxp, bool resexp=false);
void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0); void AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0);
void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp); void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp);
@ -1129,7 +1130,6 @@ public:
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); } inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses(); void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); } inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
inline void ResetPositionTimer() { position_timer_counter = 0; }
void SendAltCurrencies(); void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount); void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0); void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);

View File

@ -4266,7 +4266,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
if((cur_time - m_TimeSinceLastPositionCheck) > 0) if((cur_time - m_TimeSinceLastPositionCheck) > 0)
{ {
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed(); int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{ {
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
@ -4334,7 +4334,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
if((cur_time - m_TimeSinceLastPositionCheck) > 2500) if((cur_time - m_TimeSinceLastPositionCheck) > 2500)
{ {
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck); float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed(); int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor))) if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{ {
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor))))) if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))

View File

@ -1484,7 +1484,7 @@ void command_npcstats(Client *c, const Seperator *sep)
c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP()); c->Message(0, "Current HP: %i Max HP: %i", c->GetTarget()->GetHP(), c->GetTarget()->GetMaxHP());
//c->Message(0, "Weapon Item Number: %s", c->GetTarget()->GetWeapNo()); //c->Message(0, "Weapon Item Number: %s", c->GetTarget()->GetWeapNo());
c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType()); c->Message(0, "Gender: %i Size: %f Bodytype: %d", c->GetTarget()->GetGender(), c->GetTarget()->GetSize(), c->GetTarget()->GetBodyType());
c->Message(0, "Runspeed: %f Walkspeed: %f", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed()); c->Message(0, "Runspeed: %i Walkspeed: %i", c->GetTarget()->GetRunspeed(), c->GetTarget()->GetWalkspeed());
c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid()); c->Message(0, "Spawn Group: %i Grid: %i", c->GetTarget()->CastToNPC()->GetSp2(), c->GetTarget()->CastToNPC()->GetGrid());
c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID()); c->Message(0, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID());
c->GetTarget()->CastToNPC()->QueryLoot(c); c->GetTarget()->CastToNPC()->QueryLoot(c);

View File

@ -138,7 +138,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
value -= GetFocusEffect(focusFcDamageAmt, spell_id); value -= GetFocusEffect(focusFcDamageAmt, spell_id);
if(itembonuses.SpellDmg && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) if(itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value); value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg, value);
if (IsNPC() && CastToNPC()->GetSpellScale()) if (IsNPC() && CastToNPC()->GetSpellScale())
@ -200,6 +200,11 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
extra_dmg /= duration; extra_dmg /= duration;
} }
//Sanctuary Custom: Spelldmg per tick
if(itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg / 6, value); //per tick
value -= extra_dmg; value -= extra_dmg;
} }
@ -211,23 +216,6 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg) int32 Mob::GetExtraSpellAmt(uint16 spell_id, int32 extra_spell_amt, int32 base_spell_dmg)
{ {
int total_cast_time = 0;
if (spells[spell_id].recast_time >= spells[spell_id].recovery_time)
total_cast_time = spells[spell_id].recast_time + spells[spell_id].cast_time;
else
total_cast_time = spells[spell_id].recovery_time + spells[spell_id].cast_time;
if (total_cast_time > 0 && total_cast_time <= 2500)
extra_spell_amt = extra_spell_amt*25/100;
else if (total_cast_time > 2500 && total_cast_time < 7000)
extra_spell_amt = extra_spell_amt*(167*((total_cast_time - 1000)/1000)) / 1000;
else
extra_spell_amt = extra_spell_amt * total_cast_time / 7000;
if(extra_spell_amt*2 < base_spell_dmg)
return 0;
return extra_spell_amt; return extra_spell_amt;
} }
@ -270,7 +258,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
value += GetFocusEffect(focusFcHealAmt, spell_id); value += GetFocusEffect(focusFcHealAmt, spell_id);
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id); value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
if(itembonuses.HealAmt && spells[spell_id].classes[(GetClass()%16) - 1] >= GetLevel() - 5) if(itembonuses.HealAmt)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier; value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt, value) * modifier;
value += value*target->GetHealRate(spell_id, this)/100; value += value*target->GetHealRate(spell_id, this)/100;
@ -301,6 +289,9 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if(chance && zone->random.Roll(chance)) if(chance && zone->random.Roll(chance))
value *= 2; value *= 2;
if(itembonuses.HealAmt)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt / 6, value) * modifier;
} }
if (IsNPC() && CastToNPC()->GetHealScale()) if (IsNPC() && CastToNPC()->GetHealScale())
@ -421,10 +412,14 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
int tic_inc = 0; int tic_inc = 0;
tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id); tic_inc = GetFocusEffect(focusSpellDurByTic, spell_id);
// unsure on the exact details, but bard songs that don't cost mana at some point get an extra tick, 60 for now // Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
// a level 53 bard reported getting 2 tics // However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60) if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
tic_inc++; IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) ||
IsBlindSpell(spell_id))
tic_inc += 1;
return (((duration * increase) / 100) + tic_inc); return (((duration * increase) / 100) + tic_inc);
} }

View File

@ -1321,6 +1321,7 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "itemid", objid); ExportVar(package_name.c_str(), "itemid", objid);
ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name); ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name);
ExportVar(package_name.c_str(), "slotid", extradata); ExportVar(package_name.c_str(), "slotid", extradata);
ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect);
break; break;
} }
@ -1399,6 +1400,14 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]); ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
break; break;
} }
case EVENT_DROP_ITEM: {
ExportVar(package_name.c_str(), "quantity", iteminst->IsStackable() ? iteminst->GetCharges() : 1);
ExportVar(package_name.c_str(), "itemname", iteminst->GetItem()->Name);
ExportVar(package_name.c_str(), "itemid", iteminst->GetItem()->ID);
ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect);
ExportVar(package_name.c_str(), "slotid", extradata);
break;
}
default: { default: {
break; break;

View File

@ -59,6 +59,97 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level)
return 10; return 10;
} }
uint32 Client::CalcEXP(uint8 conlevel) {
uint32 in_add_exp = EXP_FORMULA;
if((XPRate != 0))
in_add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
float totalmod = 1.0;
float zemmod = 1.0;
//get modifiers
if(RuleR(Character, ExpMultiplier) >= 0){
totalmod *= RuleR(Character, ExpMultiplier);
}
if(zone->newzone_data.zone_exp_multiplier >= 0){
zemmod *= zone->newzone_data.zone_exp_multiplier;
}
if(RuleB(Character,UseRaceClassExpBonuses))
{
if(GetBaseRace() == HALFLING){
totalmod *= 1.05;
}
if(GetClass() == ROGUE || GetClass() == WARRIOR){
totalmod *= 1.05;
}
}
if(zone->IsHotzone())
{
totalmod += RuleR(Zone, HotZoneBonus);
}
in_add_exp = uint32(float(in_add_exp) * totalmod * zemmod);
if(RuleB(Character,UseXPConScaling))
{
if (conlevel != 0xFF) {
switch (conlevel)
{
case CON_GREEN:
in_add_exp = 0;
return 0;
case CON_LIGHTBLUE:
in_add_exp = in_add_exp * RuleI(Character, LightBlueModifier)/100;
break;
case CON_BLUE:
in_add_exp = in_add_exp * RuleI(Character, BlueModifier)/100;
break;
case CON_WHITE:
in_add_exp = in_add_exp * RuleI(Character, WhiteModifier)/100;
break;
case CON_YELLOW:
in_add_exp = in_add_exp * RuleI(Character, YellowModifier)/100;
break;
case CON_RED:
in_add_exp = in_add_exp * RuleI(Character, RedModifier)/100;
break;
}
}
}
float aatotalmod = 1.0;
if(zone->newzone_data.zone_exp_multiplier >= 0){
aatotalmod *= zone->newzone_data.zone_exp_multiplier;
}
if(RuleB(Character,UseRaceClassExpBonuses))
{
if(GetBaseRace() == HALFLING){
aatotalmod *= 1.05;
}
if(GetClass() == ROGUE || GetClass() == WARRIOR){
aatotalmod *= 1.05;
}
}
if(RuleB(Zone, LevelBasedEXPMods)){
if(zone->level_exp_mod[GetLevel()].ExpMod){
in_add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
}
}
return in_add_exp;
}
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) { void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
this->EVENT_ITEM_ScriptStopReturn(); this->EVENT_ITEM_ScriptStopReturn();
@ -78,7 +169,7 @@ void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
//figure out how much of this goes to AAs //figure out how much of this goes to AAs
add_aaxp = add_exp * m_epp.perAA / 100; add_aaxp = add_exp * m_epp.perAA / 100;
//take that ammount away from regular exp //take that amount away from regular exp
add_exp -= add_aaxp; add_exp -= add_aaxp;
float totalmod = 1.0; float totalmod = 1.0;
@ -247,12 +338,22 @@ void Client::SetEXP(uint32 set_exp, uint32 set_aaxp, bool isrezzexp) {
Message(13, "Error in Client::SetEXP. EXP not set."); Message(13, "Error in Client::SetEXP. EXP not set.");
return; // Must be invalid class/race return; // Must be invalid class/race
} }
uint32 i = 0;
uint32 membercount = 0;
if(GetGroup())
{
for (i = 0; i < MAX_GROUP_MEMBERS; i++) {
if (GetGroup()->members[i] != nullptr) {
membercount++;
}
}
}
if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) { if ((set_exp + set_aaxp) > (m_pp.exp+m_pp.expAA)) {
if (isrezzexp) if (isrezzexp)
this->Message_StringID(MT_Experience, REZ_REGAIN); this->Message_StringID(MT_Experience, REZ_REGAIN);
else{ else{
if(this->IsGrouped()) if(membercount > 1)
this->Message_StringID(MT_Experience, GAIN_GROUPXP); this->Message_StringID(MT_Experience, GAIN_GROUPXP);
else if(IsRaidGrouped()) else if(IsRaidGrouped())
Message_StringID(MT_Experience, GAIN_RAIDEXP); Message_StringID(MT_Experience, GAIN_RAIDEXP);
@ -604,8 +705,8 @@ void Group::SplitExp(uint32 exp, Mob* other) {
groupmod = 2.16; groupmod = 2.16;
else else
groupmod = 1.0; groupmod = 1.0;
if(membercount > 1 && membercount < 6)
groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier))); groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier)));
int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel()); int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel());
if(conlevel == CON_GREEN) if(conlevel == CON_GREEN)

View File

@ -123,29 +123,6 @@ void Mob::ProcessFlee()
} }
} }
float Mob::GetFearSpeed()
{
if (flee_mode) {
//we know ratio < FLEE_HP_RATIO
float speed = GetBaseRunspeed();
float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
if (GetSnaredAmount() > 40)
multiplier = multiplier / 6.0f;
speed = speed * ratio * multiplier / 100;
//NPC will eventually stop. Snares speeds this up.
if (speed < 0.09)
speed = 0.0001f;
return speed;
}
// fear and blind use their normal run speed
return GetRunspeed();
}
void Mob::CalculateNewFearpoint() void Mob::CalculateNewFearpoint()
{ {
if(RuleB(Pathing, Fear) && zone->pathing) if(RuleB(Pathing, Fear) && zone->pathing)

View File

@ -606,7 +606,7 @@ void Client::DropItem(int16 slot_id)
// Take control of item in client inventory // Take control of item in client inventory
ItemInst *inst = m_inv.PopItem(slot_id); ItemInst *inst = m_inv.PopItem(slot_id);
if(inst) { if(inst) {
int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", 0); int i = parse->EventItem(EVENT_DROP_ITEM, this, inst, nullptr, "", slot_id);
if(i != 0) { if(i != 0) {
safe_delete(inst); safe_delete(inst);
} }

View File

@ -1460,8 +1460,7 @@ void Merc::AI_Process() {
if(moved) { if(moved) {
moved = false; moved = false;
SendPosition(); SetCurrentSpeed(0);
SetMoving(false);
} }
} }
@ -1497,9 +1496,7 @@ void Merc::AI_Process() {
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
if(moved) { if(moved) {
moved = false; SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
} }
} }
@ -1710,7 +1707,7 @@ void Merc::AI_Process() {
if(follow) if(follow)
{ {
float dist = DistanceSquared(m_Position, follow->GetPosition()); float dist = DistanceSquared(m_Position, follow->GetPosition());
float speed = GetRunspeed(); int speed = GetRunspeed();
if(dist < GetFollowDistance() + 1000) if(dist < GetFollowDistance() + 1000)
speed = GetWalkspeed(); speed = GetWalkspeed();
@ -1727,9 +1724,8 @@ void Merc::AI_Process() {
{ {
if(moved) if(moved)
{ {
moved=false; SetCurrentSpeed(0);
SendPosition(); moved = false;
SetMoving(false);
} }
} }
} }

View File

@ -26,6 +26,7 @@
#include <limits.h> #include <limits.h>
#include <math.h> #include <math.h>
#include <sstream> #include <sstream>
#include <algorithm>
#ifdef BOTS #ifdef BOTS
#include "bot.h" #include "bot.h"
@ -148,6 +149,28 @@ Mob::Mob(const char* in_name,
size = in_size; size = in_size;
base_size = size; base_size = size;
runspeed = in_runspeed; runspeed = in_runspeed;
// neotokyo: sanity check
if (runspeed < 0 || runspeed > 20)
runspeed = 1.25f;
base_runspeed = (int)((float)runspeed * 40.0f);
// clients
if (runspeed == 0.7f) {
base_runspeed = 28;
walkspeed = 0.3f;
base_walkspeed = 12;
fearspeed = 0.625f;
base_fearspeed = 25;
// npcs
} else {
base_walkspeed = base_runspeed * 100 / 265;
walkspeed = ((float)base_walkspeed) * 0.025f;
base_fearspeed = base_runspeed * 100 / 127;
fearspeed = ((float)base_fearspeed) * 0.025f;
}
current_speed = base_runspeed;
m_PlayerState = 0; m_PlayerState = 0;
@ -531,48 +554,32 @@ bool Mob::IsInvisible(Mob* other) const
return(false); return(false);
} }
float Mob::_GetMovementSpeed(int mod) const int Mob::_GetWalkSpeed() const {
{
// List of movement speed modifiers, including AAs & spells: if (IsRooted() || IsStunned() || IsMezzed())
// http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352 return 0;
if (IsRooted())
return 0.0f;
else if (IsPseudoRooted()) else if (IsPseudoRooted())
return 0.00001f; return 0;
float speed_mod = runspeed; int aa_mod = 0;
int speed_mod = base_walkspeed;
int base_run = base_runspeed;
bool has_horse = false;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod += aabonuses.BaseMovementSpeed;
// These two cases ignore the cap, be wise in the DB for horses.
if (IsClient()) { if (IsClient()) {
if (CastToClient()->GetGMSpeed()) { Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
speed_mod = 3.125f; if (horse) {
if (mod != 0) speed_mod = horse->GetBaseRunspeed();
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
return speed_mod; return speed_mod;
} else {
Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
if (horse) {
speed_mod = horse->GetBaseRunspeed();
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
return speed_mod;
}
} }
} }
int aa_mod = 0; int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int spell_mod = 0;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
int movemod = 0; int movemod = 0;
float frunspeedcap = 0.0f;
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod += itembonuses.BaseMovementSpeed + spellbonuses.BaseMovementSpeed + aabonuses.BaseMovementSpeed;
spell_mod += spellbonuses.movementspeed + itembonuses.movementspeed;
// hard cap
if (runspeedcap > 225)
runspeedcap = 225;
if (spell_mod < 0) if (spell_mod < 0)
movemod += spell_mod; movemod += spell_mod;
@ -581,27 +588,189 @@ float Mob::_GetMovementSpeed(int mod) const
else else
movemod = aa_mod; movemod = aa_mod;
// cap negative movemods from snares mostly // hard cap
if (movemod < -85) if (runspeedcap > 225)
runspeedcap = 225;
if(movemod < -85) //cap it at moving very very slow
movemod = -85; movemod = -85;
if (movemod != 0) if (!has_horse && movemod != 0)
speed_mod += speed_mod * static_cast<float>(movemod) / 100.0f; speed_mod += (base_run * movemod / 100);
// runspeed caps if(speed_mod < 1)
frunspeedcap = static_cast<float>(runspeedcap) / 100.0f; return(1);
if (IsClient() && speed_mod > frunspeedcap)
speed_mod = frunspeedcap;
// apply final mod such as the -47 for walking //runspeed cap.
// use runspeed since it should stack with snares if(IsClient())
// and if we get here, we know runspeed was the initial {
// value before we applied movemod. if(speed_mod > runspeedcap)
if (mod != 0) speed_mod = runspeedcap;
speed_mod += runspeed * static_cast<float>(mod) / 100.0f; }
return speed_mod;
}
if (speed_mod <= 0.0f) int Mob::_GetRunSpeed() const {
speed_mod = IsClient() ? 0.0001f : 0.0f; if (IsRooted() || IsStunned() || IsMezzed())
return 0;
int aa_mod = 0;
int speed_mod = base_runspeed;
int base_walk = base_walkspeed;
bool has_horse = false;
if (IsClient())
{
if(CastToClient()->GetGMSpeed())
{
speed_mod = 325;
}
else
{
Mob* horse = entity_list.GetMob(CastToClient()->GetHorseId());
if(horse)
{
speed_mod = horse->GetBaseRunspeed();
base_walk = horse->GetBaseWalkspeed();
has_horse = true;
}
}
}
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
runspeedcap += itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
aa_mod = itembonuses.IncreaseRunSpeedCap + spellbonuses.IncreaseRunSpeedCap + aabonuses.IncreaseRunSpeedCap;
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int movemod = 0;
if(spell_mod < 0)
{
movemod += spell_mod;
}
else if(spell_mod > aa_mod)
{
movemod = spell_mod;
}
else
{
movemod = aa_mod;
}
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (!has_horse && movemod != 0)
{
if (IsClient())
{
speed_mod += (speed_mod * movemod / 100);
} else {
if (movemod < 0) {
speed_mod += (50 * movemod / 100);
// basically stoped
if(speed_mod < 1)
{
return(1);
}
// moving slowly
if (speed_mod < 8)
return(8);
} else {
speed_mod += GetBaseWalkspeed();
if (movemod > 50)
speed_mod += 4;
if (movemod > 40)
speed_mod += 3;
}
}
}
if(speed_mod < 1)
{
return(1);
}
//runspeed cap.
if(IsClient())
{
if(speed_mod > runspeedcap)
speed_mod = runspeedcap;
}
return speed_mod;
}
int Mob::_GetFearSpeed() const {
if (IsRooted() || IsStunned() || IsMezzed())
return 0;
//float speed_mod = fearspeed;
int speed_mod = GetBaseFearSpeed();
// use a max of 1.75f in calcs.
int base_run = std::min(GetBaseRunspeed(), 70);
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
int movemod = 0;
if(spell_mod < 0)
{
movemod += spell_mod;
}
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (IsClient()) {
if (CastToClient()->IsRunning())
speed_mod = GetBaseRunspeed();
else
speed_mod = GetBaseWalkspeed();
if (movemod < 0)
return GetBaseWalkspeed();
speed_mod += (base_run * movemod / 100);
return speed_mod;
} else {
int hp_ratio = GetIntHPRatio();
// very large snares 50% or higher
if (movemod < -49)
{
if (hp_ratio < 25)
{
return (1);
}
if (hp_ratio < 50)
return (8);
else
return (12);
}
if (hp_ratio < 5) {
speed_mod = base_walkspeed / 3;
} else if (hp_ratio < 15) {
speed_mod = base_walkspeed / 2;
} else if (hp_ratio < 25) {
speed_mod = base_walkspeed + 1; // add the +1 so they do the run animation
} else if (hp_ratio < 50) {
speed_mod *= 82;
speed_mod /= 100;
}
if (movemod > 0) {
speed_mod += GetBaseWalkspeed();
if (movemod > 50)
speed_mod += 4;
if (movemod > 40)
speed_mod += 3;
return speed_mod;
}
else if (movemod < 0) {
speed_mod += (base_run * movemod / 100);
}
}
if (speed_mod < 1)
return (1);
if (speed_mod < 9)
return (8);
if (speed_mod < 13)
return (12);
return speed_mod; return speed_mod;
} }
@ -1201,7 +1370,6 @@ void Mob::SendPosition()
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer; PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer;
MakeSpawnUpdateNoDelta(spu); MakeSpawnUpdateNoDelta(spu);
move_tic_count = 0; move_tic_count = 0;
tar_ndx = 20;
entity_list.QueueClients(this, app, true); entity_list.QueueClients(this, app, true);
safe_delete(app); safe_delete(app);
} }
@ -1303,7 +1471,7 @@ void Mob::ShowStats(Client* client)
if(n->respawn2 != 0) if(n->respawn2 != 0)
spawngroupid = n->respawn2->SpawnGroupID(); spawngroupid = n->respawn2->SpawnGroupID();
client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID()); client->Message(0, " NPCID: %u SpawnGroupID: %u Grid: %i LootTable: %u FactionID: %i SpellsID: %u ", GetNPCTypeID(),spawngroupid, n->GetGrid(), n->GetLoottableID(), n->GetNPCFactionID(), n->GetNPCSpellsID());
client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %f Walkspeed: %f", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed()); client->Message(0, " Accuracy: %i MerchantID: %i EmoteID: %i Runspeed: %u Walkspeed: %u", n->GetAccuracyRating(), n->MerchantType, n->GetEmoteID(), n->GetRunspeed(), n->GetWalkspeed());
n->QueryLoot(client); n->QueryLoot(client);
} }
if (IsAIControlled()) { if (IsAIControlled()) {
@ -5437,3 +5605,15 @@ void Mob::SendRemovePlayerState(PlayerState old_state)
safe_delete(app); safe_delete(app);
} }
void Mob::SetCurrentSpeed(int in){
if (current_speed != in)
{
current_speed = in;
tar_ndx = 20;
if (in == 0) {
SetRunAnimSpeed(0);
SetMoving(false);
SendPosition();
}
}
}

View File

@ -364,6 +364,7 @@ public:
inline Mob* GetTarget() const { return target; } inline Mob* GetTarget() const { return target; }
virtual void SetTarget(Mob* mob); virtual void SetTarget(Mob* mob);
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); } virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); }
virtual inline float GetIntHPRatio() const { return max_hp == 0 ? 0 : (cur_hp/max_hp*100); }
inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; } inline virtual int32 GetAC() const { return AC + itembonuses.AC + spellbonuses.AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; } inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; } inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
@ -441,9 +442,12 @@ public:
virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); } virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); }
virtual void GoToBind(uint8 bindnum = 0) { } virtual void GoToBind(uint8 bindnum = 0) { }
virtual void Gate(); virtual void Gate();
float GetWalkspeed() const { return(_GetMovementSpeed(-47)); } int GetWalkspeed() const { return(_GetWalkSpeed()); }
float GetRunspeed() const { return(_GetMovementSpeed(0)); } int GetRunspeed() const { return(_GetRunSpeed()); }
float GetBaseRunspeed() const { return runspeed; } void SetCurrentSpeed(int in);
int GetBaseRunspeed() const { return base_runspeed; }
int GetBaseWalkspeed() const { return base_walkspeed; }
int GetBaseFearSpeed() const { return base_fearspeed; }
float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); } float GetMovespeed() const { return IsRunning() ? GetRunspeed() : GetWalkspeed(); }
bool IsRunning() const { return m_is_running; } bool IsRunning() const { return m_is_running; }
void SetRunning(bool val) { m_is_running = val; } void SetRunning(bool val) { m_is_running = val; }
@ -801,7 +805,7 @@ public:
//old fear function //old fear function
//void SetFeared(Mob *caster, uint32 duration, bool flee = false); //void SetFeared(Mob *caster, uint32 duration, bool flee = false);
float GetFearSpeed(); int GetFearSpeed() { return _GetFearSpeed(); }
bool IsFeared() { return (spellbonuses.IsFeared || flee_mode); } // This returns true if the mob is feared or fleeing due to low HP bool IsFeared() { return (spellbonuses.IsFeared || flee_mode); } // This returns true if the mob is feared or fleeing due to low HP
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); } inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
void ProcessFlee(); void ProcessFlee();
@ -810,8 +814,8 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);} inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y); float CalculateHeadingToTarget(float in_x, float in_y);
bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false); bool CalculateNewPosition(float x, float y, float z, int speed, bool checkZ = false, bool calcheading = true);
virtual bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true); virtual bool CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ = true, bool calcheading = true);
float CalculateDistance(float x, float y, float z); float CalculateDistance(float x, float y, float z);
float GetGroundZ(float new_x, float new_y, float z_offset=0.0); float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
void SendTo(float new_x, float new_y, float new_z); void SendTo(float new_x, float new_y, float new_z);
@ -882,6 +886,8 @@ public:
Timer *GetSpecialAbilityTimer(int ability); Timer *GetSpecialAbilityTimer(int ability);
void ClearSpecialAbilities(); void ClearSpecialAbilities();
void ProcessSpecialAbilities(const std::string &str); void ProcessSpecialAbilities(const std::string &str);
bool IsMoved() { return moved; }
void SetMoved(bool moveflag) { moved = moveflag; }
Shielders_Struct shielder[MAX_SHIELDERS]; Shielders_Struct shielder[MAX_SHIELDERS];
Trade* trade; Trade* trade;
@ -951,7 +957,10 @@ protected:
void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic); void CommonDamage(Mob* other, int32 &damage, const uint16 spell_id, const SkillUseTypes attack_skill, bool &avoidable, const int8 buffslot, const bool iBuffTic);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index); static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
float _GetMovementSpeed(int mod) const; float _GetMovementSpeed(int mod) const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ); int _GetWalkSpeed() const;
int _GetRunSpeed() const;
int _GetFearSpeed() const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ);
virtual bool AI_EngagedCastCheck() { return(false); } virtual bool AI_EngagedCastCheck() { return(false); }
virtual bool AI_PursueCastCheck() { return(false); } virtual bool AI_PursueCastCheck() { return(false); }
@ -1048,6 +1057,13 @@ protected:
float base_size; float base_size;
float size; float size;
float runspeed; float runspeed;
float walkspeed;
float fearspeed;
int base_runspeed;
int base_walkspeed;
int base_fearspeed;
int current_speed;
uint32 pLastChange; uint32 pLastChange;
bool held; bool held;
bool nocast; bool nocast;

View File

@ -339,9 +339,7 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
//stop moving if were casting a spell and were not a bard... //stop moving if were casting a spell and were not a bard...
if(!IsBardSong(AIspells[i].spellid)) { if(!IsBardSong(AIspells[i].spellid)) {
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
} }
return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust)); return CastSpell(AIspells[i].spellid, tar->GetID(), 1, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0, &(AIspells[i].resist_adjust));
@ -698,9 +696,7 @@ void Client::AI_SpellCast()
{ {
if(!IsBardSong(spell_to_cast)) if(!IsBardSong(spell_to_cast))
{ {
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
} }
CastSpell(spell_to_cast, tar->GetID(), slot_to_use); CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return; return;
@ -714,9 +710,7 @@ void Client::AI_SpellCast()
{ {
if(!IsBardSong(spell_to_cast)) if(!IsBardSong(spell_to_cast))
{ {
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
} }
CastSpell(spell_to_cast, tar->GetID(), slot_to_use); CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return; return;
@ -772,16 +766,13 @@ void Client::AI_Process()
{ {
if(GetTarget()) if(GetTarget())
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
} }
//continue on to attack code, ensuring that we execute the engaged code //continue on to attack code, ensuring that we execute the engaged code
engaged = true; engaged = true;
} else { } else {
if(AImovement_timer->Check()) { if(AImovement_timer->Check()) {
animation = GetRunspeed() * 21; //animation = GetFearSpeed() * 21;
// Check if we have reached the last fear point // Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) && if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) { (std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
@ -839,16 +830,13 @@ void Client::AI_Process()
} }
if (AImovement_timer->Check()) { if (AImovement_timer->Check()) {
SetRunAnimSpeed(0); if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
} }
if(IsMoving()) {
SetMoving(false);
moved=false;
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
tar_ndx =0;
}
if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) { if(GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if(attack_timer.Check()) { if(attack_timer.Check()) {
Attack(GetTarget(), MainPrimary); Attack(GetTarget(), MainPrimary);
@ -944,28 +932,27 @@ void Client::AI_Process()
{ {
if(!IsRooted()) if(!IsRooted())
{ {
animation = 21 * GetRunspeed(); if(AImovement_timer->Check())
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
else
{ {
bool WaypointChanged, NodeReached; if(!RuleB(Pathing, Aggro) || !zone->pathing)
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
GetRunspeed(), WaypointChanged, NodeReached); else
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(WaypointChanged) if(WaypointChanged)
tar_ndx = 20; tar_ndx = 20;
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed()); CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
}
} }
} }
else if(IsMoving()) else if(IsMoving())
{ {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
} }
} }
AI_SpellCast(); AI_SpellCast();
@ -998,21 +985,23 @@ void Client::AI_Process()
return; return;
float dist = DistanceSquared(m_Position, owner->GetPosition()); float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 100) if (dist >= 400)
{ {
float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed(); if(AImovement_timer->Check())
animation = 21 * speed; {
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
} }
else else
{ {
SetHeading(owner->GetHeading());
if(moved) if(moved)
{ {
moved=false; SetCurrentSpeed(0);
SetMoving(false); moved = false;
SendPosition();
SetRunAnimSpeed(0);
} }
} }
} }
@ -1042,9 +1031,7 @@ void Mob::AI_Process() {
{ {
if(target) if(target)
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
moved=false; moved=false;
} }
//continue on to attack code, ensuring that we execute the engaged code //continue on to attack code, ensuring that we execute the engaged code
@ -1058,7 +1045,9 @@ void Mob::AI_Process() {
CalculateNewFearpoint(); CalculateNewFearpoint();
} }
if(!RuleB(Pathing, Fear) || !zone->pathing) if(!RuleB(Pathing, Fear) || !zone->pathing)
{
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true); CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true);
}
else else
{ {
bool WaypointChanged, NodeReached; bool WaypointChanged, NodeReached;
@ -1156,15 +1145,21 @@ void Mob::AI_Process() {
{ {
if (AImovement_timer->Check()) if (AImovement_timer->Check())
{ {
SetRunAnimSpeed(0); if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
} }
if(IsMoving()) if(IsMoving())
{ {
SetMoving(false); if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
moved=false; {
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition(); SendPosition();
tar_ndx =0; }
SetCurrentSpeed(0);
} }
//casting checked above... //casting checked above...
@ -1369,7 +1364,7 @@ void Mob::AI_Process() {
CastToNPC()->DoClassAttacks(target); CastToNPC()->DoClassAttacks(target);
} }
AI_EngagedCastCheck(); AI_EngagedCastCheck();
} //end is within combat range } //end is within combat rangepet
else { else {
//we cannot reach our target... //we cannot reach our target...
//underwater stuff only works with water maps in the zone! //underwater stuff only works with water maps in the zone!
@ -1425,10 +1420,7 @@ void Mob::AI_Process() {
} }
else if(IsMoving()) { else if(IsMoving()) {
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY())); SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
} }
} }
@ -1481,7 +1473,6 @@ void Mob::AI_Process() {
} }
else if (AImovement_timer->Check() && !IsRooted()) else if (AImovement_timer->Check() && !IsRooted())
{ {
SetRunAnimSpeed(0);
if (IsPet()) if (IsPet())
{ {
// we're a pet, do as we're told // we're a pet, do as we're told
@ -1500,18 +1491,18 @@ void Mob::AI_Process() {
float dist = DistanceSquared(m_Position, owner->GetPosition()); float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 400) if (dist >= 400)
{ {
float speed = GetWalkspeed(); int speed = GetWalkspeed();
if (dist >= 5625) if (dist >= 5625)
speed = GetRunspeed(); speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed); CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
} }
else else
{ {
if(moved) if(moved)
{ {
moved=false; SetCurrentSpeed(0);
SetMoving(false); moved = false;
SendPosition();
} }
} }
@ -1557,19 +1548,15 @@ void Mob::AI_Process() {
if (dist2 >= followdist) // Default follow distance is 100 if (dist2 >= followdist) // Default follow distance is 100
{ {
float speed = GetWalkspeed(); int speed = GetWalkspeed();
if (dist2 >= followdist + 150) if (dist2 >= followdist + 150)
speed = GetRunspeed(); speed = GetRunspeed();
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed); CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
} }
else else
{ {
if(moved) moved = false;
{ SetCurrentSpeed(0);
SendPosition();
moved=false;
SetMoving(false);
}
} }
} }
} }
@ -1666,40 +1653,92 @@ void NPC::AI_DoMovement() {
if (gridno > 0 || cur_wp==-2) { if (gridno > 0 || cur_wp==-2) {
if (movetimercompleted==true) { // time to pause at wp is over if (movetimercompleted==true) { // time to pause at wp is over
AI_SetupNextWaypoint();
int32 spawn_id = this->GetSpawnPointID();
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
Spawn2 *found_spawn = nullptr;
while(iterator.MoreElements())
{
Spawn2* cur = iterator.GetData();
iterator.Advance();
if(cur->GetID() == spawn_id)
{
found_spawn = cur;
break;
}
}
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and resart spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted=false;
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if(cur_wp == -2) {
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
}
if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false);
entity_list.OpenDoorsNear(CastToNPC());
if(!DistractedFromGrid) {
//kick off event_waypoint depart
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
//setup our next waypoint, if we are still on our normal grid
//remember that the quest event above could have done anything it wanted with our grid
if(gridno > 0) {
CastToNPC()->CalculateNewWaypoint();
}
}
else {
DistractedFromGrid = false;
}
}
} // endif (movetimercompleted==true) } // endif (movetimercompleted==true)
else if (!(AIwalking_timer->Enabled())) else if (!(AIwalking_timer->Enabled()))
{ // currently moving { // currently moving
bool doMove = true;
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY())
{ // are we there yet? then stop { // are we there yet? then stop
Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid()); Log.Out(Logs::Detail, Logs::AI, "We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d", cur_wp, GetX(), GetY(), GetZ(), GetGrid());
if (cur_wp_pause != 0) { SetWaypointPause();
SetWaypointPause(); if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false); SetAppearance(eaStanding, false);
SetMoving(false); SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) { if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w); SetHeading(m_CurrentWayPoint.w);
}
SendPosition();
} }
SendPosition();
//kick off event_waypoint arrive //kick off event_waypoint arrive
char temp[16]; char temp[16];
sprintf(temp, "%d", cur_wp); sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0); parse->EventNPC(EVENT_WAYPOINT_ARRIVE, CastToNPC(), nullptr, temp, 0);
// start moving directly to next waypoint if we're at a 0 pause waypoint and we didn't get quest halted.
if (!AIwalking_timer->Enabled())
AI_SetupNextWaypoint();
else
doMove = false;
// wipe feign memory since we reached our first waypoint // wipe feign memory since we reached our first waypoint
if(cur_wp == 1) if(cur_wp == 1)
ClearFeignMemory(); ClearFeignMemory();
} }
if (doMove) else
{ // not at waypoint yet or at 0 pause WP, so keep moving { // not at waypoint yet, so keep moving
if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0)) if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true); CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true);
else else
@ -1727,7 +1766,8 @@ void NPC::AI_DoMovement() {
SetGrid( 0 - GetGrid()); // revert to AI control SetGrid( 0 - GetGrid()); // revert to AI control
Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid()); Log.Out(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid());
SetAppearance(eaStanding, false); if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false);
CalculateNewWaypoint(); CalculateNewWaypoint();
} }
@ -1763,86 +1803,28 @@ void NPC::AI_DoMovement() {
Log.Out(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z); Log.Out(Logs::Detail, Logs::AI, "Reached guard point (%.3f,%.3f,%.3f)", m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z);
ClearFeignMemory(); ClearFeignMemory();
moved=false; moved=false;
SetMoving(false);
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 ) if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 )
{ {
SetHeading(m_GuardPoint.w); SetHeading(m_GuardPoint.w);
} else { } else {
FaceTarget(GetTarget()); FaceTarget(GetTarget());
} }
SendPosition(); SetCurrentSpeed(0);
SetAppearance(GetGuardPointAnim()); SetAppearance(GetGuardPointAnim());
} }
} }
} }
} }
void NPC::AI_SetupNextWaypoint() {
int32 spawn_id = this->GetSpawnPointID();
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
iterator.Reset();
Spawn2 *found_spawn = nullptr;
while (iterator.MoreElements())
{
Spawn2* cur = iterator.GetData();
iterator.Advance();
if (cur->GetID() == spawn_id)
{
found_spawn = cur;
break;
}
}
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and restart spawn timer
if (found_spawn)
found_spawn->SetNPCPointerNull();
}
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer
if (found_spawn)
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted = false;
Log.Out(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if (cur_wp == -2) {
Log.Out(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
}
SetAppearance(eaStanding, false);
entity_list.OpenDoorsNear(CastToNPC());
if (!DistractedFromGrid) {
//kick off event_waypoint depart
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
//setup our next waypoint, if we are still on our normal grid
//remember that the quest event above could have done anything it wanted with our grid
if (GetGrid() > 0) {
CastToNPC()->CalculateNewWaypoint();
}
}
else {
DistractedFromGrid = false;
}
}
}
// Note: Mob that caused this may not get added to the hate list until after this function call completes // Note: Mob that caused this may not get added to the hate list until after this function call completes
void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) { void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
if (!IsAIControlled()) if (!IsAIControlled())
return; return;
SetAppearance(eaStanding); if(GetAppearance() != eaStanding)
{
SetAppearance(eaStanding);
}
if (iYellForHelp) { if (iYellForHelp) {
if(IsPet()) { if(IsPet()) {
@ -1889,9 +1871,10 @@ void Mob::AI_Event_NoLongerEngaged() {
pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving); pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
// So mobs don't keep running as a ghost until AIwalking_timer fires // So mobs don't keep running as a ghost until AIwalking_timer fires
// if they were moving prior to losing all hate // if they were moving prior to losing all hate
if(IsMoving()){ // except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving
if(!IsPet())
{
SetRunAnimSpeed(0); SetRunAnimSpeed(0);
SetMoving(false);
SendPosition(); SendPosition();
} }
ClearRampage(); ClearRampage();

View File

@ -119,7 +119,6 @@ public:
virtual void AI_Start(uint32 iMoveDelay = 0); virtual void AI_Start(uint32 iMoveDelay = 0);
virtual void AI_Stop(); virtual void AI_Stop();
void AI_DoMovement(); void AI_DoMovement();
void AI_SetupNextWaypoint();
bool AI_AddNPCSpells(uint32 iDBSpellsID); bool AI_AddNPCSpells(uint32 iDBSpellsID);
bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID); bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID);
virtual bool AI_EngagedCastCheck(); virtual bool AI_EngagedCastCheck();

View File

@ -6188,6 +6188,35 @@ XS(XS_Client_GetTargetRingZ)
XSRETURN(1); XSRETURN(1);
} }
XS(XS_Client_CalcEXP); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_CalcEXP)
{
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: CalcEXP(THIS, uint8 conlevel)");
{
Client * THIS;
uint8 conlevel = 0xFF;
uint32 RETVAL;
if(items == 2)
conlevel = (uint16)SvUV(ST(1));
dXSTARG;
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV*)SvRV(ST(0)));
THIS = INT2PTR(Client *,tmp);
}
else
Perl_croak(aTHX_ "THIS is not of type Client");
if(THIS == NULL)
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
RETVAL = THIS->CalcEXP(conlevel);
XSprePUSH; PUSHi((IV)RETVAL);
}
XSRETURN(1);
}
XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */ XS(XS_Client_QuestReward); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_QuestReward) XS(XS_Client_QuestReward)
{ {
@ -6482,6 +6511,7 @@ XS(boot_Client)
newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$"); newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$"); newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$");
XSRETURN_YES; XSRETURN_YES;
} }

View File

@ -787,8 +787,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if (IsClient()) if (IsClient())
{ {
AI_Start(); CastToClient()->AI_Start();
SendAppearancePacket(14, 100, true, true);
} else if(IsNPC()) { } else if(IsNPC()) {
CastToNPC()->SetPetSpellID(0); //not a pet spell. CastToNPC()->SetPetSpellID(0); //not a pet spell.
} }
@ -877,8 +876,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
if(RuleB(Combat, EnableFearPathing)){ if(RuleB(Combat, EnableFearPathing)){
if(IsClient()) if(IsClient())
{ {
AI_Start(); CastToClient()->AI_Start();
animation = static_cast<uint16>(GetRunspeed() * 21.0f); //set our animation to match our speed about
} }
CalculateNewFearpoint(); CalculateNewFearpoint();
@ -3911,9 +3909,9 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if(IsNPC()) if(IsNPC())
{ {
CastToNPC()->RestoreGuardSpotCharm(); CastToNPC()->RestoreGuardSpotCharm();
SendAppearancePacket(AT_Pet, 0, true, true);
} }
SendAppearancePacket(AT_Pet, 0, true, true);
Mob* tempmob = GetOwner(); Mob* tempmob = GetOwner();
SetOwnerID(0); SetOwnerID(0);
if(tempmob) if(tempmob)
@ -3943,12 +3941,12 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{ {
InterruptSpell(); InterruptSpell();
if (this->CastToClient()->IsLD()) if (this->CastToClient()->IsLD())
AI_Start(CLIENT_LD_TIMEOUT); CastToClient()->AI_Start(CLIENT_LD_TIMEOUT);
else else
{ {
bool feared = FindType(SE_Fear); bool feared = FindType(SE_Fear);
if(!feared) if(!feared)
AI_Stop(); CastToClient()->AI_Stop();
} }
} }
break; break;
@ -3973,7 +3971,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{ {
bool charmed = FindType(SE_Charm); bool charmed = FindType(SE_Charm);
if(!charmed) if(!charmed)
AI_Stop(); CastToClient()->AI_Stop();
} }
if(curfp) { if(curfp) {

View File

@ -4754,14 +4754,12 @@ void Client::UnStun() {
void NPC::Stun(int duration) { void NPC::Stun(int duration) {
Mob::Stun(duration); Mob::Stun(duration);
SetRunAnimSpeed(0); SetCurrentSpeed(0);
SendPosition();
} }
void NPC::UnStun() { void NPC::UnStun() {
Mob::UnStun(); Mob::UnStun();
SetRunAnimSpeed(static_cast<int8>(GetRunspeed())); SetCurrentSpeed(GetRunspeed());
SendPosition();
} }
void Mob::Mesmerize() void Mob::Mesmerize()

View File

@ -1561,6 +1561,8 @@ bool ZoneDatabase::EnableRecipe(uint32 recipe_id)
if (!results.Success()) if (!results.Success())
return results.RowsAffected() > 0; return results.RowsAffected() > 0;
return false;
} }
bool ZoneDatabase::DisableRecipe(uint32 recipe_id) bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
@ -1571,4 +1573,6 @@ bool ZoneDatabase::DisableRecipe(uint32 recipe_id)
if (!results.Success()) if (!results.Success())
return results.RowsAffected() > 0; return results.RowsAffected() > 0;
return false;
} }

View File

@ -394,7 +394,6 @@ void NPC::SetWaypointPause()
if (cur_wp_pause == 0) { if (cur_wp_pause == 0) {
AIwalking_timer->Start(100); AIwalking_timer->Start(100);
AIwalking_timer->Trigger();
} }
else else
{ {
@ -437,9 +436,8 @@ void NPC::NextGuardPosition() {
{ {
if(moved) if(moved)
{ {
moved=false; moved = false;
SetMoving(false); SetCurrentSpeed(0);
SendPosition();
} }
} }
} }
@ -490,10 +488,16 @@ float Mob::CalculateHeadingToTarget(float in_x, float in_y) {
return (256*(360-angle)/360.0f); return (256*(360-angle)/360.0f);
} }
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool checkZ) { bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ) {
if(GetID()==0) if(GetID()==0)
return true; return true;
if(speed == 0)
{
SetCurrentSpeed(0);
return true;
}
if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords if ((m_Position.x-x == 0) && (m_Position.y-y == 0)) {//spawn is at target coords
if(m_Position.z-z != 0) { if(m_Position.z-z != 0) {
m_Position.z = z; m_Position.z = z;
@ -515,9 +519,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
return true; return true;
} }
bool send_update = false;
int compare_steps = IsBoat() ? 1 : 20; int compare_steps = IsBoat() ? 1 : 20;
if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) { if(tar_ndx < compare_steps && m_TargetLocation.x==x && m_TargetLocation.y==y) {
float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_x = m_Position.x + m_TargetV.x*tar_vector;
@ -589,7 +591,12 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
m_TargetV.x = x - nx; m_TargetV.x = x - nx;
m_TargetV.y = y - ny; m_TargetV.y = y - ny;
m_TargetV.z = z - nz; m_TargetV.z = z - nz;
SetCurrentSpeed((int8)speed);
pRunAnimSpeed = speed;
if(IsClient())
{
animation = speed;
}
//pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO); //pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
//speed *= NPC_SPEED_MULTIPLIER; //speed *= NPC_SPEED_MULTIPLIER;
@ -599,10 +606,10 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
// 2: get unit vector // 2: get unit vector
// -------------------------------------------------------------------------- // --------------------------------------------------------------------------
float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z); float mag = sqrtf (m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z);
tar_vector = speed / mag; tar_vector = (float)speed / mag;
// mob move fix // mob move fix
int numsteps = (int) ( mag * 20 / speed) + 1; int numsteps = (int) ( mag * 16.0f / (float)speed);
// mob move fix // mob move fix
@ -612,9 +619,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
if (numsteps>1) if (numsteps>1)
{ {
tar_vector=1.0f ; tar_vector=1.0f ;
m_TargetV.x = m_TargetV.x/numsteps; m_TargetV.x = 1.25f * m_TargetV.x/(float)numsteps;
m_TargetV.y = m_TargetV.y/numsteps; m_TargetV.y = 1.25f * m_TargetV.y/(float)numsteps;
m_TargetV.z = m_TargetV.z/numsteps; m_TargetV.z = 1.25f *m_TargetV.z/(float)numsteps;
float new_x = m_Position.x + m_TargetV.x; float new_x = m_Position.x + m_TargetV.x;
float new_y = m_Position.y + m_TargetV.y; float new_y = m_Position.y + m_TargetV.y;
@ -640,14 +647,13 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
m_Position.y = y; m_Position.y = y;
m_Position.z = z; m_Position.z = z;
tar_ndx = 20;
Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping."); Log.Out(Logs::Detail, Logs::AI, "Only a single step to get there... jumping.");
} }
} }
else { else {
tar_vector/=20.0f; tar_vector/=16.0f;
float new_x = m_Position.x + m_TargetV.x*tar_vector; float new_x = m_Position.x + m_TargetV.x*tar_vector;
float new_y = m_Position.y + m_TargetV.y*tar_vector; float new_y = m_Position.y + m_TargetV.y*tar_vector;
@ -703,32 +709,20 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f); m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f);
if (IsClient()) if (IsClient())
{
SendPosUpdate(1); SendPosUpdate(1);
CastToClient()->ResetPositionTimer();
}
else else
{
// force an update now
move_tic_count = RuleI(Zone, NPCPositonUpdateTicCount);
SendPosUpdate(); SendPosUpdate();
SetAppearance(eaStanding, false);
}
pLastChange = Timer::GetCurrentTime();
SetAppearance(eaStanding, false);
pLastChange = Timer::GetCurrentTime();
return true; return true;
} }
bool Mob::CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ) { bool Mob::CalculateNewPosition2(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
if(IsNPC() || IsClient() || IsPet()) {
pRunAnimSpeed = (int8)(speed*NPC_RUNANIM_RATIO);
speed *= NPC_SPEED_MULTIPLIER;
}
return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ); return MakeNewPositionAndSendUpdate(x, y, z, speed, checkZ);
} }
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool checkZ) { bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
if(GetID()==0) if(GetID()==0)
return true; return true;
@ -737,14 +731,12 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
float nz = m_Position.z; float nz = m_Position.z;
// if NPC is rooted // if NPC is rooted
if (speed == 0.0) { if (speed == 0) {
SetHeading(CalculateHeadingToTarget(x, y)); SetHeading(CalculateHeadingToTarget(x, y));
if(moved){ if(moved){
SendPosition(); SetCurrentSpeed(0);
SetMoving(false);
moved=false; moved=false;
} }
SetRunAnimSpeed(0);
Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z); Log.Out(Logs::Detail, Logs::AI, "Rooted while calculating new position to (%.3f, %.3f, %.3f)", x, y, z);
return true; return true;
} }
@ -756,8 +748,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
if (m_TargetV.x == 0 && m_TargetV.y == 0) if (m_TargetV.x == 0 && m_TargetV.y == 0)
return false; return false;
pRunAnimSpeed = (uint8)(speed*NPC_RUNANIM_RATIO); SetCurrentSpeed((int8)(speed)); //*NPC_RUNANIM_RATIO);
speed *= NPC_SPEED_MULTIPLIER; //speed *= NPC_SPEED_MULTIPLIER;
Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed); Log.Out(Logs::Detail, Logs::AI, "Calculating new position to (%.3f, %.3f, %.3f) vector (%.3f, %.3f, %.3f) rate %.3f RAS %d", x, y, z, m_TargetV.x, m_TargetV.y, m_TargetV.z, speed, pRunAnimSpeed);

View File

@ -2209,9 +2209,9 @@ void Zone::ReloadWorld(uint32 Option){
entity_list.ClearAreas(); entity_list.ClearAreas();
parse->ReloadQuests(); parse->ReloadQuests();
} else if(Option == 1) { } else if(Option == 1) {
zone->Repop(0);
entity_list.ClearAreas(); entity_list.ClearAreas();
parse->ReloadQuests(); parse->ReloadQuests();
zone->Repop(0);
} }
} }