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
if(newbon->haste < (int32)item->Haste) {
newbon->haste = item->Haste;
if((int32)item->Haste > 0) {
newbon->haste += item->Haste;
}
if(item->Regen > 0)
newbon->HPRegen += item->Regen;
@ -390,10 +390,10 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
newbon->HitChance += item->Accuracy;
}
if(item->CombatEffects > 0) {
if((newbon->ProcChance + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap))
newbon->ProcChance = RuleI(Character, ItemCombatEffectsCap);
if((newbon->MeleeDamage + item->CombatEffects) > RuleI(Character, ItemCombatEffectsCap))
newbon->MeleeDamage = RuleI(Character, ItemCombatEffectsCap);
else
newbon->ProcChance += item->CombatEffects;
newbon->MeleeDamage += item->CombatEffects;
}
if(item->DotShielding > 0) {
if((newbon->DoTShielding + item->DotShielding) > RuleI(Character, ItemDoTShieldingCap))
@ -428,7 +428,7 @@ void Client::AddItemBonuses(const ItemInst *inst, StatBonuses* newbon, bool isAu
newbon->DSMitigation += item->DSMitigation;
}
if (item->Worn.Effect > 0 && item->Worn.Type == ET_WornEffect) {// latent effects
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);
}
if (item->Focus.Effect>0 && (item->Focus.Type == ET_Focus)) { // focus effects
@ -559,7 +559,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool
/*
Powerful Non-live like option allows developers to add worn effects on items that
can stack with other worn effects of the same spell effect type, instead of only taking the highest value.
Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus.
Ie Cleave I = 40 pct cleave - So if you equip 3 cleave I items you will have a 120 pct cleave bonus.
To enable use RuleI(Spells, AdditiveBonusWornType)
Setting value = 2 Will force all live items to automatically be calculated additivily
Setting value to anything else will indicate the item 'worntype' that if set to the same, will cause the bonuses to use this calculation
@ -579,7 +579,7 @@ void Client::AdditiveWornBonuses(const ItemInst *inst, StatBonuses* newbon, bool
if(GetLevel() < item->ReqLevel)
return;
if (item->Worn.Effect > 0 && item->Worn.Type == RuleI(Spells, AdditiveBonusWornType))
ApplySpellsBonuses(item->Worn.Effect, item->Worn.Level, newbon, 0, item->Worn.Type);// Non-live like - Addititive latent effects
@ -691,7 +691,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
continue;
Log.Out(Logs::Detail, Logs::AA, "Applying Effect %d from AA %u in slot %d (base1: %d, base2: %d) on %s", effect, aaid, slot, base1, base2, this->GetCleanName());
uint8 focus = IsFocusEffect(0, 0, true,effect);
if (focus)
{
@ -1007,7 +1007,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_BlockBehind:
newbon->BlockBehind += base1;
break;
case SE_StrikeThrough:
case SE_StrikeThrough2:
newbon->StrikeThrough += base1;
@ -1313,7 +1313,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
case SE_Vampirism:
newbon->Vampirism += base1;
break;
break;
case SE_FrenziedDevastation:
newbon->FrenziedDevastation += base2;
@ -1416,7 +1416,7 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
}
case SE_SkillProcSuccess:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(newbon->SkillProcSuccess[e] && newbon->SkillProcSuccess[e] == aaid)
@ -1449,7 +1449,7 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
int buff_count = GetMaxTotalSlots();
for(i = 0; i < buff_count; i++) {
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)
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.
}
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *new_bonus, uint16 casterId,
uint8 WornType, int32 ticsremaining, int buffslot, int instrument_mod,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* new_bonus, uint16 casterId, uint8 WornType, uint32 ticsremaining, int buffslot,
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
{
int i, effect_value, base2, max, effectid;
bool AdditiveWornBonus = false;
@ -1510,9 +1509,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
if (WornType && (RuleI(Spells, AdditiveBonusWornType) == WornType))
AdditiveWornBonus = true;
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];
max = spells[spell_id].max[i];
}
@ -1561,49 +1560,100 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_AttackSpeed:
{
if ((effect_value - 100) > 0) { // Haste
if (new_bonus->haste < 0) break; // Slowed - Don't apply haste
if ((effect_value - 100) > new_bonus->haste) {
new_bonus->haste = effect_value - 100;
if (AdditiveWornBonus) {
if ((effect_value - 100) > 0) { // Haste
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
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) { // Haste
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;
}
}
break;
}
case SE_AttackSpeed2:
{
if ((effect_value - 100) > 0) { // Haste V2 - Stacks with V1 but does not Overcap
if (new_bonus->hastetype2 < 0) break; //Slowed - Don't apply haste2
if ((effect_value - 100) > new_bonus->hastetype2) {
new_bonus->hastetype2 = effect_value - 100;
{
if (AdditiveWornBonus) {
if ((effect_value - 100) > 0) { // Haste
if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste
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
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) { // Haste
if (new_bonus->hastetype2 < 0) break; // Slowed - Don't apply haste
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;
}
case SE_AttackSpeed3:
{
if (effect_value < 0){ //Slow
effect_value -= ((effect_value * GetSlowMitigation()/100));
if (effect_value < new_bonus->hastetype3)
new_bonus->hastetype3 = effect_value;
}
if (AdditiveWornBonus) {
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;
else if (effect_value > 0) { // Haste V3 - Stacks and Overcaps
if (effect_value > new_bonus->hastetype3) {
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;
@ -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)
effect_value = effect_value * -1;
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;
if (AdditiveWornBonus) {
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;
}
}
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;
}
@ -1840,7 +1898,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
new_bonus->DamageShieldType = GetDamageShieldType(spell_id, max);
else
new_bonus->DamageShieldType = GetDamageShieldType(spell_id);
break;
}
@ -2021,7 +2079,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_Vampirism:
new_bonus->Vampirism += effect_value;
break;
break;
case SE_AllInstrumentMod:
{
@ -2264,7 +2322,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_CriticalSpellChance:
{
new_bonus->CriticalSpellChance += effect_value;
if (base2 > new_bonus->SpellCritDmgIncNoStack)
new_bonus->SpellCritDmgIncNoStack = base2;
break;
@ -2474,7 +2532,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
case SE_NegateAttacks:
{
if (!new_bonus->NegateAttacks[0] ||
if (!new_bonus->NegateAttacks[0] ||
((new_bonus->NegateAttacks[0] && new_bonus->NegateAttacks[2]) && (new_bonus->NegateAttacks[2] < max))){
new_bonus->NegateAttacks[0] = 1;
new_bonus->NegateAttacks[1] = buffslot;
@ -2494,7 +2552,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
break;
}
case SE_MeleeThresholdGuard:
{
if (new_bonus->MeleeThresholdGuard[0] < effect_value){
@ -2861,17 +2919,17 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
new_bonus->NegateIfCombat = true;
break;
case SE_Screech:
case SE_Screech:
new_bonus->Screech = effect_value;
break;
case SE_AlterNPCLevel:
if (IsNPC()){
if (!new_bonus->AlterNPCLevel
|| ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value))
if (!new_bonus->AlterNPCLevel
|| ((effect_value < 0) && (new_bonus->AlterNPCLevel > effect_value))
|| ((effect_value > 0) && (new_bonus->AlterNPCLevel < effect_value))) {
int tmp_lv = GetOrigLevel() + effect_value;
if (tmp_lv < 1)
tmp_lv = 1;
@ -2909,7 +2967,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
new_bonus->BerserkSPA = true;
break;
case SE_Metabolism:
new_bonus->Metabolism += effect_value;
break;
@ -3010,7 +3068,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
}
case SE_SkillProc:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(new_bonus->SkillProc[e] && new_bonus->SkillProc[e] == spell_id)
@ -3025,7 +3083,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
}
case SE_SkillProcSuccess:{
for(int e = 0; e < MAX_SKILL_PROCS; e++)
{
if(new_bonus->SkillProcSuccess[e] && new_bonus->SkillProcSuccess[e] == spell_id)
@ -3041,9 +3099,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
if (IsAISpellEffect) {
//Non-Focused Effect to modify incoming spell damage by resist type.
case SE_FcSpellVulnerability:
case SE_FcSpellVulnerability:
ModVulnerability(base2, effect_value);
break;
}
@ -3109,7 +3167,7 @@ void NPC::CalcItemBonuses(StatBonuses *newbon)
newbon->HitChance += cur->Accuracy;
}
if(cur->CombatEffects > 0) {
newbon->ProcChance += cur->CombatEffects;
newbon->MeleeDamage += cur->CombatEffects;
}
if (cur->Worn.Effect>0 && (cur->Worn.Type == ET_WornEffect)) { // latent effects
ApplySpellsBonuses(cur->Worn.Effect, cur->Worn.Level, newbon, 0, cur->Worn.Type);
@ -4395,7 +4453,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.SlayUndead[0] = effect_value;
aabonuses.SlayUndead[1] = effect_value;
break;
case SE_DoubleRangedAttack:
spellbonuses.DoubleRangedAttack = effect_value;
aabonuses.DoubleRangedAttack = effect_value;
@ -4415,7 +4473,7 @@ void Mob::NegateSpellsBonuses(uint16 spell_id)
aabonuses.ShieldEquipDmgMod[1] = effect_value;
itembonuses.ShieldEquipDmgMod[0] = effect_value;
itembonuses.ShieldEquipDmgMod[1] = effect_value;
break;
break;
case SE_TriggerMeleeThreshold:
spellbonuses.TriggerMeleeThreshold = false;

View File

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

View File

@ -4991,7 +4991,7 @@ void Client::SetShadowStepExemption(bool v)
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
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)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * 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)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))

View File

@ -557,6 +557,7 @@ public:
void SendCrystalCounts();
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 AddLevelBasedExp(uint8 exp_percentage, uint8 max_level=0);
void SetLeadershipEXP(uint32 group_exp, uint32 raid_exp);
@ -1129,7 +1130,6 @@ public:
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
inline void ResetPositionTimer() { position_timer_counter = 0; }
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
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)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * 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)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
float runs = GetRunspeed();
int runs = GetRunspeed();
if(speed > (runs * 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, "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, "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, "EmoteID: %i", c->GetTarget()->CastToNPC()->GetEmoteID());
c->GetTarget()->CastToNPC()->QueryLoot(c);

View File

@ -89,7 +89,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
if (IsClient() && GetClass() == WIZARD)
ratio += RuleI(Spells, WizCritRatio); //Default is zero
if (Critical){
value = value_BaseEffect*ratio/100;
@ -138,7 +138,7 @@ int32 Mob::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
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);
if (IsNPC() && CastToNPC()->GetSpellScale())
@ -172,7 +172,7 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
value += int(value_BaseEffect*GetFocusEffect(focusImprovedDamage, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*GetFocusEffect(focusFcDamagePctCrit, spell_id)/100)*ratio/100;
value += int(value_BaseEffect*target->GetVulnerability(this, spell_id, 0)/100)*ratio/100;
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
extra_dmg = target->GetFcDamageAmtIncoming(this, spell_id) +
int(GetFocusEffect(focusFcDamageAmtCrit, spell_id)*ratio/100) +
GetFocusEffect(focusFcDamageAmt, spell_id);
@ -200,6 +200,11 @@ int32 Mob::GetActDoTDamage(uint16 spell_id, int32 value, Mob* target) {
extra_dmg /= duration;
}
//Sanctuary Custom: Spelldmg per tick
if(itembonuses.SpellDmg)
value -= GetExtraSpellAmt(spell_id, itembonuses.SpellDmg / 6, value); //per tick
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)
{
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;
}
@ -270,7 +258,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
value += GetFocusEffect(focusFcHealAmt, 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 += value*target->GetHealRate(spell_id, this)/100;
@ -281,7 +269,7 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if (Critical) {
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits,
OTHER_CRIT_HEAL, GetName(), itoa(value));
if (IsClient())
Message_StringID(MT_SpellCrits, YOU_CRIT_HEAL, itoa(value));
}
@ -301,6 +289,9 @@ int32 Mob::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
if(chance && zone->random.Roll(chance))
value *= 2;
if(itembonuses.HealAmt)
value += GetExtraSpellAmt(spell_id, itembonuses.HealAmt / 6, value) * modifier;
}
if (IsNPC() && CastToNPC()->GetHealScale())
@ -421,10 +412,14 @@ int32 Mob::GetActSpellDuration(uint16 spell_id, int32 duration)
int tic_inc = 0;
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
// a level 53 bard reported getting 2 tics
if (IsShortDurationBuff(spell_id) && IsBardSong(spell_id) && spells[spell_id].mana == 0 && GetClass() == BARD && GetLevel() > 60)
tic_inc++;
// Only need this for clients, since the change was for bard songs, I assume we should keep non bard songs getting +1
// However if its bard or not and is mez, charm or fear, we need to add 1 so that client is in sync
if (IsClient() && !(IsShortDurationBuff(spell_id) && IsBardSong(spell_id)) ||
IsFearSpell(spell_id) ||
IsCharmSpell(spell_id) ||
IsMezSpell(spell_id) ||
IsBlindSpell(spell_id))
tic_inc += 1;
return (((duration * increase) / 100) + tic_inc);
}
@ -767,7 +762,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
} else {
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
if (!spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
@ -855,7 +850,7 @@ void EntityList::AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool aff
if (!center->CheckLosFN(curmob))
continue;
} else { // check to stop casting beneficial ae buffs (to wit: bard songs) on enemies...
// See notes in AESpell() above for more info.
// See notes in AESpell() above for more info.
if (caster->IsAttackAllowed(curmob, true))
continue;
if (caster->CheckAggro(curmob))

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(), "itemname", iteminst->GetItem()->Name);
ExportVar(package_name.c_str(), "slotid", extradata);
ExportVar(package_name.c_str(), "spell_id", iteminst->GetItem()->Click.Effect);
break;
}
@ -1399,6 +1400,14 @@ void PerlembParser::ExportEventVariables(std::string &package_name, QuestEventID
ExportVar(package_name.c_str(), "killer_skill", sep.arg[3]);
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: {
break;

View File

@ -59,6 +59,97 @@ static uint32 MaxBankedRaidLeadershipPoints(int Level)
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) {
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
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;
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.");
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 (isrezzexp)
this->Message_StringID(MT_Experience, REZ_REGAIN);
else{
if(this->IsGrouped())
if(membercount > 1)
this->Message_StringID(MT_Experience, GAIN_GROUPXP);
else if(IsRaidGrouped())
Message_StringID(MT_Experience, GAIN_RAIDEXP);
@ -604,8 +705,8 @@ void Group::SplitExp(uint32 exp, Mob* other) {
groupmod = 2.16;
else
groupmod = 1.0;
groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier)));
if(membercount > 1 && membercount < 6)
groupexp += (uint32)((float)exp * groupmod * (RuleR(Character, GroupExpMultiplier)));
int conlevel = Mob::GetLevelCon(maxlevel, other->GetLevel());
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()
{
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
ItemInst *inst = m_inv.PopItem(slot_id);
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) {
safe_delete(inst);
}

View File

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

View File

@ -26,6 +26,7 @@
#include <limits.h>
#include <math.h>
#include <sstream>
#include <algorithm>
#ifdef BOTS
#include "bot.h"
@ -148,6 +149,28 @@ Mob::Mob(const char* in_name,
size = in_size;
base_size = size;
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;
@ -531,48 +554,32 @@ bool Mob::IsInvisible(Mob* other) const
return(false);
}
float Mob::_GetMovementSpeed(int mod) const
{
// List of movement speed modifiers, including AAs & spells:
// http://everquest.allakhazam.com/db/item.html?item=1721;page=1;howmany=50#m10822246245352
if (IsRooted())
return 0.0f;
int Mob::_GetWalkSpeed() const {
if (IsRooted() || IsStunned() || IsMezzed())
return 0;
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 (CastToClient()->GetGMSpeed()) {
speed_mod = 3.125f;
if (mod != 0)
speed_mod += speed_mod * static_cast<float>(mod) / 100.0f;
Mob *horse = entity_list.GetMob(CastToClient()->GetHorseId());
if (horse) {
speed_mod = horse->GetBaseRunspeed();
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 = 0;
int runspeedcap = RuleI(Character,BaseRunSpeedCap);
int spell_mod = spellbonuses.movementspeed + itembonuses.movementspeed;
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)
movemod += spell_mod;
@ -581,27 +588,189 @@ float Mob::_GetMovementSpeed(int mod) const
else
movemod = aa_mod;
// cap negative movemods from snares mostly
if (movemod < -85)
// hard cap
if (runspeedcap > 225)
runspeedcap = 225;
if(movemod < -85) //cap it at moving very very slow
movemod = -85;
if (movemod != 0)
speed_mod += speed_mod * static_cast<float>(movemod) / 100.0f;
if (!has_horse && movemod != 0)
speed_mod += (base_run * movemod / 100);
// runspeed caps
frunspeedcap = static_cast<float>(runspeedcap) / 100.0f;
if (IsClient() && speed_mod > frunspeedcap)
speed_mod = frunspeedcap;
if(speed_mod < 1)
return(1);
// apply final mod such as the -47 for walking
// use runspeed since it should stack with snares
// and if we get here, we know runspeed was the initial
// value before we applied movemod.
if (mod != 0)
speed_mod += runspeed * static_cast<float>(mod) / 100.0f;
//runspeed cap.
if(IsClient())
{
if(speed_mod > runspeedcap)
speed_mod = runspeedcap;
}
return speed_mod;
}
if (speed_mod <= 0.0f)
speed_mod = IsClient() ? 0.0001f : 0.0f;
int Mob::_GetRunSpeed() const {
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;
}
@ -1201,7 +1370,6 @@ void Mob::SendPosition()
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)app->pBuffer;
MakeSpawnUpdateNoDelta(spu);
move_tic_count = 0;
tar_ndx = 20;
entity_list.QueueClients(this, app, true);
safe_delete(app);
}
@ -1303,7 +1471,7 @@ void Mob::ShowStats(Client* client)
if(n->respawn2 != 0)
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, " 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);
}
if (IsAIControlled()) {
@ -5437,3 +5605,15 @@ void Mob::SendRemovePlayerState(PlayerState old_state)
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; }
virtual void SetTarget(Mob* mob);
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 GetATK() const { return ATK + 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 GoToBind(uint8 bindnum = 0) { }
virtual void Gate();
float GetWalkspeed() const { return(_GetMovementSpeed(-47)); }
float GetRunspeed() const { return(_GetMovementSpeed(0)); }
float GetBaseRunspeed() const { return runspeed; }
int GetWalkspeed() const { return(_GetWalkSpeed()); }
int GetRunspeed() const { return(_GetRunSpeed()); }
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(); }
bool IsRunning() const { return m_is_running; }
void SetRunning(bool val) { m_is_running = val; }
@ -801,7 +805,7 @@ public:
//old fear function
//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
inline void StartFleeing() { flee_mode = true; CalculateNewFearpoint(); }
void ProcessFlee();
@ -810,8 +814,8 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y);
bool CalculateNewPosition(float x, float y, float z, float speed, bool checkZ = false);
virtual bool CalculateNewPosition2(float x, float y, float z, float speed, bool checkZ = true);
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, int speed, bool checkZ = true, bool calcheading = true);
float CalculateDistance(float x, float y, float z);
float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
void SendTo(float new_x, float new_y, float new_z);
@ -882,6 +886,8 @@ public:
Timer *GetSpecialAbilityTimer(int ability);
void ClearSpecialAbilities();
void ProcessSpecialAbilities(const std::string &str);
bool IsMoved() { return moved; }
void SetMoved(bool moveflag) { moved = moveflag; }
Shielders_Struct shielder[MAX_SHIELDERS];
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);
static uint16 GetProcID(uint16 spell_id, uint8 effect_index);
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_PursueCastCheck() { return(false); }
@ -1048,6 +1057,13 @@ protected:
float base_size;
float size;
float runspeed;
float walkspeed;
float fearspeed;
int base_runspeed;
int base_walkspeed;
int base_fearspeed;
int current_speed;
uint32 pLastChange;
bool held;
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...
if(!IsBardSong(AIspells[i].spellid)) {
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
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))
{
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@ -714,9 +710,7 @@ void Client::AI_SpellCast()
{
if(!IsBardSong(spell_to_cast))
{
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@ -772,16 +766,13 @@ void Client::AI_Process()
{
if(GetTarget())
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
//continue on to attack code, ensuring that we execute the engaged code
engaged = true;
} else {
if(AImovement_timer->Check()) {
animation = GetRunspeed() * 21;
//animation = GetFearSpeed() * 21;
// Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
@ -839,16 +830,13 @@ void Client::AI_Process()
}
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(attack_timer.Check()) {
Attack(GetTarget(), MainPrimary);
@ -944,28 +932,27 @@ void Client::AI_Process()
{
if(!IsRooted())
{
animation = 21 * GetRunspeed();
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
else
if(AImovement_timer->Check())
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition2(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
else
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(WaypointChanged)
tar_ndx = 20;
if(WaypointChanged)
tar_ndx = 20;
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
CalculateNewPosition2(Goal.x, Goal.y, Goal.z, GetRunspeed());
}
}
}
else if(IsMoving())
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
}
AI_SpellCast();
@ -998,21 +985,23 @@ void Client::AI_Process()
return;
float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 100)
if (dist >= 400)
{
float speed = dist >= 225 ? GetRunspeed() : GetWalkspeed();
animation = 21 * speed;
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
if(AImovement_timer->Check())
{
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
}
else
{
SetHeading(owner->GetHeading());
if(moved)
{
moved=false;
SetMoving(false);
SendPosition();
SetRunAnimSpeed(0);
SetCurrentSpeed(0);
moved = false;
}
}
}
@ -1042,9 +1031,7 @@ void Mob::AI_Process() {
{
if(target)
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
SetCurrentSpeed(0);
moved=false;
}
//continue on to attack code, ensuring that we execute the engaged code
@ -1058,7 +1045,9 @@ void Mob::AI_Process() {
CalculateNewFearpoint();
}
if(!RuleB(Pathing, Fear) || !zone->pathing)
{
CalculateNewPosition2(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, GetFearSpeed(), true);
}
else
{
bool WaypointChanged, NodeReached;
@ -1156,15 +1145,21 @@ void Mob::AI_Process() {
{
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(target->GetX(), target->GetY()));
SendPosition();
tar_ndx =0;
if(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w)
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
//casting checked above...
@ -1369,7 +1364,7 @@ void Mob::AI_Process() {
CastToNPC()->DoClassAttacks(target);
}
AI_EngagedCastCheck();
} //end is within combat range
} //end is within combat rangepet
else {
//we cannot reach our target...
//underwater stuff only works with water maps in the zone!
@ -1425,10 +1420,7 @@ void Mob::AI_Process() {
}
else if(IsMoving()) {
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetRunAnimSpeed(0);
SendPosition();
SetMoving(false);
moved=false;
SetCurrentSpeed(0);
}
}
@ -1481,7 +1473,6 @@ void Mob::AI_Process() {
}
else if (AImovement_timer->Check() && !IsRooted())
{
SetRunAnimSpeed(0);
if (IsPet())
{
// we're a pet, do as we're told
@ -1500,18 +1491,18 @@ void Mob::AI_Process() {
float dist = DistanceSquared(m_Position, owner->GetPosition());
if (dist >= 400)
{
float speed = GetWalkspeed();
int speed = GetWalkspeed();
if (dist >= 5625)
speed = GetRunspeed();
CalculateNewPosition2(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
}
else
{
if(moved)
{
moved=false;
SetMoving(false);
SendPosition();
SetCurrentSpeed(0);
moved = false;
}
}
@ -1557,19 +1548,15 @@ void Mob::AI_Process() {
if (dist2 >= followdist) // Default follow distance is 100
{
float speed = GetWalkspeed();
int speed = GetWalkspeed();
if (dist2 >= followdist + 150)
speed = GetRunspeed();
CalculateNewPosition2(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
}
else
{
if(moved)
{
SendPosition();
moved=false;
SetMoving(false);
}
moved = false;
SetCurrentSpeed(0);
}
}
}
@ -1666,40 +1653,92 @@ void NPC::AI_DoMovement() {
if (gridno > 0 || cur_wp==-2) {
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)
else if (!(AIwalking_timer->Enabled()))
{ // currently moving
bool doMove = true;
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY())
{ // 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());
if (cur_wp_pause != 0) {
SetWaypointPause();
SetWaypointPause();
if(GetAppearance() != eaStanding)
SetAppearance(eaStanding, false);
SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w);
}
SendPosition();
SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w);
}
SendPosition();
//kick off event_waypoint arrive
char temp[16];
sprintf(temp, "%d", cur_wp);
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
if(cur_wp == 1)
ClearFeignMemory();
}
if (doMove)
{ // not at waypoint yet or at 0 pause WP, so keep moving
else
{ // not at waypoint yet, so keep moving
if(!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
CalculateNewPosition2(m_CurrentWayPoint.x, m_CurrentWayPoint.y, m_CurrentWayPoint.z, walksp, true);
else
@ -1727,7 +1766,8 @@ void NPC::AI_DoMovement() {
SetGrid( 0 - GetGrid()); // revert to AI control
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();
}
@ -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);
ClearFeignMemory();
moved=false;
SetMoving(false);
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5*5 )
{
SetHeading(m_GuardPoint.w);
} else {
FaceTarget(GetTarget());
}
SendPosition();
SetCurrentSpeed(0);
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
void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
if (!IsAIControlled())
return;
SetAppearance(eaStanding);
if(GetAppearance() != eaStanding)
{
SetAppearance(eaStanding);
}
if (iYellForHelp) {
if(IsPet()) {
@ -1889,9 +1871,10 @@ void Mob::AI_Event_NoLongerEngaged() {
pLastFightingDelayMoving += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
// So mobs don't keep running as a ghost until AIwalking_timer fires
// 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);
SetMoving(false);
SendPosition();
}
ClearRampage();

View File

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

View File

@ -6188,6 +6188,35 @@ XS(XS_Client_GetTargetRingZ)
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)
{
@ -6482,6 +6511,7 @@ XS(boot_Client)
newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$");
XSRETURN_YES;
}

View File

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

View File

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

View File

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

View File

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

View File

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