mirror of
https://github.com/EQEmu/Server.git
synced 2026-09-05 03:06:38 +00:00
Merge and compile fixes (non-bot, will do bots later)
This commit is contained in:
@@ -28,6 +28,7 @@ SET(zone_sources
|
||||
encounter.cpp
|
||||
entity.cpp
|
||||
exp.cpp
|
||||
fastmath.cpp
|
||||
fearpath.cpp
|
||||
forage.cpp
|
||||
groups.cpp
|
||||
@@ -68,6 +69,7 @@ SET(zone_sources
|
||||
exp.cpp
|
||||
fearpath.cpp
|
||||
forage.cpp
|
||||
global_loot_manager.cpp
|
||||
groups.cpp
|
||||
guild.cpp
|
||||
guild_mgr.cpp
|
||||
@@ -161,7 +163,9 @@ SET(zone_headers
|
||||
entity.h
|
||||
errmsg.h
|
||||
event_codes.h
|
||||
fastmath.h
|
||||
forage.h
|
||||
global_loot_manager.h
|
||||
groups.h
|
||||
guild_mgr.h
|
||||
hate_list.h
|
||||
|
||||
@@ -424,6 +424,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
|
||||
npca->GetSwarmInfo()->duration->Start(duration*1000);
|
||||
}
|
||||
|
||||
npca->StartSwarmTimer(duration * 1000);
|
||||
npca->GetSwarmInfo()->owner_id = GetID();
|
||||
|
||||
//give the pet somebody to "love"
|
||||
|
||||
+28
-1
@@ -253,7 +253,7 @@ bool Mob::CheckWillAggro(Mob *mob) {
|
||||
//sometimes if a client has some lag while zoning into a dangerous place while either invis or a GM
|
||||
//they will aggro mobs even though it's supposed to be impossible, to lets make sure we've finished connecting
|
||||
if (mob->IsClient()) {
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->zoning)
|
||||
if (!mob->CastToClient()->ClientFinishedLoading() || mob->CastToClient()->IsHoveringForRespawn() || mob->CastToClient()->bZoning)
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1007,6 +1007,28 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) {
|
||||
return zone->zonemap->CheckLoS(myloc, oloc);
|
||||
}
|
||||
|
||||
bool Mob::CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget) {
|
||||
if (zone->zonemap == nullptr) {
|
||||
//not sure what the best return is on error
|
||||
//should make this a database variable, but im lazy today
|
||||
#ifdef LOS_DEFAULT_CAN_SEE
|
||||
return(true);
|
||||
#else
|
||||
return(false);
|
||||
#endif
|
||||
}
|
||||
|
||||
#define LOS_DEFAULT_HEIGHT 6.0f
|
||||
|
||||
posWatcher.z += (sizeWatcher == 0.0f ? LOS_DEFAULT_HEIGHT : sizeWatcher) / 2 * HEAD_POSITION;
|
||||
posTarget.z += (sizeTarget == 0.0f ? LOS_DEFAULT_HEIGHT : sizeTarget) / 2 * SEE_POSITION;
|
||||
|
||||
#if LOSDEBUG>=5
|
||||
Log(Logs::General, Logs::None, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f) [static]", posWatcher.x, posWatcher.y, posWatcher.z, posTarget.x, posTarget.y, posTarget.z, sizeWatcher, sizeTarget);
|
||||
#endif
|
||||
return zone->zonemap->CheckLoS(posWatcher, posTarget);
|
||||
}
|
||||
|
||||
//offensive spell aggro
|
||||
int32 Mob::CheckAggroAmount(uint16 spell_id, Mob *target, bool isproc)
|
||||
{
|
||||
@@ -1274,6 +1296,11 @@ void Mob::ClearFeignMemory() {
|
||||
AI_feign_remember_timer->Disable();
|
||||
}
|
||||
|
||||
bool Mob::IsOnFeignMemory(Client *attacker) const
|
||||
{
|
||||
return feign_memory_list.find(attacker->CharacterID()) != feign_memory_list.end();
|
||||
}
|
||||
|
||||
bool Mob::PassCharismaCheck(Mob* caster, uint16 spell_id) {
|
||||
|
||||
/*
|
||||
|
||||
+101
-35
@@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "worldserver.h"
|
||||
#include "zone.h"
|
||||
#include "lua_parser.h"
|
||||
#include "fastmath.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
@@ -43,6 +44,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern WorldServer worldserver;
|
||||
extern FastMath g_Math;
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#define snprintf _snprintf
|
||||
@@ -358,7 +360,7 @@ bool Mob::AvoidDamage(Mob *other, DamageHitInfo &hit)
|
||||
Mob *attacker = other;
|
||||
Mob *defender = this;
|
||||
|
||||
bool InFront = attacker->InFrontMob(this, attacker->GetX(), attacker->GetY());
|
||||
bool InFront = !attacker->BehindMob(this, attacker->GetX(), attacker->GetY());
|
||||
|
||||
/*
|
||||
This special ability adds a negative modifer to the defenders riposte/block/parry/chance
|
||||
@@ -852,16 +854,56 @@ int Mob::ACSum()
|
||||
return ac;
|
||||
}
|
||||
|
||||
int Mob::GetBestMeleeSkill()
|
||||
{
|
||||
int bestSkill=0;
|
||||
EQEmu::skills::SkillType meleeSkills[]=
|
||||
{ EQEmu::skills::Skill1HBlunt,
|
||||
EQEmu::skills::Skill1HSlashing,
|
||||
EQEmu::skills::Skill2HBlunt,
|
||||
EQEmu::skills::Skill2HSlashing,
|
||||
EQEmu::skills::SkillHandtoHand,
|
||||
EQEmu::skills::Skill1HPiercing,
|
||||
EQEmu::skills::Skill2HPiercing,
|
||||
EQEmu::skills::SkillCount
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i=0; meleeSkills[i] != EQEmu::skills::SkillCount; ++i) {
|
||||
int value;
|
||||
value = GetSkill(meleeSkills[i]);
|
||||
bestSkill = std::max(value, bestSkill);
|
||||
}
|
||||
|
||||
return bestSkill;
|
||||
}
|
||||
|
||||
int Mob::offense(EQEmu::skills::SkillType skill)
|
||||
{
|
||||
int offense = GetSkill(skill);
|
||||
int stat_bonus = 0;
|
||||
if (skill == EQEmu::skills::SkillArchery || skill == EQEmu::skills::SkillThrowing)
|
||||
stat_bonus = GetDEX();
|
||||
else
|
||||
stat_bonus = GetSTR();
|
||||
int stat_bonus = GetSTR();
|
||||
|
||||
switch (skill) {
|
||||
case EQEmu::skills::SkillArchery:
|
||||
case EQEmu::skills::SkillThrowing:
|
||||
stat_bonus = GetDEX();
|
||||
break;
|
||||
|
||||
// Mobs with no weapons default to H2H.
|
||||
// Since H2H is capped at 100 for many many classes,
|
||||
// lets not handicap mobs based on not spawning with a
|
||||
// weapon.
|
||||
//
|
||||
// Maybe we tweak this if Disarm is actually implemented.
|
||||
|
||||
case EQEmu::skills::SkillHandtoHand:
|
||||
offense = GetBestMeleeSkill();
|
||||
break;
|
||||
}
|
||||
|
||||
if (stat_bonus >= 75)
|
||||
offense += (2 * stat_bonus - 150) / 3;
|
||||
|
||||
offense += GetATK();
|
||||
return offense;
|
||||
}
|
||||
@@ -1688,6 +1730,15 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
|
||||
if (!RuleB(Character, UseDeathExpLossMult)) {
|
||||
exploss = (int)(GetLevel() * (GetLevel() / 18.0) * 12000);
|
||||
}
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
// Death in levels with xp_mod (such as hell levels) was resulting
|
||||
// in losing more that appropriate since the loss was the same but
|
||||
// getting it back would take way longer. This makes the death the
|
||||
// same amount of time to recover. Will also lose more if level is
|
||||
// granting a bonus.
|
||||
exploss *= zone->level_exp_mod[GetLevel()].ExpMod;
|
||||
}
|
||||
|
||||
if ((GetLevel() < RuleI(Character, DeathExpLossLevel)) || (GetLevel() > RuleI(Character, DeathExpLossMaxLevel)) || IsBecomeNPC())
|
||||
{
|
||||
@@ -2533,6 +2584,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
if (other == this)
|
||||
return;
|
||||
|
||||
if (other->IsTrap())
|
||||
return;
|
||||
|
||||
if (damage < 0) {
|
||||
hate = 1;
|
||||
}
|
||||
@@ -2632,7 +2686,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
|
||||
hate_list.AddEntToHateList(other, hate, damage, bFrenzy, !iBuffTic);
|
||||
|
||||
if (other->IsClient() && !on_hatelist)
|
||||
if (other->IsClient() && !on_hatelist && !IsOnFeignMemory(other->CastToClient()))
|
||||
other->CastToClient()->AddAutoXTarget(this);
|
||||
|
||||
#ifdef BOTS
|
||||
@@ -2673,9 +2727,9 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
|
||||
// owner must get on list, but he's not actually gained any hate yet
|
||||
if (!owner->GetSpecialAbility(IMMUNE_AGGRO))
|
||||
{
|
||||
hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic);
|
||||
if (owner->IsClient() && !CheckAggro(owner))
|
||||
owner->CastToClient()->AddAutoXTarget(this);
|
||||
hate_list.AddEntToHateList(owner, 0, 0, false, !iBuffTic);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3311,6 +3365,12 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
|
||||
damage = DMG_INVULNERABLE;
|
||||
}
|
||||
|
||||
// this should actually happen MUCH sooner, need to investigate though -- good enough for now
|
||||
if ((skill_used == EQEmu::skills::SkillArchery || skill_used == EQEmu::skills::SkillThrowing) && GetSpecialAbility(IMMUNE_RANGED_ATTACKS)) {
|
||||
Log(Logs::Detail, Logs::Combat, "Avoiding %d damage due to IMMUNE_RANGED_ATTACKS.", damage);
|
||||
damage = DMG_INVULNERABLE;
|
||||
}
|
||||
|
||||
if (spell_id != SPELL_UNKNOWN || attacker == nullptr)
|
||||
avoidable = false;
|
||||
|
||||
@@ -3364,7 +3424,7 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
|
||||
// pets that have GHold will never automatically add NPCs
|
||||
// pets that have Hold and no Focus will add NPCs if they're engaged
|
||||
// pets that have Hold and Focus will not add NPCs
|
||||
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld())
|
||||
if (pet && !pet->IsFamiliar() && !pet->GetSpecialAbility(IMMUNE_AGGRO) && !pet->IsEngaged() && attacker && attacker != this && !attacker->IsCorpse() && !pet->IsGHeld() && !attacker->IsTrap())
|
||||
{
|
||||
if (!pet->IsHeld()) {
|
||||
Log(Logs::Detail, Logs::Aggro, "Sending pet %s into battle due to attack.", pet->GetName());
|
||||
@@ -3553,26 +3613,21 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const
|
||||
a->special = 2;
|
||||
else
|
||||
a->special = 0;
|
||||
a->meleepush_xy = attacker ? attacker->GetHeading() * 2.0f : 0.0f;
|
||||
a->hit_heading = attacker ? attacker->GetHeading() : 0.0f;
|
||||
if (RuleB(Combat, MeleePush) && damage > 0 && !IsRooted() &&
|
||||
(IsClient() || zone->random.Roll(RuleI(Combat, MeleePushChance)))) {
|
||||
a->force = EQEmu::skills::GetSkillMeleePushForce(skill_used);
|
||||
// update NPC stuff
|
||||
auto new_pos = glm::vec3(m_Position.x + (a->force * std::sin(a->meleepush_xy) + m_Delta.x),
|
||||
m_Position.y + (a->force * std::cos(a->meleepush_xy) + m_Delta.y), m_Position.z);
|
||||
if (zone->zonemap && zone->zonemap->CheckLoS(glm::vec3(m_Position), new_pos)) { // If we have LoS on the new loc it should be reachable.
|
||||
if (IsNPC()) {
|
||||
// Is this adequate?
|
||||
|
||||
Teleport(new_pos);
|
||||
if (position_update_melee_push_timer.Check()) {
|
||||
SendPositionUpdate();
|
||||
}
|
||||
if (IsNPC()) {
|
||||
if (attacker->IsNPC())
|
||||
a->force = 0.0f; // 2013 change that disabled NPC vs NPC push
|
||||
else
|
||||
a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC
|
||||
if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) {
|
||||
m_Delta.x += a->force * g_Math.FastSin(a->hit_heading);
|
||||
m_Delta.y += a->force * g_Math.FastCos(a->hit_heading);
|
||||
ForcedMovement = 3;
|
||||
}
|
||||
}
|
||||
else {
|
||||
a->force = 0.0f; // we couldn't move there, so lets not
|
||||
}
|
||||
}
|
||||
|
||||
//Note: if players can become pets, they will not receive damage messages of their own
|
||||
@@ -3922,10 +3977,10 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *
|
||||
float WPC = ProcChance * (100.0f + // Proc chance for this weapon
|
||||
static_cast<float>(weapon->ProcRate)) / 100.0f;
|
||||
if (zone->random.Roll(WPC)) { // 255 dex = 0.084 chance of proc. No idea what this number should be really.
|
||||
if (weapon->Proc.Level > ourlevel) {
|
||||
if (weapon->Proc.Level2 > ourlevel) {
|
||||
Log(Logs::Detail, Logs::Combat,
|
||||
"Tried to proc (%s), but our level (%d) is lower than required (%d)",
|
||||
weapon->Name, ourlevel, weapon->Proc.Level);
|
||||
weapon->Name, ourlevel, weapon->Proc.Level2);
|
||||
if (IsPet()) {
|
||||
Mob *own = GetOwner();
|
||||
if (own)
|
||||
@@ -3962,7 +4017,7 @@ void Mob::TryWeaponProc(const EQEmu::ItemInstance *inst, const EQEmu::ItemData *
|
||||
float APC = ProcChance * (100.0f + // Proc chance for this aug
|
||||
static_cast<float>(aug->ProcRate)) / 100.0f;
|
||||
if (zone->random.Roll(APC)) {
|
||||
if (aug->Proc.Level > ourlevel) {
|
||||
if (aug->Proc.Level2 > ourlevel) {
|
||||
if (IsPet()) {
|
||||
Mob *own = GetOwner();
|
||||
if (own)
|
||||
@@ -4387,6 +4442,12 @@ void Mob::DoRiposte(Mob *defender)
|
||||
if (!defender)
|
||||
return;
|
||||
|
||||
// so ahhh the angle you can riposte is larger than the angle you can hit :P
|
||||
if (!defender->IsFacingMob(this)) {
|
||||
defender->Message_StringID(MT_TooFarAway, CANT_SEE_TARGET);
|
||||
return;
|
||||
}
|
||||
|
||||
defender->Attack(this, EQEmu::inventory::slotPrimary, true);
|
||||
if (HasDied())
|
||||
return;
|
||||
@@ -5247,19 +5308,30 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
|
||||
// extra off hand non-sense, can only double with skill of 150 or above
|
||||
// or you have any amount of GiveDoubleAttack
|
||||
if (candouble && hand == EQEmu::inventory::slotSecondary)
|
||||
candouble = GetSkill(EQEmu::skills::SkillDoubleAttack) > 149 || (aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0;
|
||||
candouble =
|
||||
GetSkill(EQEmu::skills::SkillDoubleAttack) > 149 ||
|
||||
(aabonuses.GiveDoubleAttack + spellbonuses.GiveDoubleAttack + itembonuses.GiveDoubleAttack) > 0;
|
||||
|
||||
if (candouble) {
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillDoubleAttack, target, -10);
|
||||
if (CheckDoubleAttack()) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
|
||||
// Modern AA description: Increases your chance of ... performing one additional hit with a 2-handed weapon when double attacking by 2%.
|
||||
if (hand == EQEmu::inventory::slotPrimary) {
|
||||
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance +
|
||||
itembonuses.ExtraAttackChance;
|
||||
if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance))
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
}
|
||||
|
||||
// you can only triple from the main hand
|
||||
if (hand == EQEmu::inventory::slotPrimary && CanThisClassTripleAttack()) {
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillTripleAttack, target, -10);
|
||||
if (CheckTripleAttack()) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
auto flurrychance = aabonuses.FlurryChance + spellbonuses.FlurryChance +
|
||||
itembonuses.FlurryChance;
|
||||
itembonuses.FlurryChance;
|
||||
if (flurrychance && zone->random.Roll(flurrychance)) {
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
if (zone->random.Roll(flurrychance))
|
||||
@@ -5270,12 +5342,6 @@ void Client::DoAttackRounds(Mob *target, int hand, bool IsFromSpell)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (hand == EQEmu::inventory::slotPrimary) {
|
||||
auto extraattackchance = aabonuses.ExtraAttackChance + spellbonuses.ExtraAttackChance + itembonuses.ExtraAttackChance;
|
||||
if (extraattackchance && HasTwoHanderEquipped() && zone->random.Roll(extraattackchance))
|
||||
Attack(target, hand, false, false, IsFromSpell);
|
||||
}
|
||||
}
|
||||
|
||||
bool Mob::CheckDualWield()
|
||||
|
||||
@@ -616,6 +616,7 @@ bool Aura::Process()
|
||||
it = spawned_for.erase(it);
|
||||
}
|
||||
}
|
||||
safe_delete(app);
|
||||
}
|
||||
// TODO: waypoints?
|
||||
|
||||
@@ -757,6 +758,8 @@ void Mob::MakeAura(uint16 spell_id)
|
||||
|
||||
auto npc = new Aura(npc_type, this, record);
|
||||
npc->SetAuraID(spell_id);
|
||||
if (trap)
|
||||
npc->TryMoveAlong(5.0f, 0.0f, false); // try to place 5 units in front
|
||||
entity_list.AddNPC(npc, false);
|
||||
|
||||
if (trap)
|
||||
|
||||
+12
-1
@@ -120,6 +120,13 @@ void Client::CalcBonuses()
|
||||
|
||||
if (GetMaxXTargets() != 5 + aabonuses.extra_xtargets)
|
||||
SetMaxXTargets(5 + aabonuses.extra_xtargets);
|
||||
|
||||
// hmm maybe a better way to do this
|
||||
int metabolism = spellbonuses.Metabolism + itembonuses.Metabolism + aabonuses.Metabolism;
|
||||
int timer = GetClass() == MONK ? CONSUMPTION_MNK_TIMER : CONSUMPTION_TIMER;
|
||||
timer = timer * (100 + metabolism) / 100;
|
||||
if (timer != consume_food_timer.GetTimerTime())
|
||||
consume_food_timer.SetTimer(timer);
|
||||
}
|
||||
|
||||
int Client::CalcRecommendedLevelBonus(uint8 level, uint8 reclevel, int basestat)
|
||||
@@ -908,7 +915,7 @@ void Mob::ApplyAABonuses(const AA::Rank &rank, StatBonuses *newbon)
|
||||
newbon->GivePetGroupTarget = true;
|
||||
break;
|
||||
case SE_ItemHPRegenCapIncrease:
|
||||
newbon->ItemHPRegenCap = +base1;
|
||||
newbon->ItemHPRegenCap += base1;
|
||||
break;
|
||||
case SE_Ambidexterity:
|
||||
newbon->Ambidexterity += base1;
|
||||
@@ -2503,6 +2510,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses *ne
|
||||
new_bonus->MagicWeapon = true;
|
||||
break;
|
||||
|
||||
case SE_Hunger:
|
||||
new_bonus->hunger = true;
|
||||
break;
|
||||
|
||||
case SE_IncreaseBlockChance:
|
||||
if (AdditiveWornBonus)
|
||||
new_bonus->IncreaseBlockChance += effect_value;
|
||||
|
||||
+619
-405
File diff suppressed because it is too large
Load Diff
+7
-13
@@ -42,6 +42,8 @@
|
||||
#define BOT_FOLLOW_DISTANCE_DEFAULT_MAX 2500 // as DSq value (50 units)
|
||||
#define BOT_FOLLOW_DISTANCE_WALK 1000 // as DSq value (~31.623 units)
|
||||
|
||||
#define BOT_LEASH_DISTANCE 250000 // as DSq value (500 units)
|
||||
|
||||
extern WorldServer worldserver;
|
||||
|
||||
const int BotAISpellRange = 100; // TODO: Write a method that calcs what the bot's spell range is based on spell, equipment, AA, whatever and replace this
|
||||
@@ -273,7 +275,7 @@ public:
|
||||
static bool IsValidRaceClassCombo(uint16 r, uint8 c);
|
||||
bool IsValidName();
|
||||
static bool IsValidName(std::string& name);
|
||||
void Spawn(Client* botCharacterOwner);
|
||||
bool Spawn(Client* botCharacterOwner);
|
||||
virtual void SetLevel(uint8 in_level, bool command = false);
|
||||
virtual void FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho);
|
||||
virtual bool Process();
|
||||
@@ -337,7 +339,7 @@ public:
|
||||
bool IsStanding();
|
||||
int GetBotWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.786f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
|
||||
int GetBotRunspeed() const { return (int)((float)_GetRunSpeed() * 1.786f); }
|
||||
bool IsBotCasterAtCombatRange(Mob *target);
|
||||
int GetBotFearSpeed() const { return (int)((float)_GetFearSpeed() * 1.786f); }
|
||||
bool UseDiscipline(uint32 spell_id, uint32 target);
|
||||
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
|
||||
bool GetNeedsCured(Mob *tar);
|
||||
@@ -404,8 +406,8 @@ public:
|
||||
bool AIHealRotation(Mob* tar, bool useFastHeals);
|
||||
bool GetPauseAI() { return _pauseAI; }
|
||||
void SetPauseAI(bool pause_flag) { _pauseAI = pause_flag; }
|
||||
void SetGuardMode();
|
||||
|
||||
|
||||
// Mob AI Virtual Override Methods
|
||||
virtual void AI_Process();
|
||||
virtual void AI_Stop();
|
||||
@@ -532,9 +534,7 @@ public:
|
||||
bool IsBotWISCaster() { return IsWISCasterClass(GetClass()); }
|
||||
bool CanHeal();
|
||||
int GetRawACNoShield(int &shield_ac);
|
||||
bool GetHasBeenSummoned() { return _hasBeenSummoned; }
|
||||
const glm::vec3 GetPreSummonLocation() const { return m_PreSummonLocation; }
|
||||
|
||||
|
||||
// new heal rotation code
|
||||
bool CreateHealRotation(uint32 cycle_duration_ms = 5000, bool fast_heals = false, bool adaptive_targeting = false, bool casting_override = false);
|
||||
bool DestroyHealRotation();
|
||||
@@ -628,9 +628,6 @@ public:
|
||||
void SetBotStance(BotStanceType botStance) { _botStance = ((botStance != BotStanceUnknown) ? (botStance) : (BotStancePassive)); }
|
||||
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
|
||||
void SetHasBeenSummoned(bool s);
|
||||
void SetPreSummonLocation(const glm::vec3& location) { m_PreSummonLocation = location; }
|
||||
|
||||
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
|
||||
void SetShowHelm(bool showhelm) { _showhelm = showhelm; }
|
||||
void SetBeardColor(uint8 value) { beardcolor = value; }
|
||||
@@ -688,7 +685,6 @@ protected:
|
||||
virtual int32 CalcBotAAFocus(BotfocusType type, uint32 aa_ID, uint32 points, uint16 spell_id);
|
||||
virtual void PerformTradeWithClient(int16 beginSlotID, int16 endSlotID, Client* client);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
virtual float GetMaxMeleeRangeToTarget(Mob* target);
|
||||
|
||||
BotCastingRoles& GetCastingRoles() { return m_CastingRoles; }
|
||||
void SetGroupHealer(bool flag = true) { m_CastingRoles.GroupHealer = flag; }
|
||||
@@ -734,9 +730,7 @@ private:
|
||||
int32 max_end;
|
||||
int32 end_regen;
|
||||
uint32 timers[MaxTimer];
|
||||
bool _hasBeenSummoned;
|
||||
glm::vec3 m_PreSummonLocation;
|
||||
|
||||
|
||||
Timer evade_timer; // can be moved to pTimers at some point
|
||||
|
||||
BotCastingRoles m_CastingRoles;
|
||||
|
||||
+25
-20
@@ -3046,13 +3046,7 @@ void bot_command_guard(Client *c, const Seperator *sep)
|
||||
|
||||
sbl.remove(nullptr);
|
||||
for (auto bot_iter : sbl) {
|
||||
bot_iter->WipeHateList();
|
||||
bot_iter->SetFollowID(0);
|
||||
if (!bot_iter->GetPet())
|
||||
continue;
|
||||
|
||||
bot_iter->GetPet()->WipeHateList();
|
||||
bot_iter->GetPet()->SetFollowID(0);
|
||||
bot_iter->SetGuardMode();
|
||||
}
|
||||
if (sbl.size() == 1)
|
||||
Bot::BotGroupSay(sbl.front(), "Guarding this position");
|
||||
@@ -5054,7 +5048,11 @@ void bot_subcommand_bot_spawn(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
my_bot->Spawn(c);
|
||||
if (!my_bot->Spawn(c)) {
|
||||
c->Message(m_fail, "Failed to spawn bot '%s' (id: %i)", bot_name.c_str(), bot_id);
|
||||
safe_delete(my_bot);
|
||||
return;
|
||||
}
|
||||
|
||||
static const char* bot_spawn_message[16] = {
|
||||
"A solid weapon is my ally!", // WARRIOR / 'generic'
|
||||
@@ -5145,10 +5143,10 @@ void bot_subcommand_bot_summon(Client *c, const Seperator *sep)
|
||||
if (!bot_iter)
|
||||
continue;
|
||||
|
||||
Bot::BotGroupSay(bot_iter, "Whee!");
|
||||
//Bot::BotGroupSay(bot_iter, "Whee!");
|
||||
|
||||
bot_iter->WipeHateList();
|
||||
bot_iter->SetTarget(bot_iter->GetBotOwner());
|
||||
bot_iter->SetTarget(nullptr);
|
||||
bot_iter->Warp(glm::vec3(c->GetPosition()));
|
||||
bot_iter->DoAnim(0);
|
||||
|
||||
@@ -5156,7 +5154,7 @@ void bot_subcommand_bot_summon(Client *c, const Seperator *sep)
|
||||
continue;
|
||||
|
||||
bot_iter->GetPet()->WipeHateList();
|
||||
bot_iter->GetPet()->SetTarget(bot_iter);
|
||||
bot_iter->GetPet()->SetTarget(nullptr);
|
||||
bot_iter->GetPet()->Warp(glm::vec3(c->GetPosition()));
|
||||
}
|
||||
|
||||
@@ -5810,18 +5808,22 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
if (!leader_id) {
|
||||
c->Message(m_fail, "Can not locate bot-group leader id for '%s'", botgroup_name_arg.c_str());
|
||||
c->Message(m_fail, "Cannot locate bot-group leader id for '%s'", botgroup_name_arg.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
auto botgroup_leader = Bot::LoadBot(leader_id);
|
||||
if (!botgroup_leader) {
|
||||
c->Message(m_fail, "Could not spawn bot-group leader for '%s'", botgroup_name_arg.c_str());
|
||||
c->Message(m_fail, "Could not load bot-group leader for '%s'", botgroup_name_arg.c_str());
|
||||
safe_delete(botgroup_leader);
|
||||
return;
|
||||
}
|
||||
|
||||
botgroup_leader->Spawn(c);
|
||||
if (!botgroup_leader->Spawn(c)) {
|
||||
c->Message(m_fail, "Could not spawn bot-group leader %s for '%s'", botgroup_leader->GetName(), botgroup_name_arg.c_str());
|
||||
safe_delete(botgroup_leader);
|
||||
return;
|
||||
}
|
||||
|
||||
Group* group_inst = new Group(botgroup_leader);
|
||||
|
||||
@@ -5840,7 +5842,12 @@ void bot_subcommand_botgroup_load(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
botgroup_member->Spawn(c);
|
||||
if (!botgroup_member->Spawn(c)) {
|
||||
c->Message(m_fail, "Could not spawn bot '%s' (id: %i)", botgroup_member->GetName(), member_iter);
|
||||
safe_delete(botgroup_member);
|
||||
return;
|
||||
}
|
||||
|
||||
Bot::AddBotToGroup(botgroup_member, group_inst);
|
||||
}
|
||||
|
||||
@@ -7077,7 +7084,6 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
const EQEmu::ItemData* item = nullptr;
|
||||
bool is2Hweapon = false;
|
||||
|
||||
std::string item_link;
|
||||
EQEmu::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
|
||||
@@ -7098,8 +7104,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst);
|
||||
item_link = linker.GenerateLink();
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", item_link.c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i));
|
||||
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), (i == 22 ? EQEmu::inventory::slotPowerSource : i));
|
||||
|
||||
++inventory_count;
|
||||
}
|
||||
@@ -7242,8 +7247,8 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep)
|
||||
|
||||
std::string window_text;
|
||||
//std::string item_link;
|
||||
//Client::TextLink linker;
|
||||
//linker.SetLinkType(linker.linkItemInst);
|
||||
//EQEmu::SayLinkEngine linker;
|
||||
//linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
|
||||
for (int i = EQEmu::legacy::EQUIPMENT_BEGIN; i <= (EQEmu::legacy::EQUIPMENT_END + 1); ++i) {
|
||||
const EQEmu::ItemData* item = nullptr;
|
||||
|
||||
@@ -273,7 +273,10 @@ bool BotDatabase::LoadBotID(const uint32 owner_id, const std::string& bot_name,
|
||||
if (!owner_id || bot_name.empty())
|
||||
return false;
|
||||
|
||||
query = StringFormat("SELECT `bot_id` FROM `bot_data` WHERE `name` = '%s' LIMIT 1", bot_name.c_str());
|
||||
query = StringFormat(
|
||||
"SELECT `bot_id` FROM `bot_data` WHERE `owner_id` = '%u' AND `name` = '%s' LIMIT 1",
|
||||
owner_id, bot_name.c_str()
|
||||
);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success())
|
||||
return false;
|
||||
|
||||
+261
-74
@@ -123,8 +123,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
hpupdate_timer(2000),
|
||||
camp_timer(29000),
|
||||
process_timer(100),
|
||||
stamina_timer(40000),
|
||||
consume_food_timer(60000),
|
||||
consume_food_timer(CONSUMPTION_TIMER),
|
||||
zoneinpacket_timer(1000),
|
||||
linkdead_timer(RuleI(Zone,ClientLinkdeadMS)),
|
||||
dead_timer(2000),
|
||||
@@ -161,7 +160,8 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
npc_close_scan_timer(6000),
|
||||
hp_self_update_throttle_timer(300),
|
||||
hp_other_update_throttle_timer(500),
|
||||
position_update_timer(10000)
|
||||
position_update_timer(10000),
|
||||
tmSitting(0)
|
||||
{
|
||||
|
||||
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
|
||||
@@ -215,7 +215,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
linkdead_timer.Disable();
|
||||
zonesummon_id = 0;
|
||||
zonesummon_ignorerestrictions = 0;
|
||||
zoning = false;
|
||||
bZoning = false;
|
||||
zone_mode = ZoneUnsolicited;
|
||||
casting_spell_id = 0;
|
||||
npcflag = false;
|
||||
@@ -254,7 +254,7 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
mercSlot = 0;
|
||||
InitializeMercInfo();
|
||||
SetMerc(0);
|
||||
|
||||
if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true, false);
|
||||
logging_enabled = CLIENT_DEFAULT_LOGGING_ENABLED;
|
||||
|
||||
//for good measure:
|
||||
@@ -271,9 +271,10 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
m_ClientVersion = EQEmu::versions::ClientVersion::Unknown;
|
||||
m_ClientVersionBit = 0;
|
||||
AggroCount = 0;
|
||||
RestRegenHP = 0;
|
||||
RestRegenMana = 0;
|
||||
RestRegenEndurance = 0;
|
||||
ooc_regen = false;
|
||||
AreaHPRegen = 1.0f;
|
||||
AreaManaRegen = 1.0f;
|
||||
AreaEndRegen = 1.0f;
|
||||
XPRate = 100;
|
||||
current_endurance = 0;
|
||||
|
||||
@@ -330,6 +331,14 @@ Client::Client(EQStreamInterface* ieqs)
|
||||
|
||||
interrogateinv_flag = false;
|
||||
|
||||
trapid = 0;
|
||||
|
||||
for (int i = 0; i < InnateSkillMax; ++i)
|
||||
m_pp.InnateSkills[i] = InnateDisabled;
|
||||
|
||||
temp_pvp = false;
|
||||
is_client_moving = false;
|
||||
|
||||
AI_Init();
|
||||
}
|
||||
|
||||
@@ -389,7 +398,7 @@ Client::~Client() {
|
||||
GetTarget()->IsTargeted(-1);
|
||||
|
||||
//if we are in a group and we are not zoning, force leave the group
|
||||
if(isgrouped && !zoning && is_zone_loaded)
|
||||
if(isgrouped && !bZoning && is_zone_loaded)
|
||||
LeaveGroup();
|
||||
|
||||
UpdateWho(2);
|
||||
@@ -456,8 +465,8 @@ void Client::SendZoneInPackets()
|
||||
if (!GetHideMe()) entity_list.QueueClients(this, outapp, true);
|
||||
safe_delete(outapp);
|
||||
SetSpawned();
|
||||
if (GetPVP()) //force a PVP update until we fix the spawn struct
|
||||
SendAppearancePacket(AT_PVP, GetPVP(), true, false);
|
||||
if (GetPVP(false)) //force a PVP update until we fix the spawn struct
|
||||
SendAppearancePacket(AT_PVP, GetPVP(false), true, false);
|
||||
|
||||
//Send AA Exp packet:
|
||||
if (GetLevel() >= 51)
|
||||
@@ -1217,18 +1226,18 @@ void Client::ChannelMessageSend(const char* from, const char* to, uint8 chan_num
|
||||
EffSkill = 100;
|
||||
cm->skill_in_language = EffSkill;
|
||||
|
||||
// Garble the message based on listener skill
|
||||
if (ListenerSkill < 100) {
|
||||
GarbleMessage(buffer, (100 - ListenerSkill));
|
||||
}
|
||||
|
||||
cm->chan_num = chan_num;
|
||||
strcpy(&cm->message[0], buffer);
|
||||
QueuePacket(&app);
|
||||
|
||||
if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up
|
||||
if (m_pp.languages[language] <= lang_skill)
|
||||
CheckLanguageSkillIncrease(language, lang_skill);
|
||||
bool senderCanTrainSelf = RuleB(Client, SelfLanguageLearning);
|
||||
bool weAreNotSender = strcmp(this->GetCleanName(), cm->sender);
|
||||
|
||||
if (senderCanTrainSelf || weAreNotSender) {
|
||||
if ((chan_num == 2) && (ListenerSkill < 100)) { // group message in unmastered language, check for skill up
|
||||
if (m_pp.languages[language] <= lang_skill)
|
||||
CheckLanguageSkillIncrease(language, lang_skill);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1949,7 +1958,7 @@ void Client::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
ns->spawn.gm = GetGM() ? 1 : 0;
|
||||
ns->spawn.guildID = GuildID();
|
||||
// ns->spawn.linkdead = IsLD() ? 1 : 0;
|
||||
// ns->spawn.pvp = GetPVP() ? 1 : 0;
|
||||
// ns->spawn.pvp = GetPVP(false) ? 1 : 0;
|
||||
ns->spawn.show_name = true;
|
||||
|
||||
|
||||
@@ -4594,7 +4603,7 @@ void Client::IncrementAggroCount() {
|
||||
//
|
||||
AggroCount++;
|
||||
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
// If we already had aggro before this method was called, the combat indicator should already be up for SoF clients,
|
||||
@@ -4631,7 +4640,7 @@ void Client::DecrementAggroCount() {
|
||||
|
||||
AggroCount--;
|
||||
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
// Something else is still aggro on us, can't rest yet.
|
||||
@@ -6718,7 +6727,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcHPRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.HPRegen);
|
||||
aa_regen_field = itoa(aabonuses.HPRegen);
|
||||
total_regen_field = itoa(CalcHPRegen());
|
||||
total_regen_field = itoa(CalcHPRegen(true));
|
||||
break;
|
||||
}
|
||||
case 1: {
|
||||
@@ -6731,7 +6740,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcManaRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.ManaRegen);
|
||||
aa_regen_field = itoa(aabonuses.ManaRegen);
|
||||
total_regen_field = itoa(CalcManaRegen());
|
||||
total_regen_field = itoa(CalcManaRegen(true));
|
||||
}
|
||||
else { continue; }
|
||||
break;
|
||||
@@ -6745,7 +6754,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
|
||||
cap_regen_field = itoa(CalcEnduranceRegenCap());
|
||||
spell_regen_field = itoa(spellbonuses.EnduranceRegen);
|
||||
aa_regen_field = itoa(aabonuses.EnduranceRegen);
|
||||
total_regen_field = itoa(CalcEnduranceRegen());
|
||||
total_regen_field = itoa(CalcEnduranceRegen(true));
|
||||
break;
|
||||
}
|
||||
default: { break; }
|
||||
@@ -7899,7 +7908,7 @@ void Client::GarbleMessage(char *message, uint8 variance)
|
||||
for (size_t i = 0; i < strlen(message); i++) {
|
||||
// Client expects hex values inside of a text link body
|
||||
if (message[i] == delimiter) {
|
||||
if (!(delimiter_count & 1)) { i += EQEmu::legacy::TEXT_LINK_BODY_LENGTH; }
|
||||
if (!(delimiter_count & 1)) { i += EQEmu::constants::SayLinkBodySize; }
|
||||
++delimiter_count;
|
||||
continue;
|
||||
}
|
||||
@@ -8591,64 +8600,52 @@ void Client::SetConsumption(int32 in_hunger, int32 in_thirst)
|
||||
|
||||
void Client::Consume(const EQEmu::ItemData *item, uint8 type, int16 slot, bool auto_consume)
|
||||
{
|
||||
if(!item) { return; }
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
/*
|
||||
Spell Bonuses 2 digit form - 10, 20, 25 etc. (10%, 20%, 25%)
|
||||
AA Bonus Ranks, 110, 125, 150 etc. (10%, 25%, 50%)
|
||||
*/
|
||||
int increase = item->CastTime_ * 100;
|
||||
if (!auto_consume) // force feeding is half as effective
|
||||
increase /= 2;
|
||||
|
||||
float aa_bonus = ((float) aabonuses.Metabolism - 100) / 100;
|
||||
float item_bonus = (float) itembonuses.Metabolism / 100;
|
||||
float spell_bonus = (float) spellbonuses.Metabolism / 100;
|
||||
|
||||
float metabolism_mod = 1 + spell_bonus + item_bonus + aa_bonus;
|
||||
|
||||
Log(Logs::General, Logs::Food, "Client::Consume() Metabolism bonuses spell_bonus: (%.2f) item_bonus: (%.2f) aa_bonus: (%.2f) final: (%.2f)",
|
||||
spell_bonus,
|
||||
item_bonus,
|
||||
aa_bonus,
|
||||
metabolism_mod
|
||||
);
|
||||
if (increase < 0) // wasn't food? oh well
|
||||
return;
|
||||
|
||||
if (type == EQEmu::item::ItemTypeFood) {
|
||||
int hunger_change = item->CastTime_ * metabolism_mod;
|
||||
hunger_change = mod_food_value(item, hunger_change);
|
||||
increase = mod_food_value(item, increase);
|
||||
|
||||
if(hunger_change < 0)
|
||||
return;
|
||||
if (increase < 0)
|
||||
return;
|
||||
|
||||
m_pp.hunger_level += hunger_change;
|
||||
m_pp.hunger_level += increase;
|
||||
|
||||
Log(Logs::General, Logs::Food, "Consuming food, points added to hunger_level: %i - current_hunger: %i", hunger_change, m_pp.hunger_level);
|
||||
|
||||
DeleteItemInInventory(slot, 1, false);
|
||||
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
Log(Logs::General, Logs::Food, "Eating from slot: %i", (int)slot);
|
||||
|
||||
}
|
||||
else {
|
||||
int thirst_change = item->CastTime_ * metabolism_mod;
|
||||
thirst_change = mod_drink_value(item, thirst_change);
|
||||
|
||||
if(thirst_change < 0)
|
||||
return;
|
||||
|
||||
m_pp.thirst_level += thirst_change;
|
||||
Log(Logs::General, Logs::Food, "Consuming food, points added to hunger_level: %i - current_hunger: %i",
|
||||
increase, m_pp.hunger_level);
|
||||
|
||||
DeleteItemInInventory(slot, 1, false);
|
||||
|
||||
Log(Logs::General, Logs::Food, "Consuming drink, points added to thirst_level: %i current_thirst: %i", thirst_change, m_pp.thirst_level);
|
||||
if (!auto_consume) // no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
Log(Logs::General, Logs::Food, "Eating from slot: %i", (int)slot);
|
||||
|
||||
} else {
|
||||
increase = mod_drink_value(item, increase);
|
||||
|
||||
if (increase < 0)
|
||||
return;
|
||||
|
||||
m_pp.thirst_level += increase;
|
||||
|
||||
DeleteItemInInventory(slot, 1, false);
|
||||
|
||||
Log(Logs::General, Logs::Food, "Consuming drink, points added to thirst_level: %i current_thirst: %i",
|
||||
increase, m_pp.thirst_level);
|
||||
|
||||
if (!auto_consume) // no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
Log(Logs::General, Logs::Food, "Drinking from slot: %i", (int)slot);
|
||||
|
||||
}
|
||||
Log(Logs::General, Logs::Food, "Drinking from slot: %i", (int)slot);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg)
|
||||
@@ -8895,9 +8892,9 @@ void Client::CheckRegionTypeChanges()
|
||||
return;
|
||||
|
||||
if (last_region_type == RegionTypePVP)
|
||||
SetPVP(true, false);
|
||||
else if (GetPVP())
|
||||
SetPVP(false, false);
|
||||
temp_pvp = true;
|
||||
else if (temp_pvp)
|
||||
temp_pvp = false;
|
||||
}
|
||||
|
||||
void Client::ProcessAggroMeter()
|
||||
@@ -9074,3 +9071,193 @@ void Client::SetPetCommandState(int button, int state)
|
||||
FastQueuePacket(&app);
|
||||
}
|
||||
|
||||
bool Client::CanMedOnHorse()
|
||||
{
|
||||
// no horse is false
|
||||
if (GetHorseId() == 0)
|
||||
return false;
|
||||
|
||||
// can't med while attacking
|
||||
if (auto_attack)
|
||||
return false;
|
||||
|
||||
return animation == 0 && m_Delta.x == 0.0f && m_Delta.y == 0.0f; // TODO: animation is SpeedRun
|
||||
}
|
||||
|
||||
void Client::EnableAreaHPRegen(int value)
|
||||
{
|
||||
AreaHPRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaHPRegen, value, false);
|
||||
}
|
||||
|
||||
void Client::DisableAreaHPRegen()
|
||||
{
|
||||
AreaHPRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaHPRegen, 1000, false);
|
||||
}
|
||||
|
||||
void Client::EnableAreaManaRegen(int value)
|
||||
{
|
||||
AreaManaRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaManaRegen, value, false);
|
||||
}
|
||||
|
||||
void Client::DisableAreaManaRegen()
|
||||
{
|
||||
AreaManaRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaManaRegen, 1000, false);
|
||||
}
|
||||
|
||||
void Client::EnableAreaEndRegen(int value)
|
||||
{
|
||||
AreaEndRegen = value * 0.001f;
|
||||
SendAppearancePacket(AT_AreaEndRegen, value, false);
|
||||
}
|
||||
|
||||
void Client::DisableAreaEndRegen()
|
||||
{
|
||||
AreaEndRegen = 1.0f;
|
||||
SendAppearancePacket(AT_AreaEndRegen, 1000, false);
|
||||
}
|
||||
|
||||
void Client::EnableAreaRegens(int value)
|
||||
{
|
||||
EnableAreaHPRegen(value);
|
||||
EnableAreaManaRegen(value);
|
||||
EnableAreaEndRegen(value);
|
||||
}
|
||||
|
||||
void Client::DisableAreaRegens()
|
||||
{
|
||||
DisableAreaHPRegen();
|
||||
DisableAreaManaRegen();
|
||||
DisableAreaEndRegen();
|
||||
}
|
||||
|
||||
void Client::InitInnates()
|
||||
{
|
||||
// this function on the client also inits the level one innate skills (like swimming, hide, etc)
|
||||
// we won't do that here, lets just do the InnateSkills for now. Basically translation of what the client is doing
|
||||
// A lot of these we could probably have ignored because they have no known use or are 100% client side
|
||||
// but I figured just in case we'll do them all out
|
||||
//
|
||||
// The client calls this in a few places. When you remove a vision buff and in SetHeights, which is called in
|
||||
// illusions, mounts, and a bunch of other cases. All of the calls to InitInnates are wrapped in restoring regen
|
||||
// besides the call initializing the first time
|
||||
auto race = GetRace();
|
||||
auto class_ = GetClass();
|
||||
|
||||
for (int i = 0; i < InnateSkillMax; ++i)
|
||||
m_pp.InnateSkills[i] = InnateDisabled;
|
||||
|
||||
m_pp.InnateSkills[InnateInspect] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateOpen] = InnateEnabled;
|
||||
if (race >= RT_FROGLOK_3) {
|
||||
if (race == RT_SKELETON_2 || race == RT_FROGLOK_3)
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
else
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
}
|
||||
switch (race) {
|
||||
case RT_BARBARIAN:
|
||||
case RT_BARBARIAN_2:
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
break;
|
||||
case RT_ERUDITE:
|
||||
case RT_ERUDITE_2:
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_WOOD_ELF:
|
||||
case RT_GUARD_3:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_HIGH_ELF:
|
||||
case RT_GUARD_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_DARK_ELF:
|
||||
case RT_DARK_ELF_2:
|
||||
case RT_VAMPIRE_2:
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
break;
|
||||
case RT_TROLL:
|
||||
case RT_TROLL_2:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_DWARF:
|
||||
case RT_DWARF_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_OGRE:
|
||||
case RT_OGRE_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateSlam] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateNoBash] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateBashDoor] = InnateEnabled;
|
||||
break;
|
||||
case RT_HALFLING:
|
||||
case RT_HALFLING_2:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_GNOME:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateLore] = InnateEnabled;
|
||||
break;
|
||||
case RT_IKSAR:
|
||||
m_pp.InnateSkills[InnateRegen] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_VAH_SHIR:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
case RT_FROGLOK_2:
|
||||
case RT_GHOST:
|
||||
case RT_GHOUL:
|
||||
case RT_SKELETON:
|
||||
case RT_VAMPIRE:
|
||||
case RT_WILL_O_WISP:
|
||||
case RT_ZOMBIE:
|
||||
case RT_SPECTRE:
|
||||
case RT_GHOST_2:
|
||||
case RT_GHOST_3:
|
||||
case RT_DRAGON_2:
|
||||
case RT_INNORUUK:
|
||||
m_pp.InnateSkills[InnateUltraVision] = InnateEnabled;
|
||||
break;
|
||||
case RT_HUMAN:
|
||||
case RT_GUARD:
|
||||
case RT_BEGGAR:
|
||||
case RT_HUMAN_2:
|
||||
case RT_HUMAN_3:
|
||||
case RT_FROGLOK_3: // client does froglok weird, but this should work out fine
|
||||
break;
|
||||
default:
|
||||
m_pp.InnateSkills[InnateInfravision] = InnateEnabled;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (class_) {
|
||||
case DRUID:
|
||||
m_pp.InnateSkills[InnateHarmony] = InnateEnabled;
|
||||
break;
|
||||
case BARD:
|
||||
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
|
||||
break;
|
||||
case ROGUE:
|
||||
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateReveal] = InnateEnabled;
|
||||
break;
|
||||
case RANGER:
|
||||
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
|
||||
break;
|
||||
case MONK:
|
||||
m_pp.InnateSkills[InnateSurprise] = InnateEnabled;
|
||||
m_pp.InnateSkills[InnateAwareness] = InnateEnabled;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+54
-9
@@ -199,6 +199,27 @@ struct RespawnOption
|
||||
float heading;
|
||||
};
|
||||
|
||||
// do not ask what all these mean because I have no idea!
|
||||
// named from the client's CEverQuest::GetInnateDesc, they're missing some
|
||||
enum eInnateSkill {
|
||||
InnateEnabled = 0,
|
||||
InnateAwareness = 1,
|
||||
InnateBashDoor = 2,
|
||||
InnateBreathFire = 3,
|
||||
InnateHarmony = 4,
|
||||
InnateInfravision = 6,
|
||||
InnateLore = 8,
|
||||
InnateNoBash = 9,
|
||||
InnateRegen = 10,
|
||||
InnateSlam = 11,
|
||||
InnateSurprise = 12,
|
||||
InnateUltraVision = 13,
|
||||
InnateInspect = 14,
|
||||
InnateOpen = 15,
|
||||
InnateReveal = 16,
|
||||
InnateSkillMax = 25, // size of array in client
|
||||
InnateDisabled = 255
|
||||
};
|
||||
|
||||
const uint32 POPUPID_UPDATE_SHOWSTATSWINDOW = 1000000;
|
||||
|
||||
@@ -374,7 +395,7 @@ public:
|
||||
void SetGM(bool toggle);
|
||||
void SetPVP(bool toggle, bool message = true);
|
||||
|
||||
inline bool GetPVP() const { return m_pp.pvp != 0; }
|
||||
inline bool GetPVP(bool inc_temp = true) const { return m_pp.pvp != 0 || (inc_temp && temp_pvp); }
|
||||
inline bool GetGM() const { return m_pp.gm != 0; }
|
||||
|
||||
inline void SetBaseClass(uint32 i) { m_pp.class_=i; }
|
||||
@@ -406,6 +427,16 @@ public:
|
||||
const int32& SetMana(int32 amount);
|
||||
int32 CalcManaRegenCap();
|
||||
|
||||
// guild pool regen shit. Sends a SpawnAppearance with a value that regens to value * 0.001
|
||||
void EnableAreaHPRegen(int value);
|
||||
void DisableAreaHPRegen();
|
||||
void EnableAreaManaRegen(int value);
|
||||
void DisableAreaManaRegen();
|
||||
void EnableAreaEndRegen(int value);
|
||||
void DisableAreaEndRegen();
|
||||
void EnableAreaRegens(int value);
|
||||
void DisableAreaRegens();
|
||||
|
||||
void ServerFilter(SetServerFilter_Struct* filter);
|
||||
void BulkSendTraderInventory(uint32 char_id);
|
||||
void SendSingleTraderItem(uint32 char_id, int uniqueid);
|
||||
@@ -540,7 +571,7 @@ public:
|
||||
/*Endurance and such*/
|
||||
void CalcMaxEndurance(); //This calculates the maximum endurance we can have
|
||||
int32 CalcBaseEndurance(); //Calculates Base End
|
||||
int32 CalcEnduranceRegen(); //Calculates endurance regen used in DoEnduranceRegen()
|
||||
int32 CalcEnduranceRegen(bool bCombat = false); //Calculates endurance regen used in DoEnduranceRegen()
|
||||
int32 GetEndurance() const {return current_endurance;} //This gets our current endurance
|
||||
int32 GetMaxEndurance() const {return max_end;} //This gets our endurance from the last CalcMaxEndurance() call
|
||||
int32 CalcEnduranceRegenCap();
|
||||
@@ -575,6 +606,10 @@ public:
|
||||
uint32 GetExperienceForKill(Mob *against);
|
||||
void AddEXP(uint32 in_add_exp, uint8 conlevel = 0xFF, bool resexp = false);
|
||||
uint32 CalcEXP(uint8 conlevel = 0xFF);
|
||||
void CalculateNormalizedAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
void CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
void CalculateLeadershipExp(uint32 &add_exp, uint8 conlevel);
|
||||
void CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp, uint8 conlevel, bool resexp);
|
||||
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);
|
||||
@@ -620,6 +655,7 @@ public:
|
||||
void Sacrifice(Client* caster);
|
||||
void GoToDeath();
|
||||
inline const int32 GetInstanceID() const { return zone->GetInstanceID(); }
|
||||
void SetZoning(bool in) { bZoning = in; }
|
||||
|
||||
FACTION_VALUE GetReverseFactionCon(Mob* iOther);
|
||||
FACTION_VALUE GetFactionLevel(uint32 char_id, uint32 npc_id, uint32 p_race, uint32 p_class, uint32 p_deity, int32 pFaction, Mob* tnpc);
|
||||
@@ -719,6 +755,7 @@ public:
|
||||
void SendTradeskillDetails(uint32 recipe_id);
|
||||
bool TradeskillExecute(DBTradeskillRecipe_Struct *spec);
|
||||
void CheckIncreaseTradeskill(int16 bonusstat, int16 stat_modifier, float skillup_modifier, uint16 success_modifier, EQEmu::skills::SkillType tradeskill);
|
||||
void InitInnates();
|
||||
|
||||
void GMKill();
|
||||
inline bool IsMedding() const {return medding;}
|
||||
@@ -760,6 +797,9 @@ public:
|
||||
void SummonHorse(uint16 spell_id);
|
||||
void SetHorseId(uint16 horseid_in);
|
||||
uint16 GetHorseId() const { return horseId; }
|
||||
bool CanMedOnHorse();
|
||||
|
||||
bool CanFastRegen() const { return ooc_regen; }
|
||||
|
||||
void NPCSpawn(NPC *target_npc, const char *identifier, uint32 extra = 0);
|
||||
|
||||
@@ -862,6 +902,7 @@ public:
|
||||
void SetHunger(int32 in_hunger);
|
||||
void SetThirst(int32 in_thirst);
|
||||
void SetConsumption(int32 in_hunger, int32 in_thirst);
|
||||
bool IsStarved() const { if (GetGM() || !RuleB(Character, EnableHungerPenalties)) return false; return m_pp.hunger_level == 0 || m_pp.thirst_level == 0; }
|
||||
|
||||
bool CheckTradeLoreConflict(Client* other);
|
||||
bool CheckTradeNonDroppable();
|
||||
@@ -1266,6 +1307,8 @@ public:
|
||||
|
||||
int32 CalcATK();
|
||||
|
||||
uint32 trapid; //ID of trap player has triggered. This is cleared when the player leaves the trap's radius, or it despawns.
|
||||
|
||||
protected:
|
||||
friend class Mob;
|
||||
void CalcItemBonuses(StatBonuses* newbon);
|
||||
@@ -1345,8 +1388,8 @@ private:
|
||||
int32 CalcCorrup();
|
||||
int32 CalcMaxHP();
|
||||
int32 CalcBaseHP();
|
||||
int32 CalcHPRegen();
|
||||
int32 CalcManaRegen();
|
||||
int32 CalcHPRegen(bool bCombat = false);
|
||||
int32 CalcManaRegen(bool bCombat = false);
|
||||
int32 CalcBaseManaRegen();
|
||||
uint32 GetClassHPFactor();
|
||||
void DoHPRegen();
|
||||
@@ -1410,6 +1453,7 @@ private:
|
||||
std::string BuyerWelcomeMessage;
|
||||
bool AbilityTimer;
|
||||
int Haste; //precalced value
|
||||
uint32 tmSitting; // time stamp started sitting, used for HP regen bonus added on MAY 5, 2004
|
||||
|
||||
int32 max_end;
|
||||
int32 current_endurance;
|
||||
@@ -1422,6 +1466,7 @@ private:
|
||||
PetInfo m_suspendedminion; // pet data for our suspended minion.
|
||||
MercInfo m_mercinfo[MAXMERCS]; // current mercenary
|
||||
InspectMessage_Struct m_inspect_message;
|
||||
bool temp_pvp;
|
||||
|
||||
void NPCSpawn(const Seperator* sep);
|
||||
uint32 GetEXPForLevel(uint16 level);
|
||||
@@ -1458,7 +1503,6 @@ private:
|
||||
Timer hpupdate_timer;
|
||||
Timer camp_timer;
|
||||
Timer process_timer;
|
||||
Timer stamina_timer;
|
||||
Timer consume_food_timer;
|
||||
Timer zoneinpacket_timer;
|
||||
Timer linkdead_timer;
|
||||
@@ -1503,7 +1547,7 @@ private:
|
||||
bool npcflag;
|
||||
uint8 npclevel;
|
||||
bool feigned;
|
||||
bool zoning;
|
||||
bool bZoning;
|
||||
bool tgb;
|
||||
bool instalog;
|
||||
int32 last_reported_mana;
|
||||
@@ -1514,9 +1558,10 @@ private:
|
||||
|
||||
unsigned int AggroCount; // How many mobs are aggro on us.
|
||||
|
||||
unsigned int RestRegenHP;
|
||||
unsigned int RestRegenMana;
|
||||
unsigned int RestRegenEndurance;
|
||||
bool ooc_regen;
|
||||
float AreaHPRegen;
|
||||
float AreaManaRegen;
|
||||
float AreaEndRegen;
|
||||
|
||||
bool EngagedRaidTarget;
|
||||
uint32 SavedRaidRestTimer;
|
||||
|
||||
+213
-34
@@ -22,6 +22,8 @@
|
||||
#include "../common/rulesys.h"
|
||||
#include "../common/spdat.h"
|
||||
|
||||
#include "../common/data_verification.h"
|
||||
|
||||
#include "client.h"
|
||||
#include "mob.h"
|
||||
|
||||
@@ -231,16 +233,81 @@ int32 Client::LevelRegen()
|
||||
return hp;
|
||||
}
|
||||
|
||||
int32 Client::CalcHPRegen()
|
||||
int32 Client::CalcHPRegen(bool bCombat)
|
||||
{
|
||||
int32 regen = LevelRegen() + itembonuses.HPRegen + spellbonuses.HPRegen;
|
||||
regen += aabonuses.HPRegen + GroupLeadershipAAHealthRegeneration();
|
||||
int item_regen = itembonuses.HPRegen; // worn spells and +regen, already capped
|
||||
item_regen += GetHeroicSTA() / 20;
|
||||
|
||||
item_regen += aabonuses.HPRegen;
|
||||
|
||||
int base = 0;
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data)
|
||||
base = static_cast<int>(base_data->hp_regen);
|
||||
|
||||
auto level = GetLevel();
|
||||
bool skip_innate = false;
|
||||
|
||||
if (IsSitting()) {
|
||||
if (level >= 50) {
|
||||
base++;
|
||||
if (level >= 65)
|
||||
base++;
|
||||
}
|
||||
|
||||
if ((Timer::GetCurrentTime() - tmSitting) > 60000) {
|
||||
if (!IsAffectedByBuffByGlobalGroup(GlobalGroup::Lich)) {
|
||||
auto tic_diff = std::min((Timer::GetCurrentTime() - tmSitting) / 60000, static_cast<uint32>(9));
|
||||
if (tic_diff != 1) { // starts at 2 mins
|
||||
int tic_bonus = tic_diff * 1.5 * base;
|
||||
if (m_pp.InnateSkills[InnateRegen] != InnateDisabled)
|
||||
tic_bonus = tic_bonus * 1.2;
|
||||
base = tic_bonus;
|
||||
skip_innate = true;
|
||||
} else if (m_pp.InnateSkills[InnateRegen] == InnateDisabled) { // no innate regen gets first tick
|
||||
int tic_bonus = base * 1.5;
|
||||
base = tic_bonus;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_innate && m_pp.InnateSkills[InnateRegen] != InnateDisabled) {
|
||||
if (level >= 50) {
|
||||
++base;
|
||||
if (level >= 55)
|
||||
++base;
|
||||
}
|
||||
base *= 2;
|
||||
}
|
||||
|
||||
if (IsStarved())
|
||||
base = 0;
|
||||
|
||||
base += GroupLeadershipAAHealthRegeneration();
|
||||
// some IsKnockedOut that sets to -1
|
||||
base = base * 100.0f * AreaHPRegen * 0.01f + 0.5f;
|
||||
// another check for IsClient && !(base + item_regen) && Cur_HP <= 0 do --base; do later
|
||||
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenHP); // TODO: this is actually zone based
|
||||
auto max_hp = GetMaxHP();
|
||||
int fast_regen = 6 * (max_hp / fast_mod);
|
||||
if (base < fast_regen) // weird, but what the client is doing
|
||||
base = fast_regen;
|
||||
}
|
||||
|
||||
int regen = base + item_regen + spellbonuses.HPRegen; // TODO: client does this in buff tick
|
||||
return (regen * RuleI(Character, HPRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcHPRegenCap()
|
||||
{
|
||||
int cap = RuleI(Character, ItemHealthRegenCap) + itembonuses.HeroicSTA / 25;
|
||||
int cap = RuleI(Character, ItemHealthRegenCap);
|
||||
if (GetLevel() > 60)
|
||||
cap = std::max(cap, GetLevel() - 30); // if the rule is set greater than normal I guess
|
||||
if (GetLevel() > 65)
|
||||
cap += GetLevel() - 65;
|
||||
cap += aabonuses.ItemHPRegenCap + spellbonuses.ItemHPRegenCap + itembonuses.ItemHPRegenCap;
|
||||
return (cap * RuleI(Character, HPRegenMultiplier) / 100);
|
||||
}
|
||||
@@ -1169,43 +1236,80 @@ int32 Client::CalcBaseManaRegen()
|
||||
return regen;
|
||||
}
|
||||
|
||||
int32 Client::CalcManaRegen()
|
||||
int32 Client::CalcManaRegen(bool bCombat)
|
||||
{
|
||||
uint8 clevel = GetLevel();
|
||||
int32 regen = 0;
|
||||
//this should be changed so we dont med while camping, etc...
|
||||
if (IsSitting() || (GetHorseId() != 0)) {
|
||||
BuffFadeBySitModifier();
|
||||
if (HasSkill(EQEmu::skills::SkillMeditate)) {
|
||||
this->medding = true;
|
||||
regen = (((GetSkill(EQEmu::skills::SkillMeditate) / 10) + (clevel - (clevel / 4))) / 4) + 4;
|
||||
regen += spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillMeditate, nullptr, -5);
|
||||
}
|
||||
else {
|
||||
regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
int regen = 0;
|
||||
auto level = GetLevel();
|
||||
// so the new formulas break down with older skill caps where you don't have the skill until 4 or 8
|
||||
// so for servers that want to use the old skill progression they can set this rule so they
|
||||
// will get at least 1 for standing and 2 for sitting.
|
||||
bool old = RuleB(Character, OldMinMana);
|
||||
if (!IsStarved()) {
|
||||
// client does some base regen for shrouds here
|
||||
if (IsSitting() || CanMedOnHorse()) {
|
||||
// kind of weird to do it here w/e
|
||||
// client does some base medding regen for shrouds here
|
||||
if (GetClass() != BARD) {
|
||||
auto skill = GetSkill(EQEmu::skills::SkillMeditate);
|
||||
if (skill > 0) {
|
||||
regen++;
|
||||
if (skill > 1)
|
||||
regen++;
|
||||
if (skill >= 15)
|
||||
regen += skill / 15;
|
||||
}
|
||||
}
|
||||
if (old)
|
||||
regen = std::max(regen, 2);
|
||||
} else if (old) {
|
||||
regen = std::max(regen, 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
this->medding = false;
|
||||
regen = 2 + spellbonuses.ManaRegen + itembonuses.ManaRegen;
|
||||
|
||||
if (level > 61) {
|
||||
regen++;
|
||||
if (level > 63)
|
||||
regen++;
|
||||
}
|
||||
//AAs
|
||||
|
||||
regen += aabonuses.ManaRegen;
|
||||
// add in + 1 bonus for SE_CompleteHeal, but we don't do anything for it yet?
|
||||
|
||||
int item_bonus = itembonuses.ManaRegen; // this is capped already
|
||||
int heroic_bonus = 0;
|
||||
|
||||
switch (GetCasterClass()) {
|
||||
case 'W':
|
||||
heroic_bonus = GetHeroicWIS();
|
||||
break;
|
||||
default:
|
||||
heroic_bonus = GetHeroicINT();
|
||||
break;
|
||||
}
|
||||
|
||||
item_bonus += heroic_bonus / 25;
|
||||
regen += item_bonus;
|
||||
|
||||
if (level <= 70 && regen > 65)
|
||||
regen = 65;
|
||||
|
||||
regen = regen * 100.0f * AreaManaRegen * 0.01f + 0.5f;
|
||||
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenMana); // TODO: this is actually zone based
|
||||
auto max_mana = GetMaxMana();
|
||||
int fast_regen = 6 * (max_mana / fast_mod);
|
||||
if (regen < fast_regen) // weird, but what the client is doing
|
||||
regen = fast_regen;
|
||||
}
|
||||
|
||||
regen += spellbonuses.ManaRegen; // TODO: live does this in buff tick
|
||||
return (regen * RuleI(Character, ManaRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcManaRegenCap()
|
||||
{
|
||||
int32 cap = RuleI(Character, ItemManaRegenCap) + aabonuses.ItemManaRegenCap;
|
||||
switch (GetCasterClass()) {
|
||||
case 'I':
|
||||
cap += (itembonuses.HeroicINT / 25);
|
||||
break;
|
||||
case 'W':
|
||||
cap += (itembonuses.HeroicWIS / 25);
|
||||
break;
|
||||
}
|
||||
return (cap * RuleI(Character, ManaRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
@@ -2091,16 +2195,91 @@ int32 Client::CalcBaseEndurance()
|
||||
return base_end;
|
||||
}
|
||||
|
||||
int32 Client::CalcEnduranceRegen()
|
||||
int32 Client::CalcEnduranceRegen(bool bCombat)
|
||||
{
|
||||
int32 regen = int32(GetLevel() * 4 / 10) + 2;
|
||||
regen += aabonuses.EnduranceRegen + spellbonuses.EnduranceRegen + itembonuses.EnduranceRegen;
|
||||
int base = 0;
|
||||
if (!IsStarved()) {
|
||||
auto base_data = database.GetBaseData(GetLevel(), GetClass());
|
||||
if (base_data) {
|
||||
base = static_cast<int>(base_data->end_regen);
|
||||
if (!auto_attack && base > 0)
|
||||
base += base / 2;
|
||||
}
|
||||
}
|
||||
|
||||
// so when we are mounted, our local client SpeedRun is always 0, so this is always false, but the packets we process it to our own shit :P
|
||||
bool is_running = runmode && animation != 0 && GetHorseId() == 0; // TODO: animation is really what MQ2 calls SpeedRun
|
||||
|
||||
int weight_limit = GetSTR();
|
||||
auto level = GetLevel();
|
||||
if (GetClass() == MONK) {
|
||||
if (level > 99)
|
||||
weight_limit = 58;
|
||||
else if (level > 94)
|
||||
weight_limit = 57;
|
||||
else if (level > 89)
|
||||
weight_limit = 56;
|
||||
else if (level > 84)
|
||||
weight_limit = 55;
|
||||
else if (level > 79)
|
||||
weight_limit = 54;
|
||||
else if (level > 64)
|
||||
weight_limit = 53;
|
||||
else if (level > 63)
|
||||
weight_limit = 50;
|
||||
else if (level > 61)
|
||||
weight_limit = 47;
|
||||
else if (level > 59)
|
||||
weight_limit = 45;
|
||||
else if (level > 54)
|
||||
weight_limit = 40;
|
||||
else if (level > 50)
|
||||
weight_limit = 38;
|
||||
else if (level > 44)
|
||||
weight_limit = 36;
|
||||
else if (level > 29)
|
||||
weight_limit = 34;
|
||||
else if (level > 14)
|
||||
weight_limit = 32;
|
||||
else
|
||||
weight_limit = 30;
|
||||
}
|
||||
|
||||
bool encumbered = (CalcCurrentWeight() / 10) >= weight_limit;
|
||||
|
||||
if (is_running)
|
||||
base += level / -15;
|
||||
|
||||
if (encumbered)
|
||||
base += level / -15;
|
||||
|
||||
auto item_bonus = GetHeroicAGI() + GetHeroicDEX() + GetHeroicSTA() + GetHeroicSTR();
|
||||
item_bonus = item_bonus / 4 / 50;
|
||||
item_bonus += itembonuses.EnduranceRegen; // this is capped already
|
||||
base += item_bonus;
|
||||
|
||||
base = base * AreaEndRegen + 0.5f;
|
||||
|
||||
auto aa_regen = aabonuses.EnduranceRegen;
|
||||
|
||||
int regen = base;
|
||||
if (!bCombat && CanFastRegen() && (IsSitting() || CanMedOnHorse())) {
|
||||
auto fast_mod = RuleI(Character, RestRegenEnd); // TODO: this is actually zone based
|
||||
auto max_end = GetMaxEndurance();
|
||||
int fast_regen = 6 * (max_end / fast_mod);
|
||||
if (aa_regen < fast_regen) // weird, but what the client is doing
|
||||
aa_regen = fast_regen;
|
||||
}
|
||||
|
||||
regen += aa_regen;
|
||||
regen += spellbonuses.EnduranceRegen; // TODO: client does this in buff tick
|
||||
|
||||
return (regen * RuleI(Character, EnduranceRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
int32 Client::CalcEnduranceRegenCap()
|
||||
{
|
||||
int cap = (RuleI(Character, ItemEnduranceRegenCap) + itembonuses.HeroicSTR / 25 + itembonuses.HeroicDEX / 25 + itembonuses.HeroicAGI / 25 + itembonuses.HeroicSTA / 25);
|
||||
int cap = RuleI(Character, ItemEnduranceRegenCap);
|
||||
return (cap * RuleI(Character, EnduranceRegenMultiplier) / 100);
|
||||
}
|
||||
|
||||
|
||||
+238
-89
@@ -72,6 +72,7 @@ extern PetitionList petition_list;
|
||||
extern EntityList entity_list;
|
||||
typedef void (Client::*ClientPacketProc)(const EQApplicationPacket *app);
|
||||
|
||||
|
||||
//Use a map for connecting opcodes since it dosent get used a lot and is sparse
|
||||
std::map<uint32, ClientPacketProc> ConnectingOpcodes;
|
||||
//Use a static array for connected, for speed
|
||||
@@ -291,6 +292,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_MercenaryTimerRequest] = &Client::Handle_OP_MercenaryTimerRequest;
|
||||
ConnectedOpcodes[OP_MoveCoin] = &Client::Handle_OP_MoveCoin;
|
||||
ConnectedOpcodes[OP_MoveItem] = &Client::Handle_OP_MoveItem;
|
||||
ConnectedOpcodes[OP_MoveMultipleItems] = &Client::Handle_OP_MoveMultipleItems;
|
||||
ConnectedOpcodes[OP_OpenContainer] = &Client::Handle_OP_OpenContainer;
|
||||
ConnectedOpcodes[OP_OpenGuildTributeMaster] = &Client::Handle_OP_OpenGuildTributeMaster;
|
||||
ConnectedOpcodes[OP_OpenInventory] = &Client::Handle_OP_OpenInventory;
|
||||
@@ -314,6 +316,7 @@ void MapOpcodes()
|
||||
ConnectedOpcodes[OP_PurchaseLeadershipAA] = &Client::Handle_OP_PurchaseLeadershipAA;
|
||||
ConnectedOpcodes[OP_PVPLeaderBoardDetailsRequest] = &Client::Handle_OP_PVPLeaderBoardDetailsRequest;
|
||||
ConnectedOpcodes[OP_PVPLeaderBoardRequest] = &Client::Handle_OP_PVPLeaderBoardRequest;
|
||||
ConnectedOpcodes[OP_QueryUCSServerStatus] = &Client::Handle_OP_QueryUCSServerStatus;
|
||||
ConnectedOpcodes[OP_RaidInvite] = &Client::Handle_OP_RaidCommand;
|
||||
ConnectedOpcodes[OP_RandomReq] = &Client::Handle_OP_RandomReq;
|
||||
ConnectedOpcodes[OP_ReadBook] = &Client::Handle_OP_ReadBook;
|
||||
@@ -790,7 +793,7 @@ void Client::CompleteConnect()
|
||||
}
|
||||
|
||||
if (zone)
|
||||
zone->weatherSend();
|
||||
zone->weatherSend(this);
|
||||
|
||||
TotalKarma = database.GetKarma(AccountID());
|
||||
SendDisciplineTimers();
|
||||
@@ -1410,6 +1413,12 @@ void Client::Handle_Connect_OP_ZoneEntry(const EQApplicationPacket *app)
|
||||
drakkin_tattoo = m_pp.drakkin_tattoo;
|
||||
drakkin_details = m_pp.drakkin_details;
|
||||
|
||||
// we know our class now, so we might have to fix our consume timer!
|
||||
if (class_ == MONK)
|
||||
consume_food_timer.SetTimer(CONSUMPTION_MNK_TIMER);
|
||||
|
||||
InitInnates();
|
||||
|
||||
/* If GM not set in DB, and does not meet min status to be GM, reset */
|
||||
if (m_pp.gm && admin < minStatusToBeGM)
|
||||
m_pp.gm = 0;
|
||||
@@ -2139,8 +2148,8 @@ void Client::Handle_OP_AdventureMerchantRequest(const EQApplicationPacket *app)
|
||||
ss << item->ID << "|";
|
||||
ss << item->LDoNPrice << "|";
|
||||
ss << theme << "|";
|
||||
ss << "0|";
|
||||
ss << "1|";
|
||||
ss << (item->Stackable ? 1 : 0) << "|";
|
||||
ss << (item->LoreFlag ? 1 : 0) << "|";
|
||||
ss << item->Races << "|";
|
||||
ss << item->Classes;
|
||||
count++;
|
||||
@@ -2854,41 +2863,60 @@ void Client::Handle_OP_ApplyPoison(const EQApplicationPacket *app)
|
||||
DumpPacket(app);
|
||||
return;
|
||||
}
|
||||
|
||||
uint32 ApplyPoisonSuccessResult = 0;
|
||||
ApplyPoison_Struct* ApplyPoisonData = (ApplyPoison_Struct*)app->pBuffer;
|
||||
const EQEmu::ItemInstance* PrimaryWeapon = GetInv().GetItem(EQEmu::inventory::slotPrimary);
|
||||
const EQEmu::ItemInstance* SecondaryWeapon = GetInv().GetItem(EQEmu::inventory::slotSecondary);
|
||||
const EQEmu::ItemInstance* PoisonItemInstance = GetInv()[ApplyPoisonData->inventorySlot];
|
||||
const EQEmu::ItemData* poison=PoisonItemInstance->GetItem();
|
||||
const EQEmu::ItemData* primary=nullptr;
|
||||
const EQEmu::ItemData* secondary=nullptr;
|
||||
bool IsPoison = PoisonItemInstance &&
|
||||
(poison->ItemType == EQEmu::item::ItemTypePoison);
|
||||
|
||||
bool IsPoison = PoisonItemInstance && (PoisonItemInstance->GetItem()->ItemType == EQEmu::item::ItemTypePoison);
|
||||
|
||||
if (!IsPoison)
|
||||
{
|
||||
Log(Logs::Detail, Logs::Spells, "Item used to cast spell effect from a poison item was missing from inventory slot %d "
|
||||
"after casting, or is not a poison!", ApplyPoisonData->inventorySlot);
|
||||
|
||||
Message(0, "Error: item not found for inventory slot #%i or is not a poison", ApplyPoisonData->inventorySlot);
|
||||
if (PrimaryWeapon) {
|
||||
primary=PrimaryWeapon->GetItem();
|
||||
}
|
||||
else if (GetClass() == ROGUE)
|
||||
{
|
||||
if ((PrimaryWeapon && PrimaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing) ||
|
||||
(SecondaryWeapon && SecondaryWeapon->GetItem()->ItemType == EQEmu::item::ItemType1HPiercing)) {
|
||||
float SuccessChance = (GetSkill(EQEmu::skills::SkillApplyPoison) + GetLevel()) / 400.0f;
|
||||
|
||||
if (SecondaryWeapon) {
|
||||
secondary=SecondaryWeapon->GetItem();
|
||||
}
|
||||
|
||||
if (IsPoison && GetClass() == ROGUE) {
|
||||
|
||||
// Live always checks for skillup, even when poison is too high
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10);
|
||||
|
||||
if (poison->Proc.Level2 > GetLevel()) {
|
||||
// Poison is too high to apply.
|
||||
Message_StringID(clientMessageTradeskill, POISON_TOO_HIGH);
|
||||
}
|
||||
else if ((primary &&
|
||||
primary->ItemType == EQEmu::item::ItemType1HPiercing) ||
|
||||
(secondary &&
|
||||
secondary->ItemType == EQEmu::item::ItemType1HPiercing)) {
|
||||
|
||||
double ChanceRoll = zone->random.Real(0, 1);
|
||||
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillApplyPoison, nullptr, 10);
|
||||
// Poisons that use this skill (old world poisons) almost
|
||||
// never fail to apply. I did 25 applies of a trivial 120+
|
||||
// poison with an apply skill of 48 and they all worked.
|
||||
// Also did 25 straight poisons at apply skill 248 for very
|
||||
// high end and they never failed.
|
||||
// Apply poison ranging from 1-9, 28/30 worked for a level 18..
|
||||
// Poisons that don't proc until a level higher than the
|
||||
// rogue simply won't apply at all, no skill check done.
|
||||
|
||||
if (ChanceRoll < SuccessChance) {
|
||||
if (ChanceRoll < (.9 + GetLevel()/1000)) {
|
||||
ApplyPoisonSuccessResult = 1;
|
||||
// NOTE: Someone may want to tweak the chance to proc the poison effect that is added to the weapon here.
|
||||
// My thinking was that DEX should be apart of the calculation.
|
||||
AddProcToWeapon(PoisonItemInstance->GetItem()->Proc.Effect, false, (GetDEX() / 100) + 103);
|
||||
AddProcToWeapon(poison->Proc.Effect, false,
|
||||
(GetDEX() / 100) + 103);
|
||||
}
|
||||
|
||||
DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true);
|
||||
|
||||
Log(Logs::General, Logs::None, "Chance to Apply Poison was %f. Roll was %f. Result is %u.", SuccessChance, ChanceRoll, ApplyPoisonSuccessResult);
|
||||
}
|
||||
|
||||
// Live always deletes the item, success or failure. Even if too high.
|
||||
DeleteItemInInventory(ApplyPoisonData->inventorySlot, 1, true);
|
||||
}
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_ApplyPoison, nullptr, sizeof(ApplyPoison_Struct));
|
||||
@@ -3953,12 +3981,23 @@ void Client::Handle_OP_BuffRemoveRequest(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_Bug(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size != sizeof(BugStruct))
|
||||
printf("Wrong size of BugStruct got %d expected %zu!\n", app->size, sizeof(BugStruct));
|
||||
else {
|
||||
BugStruct* bug = (BugStruct*)app->pBuffer;
|
||||
database.UpdateBug(bug);
|
||||
if (!RuleB(Bugs, ReportingSystemActive)) {
|
||||
Message(0, "Bug reporting is disabled on this server.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (app->size != sizeof(BugReport_Struct)) {
|
||||
printf("Wrong size of BugReport_Struct got %d expected %zu!\n", app->size, sizeof(BugReport_Struct));
|
||||
}
|
||||
else {
|
||||
BugReport_Struct* bug_report = (BugReport_Struct*)app->pBuffer;
|
||||
|
||||
if (RuleB(Bugs, UseOldReportingMethod))
|
||||
database.RegisterBug(bug_report);
|
||||
else
|
||||
database.RegisterBug(this, bug_report);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -4396,7 +4435,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading);
|
||||
auto boat_delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading));
|
||||
boat->SetDelta(boat_delta);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
@@ -4406,7 +4445,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
safe_delete(outapp);
|
||||
|
||||
/* Update the boat's position on the server, without sending an update */
|
||||
boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ19toFloat(ppu->heading), false);
|
||||
boat->GMMove(ppu->x_pos, ppu->y_pos, ppu->z_pos, EQ12toFloat(ppu->heading), false);
|
||||
return;
|
||||
}
|
||||
else return;
|
||||
@@ -4551,7 +4590,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
/* Update internal state */
|
||||
m_Delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, ppu->delta_heading);
|
||||
m_Delta = glm::vec4(ppu->delta_x, ppu->delta_y, ppu->delta_z, EQ10toFloat(ppu->delta_heading));
|
||||
|
||||
if (IsTracking() && ((m_Position.x != ppu->x_pos) || (m_Position.y != ppu->y_pos))) {
|
||||
if (zone->random.Real(0, 100) < 70)//should be good
|
||||
@@ -4604,7 +4643,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
}
|
||||
}
|
||||
|
||||
float new_heading = EQ19toFloat(ppu->heading);
|
||||
float new_heading = EQ12toFloat(ppu->heading);
|
||||
int32 new_animation = ppu->animation;
|
||||
|
||||
/* Update internal server position from what the client has sent */
|
||||
@@ -4623,7 +4662,7 @@ void Client::Handle_OP_ClientUpdate(const EQApplicationPacket *app)
|
||||
if (is_client_moving || new_heading != m_Position.w || new_animation != animation) {
|
||||
|
||||
animation = ppu->animation;
|
||||
m_Position.w = EQ19toFloat(ppu->heading);
|
||||
m_Position.w = EQ12toFloat(ppu->heading);
|
||||
|
||||
/* Broadcast update to other clients */
|
||||
auto outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
@@ -4795,7 +4834,6 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
|
||||
mod_consider(tmob, con);
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
// only wanted to check raid target once
|
||||
// and need con to still be around so, do it here!
|
||||
if (tmob->IsRaidTarget()) {
|
||||
@@ -4842,6 +4880,8 @@ void Client::Handle_OP_Consider(const EQApplicationPacket *app)
|
||||
else if ((invisible || invisible_undead || hidden || invisible_animals) && !IsInvisible(tmob))
|
||||
Message_StringID(10, SUSPECT_SEES_YOU);
|
||||
|
||||
safe_delete(outapp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -5324,31 +5364,44 @@ void Client::Handle_OP_DisarmTraps(const EQApplicationPacket *app)
|
||||
|
||||
p_timers.Start(pTimerDisarmTraps, reuse - 1);
|
||||
|
||||
Trap* trap = entity_list.FindNearbyTrap(this, 60);
|
||||
uint8 success = SKILLUP_FAILURE;
|
||||
float curdist = 0;
|
||||
Trap* trap = entity_list.FindNearbyTrap(this, 250, curdist, true);
|
||||
if (trap && trap->detected)
|
||||
{
|
||||
int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps);
|
||||
if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill))
|
||||
float max_radius = (trap->radius * 2) * (trap->radius * 2); // radius is used to trigger trap, so disarm radius should be a bit bigger.
|
||||
Log(Logs::General, Logs::Traps, "%s is attempting to disarm trap %d. Curdist is %0.2f maxdist is %0.2f", GetName(), trap->trap_id, curdist, max_radius);
|
||||
if (curdist <= max_radius)
|
||||
{
|
||||
Message(MT_Skills, "You disarm a trap.");
|
||||
trap->disarmed = true;
|
||||
trap->chkarea_timer.Disable();
|
||||
trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000);
|
||||
int uskill = GetSkill(EQEmu::skills::SkillDisarmTraps);
|
||||
if ((zone->random.Int(0, 49) + uskill) >= (zone->random.Int(0, 49) + trap->skill))
|
||||
{
|
||||
success = SKILLUP_SUCCESS;
|
||||
Message_StringID(MT_Skills, DISARMED_TRAP);
|
||||
trap->disarmed = true;
|
||||
Log(Logs::General, Logs::Traps, "Trap %d is disarmed.", trap->trap_id);
|
||||
trap->UpdateTrap();
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Skills, FAIL_DISARM_DETECTED_TRAP);
|
||||
if (zone->random.Int(0, 99) < 25) {
|
||||
trap->Trigger(this);
|
||||
}
|
||||
}
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr);
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (zone->random.Int(0, 99) < 25) {
|
||||
Message(MT_Skills, "You set off the trap while trying to disarm it!");
|
||||
trap->Trigger(this);
|
||||
}
|
||||
else {
|
||||
Message(MT_Skills, "You failed to disarm a trap.");
|
||||
}
|
||||
Message_StringID(MT_Skills, TRAP_TOO_FAR);
|
||||
}
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillDisarmTraps, nullptr);
|
||||
return;
|
||||
}
|
||||
Message(MT_Skills, "You did not find any traps close enough to disarm.");
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Skills, LDON_SENSE_TRAP2);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -9820,6 +9873,11 @@ void Client::Handle_OP_MoveItem(const EQApplicationPacket *app)
|
||||
return;
|
||||
}
|
||||
|
||||
void Client::Handle_OP_MoveMultipleItems(const EQApplicationPacket *app)
|
||||
{
|
||||
Kick(); // TODO: lets not desync though
|
||||
}
|
||||
|
||||
void Client::Handle_OP_OpenContainer(const EQApplicationPacket *app)
|
||||
{
|
||||
// Does not exist in Ti client
|
||||
@@ -9919,22 +9977,18 @@ void Client::Handle_OP_PetCommands(const EQApplicationPacket *app)
|
||||
Mob* mypet = this->GetPet();
|
||||
Mob *target = entity_list.GetMob(pet->target);
|
||||
|
||||
if (!mypet || pet->command == PET_LEADER)
|
||||
{
|
||||
if (pet->command == PET_LEADER)
|
||||
{
|
||||
if (mypet && (!GetTarget() || GetTarget() == mypet))
|
||||
{
|
||||
if (!mypet || pet->command == PET_LEADER) {
|
||||
if (pet->command == PET_LEADER) {
|
||||
// we either send the ID of an NPC we're interested in or no ID for our own pet
|
||||
if (target) {
|
||||
auto owner = target->GetOwner();
|
||||
if (owner)
|
||||
target->Say_StringID(PET_LEADERIS, owner->GetCleanName());
|
||||
else
|
||||
target->Say_StringID(I_FOLLOW_NOONE);
|
||||
} else if (mypet) {
|
||||
mypet->Say_StringID(PET_LEADERIS, GetName());
|
||||
}
|
||||
else if ((mypet = GetTarget()))
|
||||
{
|
||||
Mob *Owner = mypet->GetOwner();
|
||||
if (Owner)
|
||||
mypet->Say_StringID(PET_LEADERIS, Owner->GetCleanName());
|
||||
else if (mypet->IsNPC())
|
||||
mypet->Say_StringID(I_FOLLOW_NOONE);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -10516,11 +10570,8 @@ void Client::Handle_OP_Petition(const EQApplicationPacket *app)
|
||||
|
||||
void Client::Handle_OP_PetitionBug(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size != sizeof(PetitionBug_Struct))
|
||||
printf("Wrong size of BugStruct! Expected: %zu, Got: %i\n", sizeof(PetitionBug_Struct), app->size);
|
||||
else {
|
||||
Message(0, "Petition Bugs are not supported, please use /bug.");
|
||||
}
|
||||
Message(0, "Petition Bugs are not supported, please use /bug.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -10940,6 +10991,84 @@ void Client::Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app)
|
||||
{
|
||||
if (zone->IsUCSServerAvailable()) {
|
||||
EQApplicationPacket* outapp = nullptr;
|
||||
std::string buffer;
|
||||
|
||||
std::string MailKey = database.GetMailKey(CharacterID(), true);
|
||||
EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown;
|
||||
|
||||
// chat server packet
|
||||
switch (ClientVersion()) {
|
||||
case EQEmu::versions::ClientVersion::Titanium:
|
||||
ConnectionType = EQEmu::versions::ucsTitaniumChat;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::SoF:
|
||||
ConnectionType = EQEmu::versions::ucsSoFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::SoD:
|
||||
ConnectionType = EQEmu::versions::ucsSoDCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::UF:
|
||||
ConnectionType = EQEmu::versions::ucsUFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::RoF:
|
||||
ConnectionType = EQEmu::versions::ucsRoFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::RoF2:
|
||||
ConnectionType = EQEmu::versions::ucsRoF2Combined;
|
||||
break;
|
||||
default:
|
||||
ConnectionType = EQEmu::versions::ucsUnknown;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = StringFormat("%s,%i,%s.%s,%c%s",
|
||||
Config->ChatHost.c_str(),
|
||||
Config->ChatPort,
|
||||
Config->ShortName.c_str(),
|
||||
GetName(),
|
||||
ConnectionType,
|
||||
MailKey.c_str()
|
||||
);
|
||||
|
||||
outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1));
|
||||
memcpy(outapp->pBuffer, buffer.c_str(), buffer.length());
|
||||
outapp->pBuffer[buffer.length()] = '\0';
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
// mail server packet
|
||||
switch (ClientVersion()) {
|
||||
case EQEmu::versions::ClientVersion::Titanium:
|
||||
ConnectionType = EQEmu::versions::ucsTitaniumMail;
|
||||
break;
|
||||
default:
|
||||
// retain value from previous switch
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = StringFormat("%s,%i,%s.%s,%c%s",
|
||||
Config->MailHost.c_str(),
|
||||
Config->MailPort,
|
||||
Config->ShortName.c_str(),
|
||||
GetName(),
|
||||
ConnectionType,
|
||||
MailKey.c_str()
|
||||
);
|
||||
|
||||
outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1));
|
||||
memcpy(outapp->pBuffer, buffer.c_str(), buffer.length());
|
||||
outapp->pBuffer[buffer.length()] = '\0';
|
||||
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
|
||||
void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
{
|
||||
if (app->size < sizeof(RaidGeneral_Struct)) {
|
||||
@@ -11458,7 +11587,7 @@ void Client::Handle_OP_RaidCommand(const EQApplicationPacket *app)
|
||||
|
||||
Client *client_moved = entity_list.GetClientByName(raid_command_packet->leader_name);
|
||||
|
||||
if (client_moved) {
|
||||
if (client_moved && client_moved->GetRaid()) {
|
||||
client_moved->GetRaid()->SendHPManaEndPacketsTo(client_moved);
|
||||
client_moved->GetRaid()->SendHPManaEndPacketsFrom(client_moved);
|
||||
|
||||
@@ -11649,10 +11778,24 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
|
||||
|
||||
// make where clause segment for container(s)
|
||||
std::string containers;
|
||||
if (tsf->some_id == 0)
|
||||
uint32 combineObjectSlots;
|
||||
if (tsf->some_id == 0) {
|
||||
containers += StringFormat(" = %u ", tsf->object_type); // world combiner so no item number
|
||||
else
|
||||
combineObjectSlots = 10;
|
||||
}
|
||||
else {
|
||||
containers += StringFormat(" in (%u, %u) ", tsf->object_type, tsf->some_id); // container in inventory
|
||||
auto item = database.GetItem(tsf->some_id);
|
||||
if (!item)
|
||||
{
|
||||
Log(Logs::General, Logs::Error, "Invalid container ID: %d. GetItem returned null. Defaulting to BagSlots = 10.\n", tsf->some_id);
|
||||
combineObjectSlots = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
combineObjectSlots = item->BagSlots;
|
||||
}
|
||||
}
|
||||
|
||||
std::string favoriteIDs; //gotta be big enough for 500 IDs
|
||||
bool first = true;
|
||||
@@ -11684,8 +11827,8 @@ void Client::Handle_OP_RecipesFavorite(const EQApplicationPacket *app)
|
||||
"((tr.must_learn & 0x3 <> 0 AND crl.madecount IS NOT NULL) "
|
||||
"OR (tr.must_learn & 0x3 = 0)) "
|
||||
"GROUP BY tr.id "
|
||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||
"LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str());
|
||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 AND SUM(tre.componentcount) <= %u "
|
||||
"LIMIT 100 ", CharacterID(), favoriteIDs.c_str(), containers.c_str(), combineObjectSlots);
|
||||
|
||||
TradeskillSearchResults(query, tsf->object_type, tsf->some_id);
|
||||
return;
|
||||
@@ -11707,13 +11850,25 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
|
||||
|
||||
// make where clause segment for container(s)
|
||||
char containers[30];
|
||||
uint32 combineObjectSlots;
|
||||
if (rss->some_id == 0) {
|
||||
// world combiner so no item number
|
||||
snprintf(containers, 29, "= %u", rss->object_type);
|
||||
combineObjectSlots = 10;
|
||||
}
|
||||
else {
|
||||
// container in inventory
|
||||
snprintf(containers, 29, "in (%u,%u)", rss->object_type, rss->some_id);
|
||||
auto item = database.GetItem(rss->some_id);
|
||||
if (!item)
|
||||
{
|
||||
Log(Logs::General, Logs::Error, "Invalid container ID: %d. GetItem returned null. Defaulting to BagSlots = 10.\n", rss->some_id);
|
||||
combineObjectSlots = 10;
|
||||
}
|
||||
else
|
||||
{
|
||||
combineObjectSlots = item->BagSlots;
|
||||
}
|
||||
}
|
||||
|
||||
std::string searchClause;
|
||||
@@ -11738,10 +11893,10 @@ void Client::Handle_OP_RecipesSearch(const EQApplicationPacket *app)
|
||||
"AND crl.madecount IS NOT NULL) "
|
||||
"OR (tr.must_learn & 0x3 = 0)) "
|
||||
"GROUP BY tr.id "
|
||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 "
|
||||
"HAVING sum(if(tre.item_id %s AND tre.iscontainer > 0,1,0)) > 0 AND SUM(tre.componentcount) <= %u "
|
||||
"LIMIT 200 ",
|
||||
CharacterID(), searchClause.c_str(),
|
||||
rss->mintrivial, rss->maxtrivial, containers);
|
||||
rss->mintrivial, rss->maxtrivial, containers, combineObjectSlots);
|
||||
TradeskillSearchResults(query, rss->object_type, rss->some_id);
|
||||
return;
|
||||
}
|
||||
@@ -12063,15 +12218,6 @@ void Client::Handle_OP_SenseHeading(const EQApplicationPacket *app)
|
||||
|
||||
int chancemod = 0;
|
||||
|
||||
// The client seems to limit sense heading packets based on skill
|
||||
// level. So if we're really low, we don't hit this routine very often.
|
||||
// I think it's the GUI deciding when to skill you up.
|
||||
// So, I'm adding a mod here which is larger at lower levels so
|
||||
// very low levels get a much better chance to skill up when the GUI
|
||||
// eventually sends a message.
|
||||
if (GetLevel() <= 8)
|
||||
chancemod += (9 - level) * 10;
|
||||
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillSenseHeading, nullptr, chancemod);
|
||||
|
||||
return;
|
||||
@@ -12094,7 +12240,8 @@ void Client::Handle_OP_SenseTraps(const EQApplicationPacket *app)
|
||||
|
||||
p_timers.Start(pTimerSenseTraps, reuse - 1);
|
||||
|
||||
Trap* trap = entity_list.FindNearbyTrap(this, 800);
|
||||
float trap_curdist = 0;
|
||||
Trap* trap = entity_list.FindNearbyTrap(this, 800, trap_curdist);
|
||||
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillSenseTraps, nullptr);
|
||||
|
||||
@@ -13034,6 +13181,8 @@ void Client::Handle_OP_SpawnAppearance(const EQApplicationPacket *app)
|
||||
InterruptSpell();
|
||||
SetFeigned(false);
|
||||
BindWound(this, false, true);
|
||||
tmSitting = Timer::GetCurrentTime();
|
||||
BuffFadeBySitModifier();
|
||||
}
|
||||
else if (sa->parameter == ANIM_CROUCH) {
|
||||
if (!UseBardSpellLogic())
|
||||
|
||||
@@ -204,6 +204,7 @@
|
||||
void Handle_OP_MercenaryTimerRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_MoveCoin(const EQApplicationPacket *app);
|
||||
void Handle_OP_MoveItem(const EQApplicationPacket *app);
|
||||
void Handle_OP_MoveMultipleItems(const EQApplicationPacket *app);
|
||||
void Handle_OP_OpenContainer(const EQApplicationPacket *app);
|
||||
void Handle_OP_OpenGuildTributeMaster(const EQApplicationPacket *app);
|
||||
void Handle_OP_OpenInventory(const EQApplicationPacket *app);
|
||||
@@ -227,6 +228,7 @@
|
||||
void Handle_OP_PurchaseLeadershipAA(const EQApplicationPacket *app);
|
||||
void Handle_OP_PVPLeaderBoardDetailsRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_PVPLeaderBoardRequest(const EQApplicationPacket *app);
|
||||
void Handle_OP_QueryUCSServerStatus(const EQApplicationPacket *app);
|
||||
void Handle_OP_RaidCommand(const EQApplicationPacket *app);
|
||||
void Handle_OP_RandomReq(const EQApplicationPacket *app);
|
||||
void Handle_OP_ReadBook(const EQApplicationPacket *app);
|
||||
|
||||
+77
-82
@@ -264,13 +264,21 @@ bool Client::Process() {
|
||||
if (distance <= scan_range) {
|
||||
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
||||
}
|
||||
else if (mob->GetAggroRange() > scan_range) {
|
||||
else if ((mob->GetAggroRange() * mob->GetAggroRange()) > scan_range) {
|
||||
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
|
||||
}
|
||||
}
|
||||
|
||||
if (force_spawn_updates && mob != this && distance <= client_update_range)
|
||||
mob->SendPositionUpdateToClient(this);
|
||||
if (force_spawn_updates && mob != this) {
|
||||
|
||||
if (mob->is_distance_roamer) {
|
||||
mob->SendPositionUpdateToClient(this);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (distance <= client_update_range)
|
||||
mob->SendPositionUpdateToClient(this);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -524,10 +532,9 @@ bool Client::Process() {
|
||||
DoEnduranceUpkeep();
|
||||
}
|
||||
|
||||
if (consume_food_timer.Check()) {
|
||||
m_pp.hunger_level = m_pp.hunger_level - 1;
|
||||
m_pp.thirst_level = m_pp.thirst_level - 1;
|
||||
}
|
||||
// this is independent of the tick timer
|
||||
if (consume_food_timer.Check())
|
||||
DoStaminaHungerUpdate();
|
||||
|
||||
if (tic_timer.Check() && !dead) {
|
||||
CalcMaxHP();
|
||||
@@ -539,7 +546,6 @@ bool Client::Process() {
|
||||
DoManaRegen();
|
||||
DoEnduranceRegen();
|
||||
BuffProcess();
|
||||
DoStaminaHungerUpdate();
|
||||
|
||||
if (tribute_timer.Check()) {
|
||||
ToggleTribute(true); //re-activate the tribute.
|
||||
@@ -648,17 +654,17 @@ bool Client::Process() {
|
||||
{
|
||||
//client logged out or errored out
|
||||
//ResetTrade();
|
||||
if (client_state != CLIENT_KICKED && !zoning && !instalog) {
|
||||
if (client_state != CLIENT_KICKED && !bZoning && !instalog) {
|
||||
Save();
|
||||
}
|
||||
|
||||
client_state = CLIENT_LINKDEAD;
|
||||
if (zoning || instalog || GetGM())
|
||||
if (bZoning || instalog || GetGM())
|
||||
{
|
||||
Group *mygroup = GetGroup();
|
||||
if (mygroup)
|
||||
{
|
||||
if (!zoning)
|
||||
if (!bZoning)
|
||||
{
|
||||
entity_list.MessageGroup(this, true, 15, "%s logged out.", GetName());
|
||||
LeaveGroup();
|
||||
@@ -677,7 +683,7 @@ bool Client::Process() {
|
||||
Raid *myraid = entity_list.GetRaidByClient(this);
|
||||
if (myraid)
|
||||
{
|
||||
if (!zoning)
|
||||
if (!bZoning)
|
||||
{
|
||||
//entity_list.MessageGroup(this,true,15,"%s logged out.",GetName());
|
||||
myraid->MemberZoned(this);
|
||||
@@ -880,7 +886,7 @@ void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
|
||||
uint8 handychance = 0;
|
||||
for (itr = merlist.begin(); itr != merlist.end() && i <= numItemSlots; ++itr) {
|
||||
MerchantList ml = *itr;
|
||||
if (merch->CastToNPC()->GetMerchantProbability() > ml.probability)
|
||||
if (ml.probability != 100 && zone->random.Int(1, 100) > ml.probability)
|
||||
continue;
|
||||
|
||||
if (GetLevel() < ml.level_required)
|
||||
@@ -1056,7 +1062,8 @@ void Client::OPRezzAnswer(uint32 Action, uint32 SpellID, uint16 ZoneID, uint16 I
|
||||
SetMana(0);
|
||||
SetHP(GetMaxHP()/5);
|
||||
int rez_eff = 756;
|
||||
if (GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE)
|
||||
if (RuleB(Character, UseOldRaceRezEffects) &&
|
||||
(GetRace() == BARBARIAN || GetRace() == DWARF || GetRace() == TROLL || GetRace() == OGRE))
|
||||
rez_eff = 757;
|
||||
SpellOnTarget(rez_eff, this); // Rezz effects
|
||||
}
|
||||
@@ -1827,7 +1834,7 @@ void Client::OPGMSummon(const EQApplicationPacket *app)
|
||||
}
|
||||
|
||||
void Client::DoHPRegen() {
|
||||
SetHP(GetHP() + CalcHPRegen() + RestRegenHP);
|
||||
SetHP(GetHP() + CalcHPRegen());
|
||||
SendHPUpdate();
|
||||
}
|
||||
|
||||
@@ -1835,46 +1842,55 @@ void Client::DoManaRegen() {
|
||||
if (GetMana() >= max_mana && spellbonuses.ManaRegen >= 0)
|
||||
return;
|
||||
|
||||
SetMana(GetMana() + CalcManaRegen() + RestRegenMana);
|
||||
if (GetMana() < max_mana && (IsSitting() || CanMedOnHorse()) && HasSkill(EQEmu::skills::SkillMeditate))
|
||||
CheckIncreaseSkill(EQEmu::skills::SkillMeditate, nullptr, -5);
|
||||
|
||||
SetMana(GetMana() + CalcManaRegen());
|
||||
CheckManaEndUpdate();
|
||||
}
|
||||
|
||||
void Client::DoStaminaHungerUpdate() {
|
||||
if(!stamina_timer.Check())
|
||||
return;
|
||||
|
||||
void Client::DoStaminaHungerUpdate()
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_Stamina, sizeof(Stamina_Struct));
|
||||
Stamina_Struct* sta = (Stamina_Struct*)outapp->pBuffer;
|
||||
Stamina_Struct *sta = (Stamina_Struct *)outapp->pBuffer;
|
||||
|
||||
Log(Logs::General, Logs::Food, "Client::DoStaminaHungerUpdate() hunger_level: %i thirst_level: %i before loss", m_pp.hunger_level, m_pp.thirst_level);
|
||||
Log(Logs::General, Logs::Food, "Client::DoStaminaHungerUpdate() hunger_level: %i thirst_level: %i before loss",
|
||||
m_pp.hunger_level, m_pp.thirst_level);
|
||||
|
||||
if (zone->GetZoneID() != 151) {
|
||||
sta->food = m_pp.hunger_level > 6000 ? 6000 : m_pp.hunger_level;
|
||||
sta->water = m_pp.thirst_level > 6000 ? 6000 : m_pp.thirst_level;
|
||||
}
|
||||
else {
|
||||
if (zone->GetZoneID() != 151 && !GetGM()) {
|
||||
int loss = RuleI(Character, FoodLossPerUpdate);
|
||||
if (GetHorseId() != 0)
|
||||
loss *= 3;
|
||||
|
||||
m_pp.hunger_level = EQEmu::Clamp(m_pp.hunger_level - loss, 0, 6000);
|
||||
m_pp.thirst_level = EQEmu::Clamp(m_pp.thirst_level - loss, 0, 6000);
|
||||
if (spellbonuses.hunger) {
|
||||
m_pp.hunger_level = EQEmu::ClampLower(m_pp.hunger_level, 3500);
|
||||
m_pp.thirst_level = EQEmu::ClampLower(m_pp.thirst_level, 3500);
|
||||
}
|
||||
sta->food = m_pp.hunger_level;
|
||||
sta->water = m_pp.thirst_level;
|
||||
} else {
|
||||
// No auto food/drink consumption in the Bazaar
|
||||
sta->food = 6000;
|
||||
sta->water = 6000;
|
||||
}
|
||||
|
||||
Log(Logs::General, Logs::Food,
|
||||
"Client::DoStaminaHungerUpdate() Current hunger_level: %i = (%i minutes left) thirst_level: %i = (%i minutes left) - after loss",
|
||||
m_pp.hunger_level,
|
||||
m_pp.hunger_level,
|
||||
m_pp.thirst_level,
|
||||
m_pp.thirst_level
|
||||
);
|
||||
Log(Logs::General, Logs::Food,
|
||||
"Client::DoStaminaHungerUpdate() Current hunger_level: %i = (%i minutes left) thirst_level: %i = (%i "
|
||||
"minutes left) - after loss",
|
||||
m_pp.hunger_level, m_pp.hunger_level, m_pp.thirst_level, m_pp.thirst_level);
|
||||
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
void Client::DoEnduranceRegen()
|
||||
{
|
||||
if(GetEndurance() >= GetMaxEndurance())
|
||||
return;
|
||||
// endurance has some negative mods that could result in a negative regen when starved
|
||||
int regen = CalcEnduranceRegen();
|
||||
|
||||
SetEndurance(GetEndurance() + CalcEnduranceRegen() + RestRegenEndurance);
|
||||
if (regen < 0 || (regen > 0 && GetEndurance() < GetMaxEndurance()))
|
||||
SetEndurance(GetEndurance() + regen);
|
||||
}
|
||||
|
||||
void Client::DoEnduranceUpkeep() {
|
||||
@@ -1923,12 +1939,12 @@ void Client::CalcRestState() {
|
||||
// The client must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
||||
// must be sitting down, and must not have any detrimental spells affecting them.
|
||||
//
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
RestRegenHP = RestRegenMana = RestRegenEndurance = 0;
|
||||
ooc_regen = false;
|
||||
|
||||
if(AggroCount || !IsSitting())
|
||||
if(AggroCount || !(IsSitting() || CanMedOnHorse()))
|
||||
return;
|
||||
|
||||
if(!rest_timer.Check(false))
|
||||
@@ -1943,67 +1959,46 @@ void Client::CalcRestState() {
|
||||
}
|
||||
}
|
||||
|
||||
RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
|
||||
ooc_regen = true;
|
||||
|
||||
RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
|
||||
|
||||
if(RuleB(Character, RestRegenEndurance))
|
||||
RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100);
|
||||
}
|
||||
|
||||
void Client::DoTracking()
|
||||
{
|
||||
if(TrackingID == 0)
|
||||
if (TrackingID == 0)
|
||||
return;
|
||||
|
||||
Mob *m = entity_list.GetMob(TrackingID);
|
||||
|
||||
if(!m || m->IsCorpse())
|
||||
{
|
||||
if (!m || m->IsCorpse()) {
|
||||
Message_StringID(MT_Skills, TRACK_LOST_TARGET);
|
||||
|
||||
TrackingID = 0;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
float RelativeHeading = GetHeading() - CalculateHeadingToTarget(m->GetX(), m->GetY());
|
||||
|
||||
if(RelativeHeading < 0)
|
||||
RelativeHeading += 256;
|
||||
if (RelativeHeading < 0)
|
||||
RelativeHeading += 512;
|
||||
|
||||
if((RelativeHeading <= 16) || (RelativeHeading >= 240))
|
||||
{
|
||||
if (RelativeHeading > 480)
|
||||
Message_StringID(MT_Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName());
|
||||
}
|
||||
else if((RelativeHeading > 16) && (RelativeHeading <= 48))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "right");
|
||||
}
|
||||
else if((RelativeHeading > 48) && (RelativeHeading <= 80))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "right");
|
||||
}
|
||||
else if((RelativeHeading > 80) && (RelativeHeading <= 112))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "right");
|
||||
}
|
||||
else if((RelativeHeading > 112) && (RelativeHeading <= 144))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_YOU, m->GetCleanName());
|
||||
}
|
||||
else if((RelativeHeading > 144) && (RelativeHeading <= 176))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "left");
|
||||
}
|
||||
else if((RelativeHeading > 176) && (RelativeHeading <= 208))
|
||||
{
|
||||
Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "left");
|
||||
}
|
||||
else if((RelativeHeading > 208) && (RelativeHeading < 240))
|
||||
{
|
||||
else if (RelativeHeading > 416)
|
||||
Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "left");
|
||||
}
|
||||
else if (RelativeHeading > 352)
|
||||
Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "left");
|
||||
else if (RelativeHeading > 288)
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "left");
|
||||
else if (RelativeHeading > 224)
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_YOU, m->GetCleanName());
|
||||
else if (RelativeHeading > 160)
|
||||
Message_StringID(MT_Skills, TRACK_BEHIND_AND_TO, m->GetCleanName(), "right");
|
||||
else if (RelativeHeading > 96)
|
||||
Message_StringID(MT_Skills, TRACK_TO_THE, m->GetCleanName(), "right");
|
||||
else if (RelativeHeading > 32)
|
||||
Message_StringID(MT_Skills, TRACK_AHEAD_AND_TO, m->GetCleanName(), "right");
|
||||
else if (RelativeHeading >= 0)
|
||||
Message_StringID(MT_Skills, TRACK_STRAIGHT_AHEAD, m->GetCleanName());
|
||||
}
|
||||
|
||||
void Client::HandleRespawnFromHover(uint32 Option)
|
||||
|
||||
+506
-231
@@ -66,10 +66,12 @@
|
||||
#include "titles.h"
|
||||
#include "water_map.h"
|
||||
#include "worldserver.h"
|
||||
#include "fastmath.h"
|
||||
|
||||
extern QueryServ* QServ;
|
||||
extern WorldServer worldserver;
|
||||
extern TaskManager *taskmanager;
|
||||
extern FastMath g_Math;
|
||||
void CatchSignal(int sig_num);
|
||||
|
||||
|
||||
@@ -291,7 +293,7 @@ int command_init(void)
|
||||
#endif
|
||||
|
||||
command_add("path", "- view and edit pathing", 200, command_path) ||
|
||||
command_add("peekinv", "[worn/inv/cursor/trib/bank/trade/world/all] - Print out contents of your player target's inventory", 100, command_peekinv) ||
|
||||
command_add("peekinv", "[equip/gen/cursor/poss/limbo/curlim/trib/bank/shbank/allbank/trade/world/all] - Print out contents of your player target's inventory", 100, command_peekinv) ||
|
||||
command_add("peqzone", "[zonename] - Go to specified zone, if you have > 75% health", 0, command_peqzone) ||
|
||||
command_add("permaclass", "[classnum] - Change your or your player target's class (target is disconnected)", 80, command_permaclass) ||
|
||||
command_add("permagender", "[gendernum] - Change your or your player target's gender (zone to take effect)", 80, command_permagender) ||
|
||||
@@ -305,6 +307,7 @@ int command_init(void)
|
||||
command_add("profilereset", "- Reset profiling info", 250, command_profilereset) ||
|
||||
#endif
|
||||
|
||||
command_add("push", "Lets you do spell push", 150, command_push) ||
|
||||
command_add("pvp", "[on/off] - Set your or your player target's PVP status", 100, command_pvp) ||
|
||||
command_add("qglobal", "[on/off/view] - Toggles qglobal functionality on an NPC", 100, command_qglobal) ||
|
||||
command_add("questerrors", "Shows quest errors.", 100, command_questerrors) ||
|
||||
@@ -316,10 +319,12 @@ int command_init(void)
|
||||
command_add("reloadallrules", "Executes a reload of all rules.", 80, command_reloadallrules) ||
|
||||
command_add("reloademote", "Reloads NPC Emotes", 80, command_reloademote) ||
|
||||
command_add("reloadlevelmods", nullptr, 255, command_reloadlevelmods) ||
|
||||
command_add("reloadmerchants", nullptr, 255, command_reloadmerchants) ||
|
||||
command_add("reloadperlexportsettings", nullptr, 255, command_reloadperlexportsettings) ||
|
||||
command_add("reloadqst", " - Clear quest cache (any argument causes it to also stop all timers)", 150, command_reloadqst) ||
|
||||
command_add("reloadrulesworld", "Executes a reload of all rules in world specifically.", 80, command_reloadworldrules) ||
|
||||
command_add("reloadstatic", "- Reload Static Zone Data", 150, command_reloadstatic) ||
|
||||
command_add("reloadtraps", "- Repops all traps in the current zone.", 80, command_reloadtraps) ||
|
||||
command_add("reloadtitles", "- Reload player titles from the database", 150, command_reloadtitles) ||
|
||||
command_add("reloadworld", "[0|1] - Clear quest cache (0 - no repop, 1 - repop)", 255, command_reloadworld) ||
|
||||
command_add("reloadzps", "- Reload zone points from database", 150, command_reloadzps) ||
|
||||
@@ -355,9 +360,11 @@ int command_init(void)
|
||||
command_add("showbonusstats", "[item|spell|all] Shows bonus stats for target from items or spells. Shows both by default.", 50, command_showbonusstats) ||
|
||||
command_add("showbuffs", "- List buffs active on your target or you if no target", 50, command_showbuffs) ||
|
||||
command_add("shownumhits", "Shows buffs numhits for yourself.", 0, command_shownumhits) ||
|
||||
command_add("shownpcgloballoot", "Show GlobalLoot entires on this npc", 50, command_shownpcgloballoot) ||
|
||||
command_add("showskills", "- Show the values of your or your player target's skills", 50, command_showskills) ||
|
||||
command_add("showspellslist", "Shows spell list of targeted NPC", 100, command_showspellslist) ||
|
||||
command_add("showstats", "- Show details about you or your target", 50, command_showstats) ||
|
||||
command_add("showzonegloballoot", "Show GlobalLoot entires on this zone", 50, command_showzonegloballoot) ||
|
||||
command_add("shutdown", "- Shut this zone process down", 150, command_shutdown) ||
|
||||
command_add("size", "[size] - Change size of you or your target", 50, command_size) ||
|
||||
command_add("spawn", "[name] [race] [level] [material] [hp] [gender] [class] [priweapon] [secweapon] [merchantid] - Spawn an NPC", 10, command_spawn) ||
|
||||
@@ -374,6 +381,7 @@ int command_init(void)
|
||||
command_add("task", "(subcommand) - Task system commands", 150, command_task) ||
|
||||
command_add("tattoo", "- Change the tattoo of your target (Drakkin Only)", 80, command_tattoo) ||
|
||||
command_add("tempname", "[newname] - Temporarily renames your target. Leave name blank to restore the original name.", 100, command_tempname) ||
|
||||
command_add("petname", "[newname] - Temporarily renames your pet. Leave name blank to restore the original name.", 100, command_petname) ||
|
||||
command_add("texture", "[texture] [helmtexture] - Change your or your target's appearance, use 255 to show equipment", 10, command_texture) ||
|
||||
command_add("time", "[HH] [MM] - Set EQ time", 90, command_time) ||
|
||||
command_add("timers", "- Display persistent timers for target", 200, command_timers) ||
|
||||
@@ -381,7 +389,9 @@ int command_init(void)
|
||||
command_add("title", "[text] [1 = create title table row] - Set your or your player target's title", 50, command_title) ||
|
||||
command_add("titlesuffix", "[text] [1 = create title table row] - Set your or your player target's title suffix", 50, command_titlesuffix) ||
|
||||
command_add("traindisc", "[level] - Trains all the disciplines usable by the target, up to level specified. (may freeze client for a few seconds)", 150, command_traindisc) ||
|
||||
command_add("trapinfo", "- Gets infomation about the traps currently spawned in the zone.", 81, command_trapinfo) ||
|
||||
command_add("tune", "Calculate ideal statical values related to combat.", 100, command_tune) ||
|
||||
command_add("ucs", "- Attempts to reconnect to the UCS server", 0, command_ucs) ||
|
||||
command_add("undyeme", "- Remove dye from all of your armor slots", 0, command_undyeme) ||
|
||||
command_add("unfreeze", "- Unfreeze your target", 80, command_unfreeze) ||
|
||||
command_add("unlock", "- Unlock the worldserver", 150, command_unlock) ||
|
||||
@@ -2315,14 +2325,18 @@ void command_race(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *t=c->CastToMob();
|
||||
|
||||
// Need to figure out max race for LoY/LDoN: going with upper bound of 500 now for testing
|
||||
if (sep->IsNumber(1) && atoi(sep->arg[1]) >= 0 && atoi(sep->arg[1]) <= 2259) {
|
||||
if ((c->GetTarget()) && c->Admin() >= commandRaceOthers)
|
||||
t=c->GetTarget();
|
||||
t->SendIllusionPacket(atoi(sep->arg[1]));
|
||||
if (sep->IsNumber(1)) {
|
||||
auto race = atoi(sep->arg[1]);
|
||||
if ((race >= 0 && race <= 732) || (race >= 2253 && race <= 2259)) {
|
||||
if ((c->GetTarget()) && c->Admin() >= commandRaceOthers)
|
||||
t = c->GetTarget();
|
||||
t->SendIllusionPacket(race);
|
||||
} else {
|
||||
c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)");
|
||||
}
|
||||
} else {
|
||||
c->Message(0, "Usage: #race [0-732, 2253-2259] (0 for back to normal)");
|
||||
}
|
||||
else
|
||||
c->Message(0, "Usage: #race [0-2259] (0 for back to normal)");
|
||||
}
|
||||
|
||||
void command_gender(Client *c, const Seperator *sep)
|
||||
@@ -2453,7 +2467,9 @@ void command_npctypespawn(Client *c, const Seperator *sep)
|
||||
if (npc && sep->IsNumber(2))
|
||||
npc->SetNPCFactionID(atoi(sep->arg[2]));
|
||||
|
||||
npc->AddLootTable();
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc);
|
||||
}
|
||||
else
|
||||
@@ -2503,244 +2519,347 @@ void command_nukeitem(Client *c, const Seperator *sep)
|
||||
|
||||
void command_peekinv(Client *c, const Seperator *sep)
|
||||
{
|
||||
// this can be cleaned up once inventory is cleaned up
|
||||
enum {
|
||||
peekWorn = 0x01,
|
||||
peekInv = 0x02,
|
||||
peekCursor = 0x04,
|
||||
peekTrib = 0x08,
|
||||
peekBank = 0x10,
|
||||
peekTrade = 0x20,
|
||||
peekWorld = 0x40
|
||||
} ;
|
||||
peekNone = 0x0000,
|
||||
peekEquip = 0x0001,
|
||||
peekGen = 0x0002,
|
||||
peekCursor = 0x0004,
|
||||
peekLimbo = 0x0008,
|
||||
peekTrib = 0x0010,
|
||||
peekBank = 0x0020,
|
||||
peekShBank = 0x0040,
|
||||
peekTrade = 0x0080,
|
||||
peekWorld = 0x0100,
|
||||
peekOutOfScope = (peekWorld * 2) // less than
|
||||
};
|
||||
|
||||
if (!c->GetTarget() || !c->GetTarget()->IsClient()) {
|
||||
c->Message(0, "You must have a PC target selected for this command");
|
||||
static char* scope_prefix[] = { "Equip", "Gen", "Cursor", "Limbo", "Trib", "Bank", "ShBank", "Trade", "World" };
|
||||
|
||||
static int16 scope_range[][2] = {
|
||||
{ EQEmu::legacy::EQUIPMENT_BEGIN, EQEmu::legacy::EQUIPMENT_END },
|
||||
{ EQEmu::legacy::GENERAL_BEGIN, EQEmu::legacy::GENERAL_END },
|
||||
{ EQEmu::legacy::SLOT_CURSOR, EQEmu::legacy::SLOT_CURSOR },
|
||||
{ EQEmu::legacy::SLOT_INVALID, EQEmu::legacy::SLOT_INVALID },
|
||||
{ EQEmu::legacy::TRIBUTE_BEGIN, EQEmu::legacy::TRIBUTE_END },
|
||||
{ EQEmu::legacy::BANK_BEGIN, EQEmu::legacy::BANK_END },
|
||||
{ EQEmu::legacy::SHARED_BANK_BEGIN, EQEmu::legacy::SHARED_BANK_END },
|
||||
{ EQEmu::legacy::TRADE_BEGIN, EQEmu::legacy::TRADE_END },
|
||||
{ EQEmu::inventory::slotBegin, (EQEmu::legacy::WORLD_SIZE - 1) }
|
||||
};
|
||||
|
||||
static bool scope_bag[] = { false, true, true, true, false, true, true, true, true };
|
||||
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
if (c->GetTarget() && !c->GetTarget()->IsClient()) {
|
||||
c->Message(0, "You must target a PC for this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
int scopeWhere = 0;
|
||||
int scopeMask = peekNone;
|
||||
|
||||
if (strcasecmp(sep->arg[1], "all") == 0) { scopeWhere = ~0; }
|
||||
else if (strcasecmp(sep->arg[1], "worn") == 0) { scopeWhere |= peekWorn; }
|
||||
else if (strcasecmp(sep->arg[1], "inv") == 0) { scopeWhere |= peekInv; }
|
||||
else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeWhere |= peekCursor; }
|
||||
else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeWhere |= peekTrib; }
|
||||
else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeWhere |= peekBank; }
|
||||
else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeWhere |= peekTrade; }
|
||||
else if (strcasecmp(sep->arg[1], "world") == 0) { scopeWhere |= peekWorld; }
|
||||
if (strcasecmp(sep->arg[1], "all") == 0) { scopeMask = (peekOutOfScope - 1); }
|
||||
else if (strcasecmp(sep->arg[1], "equip") == 0) { scopeMask |= peekEquip; }
|
||||
else if (strcasecmp(sep->arg[1], "gen") == 0) { scopeMask |= peekGen; }
|
||||
else if (strcasecmp(sep->arg[1], "cursor") == 0) { scopeMask |= peekCursor; }
|
||||
else if (strcasecmp(sep->arg[1], "poss") == 0) { scopeMask |= (peekEquip | peekGen | peekCursor); }
|
||||
else if (strcasecmp(sep->arg[1], "limbo") == 0) { scopeMask |= peekLimbo; }
|
||||
else if (strcasecmp(sep->arg[1], "curlim") == 0) { scopeMask |= (peekCursor | peekLimbo); }
|
||||
else if (strcasecmp(sep->arg[1], "trib") == 0) { scopeMask |= peekTrib; }
|
||||
else if (strcasecmp(sep->arg[1], "bank") == 0) { scopeMask |= peekBank; }
|
||||
else if (strcasecmp(sep->arg[1], "shbank") == 0) { scopeMask |= peekShBank; }
|
||||
else if (strcasecmp(sep->arg[1], "allbank") == 0) { scopeMask |= (peekBank | peekShBank); }
|
||||
else if (strcasecmp(sep->arg[1], "trade") == 0) { scopeMask |= peekTrade; }
|
||||
else if (strcasecmp(sep->arg[1], "world") == 0) { scopeMask |= peekWorld; }
|
||||
|
||||
if (scopeWhere == 0) {
|
||||
c->Message(0, "Usage: #peekinv [worn|inv|cursor|trib|bank|trade|world|all]");
|
||||
c->Message(0, " Displays a portion of the targeted user's inventory");
|
||||
c->Message(0, " Caution: 'all' is a lot of information!");
|
||||
if (!scopeMask) {
|
||||
c->Message(0, "Usage: #peekinv [equip|gen|cursor|poss|limbo|curlim|trib|bank|shbank|allbank|trade|world|all]");
|
||||
c->Message(0, "- Displays a portion of the targeted user's inventory");
|
||||
c->Message(0, "- Caution: 'all' is a lot of information!");
|
||||
return;
|
||||
}
|
||||
|
||||
Client* targetClient = c->GetTarget()->CastToClient();
|
||||
Client* targetClient = c;
|
||||
if (c->GetTarget())
|
||||
targetClient = c->GetTarget()->CastToClient();
|
||||
|
||||
const EQEmu::ItemInstance* inst_main = nullptr;
|
||||
const EQEmu::ItemInstance* inst_sub = nullptr;
|
||||
const EQEmu::ItemInstance* inst_aug = nullptr;
|
||||
const EQEmu::ItemData* item_data = nullptr;
|
||||
std::string item_link;
|
||||
|
||||
EQEmu::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
|
||||
c->Message(0, "Displaying inventory for %s...", targetClient->GetName());
|
||||
c->Message(0, "Displaying inventory for %s...", targetClient->GetName());
|
||||
|
||||
// worn
|
||||
for (int16 indexMain = EQEmu::legacy::EQUIPMENT_BEGIN; (scopeWhere & peekWorn) && (indexMain <= EQEmu::legacy::EQUIPMENT_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
Object* objectTradeskill = targetClient->GetTradeskillObject();
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
bool itemsFound = false;
|
||||
|
||||
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
}
|
||||
for (int scopeIndex = 0, scopeBit = peekEquip; scopeBit < peekOutOfScope; ++scopeIndex, scopeBit <<= 1) {
|
||||
if (scopeBit & ~scopeMask)
|
||||
continue;
|
||||
|
||||
if ((scopeWhere & peekWorn) && (targetClient->ClientVersion() >= EQEmu::versions::ClientVersion::SoF)) {
|
||||
inst_main = targetClient->GetInv().GetItem(EQEmu::inventory::slotPowerSource);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "WornSlot: %i, Item: %i (%s), Charges: %i",
|
||||
EQEmu::inventory::slotPowerSource, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
}
|
||||
|
||||
// inv
|
||||
for (int16 indexMain = EQEmu::legacy::GENERAL_BEGIN; (scopeWhere & peekInv) && (indexMain <= EQEmu::legacy::GENERAL_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "InvSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), " InvBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
if (scopeBit & peekWorld) {
|
||||
if (objectTradeskill == nullptr) {
|
||||
c->Message(1, "No world tradeskill object selected...");
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
c->Message(0, "[WorldObject DBID: %i (entityid: %i)]", objectTradeskill->GetDBID(), objectTradeskill->GetID());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// cursor
|
||||
if (scopeWhere & peekCursor) {
|
||||
if (targetClient->GetInv().CursorEmpty()) {
|
||||
linker.SetItemInst(nullptr);
|
||||
for (int16 indexMain = scope_range[scopeIndex][0]; indexMain <= scope_range[scopeIndex][1]; ++indexMain) {
|
||||
if (indexMain == EQEmu::legacy::SLOT_INVALID)
|
||||
continue;
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
inst_main = ((scopeBit & peekWorld) ? objectTradeskill->GetItem(indexMain) : targetClient->GetInv().GetItem(indexMain));
|
||||
if (inst_main) {
|
||||
itemsFound = true;
|
||||
item_data = inst_main->GetItem();
|
||||
}
|
||||
else {
|
||||
item_data = nullptr;
|
||||
}
|
||||
|
||||
c->Message(1, "CursorSlot: %i, Item: %i (%s), Charges: %i",
|
||||
EQEmu::inventory::slotCursor, 0, item_link.c_str(), 0);
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
((scopeBit & peekWorld) ? (EQEmu::legacy::WORLD_BEGIN + indexMain) : indexMain),
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
||||
);
|
||||
|
||||
if (inst_main && inst_main->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQEmu::inventory::socketBegin; indexAug < EQEmu::inventory::SocketCount; ++indexAug) {
|
||||
inst_aug = inst_main->GetItem(indexAug);
|
||||
if (!inst_aug) // extant only
|
||||
continue;
|
||||
|
||||
item_data = inst_aug->GetItem();
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
((scopeBit & peekWorld) ? (EQEmu::legacy::WORLD_BEGIN + indexMain) : indexMain),
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag()))
|
||||
continue;
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; indexSub < EQEmu::inventory::ContainerCount; ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
if (!inst_sub) // extant only
|
||||
continue;
|
||||
|
||||
item_data = inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub)),
|
||||
((scopeBit & peekWorld) ? (EQEmu::legacy::WORLD_BEGIN + indexMain) : indexMain),
|
||||
indexSub,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
|
||||
if (inst_sub->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQEmu::inventory::socketBegin; indexAug < EQEmu::inventory::SocketCount; ++indexAug) {
|
||||
inst_aug = inst_sub->GetItem(indexAug);
|
||||
if (!inst_aug) // extant only
|
||||
continue;
|
||||
|
||||
item_data = inst_aug->GetItem();
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
((scopeBit & peekWorld) ? INVALID_INDEX : EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub)),
|
||||
indexSub,
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
int cursorDepth = 0;
|
||||
for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++cursorDepth) {
|
||||
|
||||
if ((scopeBit & peekEquip) && (targetClient->ClientVersion() >= EQEmu::versions::ClientVersion::SoF)) {
|
||||
inst_main = targetClient->GetInv().GetItem(EQEmu::inventory::slotPowerSource);
|
||||
if (inst_main) {
|
||||
itemsFound = true;
|
||||
item_data = inst_main->GetItem();
|
||||
}
|
||||
else {
|
||||
item_data = nullptr;
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
EQEmu::inventory::slotPowerSource,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
||||
);
|
||||
|
||||
if (inst_main && inst_main->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQEmu::inventory::socketBegin; indexAug < EQEmu::inventory::SocketCount; ++indexAug) {
|
||||
inst_aug = inst_main->GetItem(indexAug);
|
||||
if (!inst_aug) // extant only
|
||||
continue;
|
||||
|
||||
item_data = inst_aug->GetItem();
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
EQEmu::inventory::slotPowerSource,
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scopeBit & peekLimbo) {
|
||||
int limboIndex = 0;
|
||||
for (auto it = targetClient->GetInv().cursor_cbegin(); (it != targetClient->GetInv().cursor_cend()); ++it, ++limboIndex) {
|
||||
if (it == targetClient->GetInv().cursor_cbegin())
|
||||
continue;
|
||||
|
||||
inst_main = *it;
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
if (inst_main) {
|
||||
itemsFound = true;
|
||||
item_data = inst_main->GetItem();
|
||||
}
|
||||
else {
|
||||
item_data = nullptr;
|
||||
}
|
||||
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"%sSlot: %i, Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
(8000 + limboIndex),
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_main == nullptr) ? 0 : inst_main->GetCharges())
|
||||
);
|
||||
|
||||
c->Message((item_data == nullptr), "CursorSlot: %i, Depth: %i, Item: %i (%s), Charges: %i",
|
||||
EQEmu::inventory::slotCursor, cursorDepth, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
if (inst_main && inst_main->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQEmu::inventory::socketBegin; indexAug < EQEmu::inventory::SocketCount; ++indexAug) {
|
||||
inst_aug = inst_main->GetItem(indexAug);
|
||||
if (!inst_aug) // extant only
|
||||
continue;
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; (cursorDepth == 0) && inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
item_data = inst_aug->GetItem();
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
".%sAugSlot: %i (Slot #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (!scope_bag[scopeIndex] || !(inst_main && inst_main->IsClassBag()))
|
||||
continue;
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; indexSub < EQEmu::inventory::ContainerCount; ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
if (!inst_sub)
|
||||
continue;
|
||||
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"..%sBagSlot: %i (Slot #%i, Bag idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexSub,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
|
||||
c->Message((item_data == nullptr), " CursorBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
EQEmu::InventoryProfile::CalcSlotId(EQEmu::inventory::slotCursor, indexSub), EQEmu::inventory::slotCursor, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
if (inst_sub->IsClassCommon()) {
|
||||
for (uint8 indexAug = EQEmu::inventory::socketBegin; indexAug < EQEmu::inventory::SocketCount; ++indexAug) {
|
||||
inst_aug = inst_sub->GetItem(indexAug);
|
||||
if (!inst_aug) // extant only
|
||||
continue;
|
||||
|
||||
item_data = inst_aug->GetItem();
|
||||
linker.SetItemInst(inst_aug);
|
||||
|
||||
c->Message(
|
||||
(item_data == nullptr),
|
||||
"...%sAugSlot: %i (Slot #%i, Sub idx #%i, Aug idx #%i), Item: %i (%s), Charges: %i",
|
||||
scope_prefix[scopeIndex],
|
||||
INVALID_INDEX,
|
||||
(8000 + limboIndex),
|
||||
indexSub,
|
||||
indexAug,
|
||||
((item_data == nullptr) ? 0 : item_data->ID),
|
||||
linker.GenerateLink().c_str(),
|
||||
((inst_sub == nullptr) ? 0 : inst_sub->GetCharges())
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// trib
|
||||
for (int16 indexMain = EQEmu::legacy::TRIBUTE_BEGIN; (scopeWhere & peekTrib) && (indexMain <= EQEmu::legacy::TRIBUTE_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "TributeSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
}
|
||||
|
||||
// bank
|
||||
for (int16 indexMain = EQEmu::legacy::BANK_BEGIN; (scopeWhere & peekBank) && (indexMain <= EQEmu::legacy::BANK_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "BankSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), " BankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
}
|
||||
}
|
||||
|
||||
for (int16 indexMain = EQEmu::legacy::SHARED_BANK_BEGIN; (scopeWhere & peekBank) && (indexMain <= EQEmu::legacy::SHARED_BANK_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "SharedBankSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), " SharedBankBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
}
|
||||
}
|
||||
|
||||
// trade
|
||||
for (int16 indexMain = EQEmu::legacy::TRADE_BEGIN; (scopeWhere & peekTrade) && (indexMain <= EQEmu::legacy::TRADE_END); ++indexMain) {
|
||||
inst_main = targetClient->GetInv().GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "TradeSlot: %i, Item: %i (%s), Charges: %i",
|
||||
indexMain, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsClassBag() && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), " TradeBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
EQEmu::InventoryProfile::CalcSlotId(indexMain, indexSub), indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
}
|
||||
}
|
||||
|
||||
// world
|
||||
if (scopeWhere & peekWorld) {
|
||||
Object* objectTradeskill = targetClient->GetTradeskillObject();
|
||||
|
||||
if (objectTradeskill == nullptr) {
|
||||
c->Message(1, "No world tradeskill object selected...");
|
||||
}
|
||||
else {
|
||||
c->Message(0, "[WorldObject DBID: %i (entityid: %i)]", objectTradeskill->GetDBID(), objectTradeskill->GetID());
|
||||
|
||||
for (int16 indexMain = EQEmu::inventory::slotBegin; indexMain < EQEmu::legacy::TYPE_WORLD_SIZE; ++indexMain) {
|
||||
inst_main = objectTradeskill->GetItem(indexMain);
|
||||
item_data = (inst_main == nullptr) ? nullptr : inst_main->GetItem();
|
||||
linker.SetItemInst(inst_main);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), "WorldSlot: %i, Item: %i (%s), Charges: %i",
|
||||
(EQEmu::legacy::WORLD_BEGIN + indexMain), ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_main == nullptr) ? 0 : inst_main->GetCharges()));
|
||||
|
||||
for (uint8 indexSub = EQEmu::inventory::containerBegin; inst_main && inst_main->IsType(EQEmu::item::ItemClassBag) && (indexSub < EQEmu::inventory::ContainerCount); ++indexSub) {
|
||||
inst_sub = inst_main->GetItem(indexSub);
|
||||
item_data = (inst_sub == nullptr) ? nullptr : inst_sub->GetItem();
|
||||
linker.SetItemInst(inst_sub);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message((item_data == nullptr), " WorldBagSlot: %i (Slot #%i, Bag #%i), Item: %i (%s), Charges: %i",
|
||||
INVALID_INDEX, indexMain, indexSub, ((item_data == nullptr) ? 0 : item_data->ID), item_link.c_str(), ((inst_sub == nullptr) ? 0 : inst_sub->GetCharges()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!itemsFound)
|
||||
c->Message(0, "No items found.");
|
||||
}
|
||||
|
||||
void command_interrogateinv(Client *c, const Seperator *sep)
|
||||
@@ -2992,6 +3111,11 @@ void command_reloadworld(Client *c, const Seperator *sep)
|
||||
safe_delete(pack);
|
||||
}
|
||||
|
||||
void command_reloadmerchants(Client *c, const Seperator *sep) {
|
||||
entity_list.ReloadMerchants();
|
||||
c->Message(15, "Reloading merchants.");
|
||||
}
|
||||
|
||||
void command_reloadlevelmods(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (sep->arg[1][0] == 0)
|
||||
@@ -3847,6 +3971,12 @@ void command_showstats(Client *c, const Seperator *sep)
|
||||
c->ShowStats(c);
|
||||
}
|
||||
|
||||
void command_showzonegloballoot(Client *c, const Seperator *sep)
|
||||
{
|
||||
c->Message(0, "GlobalLoot for %s (%d:%d)", zone->GetShortName(), zone->GetZoneID(), zone->GetInstanceVersion());
|
||||
zone->ShowZoneGlobalLoot(c);
|
||||
}
|
||||
|
||||
void command_mystats(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (c->GetTarget() && c->GetPet()) {
|
||||
@@ -4059,6 +4189,33 @@ void command_unfreeze(Client *c, const Seperator *sep)
|
||||
c->Message(0, "ERROR: Unfreeze requires a target.");
|
||||
}
|
||||
|
||||
void command_push(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *t = c;
|
||||
if (c->GetTarget() != nullptr)
|
||||
t = c->GetTarget();
|
||||
|
||||
if (!sep->arg[1] || !sep->IsNumber(1)) {
|
||||
c->Message(0, "ERROR: Must provide at least a push back.");
|
||||
return;
|
||||
}
|
||||
|
||||
float back = atof(sep->arg[1]);
|
||||
float up = 0.0f;
|
||||
|
||||
if (sep->arg[2] && sep->IsNumber(2))
|
||||
up = atof(sep->arg[2]);
|
||||
|
||||
if (t->IsNPC()) {
|
||||
t->IncDeltaX(back * g_Math.FastSin(c->GetHeading()));
|
||||
t->IncDeltaY(back * g_Math.FastCos(c->GetHeading()));
|
||||
t->IncDeltaZ(up);
|
||||
t->SetForcedMovement(6);
|
||||
} else if (t->IsClient()) {
|
||||
// TODO: send packet to push
|
||||
}
|
||||
}
|
||||
|
||||
void command_pvp(Client *c, const Seperator *sep)
|
||||
{
|
||||
bool state=atobool(sep->arg[1]);
|
||||
@@ -4156,6 +4313,26 @@ void command_tempname(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void command_petname(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *target;
|
||||
target = c->GetTarget();
|
||||
|
||||
if(!target)
|
||||
c->Message(0, "Usage: #petname newname (requires a target)");
|
||||
else if(target->IsPet() && (target->GetOwnerID() == c->GetID()) && strlen(sep->arg[1]) > 0)
|
||||
{
|
||||
char *oldname = strdup(target->GetName());
|
||||
target->TempName(sep->arg[1]);
|
||||
c->Message(0, "Renamed %s to %s", oldname, sep->arg[1]);
|
||||
free(oldname);
|
||||
}
|
||||
else {
|
||||
target->TempName();
|
||||
c->Message(0, "Restored the original name");
|
||||
}
|
||||
}
|
||||
|
||||
void command_npcspecialattk(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (c->GetTarget()==0 || c->GetTarget()->IsClient() || strlen(sep->arg[1]) <= 0 || strlen(sep->arg[2]) <= 0)
|
||||
@@ -4360,9 +4537,7 @@ void command_iteminfo(Client *c, const Seperator *sep)
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
linker.SetItemInst(inst);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
|
||||
c->Message(0, "*** Item Info for [%s] ***", item_link.c_str());
|
||||
c->Message(0, "*** Item Info for [%s] ***", linker.GenerateLink().c_str());
|
||||
c->Message(0, ">> ID: %u, ItemUseType: %u, ItemClassType: %u", item->ID, item->ItemType, item->ItemClass);
|
||||
c->Message(0, ">> IDFile: '%s', IconID: %u", item->IDFile, item->Icon);
|
||||
c->Message(0, ">> Size: %u, Weight: %u, Price: %u, LDoNPrice: %u", item->Size, item->Weight, item->Price, item->LDoNPrice);
|
||||
@@ -5506,9 +5681,9 @@ void command_summonitem(Client *c, const Seperator *sep)
|
||||
std::string cmd_msg = sep->msg;
|
||||
size_t link_open = cmd_msg.find('\x12');
|
||||
size_t link_close = cmd_msg.find_last_of('\x12');
|
||||
if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::legacy::TEXT_LINK_BODY_LENGTH) {
|
||||
if (link_open != link_close && (cmd_msg.length() - link_open) > EQEmu::constants::SayLinkBodySize) {
|
||||
EQEmu::SayLinkBody_Struct link_body;
|
||||
EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::legacy::TEXT_LINK_BODY_LENGTH));
|
||||
EQEmu::saylink::DegenerateLinkBody(link_body, cmd_msg.substr(link_open + 1, EQEmu::constants::SayLinkBodySize));
|
||||
itemid = link_body.item_id;
|
||||
}
|
||||
else if (!sep->IsNumber(1)) {
|
||||
@@ -5617,7 +5792,6 @@ void command_itemsearch(Client *c, const Seperator *sep)
|
||||
const char *search_criteria=sep->argplus[1];
|
||||
|
||||
const EQEmu::ItemData* item = nullptr;
|
||||
std::string item_link;
|
||||
EQEmu::SayLinkEngine linker;
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemData);
|
||||
|
||||
@@ -5626,9 +5800,7 @@ void command_itemsearch(Client *c, const Seperator *sep)
|
||||
if (item) {
|
||||
linker.SetItemData(item);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message(0, "%u: %s", item->ID, item_link.c_str());
|
||||
c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str());
|
||||
}
|
||||
else {
|
||||
c->Message(0, "Item #%s not found", search_criteria);
|
||||
@@ -5651,9 +5823,7 @@ void command_itemsearch(Client *c, const Seperator *sep)
|
||||
if (pdest != nullptr) {
|
||||
linker.SetItemData(item);
|
||||
|
||||
item_link = linker.GenerateLink();
|
||||
|
||||
c->Message(0, "%u: %s", item->ID, item_link.c_str());
|
||||
c->Message(0, "%u: %s", item->ID, linker.GenerateLink().c_str());
|
||||
|
||||
++count;
|
||||
}
|
||||
@@ -6854,6 +7024,90 @@ void command_undye(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void command_ucs(Client *c, const Seperator *sep)
|
||||
{
|
||||
if (!c)
|
||||
return;
|
||||
|
||||
Log(Logs::Detail, Logs::UCS_Server, "Character %s attempting ucs reconnect while ucs server is %savailable",
|
||||
c->GetName(), (zone->IsUCSServerAvailable() ? "" : "un"));
|
||||
|
||||
if (zone->IsUCSServerAvailable()) {
|
||||
EQApplicationPacket* outapp = nullptr;
|
||||
std::string buffer;
|
||||
|
||||
std::string MailKey = database.GetMailKey(c->CharacterID(), true);
|
||||
EQEmu::versions::UCSVersion ConnectionType = EQEmu::versions::ucsUnknown;
|
||||
|
||||
// chat server packet
|
||||
switch (c->ClientVersion()) {
|
||||
case EQEmu::versions::ClientVersion::Titanium:
|
||||
ConnectionType = EQEmu::versions::ucsTitaniumChat;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::SoF:
|
||||
ConnectionType = EQEmu::versions::ucsSoFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::SoD:
|
||||
ConnectionType = EQEmu::versions::ucsSoDCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::UF:
|
||||
ConnectionType = EQEmu::versions::ucsUFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::RoF:
|
||||
ConnectionType = EQEmu::versions::ucsRoFCombined;
|
||||
break;
|
||||
case EQEmu::versions::ClientVersion::RoF2:
|
||||
ConnectionType = EQEmu::versions::ucsRoF2Combined;
|
||||
break;
|
||||
default:
|
||||
ConnectionType = EQEmu::versions::ucsUnknown;
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = StringFormat("%s,%i,%s.%s,%c%s",
|
||||
Config->ChatHost.c_str(),
|
||||
Config->ChatPort,
|
||||
Config->ShortName.c_str(),
|
||||
c->GetName(),
|
||||
ConnectionType,
|
||||
MailKey.c_str()
|
||||
);
|
||||
|
||||
outapp = new EQApplicationPacket(OP_SetChatServer, (buffer.length() + 1));
|
||||
memcpy(outapp->pBuffer, buffer.c_str(), buffer.length());
|
||||
outapp->pBuffer[buffer.length()] = '\0';
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
|
||||
// mail server packet
|
||||
switch (c->ClientVersion()) {
|
||||
case EQEmu::versions::ClientVersion::Titanium:
|
||||
ConnectionType = EQEmu::versions::ucsTitaniumMail;
|
||||
break;
|
||||
default:
|
||||
// retain value from previous switch
|
||||
break;
|
||||
}
|
||||
|
||||
buffer = StringFormat("%s,%i,%s.%s,%c%s",
|
||||
Config->MailHost.c_str(),
|
||||
Config->MailPort,
|
||||
Config->ShortName.c_str(),
|
||||
c->GetName(),
|
||||
ConnectionType,
|
||||
MailKey.c_str()
|
||||
);
|
||||
|
||||
outapp = new EQApplicationPacket(OP_SetChatServer2, (buffer.length() + 1));
|
||||
memcpy(outapp->pBuffer, buffer.c_str(), buffer.length());
|
||||
outapp->pBuffer[buffer.length()] = '\0';
|
||||
|
||||
c->QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
|
||||
void command_undyeme(Client *c, const Seperator *sep)
|
||||
{
|
||||
if(c) {
|
||||
@@ -8416,7 +8670,7 @@ void command_object(Client *c, const Seperator *sep)
|
||||
od.x = c->GetX();
|
||||
od.y = c->GetY();
|
||||
od.z = c->GetZ() - (c->GetSize() * 0.625f);
|
||||
od.heading = c->GetHeading() * 2.0f; // GetHeading() is half of actual. Compensate by doubling.
|
||||
od.heading = c->GetHeading();
|
||||
|
||||
std::string query;
|
||||
if (id) {
|
||||
@@ -8521,11 +8775,9 @@ void command_object(Client *c, const Seperator *sep)
|
||||
|
||||
// Bump player back to avoid getting stuck inside new object
|
||||
|
||||
// GetHeading() returns half of the actual heading, for some reason, so we'll double it here for
|
||||
// computation
|
||||
x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f);
|
||||
y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f);
|
||||
c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2);
|
||||
x2 = 10.0f * sin(c->GetHeading() / 256.0f * 3.14159265f);
|
||||
y2 = 10.0f * cos(c->GetHeading() / 256.0f * 3.14159265f);
|
||||
c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading());
|
||||
|
||||
c->Message(0, "Spawning object with tentative id %u at location (%.1f, %.1f, %.1f heading %.1f). Use "
|
||||
"'#object Save' to save to database when satisfied with placement.",
|
||||
@@ -8843,14 +9095,13 @@ void command_object(Client *c, const Seperator *sep)
|
||||
(c->GetSize() *
|
||||
0.625f); // Compensate for #loc bumping up Z coordinate by 62.5% of character's size.
|
||||
|
||||
o->SetHeading(c->GetHeading() * 2.0f); // Compensate for GetHeading() returning half of actual
|
||||
o->SetHeading(c->GetHeading());
|
||||
|
||||
// Bump player back to avoid getting stuck inside object
|
||||
|
||||
// GetHeading() returns half of the actual heading, for some reason
|
||||
x2 = 10.0f * sin(c->GetHeading() * 2.0f / 256.0f * 3.14159265f);
|
||||
y2 = 10.0f * cos(c->GetHeading() * 2.0f / 256.0f * 3.14159265f);
|
||||
c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading() * 2.0f);
|
||||
x2 = 10.0f * std::sin(c->GetHeading() / 256.0f * 3.14159265f);
|
||||
y2 = 10.0f * std::cos(c->GetHeading() / 256.0f * 3.14159265f);
|
||||
c->MovePC(c->GetX() - x2, c->GetY() - y2, c->GetZ(), c->GetHeading());
|
||||
} // Move to x, y, z [h]
|
||||
else {
|
||||
od.x = atof(sep->arg[3]);
|
||||
@@ -10108,6 +10359,20 @@ void command_shownumhits(Client *c, const Seperator *sep)
|
||||
return;
|
||||
}
|
||||
|
||||
void command_shownpcgloballoot(Client *c, const Seperator *sep)
|
||||
{
|
||||
auto tar = c->GetTarget();
|
||||
|
||||
if (!tar || !tar->IsNPC()) {
|
||||
c->Message(0, "You must target an NPC to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
auto npc = tar->CastToNPC();
|
||||
c->Message(0, "GlobalLoot for %s (%d)", npc->GetName(), npc->GetNPCTypeID());
|
||||
zone->ShowNPCGlobalLoot(c, npc);
|
||||
}
|
||||
|
||||
void command_tune(Client *c, const Seperator *sep)
|
||||
{
|
||||
//Work in progress - Kayen
|
||||
@@ -10481,7 +10746,7 @@ void command_hotfix(Client *c, const Seperator *sep) {
|
||||
}
|
||||
worldserver.SendPacket(&pack);
|
||||
|
||||
c->Message(0, "Hotfix applied");
|
||||
if (c) c->Message(0, "Hotfix applied");
|
||||
});
|
||||
|
||||
t1.detach();
|
||||
@@ -10547,6 +10812,16 @@ void command_reloadperlexportsettings(Client *c, const Seperator *sep)
|
||||
}
|
||||
}
|
||||
|
||||
void command_trapinfo(Client *c, const Seperator *sep)
|
||||
{
|
||||
entity_list.GetTrapInfo(c);
|
||||
}
|
||||
|
||||
void command_reloadtraps(Client *c, const Seperator *sep)
|
||||
{
|
||||
entity_list.UpdateAllTraps(true, true);
|
||||
c->Message(CC_Default, "Traps reloaded for %s.", zone->GetShortName());
|
||||
}
|
||||
|
||||
// All new code added to command.cpp should be BEFORE this comment line. Do no append code to this file below the BOTS code block.
|
||||
#ifdef BOTS
|
||||
|
||||
@@ -211,6 +211,7 @@ void command_profiledump(Client *c, const Seperator *sep);
|
||||
void command_profilereset(Client *c, const Seperator *sep);
|
||||
#endif
|
||||
|
||||
void command_push(Client *c, const Seperator *sep);
|
||||
void command_pvp(Client *c, const Seperator *sep);
|
||||
void command_qglobal(Client *c, const Seperator *sep);
|
||||
void command_qtest(Client *c, const Seperator *sep);
|
||||
@@ -224,10 +225,12 @@ void command_reloadaa(Client *c, const Seperator *sep);
|
||||
void command_reloadallrules(Client *c, const Seperator *sep);
|
||||
void command_reloademote(Client* c, const Seperator *sep);
|
||||
void command_reloadlevelmods(Client *c, const Seperator *sep);
|
||||
void command_reloadmerchants(Client *c, const Seperator *sep);
|
||||
void command_reloadperlexportsettings(Client *c, const Seperator *sep);
|
||||
void command_reloadqst(Client *c, const Seperator *sep);
|
||||
void command_reloadstatic(Client *c, const Seperator *sep);
|
||||
void command_reloadtitles(Client *c, const Seperator *sep);
|
||||
void command_reloadtraps(Client* c, const Seperator *sep);
|
||||
void command_reloadworld(Client *c, const Seperator *sep);
|
||||
void command_reloadworldrules(Client *c, const Seperator *sep);
|
||||
void command_reloadzps(Client *c, const Seperator *sep);
|
||||
@@ -265,10 +268,12 @@ void command_setxp(Client *c, const Seperator *sep);
|
||||
void command_showbonusstats(Client *c, const Seperator *sep);
|
||||
void command_showbuffs(Client *c, const Seperator *sep);
|
||||
void command_shownumhits(Client *c, const Seperator *sep);
|
||||
void command_shownpcgloballoot(Client *c, const Seperator *sep);
|
||||
void command_showpetspell(Client *c, const Seperator *sep);
|
||||
void command_showskills(Client *c, const Seperator *sep);
|
||||
void command_showspellslist(Client *c, const Seperator *sep);
|
||||
void command_showstats(Client *c, const Seperator *sep);
|
||||
void command_showzonegloballoot(Client *c, const Seperator *sep);
|
||||
void command_shutdown(Client *c, const Seperator *sep);
|
||||
void command_size(Client *c, const Seperator *sep);
|
||||
void command_spawn(Client *c, const Seperator *sep);
|
||||
@@ -286,6 +291,7 @@ void command_synctod(Client *c, const Seperator *sep);
|
||||
void command_task(Client *c, const Seperator *sep);
|
||||
void command_tattoo(Client *c, const Seperator *sep);
|
||||
void command_tempname(Client *c, const Seperator *sep);
|
||||
void command_petname(Client *c, const Seperator *sep);
|
||||
void command_testspawn(Client *c, const Seperator *sep);
|
||||
void command_testspawnkill(Client *c, const Seperator *sep);
|
||||
void command_texture(Client *c, const Seperator *sep);
|
||||
@@ -295,7 +301,9 @@ void command_timezone(Client *c, const Seperator *sep);
|
||||
void command_title(Client *c, const Seperator *sep);
|
||||
void command_titlesuffix(Client *c, const Seperator *sep);
|
||||
void command_traindisc(Client *c, const Seperator *sep);
|
||||
void command_trapinfo(Client* c, const Seperator *sep);
|
||||
void command_tune(Client *c, const Seperator *sep);
|
||||
void command_ucs(Client *c, const Seperator *sep);
|
||||
void command_undye(Client *c, const Seperator *sep);
|
||||
void command_undyeme(Client *c, const Seperator *sep);
|
||||
void command_unfreeze(Client *c, const Seperator *sep);
|
||||
|
||||
+8
-1
@@ -194,7 +194,8 @@ enum {
|
||||
CASTING_RESIST_DIFF = 43,
|
||||
COUNTER_AVOID_DAMAGE = 44,
|
||||
PROX_AGGRO = 45,
|
||||
MAX_SPECIAL_ATTACK = 46
|
||||
IMMUNE_RANGED_ATTACKS = 46,
|
||||
MAX_SPECIAL_ATTACK = 47
|
||||
};
|
||||
|
||||
typedef enum { //fear states
|
||||
@@ -550,6 +551,7 @@ struct StatBonuses {
|
||||
int FeignedMinionChance; // SPA 281 base1 = chance, just like normal FD
|
||||
int aura_slots;
|
||||
int trap_slots;
|
||||
bool hunger; // Song of Sustenance -- min caps to 3500
|
||||
};
|
||||
|
||||
typedef struct
|
||||
@@ -603,6 +605,11 @@ enum { //type arguments to DoAnim
|
||||
|
||||
};
|
||||
|
||||
enum {
|
||||
SKILLUP_UNKNOWN = 0,
|
||||
SKILLUP_SUCCESS = 1,
|
||||
SKILLUP_FAILURE = 2
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
petFamiliar, //only listens to /pet get lost
|
||||
|
||||
+4
-4
@@ -1253,20 +1253,20 @@ void Corpse::LootItem(Client *client, const EQApplicationPacket *app)
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemInst);
|
||||
linker.SetItemInst(inst);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
linker.GenerateLink();
|
||||
|
||||
client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, item_link.c_str());
|
||||
client->Message_StringID(MT_LootMessages, LOOTED_MESSAGE, linker.Link().c_str());
|
||||
|
||||
if (!IsPlayerCorpse()) {
|
||||
Group *g = client->GetGroup();
|
||||
if (g != nullptr) {
|
||||
g->GroupMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE,
|
||||
client->GetName(), item_link.c_str());
|
||||
client->GetName(), linker.Link().c_str());
|
||||
} else {
|
||||
Raid *r = client->GetRaid();
|
||||
if (r != nullptr) {
|
||||
r->RaidMessage_StringID(client, MT_LootMessages, OTHER_LOOTED_MESSAGE,
|
||||
client->GetName(), item_link.c_str());
|
||||
client->GetName(), linker.Link().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -611,6 +611,10 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// the client does this check before calling CastSpell, should prevent discs being eaten
|
||||
if (spell.buffdurationformula != 0 && spell.targettype == ST_Self && HasDiscBuff())
|
||||
return false;
|
||||
|
||||
//Check the disc timer
|
||||
pTimerType DiscTimer = pTimerDisciplineReuseStart + spell.EndurTimerIndex;
|
||||
if(!p_timers.Expired(&database, DiscTimer, false)) { // lets not set the reuse timer in case CastSpell fails (or we would have to turn off the timer, but CastSpell will set it as well)
|
||||
|
||||
+399
-394
File diff suppressed because it is too large
Load Diff
+24
-11
@@ -644,7 +644,6 @@ void EntityList::AddCorpse(Corpse *corpse, uint32 in_id)
|
||||
void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
|
||||
{
|
||||
npc->SetID(GetFreeID());
|
||||
npc->SetMerchantProbability((uint8) zone->random.Int(0, 99));
|
||||
|
||||
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
|
||||
|
||||
@@ -1419,10 +1418,10 @@ void EntityList::RemoveFromTargets(Mob *mob, bool RemoveFromXTargets)
|
||||
continue;
|
||||
|
||||
if (RemoveFromXTargets) {
|
||||
if (m->IsClient() && mob->CheckAggro(m))
|
||||
if (m->IsClient() && (mob->CheckAggro(m) || mob->IsOnFeignMemory(m->CastToClient())))
|
||||
m->CastToClient()->RemoveXTarget(mob, false);
|
||||
// FadingMemories calls this function passing the client.
|
||||
else if (mob->IsClient() && m->CheckAggro(mob))
|
||||
else if (mob->IsClient() && (m->CheckAggro(mob) || m->IsOnFeignMemory(mob->CastToClient())))
|
||||
mob->CastToClient()->RemoveXTarget(m, false);
|
||||
}
|
||||
|
||||
@@ -1461,7 +1460,7 @@ void EntityList::RefreshAutoXTargets(Client *c)
|
||||
if (!m || m->GetHP() <= 0)
|
||||
continue;
|
||||
|
||||
if (m->CheckAggro(c) && !c->IsXTarget(m)) {
|
||||
if ((m->CheckAggro(c) || m->IsOnFeignMemory(c)) && !c->IsXTarget(m)) {
|
||||
c->AddAutoXTarget(m, false); // we only call this before a bulk, so lets not send right away
|
||||
break;
|
||||
}
|
||||
@@ -2617,12 +2616,13 @@ void EntityList::RemoveFromHateLists(Mob *mob, bool settoone)
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
if (it->second->CheckAggro(mob)) {
|
||||
if (!settoone)
|
||||
if (!settoone) {
|
||||
it->second->RemoveFromHateList(mob);
|
||||
else
|
||||
if (mob->IsClient())
|
||||
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
|
||||
} else {
|
||||
it->second->SetHateAmountOnEnt(mob, 1);
|
||||
if (mob->IsClient())
|
||||
mob->CastToClient()->RemoveXTarget(it->second, false); // gotta do book keeping
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
@@ -3059,7 +3059,10 @@ void EntityList::ClearAggro(Mob* targ)
|
||||
c->RemoveXTarget(it->second, false);
|
||||
it->second->RemoveFromHateList(targ);
|
||||
}
|
||||
it->second->RemoveFromFeignMemory(targ->CastToClient()); //just in case we feigned
|
||||
if (c && it->second->IsOnFeignMemory(c)) {
|
||||
it->second->RemoveFromFeignMemory(c); //just in case we feigned
|
||||
c->RemoveXTarget(it->second, false);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
@@ -3068,7 +3071,8 @@ void EntityList::ClearFeignAggro(Mob *targ)
|
||||
{
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
if (it->second->CheckAggro(targ)) {
|
||||
// add Feign Memory check because sometimes weird stuff happens
|
||||
if (it->second->CheckAggro(targ) || (targ->IsClient() && it->second->IsOnFeignMemory(targ->CastToClient()))) {
|
||||
if (it->second->GetSpecialAbility(IMMUNE_FEIGN_DEATH)) {
|
||||
++it;
|
||||
continue;
|
||||
@@ -3232,7 +3236,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate)
|
||||
|
||||
for (auto &e : npc_list) {
|
||||
auto &npc = e.second;
|
||||
if (!npc->CheckAggro(target) || npc->IsFeared())
|
||||
if (!npc->CheckAggro(target) || npc->IsFeared() || npc->IsPet())
|
||||
continue;
|
||||
|
||||
if (zone->random.Roll(50)) // witness check -- place holder
|
||||
@@ -4846,3 +4850,12 @@ void EntityList::SendAlternateAdvancementStats() {
|
||||
c.second->SendAlternateAdvancementPoints();
|
||||
}
|
||||
}
|
||||
|
||||
void EntityList::ReloadMerchants() {
|
||||
for (auto it = npc_list.begin();it != npc_list.end(); ++it) {
|
||||
NPC *cur = it->second;
|
||||
if (cur->MerchantType != 0) {
|
||||
zone->LoadNewMerchantData(cur->MerchantType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -244,6 +244,7 @@ public:
|
||||
void AddArea(int id, int type, float min_x, float max_x, float min_y, float max_y, float min_z, float max_z);
|
||||
void RemoveArea(int id);
|
||||
void ClearAreas();
|
||||
void ReloadMerchants();
|
||||
void ProcessProximitySay(const char *Message, Client *c, uint8 language = 0);
|
||||
void SendAATimer(uint32 charid,UseAA_Struct* uaa);
|
||||
Doors *FindDoor(uint8 door_id);
|
||||
@@ -365,7 +366,7 @@ public:
|
||||
//trap stuff
|
||||
Mob* GetTrapTrigger(Trap* trap);
|
||||
void SendAlarm(Trap* trap, Mob* currenttarget, uint8 kos);
|
||||
Trap* FindNearbyTrap(Mob* searcher, float max_dist);
|
||||
Trap* FindNearbyTrap(Mob* searcher, float max_dist, float &curdist, bool detected = false);
|
||||
|
||||
void AddHealAggro(Mob* target, Mob* caster, uint16 hate);
|
||||
Mob* FindDefenseNPC(uint32 npcid);
|
||||
@@ -472,6 +473,10 @@ public:
|
||||
void RefreshClientXTargets(Client *c);
|
||||
void SendAlternateAdvancementStats();
|
||||
|
||||
void GetTrapInfo(Client* client);
|
||||
bool IsTrapGroupSpawned(uint32 trap_id, uint8 group);
|
||||
void UpdateAllTraps(bool respawn, bool repopnow = false);
|
||||
void ClearTrapPointers();
|
||||
protected:
|
||||
friend class Zone;
|
||||
void Depop(bool StartSpawnTimer = false);
|
||||
|
||||
+297
-137
@@ -37,6 +37,49 @@
|
||||
|
||||
extern QueryServ* QServ;
|
||||
|
||||
static uint32 ScaleAAXPBasedOnCurrentAATotal(int earnedAA, uint32 add_aaxp)
|
||||
{
|
||||
float baseModifier = RuleR(AA, ModernAAScalingStartPercent);
|
||||
int aaMinimum = RuleI(AA, ModernAAScalingAAMinimum);
|
||||
int aaLimit = RuleI(AA, ModernAAScalingAALimit);
|
||||
|
||||
// Are we within the scaling window?
|
||||
if (earnedAA >= aaLimit || earnedAA < aaMinimum)
|
||||
{
|
||||
Log(Logs::Detail, Logs::None, "Not within AA scaling window.");
|
||||
|
||||
// At or past the limit. We're done.
|
||||
return add_aaxp;
|
||||
}
|
||||
|
||||
// We're not at the limit yet. How close are we?
|
||||
int remainingAA = aaLimit - earnedAA;
|
||||
|
||||
// We might not always be X - 0
|
||||
int scaleRange = aaLimit - aaMinimum;
|
||||
|
||||
// Normalize and get the effectiveness based on the range and the character's
|
||||
// current spent AA.
|
||||
float normalizedScale = (float)remainingAA / scaleRange;
|
||||
|
||||
// Scale.
|
||||
uint32 totalWithExpMod = add_aaxp * (baseModifier / 100) * normalizedScale;
|
||||
|
||||
// Are we so close to the scale limit that we're earning more XP without scaling? This
|
||||
// will happen when we get very close to the limit. In this case, just grant the unscaled
|
||||
// amount.
|
||||
if (totalWithExpMod < add_aaxp)
|
||||
{
|
||||
return add_aaxp;
|
||||
}
|
||||
|
||||
Log(Logs::Detail,
|
||||
Logs::None,
|
||||
"Total before the modifier %d :: NewTotal %d :: ScaleRange: %d, SpentAA: %d, RemainingAA: %d, normalizedScale: %0.3f",
|
||||
add_aaxp, totalWithExpMod, scaleRange, earnedAA, remainingAA, normalizedScale);
|
||||
|
||||
return totalWithExpMod;
|
||||
}
|
||||
|
||||
static uint32 MaxBankedGroupLeadershipPoints(int Level)
|
||||
{
|
||||
@@ -174,187 +217,302 @@ uint32 Client::GetExperienceForKill(Mob *against)
|
||||
return 0;
|
||||
}
|
||||
|
||||
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
|
||||
float static GetConLevelModifierPercent(uint8 conlevel)
|
||||
{
|
||||
switch (conlevel)
|
||||
{
|
||||
case CON_GREEN:
|
||||
return (float)RuleI(Character, GreenModifier) / 100;
|
||||
break;
|
||||
case CON_LIGHTBLUE:
|
||||
return (float)RuleI(Character, LightBlueModifier) / 100;
|
||||
break;
|
||||
case CON_BLUE:
|
||||
return (float)RuleI(Character, BlueModifier) / 100;
|
||||
break;
|
||||
case CON_WHITE:
|
||||
return (float)RuleI(Character, WhiteModifier) / 100;
|
||||
break;
|
||||
case CON_YELLOW:
|
||||
return (float)RuleI(Character, YellowModifier) / 100;
|
||||
break;
|
||||
case CON_RED:
|
||||
return (float)RuleI(Character, RedModifier) / 100;
|
||||
break;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
this->EVENT_ITEM_ScriptStopReturn();
|
||||
|
||||
uint32 add_exp = in_add_exp;
|
||||
|
||||
if(!resexp && (XPRate != 0))
|
||||
add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
|
||||
|
||||
if (m_epp.perAA<0 || m_epp.perAA>100)
|
||||
m_epp.perAA=0; // stop exploit with sanity check
|
||||
|
||||
uint32 add_aaxp;
|
||||
if(resexp) {
|
||||
void Client::CalculateNormalizedAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
// Functionally this is the same as having the case in the switch, but this is
|
||||
// cleaner to read.
|
||||
if (CON_GRAY == conlevel || resexp)
|
||||
{
|
||||
add_aaxp = 0;
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
// For this, we ignore the provided value of add_aaxp because it doesn't
|
||||
// apply. XP per AA is normalized such that there are X white con kills
|
||||
// per AA.
|
||||
|
||||
uint32 whiteConKillsPerAA = RuleI(AA, NormalizedAANumberOfWhiteConPerAA);
|
||||
uint32 xpPerAA = RuleI(AA, ExpPerPoint);
|
||||
|
||||
float colorModifier = GetConLevelModifierPercent(conlevel);
|
||||
float percentToAAXp = (float)m_epp.perAA / 100;
|
||||
|
||||
// Normalize the amount of AA XP we earned for this kill.
|
||||
add_aaxp = percentToAAXp * (xpPerAA / (whiteConKillsPerAA / colorModifier));
|
||||
}
|
||||
|
||||
void Client::CalculateStandardAAExp(uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
if (!resexp)
|
||||
{
|
||||
//if XP scaling is based on the con of a monster, do that now.
|
||||
if (RuleB(Character, UseXPConScaling))
|
||||
{
|
||||
if (conlevel != 0xFF && !resexp)
|
||||
{
|
||||
add_aaxp *= GetConLevelModifierPercent(conlevel);
|
||||
}
|
||||
}
|
||||
} //end !resexp
|
||||
|
||||
float aatotalmod = 1.0;
|
||||
if (zone->newzone_data.zone_exp_multiplier >= 0) {
|
||||
aatotalmod *= zone->newzone_data.zone_exp_multiplier;
|
||||
}
|
||||
|
||||
// Shouldn't race not affect AA XP?
|
||||
if (RuleB(Character, UseRaceClassExpBonuses))
|
||||
{
|
||||
if (GetBaseRace() == HALFLING) {
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
|
||||
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
|
||||
aatotalmod *= 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
// why wasn't this here? Where should it be?
|
||||
if (zone->IsHotzone())
|
||||
{
|
||||
aatotalmod += RuleR(Zone, HotZoneBonus);
|
||||
}
|
||||
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
if (zone->level_exp_mod[GetLevel()].ExpMod) {
|
||||
add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
|
||||
}
|
||||
}
|
||||
|
||||
add_aaxp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
|
||||
}
|
||||
|
||||
void Client::CalculateLeadershipExp(uint32 &add_exp, uint8 conlevel)
|
||||
{
|
||||
if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED))
|
||||
{
|
||||
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
|
||||
|
||||
if (GetGroup())
|
||||
{
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0)
|
||||
{
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = GetGroup()->GetMentoree();
|
||||
if (GetGroup()->GetMentorPercent() && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel()))
|
||||
{
|
||||
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0)
|
||||
{
|
||||
// possible if you mentor 100% to the other client
|
||||
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Raid *raid = GetRaid();
|
||||
// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
|
||||
if (raid->IsLeader(this))
|
||||
{
|
||||
if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerRaidLeadershipAA) > 0)
|
||||
{
|
||||
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
|
||||
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0)
|
||||
{
|
||||
uint32 group_id = raid->GetGroup(this);
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = raid->GetMentoree(group_id);
|
||||
if (raid->GetMentorPercent(group_id) && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel()))
|
||||
{
|
||||
uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0);
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0)
|
||||
{
|
||||
AddLeadershipEXP(exp, 0);
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Client::CalculateExp(uint32 in_add_exp, uint32 &add_exp, uint32 &add_aaxp, uint8 conlevel, bool resexp)
|
||||
{
|
||||
add_exp = in_add_exp;
|
||||
|
||||
if (!resexp && (XPRate != 0))
|
||||
{
|
||||
add_exp = static_cast<uint32>(in_add_exp * (static_cast<float>(XPRate) / 100.0f));
|
||||
}
|
||||
|
||||
// Make sure it was initialized.
|
||||
add_aaxp = 0;
|
||||
|
||||
if (!resexp)
|
||||
{
|
||||
//figure out how much of this goes to AAs
|
||||
add_aaxp = add_exp * m_epp.perAA / 100;
|
||||
|
||||
//take that amount away from regular exp
|
||||
add_exp -= add_aaxp;
|
||||
|
||||
float totalmod = 1.0;
|
||||
float zemmod = 1.0;
|
||||
|
||||
//get modifiers
|
||||
if(RuleR(Character, ExpMultiplier) >= 0){
|
||||
if (RuleR(Character, ExpMultiplier) >= 0) {
|
||||
totalmod *= RuleR(Character, ExpMultiplier);
|
||||
}
|
||||
|
||||
if(zone->newzone_data.zone_exp_multiplier >= 0){
|
||||
//add the zone exp modifier.
|
||||
if (zone->newzone_data.zone_exp_multiplier >= 0) {
|
||||
zemmod *= zone->newzone_data.zone_exp_multiplier;
|
||||
}
|
||||
|
||||
if(RuleB(Character,UseRaceClassExpBonuses))
|
||||
if (RuleB(Character, UseRaceClassExpBonuses))
|
||||
{
|
||||
if(GetBaseRace() == HALFLING){
|
||||
if (GetBaseRace() == HALFLING) {
|
||||
totalmod *= 1.05;
|
||||
}
|
||||
|
||||
if(GetClass() == ROGUE || GetClass() == WARRIOR){
|
||||
if (GetClass() == ROGUE || GetClass() == WARRIOR) {
|
||||
totalmod *= 1.05;
|
||||
}
|
||||
}
|
||||
|
||||
if(zone->IsHotzone())
|
||||
//add hotzone modifier if one has been set.
|
||||
if (zone->IsHotzone())
|
||||
{
|
||||
totalmod += RuleR(Zone, HotZoneBonus);
|
||||
}
|
||||
|
||||
add_exp = uint32(float(add_exp) * totalmod * zemmod);
|
||||
|
||||
if(RuleB(Character,UseXPConScaling))
|
||||
//if XP scaling is based on the con of a monster, do that now.
|
||||
if (RuleB(Character, UseXPConScaling))
|
||||
{
|
||||
if (conlevel != 0xFF && !resexp) {
|
||||
switch (conlevel)
|
||||
{
|
||||
case CON_GRAY:
|
||||
add_exp = 0;
|
||||
add_aaxp = 0;
|
||||
return;
|
||||
case CON_GREEN:
|
||||
add_exp = add_exp * RuleI(Character, GreenModifier) / 100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, GreenModifier) / 100;
|
||||
break;
|
||||
case CON_LIGHTBLUE:
|
||||
add_exp = add_exp * RuleI(Character, LightBlueModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, LightBlueModifier)/100;
|
||||
break;
|
||||
case CON_BLUE:
|
||||
add_exp = add_exp * RuleI(Character, BlueModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, BlueModifier)/100;
|
||||
break;
|
||||
case CON_WHITE:
|
||||
add_exp = add_exp * RuleI(Character, WhiteModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, WhiteModifier)/100;
|
||||
break;
|
||||
case CON_YELLOW:
|
||||
add_exp = add_exp * RuleI(Character, YellowModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, YellowModifier)/100;
|
||||
break;
|
||||
case CON_RED:
|
||||
add_exp = add_exp * RuleI(Character, RedModifier)/100;
|
||||
add_aaxp = add_aaxp * RuleI(Character, RedModifier)/100;
|
||||
break;
|
||||
}
|
||||
if (conlevel != 0xFF && !resexp)
|
||||
{
|
||||
add_exp = add_exp * GetConLevelModifierPercent(conlevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (IsLeadershipEXPOn() && (conlevel == CON_BLUE || conlevel == CON_WHITE || conlevel == CON_YELLOW || conlevel == CON_RED)) {
|
||||
add_exp = static_cast<uint32>(static_cast<float>(add_exp) * 0.8f);
|
||||
|
||||
if (GetGroup()) {
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = GetGroup()->GetMentoree();
|
||||
if (GetGroup()->GetMentorPercent() && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
|
||||
uint32 mentor_exp = exp * (GetGroup()->GetMentorPercent() / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0); // ends up rounded down
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0) { // possible if you mentor 100% to the other client
|
||||
AddLeadershipEXP(exp, 0); // ends up rounded up if mentored, no idea how live actually does it
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
} else {
|
||||
Raid *raid = GetRaid();
|
||||
// Raid leaders CAN NOT gain group AA XP, other group leaders can though!
|
||||
if (raid->IsLeader(this)) {
|
||||
if (m_pp.raid_leadership_points < MaxBankedRaidLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerRaidLeadershipAA) > 0) {
|
||||
AddLeadershipEXP(0, RAID_EXP_PER_POINT / RuleI(Character, KillsPerRaidLeadershipAA));
|
||||
Message_StringID(MT_Leadership, GAIN_RAID_LEADERSHIP_EXP);
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_RAID_LEADERSHIP_POINTS);
|
||||
}
|
||||
} else {
|
||||
if (m_pp.group_leadership_points < MaxBankedGroupLeadershipPoints(GetLevel())
|
||||
&& RuleI(Character, KillsPerGroupLeadershipAA) > 0) {
|
||||
uint32 group_id = raid->GetGroup(this);
|
||||
uint32 exp = GROUP_EXP_PER_POINT / RuleI(Character, KillsPerGroupLeadershipAA);
|
||||
Client *mentoree = raid->GetMentoree(group_id);
|
||||
if (raid->GetMentorPercent(group_id) && mentoree &&
|
||||
mentoree->GetGroupPoints() < MaxBankedGroupLeadershipPoints(mentoree->GetLevel())) {
|
||||
uint32 mentor_exp = exp * (raid->GetMentorPercent(group_id) / 100.0f);
|
||||
exp -= mentor_exp;
|
||||
mentoree->AddLeadershipEXP(mentor_exp, 0);
|
||||
mentoree->Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
if (exp > 0) {
|
||||
AddLeadershipEXP(exp, 0);
|
||||
Message_StringID(MT_Leadership, GAIN_GROUP_LEADERSHIP_EXP);
|
||||
}
|
||||
} else {
|
||||
Message_StringID(MT_Leadership, MAX_GROUP_LEADERSHIP_POINTS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Calculate any changes to leadership experience.
|
||||
CalculateLeadershipExp(add_exp, conlevel);
|
||||
} //end !resexp
|
||||
|
||||
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){
|
||||
if (RuleB(Zone, LevelBasedEXPMods)) {
|
||||
if (zone->level_exp_mod[GetLevel()].ExpMod) {
|
||||
add_exp *= zone->level_exp_mod[GetLevel()].ExpMod;
|
||||
add_aaxp *= zone->level_exp_mod[GetLevel()].AAExpMod;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 exp = GetEXP() + add_exp;
|
||||
add_exp = GetEXP() + add_exp;
|
||||
}
|
||||
|
||||
uint32 aaexp = (uint32)(RuleR(Character, AAExpMultiplier) * add_aaxp * aatotalmod);
|
||||
void Client::AddEXP(uint32 in_add_exp, uint8 conlevel, bool resexp) {
|
||||
|
||||
this->EVENT_ITEM_ScriptStopReturn();
|
||||
|
||||
uint32 exp = 0;
|
||||
uint32 aaexp = 0;
|
||||
|
||||
if (m_epp.perAA<0 || m_epp.perAA>100)
|
||||
m_epp.perAA=0; // stop exploit with sanity check
|
||||
|
||||
// Calculate regular XP
|
||||
CalculateExp(in_add_exp, exp, aaexp, conlevel, resexp);
|
||||
|
||||
// Calculate regular AA XP
|
||||
if (!RuleB(AA, NormalizedAAEnabled))
|
||||
{
|
||||
CalculateStandardAAExp(aaexp, conlevel, resexp);
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateNormalizedAAExp(aaexp, conlevel, resexp);
|
||||
}
|
||||
|
||||
// Are we also doing linear AA acceleration?
|
||||
if (RuleB(AA, ModernAAScalingEnabled) && aaexp > 0)
|
||||
{
|
||||
aaexp = ScaleAAXPBasedOnCurrentAATotal(GetAAPoints(), aaexp);
|
||||
}
|
||||
|
||||
// Get current AA XP total
|
||||
uint32 had_aaexp = GetAAXP();
|
||||
aaexp += had_aaexp;
|
||||
if(aaexp < had_aaexp)
|
||||
aaexp = had_aaexp; //watch for wrap
|
||||
|
||||
// Add it to the XP we just earned.
|
||||
aaexp += had_aaexp;
|
||||
|
||||
// Make sure our new total (existing + just earned) isn't lower than the
|
||||
// existing total. If it is, we overflowed the bounds of uint32 and wrapped.
|
||||
// Reset to the existing total.
|
||||
if (aaexp < had_aaexp)
|
||||
{
|
||||
aaexp = had_aaexp; //watch for wrap
|
||||
}
|
||||
|
||||
// Now update our character's normal and AA xp
|
||||
SetEXP(exp, aaexp, resexp);
|
||||
}
|
||||
|
||||
@@ -673,6 +831,8 @@ void Client::SetLevel(uint8 set_level, bool command)
|
||||
SetHP(CalcMaxHP()); // Why not, lets give them a free heal
|
||||
}
|
||||
|
||||
if (RuleI(World, PVPMinLevel) > 0 && level >= RuleI(World, PVPMinLevel) && m_pp.pvp == 0) SetPVP(true);
|
||||
|
||||
DoTributeUpdate();
|
||||
SendHPUpdate();
|
||||
SetMana(CalcMaxMana());
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
#if defined(_MSC_VER)
|
||||
#define _USE_MATH_DEFINES
|
||||
#endif
|
||||
#include <cmath>
|
||||
#include "fastmath.h"
|
||||
|
||||
FastMath g_Math;
|
||||
|
||||
// This should match EQ's sin/cos LUTs
|
||||
// Some values didn't match on linux, but they were the "same" float :P
|
||||
FastMath::FastMath()
|
||||
{
|
||||
int ci = 0;
|
||||
int si = 128;
|
||||
float res;
|
||||
do {
|
||||
res = std::cos(static_cast<float>(ci) * M_PI * 2 / 512);
|
||||
lut_cos[ci] = res;
|
||||
if (si == 512)
|
||||
si = 0;
|
||||
lut_sin[si] = res;
|
||||
++ci;
|
||||
++si;
|
||||
} while (ci < 512);
|
||||
|
||||
lut_sin[0] = 0.0f;
|
||||
lut_sin[128] = 1.0f;
|
||||
lut_sin[256] = 0.0f;
|
||||
lut_sin[384] = -1.0f;
|
||||
|
||||
lut_cos[0] = 1.0f;
|
||||
lut_cos[128] = 0.0f;
|
||||
lut_cos[384] = 0.0f;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
#ifndef FASTMATH_H
|
||||
#define FASTMATH_H
|
||||
|
||||
class FastMath
|
||||
{
|
||||
private:
|
||||
float lut_cos[512];
|
||||
float lut_sin[512];
|
||||
|
||||
public:
|
||||
FastMath();
|
||||
|
||||
inline float FastSin(float a) { return lut_sin[static_cast<int>(a) & 0x1ff]; }
|
||||
inline float FastCos(float a) { return lut_cos[static_cast<int>(a) & 0x1ff]; }
|
||||
|
||||
};
|
||||
|
||||
#endif /* !FASTMATH_H */
|
||||
+14
-11
@@ -277,20 +277,23 @@ void Client::GoFish()
|
||||
food_id = database.GetZoneFishing(m_pp.zone_id, fishing_skill, npc_id, npc_chance);
|
||||
|
||||
//check for add NPC
|
||||
if(npc_chance > 0 && npc_id) {
|
||||
if(npc_chance < zone->random.Int(0, 99)) {
|
||||
const NPCType* tmp = database.LoadNPCTypesData(npc_id);
|
||||
if(tmp != nullptr) {
|
||||
auto positionNPC = GetPosition();
|
||||
positionNPC.x = positionNPC.x + 3;
|
||||
auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc_chance > 0 && npc_id) {
|
||||
if (zone->random.Roll(npc_chance)) {
|
||||
const NPCType *tmp = database.LoadNPCTypesData(npc_id);
|
||||
if (tmp != nullptr) {
|
||||
auto positionNPC = GetPosition();
|
||||
positionNPC.x = positionNPC.x + 3;
|
||||
auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
|
||||
npc->AddToHateList(this, 1, 0, false); // no help yelling
|
||||
npc->AddToHateList(this, 1, 0, false); // no help yelling
|
||||
|
||||
entity_list.AddNPC(npc);
|
||||
entity_list.AddNPC(npc);
|
||||
|
||||
Message(MT_Emote, "You fish up a little more than you bargained for...");
|
||||
Message(MT_Emote,
|
||||
"You fish up a little more than you bargained for...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
#include "global_loot_manager.h"
|
||||
#include "npc.h"
|
||||
#include "client.h"
|
||||
|
||||
std::vector<int> GlobalLootManager::GetGlobalLootTables(NPC *mob) const
|
||||
{
|
||||
// we may be able to add a cache here if performance is an issue, but for now
|
||||
// just return NRVO'd vector
|
||||
// The cache would have to be keyed by NPCType and level (for NPCs with Max Level set)
|
||||
std::vector<int> tables;
|
||||
|
||||
for (auto &e : m_entries) {
|
||||
if (e.PassesRules(mob)) {
|
||||
tables.push_back(e.GetLootTableID());
|
||||
}
|
||||
}
|
||||
|
||||
return tables;
|
||||
}
|
||||
|
||||
void GlobalLootManager::ShowZoneGlobalLoot(Client *to) const
|
||||
{
|
||||
for (auto &e : m_entries)
|
||||
to->Message(0, " %s : %d table %d", e.GetDescription().c_str(), e.GetID(), e.GetLootTableID());
|
||||
}
|
||||
|
||||
void GlobalLootManager::ShowNPCGlobalLoot(Client *to, NPC *who) const
|
||||
{
|
||||
for (auto &e : m_entries) {
|
||||
if (e.PassesRules(who))
|
||||
to->Message(0, " %s : %d table %d", e.GetDescription().c_str(), e.GetID(), e.GetLootTableID());
|
||||
}
|
||||
}
|
||||
|
||||
bool GlobalLootEntry::PassesRules(NPC *mob) const
|
||||
{
|
||||
bool bRace = false;
|
||||
bool bPassesRace = false;
|
||||
bool bBodyType = false;
|
||||
bool bPassesBodyType = false;
|
||||
bool bClass = false;
|
||||
bool bPassesClass = false;
|
||||
|
||||
for (auto &r : m_rules) {
|
||||
switch (r.type) {
|
||||
case GlobalLoot::RuleTypes::LevelMin:
|
||||
if (mob->GetLevel() < r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::LevelMax:
|
||||
if (mob->GetLevel() > r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Raid: // value == 0 must not be raid, value != 0 must be raid
|
||||
if (mob->IsRaidTarget() && !r.value)
|
||||
return false;
|
||||
if (!mob->IsRaidTarget() && r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Rare:
|
||||
if (mob->IsRareSpawn() && !r.value)
|
||||
return false;
|
||||
if (!mob->IsRareSpawn() && r.value)
|
||||
return false;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Race: // can have multiple races per rule set
|
||||
bRace = true; // we must pass race
|
||||
if (mob->GetRace() == r.value)
|
||||
bPassesRace = true;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::Class: // can have multiple classes per rule set
|
||||
bClass = true; // we must pass class
|
||||
if (mob->GetClass() == r.value)
|
||||
bPassesClass = true;
|
||||
break;
|
||||
case GlobalLoot::RuleTypes::BodyType: // can have multiple bodytypes per rule set
|
||||
bBodyType = true; // we must pass BodyType
|
||||
if (mob->GetBodyType() == r.value)
|
||||
bPassesBodyType = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (bRace && !bPassesRace)
|
||||
return false;
|
||||
|
||||
if (bClass && !bPassesClass)
|
||||
return false;
|
||||
|
||||
if (bBodyType && !bPassesBodyType)
|
||||
return false;
|
||||
|
||||
// we abort as early as possible if we fail a rule, so if we get here, we passed
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
#ifndef GLOBAL_LOOT_MANAGER_H
|
||||
#define GLOBAL_LOOT_MANAGER_H
|
||||
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
class NPC;
|
||||
class Client;
|
||||
|
||||
namespace GlobalLoot {
|
||||
|
||||
enum class RuleTypes {
|
||||
LevelMin = 0,
|
||||
LevelMax = 1,
|
||||
Race = 2,
|
||||
Class = 3,
|
||||
BodyType = 4,
|
||||
Rare = 5,
|
||||
Raid = 6,
|
||||
Max
|
||||
};
|
||||
|
||||
struct Rule {
|
||||
RuleTypes type;
|
||||
int value;
|
||||
Rule(RuleTypes t, int v) : type(t), value(v) { }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
class GlobalLootEntry {
|
||||
int m_id;
|
||||
int m_loottable_id;
|
||||
std::string m_description;
|
||||
std::vector<GlobalLoot::Rule> m_rules;
|
||||
public:
|
||||
GlobalLootEntry(int id, int loottable, std::string des)
|
||||
: m_id(id), m_loottable_id(loottable), m_description(std::move(des))
|
||||
{ }
|
||||
bool PassesRules(NPC *mob) const;
|
||||
inline int GetLootTableID() const { return m_loottable_id; }
|
||||
inline int GetID() const { return m_id; }
|
||||
inline const std::string &GetDescription() const { return m_description; }
|
||||
inline void SetLootTableID(int in) { m_loottable_id = in; }
|
||||
inline void SetID(int in) { m_id = in; }
|
||||
inline void SetDescription(const std::string &in) { m_description = in; }
|
||||
inline void AddRule(GlobalLoot::RuleTypes rule, int value) { m_rules.emplace_back(rule, value); }
|
||||
};
|
||||
|
||||
class GlobalLootManager {
|
||||
std::vector<GlobalLootEntry> m_entries;
|
||||
|
||||
public:
|
||||
std::vector<int> GetGlobalLootTables(NPC *mob) const;
|
||||
inline void Clear() { m_entries.clear(); }
|
||||
inline void AddEntry(GlobalLootEntry &in) { m_entries.push_back(in); }
|
||||
void ShowZoneGlobalLoot(Client *to) const;
|
||||
void ShowNPCGlobalLoot(Client *to, NPC *who) const;
|
||||
};
|
||||
|
||||
#endif /* !GLOBAL_LOOT_MANAGER_H */
|
||||
@@ -2471,6 +2471,9 @@ void Group::QueueClients(Mob *sender, const EQApplicationPacket *app, bool ack_r
|
||||
if (!members[i])
|
||||
continue;
|
||||
|
||||
if (!members[i]->IsClient())
|
||||
continue;
|
||||
|
||||
if (ignore_sender && members[i] == sender)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ void HateList::SetHateAmountOnEnt(Mob* other, uint32 in_hate, uint32 in_damage)
|
||||
entity->hatelist_damage = in_damage;
|
||||
if (in_hate > 0)
|
||||
entity->stored_hate_amount = in_hate;
|
||||
entity->last_modified = Timer::GetCurrentTime();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,6 +193,7 @@ void HateList::AddEntToHateList(Mob *in_entity, int32 in_hate, int32 in_damage,
|
||||
entity->hatelist_damage += (in_damage >= 0) ? in_damage : 0;
|
||||
entity->stored_hate_amount += in_hate;
|
||||
entity->is_entity_frenzy = in_is_entity_frenzied;
|
||||
entity->last_modified = Timer::GetCurrentTime();
|
||||
}
|
||||
else if (iAddIfNotExist) {
|
||||
entity = new struct_HateList;
|
||||
@@ -199,6 +201,8 @@ void HateList::AddEntToHateList(Mob *in_entity, int32 in_hate, int32 in_damage,
|
||||
entity->hatelist_damage = (in_damage >= 0) ? in_damage : 0;
|
||||
entity->stored_hate_amount = in_hate;
|
||||
entity->is_entity_frenzy = in_is_entity_frenzied;
|
||||
entity->oor_count = 0;
|
||||
entity->last_modified = Timer::GetCurrentTime();
|
||||
list.push_back(entity);
|
||||
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), in_entity, "1", 0);
|
||||
|
||||
@@ -646,3 +650,45 @@ void HateList::SpellCast(Mob *caster, uint32 spell_id, float range, Mob* ae_cent
|
||||
iter++;
|
||||
}
|
||||
}
|
||||
|
||||
void HateList::RemoveStaleEntries(int time_ms, float dist)
|
||||
{
|
||||
auto it = list.begin();
|
||||
|
||||
auto cur_time = Timer::GetCurrentTime();
|
||||
|
||||
auto dist2 = dist * dist;
|
||||
|
||||
while (it != list.end()) {
|
||||
auto m = (*it)->entity_on_hatelist;
|
||||
if (m) {
|
||||
bool remove = false;
|
||||
|
||||
if (cur_time - (*it)->last_modified > time_ms)
|
||||
remove = true;
|
||||
|
||||
if (!remove && DistanceSquaredNoZ(hate_owner->GetPosition(), m->GetPosition()) > dist2) {
|
||||
(*it)->oor_count++;
|
||||
if ((*it)->oor_count == 2)
|
||||
remove = true;
|
||||
} else if ((*it)->oor_count != 0) {
|
||||
(*it)->oor_count = 0;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
parse->EventNPC(EVENT_HATE_LIST, hate_owner->CastToNPC(), m, "0", 0);
|
||||
|
||||
if (m->IsClient()) {
|
||||
m->CastToClient()->DecrementAggroCount();
|
||||
m->CastToClient()->RemoveXTarget(hate_owner, true);
|
||||
}
|
||||
|
||||
delete (*it);
|
||||
it = list.erase(it);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,8 @@ struct struct_HateList
|
||||
int32 hatelist_damage;
|
||||
uint32 stored_hate_amount;
|
||||
bool is_entity_frenzy;
|
||||
int8 oor_count; // count on how long we've been out of range
|
||||
uint32 last_modified; // we need to remove this if it gets higher than 10 mins
|
||||
};
|
||||
|
||||
class HateList
|
||||
@@ -65,6 +67,7 @@ public:
|
||||
void SetHateOwner(Mob *new_hate_owner) { hate_owner = new_hate_owner; }
|
||||
void SpellCast(Mob *caster, uint32 spell_id, float range, Mob *ae_center = nullptr);
|
||||
void WipeHateList();
|
||||
void RemoveStaleEntries(int time_ms, float dist);
|
||||
|
||||
|
||||
protected:
|
||||
|
||||
+17
-6
@@ -169,9 +169,10 @@ bool HealRotation::ClearMemberPool()
|
||||
m_casting_target_poke = false;
|
||||
m_active_heal_target = false;
|
||||
|
||||
ClearTargetPool();
|
||||
if (!ClearTargetPool())
|
||||
Log(Logs::General, Logs::Error, "HealRotation::ClearTargetPool() failed to clear m_target_pool (size: %u)", m_target_pool.size());
|
||||
|
||||
auto clear_list = m_member_pool;
|
||||
auto clear_list = const_cast<const std::list<Bot*>&>(m_member_pool);
|
||||
for (auto member_iter : clear_list)
|
||||
member_iter->LeaveHealRotationMemberPool();
|
||||
|
||||
@@ -183,13 +184,23 @@ bool HealRotation::ClearTargetPool()
|
||||
m_hot_target = nullptr;
|
||||
m_hot_active = false;
|
||||
m_is_active = false;
|
||||
|
||||
auto clear_list = m_target_pool;
|
||||
|
||||
auto clear_list = const_cast<const std::list<Mob*>&>(m_target_pool);
|
||||
for (auto target_iter : clear_list)
|
||||
target_iter->LeaveHealRotationTargetPool();
|
||||
|
||||
m_casting_target_poke = false;
|
||||
bias_targets();
|
||||
//m_casting_target_poke = false;
|
||||
//bias_targets();
|
||||
|
||||
// strange crash point...
|
||||
// bias_targets() should be returning on m_target_pool.empty()
|
||||
// and setting this two properties as below
|
||||
m_casting_target_poke = true;
|
||||
m_active_heal_target = false;
|
||||
// instead, the list retains mob shared_ptrs and
|
||||
// attempts to process them - and crashes program
|
||||
// predominate when adaptive_healing = true
|
||||
// (shared_ptr now has a delayed gc action? this did work before...)
|
||||
|
||||
return m_target_pool.empty();
|
||||
}
|
||||
|
||||
@@ -151,6 +151,7 @@ void Client::SummonHorse(uint16 spell_id) {
|
||||
|
||||
uint16 tmpID = horse->GetID();
|
||||
SetHorseId(tmpID);
|
||||
BuffFadeBySitModifier();
|
||||
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1326,7 +1326,7 @@ int Client::GetItemLinkHash(const EQEmu::ItemInstance* inst) {
|
||||
return hash;
|
||||
}
|
||||
|
||||
// This appears to still be in use... The core of this should be incorporated into class Client::TextLink
|
||||
// This appears to still be in use... The core of this should be incorporated into class EQEmu::SayLinkEngine
|
||||
void Client::SendItemLink(const EQEmu::ItemInstance* inst, bool send_to_all)
|
||||
{
|
||||
/*
|
||||
|
||||
+95
-13
@@ -26,6 +26,7 @@
|
||||
#include "mob.h"
|
||||
#include "npc.h"
|
||||
#include "zonedb.h"
|
||||
#include "global_loot_manager.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <stdlib.h>
|
||||
@@ -37,10 +38,14 @@
|
||||
// Queries the loottable: adds item & coin to the npc
|
||||
void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat) {
|
||||
const LootTable_Struct* lts = nullptr;
|
||||
*copper = 0;
|
||||
*silver = 0;
|
||||
*gold = 0;
|
||||
*plat = 0;
|
||||
// global loot passes nullptr for these
|
||||
bool bGlobal = copper == nullptr && silver == nullptr && gold == nullptr && plat == nullptr;
|
||||
if (!bGlobal) {
|
||||
*copper = 0;
|
||||
*silver = 0;
|
||||
*gold = 0;
|
||||
*plat = 0;
|
||||
}
|
||||
|
||||
lts = database.GetLootTable(loottable_id);
|
||||
if (!lts)
|
||||
@@ -55,17 +60,19 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
|
||||
}
|
||||
|
||||
uint32 cash = 0;
|
||||
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
|
||||
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
|
||||
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
|
||||
if (!bGlobal) {
|
||||
if(max_cash > 0 && lts->avgcoin > 0 && EQEmu::ValueWithin(lts->avgcoin, min_cash, max_cash)) {
|
||||
float upper_chance = (float)(lts->avgcoin - min_cash) / (float)(max_cash - min_cash);
|
||||
float avg_cash_roll = (float)zone->random.Real(0.0, 1.0);
|
||||
|
||||
if(avg_cash_roll < upper_chance) {
|
||||
cash = zone->random.Int(lts->avgcoin, max_cash);
|
||||
if(avg_cash_roll < upper_chance) {
|
||||
cash = zone->random.Int(lts->avgcoin, max_cash);
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, lts->avgcoin);
|
||||
}
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, lts->avgcoin);
|
||||
cash = zone->random.Int(min_cash, max_cash);
|
||||
}
|
||||
} else {
|
||||
cash = zone->random.Int(min_cash, max_cash);
|
||||
}
|
||||
|
||||
if(cash != 0) {
|
||||
@@ -80,6 +87,7 @@ void ZoneDatabase::AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* ite
|
||||
|
||||
*copper = cash;
|
||||
}
|
||||
|
||||
uint32 global_loot_multiplier = RuleI(Zone, GlobalLootMultiplier);
|
||||
|
||||
// Do items
|
||||
@@ -336,7 +344,8 @@ void NPC::AddLootDrop(const EQEmu::ItemData *item2, ItemList* itemlist, int16 ch
|
||||
eslot = EQEmu::textures::weaponPrimary;
|
||||
if (item2->Damage > 0) {
|
||||
SendAddPlayerState(PlayerState::PrimaryWeaponEquipped);
|
||||
SetFacestab(true);
|
||||
if (!RuleB(Combat, ClassicNPCBackstab))
|
||||
SetFacestab(true);
|
||||
}
|
||||
if (item2->IsType2HWeapon())
|
||||
SetTwoHanderEquipped(true);
|
||||
@@ -443,3 +452,76 @@ void NPC::AddLootTable(uint32 ldid) {
|
||||
database.AddLootTableToNPC(this,ldid, &itemlist, &copper, &silver, &gold, &platinum);
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::CheckGlobalLootTables()
|
||||
{
|
||||
auto tables = zone->GetGlobalLootTables(this);
|
||||
|
||||
for (auto &id : tables)
|
||||
database.AddLootTableToNPC(this, id, &itemlist, nullptr, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
void ZoneDatabase::LoadGlobalLoot()
|
||||
{
|
||||
auto query = StringFormat("SELECT id, loottable_id, description, min_level, max_level, rare, raid, race, "
|
||||
"class, bodytype, zone FROM global_loot WHERE enabled = 1");
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success() || results.RowCount() == 0)
|
||||
return;
|
||||
|
||||
// we might need this, lets not keep doing it in a loop
|
||||
auto zoneid = std::to_string(zone->GetZoneID());
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
// checking zone limits
|
||||
if (row[10]) {
|
||||
auto zones = SplitString(row[10], '|');
|
||||
|
||||
auto it = std::find(zones.begin(), zones.end(), zoneid);
|
||||
if (it == zones.end()) // not in here, skip
|
||||
continue;
|
||||
}
|
||||
|
||||
GlobalLootEntry e(atoi(row[0]), atoi(row[1]), row[2] ? row[2] : "");
|
||||
|
||||
auto min_level = atoi(row[3]);
|
||||
if (min_level)
|
||||
e.AddRule(GlobalLoot::RuleTypes::LevelMin, min_level);
|
||||
|
||||
auto max_level = atoi(row[4]);
|
||||
if (max_level)
|
||||
e.AddRule(GlobalLoot::RuleTypes::LevelMax, max_level);
|
||||
|
||||
// null is not used
|
||||
if (row[5])
|
||||
e.AddRule(GlobalLoot::RuleTypes::Rare, atoi(row[5]));
|
||||
|
||||
// null is not used
|
||||
if (row[6])
|
||||
e.AddRule(GlobalLoot::RuleTypes::Raid, atoi(row[6]));
|
||||
|
||||
if (row[7]) {
|
||||
auto races = SplitString(row[7], '|');
|
||||
|
||||
for (auto &r : races)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Race, std::stoi(r));
|
||||
}
|
||||
|
||||
if (row[8]) {
|
||||
auto classes = SplitString(row[8], '|');
|
||||
|
||||
for (auto &c : classes)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Class, std::stoi(c));
|
||||
}
|
||||
|
||||
if (row[9]) {
|
||||
auto bodytypes = SplitString(row[9], '|');
|
||||
|
||||
for (auto &b : bodytypes)
|
||||
e.AddRule(GlobalLoot::RuleTypes::Class, std::stoi(b));
|
||||
}
|
||||
|
||||
zone->AddGlobalLootEntry(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+57
-1
@@ -1440,6 +1440,54 @@ void Lua_Client::FilteredMessage(Mob *sender, uint32 type, int filter, const cha
|
||||
self->FilteredMessage(sender, type, (eqFilterType)filter, message);
|
||||
}
|
||||
|
||||
void Lua_Client::EnableAreaHPRegen(int value)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->EnableAreaHPRegen(value);
|
||||
}
|
||||
|
||||
void Lua_Client::DisableAreaHPRegen()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->DisableAreaHPRegen();
|
||||
}
|
||||
|
||||
void Lua_Client::EnableAreaManaRegen(int value)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->EnableAreaManaRegen(value);
|
||||
}
|
||||
|
||||
void Lua_Client::DisableAreaManaRegen()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->DisableAreaManaRegen();
|
||||
}
|
||||
|
||||
void Lua_Client::EnableAreaEndRegen(int value)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->EnableAreaEndRegen(value);
|
||||
}
|
||||
|
||||
void Lua_Client::DisableAreaEndRegen()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->DisableAreaEndRegen();
|
||||
}
|
||||
|
||||
void Lua_Client::EnableAreaRegens(int value)
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->EnableAreaRegens(value);
|
||||
}
|
||||
|
||||
void Lua_Client::DisableAreaRegens()
|
||||
{
|
||||
Lua_Safe_Call_Void();
|
||||
self->DisableAreaRegens();
|
||||
}
|
||||
|
||||
luabind::scope lua_register_client() {
|
||||
return luabind::class_<Lua_Client, Lua_Mob>("Client")
|
||||
.def(luabind::constructor<>())
|
||||
@@ -1712,7 +1760,15 @@ luabind::scope lua_register_client() {
|
||||
.def("IsDead", &Lua_Client::IsDead)
|
||||
.def("CalcCurrentWeight", &Lua_Client::CalcCurrentWeight)
|
||||
.def("CalcATK", &Lua_Client::CalcATK)
|
||||
.def("FilteredMessage", &Lua_Client::FilteredMessage);
|
||||
.def("FilteredMessage", &Lua_Client::FilteredMessage)
|
||||
.def("EnableAreaHPRegen", &Lua_Client::EnableAreaHPRegen)
|
||||
.def("DisableAreaHPRegen", &Lua_Client::DisableAreaHPRegen)
|
||||
.def("EnableAreaManaRegen", &Lua_Client::EnableAreaManaRegen)
|
||||
.def("DisableAreaManaRegen", &Lua_Client::DisableAreaManaRegen)
|
||||
.def("EnableAreaEndRegen", &Lua_Client::EnableAreaEndRegen)
|
||||
.def("DisableAreaEndRegen", &Lua_Client::DisableAreaEndRegen)
|
||||
.def("EnableAreaRegens", &Lua_Client::EnableAreaRegens)
|
||||
.def("DisableAreaRegens", &Lua_Client::DisableAreaRegens);
|
||||
}
|
||||
|
||||
luabind::scope lua_register_inventory_where() {
|
||||
|
||||
@@ -301,6 +301,14 @@ public:
|
||||
int CalcCurrentWeight();
|
||||
int CalcATK();
|
||||
void FilteredMessage(Mob *sender, uint32 type, int filter, const char* message);
|
||||
void EnableAreaHPRegen(int value);
|
||||
void DisableAreaHPRegen();
|
||||
void EnableAreaManaRegen(int value);
|
||||
void DisableAreaManaRegen();
|
||||
void EnableAreaEndRegen(int value);
|
||||
void DisableAreaEndRegen();
|
||||
void EnableAreaRegens(int value);
|
||||
void DisableAreaRegens();
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -493,6 +493,10 @@ void lua_set_proximity(float min_x, float max_x, float min_y, float max_y, float
|
||||
quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z);
|
||||
}
|
||||
|
||||
void lua_set_proximity(float min_x, float max_x, float min_y, float max_y, float min_z, float max_z, bool say) {
|
||||
quest_manager.set_proximity(min_x, max_x, min_y, max_y, min_z, max_z, say);
|
||||
}
|
||||
|
||||
void lua_clear_proximity() {
|
||||
quest_manager.clear_proximity();
|
||||
}
|
||||
@@ -1464,7 +1468,6 @@ void lua_create_npc(luabind::adl::object table, float x, float y, float z, float
|
||||
LuaCreateNPCParse(healscale, float, 0);
|
||||
LuaCreateNPCParse(no_target_hotkey, bool, false);
|
||||
LuaCreateNPCParse(raid_target, bool, false);
|
||||
LuaCreateNPCParse(probability, uint8, 0);
|
||||
|
||||
NPC* npc = new NPC(npc_type, nullptr, glm::vec4(x, y, z, heading), FlyMode3);
|
||||
npc->GiveNPCTypeData(npc_type);
|
||||
@@ -1582,6 +1585,7 @@ luabind::scope lua_register_general() {
|
||||
luabind::def("respawn", &lua_respawn),
|
||||
luabind::def("set_proximity", (void(*)(float,float,float,float))&lua_set_proximity),
|
||||
luabind::def("set_proximity", (void(*)(float,float,float,float,float,float))&lua_set_proximity),
|
||||
luabind::def("set_proximity", (void(*)(float,float,float,float,float,float,bool))&lua_set_proximity),
|
||||
luabind::def("clear_proximity", &lua_clear_proximity),
|
||||
luabind::def("enable_proximity_say", &lua_enable_proximity_say),
|
||||
luabind::def("disable_proximity_say", &lua_disable_proximity_say),
|
||||
|
||||
+104
-1
@@ -282,6 +282,16 @@ void Lua_Mob::GMMove(double x, double y, double z, double heading, bool send_upd
|
||||
self->GMMove(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z), static_cast<float>(heading), send_update);
|
||||
}
|
||||
|
||||
void Lua_Mob::TryMoveAlong(float distance, float angle) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->TryMoveAlong(distance, angle);
|
||||
}
|
||||
|
||||
void Lua_Mob::TryMoveAlong(float distance, float angle, bool send) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->TryMoveAlong(distance, angle, send);
|
||||
}
|
||||
|
||||
bool Lua_Mob::HasProcs() {
|
||||
Lua_Safe_Call_Bool();
|
||||
return self->HasProcs();
|
||||
@@ -1594,6 +1604,76 @@ void Lua_Mob::SendIllusionPacket(luabind::adl::object illusion) {
|
||||
beard, aa_title, drakkin_heritage, drakkin_tattoo, drakkin_details, size);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeRace(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeRace(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeGender(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeGender(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeTexture(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeTexture(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeHelmTexture(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeHelmTexture(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeHairColor(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeHairColor(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeBeardColor(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeBeardColor(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeEyeColor1(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeEyeColor1(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeEyeColor2(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeEyeColor2(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeHairStyle(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeHairStyle(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeLuclinFace(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeLuclinFace(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeBeard(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeBeard(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeDrakkinHeritage(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeDrakkinHeritage(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeDrakkinTattoo(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeDrakkinTattoo(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::ChangeDrakkinDetails(int in) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->ChangeDrakkinDetails(in);
|
||||
}
|
||||
|
||||
void Lua_Mob::CameraEffect(uint32 duration, uint32 intensity) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->CameraEffect(duration, intensity);
|
||||
@@ -1675,6 +1755,11 @@ void Lua_Mob::DoKnockback(Lua_Mob caster, uint32 pushback, uint32 pushup) {
|
||||
self->DoKnockback(caster, pushback, pushup);
|
||||
}
|
||||
|
||||
void Lua_Mob::AddNimbusEffect(int effect_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddNimbusEffect(effect_id);
|
||||
}
|
||||
|
||||
void Lua_Mob::RemoveNimbusEffect(int effect_id) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->RemoveNimbusEffect(effect_id);
|
||||
@@ -2111,6 +2196,8 @@ luabind::scope lua_register_mob() {
|
||||
.def("GMMove", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::GMMove)
|
||||
.def("GMMove", (void(Lua_Mob::*)(double,double,double,double))&Lua_Mob::GMMove)
|
||||
.def("GMMove", (void(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::GMMove)
|
||||
.def("TryMoveAlong", (void(Lua_Mob::*)(float,float))&Lua_Mob::TryMoveAlong)
|
||||
.def("TryMoveAlong", (void(Lua_Mob::*)(float,float,bool))&Lua_Mob::TryMoveAlong)
|
||||
.def("HasProcs", &Lua_Mob::HasProcs)
|
||||
.def("IsInvisible", (bool(Lua_Mob::*)(void))&Lua_Mob::IsInvisible)
|
||||
.def("IsInvisible", (bool(Lua_Mob::*)(Lua_Mob))&Lua_Mob::IsInvisible)
|
||||
@@ -2338,6 +2425,20 @@ luabind::scope lua_register_mob() {
|
||||
.def("SetRace", (void(Lua_Mob::*)(int))&Lua_Mob::SetRace)
|
||||
.def("SetGender", (void(Lua_Mob::*)(int))&Lua_Mob::SetGender)
|
||||
.def("SendIllusionPacket", (void(Lua_Mob::*)(luabind::adl::object))&Lua_Mob::SendIllusionPacket)
|
||||
.def("ChangeRace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeRace)
|
||||
.def("ChangeGender", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeGender)
|
||||
.def("ChangeTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeTexture)
|
||||
.def("ChangeHelmTexture", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHelmTexture)
|
||||
.def("ChangeHairColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairColor)
|
||||
.def("ChangeBeardColor", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeardColor)
|
||||
.def("ChangeEyeColor1", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor1)
|
||||
.def("ChangeEyeColor2", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeEyeColor2)
|
||||
.def("ChangeHairStyle", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeHairStyle)
|
||||
.def("ChangeLuclinFace", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeLuclinFace)
|
||||
.def("ChangeBeard", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeBeard)
|
||||
.def("ChangeDrakkinHeritage", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinHeritage)
|
||||
.def("ChangeDrakkinTattoo", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinTattoo)
|
||||
.def("ChangeDrakkinDetails", (void(Lua_Mob::*)(int))&Lua_Mob::ChangeDrakkinDetails)
|
||||
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32))&Lua_Mob::CameraEffect)
|
||||
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client))&Lua_Mob::CameraEffect)
|
||||
.def("CameraEffect", (void(Lua_Mob::*)(uint32,uint32,Lua_Client,bool))&Lua_Mob::CameraEffect)
|
||||
@@ -2354,6 +2455,7 @@ luabind::scope lua_register_mob() {
|
||||
.def("SetSlotTint", (void(Lua_Mob::*)(int,int,int,int))&Lua_Mob::SetSlotTint)
|
||||
.def("WearChange", (void(Lua_Mob::*)(int,int,uint32))&Lua_Mob::WearChange)
|
||||
.def("DoKnockback", (void(Lua_Mob::*)(Lua_Mob,uint32,uint32))&Lua_Mob::DoKnockback)
|
||||
.def("AddNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::AddNimbusEffect)
|
||||
.def("RemoveNimbusEffect", (void(Lua_Mob::*)(int))&Lua_Mob::RemoveNimbusEffect)
|
||||
.def("IsFeared", (bool(Lua_Mob::*)(void))&Lua_Mob::IsFeared)
|
||||
.def("IsBlind", (bool(Lua_Mob::*)(void))&Lua_Mob::IsBlind)
|
||||
@@ -2480,7 +2582,8 @@ luabind::scope lua_register_special_abilities() {
|
||||
luabind::value("allow_to_tank", static_cast<int>(ALLOW_TO_TANK)),
|
||||
luabind::value("ignore_root_aggro_rules", static_cast<int>(IGNORE_ROOT_AGGRO_RULES)),
|
||||
luabind::value("casting_resist_diff", static_cast<int>(CASTING_RESIST_DIFF)),
|
||||
luabind::value("counter_avoid_damage", static_cast<int>(COUNTER_AVOID_DAMAGE))
|
||||
luabind::value("counter_avoid_damage", static_cast<int>(COUNTER_AVOID_DAMAGE)),
|
||||
luabind::value("immune_ranged_attacks", static_cast<int>(IMMUNE_RANGED_ATTACKS))
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
@@ -74,6 +74,8 @@ public:
|
||||
void GMMove(double x, double y, double z);
|
||||
void GMMove(double x, double y, double z, double heading);
|
||||
void GMMove(double x, double y, double z, double heading, bool send_update);
|
||||
void TryMoveAlong(float distance, float heading);
|
||||
void TryMoveAlong(float distance, float heading, bool send);
|
||||
bool HasProcs();
|
||||
bool IsInvisible();
|
||||
bool IsInvisible(Lua_Mob other);
|
||||
@@ -302,6 +304,20 @@ public:
|
||||
void SetRace(int in);
|
||||
void SetGender(int in);
|
||||
void SendIllusionPacket(luabind::adl::object illusion);
|
||||
void ChangeRace(int in);
|
||||
void ChangeGender(int in);
|
||||
void ChangeTexture(int in);
|
||||
void ChangeHelmTexture(int in);
|
||||
void ChangeHairColor(int in);
|
||||
void ChangeBeardColor(int in);
|
||||
void ChangeEyeColor1(int in);
|
||||
void ChangeEyeColor2(int in);
|
||||
void ChangeHairStyle(int in);
|
||||
void ChangeLuclinFace(int in);
|
||||
void ChangeBeard(int in);
|
||||
void ChangeDrakkinHeritage(int in);
|
||||
void ChangeDrakkinTattoo(int in);
|
||||
void ChangeDrakkinDetails(int in);
|
||||
void CameraEffect(uint32 duration, uint32 intensity);
|
||||
void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c);
|
||||
void CameraEffect(uint32 duration, uint32 intensity, Lua_Client c, bool global);
|
||||
@@ -321,6 +337,7 @@ public:
|
||||
void SetSlotTint(int material_slot, int red_tint, int green_tint, int blue_tint);
|
||||
void WearChange(int material_slot, int texture, uint32 color);
|
||||
void DoKnockback(Lua_Mob caster, uint32 pushback, uint32 pushup);
|
||||
void AddNimbusEffect(int effect_id);
|
||||
void RemoveNimbusEffect(int effect_id);
|
||||
bool IsRunning();
|
||||
void SetRunning(bool running);
|
||||
|
||||
+7
-13
@@ -420,7 +420,12 @@ void Lua_NPC::ModifyNPCStat(const char *stat, const char *value) {
|
||||
|
||||
void Lua_NPC::AddAISpell(int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust);
|
||||
self->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust, 0, 0);
|
||||
}
|
||||
|
||||
void Lua_NPC::AddAISpell(int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust, int min_hp, int max_hp) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust, min_hp, max_hp);
|
||||
}
|
||||
|
||||
void Lua_NPC::RemoveAISpell(int spell_id) {
|
||||
@@ -488,16 +493,6 @@ void Lua_NPC::MerchantCloseShop() {
|
||||
self->MerchantCloseShop();
|
||||
}
|
||||
|
||||
void Lua_NPC::SetMerchantProbability(uint8 amt) {
|
||||
Lua_Safe_Call_Void();
|
||||
self->SetMerchantProbability(amt);
|
||||
}
|
||||
|
||||
uint8 Lua_NPC::GetMerchantProbability() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetMerchantProbability();
|
||||
}
|
||||
|
||||
int Lua_NPC::GetRawAC() {
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetRawAC();
|
||||
@@ -595,6 +590,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("SetSwarmTarget", (void(Lua_NPC::*)(int))&Lua_NPC::SetSwarmTarget)
|
||||
.def("ModifyNPCStat", (void(Lua_NPC::*)(const char*,const char*))&Lua_NPC::ModifyNPCStat)
|
||||
.def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int))&Lua_NPC::AddAISpell)
|
||||
.def("AddAISpell", (void(Lua_NPC::*)(int,int,int,int,int,int,int,int))&Lua_NPC::AddAISpell)
|
||||
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
|
||||
.def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG)
|
||||
.def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal)
|
||||
@@ -608,8 +604,6 @@ luabind::scope lua_register_npc() {
|
||||
.def("GetScore", (int(Lua_NPC::*)(void))&Lua_NPC::GetScore)
|
||||
.def("MerchantOpenShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantOpenShop)
|
||||
.def("MerchantCloseShop", (void(Lua_NPC::*)(void))&Lua_NPC::MerchantCloseShop)
|
||||
.def("SetMerchantProbability", (void(Lua_NPC::*)(void))&Lua_NPC::SetMerchantProbability)
|
||||
.def("GetMerchantProbability", (uint8(Lua_NPC::*)(void))&Lua_NPC::GetMerchantProbability)
|
||||
.def("GetRawAC", (int(Lua_NPC::*)(void))&Lua_NPC::GetRawAC)
|
||||
.def("GetAvoidanceRating", &Lua_NPC::GetAvoidanceRating);
|
||||
}
|
||||
|
||||
+1
-2
@@ -110,6 +110,7 @@ public:
|
||||
void SetSwarmTarget(int target);
|
||||
void ModifyNPCStat(const char *stat, const char *value);
|
||||
void AddAISpell(int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust);
|
||||
void AddAISpell(int priority, int spell_id, int type, int mana_cost, int recast_delay, int resist_adjust, int min_hp, int max_hp);
|
||||
void RemoveAISpell(int spell_id);
|
||||
void SetSpellFocusDMG(int focus);
|
||||
void SetSpellFocusHeal(int focus);
|
||||
@@ -123,8 +124,6 @@ public:
|
||||
int GetScore();
|
||||
void MerchantOpenShop();
|
||||
void MerchantCloseShop();
|
||||
void SetMerchantProbability(uint8 amt);
|
||||
uint8 GetMerchantProbability();
|
||||
int GetRawAC();
|
||||
int GetAvoidanceRating();
|
||||
};
|
||||
|
||||
@@ -192,6 +192,14 @@ bool Map::CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const {
|
||||
return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
// returns true if a collision happens
|
||||
bool Map::DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const {
|
||||
if(!imp)
|
||||
return false;
|
||||
|
||||
return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, (RmReal *)&outnorm, (RmReal *)&distance);
|
||||
}
|
||||
|
||||
inline bool file_exists(const std::string& name) {
|
||||
std::ifstream f(name.c_str());
|
||||
return f.good();
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
bool LineIntersectsZone(glm::vec3 start, glm::vec3 end, float step, glm::vec3 *result) const;
|
||||
bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const;
|
||||
bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const;
|
||||
bool DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const;
|
||||
|
||||
#ifdef USE_MAP_MMFS
|
||||
bool Load(std::string filename, bool force_mmf_overwrite = false);
|
||||
|
||||
+4
-5
@@ -1154,7 +1154,7 @@ void Merc::CalcRestState() {
|
||||
// The bot must have been out of combat for RuleI(Character, RestRegenTimeToActivate) seconds,
|
||||
// must be sitting down, and must not have any detrimental spells affecting them.
|
||||
//
|
||||
if(!RuleI(Character, RestRegenPercent))
|
||||
if(!RuleB(Character, RestRegenEnabled))
|
||||
return;
|
||||
|
||||
RestRegenHP = RestRegenMana = RestRegenEndurance = 0;
|
||||
@@ -1174,12 +1174,11 @@ void Merc::CalcRestState() {
|
||||
}
|
||||
}
|
||||
|
||||
RestRegenHP = (GetMaxHP() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenHP = 6 * (GetMaxHP() / RuleI(Character, RestRegenHP));
|
||||
|
||||
RestRegenMana = (GetMaxMana() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenMana = 6 * (GetMaxMana() / RuleI(Character, RestRegenMana));
|
||||
|
||||
if(RuleB(Character, RestRegenEndurance))
|
||||
RestRegenEndurance = (GetMaxEndurance() * RuleI(Character, RestRegenPercent) / 100);
|
||||
RestRegenEndurance = 6 * (GetMaxEndurance() / RuleI(Character, RestRegenEnd));
|
||||
}
|
||||
|
||||
bool Merc::HasSkill(EQEmu::skills::SkillType skill_id) const {
|
||||
|
||||
+176
-129
@@ -75,7 +75,6 @@ Mob::Mob(const char* in_name,
|
||||
uint32 in_drakkin_tattoo,
|
||||
uint32 in_drakkin_details,
|
||||
EQEmu::TintProfile in_armor_tint,
|
||||
|
||||
uint8 in_aa_title,
|
||||
uint8 in_see_invis, // see through invis/ivu
|
||||
uint8 in_see_invis_undead,
|
||||
@@ -91,24 +90,24 @@ Mob::Mob(const char* in_name,
|
||||
uint8 in_handtexture,
|
||||
uint8 in_legtexture,
|
||||
uint8 in_feettexture
|
||||
) :
|
||||
) :
|
||||
attack_timer(2000),
|
||||
attack_dw_timer(2000),
|
||||
ranged_timer(2000),
|
||||
tic_timer(6000),
|
||||
mana_timer(2000),
|
||||
spellend_timer(0),
|
||||
rewind_timer(30000), //Timer used for determining amount of time between actual player position updates for /rewind.
|
||||
rewind_timer(30000),
|
||||
bindwound_timer(10000),
|
||||
stunned_timer(0),
|
||||
spun_timer(0),
|
||||
bardsong_timer(6000),
|
||||
gravity_timer(1000),
|
||||
viral_timer(0),
|
||||
m_FearWalkTarget(-999999.0f,-999999.0f,-999999.0f),
|
||||
m_FearWalkTarget(-999999.0f, -999999.0f, -999999.0f),
|
||||
m_TargetLocation(glm::vec3()),
|
||||
m_TargetV(glm::vec3()),
|
||||
flee_timer(FLEE_CHECK_TIMER),
|
||||
flee_timer(FLEE_CHECK_TIMER),
|
||||
m_Position(position),
|
||||
tmHidden(-1),
|
||||
mitigation_ac(0),
|
||||
@@ -116,50 +115,50 @@ Mob::Mob(const char* in_name,
|
||||
fix_z_timer(300),
|
||||
fix_z_timer_engaged(100),
|
||||
attack_anim_timer(1000),
|
||||
position_update_melee_push_timer(1000)
|
||||
position_update_melee_push_timer(500),
|
||||
mHateListCleanup(6000)
|
||||
{
|
||||
targeted = 0;
|
||||
tar_ndx=0;
|
||||
tar_vector=0;
|
||||
tar_ndx = 0;
|
||||
tar_vector = 0;
|
||||
currently_fleeing = false;
|
||||
|
||||
last_z = 0;
|
||||
|
||||
last_major_update_position = m_Position;
|
||||
is_distance_roamer = false;
|
||||
|
||||
AI_Init();
|
||||
SetMoving(false);
|
||||
moved=false;
|
||||
moved = false;
|
||||
m_RewindLocation = glm::vec3();
|
||||
|
||||
_egnode = nullptr;
|
||||
name[0]=0;
|
||||
orig_name[0]=0;
|
||||
clean_name[0]=0;
|
||||
lastname[0]=0;
|
||||
if(in_name) {
|
||||
strn0cpy(name,in_name,64);
|
||||
strn0cpy(orig_name,in_name,64);
|
||||
name[0] = 0;
|
||||
orig_name[0] = 0;
|
||||
clean_name[0] = 0;
|
||||
lastname[0] = 0;
|
||||
if (in_name) {
|
||||
strn0cpy(name, in_name, 64);
|
||||
strn0cpy(orig_name, in_name, 64);
|
||||
}
|
||||
if(in_lastname)
|
||||
strn0cpy(lastname,in_lastname,64);
|
||||
cur_hp = in_cur_hp;
|
||||
max_hp = in_max_hp;
|
||||
base_hp = in_max_hp;
|
||||
gender = in_gender;
|
||||
race = in_race;
|
||||
base_gender = in_gender;
|
||||
base_race = in_race;
|
||||
class_ = in_class;
|
||||
bodytype = in_bodytype;
|
||||
if (in_lastname)
|
||||
strn0cpy(lastname, in_lastname, 64);
|
||||
cur_hp = in_cur_hp;
|
||||
max_hp = in_max_hp;
|
||||
base_hp = in_max_hp;
|
||||
gender = in_gender;
|
||||
race = in_race;
|
||||
base_gender = in_gender;
|
||||
base_race = in_race;
|
||||
class_ = in_class;
|
||||
bodytype = in_bodytype;
|
||||
orig_bodytype = in_bodytype;
|
||||
deity = in_deity;
|
||||
level = in_level;
|
||||
deity = in_deity;
|
||||
level = in_level;
|
||||
orig_level = in_level;
|
||||
npctype_id = in_npctype_id;
|
||||
size = in_size;
|
||||
base_size = size;
|
||||
runspeed = in_runspeed;
|
||||
npctype_id = in_npctype_id;
|
||||
size = in_size;
|
||||
base_size = size;
|
||||
runspeed = in_runspeed;
|
||||
// neotokyo: sanity check
|
||||
if (runspeed < 0 || runspeed > 20)
|
||||
runspeed = 1.25f;
|
||||
@@ -172,7 +171,8 @@ Mob::Mob(const char* in_name,
|
||||
fearspeed = 0.625f;
|
||||
base_fearspeed = 25;
|
||||
// npcs
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
base_walkspeed = base_runspeed * 100 / 265;
|
||||
walkspeed = ((float)base_walkspeed) * 0.025f;
|
||||
base_fearspeed = base_runspeed * 100 / 127;
|
||||
@@ -184,7 +184,7 @@ Mob::Mob(const char* in_name,
|
||||
|
||||
current_speed = base_runspeed;
|
||||
|
||||
m_PlayerState = 0;
|
||||
m_PlayerState = 0;
|
||||
|
||||
|
||||
// sanity check
|
||||
@@ -196,8 +196,8 @@ Mob::Mob(const char* in_name,
|
||||
m_Light.Type[EQEmu::lightsource::LightActive] = m_Light.Type[EQEmu::lightsource::LightInnate];
|
||||
m_Light.Level[EQEmu::lightsource::LightActive] = m_Light.Level[EQEmu::lightsource::LightInnate];
|
||||
|
||||
texture = in_texture;
|
||||
helmtexture = in_helmtexture;
|
||||
texture = in_texture;
|
||||
helmtexture = in_helmtexture;
|
||||
armtexture = in_armtexture;
|
||||
bracertexture = in_bracertexture;
|
||||
handtexture = in_handtexture;
|
||||
@@ -205,21 +205,21 @@ Mob::Mob(const char* in_name,
|
||||
feettexture = in_feettexture;
|
||||
multitexture = (armtexture || bracertexture || handtexture || legtexture || feettexture);
|
||||
|
||||
haircolor = in_haircolor;
|
||||
beardcolor = in_beardcolor;
|
||||
eyecolor1 = in_eyecolor1;
|
||||
eyecolor2 = in_eyecolor2;
|
||||
hairstyle = in_hairstyle;
|
||||
luclinface = in_luclinface;
|
||||
beard = in_beard;
|
||||
drakkin_heritage = in_drakkin_heritage;
|
||||
drakkin_tattoo = in_drakkin_tattoo;
|
||||
drakkin_details = in_drakkin_details;
|
||||
haircolor = in_haircolor;
|
||||
beardcolor = in_beardcolor;
|
||||
eyecolor1 = in_eyecolor1;
|
||||
eyecolor2 = in_eyecolor2;
|
||||
hairstyle = in_hairstyle;
|
||||
luclinface = in_luclinface;
|
||||
beard = in_beard;
|
||||
drakkin_heritage = in_drakkin_heritage;
|
||||
drakkin_tattoo = in_drakkin_tattoo;
|
||||
drakkin_details = in_drakkin_details;
|
||||
attack_speed = 0;
|
||||
attack_delay = 0;
|
||||
slow_mitigation = 0;
|
||||
findable = false;
|
||||
trackable = true;
|
||||
findable = false;
|
||||
trackable = true;
|
||||
has_shieldequiped = false;
|
||||
has_twohandbluntequiped = false;
|
||||
has_twohanderequipped = false;
|
||||
@@ -230,19 +230,19 @@ Mob::Mob(const char* in_name,
|
||||
SpellPowerDistanceMod = 0;
|
||||
last_los_check = false;
|
||||
|
||||
if(in_aa_title>0)
|
||||
aa_title = in_aa_title;
|
||||
if (in_aa_title > 0)
|
||||
aa_title = in_aa_title;
|
||||
else
|
||||
aa_title =0xFF;
|
||||
AC = in_ac;
|
||||
ATK = in_atk;
|
||||
STR = in_str;
|
||||
STA = in_sta;
|
||||
DEX = in_dex;
|
||||
AGI = in_agi;
|
||||
INT = in_int;
|
||||
WIS = in_wis;
|
||||
CHA = in_cha;
|
||||
aa_title = 0xFF;
|
||||
AC = in_ac;
|
||||
ATK = in_atk;
|
||||
STR = in_str;
|
||||
STA = in_sta;
|
||||
DEX = in_dex;
|
||||
AGI = in_agi;
|
||||
INT = in_int;
|
||||
WIS = in_wis;
|
||||
CHA = in_cha;
|
||||
MR = CR = FR = DR = PR = Corrup = 0;
|
||||
|
||||
ExtraHaste = 0;
|
||||
@@ -263,9 +263,10 @@ Mob::Mob(const char* in_name,
|
||||
hidden = false;
|
||||
improved_hidden = false;
|
||||
invulnerable = false;
|
||||
IsFullHP = (cur_hp == max_hp);
|
||||
qglobal=0;
|
||||
IsFullHP = (cur_hp == max_hp);
|
||||
qglobal = 0;
|
||||
spawned = false;
|
||||
rare_spawn = false;
|
||||
|
||||
InitializeBuffSlots();
|
||||
|
||||
@@ -305,7 +306,7 @@ Mob::Mob(const char* in_name,
|
||||
logging_enabled = false;
|
||||
isgrouped = false;
|
||||
israidgrouped = false;
|
||||
|
||||
|
||||
IsHorse = false;
|
||||
|
||||
entity_id_being_looted = 0;
|
||||
@@ -376,13 +377,13 @@ Mob::Mob(const char* in_name,
|
||||
}
|
||||
|
||||
destructibleobject = false;
|
||||
wandertype=0;
|
||||
pausetype=0;
|
||||
wandertype = 0;
|
||||
pausetype = 0;
|
||||
cur_wp = 0;
|
||||
m_CurrentWayPoint = glm::vec4();
|
||||
cur_wp_pause = 0;
|
||||
patrol=0;
|
||||
follow=0;
|
||||
patrol = 0;
|
||||
follow = 0;
|
||||
follow_dist = 100; // Default Distance for Follow
|
||||
no_target_hotkey = false;
|
||||
flee_mode = false;
|
||||
@@ -392,11 +393,12 @@ Mob::Mob(const char* in_name,
|
||||
permarooted = (runspeed > 0) ? false : true;
|
||||
|
||||
movetimercompleted = false;
|
||||
ForcedMovement = 0;
|
||||
roamer = false;
|
||||
rooted = false;
|
||||
charmed = false;
|
||||
has_virus = false;
|
||||
for (i=0; i<MAX_SPELL_TRIGGER*2; i++) {
|
||||
for (i = 0; i < MAX_SPELL_TRIGGER * 2; i++) {
|
||||
viral_spells[i] = 0;
|
||||
}
|
||||
pStandingPetOrder = SPO_Follow;
|
||||
@@ -427,7 +429,7 @@ Mob::Mob(const char* in_name,
|
||||
nimbus_effect3 = 0;
|
||||
m_targetable = true;
|
||||
|
||||
m_TargetRing = glm::vec3();
|
||||
m_TargetRing = glm::vec3();
|
||||
|
||||
flymode = FlyMode3;
|
||||
DistractedFromGrid = false;
|
||||
@@ -436,7 +438,7 @@ Mob::Mob(const char* in_name,
|
||||
m_AllowBeneficial = false;
|
||||
m_DisableMelee = false;
|
||||
for (int i = 0; i < EQEmu::skills::HIGHEST_SKILL + 2; i++) { SkillDmgTaken_Mod[i] = 0; }
|
||||
for (int i = 0; i < HIGHEST_RESIST+2; i++) { Vulnerability_Mod[i] = 0; }
|
||||
for (int i = 0; i < HIGHEST_RESIST + 2; i++) { Vulnerability_Mod[i] = 0; }
|
||||
|
||||
emoteid = 0;
|
||||
endur_upkeep = false;
|
||||
@@ -1100,7 +1102,7 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
strn0cpy(ns->spawn.lastName, lastname, sizeof(ns->spawn.lastName));
|
||||
}
|
||||
|
||||
ns->spawn.heading = FloatToEQ19(m_Position.w);
|
||||
ns->spawn.heading = FloatToEQ12(m_Position.w);
|
||||
ns->spawn.x = FloatToEQ19(m_Position.x);//((int32)x_pos)<<3;
|
||||
ns->spawn.y = FloatToEQ19(m_Position.y);//((int32)y_pos)<<3;
|
||||
ns->spawn.z = FloatToEQ19(m_Position.z);//((int32)z_pos)<<3;
|
||||
@@ -1436,6 +1438,21 @@ void Mob::SendHPUpdate(bool skip_self /*= false*/, bool force_update_all /*= fal
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::StopMoving() {
|
||||
FixZ();
|
||||
SetCurrentSpeed(0);
|
||||
if (moved)
|
||||
moved = false;
|
||||
}
|
||||
|
||||
void Mob::StopMoving(float new_heading) {
|
||||
SetHeading(new_heading);
|
||||
FixZ();
|
||||
SetCurrentSpeed(0);
|
||||
if (moved)
|
||||
moved = false;
|
||||
}
|
||||
|
||||
/* Used for mobs standing still - this does not send a delta */
|
||||
void Mob::SendPosition() {
|
||||
auto app = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
@@ -1446,6 +1463,7 @@ void Mob::SendPosition() {
|
||||
if (DistanceSquared(last_major_update_position, m_Position) >= (100 * 100)) {
|
||||
entity_list.QueueClients(this, app, true, true);
|
||||
last_major_update_position = m_Position;
|
||||
is_distance_roamer = true;
|
||||
}
|
||||
else {
|
||||
entity_list.QueueCloseClients(this, app, true, RuleI(Range, MobPositionUpdates), nullptr, false);
|
||||
@@ -1479,6 +1497,11 @@ void Mob::SendPositionUpdate(uint8 iSendToSelf) {
|
||||
CastToClient()->FastQueuePacket(&app, false);
|
||||
}
|
||||
}
|
||||
else if (DistanceSquared(last_major_update_position, m_Position) >= (100 * 100)) {
|
||||
entity_list.QueueClients(this, app, true, true);
|
||||
last_major_update_position = m_Position;
|
||||
is_distance_roamer = true;
|
||||
}
|
||||
else {
|
||||
entity_list.QueueCloseClients(this, app, (iSendToSelf == 0), RuleI(Range, MobPositionUpdates), nullptr, false);
|
||||
}
|
||||
@@ -1492,12 +1515,12 @@ void Mob::MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct *spu) {
|
||||
spu->x_pos = FloatToEQ19(m_Position.x);
|
||||
spu->y_pos = FloatToEQ19(m_Position.y);
|
||||
spu->z_pos = FloatToEQ19(m_Position.z);
|
||||
spu->delta_x = NewFloatToEQ13(0);
|
||||
spu->delta_y = NewFloatToEQ13(0);
|
||||
spu->delta_z = NewFloatToEQ13(0);
|
||||
spu->heading = FloatToEQ19(m_Position.w);
|
||||
spu->delta_x = FloatToEQ13(0);
|
||||
spu->delta_y = FloatToEQ13(0);
|
||||
spu->delta_z = FloatToEQ13(0);
|
||||
spu->heading = FloatToEQ12(m_Position.w);
|
||||
spu->animation = 0;
|
||||
spu->delta_heading = NewFloatToEQ13(0);
|
||||
spu->delta_heading = FloatToEQ10(0);
|
||||
spu->padding0002 = 0;
|
||||
spu->padding0006 = 7;
|
||||
spu->padding0014 = 0x7f;
|
||||
@@ -1511,10 +1534,10 @@ void Mob::MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu) {
|
||||
spu->x_pos = FloatToEQ19(m_Position.x);
|
||||
spu->y_pos = FloatToEQ19(m_Position.y);
|
||||
spu->z_pos = FloatToEQ19(m_Position.z);
|
||||
spu->delta_x = NewFloatToEQ13(m_Delta.x);
|
||||
spu->delta_y = NewFloatToEQ13(m_Delta.y);
|
||||
spu->delta_z = NewFloatToEQ13(m_Delta.z);
|
||||
spu->heading = FloatToEQ19(m_Position.w);
|
||||
spu->delta_x = FloatToEQ13(m_Delta.x);
|
||||
spu->delta_y = FloatToEQ13(m_Delta.y);
|
||||
spu->delta_z = FloatToEQ13(m_Delta.z);
|
||||
spu->heading = FloatToEQ12(m_Position.w);
|
||||
spu->padding0002 = 0;
|
||||
spu->padding0006 = 7;
|
||||
spu->padding0014 = 0x7f;
|
||||
@@ -1528,7 +1551,7 @@ void Mob::MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu) {
|
||||
else
|
||||
spu->animation = pRunAnimSpeed;//animation;
|
||||
|
||||
spu->delta_heading = NewFloatToEQ13(m_Delta.w);
|
||||
spu->delta_heading = FloatToEQ10(m_Delta.w);
|
||||
}
|
||||
|
||||
void Mob::ShowStats(Client* client)
|
||||
@@ -2414,18 +2437,18 @@ float Mob::MobAngle(Mob *other, float ourx, float oury) const {
|
||||
float mobx = -(other->GetX()); // mob xloc (inverse because eq)
|
||||
float moby = other->GetY(); // mob yloc
|
||||
float heading = other->GetHeading(); // mob heading
|
||||
heading = (heading * 360.0f) / 256.0f; // convert to degrees
|
||||
heading = (heading * 360.0f) / 512.0f; // convert to degrees
|
||||
if (heading < 270)
|
||||
heading += 90;
|
||||
else
|
||||
heading -= 270;
|
||||
|
||||
heading = heading * 3.1415f / 180.0f; // convert to radians
|
||||
vectorx = mobx + (10.0f * cosf(heading)); // create a vector based on heading
|
||||
vectory = moby + (10.0f * sinf(heading)); // of mob length 10
|
||||
vectorx = mobx + (10.0f * std::cos(heading)); // create a vector based on heading
|
||||
vectory = moby + (10.0f * std::sin(heading)); // of mob length 10
|
||||
|
||||
// length of mob to player vector
|
||||
lengthb = (float) sqrtf(((-ourx - mobx) * (-ourx - mobx)) + ((oury - moby) * (oury - moby)));
|
||||
lengthb = (float) std::sqrt(((-ourx - mobx) * (-ourx - mobx)) + ((oury - moby) * (oury - moby)));
|
||||
|
||||
// calculate dot product to get angle
|
||||
// Handle acos domain errors due to floating point rounding errors
|
||||
@@ -2438,7 +2461,7 @@ float Mob::MobAngle(Mob *other, float ourx, float oury) const {
|
||||
else if (dotp < -1)
|
||||
return 180.0f;
|
||||
|
||||
angle = acosf(dotp);
|
||||
angle = std::acos(dotp);
|
||||
angle = angle * 180.0f / 3.1415f;
|
||||
|
||||
return angle;
|
||||
@@ -2607,7 +2630,7 @@ bool Mob::PlotPositionAroundTarget(Mob* target, float &x_dest, float &y_dest, fl
|
||||
look_heading = target->GetHeading();
|
||||
|
||||
// Convert to sony heading to radians
|
||||
look_heading = (look_heading / 256.0f) * 6.283184f;
|
||||
look_heading = (look_heading / 512.0f) * 6.283184f;
|
||||
|
||||
float tempX = 0;
|
||||
float tempY = 0;
|
||||
@@ -2715,20 +2738,10 @@ bool Mob::HateSummon() {
|
||||
if(summon_level == 1) {
|
||||
entity_list.MessageClose(this, true, 500, MT_Say, "%s says,'You will not evade me, %s!' ", GetCleanName(), target->GetCleanName() );
|
||||
|
||||
if (target->IsClient()) {
|
||||
if (target->IsClient())
|
||||
target->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), m_Position.x, m_Position.y, m_Position.z, target->GetHeading(), 0, SummonPC);
|
||||
}
|
||||
else {
|
||||
#ifdef BOTS
|
||||
if(target && target->IsBot()) {
|
||||
// set pre summoning info to return to (to get out of melee range for caster)
|
||||
target->CastToBot()->SetHasBeenSummoned(true);
|
||||
target->CastToBot()->SetPreSummonLocation(glm::vec3(target->GetPosition()));
|
||||
|
||||
}
|
||||
#endif //BOTS
|
||||
else
|
||||
target->GMMove(m_Position.x, m_Position.y, m_Position.z, target->GetHeading());
|
||||
}
|
||||
|
||||
return true;
|
||||
} else if(summon_level == 2) {
|
||||
@@ -3395,13 +3408,18 @@ int Mob::GetHaste()
|
||||
else // 1-25
|
||||
h += itembonuses.haste > 10 ? 10 : itembonuses.haste;
|
||||
|
||||
// 60+ 100, 51-59 85, 1-50 level+25
|
||||
if (level > 59) // 60+
|
||||
cap = RuleI(Character, HasteCap);
|
||||
else if (level > 50) // 51-59
|
||||
cap = 85;
|
||||
else // 1-50
|
||||
cap = level + 25;
|
||||
// mobs are different!
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet())
|
||||
owner = GetOwner();
|
||||
else if (IsNPC() && CastToNPC()->GetSwarmOwner())
|
||||
owner = entity_list.GetMobID(CastToNPC()->GetSwarmOwner());
|
||||
if (owner) {
|
||||
cap = 10 + level;
|
||||
cap += std::max(0, owner->GetLevel() - 39) + std::max(0, owner->GetLevel() - 60);
|
||||
} else {
|
||||
cap = 150;
|
||||
}
|
||||
|
||||
if(h > cap)
|
||||
h = cap;
|
||||
@@ -3437,6 +3455,19 @@ void Mob::SetTarget(Mob* mob) {
|
||||
this->GetTarget()->SendHPUpdate(false, true);
|
||||
}
|
||||
|
||||
// For when we want a Ground Z at a location we are not at yet
|
||||
// Like MoveTo.
|
||||
float Mob::FindDestGroundZ(glm::vec3 dest, float z_offset)
|
||||
{
|
||||
float best_z = BEST_Z_INVALID;
|
||||
if (zone->zonemap != nullptr)
|
||||
{
|
||||
dest.z += z_offset;
|
||||
best_z = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
}
|
||||
return best_z;
|
||||
}
|
||||
|
||||
float Mob::FindGroundZ(float new_x, float new_y, float z_offset)
|
||||
{
|
||||
float ret = BEST_Z_INVALID;
|
||||
@@ -3773,7 +3804,7 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
|
||||
if ((base2 >= 500 && base2 <= 520) && GetHPRatio() < (base2 - 500)*5)
|
||||
use_spell = true;
|
||||
|
||||
else if (base2 = 1004 && GetHPRatio() < 80)
|
||||
else if (base2 == 1004 && GetHPRatio() < 80)
|
||||
use_spell = true;
|
||||
}
|
||||
|
||||
@@ -3781,12 +3812,12 @@ void Mob::TryTriggerOnValueAmount(bool IsHP, bool IsMana, bool IsEndur, bool IsP
|
||||
if ( (base2 = 521 && GetManaRatio() < 20) || (base2 = 523 && GetManaRatio() < 40))
|
||||
use_spell = true;
|
||||
|
||||
else if (base2 = 38311 && GetManaRatio() < 10)
|
||||
else if (base2 == 38311 && GetManaRatio() < 10)
|
||||
use_spell = true;
|
||||
}
|
||||
|
||||
else if (IsEndur){
|
||||
if (base2 = 522 && GetEndurancePercent() < 40){
|
||||
if (base2 == 522 && GetEndurancePercent() < 40){
|
||||
use_spell = true;
|
||||
}
|
||||
}
|
||||
@@ -3947,10 +3978,17 @@ int16 Mob::GetHealRate(uint16 spell_id, Mob* caster) {
|
||||
|
||||
bool Mob::TryFadeEffect(int slot)
|
||||
{
|
||||
if (!buffs[slot].spellid)
|
||||
return false;
|
||||
|
||||
if(IsValidSpell(buffs[slot].spellid))
|
||||
{
|
||||
for(int i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
|
||||
if (!spells[buffs[slot].spellid].effectid[i])
|
||||
continue;
|
||||
|
||||
if (spells[buffs[slot].spellid].effectid[i] == SE_CastOnFadeEffectAlways ||
|
||||
spells[buffs[slot].spellid].effectid[i] == SE_CastOnRuneFadeEffect)
|
||||
{
|
||||
@@ -4637,16 +4675,16 @@ void Mob::DoKnockback(Mob *caster, uint32 pushback, uint32 pushup)
|
||||
spu->x_pos = FloatToEQ19(GetX());
|
||||
spu->y_pos = FloatToEQ19(GetY());
|
||||
spu->z_pos = FloatToEQ19(GetZ());
|
||||
spu->delta_x = NewFloatToEQ13(static_cast<float>(new_x));
|
||||
spu->delta_y = NewFloatToEQ13(static_cast<float>(new_y));
|
||||
spu->delta_z = NewFloatToEQ13(static_cast<float>(pushup));
|
||||
spu->heading = FloatToEQ19(GetHeading());
|
||||
spu->delta_x = FloatToEQ13(static_cast<float>(new_x));
|
||||
spu->delta_y = FloatToEQ13(static_cast<float>(new_y));
|
||||
spu->delta_z = FloatToEQ13(static_cast<float>(pushup));
|
||||
spu->heading = FloatToEQ12(GetHeading());
|
||||
spu->padding0002 =0;
|
||||
spu->padding0006 =7;
|
||||
spu->padding0014 =0x7f;
|
||||
spu->padding0018 =0x5df27;
|
||||
spu->animation = 0;
|
||||
spu->delta_heading = NewFloatToEQ13(0);
|
||||
spu->delta_heading = FloatToEQ10(0);
|
||||
outapp_push->priority = 6;
|
||||
entity_list.QueueClients(this, outapp_push, true);
|
||||
CastToClient()->FastQueuePacket(&outapp_push);
|
||||
@@ -4964,7 +5002,7 @@ void Mob::DoGravityEffect()
|
||||
}
|
||||
|
||||
if(IsClient())
|
||||
this->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), cur_x, cur_y, new_ground, GetHeading()*2); // I know the heading thing is weird(chance of movepc to halve the heading value, too lazy to figure out why atm)
|
||||
this->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), cur_x, cur_y, new_ground, GetHeading());
|
||||
else
|
||||
this->GMMove(cur_x, cur_y, new_ground, GetHeading());
|
||||
}
|
||||
@@ -4992,6 +5030,18 @@ void Mob::SpreadVirus(uint16 spell_id, uint16 casterID)
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::AddNimbusEffect(int effectid)
|
||||
{
|
||||
SetNimbusEffect(effectid);
|
||||
|
||||
auto outapp = new EQApplicationPacket(OP_AddNimbusEffect, sizeof(RemoveNimbusEffect_Struct));
|
||||
auto ane = (RemoveNimbusEffect_Struct *)outapp->pBuffer;
|
||||
ane->spawnid = GetID();
|
||||
ane->nimbus_effect = effectid;
|
||||
entity_list.QueueClients(this, outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Mob::RemoveNimbusEffect(int effectid)
|
||||
{
|
||||
if (effectid == nimbus_effect1)
|
||||
@@ -5589,8 +5639,7 @@ bool Mob::IsFacingMob(Mob *other)
|
||||
if (!other)
|
||||
return false;
|
||||
float angle = HeadingAngleToMob(other);
|
||||
// what the client uses appears to be 2x our internal heading
|
||||
float heading = GetHeading() * 2.0f;
|
||||
float heading = GetHeading();
|
||||
|
||||
if (angle > 472.0 && heading < 40.0)
|
||||
angle = heading;
|
||||
@@ -5604,15 +5653,13 @@ bool Mob::IsFacingMob(Mob *other)
|
||||
}
|
||||
|
||||
// All numbers derived from the client
|
||||
float Mob::HeadingAngleToMob(Mob *other)
|
||||
float Mob::HeadingAngleToMob(float other_x, float other_y)
|
||||
{
|
||||
float mob_x = other->GetX();
|
||||
float mob_y = other->GetY();
|
||||
float this_x = GetX();
|
||||
float this_y = GetY();
|
||||
|
||||
float y_diff = std::abs(this_y - mob_y);
|
||||
float x_diff = std::abs(this_x - mob_x);
|
||||
float y_diff = std::abs(this_y - other_y);
|
||||
float x_diff = std::abs(this_x - other_x);
|
||||
if (y_diff < 0.0000009999999974752427)
|
||||
y_diff = 0.0000009999999974752427;
|
||||
|
||||
@@ -5620,13 +5667,13 @@ float Mob::HeadingAngleToMob(Mob *other)
|
||||
|
||||
// return the right thing based on relative quadrant
|
||||
// I'm sure this could be improved for readability, but whatever
|
||||
if (this_y >= mob_y) {
|
||||
if (mob_x >= this_x)
|
||||
if (this_y >= other_y) {
|
||||
if (other_x >= this_x)
|
||||
return (90.0f - angle + 90.0f) * 511.5f * 0.0027777778f;
|
||||
if (mob_x <= this_x)
|
||||
if (other_x <= this_x)
|
||||
return (angle + 180.0f) * 511.5f * 0.0027777778f;
|
||||
}
|
||||
if (this_y > mob_y || mob_x > this_x)
|
||||
if (this_y > other_y || other_x > this_x)
|
||||
return angle * 511.5f * 0.0027777778f;
|
||||
else
|
||||
return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f;
|
||||
|
||||
+52
-8
@@ -50,6 +50,8 @@ struct AuraRecord;
|
||||
struct NewSpawn_Struct;
|
||||
struct PlayerPositionUpdateServer_Struct;
|
||||
|
||||
const int COLLISION_BOX_SIZE = 8;
|
||||
|
||||
namespace EQEmu
|
||||
{
|
||||
struct ItemData;
|
||||
@@ -162,6 +164,8 @@ public:
|
||||
inline virtual bool IsMob() const { return true; }
|
||||
inline virtual bool InZone() const { return true; }
|
||||
|
||||
bool is_distance_roamer;
|
||||
|
||||
//Somewhat sorted: needs documenting!
|
||||
|
||||
//Attack
|
||||
@@ -175,7 +179,8 @@ public:
|
||||
inline bool InFrontMob(Mob *other = 0, float ourx = 0.0f, float oury = 0.0f) const
|
||||
{ return (!other || other == this) ? true : MobAngle(other, ourx, oury) < 56.0f; }
|
||||
bool IsFacingMob(Mob *other); // kind of does the same as InFrontMob, but derived from client
|
||||
float HeadingAngleToMob(Mob *other); // to keep consistent with client generated messages
|
||||
float HeadingAngleToMob(Mob *other) { return HeadingAngleToMob(other->GetX(), other->GetY()); }
|
||||
float HeadingAngleToMob(float other_x, float other_y); // to keep consistent with client generated messages
|
||||
virtual void RangedAttack(Mob* other) { }
|
||||
virtual void ThrowingAttack(Mob* other) { }
|
||||
// 13 = Primary (default), 14 = secondary
|
||||
@@ -199,6 +204,7 @@ public:
|
||||
void ApplyMeleeDamageMods(uint16 skill, int &damage, Mob * defender = nullptr, ExtraAttackOptions *opts = nullptr);
|
||||
int ACSum();
|
||||
int offense(EQEmu::skills::SkillType skill);
|
||||
int GetBestMeleeSkill();
|
||||
void CalcAC() { mitigation_ac = ACSum(); }
|
||||
int GetACSoftcap();
|
||||
double GetSoftcapReturns();
|
||||
@@ -278,6 +284,7 @@ public:
|
||||
float ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use_resist_override = false,
|
||||
int resist_override = 0, bool CharismaCheck = false, bool CharmTick = false, bool IsRoot = false,
|
||||
int level_override = -1);
|
||||
int GetResist(uint8 resist_type);
|
||||
int ResistPhysical(int level_diff, uint8 caster_level);
|
||||
int ResistElementalWeaponDmg(const EQEmu::ItemInstance *item);
|
||||
int CheckBaneDamage(const EQEmu::ItemInstance *item);
|
||||
@@ -336,6 +343,7 @@ public:
|
||||
void BuffFadeDetrimentalByCaster(Mob *caster);
|
||||
void BuffFadeBySitModifier();
|
||||
bool IsAffectedByBuff(uint16 spell_id);
|
||||
bool IsAffectedByBuffByGlobalGroup(GlobalGroup group);
|
||||
void BuffModifyDurationBySpellID(uint16 spell_id, int32 newDuration);
|
||||
int AddBuff(Mob *caster, const uint16 spell_id, int duration = 0, int32 level_override = -1);
|
||||
int CanBuffStack(uint16 spellid, uint8 caster_level, bool iFailIfOverwrite = false);
|
||||
@@ -348,6 +356,7 @@ public:
|
||||
virtual int GetMaxSongSlots() const { return 0; }
|
||||
virtual int GetMaxDiscSlots() const { return 0; }
|
||||
virtual int GetMaxTotalSlots() const { return 0; }
|
||||
bool HasDiscBuff();
|
||||
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
|
||||
virtual uint32 GetLastBuffSlot(bool disc, bool song);
|
||||
virtual void InitializeBuffSlots() { buffs = nullptr; current_buff_count = 0; }
|
||||
@@ -376,6 +385,7 @@ public:
|
||||
inline virtual uint32 GetNimbusEffect1() const { return nimbus_effect1; }
|
||||
inline virtual uint32 GetNimbusEffect2() const { return nimbus_effect2; }
|
||||
inline virtual uint32 GetNimbusEffect3() const { return nimbus_effect3; }
|
||||
void AddNimbusEffect(int effectid);
|
||||
void RemoveNimbusEffect(int effectid);
|
||||
inline const glm::vec3& GetTargetRingLocation() const { return m_TargetRing; }
|
||||
inline float GetTargetRingX() const { return m_TargetRing.x; }
|
||||
@@ -432,6 +442,20 @@ public:
|
||||
inline uint8 GetDrakkinHeritage() const { return drakkin_heritage; }
|
||||
inline uint8 GetDrakkinTattoo() const { return drakkin_tattoo; }
|
||||
inline uint8 GetDrakkinDetails() const { return drakkin_details; }
|
||||
inline void ChangeRace(uint16 in) { race = in; }
|
||||
inline void ChangeGender(uint8 in) { gender = in;}
|
||||
inline void ChangeTexture(uint8 in) { texture = in; }
|
||||
inline void ChangeHelmTexture(uint8 in) { helmtexture = in; }
|
||||
inline void ChangeHairColor(uint8 in) { haircolor = in; }
|
||||
inline void ChangeBeardColor(uint8 in) { beardcolor = in; }
|
||||
inline void ChangeEyeColor1(uint8 in) { eyecolor1 = in; }
|
||||
inline void ChangeEyeColor2(uint8 in) { eyecolor2 = in; }
|
||||
inline void ChangeHairStyle(uint8 in) { hairstyle = in; }
|
||||
inline void ChangeLuclinFace(uint8 in) { luclinface = in; }
|
||||
inline void ChangeBeard(uint8 in) { beard = in; }
|
||||
inline void ChangeDrakkinHeritage(uint8 in) { drakkin_heritage = in; }
|
||||
inline void ChangeDrakkinTattoo(uint8 in) { drakkin_tattoo = in; }
|
||||
inline void ChangeDrakkinDetails(uint8 in) { drakkin_details = in; }
|
||||
inline uint32 GetArmorTint(uint8 i) const { return armor_tint.Slot[(i < EQEmu::textures::materialCount) ? i : 0].Color; }
|
||||
inline uint8 GetClass() const { return class_; }
|
||||
inline uint8 GetLevel() const { return level; }
|
||||
@@ -548,12 +572,20 @@ public:
|
||||
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
|
||||
void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu);
|
||||
void SendPosition();
|
||||
void StopMoving();
|
||||
void StopMoving(float new_heading);
|
||||
void SetSpawned() { spawned = true; };
|
||||
bool Spawned() { return spawned; };
|
||||
virtual bool ShouldISpawnFor(Client *c) { return true; }
|
||||
void SetFlyMode(uint8 flymode);
|
||||
inline void Teleport(glm::vec3 NewPosition) { m_Position.x = NewPosition.x; m_Position.y = NewPosition.y;
|
||||
m_Position.z = NewPosition.z; };
|
||||
void TryMoveAlong(float distance, float angle, bool send = true);
|
||||
void ProcessForcedMovement();
|
||||
inline void IncDeltaX(float in) { m_Delta.x += in; }
|
||||
inline void IncDeltaY(float in) { m_Delta.y += in; }
|
||||
inline void IncDeltaZ(float in) { m_Delta.z += in; }
|
||||
inline void SetForcedMovement(int in) { ForcedMovement = in; }
|
||||
|
||||
//AI
|
||||
static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel);
|
||||
@@ -586,10 +618,12 @@ public:
|
||||
void AddFeignMemory(Client* attacker);
|
||||
void RemoveFromFeignMemory(Client* attacker);
|
||||
void ClearFeignMemory();
|
||||
bool IsOnFeignMemory(Client *attacker) const;
|
||||
void PrintHateListToClient(Client *who) { hate_list.PrintHateListToClient(who); }
|
||||
std::list<struct_HateList*>& GetHateList() { return hate_list.GetHateList(); }
|
||||
bool CheckLosFN(Mob* other);
|
||||
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
|
||||
static bool CheckLosFN(glm::vec3 posWatcher, float sizeWatcher, glm::vec3 posTarget, float sizeTarget);
|
||||
inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); }
|
||||
inline const uint32 LastChange() const { return pLastChange; }
|
||||
inline void SetLastLosState(bool value) { last_los_check = value; }
|
||||
@@ -664,6 +698,8 @@ public:
|
||||
void SetFollowDistance(uint32 dist) { follow_dist = dist; }
|
||||
uint32 GetFollowID() const { return follow; }
|
||||
uint32 GetFollowDistance() const { return follow_dist; }
|
||||
inline bool IsRareSpawn() const { return rare_spawn; }
|
||||
inline void SetRareSpawn(bool in) { rare_spawn = in; }
|
||||
|
||||
virtual void Message(uint32 type, const char* message, ...) { }
|
||||
virtual void Message_StringID(uint32 type, uint32 string_id, uint32 distance = 0) { }
|
||||
@@ -943,14 +979,16 @@ public:
|
||||
inline bool IsBlind() { return spellbonuses.IsBlind; }
|
||||
|
||||
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) { return HeadingAngleToMob(in_x, in_y); }
|
||||
virtual bool CalculateNewPosition(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);
|
||||
void SendToFixZ(float new_x, float new_y, float new_z);
|
||||
void FixZ();
|
||||
float GetModelOffset() const;
|
||||
float GetZOffset() const;
|
||||
void FixZ(int32 z_find_offset = 5);
|
||||
float GetFixedZ(glm::vec3 position, int32 z_find_offset = 5);
|
||||
|
||||
void NPCSpecialAttacks(const char* parse, int permtag, bool reset = true, bool remove = false);
|
||||
inline uint32 DontHealMeBefore() const { return pDontHealMeBefore; }
|
||||
inline uint32 DontBuffMeBefore() const { return pDontBuffMeBefore; }
|
||||
@@ -1104,8 +1142,6 @@ public:
|
||||
int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item);
|
||||
int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr);
|
||||
|
||||
float last_z;
|
||||
|
||||
// Bots HealRotation methods
|
||||
#ifdef BOTS
|
||||
bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); }
|
||||
@@ -1127,7 +1163,7 @@ protected:
|
||||
int _GetWalkSpeed() const;
|
||||
int _GetRunSpeed() const;
|
||||
int _GetFearSpeed() const;
|
||||
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed);
|
||||
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ = true, bool calcHeading = true);
|
||||
|
||||
virtual bool AI_EngagedCastCheck() { return(false); }
|
||||
virtual bool AI_PursueCastCheck() { return(false); }
|
||||
@@ -1200,6 +1236,7 @@ protected:
|
||||
uint32 follow;
|
||||
uint32 follow_dist;
|
||||
bool no_target_hotkey;
|
||||
bool rare_spawn;
|
||||
|
||||
uint32 m_PlayerState;
|
||||
uint32 GetPlayerState() { return m_PlayerState; }
|
||||
@@ -1221,7 +1258,8 @@ protected:
|
||||
uint32 npctype_id;
|
||||
glm::vec4 m_Position;
|
||||
/* Used to determine when an NPC has traversed so many units - to send a zone wide pos update */
|
||||
glm::vec4 last_major_update_position;
|
||||
glm::vec4 last_major_update_position;
|
||||
|
||||
int animation; // this is really what MQ2 calls SpeedRun just packed like (int)(SpeedRun * 40.0f)
|
||||
float base_size;
|
||||
float size;
|
||||
@@ -1263,6 +1301,7 @@ protected:
|
||||
virtual int16 GetFocusEffect(focusType type, uint16 spell_id) { return 0; }
|
||||
void CalculateNewFearpoint();
|
||||
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
float FindDestGroundZ(glm::vec3 dest, float z_offset=0.0);
|
||||
glm::vec3 UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
glm::vec3 HandleStuckPath(const glm::vec3 &To, const glm::vec3 &From);
|
||||
|
||||
@@ -1282,6 +1321,9 @@ protected:
|
||||
char lastname[64];
|
||||
|
||||
glm::vec4 m_Delta;
|
||||
// just locs around them to double check, if we do expand collision this should be cached on movement
|
||||
// ideally we should use real models, but this should be quick and work mostly
|
||||
glm::vec4 m_CollisionBox[COLLISION_BOX_SIZE];
|
||||
|
||||
EQEmu::LightSourceProfile m_Light;
|
||||
|
||||
@@ -1392,6 +1434,7 @@ protected:
|
||||
std::unique_ptr<Timer> AI_movement_timer;
|
||||
std::unique_ptr<Timer> AI_target_check_timer;
|
||||
bool movetimercompleted;
|
||||
int8 ForcedMovement; // push
|
||||
bool permarooted;
|
||||
std::unique_ptr<Timer> AI_scan_area_timer;
|
||||
std::unique_ptr<Timer> AI_walking_timer;
|
||||
@@ -1409,6 +1452,7 @@ protected:
|
||||
void AddItemFactionBonus(uint32 pFactionID,int32 bonus);
|
||||
int32 GetItemFactionBonus(uint32 pFactionID);
|
||||
void ClearItemFactionBonuses();
|
||||
Timer mHateListCleanup;
|
||||
|
||||
bool flee_mode;
|
||||
Timer flee_timer;
|
||||
|
||||
+337
-186
@@ -29,12 +29,16 @@
|
||||
#include "quest_parser_collection.h"
|
||||
#include "string_ids.h"
|
||||
#include "water_map.h"
|
||||
#include "fastmath.h"
|
||||
|
||||
#include <glm/gtx/projection.hpp>
|
||||
#include <algorithm>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <math.h>
|
||||
|
||||
extern EntityList entity_list;
|
||||
extern FastMath g_Math;
|
||||
|
||||
extern Zone *zone;
|
||||
|
||||
@@ -47,7 +51,7 @@ extern Zone *zone;
|
||||
#endif
|
||||
|
||||
//NOTE: do NOT pass in beneficial and detrimental spell types into the same call here!
|
||||
bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates) {
|
||||
if (!tar)
|
||||
return false;
|
||||
|
||||
@@ -57,7 +61,12 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
if(AI_HasSpells() == false)
|
||||
return false;
|
||||
|
||||
if (iChance < 100) {
|
||||
// Rooted mobs were just standing around when tar out of range.
|
||||
// Any sane mob would cast if they can.
|
||||
bool cast_only_option = (IsRooted() && !CombatRange(tar));
|
||||
|
||||
// innates are always attempted
|
||||
if (!cast_only_option && iChance < 100 && !bInnates) {
|
||||
if (zone->random.Int(0, 100) >= iChance)
|
||||
return false;
|
||||
}
|
||||
@@ -80,6 +89,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
//return false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if ((AIspells[i].priority == 0 && !bInnates) || (AIspells[i].priority != 0 && bInnates)) {
|
||||
// so "innate" spells are special and spammed a bit
|
||||
// we define an innate spell as a spell with priority 0
|
||||
continue;
|
||||
}
|
||||
|
||||
if (AIspells[i].min_hp != 0 && GetIntHPRatio() < AIspells[i].min_hp)
|
||||
continue;
|
||||
|
||||
if (AIspells[i].max_hp != 0 && GetIntHPRatio() > AIspells[i].max_hp)
|
||||
continue;
|
||||
|
||||
if (iSpellTypes & AIspells[i].type) {
|
||||
// manacost has special values, -1 is no mana cost, -2 is instant cast (no mana)
|
||||
int32 mana_cost = AIspells[i].manacost;
|
||||
@@ -87,15 +109,19 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
mana_cost = spells[AIspells[i].spellid].mana;
|
||||
else if (mana_cost == -2)
|
||||
mana_cost = 0;
|
||||
// this is ugly -- ignore distance for hatelist spells, looks like the client is only checking distance for some targettypes in CastSpell,
|
||||
// should probably match that eventually. This should be good enough for now I guess ....
|
||||
if (
|
||||
((
|
||||
(spells[AIspells[i].spellid].targettype==ST_AECaster || spells[AIspells[i].spellid].targettype==ST_AEBard)
|
||||
&& dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange
|
||||
) ||
|
||||
dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range
|
||||
)
|
||||
(
|
||||
(spells[AIspells[i].spellid].targettype == ST_HateList || spells[AIspells[i].spellid].targettype == ST_AETargetHateList) ||
|
||||
(
|
||||
(spells[AIspells[i].spellid].targettype==ST_AECaster || spells[AIspells[i].spellid].targettype==ST_AEBard)
|
||||
&& dist2 <= spells[AIspells[i].spellid].aoerange*spells[AIspells[i].spellid].aoerange
|
||||
) ||
|
||||
dist2 <= spells[AIspells[i].spellid].range*spells[AIspells[i].spellid].range
|
||||
)
|
||||
&& (mana_cost <= GetMana() || GetMana() == GetMaxMana())
|
||||
&& (AIspells[i].time_cancast + (zone->random.Int(0, 4) * 1000)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
|
||||
&& (AIspells[i].time_cancast + (zone->random.Int(0, 4) * 500)) <= Timer::GetCurrentTime() //break up the spelling casting over a period of time.
|
||||
) {
|
||||
|
||||
#if MobAI_DEBUG_Spells >= 21
|
||||
@@ -123,7 +149,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
case SpellType_Root: {
|
||||
Mob *rootee = GetHateRandom();
|
||||
if (rootee && !rootee->IsRooted() && zone->random.Roll(50)
|
||||
if (rootee && !rootee->IsRooted() && !rootee->IsFeared() && (bInnates || zone->random.Roll(50))
|
||||
&& rootee->DontRootMeBefore() < Timer::GetCurrentTime()
|
||||
&& rootee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
@@ -162,7 +188,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
|
||||
case SpellType_InCombatBuff: {
|
||||
if(zone->random.Roll(50))
|
||||
if(bInnates || zone->random.Roll(50))
|
||||
{
|
||||
AIDoSpellCast(i, tar, mana_cost);
|
||||
return true;
|
||||
@@ -181,7 +207,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
case SpellType_Slow:
|
||||
case SpellType_Debuff: {
|
||||
Mob * debuffee = GetHateRandom();
|
||||
if (debuffee && manaR >= 10 && zone->random.Roll(70) &&
|
||||
if (debuffee && manaR >= 10 && (bInnates || zone->random.Roll(70)) &&
|
||||
debuffee->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0) {
|
||||
if (!checked_los) {
|
||||
if (!CheckLosFN(debuffee))
|
||||
@@ -195,8 +221,8 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
case SpellType_Nuke: {
|
||||
if (
|
||||
manaR >= 10 && zone->random.Roll(70)
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
manaR >= 10 && (bInnates || (zone->random.Roll(70)
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), false) >= 0)) // saying it's a nuke here, AI shouldn't care too much if overwriting
|
||||
) {
|
||||
if(!checked_los) {
|
||||
if(!CheckLosFN(tar))
|
||||
@@ -209,7 +235,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
break;
|
||||
}
|
||||
case SpellType_Dispel: {
|
||||
if(zone->random.Roll(15))
|
||||
if(bInnates || zone->random.Roll(15))
|
||||
{
|
||||
if(!checked_los) {
|
||||
if(!CheckLosFN(tar))
|
||||
@@ -225,7 +251,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
break;
|
||||
}
|
||||
case SpellType_Mez: {
|
||||
if(zone->random.Roll(20))
|
||||
if(bInnates || zone->random.Roll(20))
|
||||
{
|
||||
Mob * mezTar = nullptr;
|
||||
mezTar = entity_list.GetTargetForMez(this);
|
||||
@@ -241,7 +267,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
case SpellType_Charm:
|
||||
{
|
||||
if(!IsPet() && zone->random.Roll(20))
|
||||
if(!IsPet() && (bInnates || zone->random.Roll(20)))
|
||||
{
|
||||
Mob * chrmTar = GetHateRandom();
|
||||
if(chrmTar && chrmTar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0)
|
||||
@@ -255,7 +281,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
|
||||
case SpellType_Pet: {
|
||||
//keep mobs from recasting pets when they have them.
|
||||
if (!IsPet() && !GetPetID() && zone->random.Roll(25)) {
|
||||
if (!IsPet() && !GetPetID() && (bInnates || zone->random.Roll(25))) {
|
||||
AIDoSpellCast(i, tar, mana_cost);
|
||||
return true;
|
||||
}
|
||||
@@ -263,7 +289,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
case SpellType_Lifetap: {
|
||||
if (GetHPRatio() <= 95
|
||||
&& zone->random.Roll(50)
|
||||
&& (bInnates || zone->random.Roll(50))
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
if(!checked_los) {
|
||||
@@ -279,7 +305,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
case SpellType_Snare: {
|
||||
if (
|
||||
!tar->IsRooted()
|
||||
&& zone->random.Roll(50)
|
||||
&& (bInnates || zone->random.Roll(50))
|
||||
&& tar->DontSnareMeBefore() < Timer::GetCurrentTime()
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
@@ -297,7 +323,7 @@ bool NPC::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
|
||||
}
|
||||
case SpellType_DOT: {
|
||||
if (
|
||||
zone->random.Roll(60)
|
||||
(bInnates || zone->random.Roll(60))
|
||||
&& tar->DontDotMeBefore() < Timer::GetCurrentTime()
|
||||
&& tar->CanBuffStack(AIspells[i].spellid, GetLevel(), true) >= 0
|
||||
) {
|
||||
@@ -498,8 +524,8 @@ void NPC::AI_Start(uint32 iMoveDelay) {
|
||||
AIautocastspell_timer = std::unique_ptr<Timer>(new Timer(1000));
|
||||
AIautocastspell_timer->Disable();
|
||||
} else {
|
||||
AIautocastspell_timer = std::unique_ptr<Timer>(new Timer(750));
|
||||
AIautocastspell_timer->Start(RandomTimer(0, 15000), false);
|
||||
AIautocastspell_timer = std::unique_ptr<Timer>(new Timer(500));
|
||||
AIautocastspell_timer->Start(RandomTimer(0, 300), false);
|
||||
}
|
||||
|
||||
if (NPCTypedata) {
|
||||
@@ -892,29 +918,26 @@ void Client::AI_Process()
|
||||
}
|
||||
}
|
||||
|
||||
if(IsPet())
|
||||
{
|
||||
Mob* owner = GetOwner();
|
||||
if(owner == nullptr)
|
||||
if (IsPet()) {
|
||||
Mob *owner = GetOwner();
|
||||
if (owner == nullptr)
|
||||
return;
|
||||
|
||||
float dist = DistanceSquared(m_Position, owner->GetPosition());
|
||||
if (dist >= 400)
|
||||
{
|
||||
if(AI_movement_timer->Check())
|
||||
{
|
||||
int nspeed = (dist >= 5625 ? GetRunspeed() : GetWalkspeed());
|
||||
if (dist >= 202500) { // >= 450 distance
|
||||
Teleport(static_cast<glm::vec3>(owner->GetPosition()));
|
||||
SendPositionUpdate(); // this shouldn't happen a lot (and hard to make it) so lets not rate limit
|
||||
} else if (dist >= 400) { // >=20
|
||||
if (AI_movement_timer->Check()) {
|
||||
int nspeed = (dist >= 1225 ? GetRunspeed() : GetWalkspeed()); // >= 35
|
||||
animation = nspeed;
|
||||
nspeed *= 2;
|
||||
SetCurrentSpeed(nspeed);
|
||||
|
||||
CalculateNewPosition(owner->GetX(), owner->GetY(), owner->GetZ(), nspeed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
} else {
|
||||
if (moved) {
|
||||
SetCurrentSpeed(0);
|
||||
moved = false;
|
||||
}
|
||||
@@ -923,6 +946,91 @@ void Client::AI_Process()
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::ProcessForcedMovement()
|
||||
{
|
||||
// we are being pushed, we will hijack this movement timer
|
||||
// this also needs to be done before casting to have a chance to interrupt
|
||||
// this flag won't be set if the mob can't be pushed (rooted etc)
|
||||
if (AI_movement_timer->Check()) {
|
||||
bool bPassed = true;
|
||||
glm::vec3 normal;
|
||||
|
||||
// no zone map = fucked
|
||||
if (zone->HasMap()) {
|
||||
// in front
|
||||
m_CollisionBox[0].x = m_Position.x + 3.0f * g_Math.FastSin(0.0f);
|
||||
m_CollisionBox[0].y = m_Position.y + 3.0f * g_Math.FastCos(0.0f);
|
||||
m_CollisionBox[0].z = m_Position.z;
|
||||
|
||||
// 45 right front
|
||||
m_CollisionBox[1].x = m_Position.x + 3.0f * g_Math.FastSin(64.0f);
|
||||
m_CollisionBox[1].y = m_Position.y + 3.0f * g_Math.FastCos(64.0f);
|
||||
m_CollisionBox[1].z = m_Position.z;
|
||||
|
||||
// to right
|
||||
m_CollisionBox[2].x = m_Position.x + 3.0f * g_Math.FastSin(128.0f);
|
||||
m_CollisionBox[2].y = m_Position.y + 3.0f * g_Math.FastCos(128.0f);
|
||||
m_CollisionBox[2].z = m_Position.z;
|
||||
|
||||
// 45 right back
|
||||
m_CollisionBox[3].x = m_Position.x + 3.0f * g_Math.FastSin(192.0f);
|
||||
m_CollisionBox[3].y = m_Position.y + 3.0f * g_Math.FastCos(192.0f);
|
||||
m_CollisionBox[3].z = m_Position.z;
|
||||
|
||||
// behind
|
||||
m_CollisionBox[4].x = m_Position.x + 3.0f * g_Math.FastSin(256.0f);
|
||||
m_CollisionBox[4].y = m_Position.y + 3.0f * g_Math.FastCos(256.0f);
|
||||
m_CollisionBox[4].z = m_Position.z;
|
||||
|
||||
// 45 left back
|
||||
m_CollisionBox[5].x = m_Position.x + 3.0f * g_Math.FastSin(320.0f);
|
||||
m_CollisionBox[5].y = m_Position.y + 3.0f * g_Math.FastCos(320.0f);
|
||||
m_CollisionBox[5].z = m_Position.z;
|
||||
|
||||
// to left
|
||||
m_CollisionBox[6].x = m_Position.x + 3.0f * g_Math.FastSin(384.0f);
|
||||
m_CollisionBox[6].y = m_Position.y + 3.0f * g_Math.FastCos(384.0f);
|
||||
m_CollisionBox[6].z = m_Position.z;
|
||||
|
||||
// 45 left front
|
||||
m_CollisionBox[7].x = m_Position.x + 3.0f * g_Math.FastSin(448.0f);
|
||||
m_CollisionBox[7].y = m_Position.y + 3.0f * g_Math.FastCos(448.0f);
|
||||
m_CollisionBox[7].z = m_Position.z;
|
||||
|
||||
// collision happened, need to move along the wall
|
||||
float distance = 0.0f, shortest = std::numeric_limits<float>::infinity();
|
||||
glm::vec3 tmp_nrm;
|
||||
for (auto &vec : m_CollisionBox) {
|
||||
if (zone->zonemap->DoCollisionCheck(vec, vec + m_Delta, tmp_nrm, distance)) {
|
||||
bPassed = false; // lets try with new projection next pass
|
||||
if (distance < shortest) {
|
||||
normal = tmp_nrm;
|
||||
shortest = distance;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bPassed) {
|
||||
ForcedMovement = 0;
|
||||
Teleport(m_Position + m_Delta);
|
||||
m_Delta = glm::vec4();
|
||||
SendPositionUpdate();
|
||||
pLastChange = Timer::GetCurrentTime();
|
||||
FixZ(); // so we teleport to the ground locally, we want the client to interpolate falling etc
|
||||
} else if (--ForcedMovement) {
|
||||
if (normal.z < -0.15f) // prevent too much wall climbing. ex. OMM's room in anguish
|
||||
normal.z = 0.0f;
|
||||
auto proj = glm::proj(static_cast<glm::vec3>(m_Delta), normal);
|
||||
m_Delta.x -= proj.x;
|
||||
m_Delta.y -= proj.y;
|
||||
m_Delta.z -= proj.z;
|
||||
} else {
|
||||
m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::AI_Process() {
|
||||
if (!IsAIControlled())
|
||||
return;
|
||||
@@ -930,6 +1038,7 @@ void Mob::AI_Process() {
|
||||
if (!(AI_think_timer->Check() || attack_timer.Check(false)))
|
||||
return;
|
||||
|
||||
|
||||
if (IsCasting())
|
||||
return;
|
||||
|
||||
@@ -1013,6 +1122,18 @@ void Mob::AI_Process() {
|
||||
|
||||
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
|
||||
SendAddPlayerState(PlayerState::Aggressive);
|
||||
|
||||
// NPCs will forget people after 10 mins of not interacting with them or out of range
|
||||
// both of these maybe zone specific, hardcoded for now
|
||||
if (mHateListCleanup.Check()) {
|
||||
hate_list.RemoveStaleEntries(600000, 600.0f);
|
||||
if (hate_list.IsHateListEmpty()) {
|
||||
AI_Event_NoLongerEngaged();
|
||||
zone->DelAggroMob();
|
||||
if (IsNPC() && !RuleB(Aggro, AllowTickPulling))
|
||||
ResetAssistCap();
|
||||
}
|
||||
}
|
||||
// we are prevented from getting here if we are blind and don't have a target in range
|
||||
// from above, so no extra blind checks needed
|
||||
if ((IsRooted() && !GetSpecialAbility(IGNORE_ROOT_AGGRO_RULES)) || IsBlind())
|
||||
@@ -1290,7 +1411,7 @@ void Mob::AI_Process() {
|
||||
if (AI_PursueCastCheck()) {
|
||||
//we did something, so do not process movement.
|
||||
}
|
||||
else if (AI_movement_timer->Check())
|
||||
else if (AI_movement_timer->Check() && target)
|
||||
{
|
||||
if (!IsRooted()) {
|
||||
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName());
|
||||
@@ -1383,19 +1504,32 @@ void Mob::AI_Process() {
|
||||
//if(owner->IsClient())
|
||||
// printf("Pet start pos: (%f, %f, %f)\n", GetX(), GetY(), GetZ());
|
||||
|
||||
float dist = DistanceSquared(m_Position, owner->GetPosition());
|
||||
if (dist >= 400)
|
||||
glm::vec4 ownerPos = owner->GetPosition();
|
||||
float dist = DistanceSquared(m_Position, ownerPos);
|
||||
float distz = ownerPos.z - m_Position.z;
|
||||
|
||||
if (dist >= 400 || distz > 100)
|
||||
{
|
||||
int speed = GetWalkspeed();
|
||||
if (dist >= 5625)
|
||||
if (dist >= 1225) // 35
|
||||
speed = GetRunspeed();
|
||||
|
||||
CalculateNewPosition(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
if (dist >= 202500 || distz > 100) // dist >= 450
|
||||
{
|
||||
m_Position = ownerPos;
|
||||
SendPositionUpdate();
|
||||
moved = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
CalculateNewPosition(owner->GetX(), owner->GetY(), owner->GetZ(), speed);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(moved)
|
||||
{
|
||||
this->FixZ();
|
||||
SetCurrentSpeed(0);
|
||||
moved = false;
|
||||
}
|
||||
@@ -1527,15 +1661,20 @@ void NPC::AI_DoMovement() {
|
||||
roambox_movingto_x = zone->random.Real(roambox_min_x+1,roambox_max_x-1);
|
||||
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
|
||||
roambox_movingto_y = zone->random.Real(roambox_min_y+1,roambox_max_y-1);
|
||||
Log(Logs::Detail, Logs::AI,
|
||||
"Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
|
||||
roambox_distance, roambox_min_x, roambox_max_x, roambox_min_y,
|
||||
roambox_max_y, roambox_movingto_x, roambox_movingto_y);
|
||||
}
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
|
||||
roambox_distance, roambox_min_x, roambox_max_x, roambox_min_y, roambox_max_y, roambox_movingto_x, roambox_movingto_y);
|
||||
|
||||
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 5) + GetModelOffset();
|
||||
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 5) + GetZOffset();
|
||||
|
||||
if (!CalculateNewPosition(roambox_movingto_x, roambox_movingto_y, new_z, walksp, true))
|
||||
{
|
||||
this->FixZ(); // FixZ on final arrival point.
|
||||
roambox_movingto_x = roambox_max_x + 1; // force update
|
||||
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
|
||||
SetMoving(false);
|
||||
@@ -1572,18 +1711,22 @@ void NPC::AI_DoMovement() {
|
||||
}
|
||||
|
||||
this->FixZ();
|
||||
|
||||
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 (!AI_walking_timer->Enabled())
|
||||
// No need to move as we are there. Next loop will
|
||||
// take care of normal grids, even at pause 0.
|
||||
// We do need to call and setup a wp if we're cur_wp=-2
|
||||
// as that is where roamer is unset and we don't want
|
||||
// the next trip through to move again based on grid stuff.
|
||||
doMove = false;
|
||||
if (cur_wp == -2) {
|
||||
AI_SetupNextWaypoint();
|
||||
else
|
||||
doMove = false;
|
||||
}
|
||||
|
||||
// wipe feign memory since we reached our first waypoint
|
||||
if(cur_wp == 1)
|
||||
ClearFeignMemory();
|
||||
@@ -1734,6 +1877,12 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
|
||||
|
||||
SetAppearance(eaStanding);
|
||||
|
||||
/*
|
||||
Kick off auto cast timer
|
||||
*/
|
||||
if (this->IsNPC())
|
||||
this->CastToNPC()->AIautocastspell_timer->Start(300, false);
|
||||
|
||||
if (iYellForHelp) {
|
||||
if(IsPet()) {
|
||||
GetOwner()->AI_Event_Engaged(attacker, iYellForHelp);
|
||||
@@ -1839,14 +1988,17 @@ bool NPC::AI_EngagedCastCheck() {
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered. Trying to cast healing spells then maybe offensive spells.");
|
||||
|
||||
// try casting a heal or gate
|
||||
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||
// try casting a heal on nearby
|
||||
if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) {
|
||||
//nobody to heal, try some detrimental spells.
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||
//no spell to cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.engaged_no_sp_recast_min, AISpellVar.engaged_no_sp_recast_max), false);
|
||||
// first try innate (spam) spells
|
||||
if(!AICastSpell(GetTarget(), 0, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root, true)) {
|
||||
// try casting a heal or gate
|
||||
if (!AICastSpell(this, AISpellVar.engaged_beneficial_self_chance, SpellType_Heal | SpellType_Escape | SpellType_InCombatBuff)) {
|
||||
// try casting a heal on nearby
|
||||
if (!entity_list.AICheckCloseBeneficialSpells(this, AISpellVar.engaged_beneficial_other_chance, MobAISpellRange, SpellType_Heal)) {
|
||||
//nobody to heal, try some detrimental spells.
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.engaged_detrimental_chance, SpellType_Nuke | SpellType_Lifetap | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff | SpellType_Charm | SpellType_Root)) {
|
||||
//no spell to cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.engaged_no_sp_recast_min, AISpellVar.engaged_no_sp_recast_max), false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1861,10 +2013,13 @@ bool NPC::AI_PursueCastCheck() {
|
||||
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
|
||||
|
||||
Log(Logs::Detail, Logs::AI, "Engaged (pursuing) autocast check triggered. Trying to cast offensive spells.");
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) {
|
||||
//no spell cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.pursue_no_sp_recast_min, AISpellVar.pursue_no_sp_recast_max), false);
|
||||
} //else, spell casting finishing will reset the timer.
|
||||
// checking innate (spam) spells first
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff, true)) {
|
||||
if(!AICastSpell(GetTarget(), AISpellVar.pursue_detrimental_chance, SpellType_Root | SpellType_Nuke | SpellType_Lifetap | SpellType_Snare | SpellType_DOT | SpellType_Dispel | SpellType_Mez | SpellType_Slow | SpellType_Debuff)) {
|
||||
//no spell cast, try again soon.
|
||||
AIautocastspell_timer->Start(RandomTimer(AISpellVar.pursue_no_sp_recast_min, AISpellVar.pursue_no_sp_recast_max), false);
|
||||
} //else, spell casting finishing will reset the timer.
|
||||
}
|
||||
return(true);
|
||||
}
|
||||
return(false);
|
||||
@@ -2004,15 +2159,34 @@ bool Mob::Rampage(ExtraAttackOptions *opts)
|
||||
if (m_target) {
|
||||
if (m_target == GetTarget())
|
||||
continue;
|
||||
if (CombatRange(m_target)) {
|
||||
if (DistanceSquaredNoZ(GetPosition(), m_target->GetPosition()) <= NPC_RAMPAGE_RANGE2) {
|
||||
ProcessAttackRounds(m_target, opts);
|
||||
index_hit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (RuleB(Combat, RampageHitsTarget) && index_hit < rampage_targets)
|
||||
ProcessAttackRounds(GetTarget(), opts);
|
||||
if (RuleB(Combat, RampageHitsTarget)) {
|
||||
if (index_hit < rampage_targets)
|
||||
ProcessAttackRounds(GetTarget(), opts);
|
||||
} else { // let's do correct behavior here, if they set above rule we can assume they want non-live like behavior
|
||||
if (index_hit < rampage_targets) {
|
||||
// so we go over in reverse order and skip range check
|
||||
// lets do it this way to still support non-live-like >1 rampage targets
|
||||
// likely live is just a fall through of the last valid mob
|
||||
for (auto i = RampageArray.crbegin(); i != RampageArray.crend(); ++i) {
|
||||
if (index_hit >= rampage_targets)
|
||||
break;
|
||||
auto m_target = entity_list.GetMob(*i);
|
||||
if (m_target) {
|
||||
if (m_target == GetTarget())
|
||||
continue;
|
||||
ProcessAttackRounds(m_target, opts);
|
||||
index_hit++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_specialattacks = eSpecialAttacks::None;
|
||||
|
||||
@@ -2307,14 +2481,13 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
return false;
|
||||
}
|
||||
DBnpcspells_Struct* parentlist = database.GetNPCSpells(spell_list->parent_list);
|
||||
uint32 i;
|
||||
#if MobAI_DEBUG_Spells >= 10
|
||||
std::string debug_msg = StringFormat("Loading NPCSpells onto %s: dbspellsid=%u", this->GetName(), iDBSpellsID);
|
||||
if (spell_list) {
|
||||
debug_msg.append(StringFormat(" (found, %u), parentlist=%u", spell_list->numentries, spell_list->parent_list));
|
||||
debug_msg.append(StringFormat(" (found, %u), parentlist=%u", spell_list->entries.size(), spell_list->parent_list));
|
||||
if (spell_list->parent_list) {
|
||||
if (parentlist)
|
||||
debug_msg.append(StringFormat(" (found, %u)", parentlist->numentries));
|
||||
debug_msg.append(StringFormat(" (found, %u)", parentlist->entries.size()));
|
||||
else
|
||||
debug_msg.append(" (not found)");
|
||||
}
|
||||
@@ -2362,14 +2535,11 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
_idle_no_sp_recast_min = parentlist->idle_no_sp_recast_min;
|
||||
_idle_no_sp_recast_max = parentlist->idle_no_sp_recast_max;
|
||||
_idle_beneficial_chance = parentlist->idle_beneficial_chance;
|
||||
for (i=0; i<parentlist->numentries; i++) {
|
||||
if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spellid > 0) {
|
||||
if (!IsSpellInList(spell_list, parentlist->entries[i].spellid))
|
||||
for (auto &e : parentlist->entries) {
|
||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
||||
if (!IsSpellInList(spell_list, e.spellid))
|
||||
{
|
||||
AddSpellToNPCList(parentlist->entries[i].priority,
|
||||
parentlist->entries[i].spellid, parentlist->entries[i].type,
|
||||
parentlist->entries[i].manacost, parentlist->entries[i].recast_delay,
|
||||
parentlist->entries[i].resist_adjust);
|
||||
AddSpellToNPCList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2408,14 +2578,12 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
_idle_beneficial_chance = spell_list->idle_beneficial_chance;
|
||||
}
|
||||
|
||||
for (i=0; i<spell_list->numentries; i++) {
|
||||
if (GetLevel() >= spell_list->entries[i].minlevel && GetLevel() <= spell_list->entries[i].maxlevel && spell_list->entries[i].spellid > 0) {
|
||||
AddSpellToNPCList(spell_list->entries[i].priority,
|
||||
spell_list->entries[i].spellid, spell_list->entries[i].type,
|
||||
spell_list->entries[i].manacost, spell_list->entries[i].recast_delay,
|
||||
spell_list->entries[i].resist_adjust);
|
||||
for (auto &e : spell_list->entries) {
|
||||
if (GetLevel() >= e.minlevel && GetLevel() <= e.maxlevel && e.spellid > 0) {
|
||||
AddSpellToNPCList(e.priority, e.spellid, e.type, e.manacost, e.recast_delay, e.resist_adjust, e.min_hp, e.max_hp);
|
||||
}
|
||||
}
|
||||
|
||||
std::sort(AIspells.begin(), AIspells.end(), [](const AISpells_Struct& a, const AISpells_Struct& b) {
|
||||
return a.priority > b.priority;
|
||||
});
|
||||
@@ -2553,16 +2721,14 @@ bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSp
|
||||
}
|
||||
|
||||
bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) {
|
||||
for (uint32 i=0; i < spell_list->numentries; i++) {
|
||||
if (spell_list->entries[i].spellid == iSpellID)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
auto it = std::find_if(spell_list->entries.begin(), spell_list->entries.end(),
|
||||
[iSpellID](const DBnpcspells_entries_Struct &a) { return a.spellid == iSpellID; });
|
||||
return it != spell_list->entries.end();
|
||||
}
|
||||
|
||||
// adds a spell to the list, taking into account priority and resorting list as needed.
|
||||
void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType,
|
||||
int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust)
|
||||
int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp)
|
||||
{
|
||||
|
||||
if(!IsValidSpell(iSpellID))
|
||||
@@ -2578,12 +2744,14 @@ void NPC::AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType,
|
||||
t.recast_delay = iRecastDelay;
|
||||
t.time_cancast = 0;
|
||||
t.resist_adjust = iResistAdjust;
|
||||
t.min_hp = min_hp;
|
||||
t.max_hp = max_hp;
|
||||
|
||||
AIspells.push_back(t);
|
||||
|
||||
// If we're going from an empty list, we need to start the timer
|
||||
if (AIspells.size() == 1)
|
||||
AIautocastspell_timer->Start(RandomTimer(0, 15000), false);
|
||||
AIautocastspell_timer->Start(RandomTimer(0, 300), false);
|
||||
}
|
||||
|
||||
void NPC::RemoveSpellFromNPCList(int16 spell_id)
|
||||
@@ -2606,131 +2774,114 @@ void NPC::AISpellsList(Client *c)
|
||||
return;
|
||||
|
||||
for (auto it = AIspells.begin(); it != AIspells.end(); ++it)
|
||||
c->Message(0, "%s (%d): Type %d, Priority %d",
|
||||
spells[it->spellid].name, it->spellid, it->type, it->priority);
|
||||
c->Message(0, "%s (%d): Type %d, Priority %d, Recast Delay %d, Resist Adjust %d, Min HP %d, Max HP %d",
|
||||
spells[it->spellid].name, it->spellid, it->type, it->priority, it->recast_delay, it->resist_adjust, it->min_hp, it->max_hp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID)
|
||||
{
|
||||
if (iDBSpellsID == 0)
|
||||
return nullptr;
|
||||
|
||||
if (!npc_spells_cache) {
|
||||
npc_spells_maxid = GetMaxNPCSpellsID();
|
||||
npc_spells_cache = new DBnpcspells_Struct*[npc_spells_maxid+1];
|
||||
npc_spells_loadtried = new bool[npc_spells_maxid+1];
|
||||
for (uint32 i=0; i<=npc_spells_maxid; i++) {
|
||||
npc_spells_cache[i] = nullptr;
|
||||
npc_spells_loadtried[i] = false;
|
||||
}
|
||||
auto it = npc_spells_cache.find(iDBSpellsID);
|
||||
|
||||
if (it != npc_spells_cache.end()) { // it's in the cache, easy =)
|
||||
return &it->second;
|
||||
}
|
||||
|
||||
if (iDBSpellsID > npc_spells_maxid)
|
||||
return nullptr;
|
||||
if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =)
|
||||
return npc_spells_cache[iDBSpellsID];
|
||||
}
|
||||
|
||||
else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spells_loadtried[iDBSpellsID] = true;
|
||||
if (!npc_spells_loadtried.count(iDBSpellsID)) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spells_loadtried.insert(iDBSpellsID);
|
||||
|
||||
std::string query = StringFormat("SELECT id, parent_list, attack_proc, proc_chance, "
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
||||
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
||||
"idle_b_chance FROM npc_spells WHERE id=%d", iDBSpellsID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
"range_proc, rproc_chance, defensive_proc, dproc_chance, "
|
||||
"fail_recast, engaged_no_sp_recast_min, engaged_no_sp_recast_max, "
|
||||
"engaged_b_self_chance, engaged_b_other_chance, engaged_d_chance, "
|
||||
"pursue_no_sp_recast_min, pursue_no_sp_recast_max, "
|
||||
"pursue_d_chance, idle_no_sp_recast_min, idle_no_sp_recast_max, "
|
||||
"idle_b_chance FROM npc_spells WHERE id=%d",
|
||||
iDBSpellsID);
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (results.RowCount() != 1)
|
||||
return nullptr;
|
||||
if (results.RowCount() != 1)
|
||||
return nullptr;
|
||||
|
||||
auto row = results.begin();
|
||||
uint32 tmpparent_list = atoi(row[1]);
|
||||
uint16 tmpattack_proc = atoi(row[2]);
|
||||
uint8 tmpproc_chance = atoi(row[3]);
|
||||
uint16 tmprange_proc = atoi(row[4]);
|
||||
int16 tmprproc_chance = atoi(row[5]);
|
||||
uint16 tmpdefensive_proc = atoi(row[6]);
|
||||
int16 tmpdproc_chance = atoi(row[7]);
|
||||
uint32 tmppfail_recast = atoi(row[8]);
|
||||
uint32 tmpengaged_no_sp_recast_min = atoi(row[9]);
|
||||
uint32 tmpengaged_no_sp_recast_max = atoi(row[10]);
|
||||
uint8 tmpengaged_b_self_chance = atoi(row[11]);
|
||||
uint8 tmpengaged_b_other_chance = atoi(row[12]);
|
||||
uint8 tmpengaged_d_chance = atoi(row[13]);
|
||||
uint32 tmppursue_no_sp_recast_min = atoi(row[14]);
|
||||
uint32 tmppursue_no_sp_recast_max = atoi(row[15]);
|
||||
uint8 tmppursue_d_chance = atoi(row[16]);
|
||||
uint32 tmpidle_no_sp_recast_min = atoi(row[17]);
|
||||
uint32 tmpidle_no_sp_recast_max = atoi(row[18]);
|
||||
uint8 tmpidle_b_chance = atoi(row[19]);
|
||||
auto row = results.begin();
|
||||
DBnpcspells_Struct spell_set;
|
||||
|
||||
spell_set.parent_list = atoi(row[1]);
|
||||
spell_set.attack_proc = atoi(row[2]);
|
||||
spell_set.proc_chance = atoi(row[3]);
|
||||
spell_set.range_proc = atoi(row[4]);
|
||||
spell_set.rproc_chance = atoi(row[5]);
|
||||
spell_set.defensive_proc = atoi(row[6]);
|
||||
spell_set.dproc_chance = atoi(row[7]);
|
||||
spell_set.fail_recast = atoi(row[8]);
|
||||
spell_set.engaged_no_sp_recast_min = atoi(row[9]);
|
||||
spell_set.engaged_no_sp_recast_max = atoi(row[10]);
|
||||
spell_set.engaged_beneficial_self_chance = atoi(row[11]);
|
||||
spell_set.engaged_beneficial_other_chance = atoi(row[12]);
|
||||
spell_set.engaged_detrimental_chance = atoi(row[13]);
|
||||
spell_set.pursue_no_sp_recast_min = atoi(row[14]);
|
||||
spell_set.pursue_no_sp_recast_max = atoi(row[15]);
|
||||
spell_set.pursue_detrimental_chance = atoi(row[16]);
|
||||
spell_set.idle_no_sp_recast_min = atoi(row[17]);
|
||||
spell_set.idle_no_sp_recast_max = atoi(row[18]);
|
||||
spell_set.idle_beneficial_chance = atoi(row[19]);
|
||||
|
||||
// pulling fixed values from an auto-increment field is dangerous...
|
||||
query = StringFormat("SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, resist_adjust "
|
||||
query = StringFormat(
|
||||
"SELECT spellid, type, minlevel, maxlevel, "
|
||||
"manacost, recast_delay, priority, min_hp, max_hp, resist_adjust "
|
||||
#ifdef BOTS
|
||||
"FROM %s "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel", (iDBSpellsID >= 3001 && iDBSpellsID <= 3016 ? "bot_spells_entries" : "npc_spells_entries"), iDBSpellsID);
|
||||
"FROM %s "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel",
|
||||
(iDBSpellsID >= 3001 && iDBSpellsID <= 3016 ? "bot_spells_entries" : "npc_spells_entries"),
|
||||
iDBSpellsID);
|
||||
#else
|
||||
"FROM npc_spells_entries "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel", iDBSpellsID);
|
||||
"FROM npc_spells_entries "
|
||||
"WHERE npc_spells_id=%d ORDER BY minlevel",
|
||||
iDBSpellsID);
|
||||
#endif
|
||||
results = QueryDatabase(query);
|
||||
results = QueryDatabase(query);
|
||||
|
||||
if (!results.Success())
|
||||
{
|
||||
if (!results.Success()) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
uint32 tmpSize = sizeof(DBnpcspells_Struct) + (sizeof(DBnpcspells_entries_Struct) * results.RowCount());
|
||||
npc_spells_cache[iDBSpellsID] = (DBnpcspells_Struct*) new uchar[tmpSize];
|
||||
memset(npc_spells_cache[iDBSpellsID], 0, tmpSize);
|
||||
npc_spells_cache[iDBSpellsID]->parent_list = tmpparent_list;
|
||||
npc_spells_cache[iDBSpellsID]->attack_proc = tmpattack_proc;
|
||||
npc_spells_cache[iDBSpellsID]->proc_chance = tmpproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->range_proc = tmprange_proc;
|
||||
npc_spells_cache[iDBSpellsID]->rproc_chance = tmprproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->defensive_proc = tmpdefensive_proc;
|
||||
npc_spells_cache[iDBSpellsID]->dproc_chance = tmpdproc_chance;
|
||||
npc_spells_cache[iDBSpellsID]->fail_recast = tmppfail_recast;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_min = tmpengaged_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_no_sp_recast_max = tmpengaged_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_beneficial_self_chance = tmpengaged_b_self_chance;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_beneficial_other_chance = tmpengaged_b_other_chance;
|
||||
npc_spells_cache[iDBSpellsID]->engaged_detrimental_chance = tmpengaged_d_chance;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_min = tmppursue_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_no_sp_recast_max = tmppursue_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->pursue_detrimental_chance = tmppursue_d_chance;
|
||||
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_min = tmpidle_no_sp_recast_min;
|
||||
npc_spells_cache[iDBSpellsID]->idle_no_sp_recast_max = tmpidle_no_sp_recast_max;
|
||||
npc_spells_cache[iDBSpellsID]->idle_beneficial_chance = tmpidle_b_chance;
|
||||
npc_spells_cache[iDBSpellsID]->numentries = results.RowCount();
|
||||
int entryIndex = 0;
|
||||
for (row = results.begin(); row != results.end(); ++row, ++entryIndex) {
|
||||
DBnpcspells_entries_Struct entry;
|
||||
int spell_id = atoi(row[0]);
|
||||
entry.spellid = spell_id;
|
||||
entry.type = atoul(row[1]);
|
||||
entry.minlevel = atoi(row[2]);
|
||||
entry.maxlevel = atoi(row[3]);
|
||||
entry.manacost = atoi(row[4]);
|
||||
entry.recast_delay = atoi(row[5]);
|
||||
entry.priority = atoi(row[6]);
|
||||
entry.min_hp = atoi(row[7]);
|
||||
entry.max_hp = atoi(row[8]);
|
||||
|
||||
int entryIndex = 0;
|
||||
for (row = results.begin(); row != results.end(); ++row, ++entryIndex)
|
||||
{
|
||||
int spell_id = atoi(row[0]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].spellid = spell_id;
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].type = atoul(row[1]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].minlevel = atoi(row[2]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].maxlevel = atoi(row[3]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].manacost = atoi(row[4]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].recast_delay = atoi(row[5]);
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].priority = atoi(row[6]);
|
||||
// some spell types don't make much since to be priority 0, so fix that
|
||||
if (!(entry.type & SpellTypes_Innate) && entry.priority == 0)
|
||||
entry.priority = 1;
|
||||
|
||||
if(row[7])
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = atoi(row[7]);
|
||||
else if(IsValidSpell(spell_id))
|
||||
npc_spells_cache[iDBSpellsID]->entries[entryIndex].resist_adjust = spells[spell_id].ResistDiff;
|
||||
}
|
||||
if (row[9])
|
||||
entry.resist_adjust = atoi(row[9]);
|
||||
else if (IsValidSpell(spell_id))
|
||||
entry.resist_adjust = spells[spell_id].ResistDiff;
|
||||
|
||||
return npc_spells_cache[iDBSpellsID];
|
||||
spell_set.entries.push_back(entry);
|
||||
}
|
||||
|
||||
npc_spells_cache.insert(std::make_pair(iDBSpellsID, spell_set));
|
||||
|
||||
return &npc_spells_cache[iDBSpellsID];
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
|
||||
+1
-1
@@ -542,7 +542,7 @@ int main(int argc, char** argv) {
|
||||
process_timer.Stop();
|
||||
process_timer.Start(1000, true);
|
||||
|
||||
if (zone) {
|
||||
if (zone && zone->GetZoneID() && zone->GetInstanceVersion()) {
|
||||
uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion());
|
||||
zone->StartShutdownTimer(shutdown_timer);
|
||||
}
|
||||
|
||||
+103
-3
@@ -135,6 +135,9 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
respawn2 = in_respawn;
|
||||
swarm_timer.Disable();
|
||||
|
||||
if (size < 0.0f)
|
||||
size = GetRaceGenderDefaultHeight(race, gender);
|
||||
|
||||
taunting = false;
|
||||
proximity = nullptr;
|
||||
copper = 0;
|
||||
@@ -208,6 +211,24 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
avoidance_rating = d->avoidance_rating;
|
||||
ATK = d->ATK;
|
||||
|
||||
// used for when switch back to charm
|
||||
default_ac = d->AC;
|
||||
default_min_dmg = min_dmg;
|
||||
default_max_dmg = max_dmg;
|
||||
default_attack_delay = d->attack_delay;
|
||||
default_accuracy_rating = d->accuracy_rating;
|
||||
default_avoidance_rating = d->avoidance_rating;
|
||||
default_atk = d->ATK;
|
||||
|
||||
// used for when getting charmed, if 0, doesn't swap
|
||||
charm_ac = d->charm_ac;
|
||||
charm_min_dmg = d->charm_min_dmg;
|
||||
charm_max_dmg = d->charm_max_dmg;
|
||||
charm_attack_delay = d->charm_attack_delay;
|
||||
charm_accuracy_rating = d->charm_accuracy_rating;
|
||||
charm_avoidance_rating = d->charm_avoidance_rating;
|
||||
charm_atk = d->charm_atk;
|
||||
|
||||
CalcMaxMana();
|
||||
SetMana(GetMaxMana());
|
||||
|
||||
@@ -227,6 +248,8 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
roambox_delay = 1000;
|
||||
p_depop = false;
|
||||
loottable_id = d->loottable_id;
|
||||
skip_global_loot = d->skip_global_loot;
|
||||
rare_spawn = d->rare_spawn;
|
||||
|
||||
no_target_hotkey = d->no_target_hotkey;
|
||||
|
||||
@@ -376,6 +399,19 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, const glm::vec4& position, int if
|
||||
raid_target = d->raid_target;
|
||||
ignore_despawn = d->ignore_despawn;
|
||||
m_targetable = !d->untargetable;
|
||||
|
||||
AISpellVar.fail_recast = RuleI(Spells, AI_SpellCastFinishedFailRecast);
|
||||
AISpellVar.engaged_no_sp_recast_min = RuleI(Spells, AI_EngagedNoSpellMinRecast);
|
||||
AISpellVar.engaged_no_sp_recast_max = RuleI(Spells, AI_EngagedNoSpellMaxRecast);
|
||||
AISpellVar.engaged_beneficial_self_chance = RuleI(Spells, AI_EngagedBeneficialSelfChance);
|
||||
AISpellVar.engaged_beneficial_other_chance = RuleI(Spells, AI_EngagedBeneficialOtherChance);
|
||||
AISpellVar.engaged_detrimental_chance = RuleI(Spells, AI_EngagedDetrimentalChance);
|
||||
AISpellVar.pursue_no_sp_recast_min = RuleI(Spells, AI_PursueNoSpellMinRecast);
|
||||
AISpellVar.pursue_no_sp_recast_max = RuleI(Spells, AI_PursueNoSpellMaxRecast);
|
||||
AISpellVar.pursue_detrimental_chance = RuleI(Spells, AI_PursueDetrimentalChance);
|
||||
AISpellVar.idle_no_sp_recast_min = RuleI(Spells, AI_IdleNoSpellMinRecast);
|
||||
AISpellVar.idle_no_sp_recast_max = RuleI(Spells, AI_IdleNoSpellMaxRecast);
|
||||
AISpellVar.idle_beneficial_chance = RuleI(Spells, AI_IdleBeneficialChance);
|
||||
}
|
||||
|
||||
NPC::~NPC()
|
||||
@@ -436,6 +472,26 @@ void NPC::SetTarget(Mob* mob) {
|
||||
//attack_timer.Disable();
|
||||
attack_dw_timer.Disable();
|
||||
}
|
||||
|
||||
// either normal pet and owner is client or charmed pet and owner is client
|
||||
Mob *owner = nullptr;
|
||||
if (IsPet() && IsPetOwnerClient()) {
|
||||
owner = GetOwner();
|
||||
} else if (IsCharmed()) {
|
||||
owner = GetOwner();
|
||||
if (owner && !owner->IsClient())
|
||||
owner = nullptr;
|
||||
}
|
||||
|
||||
if (owner) {
|
||||
auto client = owner->CastToClient();
|
||||
if (client->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) {
|
||||
auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct));
|
||||
auto ct = (ClientTarget_Struct *)app->pBuffer;
|
||||
ct->new_target = mob ? mob->GetID() : 0;
|
||||
client->FastQueuePacket(&app);
|
||||
}
|
||||
}
|
||||
Mob::SetTarget(mob);
|
||||
}
|
||||
|
||||
@@ -543,9 +599,7 @@ void NPC::QueryLoot(Client* to)
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkLootItem);
|
||||
linker.SetLootData(*cur);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
|
||||
to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", item_link.c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level);
|
||||
to->Message(0, "%s, ID: %u, Level: (min: %u, max: %u)", linker.GenerateLink().c_str(), (*cur)->item_id, (*cur)->min_level, (*cur)->max_level);
|
||||
}
|
||||
|
||||
to->Message(0, "%i items on %s.", x, GetName());
|
||||
@@ -724,6 +778,10 @@ bool NPC::Process()
|
||||
reface_timer->Disable();
|
||||
}
|
||||
|
||||
// needs to be done before mez and stun
|
||||
if (ForcedMovement)
|
||||
ProcessForcedMovement();
|
||||
|
||||
if (IsMezzed())
|
||||
return true;
|
||||
|
||||
@@ -900,6 +958,7 @@ bool NPC::SpawnZoneController(){
|
||||
npc_type->d_melee_texture2 = 0;
|
||||
npc_type->merchanttype = 0;
|
||||
npc_type->bodytype = 11;
|
||||
npc_type->skip_global_loot = true;
|
||||
|
||||
if (RuleB(Zone, EnableZoneControllerGlobals)) {
|
||||
npc_type->qglobal = true;
|
||||
@@ -2110,6 +2169,8 @@ void NPC::LevelScale() {
|
||||
if(level > 15 && level <= 25)
|
||||
scale_adjust = 2;
|
||||
|
||||
AC += (int)(AC * scaling);
|
||||
ATK += (int)(ATK * scaling);
|
||||
base_hp += (int)(base_hp * scaling);
|
||||
max_hp += (int)(max_hp * scaling);
|
||||
cur_hp = max_hp;
|
||||
@@ -2616,3 +2677,42 @@ void NPC::DepopSwarmPets()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NPC::ModifyStatsOnCharm(bool bRemoved)
|
||||
{
|
||||
if (bRemoved) {
|
||||
if (charm_ac)
|
||||
AC = default_ac;
|
||||
if (charm_attack_delay)
|
||||
attack_delay = default_attack_delay;
|
||||
if (charm_accuracy_rating)
|
||||
accuracy_rating = default_accuracy_rating;
|
||||
if (charm_avoidance_rating)
|
||||
avoidance_rating = default_avoidance_rating;
|
||||
if (charm_atk)
|
||||
ATK = default_atk;
|
||||
if (charm_min_dmg || charm_max_dmg) {
|
||||
base_damage = round((default_max_dmg - default_min_dmg) / 1.9);
|
||||
min_damage = default_min_dmg - round(base_damage / 10.0);
|
||||
}
|
||||
} else {
|
||||
if (charm_ac)
|
||||
AC = charm_ac;
|
||||
if (charm_attack_delay)
|
||||
attack_delay = charm_attack_delay;
|
||||
if (charm_accuracy_rating)
|
||||
accuracy_rating = charm_accuracy_rating;
|
||||
if (charm_avoidance_rating)
|
||||
avoidance_rating = charm_avoidance_rating;
|
||||
if (charm_atk)
|
||||
ATK = charm_atk;
|
||||
if (charm_min_dmg || charm_max_dmg) {
|
||||
base_damage = round((charm_max_dmg - charm_min_dmg) / 1.9);
|
||||
min_damage = charm_min_dmg - round(base_damage / 10.0);
|
||||
}
|
||||
}
|
||||
// the rest of the stats aren't cached, so lets just do these two instead of full CalcBonuses()
|
||||
SetAttackTimer();
|
||||
CalcAC();
|
||||
}
|
||||
|
||||
|
||||
+31
-6
@@ -61,6 +61,8 @@ struct AISpells_Struct {
|
||||
int32 recast_delay;
|
||||
int16 priority;
|
||||
int16 resist_adjust;
|
||||
int8 min_hp; // >0 won't cast if HP is below
|
||||
int8 max_hp; // >0 won't cast if HP is above
|
||||
};
|
||||
|
||||
struct AISpellsEffects_Struct {
|
||||
@@ -183,6 +185,7 @@ public:
|
||||
void AddItem(uint32 itemid, uint16 charges, bool equipitem = true, uint32 aug1 = 0, uint32 aug2 = 0, uint32 aug3 = 0, uint32 aug4 = 0, uint32 aug5 = 0, uint32 aug6 = 0);
|
||||
void AddLootTable();
|
||||
void AddLootTable(uint32 ldid);
|
||||
void CheckGlobalLootTables();
|
||||
void DescribeAggro(Client *towho, Mob *mob, bool verbose);
|
||||
void RemoveItem(uint32 item_id, uint16 quantity = 0, uint16 slot = 0);
|
||||
void CheckMinMaxLevel(Mob *them);
|
||||
@@ -195,6 +198,7 @@ public:
|
||||
uint32 CountLoot();
|
||||
inline uint32 GetLoottableID() const { return loottable_id; }
|
||||
virtual void UpdateEquipmentLight();
|
||||
inline bool DropsGlobalLoot() const { return !skip_global_loot; }
|
||||
|
||||
inline uint32 GetCopper() const { return copper; }
|
||||
inline uint32 GetSilver() const { return silver; }
|
||||
@@ -280,6 +284,8 @@ public:
|
||||
int32 GetNPCHPRegen() const { return hp_regen + itembonuses.HPRegen + spellbonuses.HPRegen; }
|
||||
inline const char* GetAmmoIDfile() const { return ammo_idfile; }
|
||||
|
||||
void ModifyStatsOnCharm(bool bRemoved);
|
||||
|
||||
//waypoint crap
|
||||
int GetMaxWp() const { return max_wp; }
|
||||
void DisplayWaypointInfo(Client *to);
|
||||
@@ -378,7 +384,7 @@ public:
|
||||
void NPCSlotTexture(uint8 slot, uint16 texture); // Sets new material values for slots
|
||||
|
||||
uint32 GetAdventureTemplate() const { return adventure_template_id; }
|
||||
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust);
|
||||
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint32 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust, int8 min_hp, int8 max_hp);
|
||||
void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
|
||||
void RemoveSpellFromNPCList(int16 spell_id);
|
||||
Timer *GetRefaceTimer() const { return reface_timer; }
|
||||
@@ -403,8 +409,6 @@ public:
|
||||
|
||||
uint32 GetSpawnKillCount();
|
||||
int GetScore();
|
||||
void SetMerchantProbability(uint8 amt) { probability = amt; }
|
||||
uint8 GetMerchantProbability() { return probability; }
|
||||
void mod_prespawn(Spawn2 *sp);
|
||||
int mod_npc_damage(int damage, EQEmu::skills::SkillType skillinuse, int hand, const EQEmu::ItemData* weapon, Mob* other);
|
||||
void mod_npc_killed_merit(Mob* c);
|
||||
@@ -420,6 +424,8 @@ public:
|
||||
|
||||
bool IgnoreDespawn() { return ignore_despawn; }
|
||||
|
||||
std::unique_ptr<Timer> AIautocastspell_timer;
|
||||
|
||||
protected:
|
||||
|
||||
const NPCType* NPCTypedata;
|
||||
@@ -453,11 +459,11 @@ protected:
|
||||
|
||||
uint32 npc_spells_id;
|
||||
uint8 casting_spell_AIindex;
|
||||
std::unique_ptr<Timer> AIautocastspell_timer;
|
||||
|
||||
uint32* pDontCastBefore_casting_spell;
|
||||
std::vector<AISpells_Struct> AIspells;
|
||||
bool HasAISpell;
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes);
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes, bool bInnates = false);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
AISpellsVar_Struct AISpellVar;
|
||||
int16 GetFocusEffect(focusType type, uint16 spell_id);
|
||||
@@ -480,6 +486,25 @@ protected:
|
||||
int32 SpellFocusDMG;
|
||||
int32 SpellFocusHeal;
|
||||
|
||||
// stats to switch back to after charm wears off
|
||||
// could probably pick a better name, but these probably aren't taken so ...
|
||||
int default_ac;
|
||||
int default_min_dmg;
|
||||
int default_max_dmg;
|
||||
int default_attack_delay;
|
||||
int default_accuracy_rating;
|
||||
int default_avoidance_rating;
|
||||
int default_atk;
|
||||
|
||||
// when charmed, switch to these
|
||||
int charm_ac;
|
||||
int charm_min_dmg;
|
||||
int charm_max_dmg;
|
||||
int charm_attack_delay;
|
||||
int charm_accuracy_rating;
|
||||
int charm_avoidance_rating;
|
||||
int charm_atk;
|
||||
|
||||
//pet crap:
|
||||
uint16 pet_spell_id;
|
||||
bool taunting;
|
||||
@@ -535,11 +560,11 @@ protected:
|
||||
std::list<MercData> mercDataList;
|
||||
|
||||
bool raid_target;
|
||||
uint8 probability;
|
||||
bool ignore_despawn; //NPCs with this set to 1 will ignore the despawn value in spawngroup
|
||||
|
||||
private:
|
||||
uint32 loottable_id;
|
||||
bool skip_global_loot;
|
||||
bool p_depop;
|
||||
};
|
||||
|
||||
|
||||
+1
-1
@@ -70,7 +70,7 @@ Object::Object(uint32 id, uint32 type, uint32 icon, const Object_Struct& object,
|
||||
|
||||
//creating a re-ocurring ground spawn.
|
||||
Object::Object(const EQEmu::ItemInstance* inst, char* name,float max_x,float min_x,float max_y,float min_y,float z,float heading,uint32 respawntimer)
|
||||
: respawn_timer(respawntimer), decay_timer(300000)
|
||||
: respawn_timer(respawntimer * 1000), decay_timer(300000)
|
||||
{
|
||||
|
||||
user = nullptr;
|
||||
|
||||
+4
-4
@@ -42,7 +42,7 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa
|
||||
bool partial = false;
|
||||
bool stuck = false;
|
||||
Route = zone->pathing->FindRoute(From, To, partial, stuck);
|
||||
AdjustRoute(Route, flymode, GetModelOffset());
|
||||
AdjustRoute(Route, flymode, GetZOffset());
|
||||
|
||||
PathingDestination = To;
|
||||
WaypointChanged = true;
|
||||
@@ -66,7 +66,7 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa
|
||||
bool partial = false;
|
||||
bool stuck = false;
|
||||
Route = zone->pathing->FindRoute(From, To, partial, stuck);
|
||||
AdjustRoute(Route, flymode, GetModelOffset());
|
||||
AdjustRoute(Route, flymode, GetZOffset());
|
||||
|
||||
PathingDestination = To;
|
||||
WaypointChanged = true;
|
||||
@@ -133,7 +133,7 @@ glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Wa
|
||||
bool partial = false;
|
||||
bool stuck = false;
|
||||
Route = zone->pathing->FindRoute(From, To, partial, stuck);
|
||||
AdjustRoute(Route, flymode, GetModelOffset());
|
||||
AdjustRoute(Route, flymode, GetZOffset());
|
||||
PathingDestination = To;
|
||||
WaypointChanged = true;
|
||||
|
||||
@@ -192,7 +192,7 @@ glm::vec3 Mob::HandleStuckPath(const glm::vec3 &To, const glm::vec3 &From)
|
||||
|
||||
auto final_node = r.back();
|
||||
Route.push_back(final_node);
|
||||
AdjustRoute(Route, flymode, GetModelOffset());
|
||||
AdjustRoute(Route, flymode, GetZOffset());
|
||||
return (*Route.begin()).pos;
|
||||
}
|
||||
|
||||
|
||||
@@ -9002,6 +9002,35 @@ XS(XS_Mob_GetMeleeMitigation) {
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_Mob_TryMoveAlong);
|
||||
XS(XS_Mob_TryMoveAlong) {
|
||||
dXSARGS;
|
||||
if (items < 3 || items > 4)
|
||||
Perl_croak(aTHX_ "Usage: Mob::TryMoveAlong(THIS, distance, angle, send?)");
|
||||
{
|
||||
Mob* THIS;
|
||||
float distance = (float)SvNV(ST(1));
|
||||
float angle = (float)SvNV(ST(2));
|
||||
bool send = true;
|
||||
|
||||
if (sv_derived_from(ST(0), "Mob")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(Mob *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type Mob");
|
||||
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
if (items == 4)
|
||||
send = (bool)SvTRUE(ST(3));
|
||||
|
||||
THIS->TryMoveAlong(distance, angle, send);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
@@ -9335,6 +9364,7 @@ XS(boot_Mob)
|
||||
newXSproto(strcpy(buf, "IsSilenced"), XS_Mob_IsSilenced, file, "$");
|
||||
newXSproto(strcpy(buf, "IsAmnesiad"), XS_Mob_IsAmnesiad, file, "$");
|
||||
newXSproto(strcpy(buf, "GetMeleeMitigation"), XS_Mob_GetMeleeMitigation, file, "$");
|
||||
newXSproto(strcpy(buf, "TryMoveAlong"), XS_Mob_TryMoveAlong, file, "$$$;$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
+1
-51
@@ -1993,7 +1993,7 @@ XS(XS_NPC_AddSpellToNPCList)
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
THIS->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust);
|
||||
THIS->AddSpellToNPCList(priority, spell_id, type, mana_cost, recast_delay, resist_adjust, 0, 0);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
@@ -2304,54 +2304,6 @@ XS(XS_NPC_GetScore)
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_NPC_SetMerchantProbability);
|
||||
XS(XS_NPC_SetMerchantProbability) {
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: NPC::SetMerchantProbability(THIS, Probability)");
|
||||
{
|
||||
NPC *THIS;
|
||||
uint8 Probability = (uint8)SvIV(ST(1));
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(NPC *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type NPC");
|
||||
if(THIS == nullptr)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
THIS->SetMerchantProbability(Probability);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_NPC_GetMerchantProbability);
|
||||
XS(XS_NPC_GetMerchantProbability) {
|
||||
dXSARGS;
|
||||
if (items != 1)
|
||||
Perl_croak(aTHX_ "Usage: NPC::GetMerchantProbability(THIS)");
|
||||
{
|
||||
NPC *THIS;
|
||||
uint8 RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
THIS = INT2PTR(NPC *,tmp);
|
||||
}
|
||||
else
|
||||
Perl_croak(aTHX_ "THIS is not of type NPC");
|
||||
if(THIS == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->GetMerchantProbability();
|
||||
XSprePUSH; PUSHu((UV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
XS(XS_NPC_AddMeleeProc);
|
||||
XS(XS_NPC_AddMeleeProc) {
|
||||
dXSARGS;
|
||||
@@ -2677,8 +2629,6 @@ XS(boot_NPC)
|
||||
newXSproto(strcpy(buf, "GetAvoidanceRating"), XS_NPC_GetAvoidanceRating, file, "$");
|
||||
newXSproto(strcpy(buf, "GetSpawnKillCount"), XS_NPC_GetSpawnKillCount, file, "$");
|
||||
newXSproto(strcpy(buf, "GetScore"), XS_NPC_GetScore, file, "$");
|
||||
newXSproto(strcpy(buf, "SetMerchantProbability"), XS_NPC_SetMerchantProbability, file, "$$");
|
||||
newXSproto(strcpy(buf, "GetMerchantProbability"), XS_NPC_GetMerchantProbability, file, "$");
|
||||
newXSproto(strcpy(buf, "AddMeleeProc"), XS_NPC_AddMeleeProc, file, "$$$");
|
||||
newXSproto(strcpy(buf, "AddRangedProc"), XS_NPC_AddRangedProc, file, "$$$");
|
||||
newXSproto(strcpy(buf, "AddDefensiveProc"), XS_NPC_AddDefensiveProc, file, "$$$");
|
||||
|
||||
+47
-96
@@ -28,6 +28,8 @@
|
||||
#include "pets.h"
|
||||
#include "zonedb.h"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef BOTS
|
||||
#include "bot.h"
|
||||
#endif
|
||||
@@ -38,82 +40,42 @@
|
||||
#endif
|
||||
|
||||
|
||||
const char *GetRandPetName()
|
||||
// need to pass in a char array of 64 chars
|
||||
void GetRandPetName(char *name)
|
||||
{
|
||||
static const char *petnames[] = { "Gabaner","Gabann","Gabantik","Gabarab","Gabarer","Gabarn","Gabartik",
|
||||
"Gabekab","Gabeker","Gabekn","Gaber","Gabn","Gabobab","Gabobn","Gabtik",
|
||||
"Ganer","Gann","Gantik","Garab","Garaner","Garann","Garantik","Gararn",
|
||||
"Garekn","Garer","Garn","Gartik","Gasaner","Gasann","Gasantik","Gasarer",
|
||||
"Gasartik","Gasekn","Gaser","Gebann","Gebantik","Gebarer","Gebarn","Gebartik",
|
||||
"Gebeker","Gebekn","Gebn","Gekab","Geker","Gekn","Genaner","Genann","Genantik",
|
||||
"Genarer","Genarn","Gener","Genn","Genobtik","Gibaner","Gibann","Gibantik",
|
||||
"Gibarn","Gibartik","Gibekn","Giber","Gibn","Gibobtik","Gibtik","Gobaber",
|
||||
"Gobaner","Gobann","Gobarn","Gobartik","Gober","Gobn","Gobober","Gobobn",
|
||||
"Gobobtik","Gobtik","Gonaner","Gonann","Gonantik","Gonarab","Gonarer",
|
||||
"Gonarn","Gonartik","Gonekab","Gonekn","Goner","Gonobtik","Gontik","Gotik",
|
||||
"Jabaner","Jabann","Jabantik","Jabarab","Jabarer","Jabarn","Jabartik",
|
||||
"Jabekab","Jabeker","Jabekn","Jaber","Jabn","Jabobtik","Jabtik","Janab",
|
||||
"Janer","Jann","Jantik","Jarab","Jaranab","Jaraner","Jararer","Jararn",
|
||||
"Jarartik","Jareker","Jarekn","Jarer","Jarn","Jarobn","Jarobtik","Jartik",
|
||||
"Jasab","Jasaner","Jasantik","Jasarer","Jasartik","Jasekab","Jaseker",
|
||||
"Jasekn","Jaser","Jasn","Jasobab","Jasober","Jastik","Jebanab","Jebann",
|
||||
"Jebantik","Jebarab","Jebarar","Jebarer","Jebarn","Jebartik","Jebeker",
|
||||
"Jebekn","Jeber","Jebobn","Jebtik","Jekab","Jeker","Jekn","Jenann",
|
||||
"Jenantik","Jenarer","Jeneker","Jenekn","Jentik","Jibaner","Jibann",
|
||||
"Jibantik","Jibarer","Jibarn","Jibartik","Jibeker","Jibn","Jibobn",
|
||||
"Jibtik","Jobab","Jobaner","Jobann","Jobantik","Jobarn","Jobartik",
|
||||
"Jobekab","Jobeker","Jober","Jobn","Jobtik","Jonanab","Jonaner",
|
||||
"Jonann","Jonantik","Jonarer","Jonarn","Jonartik","Jonekab","Joneker",
|
||||
"Jonekn","Joner","Jonn","Jonnarn","Jonober","Jonobn","Jonobtik","Jontik",
|
||||
"Kabanab","Kabaner","Kabann","Kabantik","Kabarer","Kabarn","Kabartik",
|
||||
"Kabeker","Kabekn","Kaber","Kabn","Kabober","Kabobn","Kabobtik","Kabtik",
|
||||
"Kanab","Kaner","Kann","Kantik","Karab","Karanab","Karaner","Karann",
|
||||
"Karantik","Kararer","Karartik","Kareker","Karer","Karn","Karobab","Karobn",
|
||||
"Kartik","Kasaner","Kasann","Kasarer","Kasartik","Kaseker","Kasekn","Kaser",
|
||||
"Kasn","Kasober","Kastik","Kebann","Kebantik","Kebarab","Kebartik","Kebeker",
|
||||
"Kebekn","Kebn","Kebobab","Kebtik","Kekab","Keker","Kekn","Kenab","Kenaner",
|
||||
"Kenantik","Kenarer","Kenarn","Keneker","Kener","Kenn","Kenobn","Kenobtik",
|
||||
"Kentik","Kibab","Kibaner","Kibantik","Kibarn","Kibartik","Kibekab","Kibeker",
|
||||
"Kibekn","Kibn","Kibobn","Kibobtik","Kobab","Kobanab","Kobaner","Kobann",
|
||||
"Kobantik","Kobarer","Kobarn","Kobartik","Kobeker","Kobekn","Kober","Kobn",
|
||||
"Kobober","Kobobn","Kobtik","Konanab","Konaner","Konann","Konantik","Konarab",
|
||||
"Konarer","Konarn","Konekab","Koneker","Konekn","Koner","Konn","Konobn",
|
||||
"Konobtik","Kontik","Labanab","Labaner","Labann","Labarab","Labarer",
|
||||
"Labarn","Labartik","Labeker","Labekn","Laner","Lann","Larab","Larantik",
|
||||
"Lararer","Lararn","Larartik","Lareker","Larer","Larn","Lartik","Lasaner",
|
||||
"Lasann","Lasarer","Laseker","Laser","Lasik","Lasn","Lastik","Lebaner",
|
||||
"Lebarer","Lebartik","Lebekn","Lebtik","Lekab","Lekn","Lenanab","Lenaner",
|
||||
"Lenann","Lenartik","Lenekab","Leneker","Lenekn","Lentik","Libab","Libaner",
|
||||
"Libann","Libantik","Libarer","Libarn","Libartik","Libeker","Libekn","Lobann",
|
||||
"Lobarab","Lobarn","Lobartik","Lobekn","Lobn","Lobober","Lobobn","Lobtik",
|
||||
"Lonaner","Lonann","Lonantik","Lonarab","Lonarer","Lonarn","Lonartik","Lonekn",
|
||||
"Loner","Lonobtik","Lontik","Vabanab","Vabaner","Vabann","Vabantik","Vabarer",
|
||||
"Vabarn","Vabartik","Vabeker","Vabekn","Vabtik","Vanikk","Vann","Varartik","Varn",
|
||||
"Vartik","Vasann","Vasantik","Vasarab","Vasarer","Vaseker","Vebaner","Vebantik",
|
||||
"Vebarab","Vebeker","Vebekn","Vebobn","Vekab","Veker","Venaner","Venantik","Venar",
|
||||
"Venarn","Vener","Ventik","Vibann","Vibantik","Viber","Vibobtik","Vobann",
|
||||
"Vobarer","Vobartik","Vobekn","Vober","Vobn","Vobtik","Vonaner","Vonann",
|
||||
"Vonantik","Vonarab","Vonarn","Vonartik","Voneker","Vonn","Xabanab","Xabaner",
|
||||
"Xabarer","Xabarn","Xabartik","Xabekab","Xabeker","Xabekn","Xaber","Xabober",
|
||||
"Xaner","Xann","Xarab","Xaranab","Xarann","Xarantik","Xararer","Xarartik","Xarer",
|
||||
"Xarn","Xartik","Xasaner","Xasann","Xasarab","Xasarn","Xasekab","Xaseker",
|
||||
"Xebarer","Xebarn","Xebeker","Xeber","Xebober","Xebtik","Xekab","Xeker",
|
||||
"Xekn","Xenann","Xenantik","Xenarer","Xenartik","Xenekn","Xener","Xenober",
|
||||
"Xentik","Xibantik","Xibarer","Xibekab","Xibeker","Xibobab","Xibober","Xibobn",
|
||||
"Xobaner","Xobann","Xobarab","Xobarn","Xobekab","Xobeker","Xobekn","Xober",
|
||||
"Xobn","Xobobn","Xobtik","Xonaner","Xonann","Xonantik","Xonarer","Xonartik",
|
||||
"Xonekab","Xoneker","Xonekn","Xoner","Xonober","Xtik","Zabaner","Zabantik",
|
||||
"Zabarab","Zabekab","Zabekn","Zaber","Zabn","Zabobab","Zabober","Zabtik",
|
||||
"Zaner","Zantik","Zarann","Zarantik","Zararn","Zarartik","Zareker","Zarekn",
|
||||
"Zarer","Zarn","Zarober","Zartik","Zasaner","Zasarer","Zaseker","Zasekn","Zasn",
|
||||
"Zebantik","Zebarer","Zebarn","Zebartik","Zebobab","Zekab","Zekn","Zenann",
|
||||
"Zenantik","Zenarer","Zenarn","Zenekab","Zeneker","Zenobtik","Zibanab","Zibaner",
|
||||
"Zibann","Zibarer","Zibartik","Zibekn","Zibn","Zibobn","Zobaner","Zobann",
|
||||
"Zobarn","Zober","Zobn","Zonanab","Zonaner","Zonann","Zonantik","Zonarer",
|
||||
"Zonartik","Zonobn","Zonobtik","Zontik","Ztik" };
|
||||
int r = zone->random.Int(0, (sizeof(petnames)/sizeof(const char *))-1);
|
||||
printf("Pet being created: %s\n",petnames[r]); // DO NOT COMMENT THIS OUT!
|
||||
return petnames[r];
|
||||
std::string temp;
|
||||
temp.reserve(64);
|
||||
// note these orders are used to make the exclusions cheap :P
|
||||
static const char *part1[] = {"G", "J", "K", "L", "V", "X", "Z"};
|
||||
static const char *part2[] = {nullptr, "ab", "ar", "as", "eb", "en", "ib", "ob", "on"};
|
||||
static const char *part3[] = {nullptr, "an", "ar", "ek", "ob"};
|
||||
static const char *part4[] = {"er", "ab", "n", "tik"};
|
||||
|
||||
const char *first = part1[zone->random.Int(0, (sizeof(part1) / sizeof(const char *)) - 1)];
|
||||
const char *second = part2[zone->random.Int(0, (sizeof(part2) / sizeof(const char *)) - 1)];
|
||||
const char *third = part3[zone->random.Int(0, (sizeof(part3) / sizeof(const char *)) - 1)];
|
||||
const char *fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 1)];
|
||||
|
||||
// if both of these are empty, we would get an illegally short name
|
||||
if (second == nullptr && third == nullptr)
|
||||
fourth = part4[(sizeof(part4) / sizeof(const char *)) - 1];
|
||||
|
||||
// "ektik" isn't allowed either I guess?
|
||||
if (third == part3[3] && fourth == part4[3])
|
||||
fourth = part4[zone->random.Int(0, (sizeof(part4) / sizeof(const char *)) - 2)];
|
||||
|
||||
// "Laser" isn't allowed either I guess?
|
||||
if (first == part1[3] && second == part2[3] && third == nullptr && fourth == part4[0])
|
||||
fourth = part4[zone->random.Int(1, (sizeof(part4) / sizeof(const char *)) - 2)];
|
||||
|
||||
temp += first;
|
||||
if (second != nullptr)
|
||||
temp += second;
|
||||
if (third != nullptr)
|
||||
temp += third;
|
||||
temp += fourth;
|
||||
|
||||
strn0cpy(name, temp.c_str(), 64);
|
||||
}
|
||||
|
||||
//not used anymore
|
||||
@@ -325,7 +287,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
|
||||
} else if (record.petnaming == 4) {
|
||||
// Keep the DB name
|
||||
} else if (record.petnaming == 3 && IsClient()) {
|
||||
strcpy(npc_type->name, GetRandPetName());
|
||||
GetRandPetName(npc_type->name);
|
||||
} else if (record.petnaming == 5 && IsClient()) {
|
||||
strcpy(npc_type->name, this->GetName());
|
||||
npc_type->name[24] = '\0';
|
||||
@@ -475,22 +437,6 @@ Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 po
|
||||
// Class should use npc constructor to set light properties
|
||||
}
|
||||
|
||||
void Pet::SetTarget(Mob *mob)
|
||||
{
|
||||
if (mob == GetTarget())
|
||||
return;
|
||||
|
||||
auto owner = GetOwner();
|
||||
if (owner && owner->IsClient() && owner->CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) {
|
||||
auto app = new EQApplicationPacket(OP_PetHoTT, sizeof(ClientTarget_Struct));
|
||||
auto ct = (ClientTarget_Struct *)app->pBuffer;
|
||||
ct->new_target = mob ? mob->GetID() : 0;
|
||||
owner->CastToClient()->QueuePacket(app);
|
||||
safe_delete(app);
|
||||
}
|
||||
NPC::SetTarget(mob);
|
||||
}
|
||||
|
||||
bool ZoneDatabase::GetPetEntry(const char *pet_type, PetRecord *into) {
|
||||
return GetPoweredPetEntry(pet_type, 0, into);
|
||||
}
|
||||
@@ -671,10 +617,15 @@ void NPC::SetPetState(SpellBuff_Struct *pet_buffs, uint32 *items) {
|
||||
continue;
|
||||
|
||||
const EQEmu::ItemData* item2 = database.GetItem(items[i]);
|
||||
if (item2 && item2->NoDrop != 0) {
|
||||
//dont bother saving item charges for now, NPCs never use them
|
||||
//and nobody should be able to get them off the corpse..?
|
||||
AddLootDrop(item2, &itemlist, 0, 1, 255, true, true);
|
||||
|
||||
if (item2) {
|
||||
bool noDrop=(item2->NoDrop == 0); // Field is reverse logic
|
||||
bool petCanHaveNoDrop = (RuleB(Pets, CanTakeNoDrop) &&
|
||||
_CLIENTPET(this) && GetPetType() <= petOther);
|
||||
|
||||
if (!noDrop || petCanHaveNoDrop) {
|
||||
AddLootDrop(item2, &itemlist, 0, 1, 255, true, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,6 @@ struct NPCType;
|
||||
class Pet : public NPC {
|
||||
public:
|
||||
Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power);
|
||||
virtual void SetTarget(Mob *mob);
|
||||
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
|
||||
|
||||
};
|
||||
|
||||
+2
-2
@@ -150,13 +150,13 @@ float GetReciprocalHeading(const float heading)
|
||||
float result = 0;
|
||||
|
||||
// Convert to radians
|
||||
float h = (heading / 256.0f) * 6.283184f;
|
||||
float h = (heading / 512.0f) * 6.283184f;
|
||||
|
||||
// Calculate the reciprocal heading in radians
|
||||
result = h + 3.141592f;
|
||||
|
||||
// Convert back to eq heading from radians
|
||||
result = (result / 6.283184f) * 256.0f;
|
||||
result = (result / 6.283184f) * 512.0f;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
+16
-11
@@ -210,6 +210,8 @@ Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& p
|
||||
{
|
||||
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc,true,true);
|
||||
if(grid > 0)
|
||||
{
|
||||
@@ -232,6 +234,8 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
|
||||
{
|
||||
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc,true,true);
|
||||
if(grid > 0)
|
||||
{
|
||||
@@ -308,6 +312,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
|
||||
|
||||
found_spawn->SetNPCPointer(npc);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
npc->SetSp2(found_spawn->SpawnGroupID());
|
||||
entity_list.AddNPC(npc);
|
||||
entity_list.LimitAddNPC(npc);
|
||||
@@ -1240,9 +1246,7 @@ void QuestManager::itemlink(int item_id) {
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemData);
|
||||
linker.SetItemData(item);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
|
||||
initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), item_link.c_str());
|
||||
initiator->Message(0, "%s tells you, %s", owner->GetCleanName(), linker.GenerateLink().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1583,6 +1587,8 @@ void QuestManager::respawn(int npcTypeID, int grid) {
|
||||
{
|
||||
owner = new NPC(npcType, nullptr, owner->GetPosition(), FlyMode3);
|
||||
owner->CastToNPC()->AddLootTable();
|
||||
if (owner->CastToNPC()->DropsGlobalLoot())
|
||||
owner->CastToNPC()->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(owner->CastToNPC(),true,true);
|
||||
if(grid > 0)
|
||||
owner->CastToNPC()->AssignWaypoints(grid);
|
||||
@@ -1591,7 +1597,7 @@ void QuestManager::respawn(int npcTypeID, int grid) {
|
||||
}
|
||||
}
|
||||
|
||||
void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz) {
|
||||
void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz, bool bSay) {
|
||||
QuestManagerCurrentQuestVars();
|
||||
if (!owner || !owner->IsNPC())
|
||||
return;
|
||||
@@ -1604,6 +1610,7 @@ void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy,
|
||||
owner->CastToNPC()->proximity->max_y = maxy;
|
||||
owner->CastToNPC()->proximity->min_z = minz;
|
||||
owner->CastToNPC()->proximity->max_z = maxz;
|
||||
owner->CastToNPC()->proximity->say = bSay;
|
||||
}
|
||||
|
||||
void QuestManager::clear_proximity() {
|
||||
@@ -1908,8 +1915,8 @@ void QuestManager::npcfeature(char *feature, int setting)
|
||||
QuestManagerCurrentQuestVars();
|
||||
uint16 Race = owner->GetRace();
|
||||
uint8 Gender = owner->GetGender();
|
||||
uint8 Texture = 0xFF;
|
||||
uint8 HelmTexture = 0xFF;
|
||||
uint8 Texture = owner->GetTexture();
|
||||
uint8 HelmTexture = owner->GetHelmTexture();
|
||||
uint8 HairColor = owner->GetHairColor();
|
||||
uint8 BeardColor = owner->GetBeardColor();
|
||||
uint8 EyeColor1 = owner->GetEyeColor1();
|
||||
@@ -2468,9 +2475,8 @@ const char* QuestManager::varlink(char* perltext, int item_id) {
|
||||
linker.SetLinkType(EQEmu::saylink::SayLinkItemData);
|
||||
linker.SetItemData(item);
|
||||
|
||||
auto item_link = linker.GenerateLink();
|
||||
strcpy(perltext, item_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink()
|
||||
|
||||
strcpy(perltext, linker.GenerateLink().c_str());
|
||||
|
||||
return perltext;
|
||||
}
|
||||
|
||||
@@ -2692,8 +2698,7 @@ const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkNam
|
||||
linker.SetProxyAugment1ID(sayid);
|
||||
linker.SetProxyText(LinkName);
|
||||
|
||||
auto say_link = linker.GenerateLink();
|
||||
strcpy(Phrase, say_link.c_str()); // link length is currently ranged from 1 to 250 in TextLink::GenerateLink()
|
||||
strcpy(Phrase, linker.GenerateLink().c_str());
|
||||
|
||||
return Phrase;
|
||||
}
|
||||
|
||||
+1
-1
@@ -148,7 +148,7 @@ public:
|
||||
void setnexthpevent(int at);
|
||||
void setnextinchpevent(int at);
|
||||
void respawn(int npc_type, int grid);
|
||||
void set_proximity(float minx, float maxx, float miny, float maxy, float minz=-999999, float maxz=999999);
|
||||
void set_proximity(float minx, float maxx, float miny, float maxy, float minz=-999999, float maxz=999999, bool bSay = false);
|
||||
void clear_proximity();
|
||||
void enable_proximity_say();
|
||||
void disable_proximity_say();
|
||||
|
||||
@@ -323,6 +323,10 @@ void Raid::SaveRaidLeaderAA()
|
||||
|
||||
void Raid::UpdateGroupAAs(uint32 gid)
|
||||
{
|
||||
|
||||
if (gid < 0 || gid > MAX_RAID_GROUPS)
|
||||
return;
|
||||
|
||||
Client *gl = GetGroupLeader(gid);
|
||||
|
||||
if (gl)
|
||||
|
||||
@@ -236,6 +236,8 @@ bool Spawn2::Process() {
|
||||
|
||||
npcthis = npc;
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
npc->SetSp2(spawngroup_id_);
|
||||
npc->SaveGuardPointAnim(anim);
|
||||
npc->SetAppearance((EmuAppearance)anim);
|
||||
|
||||
@@ -37,6 +37,7 @@ int Mob::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target)
|
||||
case EQEmu::skills::SkillDragonPunch:
|
||||
case EQEmu::skills::SkillEagleStrike:
|
||||
case EQEmu::skills::SkillTigerClaw:
|
||||
case EQEmu::skills::SkillRoundKick:
|
||||
if (skill_level >= 25)
|
||||
base++;
|
||||
if (skill_level >= 75)
|
||||
@@ -1442,7 +1443,7 @@ void Mob::SendItemAnimation(Mob *to, const EQEmu::ItemData *item, EQEmu::skills:
|
||||
|
||||
//these angle and tilt used together seem to make the arrow/knife throw as straight as I can make it
|
||||
|
||||
as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY()) * 2;
|
||||
as->launch_angle = CalculateHeadingToTarget(to->GetX(), to->GetY());
|
||||
as->tilt = 125;
|
||||
as->arc = 50;
|
||||
|
||||
|
||||
+36
-87
@@ -60,6 +60,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
|
||||
const SPDat_Spell_Struct &spell = spells[spell_id];
|
||||
|
||||
if (spell.disallow_sit && IsBuffSpell(spell_id) && IsClient() && (CastToClient()->IsSitting() || CastToClient()->GetHorseId() != 0))
|
||||
return false;
|
||||
|
||||
bool c_override = false;
|
||||
if (caster && caster->IsClient() && GetCastedSpellInvSlot() > 0) {
|
||||
const EQEmu::ItemInstance *inst = caster->CastToClient()->GetInv().GetItem(GetCastedSpellInvSlot());
|
||||
@@ -134,6 +137,15 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
buffs[buffslot].magic_rune = 0;
|
||||
buffs[buffslot].numhits = 0;
|
||||
|
||||
if (spells[spell_id].numhits > 0) {
|
||||
|
||||
int numhit = spells[spell_id].numhits;
|
||||
|
||||
numhit += numhit * caster->GetFocusEffect(focusFcLimitUse, spell_id) / 100;
|
||||
numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id);
|
||||
buffs[buffslot].numhits = numhit;
|
||||
}
|
||||
|
||||
if (spells[spell_id].EndurUpkeep > 0)
|
||||
SetEndurUpkeep(true);
|
||||
|
||||
@@ -181,14 +193,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
}
|
||||
}
|
||||
|
||||
if(spells[spell_id].numhits > 0 && buffslot >= 0){
|
||||
|
||||
int numhit = spells[spell_id].numhits;
|
||||
|
||||
numhit += numhit*caster->GetFocusEffect(focusFcLimitUse, spell_id)/100;
|
||||
numhit += caster->GetFocusEffect(focusIncreaseNumHits, spell_id);
|
||||
buffs[buffslot].numhits = numhit;
|
||||
}
|
||||
|
||||
if (!IsPowerDistModSpell(spell_id))
|
||||
SetSpellPowerDistanceMod(0);
|
||||
@@ -786,6 +790,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
CastToClient()->AI_Start();
|
||||
} else if(IsNPC()) {
|
||||
CastToNPC()->SetPetSpellID(0); //not a pet spell.
|
||||
CastToNPC()->ModifyStatsOnCharm(false);
|
||||
}
|
||||
|
||||
bool bBreak = false;
|
||||
@@ -908,16 +913,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
action->source = caster ? caster->GetID() : GetID();
|
||||
action->level = 65;
|
||||
action->instrument_mod = 10;
|
||||
action->sequence = static_cast<uint32>((GetHeading() * 12345 / 2));
|
||||
action->hit_heading = GetHeading();
|
||||
action->type = 231;
|
||||
action->spell = spell_id;
|
||||
action->buff_unknown = 4;
|
||||
action->effect_flag = 4;
|
||||
|
||||
cd->target = action->target;
|
||||
cd->source = action->source;
|
||||
cd->type = action->type;
|
||||
cd->spellid = action->spell;
|
||||
cd->meleepush_xy = action->sequence;
|
||||
cd->hit_heading = action->hit_heading;
|
||||
|
||||
CastToClient()->QueuePacket(action_packet);
|
||||
if(caster && caster->IsClient() && caster != this)
|
||||
@@ -959,16 +964,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
action->source = caster ? caster->GetID() : GetID();
|
||||
action->level = 65;
|
||||
action->instrument_mod = 10;
|
||||
action->sequence = static_cast<uint32>((GetHeading() * 12345 / 2));
|
||||
action->hit_heading = GetHeading();
|
||||
action->type = 231;
|
||||
action->spell = spell_id;
|
||||
action->buff_unknown = 4;
|
||||
action->effect_flag = 4;
|
||||
|
||||
cd->target = action->target;
|
||||
cd->source = action->source;
|
||||
cd->type = action->type;
|
||||
cd->spellid = action->spell;
|
||||
cd->meleepush_xy = action->sequence;
|
||||
cd->hit_heading = action->hit_heading;
|
||||
|
||||
CastToClient()->QueuePacket(action_packet);
|
||||
if(caster->IsClient() && caster != this)
|
||||
@@ -997,16 +1002,16 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
action->source = caster ? caster->GetID() : GetID();
|
||||
action->level = 65;
|
||||
action->instrument_mod = 10;
|
||||
action->sequence = static_cast<uint32>((GetHeading() * 12345 / 2));
|
||||
action->hit_heading = GetHeading();
|
||||
action->type = 231;
|
||||
action->spell = spell_id;
|
||||
action->buff_unknown = 4;
|
||||
action->effect_flag = 4;
|
||||
|
||||
cd->target = action->target;
|
||||
cd->source = action->source;
|
||||
cd->type = action->type;
|
||||
cd->spellid = action->spell;
|
||||
cd->meleepush_xy = action->sequence;
|
||||
cd->hit_heading = action->hit_heading;
|
||||
|
||||
CastToClient()->QueuePacket(action_packet);
|
||||
if(caster->IsClient() && caster != this)
|
||||
@@ -1620,13 +1625,14 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
break;
|
||||
|
||||
if(IsClient()) {
|
||||
CastToClient()->SetHorseId(0); // dismount if have horse
|
||||
|
||||
if (zone->random.Int(0, 99) > spells[spell_id].base[i]) {
|
||||
CastToClient()->SetFeigned(false);
|
||||
entity_list.MessageClose_StringID(this, false, 200, 10, STRING_FEIGNFAILED, GetName());
|
||||
}
|
||||
else
|
||||
} else {
|
||||
CastToClient()->SetFeigned(true);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1792,8 +1798,12 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
Message_StringID(4, CORPSE_CANT_SENSE);
|
||||
}
|
||||
}
|
||||
else if (caster)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_LEVEL_REQ);
|
||||
else if (caster) {
|
||||
char level[4];
|
||||
ConvertArray(effect_value, level);
|
||||
caster->Message_StringID(MT_SpellFailure,
|
||||
SPELL_LEVEL_REQ, level);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Message_StringID(4, TARGET_NOT_FOUND);
|
||||
@@ -2065,61 +2075,9 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Toss Up: %d", effect_value);
|
||||
#endif
|
||||
double toss_amt = (double)spells[spell_id].base[i];
|
||||
if(toss_amt < 0)
|
||||
toss_amt = -toss_amt;
|
||||
|
||||
if(IsNPC())
|
||||
{
|
||||
Stun(static_cast<int>(toss_amt));
|
||||
if (IsNPC()) {
|
||||
Damage(caster, std::abs(effect_value), spell_id, spell.skill, false, buffslot, false);
|
||||
}
|
||||
toss_amt = sqrt(toss_amt)-2.0;
|
||||
|
||||
if(toss_amt < 0.0)
|
||||
toss_amt = 0.0;
|
||||
|
||||
if(toss_amt > 20.0)
|
||||
toss_amt = 20.0;
|
||||
|
||||
if(IsClient())
|
||||
{
|
||||
CastToClient()->SetKnockBackExemption(true);
|
||||
}
|
||||
|
||||
double look_heading = GetHeading();
|
||||
look_heading /= 256;
|
||||
look_heading *= 360;
|
||||
look_heading += 180;
|
||||
if(look_heading > 360)
|
||||
look_heading -= 360;
|
||||
|
||||
//x and y are crossed mkay
|
||||
double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0));
|
||||
double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0));
|
||||
|
||||
auto outapp_push =
|
||||
new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
||||
|
||||
spu->spawn_id = GetID();
|
||||
spu->x_pos = FloatToEQ19(GetX());
|
||||
spu->y_pos = FloatToEQ19(GetY());
|
||||
spu->z_pos = FloatToEQ19(GetZ());
|
||||
spu->delta_x = NewFloatToEQ13(new_x);
|
||||
spu->delta_y = NewFloatToEQ13(new_y);
|
||||
spu->delta_z = NewFloatToEQ13(toss_amt);
|
||||
spu->heading = FloatToEQ19(GetHeading());
|
||||
spu->padding0002 =0;
|
||||
spu->padding0006 =7;
|
||||
spu->padding0014 =0x7f;
|
||||
spu->padding0018 =0x5df27;
|
||||
spu->animation = 0;
|
||||
spu->delta_heading = NewFloatToEQ13(0);
|
||||
outapp_push->priority = 5;
|
||||
entity_list.QueueClients(this, outapp_push, true);
|
||||
if(IsClient())
|
||||
CastToClient()->FastQueuePacket(&outapp_push);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2644,7 +2602,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
|
||||
float new_ground = GetGroundZ(my_x, my_y);
|
||||
|
||||
if(caster->IsClient())
|
||||
caster->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), my_x, my_y, new_ground, GetHeading()*2);
|
||||
caster->CastToClient()->MovePC(zone->GetZoneID(), zone->GetInstanceID(), my_x, my_y, new_ground, GetHeading());
|
||||
else
|
||||
caster->GMMove(my_x, my_y, new_ground, GetHeading());
|
||||
}
|
||||
@@ -3656,16 +3614,6 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Hunger: {
|
||||
// this procedure gets called 7 times for every once that the stamina update occurs so we add
|
||||
// 1/7 of the subtraction.
|
||||
// It's far from perfect, but works without any unnecessary buff checks to bog down the server.
|
||||
if (IsClient()) {
|
||||
CastToClient()->m_pp.hunger_level += 5;
|
||||
CastToClient()->m_pp.thirst_level += 5;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SE_Invisibility:
|
||||
case SE_InvisVsAnimals:
|
||||
case SE_InvisVsUndead: {
|
||||
@@ -3711,7 +3659,7 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
|
||||
case SE_CastOnFadeEffectNPC:
|
||||
case SE_CastOnFadeEffectAlways: {
|
||||
if (buff.ticsremaining == 0) {
|
||||
SpellOnTarget(spells[buff.spellid].base[i], this);
|
||||
SpellFinished(spells[buff.spellid].base[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base[i]].ResistDiff);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3968,6 +3916,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
|
||||
if(IsNPC())
|
||||
{
|
||||
CastToNPC()->RestoreGuardSpotCharm();
|
||||
CastToNPC()->ModifyStatsOnCharm(true);
|
||||
}
|
||||
|
||||
SendAppearancePacket(AT_Pet, 0, true, true);
|
||||
|
||||
+124
-170
@@ -81,6 +81,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
#include "quest_parser_collection.h"
|
||||
#include "string_ids.h"
|
||||
#include "worldserver.h"
|
||||
#include "fastmath.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
@@ -104,6 +105,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
extern Zone* zone;
|
||||
extern volatile bool is_zone_loaded;
|
||||
extern WorldServer worldserver;
|
||||
extern FastMath g_Math;
|
||||
|
||||
using EQEmu::CastingSlot;
|
||||
|
||||
@@ -1208,7 +1210,10 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
|
||||
// handle the components for traditional casters
|
||||
else {
|
||||
if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found
|
||||
if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) {
|
||||
//bypass reagent cost
|
||||
}
|
||||
else if(c->GetInv().HasItem(component, component_count, invWhereWorn|invWherePersonal) == -1) // item not found
|
||||
{
|
||||
if (!missingreags)
|
||||
{
|
||||
@@ -1238,6 +1243,9 @@ void Mob::CastedSpellFinished(uint16 spell_id, uint32 target_id, CastingSlot slo
|
||||
return;
|
||||
}
|
||||
}
|
||||
else if (!RuleB(Character, PetsUseReagents) && IsEffectInSpell(spell_id, SE_SummonPet)) {
|
||||
//bypass reagent cost
|
||||
}
|
||||
else if (!bard_song_mode)
|
||||
{
|
||||
int noexpend;
|
||||
@@ -2646,20 +2654,18 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
|
||||
action->source = caster->GetID();
|
||||
action->target = GetID();
|
||||
action->spell = spell_id;
|
||||
action->sequence = (uint32) (GetHeading() * 2); // just some random number
|
||||
action->force = spells[spell_id].pushback;
|
||||
action->hit_heading = GetHeading();
|
||||
action->hit_pitch = spells[spell_id].pushup;
|
||||
action->instrument_mod = caster->GetInstrumentMod(spell_id);
|
||||
action->buff_unknown = 0;
|
||||
action->level = buffs[buffs_i].casterlevel;
|
||||
action->effect_flag = 0;
|
||||
action->spell_level = action->level = buffs[buffs_i].casterlevel;
|
||||
action->type = DamageTypeSpell;
|
||||
entity_list.QueueCloseClients(this, packet, false, RuleI(Range, SongMessages), 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells);
|
||||
|
||||
action->buff_unknown = 4;
|
||||
action->effect_flag = 4;
|
||||
|
||||
if(IsEffectInSpell(spell_id, SE_TossUp))
|
||||
{
|
||||
action->buff_unknown = 0;
|
||||
}
|
||||
else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0)
|
||||
if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
|
||||
{
|
||||
if(IsClient())
|
||||
{
|
||||
@@ -2667,38 +2673,6 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
|
||||
{
|
||||
CastToClient()->SetKnockBackExemption(true);
|
||||
|
||||
action->buff_unknown = 0;
|
||||
auto outapp_push = new EQApplicationPacket(
|
||||
OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
||||
|
||||
double look_heading = caster->CalculateHeadingToTarget(GetX(), GetY());
|
||||
look_heading /= 256;
|
||||
look_heading *= 360;
|
||||
if(look_heading > 360)
|
||||
look_heading -= 360;
|
||||
|
||||
//x and y are crossed mkay
|
||||
double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0));
|
||||
double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0));
|
||||
|
||||
spu->spawn_id = GetID();
|
||||
spu->x_pos = FloatToEQ19(GetX());
|
||||
spu->y_pos = FloatToEQ19(GetY());
|
||||
spu->z_pos = FloatToEQ19(GetZ());
|
||||
spu->delta_x = NewFloatToEQ13(new_x);
|
||||
spu->delta_y = NewFloatToEQ13(new_y);
|
||||
spu->delta_z = NewFloatToEQ13(spells[spell_id].pushup);
|
||||
spu->heading = FloatToEQ19(GetHeading());
|
||||
spu->padding0002 =0;
|
||||
spu->padding0006 =7;
|
||||
spu->padding0014 =0x7f;
|
||||
spu->padding0018 =0x5df27;
|
||||
spu->animation = 0;
|
||||
spu->delta_heading = NewFloatToEQ13(0);
|
||||
outapp_push->priority = 6;
|
||||
entity_list.QueueClients(this, outapp_push, true);
|
||||
CastToClient()->FastQueuePacket(&outapp_push);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2719,7 +2693,9 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
|
||||
cd->source = action->source;
|
||||
cd->type = DamageTypeSpell;
|
||||
cd->spellid = action->spell;
|
||||
cd->meleepush_xy = action->sequence;
|
||||
cd->force = action->force;
|
||||
cd->hit_heading = action->hit_heading;
|
||||
cd->hit_pitch = action->hit_pitch;
|
||||
cd->damage = 0;
|
||||
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
|
||||
{
|
||||
@@ -3190,6 +3166,12 @@ uint32 Client::GetLastBuffSlot(bool disc, bool song)
|
||||
return GetCurrentBuffSlots();
|
||||
}
|
||||
|
||||
bool Mob::HasDiscBuff()
|
||||
{
|
||||
int slot = GetFirstBuffSlot(true, false);
|
||||
return buffs[slot].spellid != SPELL_UNKNOWN;
|
||||
}
|
||||
|
||||
// returns the slot the buff was added to, -1 if it wasn't added due to
|
||||
// stacking problems, and -2 if this is not a buff
|
||||
// if caster is null, the buff will be added with the caster level being
|
||||
@@ -3309,8 +3291,8 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
|
||||
|
||||
buffs[emptyslot].spellid = spell_id;
|
||||
buffs[emptyslot].casterlevel = caster_level;
|
||||
if (caster && caster->IsClient())
|
||||
strcpy(buffs[emptyslot].caster_name, caster->GetName());
|
||||
if (caster && !caster->IsAura()) // maybe some other things we don't want to ...
|
||||
strcpy(buffs[emptyslot].caster_name, caster->GetCleanName());
|
||||
else
|
||||
memset(buffs[emptyslot].caster_name, 0, 64);
|
||||
buffs[emptyslot].casterid = caster ? caster->GetID() : 0;
|
||||
@@ -3521,12 +3503,14 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
action->target = spelltar->GetID();
|
||||
}
|
||||
|
||||
action->level = caster_level; // caster level, for animation only
|
||||
action->spell_level = action->level = caster_level; // caster level, for animation only
|
||||
action->type = 231; // 231 means a spell
|
||||
action->spell = spell_id;
|
||||
action->sequence = (uint32) (GetHeading() * 2); // just some random number
|
||||
action->force = spells[spell_id].pushback;
|
||||
action->hit_heading = GetHeading();
|
||||
action->hit_pitch = spells[spell_id].pushup;
|
||||
action->instrument_mod = GetInstrumentMod(spell_id);
|
||||
action->buff_unknown = 0;
|
||||
action->effect_flag = 0;
|
||||
|
||||
if(spelltar != this && spelltar->IsClient()) // send to target
|
||||
spelltar->CastToClient()->QueuePacket(action_packet);
|
||||
@@ -3953,53 +3937,21 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
// NOTE: this is what causes the buff icon to appear on the client, if
|
||||
// this is a buff - but it sortof relies on the first packet.
|
||||
// the complete sequence is 2 actions and 1 damage message
|
||||
action->buff_unknown = 0x04; // this is a success flag
|
||||
action->effect_flag = 0x04; // this is a success flag
|
||||
|
||||
if(IsEffectInSpell(spell_id, SE_TossUp))
|
||||
{
|
||||
action->buff_unknown = 0;
|
||||
}
|
||||
else if(spells[spell_id].pushback > 0 || spells[spell_id].pushup > 0)
|
||||
if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
|
||||
{
|
||||
if(spelltar->IsClient())
|
||||
{
|
||||
if(!IsBuffSpell(spell_id))
|
||||
{
|
||||
spelltar->CastToClient()->SetKnockBackExemption(true);
|
||||
|
||||
action->buff_unknown = 0;
|
||||
auto outapp_push =
|
||||
new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
|
||||
PlayerPositionUpdateServer_Struct* spu = (PlayerPositionUpdateServer_Struct*)outapp_push->pBuffer;
|
||||
|
||||
double look_heading = CalculateHeadingToTarget(spelltar->GetX(), spelltar->GetY());
|
||||
look_heading /= 256;
|
||||
look_heading *= 360;
|
||||
if(look_heading > 360)
|
||||
look_heading -= 360;
|
||||
|
||||
//x and y are crossed mkay
|
||||
double new_x = spells[spell_id].pushback * sin(double(look_heading * 3.141592 / 180.0));
|
||||
double new_y = spells[spell_id].pushback * cos(double(look_heading * 3.141592 / 180.0));
|
||||
|
||||
spu->spawn_id = spelltar->GetID();
|
||||
spu->x_pos = FloatToEQ19(spelltar->GetX());
|
||||
spu->y_pos = FloatToEQ19(spelltar->GetY());
|
||||
spu->z_pos = FloatToEQ19(spelltar->GetZ());
|
||||
spu->delta_x = NewFloatToEQ13(new_x);
|
||||
spu->delta_y = NewFloatToEQ13(new_y);
|
||||
spu->delta_z = NewFloatToEQ13(spells[spell_id].pushup);
|
||||
spu->heading = FloatToEQ19(spelltar->GetHeading());
|
||||
spu->padding0002 =0;
|
||||
spu->padding0006 =7;
|
||||
spu->padding0014 =0x7f;
|
||||
spu->padding0018 =0x5df27;
|
||||
spu->animation = 0;
|
||||
spu->delta_heading = NewFloatToEQ13(0);
|
||||
outapp_push->priority = 6;
|
||||
entity_list.QueueClients(this, outapp_push, true);
|
||||
spelltar->CastToClient()->FastQueuePacket(&outapp_push);
|
||||
}
|
||||
} else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) {
|
||||
spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading);
|
||||
spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading);
|
||||
spelltar->m_Delta.z += action->hit_pitch;
|
||||
spelltar->ForcedMovement = 6;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4027,7 +3979,9 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
|
||||
cd->source = action->source;
|
||||
cd->type = action->type;
|
||||
cd->spellid = action->spell;
|
||||
cd->meleepush_xy = action->sequence;
|
||||
cd->force = action->force;
|
||||
cd->hit_heading = action->hit_heading;
|
||||
cd->hit_pitch = action->hit_pitch;
|
||||
cd->damage = 0;
|
||||
if(!IsEffectInSpell(spell_id, SE_BindAffinity)){
|
||||
entity_list.QueueCloseClients(
|
||||
@@ -4234,6 +4188,19 @@ bool Mob::IsAffectedByBuff(uint16 spell_id)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::IsAffectedByBuffByGlobalGroup(GlobalGroup group)
|
||||
{
|
||||
int buff_count = GetMaxTotalSlots();
|
||||
for (int i = 0; i < buff_count; ++i) {
|
||||
if (buffs[i].spellid == SPELL_UNKNOWN)
|
||||
continue;
|
||||
if (spells[buffs[i].spellid].spell_category == static_cast<int>(group))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// checks if 'this' can be affected by spell_id from caster
|
||||
// returns true if the spell should fail, false otherwise
|
||||
bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||
@@ -4418,6 +4385,36 @@ bool Mob::IsImmuneToSpell(uint16 spell_id, Mob *caster)
|
||||
return false;
|
||||
}
|
||||
|
||||
int Mob::GetResist(uint8 resist_type)
|
||||
{
|
||||
switch(resist_type)
|
||||
{
|
||||
case RESIST_FIRE:
|
||||
return GetFR();
|
||||
case RESIST_COLD:
|
||||
return GetCR();
|
||||
case RESIST_MAGIC:
|
||||
return GetMR();
|
||||
case RESIST_DISEASE:
|
||||
return GetDR();
|
||||
case RESIST_POISON:
|
||||
return GetPR();
|
||||
case RESIST_CORRUPTION:
|
||||
return GetCorrup();
|
||||
case RESIST_PRISMATIC:
|
||||
return (GetFR() + GetCR() + GetMR() + GetDR() + GetPR()) / 5;
|
||||
case RESIST_CHROMATIC:
|
||||
return std::min({GetFR(), GetCR(), GetMR(), GetDR(), GetPR()});
|
||||
case RESIST_PHYSICAL:
|
||||
if (IsNPC())
|
||||
return GetPhR();
|
||||
else
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Spell resists:
|
||||
// returns an effectiveness index from 0 to 100. for most spells, 100 means
|
||||
@@ -4501,68 +4498,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
return 100;
|
||||
}
|
||||
|
||||
int target_resist;
|
||||
switch(resist_type)
|
||||
{
|
||||
case RESIST_FIRE:
|
||||
target_resist = GetFR();
|
||||
break;
|
||||
case RESIST_COLD:
|
||||
target_resist = GetCR();
|
||||
break;
|
||||
case RESIST_MAGIC:
|
||||
target_resist = GetMR();
|
||||
break;
|
||||
case RESIST_DISEASE:
|
||||
target_resist = GetDR();
|
||||
break;
|
||||
case RESIST_POISON:
|
||||
target_resist = GetPR();
|
||||
break;
|
||||
case RESIST_CORRUPTION:
|
||||
target_resist = GetCorrup();
|
||||
break;
|
||||
case RESIST_PRISMATIC:
|
||||
target_resist = (GetFR() + GetCR() + GetMR() + GetDR() + GetPR()) / 5;
|
||||
break;
|
||||
case RESIST_CHROMATIC:
|
||||
{
|
||||
target_resist = GetFR();
|
||||
int temp = GetCR();
|
||||
if(temp < target_resist)
|
||||
{
|
||||
target_resist = temp;
|
||||
}
|
||||
int target_resist = GetResist(resist_type);
|
||||
|
||||
temp = GetMR();
|
||||
if(temp < target_resist)
|
||||
{
|
||||
target_resist = temp;
|
||||
}
|
||||
|
||||
temp = GetDR();
|
||||
if(temp < target_resist)
|
||||
{
|
||||
target_resist = temp;
|
||||
}
|
||||
|
||||
temp = GetPR();
|
||||
if(temp < target_resist)
|
||||
{
|
||||
target_resist = temp;
|
||||
}
|
||||
// JULY 24, 2002 changes
|
||||
int level = GetLevel();
|
||||
if (IsPetOwnerClient() && caster->IsNPC() && !caster->IsPetOwnerClient()) {
|
||||
auto owner = GetOwner();
|
||||
if (owner != nullptr) {
|
||||
target_resist = std::max(target_resist, owner->GetResist(resist_type));
|
||||
level = owner->GetLevel();
|
||||
}
|
||||
break;
|
||||
case RESIST_PHYSICAL:
|
||||
{
|
||||
if (IsNPC())
|
||||
target_resist = GetPhR();
|
||||
else
|
||||
target_resist = 0;
|
||||
}
|
||||
default:
|
||||
|
||||
target_resist = 0;
|
||||
}
|
||||
|
||||
//Setup our base resist chance.
|
||||
@@ -4571,7 +4516,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
|
||||
//Adjust our resist chance based on level modifiers
|
||||
uint8 caster_level = level_override > 0 ? level_override : caster->GetLevel();
|
||||
int temp_level_diff = GetLevel() - caster_level;
|
||||
int temp_level_diff = level - caster_level;
|
||||
|
||||
//Physical Resists are calclated using their own formula derived from extensive parsing.
|
||||
if (resist_type == RESIST_PHYSICAL) {
|
||||
@@ -4580,7 +4525,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
|
||||
else {
|
||||
|
||||
if(IsNPC() && GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
if(IsNPC() && level >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
int a = (RuleI(Casting,ResistFalloff)-1) - caster_level;
|
||||
if(a > 0)
|
||||
@@ -4593,7 +4538,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
}
|
||||
|
||||
if(IsClient() && GetLevel() >= 21 && temp_level_diff > 15)
|
||||
if(IsClient() && level >= 21 && temp_level_diff > 15)
|
||||
{
|
||||
temp_level_diff = 15;
|
||||
}
|
||||
@@ -4609,16 +4554,16 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
level_mod = -level_mod;
|
||||
}
|
||||
|
||||
if(IsNPC() && (caster_level - GetLevel()) < -20)
|
||||
if(IsNPC() && (caster_level - level) < -20)
|
||||
{
|
||||
level_mod = 1000;
|
||||
}
|
||||
|
||||
//Even more level stuff this time dealing with damage spells
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && GetLevel() >= 17)
|
||||
if(IsNPC() && IsDamageSpell(spell_id) && level >= 17)
|
||||
{
|
||||
int level_diff;
|
||||
if(GetLevel() >= RuleI(Casting,ResistFalloff))
|
||||
if(level >= RuleI(Casting,ResistFalloff))
|
||||
{
|
||||
level_diff = (RuleI(Casting,ResistFalloff)-1) - caster_level;
|
||||
if(level_diff < 0)
|
||||
@@ -4628,7 +4573,7 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
}
|
||||
else
|
||||
{
|
||||
level_diff = GetLevel() - caster_level;
|
||||
level_diff = level - caster_level;
|
||||
}
|
||||
level_mod += (2 * level_diff);
|
||||
}
|
||||
@@ -4739,17 +4684,17 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
|
||||
if(IsNPC())
|
||||
{
|
||||
if(GetLevel() > caster_level && GetLevel() >= 17 && caster_level <= 50)
|
||||
if(level > caster_level && level >= 17 && caster_level <= 50)
|
||||
{
|
||||
partial_modifier += 5;
|
||||
}
|
||||
|
||||
if(GetLevel() >= 30 && caster_level < 50)
|
||||
if(level >= 30 && caster_level < 50)
|
||||
{
|
||||
partial_modifier += (caster_level - 25);
|
||||
}
|
||||
|
||||
if(GetLevel() < 15)
|
||||
if(level < 15)
|
||||
{
|
||||
partial_modifier -= 5;
|
||||
}
|
||||
@@ -4757,9 +4702,9 @@ float Mob::ResistSpell(uint8 resist_type, uint16 spell_id, Mob *caster, bool use
|
||||
|
||||
if(caster->IsNPC())
|
||||
{
|
||||
if((GetLevel() - caster_level) >= 20)
|
||||
if((level - caster_level) >= 20)
|
||||
{
|
||||
partial_modifier += (GetLevel() - caster_level) * 1.5;
|
||||
partial_modifier += (level - caster_level) * 1.5;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5535,6 +5480,8 @@ void Client::SendBuffNumHitPacket(Buffs_Struct &buff, int slot)
|
||||
bi->entries[0].spell_id = buff.spellid;
|
||||
bi->entries[0].tics_remaining = buff.ticsremaining;
|
||||
bi->entries[0].num_hits = buff.numhits;
|
||||
strn0cpy(bi->entries[0].caster, buff.caster_name, 64);
|
||||
bi->name_lengths = strlen(bi->entries[0].caster);
|
||||
FastQueuePacket(&outapp);
|
||||
}
|
||||
|
||||
@@ -5620,6 +5567,7 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
|
||||
else
|
||||
buff->type = 0;
|
||||
|
||||
buff->name_lengths = 0; // hacky shit
|
||||
uint32 index = 0;
|
||||
for(int i = 0; i < buff_count; ++i)
|
||||
{
|
||||
@@ -5629,6 +5577,8 @@ EQApplicationPacket *Mob::MakeBuffsPacket(bool for_target)
|
||||
buff->entries[index].spell_id = buffs[i].spellid;
|
||||
buff->entries[index].tics_remaining = buffs[i].ticsremaining;
|
||||
buff->entries[index].num_hits = buffs[i].numhits;
|
||||
strn0cpy(buff->entries[index].caster, buffs[i].caster_name, 64);
|
||||
buff->name_lengths += strlen(buff->entries[index].caster);
|
||||
++index;
|
||||
}
|
||||
}
|
||||
@@ -5714,7 +5664,7 @@ void Client::SendSpellAnim(uint16 targetid, uint16 spell_id)
|
||||
a->source = this->GetID();
|
||||
a->type = 231;
|
||||
a->spell = spell_id;
|
||||
a->sequence = 231;
|
||||
a->hit_heading = GetHeading();
|
||||
|
||||
app.priority = 1;
|
||||
entity_list.QueueCloseClients(this, &app, false, RuleI(Range, SpellParticles));
|
||||
@@ -5725,8 +5675,8 @@ void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, flo
|
||||
if (!distance) { return; }
|
||||
if (!MaxZDiff) { MaxZDiff = 5; }
|
||||
|
||||
float ReverseHeading = 256 - heading;
|
||||
float ConvertAngle = ReverseHeading * 1.40625f;
|
||||
float ReverseHeading = 512 - heading;
|
||||
float ConvertAngle = ReverseHeading * 360.0f / 512.0f;
|
||||
if (ConvertAngle <= 270)
|
||||
ConvertAngle = ConvertAngle + 90;
|
||||
else
|
||||
@@ -5734,8 +5684,8 @@ void Mob::CalcDestFromHeading(float heading, float distance, float MaxZDiff, flo
|
||||
|
||||
float Radian = ConvertAngle * (3.1415927f / 180.0f);
|
||||
|
||||
float CircleX = distance * cos(Radian);
|
||||
float CircleY = distance * sin(Radian);
|
||||
float CircleX = distance * std::cos(Radian);
|
||||
float CircleY = distance * std::sin(Radian);
|
||||
dX = CircleX + StartX;
|
||||
dY = CircleY + StartY;
|
||||
dZ = FindGroundZ(dX, dY, MaxZDiff);
|
||||
@@ -5800,7 +5750,8 @@ void Mob::BeamDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
maxtarget_count++;
|
||||
}
|
||||
|
||||
if (maxtarget_count >= spells[spell_id].aemaxtargets)
|
||||
// not sure if we need this check, but probably do, need to check if it should be default limited or not
|
||||
if (spells[spell_id].aemaxtargets && maxtarget_count >= spells[spell_id].aemaxtargets)
|
||||
return;
|
||||
}
|
||||
++iter;
|
||||
@@ -5815,8 +5766,10 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
if (IsBeneficialSpell(spell_id) && IsClient())
|
||||
beneficial_targets = true;
|
||||
|
||||
float angle_start = spells[spell_id].directional_start + (GetHeading() * 360.0f / 256.0f);
|
||||
float angle_end = spells[spell_id].directional_end + (GetHeading() * 360.0f / 256.0f);
|
||||
float heading = GetHeading() * 360.0f / 512.0f; // convert to degrees
|
||||
|
||||
float angle_start = spells[spell_id].directional_start + heading;
|
||||
float angle_end = spells[spell_id].directional_end + heading;
|
||||
|
||||
while (angle_start > 360.0f)
|
||||
angle_start -= 360.0f;
|
||||
@@ -5837,7 +5790,7 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
}
|
||||
|
||||
float heading_to_target =
|
||||
(CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 256.0f);
|
||||
(CalculateHeadingToTarget((*iter)->GetX(), (*iter)->GetY()) * 360.0f / 512.0f);
|
||||
|
||||
while (heading_to_target < 0.0f)
|
||||
heading_to_target += 360.0f;
|
||||
@@ -5881,7 +5834,8 @@ void Mob::ConeDirectional(uint16 spell_id, int16 resist_adjust)
|
||||
}
|
||||
}
|
||||
|
||||
if (maxtarget_count >= spells[spell_id].aemaxtargets)
|
||||
// my SHM breath could hit all 5 dummies I could summon in arena
|
||||
if (spells[spell_id].aemaxtargets && maxtarget_count >= spells[spell_id].aemaxtargets)
|
||||
return;
|
||||
|
||||
++iter;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#define PROC_TOOLOW 126 //Your will is not sufficient to command this weapon.
|
||||
#define PROC_PETTOOLOW 127 //Your pet's will is not sufficient to command its weapon.
|
||||
#define YOU_FLURRY 128 //You unleash a flurry of attacks.
|
||||
#define FAILED_DISARM_TRAP 129 //You failed to disarm the trap.
|
||||
#define DOORS_LOCKED 130 //It's locked and you're not holding the key.
|
||||
#define DOORS_CANT_PICK 131 //This lock cannot be picked.
|
||||
#define DOORS_INSUFFICIENT_SKILL 132 //You are not sufficiently skilled to pick this lock.
|
||||
@@ -98,6 +99,7 @@
|
||||
#define DUP_LORE 290 //Duplicate lore items are not allowed.
|
||||
#define TGB_ON 293 //Target other group buff is *ON*.
|
||||
#define TGB_OFF 294 //Target other group buff is *OFF*.
|
||||
#define DISARMED_TRAP 305 //You have disarmed the trap.
|
||||
#define LDON_SENSE_TRAP1 306 //You do not Sense any traps.
|
||||
#define TRADESKILL_NOCOMBINE 334 //You cannot combine these items in this container type!
|
||||
#define TRADESKILL_FAILED 336 //You lacked the skills to fashion the items together.
|
||||
@@ -114,8 +116,11 @@
|
||||
#define MEND_WORSEN 351 //You have worsened your wounds!
|
||||
#define MEND_FAIL 352 //You have failed to mend your wounds.
|
||||
#define LDON_SENSE_TRAP2 367 //You have not detected any traps.
|
||||
#define TRAP_TOO_FAR 368 //You are too far away from that trap to affect it.
|
||||
#define FAIL_DISARM_DETECTED_TRAP 370 //You fail to disarm the detected trap.
|
||||
#define LOOT_LORE_ERROR 371 //You cannot loot this Lore Item. You already have one.
|
||||
#define PICK_LORE 379 //You cannot pick up a lore item you already possess.
|
||||
#define POISON_TOO_HIGH 382 // This poison is too high level for you to apply.
|
||||
#define CONSENT_DENIED 390 //You do not have consent to summon that corpse.
|
||||
#define DISCIPLINE_RDY 393 //You are ready to use a new discipline now.
|
||||
#define CONSENT_INVALID_NAME 397 //Not a valid consent name.
|
||||
@@ -421,6 +426,7 @@
|
||||
#define SENSE_ANIMAL 12472 //You sense an animal in this direction.
|
||||
#define SENSE_SUMMONED 12473 //You sense a summoned being in this direction.
|
||||
#define SENSE_NOTHING 12474 //You don't sense anything.
|
||||
#define SENSE_TRAP 12475 //You sense a trap in this direction.
|
||||
#define LDON_SENSE_TRAP3 12476 //You don't sense any traps.
|
||||
#define INTERRUPT_SPELL_OTHER 12478 //%1's casting is interrupted!
|
||||
#define YOU_HIT_NONMELEE 12481 //You were hit by non-melee for %1 damage.
|
||||
|
||||
+1
-2
@@ -2804,8 +2804,7 @@ void TaskManager::SendActiveTaskDescription(Client *c, int TaskID, int SequenceN
|
||||
if (strlen(Tasks[TaskID]->Reward) != 0)
|
||||
linker.SetProxyText(Tasks[TaskID]->Reward);
|
||||
|
||||
auto reward_link = linker.GenerateLink();
|
||||
reward_text.append(reward_link);
|
||||
reward_text.append(linker.GenerateLink());
|
||||
}
|
||||
else {
|
||||
reward_text.append(Tasks[TaskID]->Reward);
|
||||
|
||||
+5
-3
@@ -523,7 +523,6 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
else
|
||||
qs_audit->char1_count += detail->charges;
|
||||
|
||||
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
|
||||
for (uint8 sub_slot = EQEmu::inventory::containerBegin; (sub_slot < EQEmu::inventory::ContainerCount); ++sub_slot) { // this is to catch ALL items
|
||||
const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
@@ -743,7 +742,6 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
qs_audit->char1_count += detail->charges;
|
||||
|
||||
// 'step 3' should never really see containers..but, just in case...
|
||||
//for (uint8 sub_slot = SUB_BEGIN; ((sub_slot < inst->GetItem()->BagSlots) && (sub_slot < EmuConstants::ITEM_CONTAINER_SIZE)); ++sub_slot) {
|
||||
for (uint8 sub_slot = EQEmu::inventory::containerBegin; (sub_slot < EQEmu::inventory::ContainerCount); ++sub_slot) { // this is to catch ALL items
|
||||
const EQEmu::ItemInstance* bag_inst = inst->GetItem(sub_slot);
|
||||
|
||||
@@ -888,8 +886,12 @@ void Client::FinishTrade(Mob* tradingWith, bool finalizer, void* event_entry, st
|
||||
|
||||
const EQEmu::ItemData* item = inst->GetItem();
|
||||
if(item && quest_npc == false) {
|
||||
bool isPetAndCanHaveNoDrop = (RuleB(Pets, CanTakeNoDrop) &&
|
||||
_CLIENTPET(tradingWith) &&
|
||||
tradingWith->GetPetType()<=petOther);
|
||||
// if it was not a NO DROP or Attuned item (or if a GM is trading), let the NPC have it
|
||||
if(GetGM() || (item->NoDrop != 0 && inst->IsAttuned() == false)) {
|
||||
if(GetGM() || (inst->IsAttuned() == false &&
|
||||
(item->NoDrop != 0 || isPetAndCanHaveNoDrop))) {
|
||||
// pets need to look inside bags and try to equip items found there
|
||||
if (item->IsClassBag() && item->BagSlots > 0) {
|
||||
for (int16 bslot = EQEmu::inventory::containerBegin; bslot < item->BagSlots; bslot++) {
|
||||
|
||||
+269
-27
@@ -52,10 +52,12 @@ CREATE TABLE traps (
|
||||
Trap::Trap() :
|
||||
Entity(),
|
||||
respawn_timer(600000),
|
||||
chkarea_timer(500),
|
||||
chkarea_timer(1000),
|
||||
reset_timer(5000),
|
||||
m_Position(glm::vec3())
|
||||
{
|
||||
trap_id = 0;
|
||||
db_id = 0;
|
||||
maxzdiff = 0;
|
||||
radius = 0;
|
||||
effect = 0;
|
||||
@@ -64,12 +66,20 @@ Trap::Trap() :
|
||||
skill = 0;
|
||||
level = 0;
|
||||
respawn_timer.Disable();
|
||||
reset_timer.Disable();
|
||||
detected = false;
|
||||
disarmed = false;
|
||||
respawn_time = 0;
|
||||
respawn_var = 0;
|
||||
hiddenTrigger = nullptr;
|
||||
ownHiddenTrigger = false;
|
||||
chance = 0;
|
||||
triggered_number = 0;
|
||||
times_triggered = 0;
|
||||
group = 0;
|
||||
despawn_when_triggered = false;
|
||||
charid = 0;
|
||||
undetectable = false;
|
||||
}
|
||||
|
||||
Trap::~Trap()
|
||||
@@ -80,8 +90,7 @@ Trap::~Trap()
|
||||
|
||||
bool Trap::Process()
|
||||
{
|
||||
if (chkarea_timer.Enabled() && chkarea_timer.Check()
|
||||
/*&& zone->GetClientCount() > 0*/ )
|
||||
if (chkarea_timer.Enabled() && chkarea_timer.Check() && !reset_timer.Enabled())
|
||||
{
|
||||
Mob* trigger = entity_list.GetTrapTrigger(this);
|
||||
if (trigger && !(trigger->IsClient() && trigger->CastToClient()->GetGM()))
|
||||
@@ -89,6 +98,13 @@ bool Trap::Process()
|
||||
Trigger(trigger);
|
||||
}
|
||||
}
|
||||
else if (reset_timer.Enabled() && reset_timer.Check())
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Reset timer disabled in Reset Check Process for trap %d.", trap_id);
|
||||
reset_timer.Disable();
|
||||
charid = 0;
|
||||
}
|
||||
|
||||
if (respawn_timer.Enabled() && respawn_timer.Check())
|
||||
{
|
||||
detected = false;
|
||||
@@ -96,11 +112,15 @@ bool Trap::Process()
|
||||
chkarea_timer.Enable();
|
||||
respawn_timer.Disable();
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void Trap::Trigger(Mob* trigger)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Trap %d triggered by %s for the %d time!", trap_id, trigger->GetName(), times_triggered + 1);
|
||||
|
||||
int i = 0;
|
||||
const NPCType* tmp = 0;
|
||||
switch (effect)
|
||||
@@ -128,7 +148,7 @@ void Trap::Trigger(Mob* trigger)
|
||||
entity_list.MessageClose(trigger,false,effectvalue,13,"%s",message.c_str());
|
||||
}
|
||||
|
||||
entity_list.SendAlarm(this,trigger,effectvalue);
|
||||
entity_list.SendAlarm(this,trigger, effectvalue2);
|
||||
break;
|
||||
case trapTypeMysticSpawn:
|
||||
if (message.empty())
|
||||
@@ -148,6 +168,8 @@ void Trap::Trigger(Mob* trigger)
|
||||
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
|
||||
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
|
||||
new_npc->AddLootTable();
|
||||
if (new_npc->DropsGlobalLoot())
|
||||
new_npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(new_npc);
|
||||
new_npc->AddToHateList(trigger,1);
|
||||
}
|
||||
@@ -171,6 +193,8 @@ void Trap::Trigger(Mob* trigger)
|
||||
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
|
||||
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
|
||||
new_npc->AddLootTable();
|
||||
if (new_npc->DropsGlobalLoot())
|
||||
new_npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(new_npc);
|
||||
new_npc->AddToHateList(trigger,1);
|
||||
}
|
||||
@@ -192,7 +216,7 @@ void Trap::Trigger(Mob* trigger)
|
||||
int dmg = zone->random.Int(effectvalue, effectvalue2);
|
||||
trigger->SetHP(trigger->GetHP() - dmg);
|
||||
a->damage = dmg;
|
||||
a->meleepush_xy = zone->random.Int(0, 1234567);
|
||||
a->hit_heading = 0.0f;
|
||||
a->source = GetHiddenTrigger()!=nullptr ? GetHiddenTrigger()->GetID() : trigger->GetID();
|
||||
a->spellid = 0;
|
||||
a->target = trigger->GetID();
|
||||
@@ -201,12 +225,41 @@ void Trap::Trigger(Mob* trigger)
|
||||
safe_delete(outapp);
|
||||
}
|
||||
}
|
||||
respawn_timer.Start((respawn_time + zone->random.Int(0, respawn_var)) * 1000);
|
||||
chkarea_timer.Disable();
|
||||
disarmed = true;
|
||||
|
||||
if (trigger && trigger->IsClient())
|
||||
{
|
||||
trigger->CastToClient()->trapid = trap_id;
|
||||
charid = trigger->CastToClient()->CharacterID();
|
||||
}
|
||||
|
||||
bool update = false;
|
||||
if (despawn_when_triggered)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Trap %d is despawning after being triggered.", trap_id);
|
||||
update = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
reset_timer.Start(5000);
|
||||
}
|
||||
|
||||
if (triggered_number > 0)
|
||||
++times_triggered;
|
||||
|
||||
if (triggered_number > 0 && triggered_number <= times_triggered)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Triggered number for trap %d reached. %d/%d", trap_id, times_triggered, triggered_number);
|
||||
update = true;
|
||||
}
|
||||
|
||||
if (update)
|
||||
{
|
||||
UpdateTrap();
|
||||
}
|
||||
}
|
||||
|
||||
Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) {
|
||||
Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist, float &trap_curdist, bool detected)
|
||||
{
|
||||
float dist = 999999;
|
||||
Trap* current_trap = nullptr;
|
||||
|
||||
@@ -215,61 +268,161 @@ Trap* EntityList::FindNearbyTrap(Mob* searcher, float max_dist) {
|
||||
|
||||
for (auto it = trap_list.begin(); it != trap_list.end(); ++it) {
|
||||
cur = it->second;
|
||||
if(cur->disarmed)
|
||||
if(cur->disarmed || (detected && !cur->detected) || cur->undetectable)
|
||||
continue;
|
||||
|
||||
auto diff = glm::vec3(searcher->GetPosition()) - cur->m_Position;
|
||||
float curdist = diff.x * diff.x + diff.y * diff.y + diff.z * diff.z;
|
||||
float curdist = diff.x*diff.x + diff.y*diff.y;
|
||||
diff.z = std::abs(diff.z);
|
||||
|
||||
if (curdist < max_dist2 && curdist < dist)
|
||||
if (curdist < max_dist2 && curdist < dist && diff.z <= cur->maxzdiff)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Trap %d is curdist %0.1f", cur->db_id, curdist);
|
||||
dist = curdist;
|
||||
current_trap = cur;
|
||||
}
|
||||
}
|
||||
|
||||
if (current_trap != nullptr)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "Trap %d is the closest trap.", current_trap->db_id);
|
||||
trap_curdist = dist;
|
||||
}
|
||||
else
|
||||
trap_curdist = INVALID_INDEX;
|
||||
|
||||
return current_trap;
|
||||
}
|
||||
|
||||
Mob* EntityList::GetTrapTrigger(Trap* trap) {
|
||||
Mob* savemob = 0;
|
||||
Mob* EntityList::GetTrapTrigger(Trap* trap)
|
||||
{
|
||||
|
||||
float maxdist = trap->radius * trap->radius;
|
||||
|
||||
for (auto it = client_list.begin(); it != client_list.end(); ++it) {
|
||||
for (auto it = client_list.begin(); it != client_list.end(); ++it)
|
||||
{
|
||||
Client* cur = it->second;
|
||||
|
||||
auto diff = glm::vec3(cur->GetPosition()) - trap->m_Position;
|
||||
diff.z = std::abs(diff.z);
|
||||
|
||||
if ((diff.x*diff.x + diff.y*diff.y) <= maxdist
|
||||
&& diff.z < trap->maxzdiff)
|
||||
&& diff.z <= trap->maxzdiff)
|
||||
{
|
||||
if (zone->random.Roll(trap->chance))
|
||||
return(cur);
|
||||
else
|
||||
savemob = cur;
|
||||
}
|
||||
//This prevents the trap from triggering on players while zoning.
|
||||
if (strcmp(cur->GetName(), "No name") == 0)
|
||||
continue;
|
||||
|
||||
if (cur->trapid == 0 && !cur->GetGM() && (trap->chance == 0 || zone->random.Roll(trap->chance)))
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "%s is about to trigger trap %d of chance %d. diff: %0.2f maxdist: %0.2f zdiff: %0.2f maxzdiff: %0.2f", cur->GetName(), trap->trap_id, trap->chance, (diff.x*diff.x + diff.y*diff.y), maxdist, diff.z, trap->maxzdiff);
|
||||
return cur;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (cur->trapid == trap->trap_id)
|
||||
{
|
||||
Log(Logs::General, Logs::Traps, "%s is clearing trapid for trap %d", cur->GetName(), trap->trap_id);
|
||||
cur->trapid = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return savemob;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//todo: rewrite this to not need direct access to trap members.
|
||||
bool EntityList::IsTrapGroupSpawned(uint32 trap_id, uint8 group)
|
||||
{
|
||||
auto it = trap_list.begin();
|
||||
while (it != trap_list.end())
|
||||
{
|
||||
Trap* cur = it->second;
|
||||
if (cur->IsTrap() && cur->group == group && cur->trap_id != trap_id)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void EntityList::UpdateAllTraps(bool respawn, bool repopnow)
|
||||
{
|
||||
auto it = trap_list.begin();
|
||||
while (it != trap_list.end())
|
||||
{
|
||||
Trap* cur = it->second;
|
||||
if (cur->IsTrap())
|
||||
{
|
||||
cur->UpdateTrap(respawn, repopnow);
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
Log(Logs::General, Logs::Traps, "All traps updated.");
|
||||
}
|
||||
|
||||
void EntityList::GetTrapInfo(Client* client)
|
||||
{
|
||||
uint8 count = 0;
|
||||
auto it = trap_list.begin();
|
||||
while (it != trap_list.end())
|
||||
{
|
||||
Trap* cur = it->second;
|
||||
if (cur->IsTrap())
|
||||
{
|
||||
bool isset = (cur->chkarea_timer.Enabled() && !cur->reset_timer.Enabled());
|
||||
client->Message(CC_Default, " Trap: (%d) found at %0.2f,%0.2f,%0.2f. Times Triggered: %d Is Active: %d Group: %d Message: %s", cur->trap_id, cur->m_Position.x, cur->m_Position.y, cur->m_Position.z, cur->times_triggered, isset, cur->group, cur->message.c_str());
|
||||
++count;
|
||||
}
|
||||
++it;
|
||||
}
|
||||
|
||||
client->Message(CC_Default, "%d traps found.", count);
|
||||
}
|
||||
|
||||
void EntityList::ClearTrapPointers()
|
||||
{
|
||||
auto it = trap_list.begin();
|
||||
while (it != trap_list.end())
|
||||
{
|
||||
Trap* cur = it->second;
|
||||
if (cur->IsTrap())
|
||||
{
|
||||
cur->DestroyHiddenTrigger();
|
||||
}
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
|
||||
|
||||
std::string query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
|
||||
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level "
|
||||
"FROM traps WHERE zone='%s' AND version=%u", zonename, version);
|
||||
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
|
||||
"`group`, triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND version=%u", zonename, version);
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
uint32 tid = atoi(row[0]);
|
||||
uint8 grp = atoi(row[15]);
|
||||
|
||||
if (grp > 0)
|
||||
{
|
||||
// If a member of our group is already spawned skip loading this trap.
|
||||
if (entity_list.IsTrapGroupSpawned(tid, grp))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
auto trap = new Trap();
|
||||
trap->trap_id = atoi(row[0]);
|
||||
trap->trap_id = tid;
|
||||
trap->db_id = tid;
|
||||
trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3]));
|
||||
trap->effect = atoi(row[4]);
|
||||
trap->effectvalue = atoi(row[5]);
|
||||
@@ -282,8 +435,13 @@ bool ZoneDatabase::LoadTraps(const char* zonename, int16 version) {
|
||||
trap->respawn_time = atoi(row[12]);
|
||||
trap->respawn_var = atoi(row[13]);
|
||||
trap->level = atoi(row[14]);
|
||||
trap->group = grp;
|
||||
trap->triggered_number = atoi(row[16]);
|
||||
trap->despawn_when_triggered = atobool(row[17]);
|
||||
trap->undetectable = atobool(row[18]);
|
||||
entity_list.AddTrap(trap);
|
||||
trap->CreateHiddenTrigger();
|
||||
Log(Logs::General, Logs::Traps, "Trap %d successfully loaded.", trap->trap_id);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -318,3 +476,87 @@ void Trap::CreateHiddenTrigger()
|
||||
hiddenTrigger = npca;
|
||||
ownHiddenTrigger = true;
|
||||
}
|
||||
bool ZoneDatabase::SetTrapData(Trap* trap, bool repopnow) {
|
||||
|
||||
uint32 dbid = trap->db_id;
|
||||
std::string query;
|
||||
|
||||
if (trap->group > 0)
|
||||
{
|
||||
query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
|
||||
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
|
||||
"triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND `group`=%d AND id != %d ORDER BY RAND() LIMIT 1", zone->GetShortName(), trap->group, dbid);
|
||||
}
|
||||
else
|
||||
{
|
||||
// We could just use the existing data here, but querying the DB is not expensive, and allows content developers to change traps without rebooting.
|
||||
query = StringFormat("SELECT id, x, y, z, effect, effectvalue, effectvalue2, skill, "
|
||||
"maxzdiff, radius, chance, message, respawn_time, respawn_var, level, "
|
||||
"triggered_number, despawn_when_triggered, undetectable FROM traps WHERE zone='%s' AND id = %d", zone->GetShortName(), dbid);
|
||||
}
|
||||
|
||||
auto results = QueryDatabase(query);
|
||||
if (!results.Success()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
|
||||
trap->db_id = atoi(row[0]);
|
||||
trap->m_Position = glm::vec3(atof(row[1]), atof(row[2]), atof(row[3]));
|
||||
trap->effect = atoi(row[4]);
|
||||
trap->effectvalue = atoi(row[5]);
|
||||
trap->effectvalue2 = atoi(row[6]);
|
||||
trap->skill = atoi(row[7]);
|
||||
trap->maxzdiff = atof(row[8]);
|
||||
trap->radius = atof(row[9]);
|
||||
trap->chance = atoi(row[10]);
|
||||
trap->message = row[11];
|
||||
trap->respawn_time = atoi(row[12]);
|
||||
trap->respawn_var = atoi(row[13]);
|
||||
trap->level = atoi(row[14]);
|
||||
trap->triggered_number = atoi(row[15]);
|
||||
trap->despawn_when_triggered = atobool(row[16]);
|
||||
trap->undetectable = atobool(row[17]);
|
||||
trap->CreateHiddenTrigger();
|
||||
|
||||
if (repopnow)
|
||||
{
|
||||
trap->chkarea_timer.Enable();
|
||||
}
|
||||
else
|
||||
{
|
||||
trap->respawn_timer.Start((trap->respawn_time + zone->random.Int(0, trap->respawn_var)) * 1000);
|
||||
}
|
||||
|
||||
if (trap->trap_id != trap->db_id)
|
||||
Log(Logs::General, Logs::Traps, "Trap (%d) DBID has changed from %d to %d", trap->trap_id, dbid, trap->db_id);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Trap::UpdateTrap(bool respawn, bool repopnow)
|
||||
{
|
||||
respawn_timer.Disable();
|
||||
chkarea_timer.Disable();
|
||||
reset_timer.Disable();
|
||||
if (hiddenTrigger)
|
||||
{
|
||||
hiddenTrigger->Depop();
|
||||
hiddenTrigger = nullptr;
|
||||
}
|
||||
times_triggered = 0;
|
||||
Client* trigger = entity_list.GetClientByCharID(charid);
|
||||
if (trigger)
|
||||
{
|
||||
trigger->trapid = 0;
|
||||
}
|
||||
charid = 0;
|
||||
if (respawn)
|
||||
{
|
||||
database.SetTrapData(this, repopnow);
|
||||
}
|
||||
}
|
||||
|
||||
+11
-2
@@ -49,11 +49,14 @@ public:
|
||||
NPC * GetHiddenTrigger() { return hiddenTrigger; }
|
||||
void SetHiddenTrigger(NPC* n) { hiddenTrigger = n; }
|
||||
void CreateHiddenTrigger();
|
||||
|
||||
void DestroyHiddenTrigger() { hiddenTrigger = nullptr; }
|
||||
void UpdateTrap(bool respawn = true, bool repopnow = false);
|
||||
//Trap data, leave this unprotected
|
||||
Timer respawn_timer; //Respawn Time when Trap's been disarmed
|
||||
Timer chkarea_timer;
|
||||
uint32 trap_id; //Database ID of trap
|
||||
Timer reset_timer; //How long a trap takes to reset before triggering again.
|
||||
uint32 trap_id; //Original ID of the trap from DB. This value never changes.
|
||||
uint32 db_id; //The DB ID of the trap that currently is spawned.
|
||||
glm::vec3 m_Position;
|
||||
float maxzdiff; //maximum z diff to be triggerable
|
||||
float radius; //radius around trap to be triggerable
|
||||
@@ -67,6 +70,12 @@ public:
|
||||
bool disarmed;
|
||||
uint32 respawn_time;
|
||||
uint32 respawn_var;
|
||||
uint8 triggered_number;
|
||||
uint8 times_triggered;
|
||||
uint8 group;
|
||||
bool despawn_when_triggered;
|
||||
uint32 charid; //ID of character that triggered trap. This is cleared when the trap despawns are resets.
|
||||
bool undetectable;
|
||||
|
||||
std::string message;
|
||||
protected:
|
||||
|
||||
+192
-161
@@ -29,10 +29,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include "npc.h"
|
||||
#include "quest_parser_collection.h"
|
||||
#include "water_map.h"
|
||||
#include "fastmath.h"
|
||||
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
extern FastMath g_Math;
|
||||
|
||||
struct wp_distance
|
||||
{
|
||||
float dist;
|
||||
@@ -176,9 +179,15 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
|
||||
cur_wp = -2; // flag as quest controlled w/no grid
|
||||
Log(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast<glm::vec3>(position)).c_str());
|
||||
}
|
||||
|
||||
glm::vec3 dest(position);
|
||||
|
||||
m_CurrentWayPoint = position;
|
||||
m_CurrentWayPoint.z = GetFixedZ(dest);
|
||||
|
||||
if (saveguardspot)
|
||||
{
|
||||
m_GuardPoint = position;
|
||||
m_GuardPoint = m_CurrentWayPoint;
|
||||
|
||||
if (m_GuardPoint.w == 0)
|
||||
m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler
|
||||
@@ -189,7 +198,6 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
|
||||
Log(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
|
||||
}
|
||||
|
||||
m_CurrentWayPoint = position;
|
||||
cur_wp_pause = 0;
|
||||
pLastFightingDelayMoving = 0;
|
||||
if (AI_walking_timer->Enabled())
|
||||
@@ -430,28 +438,7 @@ float Mob::CalculateDistance(float x, float y, float z) {
|
||||
return (float)sqrtf(((m_Position.x - x)*(m_Position.x - x)) + ((m_Position.y - y)*(m_Position.y - y)) + ((m_Position.z - z)*(m_Position.z - z)));
|
||||
}
|
||||
|
||||
float Mob::CalculateHeadingToTarget(float in_x, float in_y) {
|
||||
float angle;
|
||||
|
||||
if (in_x - m_Position.x > 0)
|
||||
angle = -90 + atan((float)(in_y - m_Position.y) / (float)(in_x - m_Position.x)) * 180 / M_PI;
|
||||
else if (in_x - m_Position.x < 0)
|
||||
angle = +90 + atan((float)(in_y - m_Position.y) / (float)(in_x - m_Position.x)) * 180 / M_PI;
|
||||
else // Added?
|
||||
{
|
||||
if (in_y - m_Position.y > 0)
|
||||
angle = 0;
|
||||
else
|
||||
angle = 180;
|
||||
}
|
||||
if (angle < 0)
|
||||
angle += 360;
|
||||
if (angle > 360)
|
||||
angle -= 360;
|
||||
return (256 * (360 - angle) / 360.0f);
|
||||
}
|
||||
|
||||
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed) {
|
||||
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, bool checkZ, bool calcHeading) {
|
||||
if (GetID() == 0)
|
||||
return true;
|
||||
|
||||
@@ -495,7 +482,7 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed) {
|
||||
m_Position.y = new_y;
|
||||
m_Position.z = new_z;
|
||||
|
||||
if(fix_z_timer.Check() &&
|
||||
if(checkZ && fix_z_timer.Check() &&
|
||||
(!this->IsEngaged() || flee_mode || currently_fleeing))
|
||||
this->FixZ();
|
||||
|
||||
@@ -562,7 +549,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed) {
|
||||
m_Position.x = new_x;
|
||||
m_Position.y = new_y;
|
||||
m_Position.z = new_z;
|
||||
m_Position.w = CalculateHeadingToTarget(x, y);
|
||||
if (calcHeading)
|
||||
m_Position.w = CalculateHeadingToTarget(x, y);
|
||||
tar_ndx = 20 - numsteps;
|
||||
}
|
||||
else
|
||||
@@ -600,10 +588,11 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed) {
|
||||
m_Position.x = new_x;
|
||||
m_Position.y = new_y;
|
||||
m_Position.z = new_z;
|
||||
m_Position.w = CalculateHeadingToTarget(x, y);
|
||||
if (calcHeading)
|
||||
m_Position.w = CalculateHeadingToTarget(x, y);
|
||||
}
|
||||
|
||||
if (fix_z_timer.Check() && !this->IsEngaged())
|
||||
if (checkZ && fix_z_timer.Check() && !this->IsEngaged())
|
||||
this->FixZ();
|
||||
|
||||
SetMoving(true);
|
||||
@@ -757,157 +746,199 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
}
|
||||
}
|
||||
|
||||
void Mob::FixZ()
|
||||
float Mob::GetFixedZ(glm::vec3 dest, int32 z_find_offset)
|
||||
{
|
||||
|
||||
BenchTimer timer;
|
||||
timer.reset();
|
||||
float new_z = dest.z;
|
||||
|
||||
if (zone->HasMap() && RuleB(Map, FixZWhenMoving) && (flymode != 1 && flymode != 2))
|
||||
if (zone->HasMap() && RuleB(Map, FixZWhenMoving) &&
|
||||
(flymode != 1 && flymode != 2))
|
||||
{
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap()
|
||||
|| (zone->HasWaterMap() &&
|
||||
!zone->watermap->InWater(glm::vec3(m_Position))))
|
||||
{
|
||||
/* Any more than 5 in the offset makes NPC's hop/snap to ceiling in small corridors */
|
||||
float new_z = this->FindGroundZ(m_Position.x, m_Position.y, 5);
|
||||
new_z += GetModelOffset();
|
||||
auto duration = timer.elapsed();
|
||||
new_z = this->FindDestGroundZ(dest, z_find_offset);
|
||||
if (new_z != BEST_Z_INVALID)
|
||||
{
|
||||
new_z += this->GetZOffset();
|
||||
|
||||
Log(
|
||||
Logs::Moderate,
|
||||
Logs::FixZ,
|
||||
"Mob::FixZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
|
||||
this->GetCleanName(),
|
||||
new_z,
|
||||
m_Position.x,
|
||||
m_Position.y,
|
||||
m_Position.z,
|
||||
duration
|
||||
);
|
||||
|
||||
if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(78, 0, 0, 0, 0);
|
||||
|
||||
m_Position.z = new_z;
|
||||
// If bad new Z restore old one
|
||||
if (new_z < -2000) {
|
||||
new_z = m_Position.z;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(103, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
Log(Logs::General, Logs::FixZ, "%s is failing to find Z %f", this->GetCleanName(), std::abs(m_Position.z - new_z));
|
||||
}
|
||||
auto duration = timer.elapsed();
|
||||
|
||||
last_z = m_Position.z;
|
||||
Log(Logs::Moderate, Logs::FixZ,
|
||||
"Mob::GetFixedZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
|
||||
this->GetCleanName(), new_z, dest.x, dest.y, dest.z, duration);
|
||||
}
|
||||
|
||||
return new_z;
|
||||
}
|
||||
|
||||
void Mob::FixZ(int32 z_find_offset /*= 5*/)
|
||||
{
|
||||
glm::vec3 current_loc(m_Position);
|
||||
float new_z = GetFixedZ(current_loc, z_find_offset);
|
||||
|
||||
if (!IsClient() && new_z != m_Position.z)
|
||||
{
|
||||
if ((new_z > -2000) && new_z != BEST_Z_INVALID) {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(78, 0, 0, 0, 0);
|
||||
|
||||
m_Position.z = new_z;
|
||||
}
|
||||
else {
|
||||
if (RuleB(Map, MobZVisualDebug))
|
||||
this->SendAppearanceEffect(103, 0, 0, 0, 0);
|
||||
|
||||
Log(Logs::General, Logs::FixZ, "%s is failing to find Z %f",
|
||||
this->GetCleanName(), std::abs(m_Position.z - new_z));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float Mob::GetModelOffset() const {
|
||||
float offset = 3.125f;
|
||||
float Mob::GetZOffset() const {
|
||||
float offset = 3.125f;
|
||||
|
||||
switch (race) {
|
||||
case 436:
|
||||
offset = 0.577f;
|
||||
break;
|
||||
case 430:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 432:
|
||||
offset = 1.9f;
|
||||
break;
|
||||
case 435:
|
||||
offset = 0.93f;
|
||||
break;
|
||||
case 450:
|
||||
offset = 0.938f;
|
||||
break;
|
||||
case 479:
|
||||
offset = 0.8f;
|
||||
break;
|
||||
case 451:
|
||||
offset = 0.816f;
|
||||
break;
|
||||
case 437:
|
||||
offset = 0.527f;
|
||||
break;
|
||||
case 439:
|
||||
offset = 1.536f;
|
||||
break;
|
||||
case 415:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 438:
|
||||
offset = 0.776f;
|
||||
break;
|
||||
case 452:
|
||||
offset = 0.776f;
|
||||
break;
|
||||
case 441:
|
||||
offset = 0.816f;
|
||||
break;
|
||||
case 440:
|
||||
offset = 0.938f;
|
||||
break;
|
||||
case 468:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 459:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 462:
|
||||
offset = 1.5f;
|
||||
break;
|
||||
case 530:
|
||||
offset = 1.2f;
|
||||
break;
|
||||
case 549:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 548:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 547:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 604:
|
||||
offset = 1.2f;
|
||||
break;
|
||||
case 653:
|
||||
offset = 5.9f;
|
||||
break;
|
||||
case 658:
|
||||
offset = 4.0f;
|
||||
break;
|
||||
case 323:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 663:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 664:
|
||||
offset = 4.0f;
|
||||
break;
|
||||
case 703:
|
||||
offset = 9.0f;
|
||||
break;
|
||||
case 688:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 669:
|
||||
offset = 7.0f;
|
||||
break;
|
||||
case 687:
|
||||
offset = 2.0f;
|
||||
break;
|
||||
case 686:
|
||||
offset = 2.0f;
|
||||
break;
|
||||
default:
|
||||
offset = 3.125f;
|
||||
switch (race) {
|
||||
case 436:
|
||||
offset = 0.577f;
|
||||
break;
|
||||
case 430:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 432:
|
||||
offset = 1.9f;
|
||||
break;
|
||||
case 435:
|
||||
offset = 0.93f;
|
||||
break;
|
||||
case 450:
|
||||
offset = 0.938f;
|
||||
break;
|
||||
case 479:
|
||||
offset = 0.8f;
|
||||
break;
|
||||
case 451:
|
||||
offset = 0.816f;
|
||||
break;
|
||||
case 437:
|
||||
offset = 0.527f;
|
||||
break;
|
||||
case 439:
|
||||
offset = 1.536f;
|
||||
break;
|
||||
case 415:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 438:
|
||||
offset = 0.776f;
|
||||
break;
|
||||
case 452:
|
||||
offset = 0.776f;
|
||||
break;
|
||||
case 441:
|
||||
offset = 0.816f;
|
||||
break;
|
||||
case 440:
|
||||
offset = 0.938f;
|
||||
break;
|
||||
case 468:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 459:
|
||||
offset = 1.0f;
|
||||
break;
|
||||
case 462:
|
||||
offset = 1.5f;
|
||||
break;
|
||||
case 530:
|
||||
offset = 1.2f;
|
||||
break;
|
||||
case 549:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 548:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 547:
|
||||
offset = 0.5f;
|
||||
break;
|
||||
case 604:
|
||||
offset = 1.2f;
|
||||
break;
|
||||
case 653:
|
||||
offset = 5.9f;
|
||||
break;
|
||||
case 658:
|
||||
offset = 4.0f;
|
||||
break;
|
||||
case 323:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 663:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 664:
|
||||
offset = 4.0f;
|
||||
break;
|
||||
case 703:
|
||||
offset = 9.0f;
|
||||
break;
|
||||
case 688:
|
||||
offset = 5.0f;
|
||||
break;
|
||||
case 669:
|
||||
offset = 7.0f;
|
||||
break;
|
||||
case 687:
|
||||
offset = 2.0f;
|
||||
break;
|
||||
case 686:
|
||||
offset = 2.0f;
|
||||
break;
|
||||
default:
|
||||
offset = 3.125f;
|
||||
}
|
||||
|
||||
return 0.2 * GetSize() * offset;
|
||||
}
|
||||
|
||||
// This function will try to move the mob along the relative angle a set distance
|
||||
// if it can't be moved, it will lower the distance and try again
|
||||
// If we want to move on like say a spawn, we can pass send as false
|
||||
void Mob::TryMoveAlong(float distance, float angle, bool send)
|
||||
{
|
||||
angle += GetHeading();
|
||||
angle = FixHeading(angle);
|
||||
|
||||
glm::vec3 tmp_pos;
|
||||
glm::vec3 new_pos = GetPosition();
|
||||
new_pos.x += distance * g_Math.FastSin(angle);
|
||||
new_pos.y += distance * g_Math.FastCos(angle);
|
||||
new_pos.z += GetZOffset();
|
||||
|
||||
if (zone->HasMap()) {
|
||||
auto new_z = zone->zonemap->FindClosestZ(new_pos, nullptr);
|
||||
if (new_z != BEST_Z_INVALID)
|
||||
new_pos.z = new_z;
|
||||
|
||||
if (zone->zonemap->LineIntersectsZone(GetPosition(), new_pos, 0.0f, &tmp_pos))
|
||||
new_pos = tmp_pos;
|
||||
}
|
||||
|
||||
return 0.2 * GetSize() * offset;
|
||||
new_pos.z = GetFixedZ(new_pos);
|
||||
Teleport(new_pos);
|
||||
if (send)
|
||||
SendPositionUpdate();
|
||||
}
|
||||
|
||||
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
|
||||
|
||||
@@ -22,6 +22,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
#include <stdio.h>
|
||||
#include <iomanip>
|
||||
#include <stdarg.h>
|
||||
#include <limits.h>
|
||||
|
||||
#ifdef _WINDOWS
|
||||
#include <process.h>
|
||||
@@ -338,6 +339,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
if (ztz->response <= 0) {
|
||||
zc2->success = ZONE_ERROR_NOTREADY;
|
||||
entity->CastToMob()->SetZone(ztz->current_zone_id, ztz->current_instance_id);
|
||||
entity->CastToClient()->SetZoning(false);
|
||||
}
|
||||
else {
|
||||
entity->CastToClient()->UpdateWho(1);
|
||||
@@ -1812,6 +1814,13 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
|
||||
|
||||
break;
|
||||
}
|
||||
case ServerOP_UCSServerStatusReply:
|
||||
{
|
||||
auto ucsss = (UCSServerStatus_Struct*)pack->pBuffer;
|
||||
if (zone)
|
||||
zone->SetUCSServerAvailable((ucsss->available != 0), ucsss->timestamp);
|
||||
break;
|
||||
}
|
||||
case ServerOP_CZSetEntityVariableByNPCTypeID:
|
||||
{
|
||||
CZSetEntVarByNPCTypeID_Struct* CZM = (CZSetEntVarByNPCTypeID_Struct*)pack->pBuffer;
|
||||
|
||||
+43
-4
@@ -148,6 +148,8 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
|
||||
UpdateWindowTitle();
|
||||
zone->GetTimeSync();
|
||||
|
||||
zone->RequestUCSServerStatus();
|
||||
|
||||
/* Set Logging */
|
||||
|
||||
LogSys.StartFileLogs(StringFormat("%s_version_%u_inst_id_%u_port_%u", zone->GetShortName(), zone->GetInstanceVersion(), zone->GetInstanceID(), ZoneConfig::get()->ZonePort));
|
||||
@@ -849,6 +851,9 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
|
||||
GuildBanks = new GuildBankManager;
|
||||
else
|
||||
GuildBanks = nullptr;
|
||||
|
||||
m_ucss_available = false;
|
||||
m_last_ucss_update = 0;
|
||||
}
|
||||
|
||||
Zone::~Zone() {
|
||||
@@ -970,6 +975,8 @@ bool Zone::Init(bool iStaticZone) {
|
||||
|
||||
LoadAlternateAdvancement();
|
||||
|
||||
database.LoadGlobalLoot();
|
||||
|
||||
//Load merchant data
|
||||
zone->GetMerchantDataForZoneLoad();
|
||||
|
||||
@@ -1430,7 +1437,8 @@ void Zone::StartShutdownTimer(uint32 set_time) {
|
||||
bool Zone::Depop(bool StartSpawnTimer) {
|
||||
std::map<uint32,NPCType *>::iterator itr;
|
||||
entity_list.Depop(StartSpawnTimer);
|
||||
|
||||
entity_list.ClearTrapPointers();
|
||||
entity_list.UpdateAllTraps(false);
|
||||
/* Refresh npctable (cache), getting current info from database. */
|
||||
while(!npctable.empty()) {
|
||||
itr = npctable.begin();
|
||||
@@ -1438,6 +1446,9 @@ bool Zone::Depop(bool StartSpawnTimer) {
|
||||
npctable.erase(itr);
|
||||
}
|
||||
|
||||
// clear spell cache
|
||||
database.ClearNPCSpells();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1498,6 +1509,8 @@ void Zone::Repop(uint32 delay) {
|
||||
iterator.RemoveCurrent();
|
||||
}
|
||||
|
||||
entity_list.ClearTrapPointers();
|
||||
|
||||
quest_manager.ClearAllTimers();
|
||||
|
||||
if (!database.PopulateZoneSpawnList(zoneid, spawn2_list, GetInstanceVersion(), delay))
|
||||
@@ -1505,6 +1518,8 @@ void Zone::Repop(uint32 delay) {
|
||||
|
||||
initgrids_timer.Start();
|
||||
|
||||
entity_list.UpdateAllTraps(true, true);
|
||||
|
||||
//MODDING HOOK FOR REPOP
|
||||
mod_repop();
|
||||
}
|
||||
@@ -1512,7 +1527,7 @@ void Zone::Repop(uint32 delay) {
|
||||
void Zone::GetTimeSync()
|
||||
{
|
||||
if (worldserver.Connected() && !zone_has_current_time) {
|
||||
auto pack = new ServerPacket(ServerOP_GetWorldTime, 0);
|
||||
auto pack = new ServerPacket(ServerOP_GetWorldTime, 1);
|
||||
worldserver.SendPacket(pack);
|
||||
safe_delete(pack);
|
||||
}
|
||||
@@ -1855,14 +1870,17 @@ bool ZoneDatabase::GetDecayTimes(npcDecayTimes_Struct* npcCorpseDecayTimes) {
|
||||
return true;
|
||||
}
|
||||
|
||||
void Zone::weatherSend()
|
||||
void Zone::weatherSend(Client* client)
|
||||
{
|
||||
auto outapp = new EQApplicationPacket(OP_Weather, 8);
|
||||
if(zone_weather>0)
|
||||
outapp->pBuffer[0] = zone_weather-1;
|
||||
if(zone_weather>0)
|
||||
outapp->pBuffer[4] = zone->weather_intensity;
|
||||
entity_list.QueueClients(0, outapp);
|
||||
if (client)
|
||||
client->QueuePacket(outapp);
|
||||
else
|
||||
entity_list.QueueClients(0, outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
@@ -2223,6 +2241,8 @@ void Zone::DoAdventureActions()
|
||||
{
|
||||
NPC* npc = new NPC(tmp, nullptr, glm::vec4(ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h), FlyMode3);
|
||||
npc->AddLootTable();
|
||||
if (npc->DropsGlobalLoot())
|
||||
npc->CheckGlobalLootTables();
|
||||
entity_list.AddNPC(npc);
|
||||
npc->Shout("Rarrrgh!");
|
||||
did_adventure_actions = true;
|
||||
@@ -2326,3 +2346,22 @@ void Zone::UpdateHotzone()
|
||||
is_hotzone = atoi(row[0]) == 0 ? false: true;
|
||||
}
|
||||
|
||||
void Zone::RequestUCSServerStatus() {
|
||||
auto outapp = new ServerPacket(ServerOP_UCSServerStatusRequest, sizeof(UCSServerStatus_Struct));
|
||||
auto ucsss = (UCSServerStatus_Struct*)outapp->pBuffer;
|
||||
ucsss->available = 0;
|
||||
ucsss->port = Config->ZonePort;
|
||||
ucsss->unused = 0;
|
||||
worldserver.SendPacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Zone::SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp) {
|
||||
if (m_last_ucss_update == update_timestamp && m_ucss_available != ucss_available) {
|
||||
m_ucss_available = false;
|
||||
RequestUCSServerStatus();
|
||||
return;
|
||||
}
|
||||
if (m_last_ucss_update < update_timestamp)
|
||||
m_ucss_available = ucss_available;
|
||||
}
|
||||
|
||||
+17
-1
@@ -29,6 +29,7 @@
|
||||
#include "spawngroup.h"
|
||||
#include "aa_ability.h"
|
||||
#include "pathfinder_interface.h"
|
||||
#include "global_loot_manager.h"
|
||||
|
||||
struct ZonePoint
|
||||
{
|
||||
@@ -209,6 +210,7 @@ public:
|
||||
void LoadAlternateCurrencies();
|
||||
void LoadNPCEmotes(LinkedList<NPC_Emote_Struct*>* NPCEmoteList);
|
||||
void ReloadWorld(uint32 Option);
|
||||
void ReloadMerchants();
|
||||
|
||||
Map* zonemap;
|
||||
WaterMap* watermap;
|
||||
@@ -222,7 +224,7 @@ public:
|
||||
void SetDate(uint16 year, uint8 month, uint8 day, uint8 hour, uint8 minute);
|
||||
void SetTime(uint8 hour, uint8 minute, bool update_world = true);
|
||||
|
||||
void weatherSend();
|
||||
void weatherSend(Client* client = nullptr);
|
||||
bool CanBind() const { return(can_bind); }
|
||||
bool IsCity() const { return(is_city); }
|
||||
bool CanDoCombat() const { return(can_combat); }
|
||||
@@ -268,6 +270,15 @@ public:
|
||||
void UpdateHotzone();
|
||||
std::unordered_map<int, item_tick_struct> tick_items;
|
||||
|
||||
inline std::vector<int> GetGlobalLootTables(NPC *mob) const { return m_global_loot.GetGlobalLootTables(mob); }
|
||||
inline void AddGlobalLootEntry(GlobalLootEntry &in) { return m_global_loot.AddEntry(in); }
|
||||
inline void ShowZoneGlobalLoot(Client *to) { m_global_loot.ShowZoneGlobalLoot(to); }
|
||||
inline void ShowNPCGlobalLoot(Client *to, NPC *who) { m_global_loot.ShowNPCGlobalLoot(to, who); }
|
||||
|
||||
void RequestUCSServerStatus();
|
||||
void SetUCSServerAvailable(bool ucss_available, uint32 update_timestamp);
|
||||
bool IsUCSServerAvailable() { return m_ucss_available; }
|
||||
|
||||
// random object that provides random values for the zone
|
||||
EQEmu::Random random;
|
||||
|
||||
@@ -346,6 +357,11 @@ private:
|
||||
QGlobalCache *qGlobals;
|
||||
|
||||
Timer hotzone_timer;
|
||||
|
||||
GlobalLootManager m_global_loot;
|
||||
|
||||
bool m_ucss_available;
|
||||
uint32 m_last_ucss_update;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
+1
-1
@@ -49,7 +49,7 @@ class ZoneConfig : public EQEmuConfig {
|
||||
_zone_config=new ZoneConfig;
|
||||
_config=_zone_config;
|
||||
|
||||
return _config->ParseFile(EQEmuConfig::ConfigFile.c_str(),"server");
|
||||
return _config->parseFile();
|
||||
}
|
||||
|
||||
// Accessors for the static private object
|
||||
|
||||
+279
-62
@@ -34,28 +34,15 @@ ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passw
|
||||
|
||||
void ZoneDatabase::ZDBInitVars() {
|
||||
memset(door_isopen_array, 0, sizeof(door_isopen_array));
|
||||
npc_spells_maxid = 0;
|
||||
npc_spellseffects_maxid = 0;
|
||||
npc_spells_cache = 0;
|
||||
npc_spellseffects_cache = 0;
|
||||
npc_spells_loadtried = 0;
|
||||
npc_spellseffects_loadtried = 0;
|
||||
max_faction = 0;
|
||||
faction_array = nullptr;
|
||||
}
|
||||
|
||||
ZoneDatabase::~ZoneDatabase() {
|
||||
unsigned int x;
|
||||
if (npc_spells_cache) {
|
||||
for (x = 0; x <= npc_spells_maxid; x++) {
|
||||
safe_delete_array(npc_spells_cache[x]);
|
||||
}
|
||||
safe_delete_array(npc_spells_cache);
|
||||
}
|
||||
safe_delete_array(npc_spells_loadtried);
|
||||
|
||||
if (npc_spellseffects_cache) {
|
||||
for (x = 0; x <= npc_spellseffects_maxid; x++) {
|
||||
for (int x = 0; x <= npc_spellseffects_maxid; x++) {
|
||||
safe_delete_array(npc_spellseffects_cache[x]);
|
||||
}
|
||||
safe_delete_array(npc_spellseffects_cache);
|
||||
@@ -63,7 +50,7 @@ ZoneDatabase::~ZoneDatabase() {
|
||||
safe_delete_array(npc_spellseffects_loadtried);
|
||||
|
||||
if (faction_array != nullptr) {
|
||||
for (x = 0; x <= max_faction; x++) {
|
||||
for (int x = 0; x <= max_faction; x++) {
|
||||
if (faction_array[x] != 0)
|
||||
safe_delete(faction_array[x]);
|
||||
}
|
||||
@@ -338,62 +325,260 @@ bool ZoneDatabase::logevents(const char* accountname,uint32 accountid,uint8 stat
|
||||
return true;
|
||||
}
|
||||
|
||||
void ZoneDatabase::RegisterBug(BugReport_Struct* bug_report) {
|
||||
if (!bug_report)
|
||||
return;
|
||||
|
||||
void ZoneDatabase::UpdateBug(BugStruct* bug) {
|
||||
|
||||
uint32 len = strlen(bug->bug);
|
||||
char* bugtext = nullptr;
|
||||
if(len > 0)
|
||||
{
|
||||
bugtext = new char[2*len+1];
|
||||
memset(bugtext, 0, 2*len+1);
|
||||
DoEscapeString(bugtext, bug->bug, len);
|
||||
size_t len = 0;
|
||||
char* name_ = nullptr;
|
||||
char* ui_ = nullptr;
|
||||
char* type_ = nullptr;
|
||||
char* target_ = nullptr;
|
||||
char* bug_ = nullptr;
|
||||
|
||||
len = strlen(bug_report->reporter_name);
|
||||
if (len) {
|
||||
if (len > 63) // check against db column size
|
||||
len = 63;
|
||||
name_ = new char[(2 * len + 1)];
|
||||
memset(name_, 0, (2 * len + 1));
|
||||
DoEscapeString(name_, bug_report->reporter_name, len);
|
||||
}
|
||||
|
||||
len = strlen(bug->ui);
|
||||
char* uitext = nullptr;
|
||||
if(len > 0)
|
||||
{
|
||||
uitext = new char[2*len+1];
|
||||
memset(uitext, 0, 2*len+1);
|
||||
DoEscapeString(uitext, bug->ui, len);
|
||||
len = strlen(bug_report->ui_path);
|
||||
if (len) {
|
||||
if (len > 127)
|
||||
len = 127;
|
||||
ui_ = new char[(2 * len + 1)];
|
||||
memset(ui_, 0, (2 * len + 1));
|
||||
DoEscapeString(ui_, bug_report->ui_path, len);
|
||||
}
|
||||
|
||||
len = strlen(bug->target_name);
|
||||
char* targettext = nullptr;
|
||||
if(len > 0)
|
||||
{
|
||||
targettext = new char[2*len+1];
|
||||
memset(targettext, 0, 2*len+1);
|
||||
DoEscapeString(targettext, bug->target_name, len);
|
||||
len = strlen(bug_report->category_name);
|
||||
if (len) {
|
||||
if (len > 63)
|
||||
len = 63;
|
||||
type_ = new char[(2 * len + 1)];
|
||||
memset(type_, 0, (2 * len + 1));
|
||||
DoEscapeString(type_, bug_report->category_name, len);
|
||||
}
|
||||
|
||||
//x and y are intentionally swapped because eq is inversexy coords
|
||||
std::string query = StringFormat("INSERT INTO bugs (zone, name, ui, x, y, z, type, flag, target, bug, date) "
|
||||
len = strlen(bug_report->target_name);
|
||||
if (len) {
|
||||
if (len > 63)
|
||||
len = 63;
|
||||
target_ = new char[(2 * len + 1)];
|
||||
memset(target_, 0, (2 * len + 1));
|
||||
DoEscapeString(target_, bug_report->target_name, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->bug_report);
|
||||
if (len) {
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
bug_ = new char[(2 * len + 1)];
|
||||
memset(bug_, 0, (2 * len + 1));
|
||||
DoEscapeString(bug_, bug_report->bug_report, len);
|
||||
}
|
||||
|
||||
//x and y are intentionally swapped because eq is inversexy coords //is this msg out-of-date or are the parameters wrong?
|
||||
std::string query = StringFormat(
|
||||
"INSERT INTO `bugs` (`zone`, `name`, `ui`, `x`, `y`, `z`, `type`, `flag`, `target`, `bug`, `date`) "
|
||||
"VALUES('%s', '%s', '%s', '%.2f', '%.2f', '%.2f', '%s', %d, '%s', '%s', CURDATE())",
|
||||
zone->GetShortName(), bug->name, uitext == nullptr ? "": uitext,
|
||||
bug->x, bug->y, bug->z, bug->chartype, bug->type, targettext == nullptr? "Unknown Target": targettext,
|
||||
bugtext==nullptr?"":bugtext);
|
||||
safe_delete_array(bugtext);
|
||||
safe_delete_array(uitext);
|
||||
safe_delete_array(targettext);
|
||||
zone->GetShortName(),
|
||||
(name_ ? name_ : ""),
|
||||
(ui_ ? ui_ : ""),
|
||||
bug_report->pos_x,
|
||||
bug_report->pos_y,
|
||||
bug_report->pos_z,
|
||||
(type_ ? type_ : ""),
|
||||
bug_report->optional_info_mask,
|
||||
(target_ ? target_ : "Unknown Target"),
|
||||
(bug_ ? bug_ : "")
|
||||
);
|
||||
safe_delete_array(name_);
|
||||
safe_delete_array(ui_);
|
||||
safe_delete_array(type_);
|
||||
safe_delete_array(target_);
|
||||
safe_delete_array(bug_);
|
||||
|
||||
QueryDatabase(query);
|
||||
}
|
||||
|
||||
void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug){
|
||||
void ZoneDatabase::RegisterBug(Client* client, BugReport_Struct* bug_report) {
|
||||
if (!client || !bug_report)
|
||||
return;
|
||||
|
||||
uint32 len = strlen(bug->text);
|
||||
auto bugtext = new char[2 * len + 1];
|
||||
memset(bugtext, 0, 2*len+1);
|
||||
DoEscapeString(bugtext, bug->text, len);
|
||||
size_t len = 0;
|
||||
char* category_name_ = nullptr;
|
||||
char* reporter_name_ = nullptr;
|
||||
char* ui_path_ = nullptr;
|
||||
char* target_name_ = nullptr;
|
||||
char* bug_report_ = nullptr;
|
||||
char* system_info_ = nullptr;
|
||||
|
||||
std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) "
|
||||
"VALUES('%s', '%s', '%s', %i)",
|
||||
"Petition", bug->name, bugtext, 25);
|
||||
safe_delete_array(bugtext);
|
||||
QueryDatabase(query);
|
||||
len = strlen(bug_report->category_name);
|
||||
if (len) {
|
||||
if (len > 63) // check against db column size
|
||||
len = 63;
|
||||
category_name_ = new char[(2 * len + 1)];
|
||||
memset(category_name_, 0, (2 * len + 1));
|
||||
DoEscapeString(category_name_, bug_report->category_name, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->reporter_name);
|
||||
if (len) {
|
||||
if (len > 63)
|
||||
len = 63;
|
||||
reporter_name_ = new char[(2 * len + 1)];
|
||||
memset(reporter_name_, 0, (2 * len + 1));
|
||||
DoEscapeString(reporter_name_, bug_report->reporter_name, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->ui_path);
|
||||
if (len) {
|
||||
if (len > 127)
|
||||
len = 127;
|
||||
ui_path_ = new char[(2 * len + 1)];
|
||||
memset(ui_path_, 0, (2 * len + 1));
|
||||
DoEscapeString(ui_path_, bug_report->ui_path, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->target_name);
|
||||
if (len) {
|
||||
if (len > 63)
|
||||
len = 63;
|
||||
target_name_ = new char[(2 * len + 1)];
|
||||
memset(target_name_, 0, (2 * len + 1));
|
||||
DoEscapeString(target_name_, bug_report->target_name, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->bug_report);
|
||||
if (len) {
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
bug_report_ = new char[(2 * len + 1)];
|
||||
memset(bug_report_, 0, (2 * len + 1));
|
||||
DoEscapeString(bug_report_, bug_report->bug_report, len);
|
||||
}
|
||||
|
||||
len = strlen(bug_report->system_info);
|
||||
if (len) {
|
||||
if (len > 1023)
|
||||
len = 1023;
|
||||
system_info_ = new char[(2 * len + 1)];
|
||||
memset(system_info_, 0, (2 * len + 1));
|
||||
DoEscapeString(system_info_, bug_report->system_info, len);
|
||||
}
|
||||
|
||||
std::string query = StringFormat(
|
||||
"INSERT INTO `bug_reports` "
|
||||
"(`zone`,"
|
||||
" `client_version_id`,"
|
||||
" `client_version_name`,"
|
||||
" `account_id`,"
|
||||
" `character_id`,"
|
||||
" `character_name`,"
|
||||
" `reporter_spoof`,"
|
||||
" `category_id`,"
|
||||
" `category_name`,"
|
||||
" `reporter_name`,"
|
||||
" `ui_path`,"
|
||||
" `pos_x`,"
|
||||
" `pos_y`,"
|
||||
" `pos_z`,"
|
||||
" `heading`,"
|
||||
" `time_played`,"
|
||||
" `target_id`,"
|
||||
" `target_name`,"
|
||||
" `optional_info_mask`,"
|
||||
" `_can_duplicate`,"
|
||||
" `_crash_bug`,"
|
||||
" `_target_info`,"
|
||||
" `_character_flags`,"
|
||||
" `_unknown_value`,"
|
||||
" `bug_report`,"
|
||||
" `system_info`) "
|
||||
"VALUES "
|
||||
"('%s',"
|
||||
" '%u',"
|
||||
" '%s',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%s',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%s',"
|
||||
" '%s',"
|
||||
" '%s',"
|
||||
" '%1.1f',"
|
||||
" '%1.1f',"
|
||||
" '%1.1f',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%s',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%u',"
|
||||
" '%s',"
|
||||
" '%s')",
|
||||
zone->GetShortName(),
|
||||
client->ClientVersion(),
|
||||
EQEmu::versions::ClientVersionName(client->ClientVersion()),
|
||||
client->AccountID(),
|
||||
client->CharacterID(),
|
||||
client->GetName(),
|
||||
(strcmp(client->GetName(), reporter_name_) != 0 ? 1 : 0),
|
||||
bug_report->category_id,
|
||||
(category_name_ ? category_name_ : ""),
|
||||
(reporter_name_ ? reporter_name_ : ""),
|
||||
(ui_path_ ? ui_path_ : ""),
|
||||
bug_report->pos_x,
|
||||
bug_report->pos_y,
|
||||
bug_report->pos_z,
|
||||
bug_report->heading,
|
||||
bug_report->time_played,
|
||||
bug_report->target_id,
|
||||
(target_name_ ? target_name_ : ""),
|
||||
bug_report->optional_info_mask,
|
||||
((bug_report->optional_info_mask & EQEmu::bug::infoCanDuplicate) != 0 ? 1 : 0),
|
||||
((bug_report->optional_info_mask & EQEmu::bug::infoCrashBug) != 0 ? 1 : 0),
|
||||
((bug_report->optional_info_mask & EQEmu::bug::infoTargetInfo) != 0 ? 1 : 0),
|
||||
((bug_report->optional_info_mask & EQEmu::bug::infoCharacterFlags) != 0 ? 1 : 0),
|
||||
((bug_report->optional_info_mask & EQEmu::bug::infoUnknownValue) != 0 ? 1 : 0),
|
||||
(bug_report_ ? bug_report_ : ""),
|
||||
(system_info_ ? system_info_ : "")
|
||||
);
|
||||
safe_delete_array(category_name_);
|
||||
safe_delete_array(reporter_name_);
|
||||
safe_delete_array(ui_path_);
|
||||
safe_delete_array(target_name_);
|
||||
safe_delete_array(bug_report_);
|
||||
safe_delete_array(system_info_);
|
||||
|
||||
auto result = QueryDatabase(query);
|
||||
|
||||
// TODO: Entity dumping [RuleB(Bugs, DumpTargetEntity)]
|
||||
}
|
||||
|
||||
//void ZoneDatabase::UpdateBug(PetitionBug_Struct* bug) {
|
||||
//
|
||||
// uint32 len = strlen(bug->text);
|
||||
// auto bugtext = new char[2 * len + 1];
|
||||
// memset(bugtext, 0, 2 * len + 1);
|
||||
// DoEscapeString(bugtext, bug->text, len);
|
||||
//
|
||||
// std::string query = StringFormat("INSERT INTO bugs (type, name, bugtext, flag) "
|
||||
// "VALUES('%s', '%s', '%s', %i)",
|
||||
// "Petition", bug->name, bugtext, 25);
|
||||
// safe_delete_array(bugtext);
|
||||
// QueryDatabase(query);
|
||||
//}
|
||||
|
||||
bool ZoneDatabase::SetSpecialAttkFlag(uint8 id, const char* flag) {
|
||||
|
||||
std::string query = StringFormat("UPDATE npc_types SET npcspecialattks='%s' WHERE id = %i;", flag, id);
|
||||
@@ -1433,6 +1618,13 @@ bool ZoneDatabase::SaveCharacterInventorySnapshot(uint32 character_id){
|
||||
}
|
||||
|
||||
bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp){
|
||||
|
||||
/* If this is ever zero - the client hasn't fully loaded and potentially crashed during zone */
|
||||
if (account_id <= 0)
|
||||
return false;
|
||||
|
||||
std::string mail_key = database.GetMailKey(character_id);
|
||||
|
||||
clock_t t = std::clock(); /* Function timer start */
|
||||
std::string query = StringFormat(
|
||||
"REPLACE INTO `character_data` ("
|
||||
@@ -1529,7 +1721,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
|
||||
" e_aa_effects, "
|
||||
" e_percent_to_aa, "
|
||||
" e_expended_aa_spent, "
|
||||
" e_last_invsnapshot "
|
||||
" e_last_invsnapshot, "
|
||||
" mailkey "
|
||||
") "
|
||||
"VALUES ("
|
||||
"%u," // id " id, "
|
||||
@@ -1625,7 +1818,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
|
||||
"%u," // e_aa_effects
|
||||
"%u," // e_percent_to_aa
|
||||
"%u," // e_expended_aa_spent
|
||||
"%u" // e_last_invsnapshot
|
||||
"%u," // e_last_invsnapshot
|
||||
"'%s'" // mailkey mail_key
|
||||
")",
|
||||
character_id, // " id, "
|
||||
account_id, // " account_id, "
|
||||
@@ -1720,7 +1914,8 @@ bool ZoneDatabase::SaveCharacterData(uint32 character_id, uint32 account_id, Pla
|
||||
m_epp->aa_effects,
|
||||
m_epp->perAA,
|
||||
m_epp->expended_aa,
|
||||
m_epp->last_invsnapshot_time
|
||||
m_epp->last_invsnapshot_time,
|
||||
mail_key.c_str()
|
||||
);
|
||||
auto results = database.QueryDatabase(query);
|
||||
Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterData %i, done... Took %f seconds", character_id, ((float)(std::clock() - t)) / CLOCKS_PER_SEC);
|
||||
@@ -1970,7 +2165,16 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
"npc_types.feettexture, "
|
||||
"npc_types.ignore_despawn, "
|
||||
"npc_types.show_name, "
|
||||
"npc_types.untargetable "
|
||||
"npc_types.untargetable, "
|
||||
"npc_types.charm_ac, "
|
||||
"npc_types.charm_min_dmg, "
|
||||
"npc_types.charm_max_dmg, "
|
||||
"npc_types.charm_attack_delay, "
|
||||
"npc_types.charm_accuracy_rating, "
|
||||
"npc_types.charm_avoidance_rating, "
|
||||
"npc_types.charm_atk, "
|
||||
"npc_types.skip_global_loot, "
|
||||
"npc_types.rare_spawn "
|
||||
"FROM npc_types %s",
|
||||
where_condition.c_str()
|
||||
);
|
||||
@@ -2149,6 +2353,17 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
|
||||
temp_npctype_data->show_name = atoi(row[98]) != 0 ? true : false;
|
||||
temp_npctype_data->untargetable = atoi(row[99]) != 0 ? true : false;
|
||||
|
||||
temp_npctype_data->charm_ac = atoi(row[100]);
|
||||
temp_npctype_data->charm_min_dmg = atoi(row[101]);
|
||||
temp_npctype_data->charm_max_dmg = atoi(row[102]);
|
||||
temp_npctype_data->charm_attack_delay = atoi(row[103]) * 100; // TODO: fix DB
|
||||
temp_npctype_data->charm_accuracy_rating = atoi(row[104]);
|
||||
temp_npctype_data->charm_avoidance_rating = atoi(row[105]);
|
||||
temp_npctype_data->charm_atk = atoi(row[106]);
|
||||
|
||||
temp_npctype_data->skip_global_loot = atoi(row[107]) != 0;
|
||||
temp_npctype_data->rare_spawn = atoi(row[108]) != 0;
|
||||
|
||||
// If NPC with duplicate NPC id already in table,
|
||||
// free item we attempted to add.
|
||||
if (zone->npctable.find(temp_npctype_data->npc_id) != zone->npctable.end()) {
|
||||
@@ -2953,9 +3168,11 @@ uint32 ZoneDatabase::GetKarma(uint32 acct_id)
|
||||
if (!results.Success())
|
||||
return 0;
|
||||
|
||||
auto row = results.begin();
|
||||
for (auto row = results.begin(); row != results.end(); ++row) {
|
||||
return atoi(row[0]);
|
||||
}
|
||||
|
||||
return atoi(row[0]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void ZoneDatabase::UpdateKarma(uint32 acct_id, uint32 amount)
|
||||
|
||||
+16
-10
@@ -1,6 +1,8 @@
|
||||
#ifndef ZONEDB_H_
|
||||
#define ZONEDB_H_
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
#include "../common/shareddb.h"
|
||||
#include "../common/eq_packet_structs.h"
|
||||
#include "position.h"
|
||||
@@ -16,6 +18,7 @@ class NPC;
|
||||
class Petition;
|
||||
class Spawn2;
|
||||
class SpawnGroupList;
|
||||
class Trap;
|
||||
struct CharacterEventLog_Struct;
|
||||
struct Door;
|
||||
struct ExtendedProfile_Struct;
|
||||
@@ -45,13 +48,15 @@ struct wplist {
|
||||
#pragma pack(1)
|
||||
struct DBnpcspells_entries_Struct {
|
||||
int16 spellid;
|
||||
uint32 type;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
uint32 type;
|
||||
int16 manacost;
|
||||
int32 recast_delay;
|
||||
int16 priority;
|
||||
int32 recast_delay;
|
||||
int16 resist_adjust;
|
||||
int8 min_hp;
|
||||
int8 max_hp;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
@@ -74,7 +79,6 @@ struct DBnpcspells_Struct {
|
||||
int16 rproc_chance;
|
||||
uint16 defensive_proc;
|
||||
int16 dproc_chance;
|
||||
uint32 numentries;
|
||||
uint32 fail_recast;
|
||||
uint32 engaged_no_sp_recast_min;
|
||||
uint32 engaged_no_sp_recast_max;
|
||||
@@ -87,7 +91,7 @@ struct DBnpcspells_Struct {
|
||||
uint32 idle_no_sp_recast_min;
|
||||
uint32 idle_no_sp_recast_max;
|
||||
uint8 idle_beneficial_chance;
|
||||
DBnpcspells_entries_Struct entries[0];
|
||||
std::vector<DBnpcspells_entries_Struct> entries;
|
||||
};
|
||||
|
||||
struct DBnpcspellseffects_Struct {
|
||||
@@ -420,9 +424,11 @@ public:
|
||||
uint32 GetMaxNPCSpellsID();
|
||||
uint32 GetMaxNPCSpellsEffectsID();
|
||||
bool GetAuraEntry(uint16 spell_id, AuraRecord &record);
|
||||
void LoadGlobalLoot();
|
||||
|
||||
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
||||
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||
void ClearNPCSpells() { npc_spells_cache.clear(); npc_spells_loadtried.clear(); }
|
||||
const NPCType* LoadNPCTypesData(uint32 id, bool bulk_load = false);
|
||||
|
||||
/* Mercs */
|
||||
@@ -436,8 +442,9 @@ public:
|
||||
bool DeleteMerc(uint32 merc_id);
|
||||
|
||||
/* Petitions */
|
||||
void UpdateBug(BugStruct* bug);
|
||||
void UpdateBug(PetitionBug_Struct* bug);
|
||||
void RegisterBug(BugReport_Struct* bug_report); // old method
|
||||
void RegisterBug(Client* client, BugReport_Struct* bug_report); // new method
|
||||
//void UpdateBug(PetitionBug_Struct* bug);
|
||||
void DeletePetitionFromDB(Petition* wpet);
|
||||
void UpdatePetitionToDB(Petition* wpet);
|
||||
void InsertPetitionToDB(Petition* wpet);
|
||||
@@ -478,7 +485,7 @@ public:
|
||||
|
||||
/* Traps */
|
||||
bool LoadTraps(const char* zonename, int16 version);
|
||||
char* GetTrapMessage(uint32 trap_id);
|
||||
bool SetTrapData(Trap* trap, bool repopnow = false);
|
||||
|
||||
/* Time */
|
||||
uint32 GetZoneTZ(uint32 zoneid, uint32 version);
|
||||
@@ -523,10 +530,9 @@ protected:
|
||||
|
||||
uint32 max_faction;
|
||||
Faction** faction_array;
|
||||
uint32 npc_spells_maxid;
|
||||
uint32 npc_spellseffects_maxid;
|
||||
DBnpcspells_Struct** npc_spells_cache;
|
||||
bool* npc_spells_loadtried;
|
||||
std::unordered_map<uint32, DBnpcspells_Struct> npc_spells_cache;
|
||||
std::unordered_set<uint32> npc_spells_loadtried;
|
||||
DBnpcspellseffects_Struct** npc_spellseffects_cache;
|
||||
bool* npc_spellseffects_loadtried;
|
||||
uint8 door_isopen_array[255];
|
||||
|
||||
+9
-1
@@ -89,6 +89,13 @@ struct NPCType
|
||||
EQEmu::TintProfile armor_tint;
|
||||
uint32 min_dmg;
|
||||
uint32 max_dmg;
|
||||
uint32 charm_ac;
|
||||
uint32 charm_min_dmg;
|
||||
uint32 charm_max_dmg;
|
||||
int charm_attack_delay;
|
||||
int charm_accuracy_rating;
|
||||
int charm_avoidance_rating;
|
||||
int charm_atk;
|
||||
int16 attack_count;
|
||||
char special_abilities[512];
|
||||
uint16 d_melee_texture1;
|
||||
@@ -126,7 +133,6 @@ struct NPCType
|
||||
float healscale;
|
||||
bool no_target_hotkey;
|
||||
bool raid_target;
|
||||
uint8 probability;
|
||||
uint8 armtexture;
|
||||
uint8 bracertexture;
|
||||
uint8 handtexture;
|
||||
@@ -135,6 +141,8 @@ struct NPCType
|
||||
bool ignore_despawn;
|
||||
bool show_name; // should default on
|
||||
bool untargetable;
|
||||
bool skip_global_loot;
|
||||
bool rare_spawn;
|
||||
};
|
||||
|
||||
namespace player_lootitem {
|
||||
|
||||
+5
-1
@@ -42,7 +42,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
|
||||
Bot::ProcessClientZoneChange(this);
|
||||
#endif
|
||||
|
||||
zoning = true;
|
||||
bZoning = true;
|
||||
if (app->size != sizeof(ZoneChange_Struct)) {
|
||||
Log(Logs::General, Logs::None, "Wrong size: OP_ZoneChange, size=%d, expected %d", app->size, sizeof(ZoneChange_Struct));
|
||||
return;
|
||||
@@ -308,6 +308,8 @@ void Client::SendZoneCancel(ZoneChange_Struct *zc) {
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
bZoning = false;
|
||||
}
|
||||
|
||||
void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||
@@ -327,6 +329,8 @@ void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
|
||||
|
||||
//reset to unsolicited.
|
||||
zone_mode = ZoneUnsolicited;
|
||||
// reset since we're not zoning anymore
|
||||
bZoning = false;
|
||||
}
|
||||
|
||||
void Client::DoZoneSuccess(ZoneChange_Struct *zc, uint16 zone_id, uint32 instance_id, float dest_x, float dest_y, float dest_z, float dest_h, int8 ignore_r) {
|
||||
|
||||
Reference in New Issue
Block a user