Merge branch 'master' into shared_tasks

This commit is contained in:
Michael Cook (mackal)
2019-03-25 19:42:52 -04:00
1718 changed files with 227799 additions and 39083 deletions
+8 -6
View File
@@ -82,16 +82,19 @@ SET(zone_sources
merc.cpp
mob.cpp
mob_ai.cpp
mob_appearance.cpp
mob_movement_manager.cpp
mob_info.cpp
mod_functions.cpp
net.cpp
npc.cpp
npc_ai.cpp
npc_scale_manager.cpp
object.cpp
oriented_bounding_box.cpp
pathfinder_interface.cpp
pathfinder_nav_mesh.cpp
pathfinder_null.cpp
pathfinder_waypoint.cpp
pathing.cpp
perl_client.cpp
perl_doors.cpp
@@ -137,8 +140,7 @@ SET(zone_sources
zone.cpp
zone_config.cpp
zonedb.cpp
zoning.cpp
)
zoning.cpp)
SET(zone_headers
aa.h
@@ -204,15 +206,16 @@ SET(zone_headers
message.h
merc.h
mob.h
mob_movement_manager.h
net.h
npc.h
npc_ai.h
npc_scale_manager.h
object.h
oriented_bounding_box.h
pathfinder_interface.h
pathfinder_nav_mesh.h
pathfinder_null.h
pathfinder_waypoint.h
perlpacket.h
petitions.h
pets.h
@@ -242,8 +245,7 @@ SET(zone_headers
zone.h
zone_config.h
zonedb.h
zonedump.h
)
zonedump.h)
IF(EQEMU_DEPOP_INVALIDATES_CACHE)
ADD_DEFINITIONS(-DDEPOP_INVALIDATES_NPC_TYPES_CACHE)
+16 -16
View File
@@ -126,7 +126,7 @@ void Mob::TemporaryPets(uint16 spell_id, Mob *targ, const char *name_override, u
(npc_dup != nullptr) ? npc_dup : npc_type, //make sure we give the NPC the correct data pointer
0,
GetPosition() + glm::vec4(swarmPetLocations[summon_count], 0.0f, 0.0f),
FlyMode3);
GravityBehavior::Water);
if (followme)
swarm_pet_npc->SetFollowID(GetID());
@@ -225,7 +225,7 @@ void Mob::TypesTemporaryPets(uint32 typesid, Mob *targ, const char *name_overrid
(npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer
0,
GetPosition() + glm::vec4(swarmPetLocations[summon_count], 0.0f, 0.0f),
FlyMode3);
GravityBehavior::Water);
if (followme)
swarm_pet_npc->SetFollowID(GetID());
@@ -286,7 +286,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
make_npc->min_dmg = 1;
//base stats
make_npc->cur_hp = (GetLevel() * 55);
make_npc->current_hp = (GetLevel() * 55);
make_npc->max_hp = (GetLevel() * 55);
make_npc->STR = 85 + (GetLevel() * 3);
make_npc->STA = 85 + (GetLevel() * 3);
@@ -356,51 +356,51 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
case PALADIN:
//SPECATK_TRIPLE
strcpy(make_npc->special_abilities, "6,1");
make_npc->cur_hp = make_npc->cur_hp * 150 / 100;
make_npc->current_hp = make_npc->current_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 8;
break;
case SHADOWKNIGHT:
strcpy(make_npc->special_abilities, "6,1");
make_npc->cur_hp = make_npc->cur_hp * 150 / 100;
make_npc->current_hp = make_npc->current_hp * 150 / 100;
make_npc->max_hp = make_npc->max_hp * 150 / 100;
make_npc->npc_spells_id = 9;
break;
case RANGER:
strcpy(make_npc->special_abilities, "7,1");
make_npc->cur_hp = make_npc->cur_hp * 135 / 100;
make_npc->current_hp = make_npc->current_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
make_npc->npc_spells_id = 10;
break;
case BARD:
strcpy(make_npc->special_abilities, "6,1");
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 11;
break;
case BEASTLORD:
strcpy(make_npc->special_abilities, "7,1");
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
make_npc->npc_spells_id = 12;
break;
case ROGUE:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 110 / 100;
make_npc->current_hp = make_npc->current_hp * 110 / 100;
make_npc->max_hp = make_npc->max_hp * 110 / 100;
break;
case MONK:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 135 / 100;
make_npc->current_hp = make_npc->current_hp * 135 / 100;
make_npc->max_hp = make_npc->max_hp * 135 / 100;
break;
case WARRIOR:
case BERSERKER:
strcpy(make_npc->special_abilities, "7,1");
make_npc->max_dmg = make_npc->max_dmg * 150 /100;
make_npc->cur_hp = make_npc->cur_hp * 175 / 100;
make_npc->current_hp = make_npc->current_hp * 175 / 100;
make_npc->max_hp = make_npc->max_hp * 175 / 100;
break;
default:
@@ -413,7 +413,7 @@ void Mob::WakeTheDead(uint16 spell_id, Mob *target, uint32 duration)
make_npc->d_melee_texture1 = 0;
make_npc->d_melee_texture2 = 0;
auto npca = new NPC(make_npc, 0, GetPosition(), FlyMode3);
auto npca = new NPC(make_npc, 0, GetPosition(), GravityBehavior::Water);
if(!npca->GetSwarmInfo()){
auto nSI = new SwarmPet;
@@ -488,7 +488,7 @@ void Client::ResetAA() {
database.DeleteCharacterLeadershipAAs(CharacterID());
// undefined for these clients
if (ClientVersionBit() & EQEmu::versions::bit_TitaniumAndEarlier)
if (ClientVersionBit() & EQEmu::versions::maskTitaniumAndEarlier)
Kick();
}
@@ -1221,12 +1221,12 @@ void Client::ActivateAlternateAdvancementAbility(int rank_id, int target_id) {
// Bards can cast instant cast AAs while they are casting another song
if(spells[rank->spell].cast_time == 0 && GetClass() == BARD && IsBardSong(casting_spell_id)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
if(!SpellFinished(rank->spell, entity_list.GetMob(target_id), EQEmu::spells::CastingSlot::AltAbility, spells[rank->spell].mana, -1, spells[rank->spell].ResistDiff, false)) {
return;
}
ExpendAlternateAdvancementCharge(ability->id);
} else {
if(!CastSpell(rank->spell, target_id, EQEmu::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
if(!CastSpell(rank->spell, target_id, EQEmu::spells::CastingSlot::AltAbility, -1, -1, 0, -1, rank->spell_type + pTimerAAStart, cooldown, nullptr, rank->id)) {
return;
}
}
@@ -1455,7 +1455,7 @@ bool Mob::CanUseAlternateAdvancementRank(AA::Rank *rank) {
//the one titanium hack i will allow
//just to make sure we dont crash the client with newer aas
//we'll exclude any expendable ones
if(IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::bit_TitaniumAndEarlier) {
if(IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::maskTitaniumAndEarlier) {
if(ability->charges > 0) {
return false;
}
+1 -1
View File
@@ -954,7 +954,7 @@ bool Mob::CombatRange(Mob* other)
if (_DistNoRoot <= size_mod)
{
//A hack to kill an exploit till we get something better.
if (flymode == 0 && _zDist > 500 && !CheckLastLosState()) {
if (flymode != GravityBehavior::Flying && _zDist > 500 && !CheckLastLosState()) {
return false;
}
+42 -24
View File
@@ -33,10 +33,13 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "zone.h"
#include "lua_parser.h"
#include "fastmath.h"
#include "mob.h"
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <boost/concept_check.hpp>
#ifdef BOTS
#include "bot.h"
@@ -1523,7 +1526,7 @@ bool Client::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, b
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
other->Damage(this, my_hit.damage_done, SPELL_UNKNOWN, my_hit.skill, true, -1, false, m_specialattacks);
@@ -1791,7 +1794,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
//this generates a lot of 'updates' to the client that the client does not need
BuffFadeNonPersistDeath();
if (RuleB(Character, UnmemSpellsOnDeath)) {
if ((ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) && RuleB(Character, RespawnFromHover))
if ((ClientVersionBit() & EQEmu::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover))
UnmemSpellAll(true);
else
UnmemSpellAll(false);
@@ -1854,7 +1857,7 @@ bool Client::Death(Mob* killerMob, int32 damage, uint16 spell, EQEmu::skills::Sk
from these and overwrite what we set in pp anyway
*/
if (LeftCorpse && (ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) && RuleB(Character, RespawnFromHover))
if (LeftCorpse && (ClientVersionBit() & EQEmu::versions::maskSoFAndLater) && RuleB(Character, RespawnFromHover))
{
ClearDraggedCorpses();
RespawnFromHoverTimer.Start(RuleI(Character, RespawnFromHoverTimer) * 1000);
@@ -2127,7 +2130,7 @@ void NPC::Damage(Mob* other, int32 damage, uint16 spell_id, EQEmu::skills::Skill
if (IsLDoNTrapped())
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
SpellFinished(GetLDoNTrapSpellID(), other, EQEmu::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
SpellFinished(GetLDoNTrapSpellID(), other, EQEmu::spells::CastingSlot::Item, 0, -1, spells[GetLDoNTrapSpellID()].ResistDiff, false);
SetLDoNTrapSpellID(0);
SetLDoNTrapped(false);
SetLDoNTrapDetected(false);
@@ -2667,12 +2670,6 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
}
}
}
if (IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
if (!zone->watermap->InLiquid(glm::vec3(other->GetPosition()))) {
return;
}
}
// first add self
// The damage on the hate list is used to award XP to the killer. This check is to prevent Killstealing.
@@ -2694,16 +2691,24 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
#ifdef BOTS
// if other is a bot, add the bots client to the hate list
if (other->IsBot()) {
if (other->CastToBot()->GetBotOwner() && other->CastToBot()->GetBotOwner()->CastToClient()->GetFeigned()) {
AddFeignMemory(other->CastToBot()->GetBotOwner()->CastToClient());
while (other->IsBot()) {
auto other_ = other->CastToBot();
if (!other_ || !other_->GetBotOwner())
break;
auto owner_ = other_->GetBotOwner()->CastToClient();
if (!owner_ || owner_->IsDead() || !owner_->InZone()) // added isdead and inzone checks to avoid issues in AddAutoXTarget(...) below
break;
if (owner_->GetFeigned()) {
AddFeignMemory(owner_);
}
else {
if (!hate_list.IsEntOnHateList(other->CastToBot()->GetBotOwner())) {
hate_list.AddEntToHateList(other->CastToBot()->GetBotOwner(), 0, 0, false, true);
other->CastToBot()->GetBotOwner()->CastToClient()->AddAutoXTarget(this);
}
else if (!hate_list.IsEntOnHateList(owner_)) {
hate_list.AddEntToHateList(owner_, 0, 0, false, true);
owner_->AddAutoXTarget(this); // this was being called on dead/out-of-zone clients
}
break;
}
#endif //BOTS
@@ -2715,6 +2720,7 @@ void Mob::AddToHateList(Mob* other, uint32 hate /*= 0*/, int32 damage /*= 0*/, b
else {
if (!hate_list.IsEntOnHateList(other->CastToMerc()->GetMercOwner()))
hate_list.AddEntToHateList(other->CastToMerc()->GetMercOwner(), 0, 0, false, true);
// if mercs are reworked to include adding 'this' to owner's xtarget list, this should reflect bots code above
}
} //MERC
@@ -3785,7 +3791,7 @@ void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
if (IsBuffSpell(spell_id)) { // hots
// message to caster
if (caster->IsClient() && caster == this) {
if (caster->CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater)
if (caster->CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoFAndLater)
FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
HOT_HEAL_SELF, itoa(acthealed), spells[spell_id].name);
else
@@ -3793,7 +3799,7 @@ void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
YOU_HEALED, GetCleanName(), itoa(acthealed));
}
else if (caster->IsClient() && caster != this) {
if (caster->CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater)
if (caster->CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoFAndLater)
caster->FilteredMessage_StringID(caster, MT_NonMelee, FilterHealOverTime,
HOT_HEAL_OTHER, GetCleanName(), itoa(acthealed),
spells[spell_id].name);
@@ -3803,7 +3809,7 @@ void Mob::HealDamage(uint32 amount, Mob *caster, uint16 spell_id)
}
// message to target
if (IsClient() && caster != this) {
if (CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater)
if (CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoFAndLater)
FilteredMessage_StringID(this, MT_NonMelee, FilterHealOverTime,
HOT_HEALED_OTHER, caster->GetCleanName(),
itoa(acthealed), spells[spell_id].name);
@@ -4508,7 +4514,9 @@ void Mob::ApplyMeleeDamageMods(uint16 skill, int &damage, Mob *defender, ExtraAt
if (defender->IsClient() && defender->GetClass() == WARRIOR)
dmgbonusmod -= 5;
// 168 defensive
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect + itembonuses.MeleeMitigationEffect + aabonuses.MeleeMitigationEffect);
dmgbonusmod += (defender->spellbonuses.MeleeMitigationEffect +
defender->itembonuses.MeleeMitigationEffect +
defender->aabonuses.MeleeMitigationEffect);
}
damage += damage * dmgbonusmod / 100;
@@ -5032,7 +5040,7 @@ void Mob::CommonOutgoingHitSuccess(Mob* defender, DamageHitInfo &hit, ExtraAttac
hit.damage_done = headshot;
}
else if (GetClass() == RANGER && GetLevel() > 50) { // no double dmg on headshot
if (defender->IsNPC() && !defender->IsMoving() && !defender->IsRooted()) {
if ((defender->IsNPC() && !defender->IsMoving() && !defender->IsRooted()) || !RuleB(Combat, ArcheryBonusRequiresStationary)) {
hit.damage_done *= 2;
Message_StringID(MT_CritMelee, BOW_DOUBLE_DAMAGE);
}
@@ -5440,7 +5448,7 @@ void Mob::DoOffHandAttackRounds(Mob *target, ExtraAttackOptions *opts)
// For now, SPECATK_QUAD means innate DW when Combat:UseLiveCombatRounds is true
if ((GetSpecialAbility(SPECATK_INNATE_DW) ||
(RuleB(Combat, UseLiveCombatRounds) && GetSpecialAbility(SPECATK_QUAD))) ||
GetEquipment(EQEmu::textures::weaponSecondary) != 0) {
GetEquippedItemFromTextureSlot(EQEmu::textures::weaponSecondary) != 0) {
if (CheckDualWield()) {
Attack(target, EQEmu::invslot::slotSecondary, false, false, false, opts);
if (CanThisClassDoubleAttack() && GetLevel() > 35 && CheckDoubleAttack()) {
@@ -5463,3 +5471,13 @@ bool Mob::GetWasSpawnedInWater() const {
void Mob::SetSpawnedInWater(bool spawned_in_water) {
Mob::spawned_in_water = spawned_in_water;
}
int32 Mob::GetHPRegen() const
{
return hp_regen;
}
int32 Mob::GetManaRegen() const
{
return mana_regen;
}
+2 -2
View File
@@ -6,8 +6,8 @@
#include "raids.h"
Aura::Aura(NPCType *type_data, Mob *owner, AuraRecord &record)
: NPC(type_data, 0, owner->GetPosition(), FlyMode3), spell_id(record.spell_id), distance(record.distance),
remove_timer(record.duration), movement_timer(100), process_timer(100), aura_id(-1)
: NPC(type_data, 0, owner->GetPosition(), GravityBehavior::Flying), spell_id(record.spell_id), distance(record.distance),
remove_timer(record.duration), movement_timer(100), process_timer(1000), aura_id(-1)
{
GiveNPCTypeData(type_data); // we will delete this later on
m_owner = owner->GetID();
+3 -7
View File
@@ -52,22 +52,18 @@ void Mob::CalcBonuses()
We set this here because NPC's can cast spells to change walkspeed/runspeed
*/
float get_walk_speed = static_cast<float>(0.025f * this->GetWalkspeed());
if (get_walk_speed >= 0.9 && this->fix_z_timer.GetDuration() != 100) {
this->fix_z_timer.SetTimer(100);
}
rooted = FindType(SE_Root);
}
void NPC::CalcBonuses()
{
memset(&itembonuses, 0, sizeof(StatBonuses));
if(RuleB(NPC, UseItemBonusesForNonPets)){
if (RuleB(NPC, UseItemBonusesForNonPets)) {
memset(&itembonuses, 0, sizeof(StatBonuses));
CalcItemBonuses(&itembonuses);
}
else{
if(GetOwner()){
else {
if (GetOwner()) {
memset(&itembonuses, 0, sizeof(StatBonuses));
CalcItemBonuses(&itembonuses);
}
+387 -245
View File
@@ -29,7 +29,9 @@
extern volatile bool is_zone_loaded;
// This constructor is used during the bot create command
Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm::vec4(), 0, false), rest_timer(1) {
Bot::Bot(NPCType *npcTypeData, Client* botOwner) : NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1) {
GiveNPCTypeData(npcTypeData);
if(botOwner) {
this->SetBotOwner(botOwner);
this->_botOwnerCharacterID = botOwner->CharacterID();
@@ -39,29 +41,30 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
}
m_inv.SetInventoryVersion(EQEmu::versions::MobVersion::Bot);
m_inv.SetGMInventory(false); // bot expansions are not currently implemented (defaults to static)
_guildRank = 0;
_guildId = 0;
_lastTotalPlayTime = 0;
_startTotalPlayTime = time(&_startTotalPlayTime);
_lastZoneId = 0;
_baseMR = npcTypeData.MR;
_baseCR = npcTypeData.CR;
_baseDR = npcTypeData.DR;
_baseFR = npcTypeData.FR;
_basePR = npcTypeData.PR;
_baseCorrup = npcTypeData.Corrup;
_baseAC = npcTypeData.AC;
_baseSTR = npcTypeData.STR;
_baseSTA = npcTypeData.STA;
_baseDEX = npcTypeData.DEX;
_baseAGI = npcTypeData.AGI;
_baseINT = npcTypeData.INT;
_baseWIS = npcTypeData.WIS;
_baseCHA = npcTypeData.CHA;
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
_baseMR = npcTypeData->MR;
_baseCR = npcTypeData->CR;
_baseDR = npcTypeData->DR;
_baseFR = npcTypeData->FR;
_basePR = npcTypeData->PR;
_baseCorrup = npcTypeData->Corrup;
_baseAC = npcTypeData->AC;
_baseSTR = npcTypeData->STR;
_baseSTA = npcTypeData->STA;
_baseDEX = npcTypeData->DEX;
_baseAGI = npcTypeData->AGI;
_baseINT = npcTypeData->INT;
_baseWIS = npcTypeData->WIS;
_baseCHA = npcTypeData->CHA;
_baseATK = npcTypeData->ATK;
_baseRace = npcTypeData->race;
_baseGender = npcTypeData->gender;
RestRegenHP = 0;
RestRegenMana = 0;
RestRegenEndurance = 0;
@@ -79,6 +82,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
SetShowHelm(true);
SetPauseAI(false);
rest_timer.Disable();
ping_timer.Disable();
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
if (IsCasterClass(GetClass()))
SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel));
@@ -89,7 +93,7 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
GenerateAppearance();
GenerateBaseStats();
// Calculate HitPoints Last As It Uses Base Stats
cur_hp = GenerateBaseHitPoints();
current_hp = GenerateBaseHitPoints();
current_mana = GenerateBaseManaPoints();
cur_end = CalcBaseEndurance();
hp_regen = CalcHPRegen();
@@ -103,8 +107,11 @@ Bot::Bot(NPCType npcTypeData, Client* botOwner) : NPC(&npcTypeData, nullptr, glm
}
// This constructor is used when the bot is loaded out of the database
Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData) : NPC(&npcTypeData, nullptr, glm::vec4(), 0, false), rest_timer(1)
Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData)
: NPC(npcTypeData, nullptr, glm::vec4(), Ground, false), rest_timer(1), ping_timer(1)
{
GiveNPCTypeData(npcTypeData);
this->_botOwnerCharacterID = botOwnerCharacterID;
if(this->_botOwnerCharacterID > 0)
this->SetBotOwner(entity_list.GetClientByCharID(this->_botOwnerCharacterID));
@@ -112,6 +119,7 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
auto bot_owner = GetBotOwner();
m_inv.SetInventoryVersion(EQEmu::versions::MobVersion::Bot);
m_inv.SetGMInventory(false); // bot expansions are not currently implemented (defaults to static)
_guildRank = 0;
_guildId = 0;
@@ -119,25 +127,25 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
_startTotalPlayTime = time(&_startTotalPlayTime);
_lastZoneId = lastZoneId;
berserk = false;
_baseMR = npcTypeData.MR;
_baseCR = npcTypeData.CR;
_baseDR = npcTypeData.DR;
_baseFR = npcTypeData.FR;
_basePR = npcTypeData.PR;
_baseCorrup = npcTypeData.Corrup;
_baseAC = npcTypeData.AC;
_baseSTR = npcTypeData.STR;
_baseSTA = npcTypeData.STA;
_baseDEX = npcTypeData.DEX;
_baseAGI = npcTypeData.AGI;
_baseINT = npcTypeData.INT;
_baseWIS = npcTypeData.WIS;
_baseCHA = npcTypeData.CHA;
_baseATK = npcTypeData.ATK;
_baseRace = npcTypeData.race;
_baseGender = npcTypeData.gender;
cur_hp = npcTypeData.cur_hp;
current_mana = npcTypeData.Mana;
_baseMR = npcTypeData->MR;
_baseCR = npcTypeData->CR;
_baseDR = npcTypeData->DR;
_baseFR = npcTypeData->FR;
_basePR = npcTypeData->PR;
_baseCorrup = npcTypeData->Corrup;
_baseAC = npcTypeData->AC;
_baseSTR = npcTypeData->STR;
_baseSTA = npcTypeData->STA;
_baseDEX = npcTypeData->DEX;
_baseAGI = npcTypeData->AGI;
_baseINT = npcTypeData->INT;
_baseWIS = npcTypeData->WIS;
_baseCHA = npcTypeData->CHA;
_baseATK = npcTypeData->ATK;
_baseRace = npcTypeData->race;
_baseGender = npcTypeData->gender;
current_hp = npcTypeData->current_hp;
current_mana = npcTypeData->Mana;
RestRegenHP = 0;
RestRegenMana = 0;
RestRegenEndurance = 0;
@@ -155,10 +163,11 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
if (!stance_flag && bot_owner)
bot_owner->Message(13, "Could not locate stance for '%s'", GetCleanName());
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == BotStanceAggressive));
SetTaunting((GetClass() == WARRIOR || GetClass() == PALADIN || GetClass() == SHADOWKNIGHT) && (GetBotStance() == EQEmu::constants::stanceAggressive));
SetPauseAI(false);
rest_timer.Disable();
ping_timer.Disable();
SetFollowDistance(BOT_FOLLOW_DISTANCE_DEFAULT);
if (IsCasterClass(GetClass()))
SetStopMeleeLevel((uint8)RuleI(Bots, CasterStopMeleeLevel));
@@ -208,10 +217,10 @@ Bot::Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double to
hp_regen = CalcHPRegen();
mana_regen = CalcManaRegen();
end_regen = CalcEnduranceRegen();
if(cur_hp > max_hp)
cur_hp = max_hp;
if(current_hp > max_hp)
current_hp = max_hp;
if(cur_hp <= 0) {
if(current_hp <= 0) {
SetHP(max_hp/5);
SetMana(0);
BuffFadeAll();
@@ -284,8 +293,7 @@ void Bot::ChangeBotArcherWeapons(bool isArcher) {
void Bot::Sit() {
if(IsMoving()) {
moved = false;
SetCurrentSpeed(0);
tar_ndx = 0;
StopNavigation();
}
SetAppearance(eaSitting);
@@ -311,112 +319,252 @@ bool Bot::IsStanding() {
return result;
}
NPCType Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) {
NPCType BotNPCType;
int CopyLength = 0;
CopyLength = botName.copy(BotNPCType.name, 63);
BotNPCType.name[CopyLength] = '\0';
CopyLength = 0;
CopyLength = botLastName.copy(BotNPCType.lastname, 69);
BotNPCType.lastname[CopyLength] = '\0';
CopyLength = 0;
BotNPCType.npc_spells_id = botSpellsID;
BotNPCType.level = botLevel;
BotNPCType.race = botRace;
BotNPCType.class_ = botClass;
BotNPCType.gender = gender;
BotNPCType.size = size;
BotNPCType.luclinface = face;
BotNPCType.hairstyle = hairStyle;
BotNPCType.haircolor = hairColor;
BotNPCType.eyecolor1 = eyeColor;
BotNPCType.eyecolor2 = eyeColor2;
BotNPCType.beardcolor = beardColor;
BotNPCType.beard = beard;
BotNPCType.drakkin_heritage = drakkinHeritage;
BotNPCType.drakkin_tattoo = drakkinTattoo;
BotNPCType.drakkin_details = drakkinDetails;
BotNPCType.cur_hp = hp;
BotNPCType.Mana = mana;
BotNPCType.MR = mr;
BotNPCType.CR = cr;
BotNPCType.DR = dr;
BotNPCType.FR = fr;
BotNPCType.PR = pr;
BotNPCType.Corrup = corrup;
BotNPCType.AC = ac;
BotNPCType.STR = str;
BotNPCType.STA = sta;
BotNPCType.DEX = dex;
BotNPCType.AGI = agi;
BotNPCType.INT = _int;
BotNPCType.WIS = wis;
BotNPCType.CHA = cha;
BotNPCType.ATK = attack;
BotNPCType.npc_id = 0;
BotNPCType.texture = 0;
BotNPCType.d_melee_texture1 = 0;
BotNPCType.d_melee_texture2 = 0;
BotNPCType.qglobal = false;
BotNPCType.attack_speed = 0;
BotNPCType.runspeed = 0.7f;
BotNPCType.bodytype = 1;
BotNPCType.findable = 0;
BotNPCType.hp_regen = 1;
BotNPCType.mana_regen = 1;
BotNPCType.maxlevel = botLevel;
BotNPCType.light = 0; // due to the way that bots are coded..this is sent post-spawn
return BotNPCType;
NPCType *Bot::FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
bot_npc_type->current_hp = hp;
bot_npc_type->max_hp = hp;
bot_npc_type->size = size;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQEmu::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
bot_npc_type->npc_spells_id = botSpellsID;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = ac;
bot_npc_type->Mana = mana;
bot_npc_type->ATK = attack;
bot_npc_type->STR = str;
bot_npc_type->STA = sta;
bot_npc_type->DEX = dex;
bot_npc_type->AGI = agi;
bot_npc_type->INT = _int;
bot_npc_type->WIS = wis;
bot_npc_type->CHA = cha;
bot_npc_type->MR = mr;
bot_npc_type->FR = fr;
bot_npc_type->CR = cr;
bot_npc_type->PR = pr;
bot_npc_type->DR = dr;
bot_npc_type->Corrup = corrup;
//bot_npc_type->PhR = 0;
bot_npc_type->haircolor = hairColor;
bot_npc_type->beardcolor = beardColor;
bot_npc_type->eyecolor1 = eyeColor;
bot_npc_type->eyecolor2 = eyeColor2;
bot_npc_type->hairstyle = hairStyle;
bot_npc_type->luclinface = face;
bot_npc_type->beard = beard;
bot_npc_type->drakkin_heritage = drakkinHeritage;
bot_npc_type->drakkin_tattoo = drakkinTattoo;
bot_npc_type->drakkin_details = drakkinDetails;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
return bot_npc_type;
}
NPCType Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) {
NPCType Result;
int CopyLength = 0;
CopyLength = botName.copy(Result.name, 63);
Result.name[CopyLength] = '\0';
CopyLength = 0;
CopyLength = botLastName.copy(Result.lastname, 69);
Result.lastname[CopyLength] = '\0';
CopyLength = 0;
Result.level = botLevel;
Result.race = botRace;
Result.class_ = botClass;
Result.gender = gender;
// default values
Result.maxlevel = botLevel;
Result.size = 6.0;
Result.npc_id = 0;
Result.cur_hp = 0;
Result.drakkin_details = 0;
Result.drakkin_heritage = 0;
Result.drakkin_tattoo = 0;
Result.runspeed = 0.7f;
Result.bodytype = 1;
Result.findable = 0;
Result.hp_regen = 1;
Result.mana_regen = 1;
Result.texture = 0;
Result.d_melee_texture1 = 0;
Result.d_melee_texture2 = 0;
Result.qglobal = false;
Result.npc_spells_id = 0;
Result.attack_speed = 0;
Result.STR = 75;
Result.STA = 75;
Result.DEX = 75;
Result.AGI = 75;
Result.WIS = 75;
Result.INT = 75;
Result.CHA = 75;
Result.ATK = 75;
Result.MR = 25;
Result.FR = 25;
Result.DR = 15;
Result.PR = 15;
Result.CR = 25;
Result.Corrup = 15;
Result.AC = 12;
return Result;
NPCType *Bot::CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender) {
auto bot_npc_type = new NPCType{ 0 };
int copy_length = 0;
copy_length = botName.copy(bot_npc_type->name, 63);
bot_npc_type->name[copy_length] = '\0';
copy_length = 0;
copy_length = botLastName.copy(bot_npc_type->lastname, 69);
bot_npc_type->lastname[copy_length] = '\0';
copy_length = 0;
//bot_npc_type->current_hp = 0;
//bot_npc_type->max_hp = 0;
bot_npc_type->size = 6.0f;
bot_npc_type->runspeed = 0.7f;
bot_npc_type->gender = gender;
bot_npc_type->race = botRace;
bot_npc_type->class_ = botClass;
bot_npc_type->bodytype = 1;
bot_npc_type->deity = EQEmu::deity::DeityAgnostic;
bot_npc_type->level = botLevel;
//bot_npc_type->npc_id = 0;
//bot_npc_type->texture = 0;
//bot_npc_type->helmtexture = 0;
//bot_npc_type->herosforgemodel = 0;
//bot_npc_type->loottable_id = 0;
//bot_npc_type->npc_spells_id = 0;
//bot_npc_type->npc_spells_effects_id = 0;
//bot_npc_type->npc_faction_id = 0;
//bot_npc_type->merchanttype = 0;
//bot_npc_type->alt_currency_type = 0;
//bot_npc_type->adventure_template = 0;
//bot_npc_type->trap_template = 0;
//bot_npc_type->light = 0;
bot_npc_type->AC = 12;
//bot_npc_type->Mana = 0;
bot_npc_type->ATK = 75;
bot_npc_type->STR = 75;
bot_npc_type->STA = 75;
bot_npc_type->DEX = 75;
bot_npc_type->AGI = 75;
bot_npc_type->INT = 75;
bot_npc_type->WIS = 75;
bot_npc_type->CHA = 75;
bot_npc_type->MR = 25;
bot_npc_type->FR = 25;
bot_npc_type->CR = 25;
bot_npc_type->PR = 15;
bot_npc_type->DR = 15;
bot_npc_type->Corrup = 15;
//bot_npc_type->PhR = 0;
//bot_npc_type->haircolor = 0;
//bot_npc_type->beardcolor = 0;
//bot_npc_type->eyecolor1 = 0;
//bot_npc_type->eyecolor2 = 0;
//bot_npc_type->hairstyle = 0;
//bot_npc_type->luclinface = 0;
//bot_npc_type->beard = 0;
//bot_npc_type->drakkin_heritage = 0;
//bot_npc_type->drakkin_tattoo = 0;
//bot_npc_type->drakkin_details = 0;
//bot_npc_type->armor_tint = { 0 };
//bot_npc_type->min_dmg = 0;
//bot_npc_type->max_dmg = 0;
//bot_npc_type->charm_ac = 0;
//bot_npc_type->charm_min_dmg = 0;
//bot_npc_type->charm_max_dmg = 0;
//bot_npc_type->charm_attack_delay = 0;
//bot_npc_type->charm_accuracy_rating = 0;
//bot_npc_type->charm_avoidance_rating = 0;
//bot_npc_type->charm_atk = 0;
//bot_npc_type->attack_count = 0;
//*bot_npc_type->special_abilities = { 0 };
//bot_npc_type->d_melee_texture1 = 0;
//bot_npc_type->d_melee_texture2 = 0;
//*bot_npc_type->ammo_idfile = { 0 };
//bot_npc_type->prim_melee_type = 0;
//bot_npc_type->sec_melee_type = 0;
//bot_npc_type->ranged_type = 0;
bot_npc_type->hp_regen = 1;
bot_npc_type->mana_regen = 1;
//bot_npc_type->aggroradius = 0;
//bot_npc_type->assistradius = 0;
//bot_npc_type->see_invis = 0;
//bot_npc_type->see_invis_undead = false;
//bot_npc_type->see_hide = false;
//bot_npc_type->see_improved_hide = false;
//bot_npc_type->qglobal = false;
//bot_npc_type->npc_aggro = false;
//bot_npc_type->spawn_limit = 0;
//bot_npc_type->mount_color = 0;
//bot_npc_type->attack_speed = 0.0f;
//bot_npc_type->attack_delay = 0;
//bot_npc_type->accuracy_rating = 0;
//bot_npc_type->avoidance_rating = 0;
//bot_npc_type->findable = false;
bot_npc_type->trackable = true;
//bot_npc_type->slow_mitigation = 0;
bot_npc_type->maxlevel = botLevel;
//bot_npc_type->scalerate = 0;
//bot_npc_type->private_corpse = false;
//bot_npc_type->unique_spawn_by_name = false;
//bot_npc_type->underwater = false;
//bot_npc_type->emoteid = 0;
//bot_npc_type->spellscale = 0.0f;
//bot_npc_type->healscale = 0.0f;
//bot_npc_type->no_target_hotkey = false;
//bot_npc_type->raid_target = false;
//bot_npc_type->armtexture = 0;
//bot_npc_type->bracertexture = 0;
//bot_npc_type->handtexture = 0;
//bot_npc_type->legtexture = 0;
//bot_npc_type->feettexture = 0;
//bot_npc_type->ignore_despawn = false;
bot_npc_type->show_name = true;
//bot_npc_type->untargetable = false;
bot_npc_type->skip_global_loot = true;
//bot_npc_type->rare_spawn = false;
bot_npc_type->stuck_behavior = Ground;
return bot_npc_type;
}
void Bot::GenerateBaseStats()
@@ -1827,7 +1975,6 @@ bool Bot::Process() {
if(tic_timer.Check()) {
//6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting
if(!IsMoving() && !IsEngaged()) {
SendPosition();
if(IsSitting()) {
if(!rest_timer.Enabled())
rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
@@ -1866,6 +2013,17 @@ bool Bot::Process() {
if(GetAppearance() == eaDead && GetHP() > 0)
SetAppearance(eaStanding);
if (IsMoving()) {
ping_timer.Disable();
}
else {
if (!ping_timer.Enabled())
ping_timer.Start(BOT_KEEP_ALIVE_INTERVAL);
if (ping_timer.Check())
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
}
if (IsStunned() || IsMezzed())
return true;
@@ -2090,10 +2248,9 @@ void Bot::AI_Process() {
Client* bot_owner = (GetBotOwner() && GetBotOwner()->IsClient() ? GetBotOwner()->CastToClient() : nullptr);
Group* bot_group = GetGroup();
Mob* follow_mob = entity_list.GetMob(GetFollowID());
// Primary reasons for not processing AI
if (!bot_owner || !bot_group || !follow_mob || !IsAIControlled())
if (!bot_owner || !bot_group || !IsAIControlled())
return;
if (bot_owner->IsDead()) {
@@ -2103,11 +2260,18 @@ void Bot::AI_Process() {
return;
}
// We also need a leash owner (subset of primary AI criteria)
// We also need a leash owner and follow mob (subset of primary AI criteria)
Client* leash_owner = (bot_group->GetLeader() && bot_group->GetLeader()->IsClient() ? bot_group->GetLeader()->CastToClient() : bot_owner);
if (!leash_owner)
return;
Mob* follow_mob = entity_list.GetMob(GetFollowID());
if (!follow_mob) {
follow_mob = leash_owner;
SetFollowID(leash_owner->GetID());
}
// Berserk updates should occur if primary AI criteria are met
if (GetClass() == WARRIOR || GetClass() == BERSERKER) {
if (!berserk && GetHP() > 0 && GetHPRatio() < 30.0f) {
@@ -2526,7 +2690,7 @@ void Bot::AI_Process() {
if (GetArchetype() == ARCHETYPE_CASTER || GetClass() == ROGUE) {
if (tar_distance <= melee_distance_max) {
if (PlotPositionAroundTarget(this, Goal.x, Goal.y, Goal.z)) {
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotWalkspeed(), true, false);
WalkTo(Goal.x, Goal.y, Goal.z);
return;
}
}
@@ -2538,7 +2702,7 @@ void Bot::AI_Process() {
if (caster_distance_min && tar_distance < caster_distance_min && !tar->IsFeared()) { // Caster back-off adjustment
if (PlotPositionAroundTarget(this, Goal.x, Goal.y, Goal.z)) {
if (DistanceSquared(Goal, tar->GetPosition()) <= caster_distance_max) {
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotWalkspeed(), true, false);
WalkTo(Goal.x, Goal.y, Goal.z);
return;
}
}
@@ -2546,7 +2710,7 @@ void Bot::AI_Process() {
else if (tar_distance < melee_distance_min) { // Melee back-off adjustment
if (PlotPositionAroundTarget(this, Goal.x, Goal.y, Goal.z)) {
if (DistanceSquared(Goal, tar->GetPosition()) <= melee_distance_max) {
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotWalkspeed(), true, false);
WalkTo(Goal.x, Goal.y, Goal.z);
return;
}
}
@@ -2554,7 +2718,7 @@ void Bot::AI_Process() {
else if (backstab_weapon && !behind_mob) { // Move the rogue to behind the mob
if (PlotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z)) {
if (DistanceSquared(Goal, tar->GetPosition()) <= melee_distance_max) {
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotRunspeed(), true, false); // rogues are agile enough to run in melee range
RunTo(Goal.x, Goal.y, Goal.z);
return;
}
}
@@ -2565,7 +2729,7 @@ void Bot::AI_Process() {
PlotPositionAroundTarget(tar, Goal.x, Goal.y, Goal.z)) // If we're behind the mob, we can attack when it's enraged
{
if (DistanceSquared(Goal, tar->GetPosition()) <= melee_distance_max) {
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotWalkspeed(), true, false);
WalkTo(Goal.x, Goal.y, Goal.z);
return;
}
}
@@ -2602,7 +2766,7 @@ void Bot::AI_Process() {
// we can't fight if we don't have a target, are stun/mezzed or dead..
// Stop attacking if the target is enraged
TEST_TARGET();
if (GetBotStance() == BotStancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
if (GetBotStance() == EQEmu::constants::stancePassive || (tar->IsEnraged() && !BehindMob(tar, GetX(), GetY())))
return;
// First, special attack per class (kick, backstab etc..)
@@ -2710,42 +2874,26 @@ void Bot::AI_Process() {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", GetTarget()->GetCleanName());
Goal = GetTarget()->GetPosition();
if (RuleB(Bots, UsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached;
Goal = UpdatePath(Goal.x, Goal.y, Goal.z,
GetBotRunspeed(), WaypointChanged, NodeReached);
if (WaypointChanged)
tar_ndx = 20;
}
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetBotRunspeed());
RunTo(Goal.x, Goal.y, Goal.z);
return;
}
else {
if (IsMoving())
StopMoving();
else
SendPosition();
return;
}
}
// Fix Z when following during pull, not when engaged and stationary
if (IsMoving() && fix_z_timer_engaged.Check()) {
FixZ();
return;
}
if (GetTarget() && GetTarget()->IsFeared() && !spellend_timer.Enabled() && AI_think_timer->Check()) {
if (!IsFacingMob(GetTarget()))
FaceTarget(GetTarget());
// This is a mob that is fleeing either because it has been feared or is low on hitpoints
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
AI_PursueCastCheck(); // This appears to always return true..can't trust for success/fail
return;
}
@@ -2753,9 +2901,7 @@ void Bot::AI_Process() {
} // end not in combat range
if (!IsMoving() && !spellend_timer.Enabled()) { // This may actually need work...
SendPosition();
if (GetBotStance() == BotStancePassive)
if (GetBotStance() == EQEmu::constants::stancePassive)
return;
if (GetTarget() && AI_EngagedCastCheck())
@@ -2789,10 +2935,10 @@ void Bot::AI_Process() {
if (IsMoving())
StopMoving();
Warp(glm::vec3(my_guard));
Teleport(my_guard);
if (HasPet())
GetPet()->Warp(glm::vec3(my_guard));
GetPet()->Teleport(my_guard);
return;
}
@@ -2802,10 +2948,10 @@ void Bot::AI_Process() {
if (IsMoving())
StopMoving();
Warp(glm::vec3(leash_owner->GetPosition()));
Teleport(leash_owner->GetPosition());
if (HasPet())
GetPet()->Warp(glm::vec3(leash_owner->GetPosition()));
GetPet()->Teleport(leash_owner->GetPosition());
return;
}
@@ -2813,7 +2959,7 @@ void Bot::AI_Process() {
// Ok to idle
if (fm_dist <= GetFollowDistance()) {
if (!IsMoving() && AI_think_timer->Check() && !spellend_timer.Enabled()) {
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
if (!AI_IdleCastCheck() && !IsCasting() && GetClass() != BARD)
BotMeditate(true);
}
@@ -2833,23 +2979,18 @@ void Bot::AI_Process() {
if (rest_timer.Enabled())
rest_timer.Disable();
int speed = GetBotRunspeed();
bool running = true;
if (fm_dist < GetFollowDistance() + BOT_FOLLOW_DISTANCE_WALK)
speed = GetBotWalkspeed();
running = false;
Goal = follow_mob->GetPosition();
if (RuleB(Bots, UsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached;
Goal = UpdatePath(Goal.x, Goal.y, Goal.z,
speed, WaypointChanged, NodeReached);
if (WaypointChanged)
tar_ndx = 20;
if (running) {
RunTo(Goal.x, Goal.y, Goal.z);
}
else {
WalkTo(Goal.x, Goal.y, Goal.z);
}
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed);
return;
}
}
@@ -2863,7 +3004,7 @@ void Bot::AI_Process() {
// Basically, bard bots get a chance to cast idle spells while moving
if (IsMoving()) {
if (GetBotStance() != BotStancePassive) {
if (GetBotStance() != EQEmu::constants::stancePassive) {
if (GetClass() == BARD && !spellend_timer.Enabled() && AI_think_timer->Check()) {
AI_IdleCastCheck();
return;
@@ -2931,14 +3072,14 @@ void Bot::PetAIProcess() {
if(botPet->GetClass() == ROGUE && !petHasAggro && !botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) {
// Move the rogue to behind the mob
if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ)) {
botPet->CalculateNewPosition(newX, newY, newZ, botPet->GetRunspeed());
botPet->RunTo(newX, newY, newZ);
return;
}
}
else if(GetTarget() == botPet->GetTarget() && !petHasAggro && !botPet->BehindMob(botPet->GetTarget(), botPet->GetX(), botPet->GetY())) {
// If the bot owner and the bot are fighting the same mob, then move the pet to the rear arc of the mob
if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ)) {
botPet->CalculateNewPosition(newX, newY, newZ, botPet->GetRunspeed());
botPet->RunTo(newX, newY, newZ);
return;
}
}
@@ -2953,7 +3094,7 @@ void Bot::PetAIProcess() {
moveBehindMob = true;
if(botPet->PlotPositionAroundTarget(botPet->GetTarget(), newX, newY, newZ, moveBehindMob)) {
botPet->CalculateNewPosition(newX, newY, newZ, botPet->GetRunspeed());
botPet->RunTo(newX, newY, newZ);
return;
}
}
@@ -3036,15 +3177,14 @@ void Bot::PetAIProcess() {
botPet->SetRunAnimSpeed(0);
if(!botPet->IsRooted()) {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", botPet->GetTarget()->GetCleanName());
botPet->CalculateNewPosition(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetOwner()->GetRunspeed());
botPet->RunTo(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ());
return;
} else {
botPet->SetHeading(botPet->GetTarget()->GetHeading());
if(moved) {
moved = false;
SetCurrentSpeed(0);
botPet->SendPosition();
botPet->SetMoving(false);
StopNavigation();
botPet->StopNavigation();
}
}
}
@@ -3064,15 +3204,14 @@ void Bot::PetAIProcess() {
float dist = DistanceSquared(botPet->GetPosition(), botPet->GetTarget()->GetPosition());
botPet->SetRunAnimSpeed(0);
if(dist > 184) {
botPet->CalculateNewPosition(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ(), botPet->GetTarget()->GetRunspeed());
botPet->RunTo(botPet->GetTarget()->GetX(), botPet->GetTarget()->GetY(), botPet->GetTarget()->GetZ());
return;
} else {
botPet->SetHeading(botPet->GetTarget()->GetHeading());
if(moved) {
moved = false;
SetCurrentSpeed(0);
botPet->SendPosition();
botPet->SetMoving(false);
StopNavigation();
botPet->StopNavigation();
}
}
break;
@@ -3134,7 +3273,8 @@ bool Bot::Spawn(Client* botCharacterOwner) {
entity_list.AddBot(this, true, true);
// Load pet
LoadPet();
this->SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
ping_timer.Start(8000);
// there is something askew with spawn struct appearance fields...
// I re-enabled this until I can sort it out
uint32 itemID = 0;
@@ -5173,7 +5313,7 @@ int Bot::GetHandToHandDamage(void) {
// everyone uses this in the revamp!
int skill = GetSkill(EQEmu::skills::SkillHandtoHand);
int epic = 0;
if (CastToNPC()->GetEquipment(EQEmu::textures::armorHands) == 10652 && GetLevel() > 46)
if (CastToNPC()->GetEquippedItemFromTextureSlot(EQEmu::textures::armorHands) == 10652 && GetLevel() > 46)
epic = 280;
if (epic > skill)
skill = epic;
@@ -5195,7 +5335,7 @@ int Bot::GetHandToHandDamage(void) {
9, 9, 9, 9, 9, 10, 10, 10, 10, 10, // 31-40
10, 11, 11, 11, 11, 11, 11, 12, 12}; // 41-49
if (GetClass() == MONK) {
if (CastToNPC()->GetEquipment(EQEmu::textures::armorHands) == 10652 && GetLevel() > 50)
if (CastToNPC()->GetEquippedItemFromTextureSlot(EQEmu::textures::armorHands) == 10652 && GetLevel() > 50)
return 9;
if (level > 62)
return 15;
@@ -6287,7 +6427,7 @@ void Bot::DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster) {
Mob::DoBuffTic(buff, slot, caster);
}
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost,
bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot, int32 cast_time, int32 mana_cost,
uint32* oSpellWillFinish, uint32 item_slot, int16 *resist_adjust, uint32 aa_id) {
bool Result = false;
if(zone && !zone->IsSpellBlocked(spell_id, glm::vec3(GetPosition()))) {
@@ -6327,7 +6467,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot,
return false;
}
if(slot < EQEmu::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
if(slot < EQEmu::spells::CastingSlot::MaxGems && !CheckFizzle(spell_id)) {
int fizzle_msg = IsBardSong(spell_id) ? MISS_NOTE : SPELL_FIZZLE;
InterruptSpell(fizzle_msg, 0x121, spell_id);
@@ -6341,7 +6481,7 @@ bool Bot::CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot,
Log(Logs::Detail, Logs::Spells, "Casting a new spell/song while singing a song. Killing old song %d.", bardsong);
bardsong = 0;
bardsong_target_id = 0;
bardsong_slot = EQEmu::CastingSlot::Gem1;
bardsong_slot = EQEmu::spells::CastingSlot::Gem1;
bardsong_timer.Disable();
}
@@ -6471,7 +6611,7 @@ bool Bot::IsImmuneToSpell(uint16 spell_id, Mob *caster) {
return Result;
}
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot) {
bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::spells::CastingSlot slot) {
bool Result = false;
SpellTargetType targetType = spells[spell_id].targettype;
if(targetType == ST_GroupClientAndPet) {
@@ -6484,7 +6624,7 @@ bool Bot::DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_ce
return Result;
}
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
bool Bot::DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot, int32 cast_time, int32 mana_cost, uint32* oSpellWillFinish, uint32 item_slot, uint32 aa_id) {
bool Result = false;
if(GetClass() == BARD)
cast_time = 0;
@@ -6588,7 +6728,7 @@ void Bot::GenerateSpecialAttacks() {
SetSpecialAbility(SPECATK_TRIPLE, 1);
}
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool& stopLogic) {
if(GetClass() == BARD) {
if(!ApplyNextBardPulse(bardsong, this, bardsong_slot))
InterruptSpell(SONG_ENDS_ABRUPTLY, 0x121, bardsong);
@@ -6598,7 +6738,7 @@ bool Bot::DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::Cast
return true;
}
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool& stopLogic) {
if(spellTarget) {
if(IsGrouped() && (spellTarget->IsBot() || spellTarget->IsClient()) && RuleB(Bots, GroupBuffing)) {
bool noGroupSpell = false;
@@ -6610,7 +6750,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::
bool spelltypeequal = ((spelltype == 2) || (spelltype == 16) || (spelltype == 32));
bool spelltypetargetequal = ((spelltype == 8) && (spells[thespell].targettype == ST_Self));
bool spelltypeclassequal = ((spelltype == 1024) && (GetClass() == SHAMAN));
bool slotequal = (slot == EQEmu::CastingSlot::Item);
bool slotequal = (slot == EQEmu::spells::CastingSlot::Item);
if(spellequal || slotequal) {
if((spelltypeequal || spelltypetargetequal) || spelltypeclassequal || slotequal) {
if(((spells[thespell].effectid[0] == 0) && (spells[thespell].base[0] < 0)) &&
@@ -6649,7 +6789,7 @@ bool Bot::DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::
return true;
}
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool& stopLogic) {
bool Bot::DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool& stopLogic) {
bool isMainGroupMGB = false;
if(isMainGroupMGB && (GetClass() != BARD)) {
BotGroupSay(this, "MGB %s", spells[spell_id].name);
@@ -7185,14 +7325,14 @@ int32 Bot::CalcMaxHP() {
bot_hp += GroupLeadershipAAHealthEnhancement();
bot_hp += (bot_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f));
max_hp = bot_hp;
if (cur_hp > max_hp)
cur_hp = max_hp;
if (current_hp > max_hp)
current_hp = max_hp;
int hp_perc_cap = spellbonuses.HPPercCap[0];
if(hp_perc_cap) {
int curHP_cap = ((max_hp * hp_perc_cap) / 100);
if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1]))
cur_hp = curHP_cap;
if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1]))
current_hp = curHP_cap;
}
return max_hp;
}
@@ -8073,7 +8213,7 @@ bool Bot::CheckLoreConflict(const EQEmu::ItemData* item) {
}
bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
Log(Logs::General, Logs::Error, "Error: detrimental spells requested from AICheckCloseBeneficialSpells!!");
return false;
}
@@ -8128,19 +8268,19 @@ bool EntityList::Bot_AICheckCloseBeneficialSpells(Bot* caster, uint8 iChance, fl
Group *g = caster->GetGroup();
float hpRatioToHeal = 25.0f;
switch(caster->GetBotStance()) {
case BotStanceReactive:
case BotStanceBalanced:
hpRatioToHeal = 50.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
hpRatioToHeal = 20.0f;
break;
case BotStanceAggressive:
case BotStanceEfficient:
default:
hpRatioToHeal = 25.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceBalanced:
hpRatioToHeal = 50.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
hpRatioToHeal = 20.0f;
break;
case EQEmu::constants::stanceAggressive:
case EQEmu::constants::stanceEfficient:
default:
hpRatioToHeal = 25.0f;
break;
}
if(g) {
@@ -8679,11 +8819,11 @@ bool Bot::HasOrMayGetAggro() {
}
void Bot::SetDefaultBotStance() {
BotStanceType defaultStance = BotStanceBalanced;
EQEmu::constants::StanceType defaultStance = EQEmu::constants::stanceBalanced;
if (GetClass() == WARRIOR)
defaultStance = BotStanceAggressive;
defaultStance = EQEmu::constants::stanceAggressive;
_baseBotStance = BotStancePassive;
_baseBotStance = EQEmu::constants::stancePassive;
_botStance = defaultStance;
}
@@ -8732,7 +8872,7 @@ bool Bot::UseDiscipline(uint32 spell_id, uint32 target) {
if(IsCasting())
InterruptSpell();
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
CastSpell(spell_id, target, EQEmu::spells::CastingSlot::Discipline);
return true;
}
@@ -8956,4 +9096,6 @@ std::string Bot::CreateSayLink(Client* c, const char* message, const char* name)
return saylink;
}
uint8 Bot::spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND] = { 0 };
#endif
+56 -122
View File
@@ -44,6 +44,8 @@
#define BOT_LEASH_DISTANCE 250000 // as DSq value (500 units)
#define BOT_KEEP_ALIVE_INTERVAL 5000 // 5 seconds
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
@@ -52,91 +54,7 @@ const int MaxDisciplineTimer = 10;
const int DisciplineReuseStart = MaxSpellTimer + 1;
const int MaxTimer = MaxSpellTimer + MaxDisciplineTimer;
enum BotStanceType {
BotStancePassive,
BotStanceBalanced,
BotStanceEfficient,
BotStanceReactive,
BotStanceAggressive,
BotStanceBurn,
BotStanceBurnAE,
BotStanceUnknown,
MaxStances = BotStanceUnknown
};
#define BOT_STANCE_COUNT 8
#define VALIDBOTSTANCE(x) ((x >= (int)BotStancePassive && x <= (int)BotStanceBurnAE) ? ((BotStanceType)x) : (BotStanceUnknown))
static const std::string bot_stance_name[BOT_STANCE_COUNT] = {
"Passive", // 0
"Balanced", // 1
"Efficient", // 2
"Reactive", // 3
"Aggressive", // 4
"Burn", // 5
"BurnAE", // 6
"Unknown" // 7
};
static const char* GetBotStanceName(int stance_id) { return bot_stance_name[VALIDBOTSTANCE(stance_id)].c_str(); }
#define VALIDBOTEQUIPSLOT(x) ((x >= EQEmu::invslot::EQUIPMENT_BEGIN && x <= EQEmu::invslot::EQUIPMENT_END) ? (x) : (EQEmu::invslot::EQUIPMENT_COUNT))
static const std::string bot_equip_slot_name[EQEmu::invslot::EQUIPMENT_COUNT + 1] =
{
"Charm", // slotCharm
"Ear 1", // slotEar1
"Head", // slotHead
"Face", // slotFace
"Ear 2", // slotEar2
"Neck", // slotNeck
"Shoulders", // slotShoulders
"Arms", // slotArms
"Back", // slotBack
"Wrist 1", // slotWrist1
"Wrist 2", // slotWrist2
"Range", // slotRange
"Hands", // slotHands
"Primary", // slotPrimary
"Secondary", // slotSecondary
"Finger 1", // slotFinger1
"Finger 2", // slotFinger2
"Chest", // slotChest
"Legs", // slotLegs
"Feet", // slotFeet
"Waist", // slotWaist
"Power Source", // slotPowerSource
"Ammo", // slotAmmo
"Unknown"
};
static const char* GetBotEquipSlotName(int slot_id) { return bot_equip_slot_name[VALIDBOTEQUIPSLOT(slot_id)].c_str(); }
enum SpellTypeIndex {
SpellType_NukeIndex,
SpellType_HealIndex,
SpellType_RootIndex,
SpellType_BuffIndex,
SpellType_EscapeIndex,
SpellType_PetIndex,
SpellType_LifetapIndex,
SpellType_SnareIndex,
SpellType_DOTIndex,
SpellType_DispelIndex,
SpellType_InCombatBuffIndex,
SpellType_MezIndex,
SpellType_CharmIndex,
SpellType_SlowIndex,
SpellType_DebuffIndex,
SpellType_CureIndex,
SpellType_ResurrectIndex,
SpellType_HateReduxIndex,
SpellType_InCombatBuffSongIndex,
SpellType_OutOfCombatBuffSongIndex,
SpellType_PreCombatBuffIndex,
SpellType_PreCombatBuffSongIndex,
MaxSpellTypes
};
// nHSND negative Healer/Slower/Nuker/Doter
// pH positive Healer
@@ -226,32 +144,38 @@ public:
BotRoleRaidHealer
};
enum EqExpansions { // expansions are off..EQ should be '0'
ExpansionNone,
ExpansionEQ,
ExpansionRoK,
ExpansionSoV,
ExpansionSoL,
ExpansionPoP,
ExpansionLoY,
ExpansionLDoN,
ExpansionGoD,
ExpansionOoW,
ExpansionDoN,
ExpansionDoDH,
ExpansionPoR,
ExpansionTSS,
ExpansionSoF,
ExpansionSoD,
ExpansionUF,
ExpansionHoT,
ExpansionVoA,
ExpansionRoF
enum SpellTypeIndex : uint32 {
spellTypeIndexNuke,
spellTypeIndexHeal,
spellTypeIndexRoot,
spellTypeIndexBuff,
spellTypeIndexEscape,
spellTypeIndexPet,
spellTypeIndexLifetap,
spellTypeIndexSnare,
spellTypeIndexDot,
spellTypeIndexDispel,
spellTypeIndexInCombatBuff,
spellTypeIndexMez,
spellTypeIndexCharm,
spellTypeIndexSlow,
spellTypeIndexDebuff,
spellTypeIndexCure,
spellTypeIndexResurrect,
spellTypeIndexHateRedux,
spellTypeIndexInCombatBuffSong,
spellTypeIndexOutOfCombatBuffSong,
spellTypeIndexPreCombatBuff,
spellTypeIndexPreCombatBuffSong
};
static const uint32 SPELL_TYPE_FIRST = spellTypeIndexNuke;
static const uint32 SPELL_TYPE_LAST = spellTypeIndexPreCombatBuffSong;
static const uint32 SPELL_TYPE_COUNT = SPELL_TYPE_LAST + 1;
// Class Constructors
Bot(NPCType npcTypeData, Client* botOwner);
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType npcTypeData);
Bot(NPCType *npcTypeData, Client* botOwner);
Bot(uint32 botID, uint32 botOwnerCharacterID, uint32 botSpellsID, double totalPlayTime, uint32 lastZoneId, NPCType *npcTypeData);
//abstract virtual function implementations requird by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
@@ -322,9 +246,9 @@ public:
virtual void SetAttackTimer();
uint32 GetClassHPFactor();
virtual int32 CalcMaxHP();
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellAETarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellSingleTarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool &stopLogic);
bool DoFinishedSpellGroupTarget(uint16 spell_id, Mob* spellTarget, EQEmu::spells::CastingSlot slot, bool &stopLogic);
void SendBotArcheryWearChange(uint8 material_slot, uint32 material, uint32 color);
void Camp(bool databaseSave = true);
virtual void AddToHateList(Mob* other, uint32 hate = 0, int32 damage = 0, bool iYellForHelp = true, bool bFrenzy = false, bool iBuffTic = false, bool pet_command = false);
@@ -337,9 +261,10 @@ public:
void Stand();
bool IsSitting();
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); }
int GetBotFearSpeed() const { return (int)((float)_GetFearSpeed() * 1.786f); }
virtual int GetWalkspeed() const { return (int)((float)_GetWalkSpeed() * 1.785714f); } // 1.25 / 0.7 = 1.7857142857142857142857142857143
virtual int GetRunspeed() const { return (int)((float)_GetRunSpeed() * 1.785714f); }
virtual void WalkTo(float x, float y, float z);
virtual void RunTo(float x, float y, float z);
bool UseDiscipline(uint32 spell_id, uint32 target);
uint8 GetNumberNeedingHealedInGroup(uint8 hpr, bool includePets);
bool GetNeedsCured(Mob *tar);
@@ -425,12 +350,12 @@ public:
virtual float GetAOERange(uint16 spell_id);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100);
virtual void DoBuffTic(const Buffs_Struct &buff, int slot, Mob* caster = nullptr);
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot = EQEmu::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0,
uint32 item_slot = 0xFFFFFFFF, int16 *resist_adjust = nullptr, uint32 aa_id = 0);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::CastingSlot slot);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center, CastAction_type &CastAction, EQEmu::spells::CastingSlot slot);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot = EQEmu::spells::CastingSlot::Item, int32 casttime = -1, int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF, uint32 aa_id = 0);
// Bot Equipment & Inventory Class Methods
void BotTradeSwapItem(Client* client, int16 lootSlot, const EQEmu::ItemInstance* inst, const EQEmu::ItemInstance* inst_swap, uint32 equipableSlots, std::string* errorMessage, bool swap = true);
@@ -494,7 +419,7 @@ public:
static BotSpell GetBestBotSpellForCure(Bot* botCaster, Mob* target);
static BotSpell GetBestBotSpellForResistDebuff(Bot* botCaster, Mob* target);
static NPCType CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
static NPCType *CreateDefaultNPCTypeStructForBot(std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender);
// Static Bot Group Methods
static bool AddBotToGroup(Bot* bot, Group* group);
@@ -516,7 +441,7 @@ public:
virtual bool IsBot() const { return true; }
bool GetRangerAutoWeaponSelect() { return _rangerAutoWeaponSelect; }
BotRoleType GetBotRole() { return _botRole; }
BotStanceType GetBotStance() { return _botStance; }
EQEmu::constants::StanceType GetBotStance() { return _botStance; }
uint8 GetChanceToCastBySpellType(uint32 spellType);
bool IsGroupHealer() { return m_CastingRoles.GroupHealer; }
@@ -630,7 +555,12 @@ public:
// void SetBotOwnerCharacterID(uint32 botOwnerCharacterID) { _botOwnerCharacterID = botOwnerCharacterID; }
void SetRangerAutoWeaponSelect(bool enable) { GetClass() == RANGER ? _rangerAutoWeaponSelect = enable : _rangerAutoWeaponSelect = false; }
void SetBotRole(BotRoleType botRole) { _botRole = botRole; }
void SetBotStance(BotStanceType botStance) { _botStance = ((botStance != BotStanceUnknown) ? (botStance) : (BotStancePassive)); }
void SetBotStance(EQEmu::constants::StanceType botStance) {
if (botStance >= EQEmu::constants::stancePassive && botStance <= EQEmu::constants::stanceBurnAE)
_botStance = botStance;
else
_botStance = EQEmu::constants::stancePassive;
}
void SetSpellRecastTimer(int timer_index, int32 recast_delay);
void SetDisciplineRecastTimer(int timer_index, int32 recast_delay);
void SetAltOutOfCombatBehavior(bool behavior_flag) { _altoutofcombatbehavior = behavior_flag;}
@@ -656,7 +586,7 @@ public:
virtual void BotRangedAttack(Mob* other);
// Publicized private functions
static NPCType FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
static NPCType *FillNPCTypeStruct(uint32 botSpellsID, std::string botName, std::string botLastName, uint8 botLevel, uint16 botRace, uint8 botClass, uint8 gender, float size, uint32 face, uint32 hairStyle, uint32 hairColor, uint32 eyeColor, uint32 eyeColor2, uint32 beardColor, uint32 beard, uint32 drakkinHeritage, uint32 drakkinTattoo, uint32 drakkinDetails, int32 hp, int32 mana, int32 mr, int32 cr, int32 dr, int32 fr, int32 pr, int32 corrup, int32 ac, uint32 str, uint32 sta, uint32 dex, uint32 agi, uint32 _int, uint32 wis, uint32 cha, uint32 attack);
void BotRemoveEquipItem(int16 slot);
void RemoveBotItemBySlot(uint32 slotID, std::string* errorMessage);
uint32 GetTotalPlayTime();
@@ -724,12 +654,13 @@ private:
uint32 _lastZoneId;
bool _rangerAutoWeaponSelect;
BotRoleType _botRole;
BotStanceType _botStance;
BotStanceType _baseBotStance;
EQEmu::constants::StanceType _botStance;
EQEmu::constants::StanceType _baseBotStance;
unsigned int RestRegenHP;
unsigned int RestRegenMana;
unsigned int RestRegenEndurance;
Timer rest_timer;
Timer ping_timer;
int32 base_end;
int32 cur_end;
int32 max_end;
@@ -788,6 +719,9 @@ private:
bool LoadPet(); // Load and spawn bot pet if there is one
bool SavePet(); // Save and depop bot pet if there is one
bool DeletePet();
public:
static uint8 spell_casting_chances[SPELL_TYPE_COUNT][PLAYER_CLASS_COUNT][EQEmu::constants::STANCE_TYPE_COUNT][cntHSND];
};
#endif // BOTS
+33 -32
View File
@@ -4249,7 +4249,7 @@ void bot_subcommand_bot_clone(Client *c, const Seperator *sep)
return;
}
int clone_stance = BotStancePassive;
int clone_stance = EQEmu::constants::stancePassive;
if (!botdb.LoadStance(my_bot->GetBotID(), clone_stance))
c->Message(m_fail, "%s for bot '%s'", BotDatabase::fail::LoadStance(), my_bot->GetCleanName());
if (!botdb.SaveStance(clone_id, clone_stance))
@@ -5160,29 +5160,34 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
if (helper_command_alias_fail(c, "bot_subcommand_bot_stance", sep->arg[0], "botstance"))
return;
if (helper_is_help_or_usage(sep->arg[1])) {
c->Message(m_usage, "usage: %s [current | value: 0-6] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
c->Message(m_usage, "usage: %s [current | value: 1-9] ([actionable: target | byname] ([actionable_name]))", sep->arg[0]);
c->Message(m_note, "value: %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s), %u(%s)",
BotStancePassive, GetBotStanceName(BotStancePassive),
BotStanceBalanced, GetBotStanceName(BotStanceBalanced),
BotStanceEfficient, GetBotStanceName(BotStanceEfficient),
BotStanceReactive, GetBotStanceName(BotStanceReactive),
BotStanceAggressive, GetBotStanceName(BotStanceAggressive),
BotStanceBurn, GetBotStanceName(BotStanceBurn),
BotStanceBurnAE, GetBotStanceName(BotStanceBurnAE)
EQEmu::constants::stancePassive, EQEmu::constants::GetStanceName(EQEmu::constants::stancePassive),
EQEmu::constants::stanceBalanced, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBalanced),
EQEmu::constants::stanceEfficient, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient),
EQEmu::constants::stanceReactive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceReactive),
EQEmu::constants::stanceAggressive, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAggressive),
EQEmu::constants::stanceAssist, EQEmu::constants::GetStanceName(EQEmu::constants::stanceAssist),
EQEmu::constants::stanceBurn, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurn),
EQEmu::constants::stanceEfficient2, EQEmu::constants::GetStanceName(EQEmu::constants::stanceEfficient2),
EQEmu::constants::stanceBurnAE, EQEmu::constants::GetStanceName(EQEmu::constants::stanceBurnAE)
);
return;
}
int ab_mask = (ActionableBots::ABM_Target | ActionableBots::ABM_ByName);
bool current_flag = false;
auto bst = BotStanceUnknown;
auto bst = EQEmu::constants::stanceUnknown;
if (!strcasecmp(sep->arg[1], "current"))
current_flag = true;
else if (sep->IsNumber(1))
bst = VALIDBOTSTANCE(atoi(sep->arg[1]));
else if (sep->IsNumber(1)) {
bst = (EQEmu::constants::StanceType)atoi(sep->arg[1]);
if (bst < EQEmu::constants::stanceUnknown || bst > EQEmu::constants::stanceBurnAE)
bst = EQEmu::constants::stanceUnknown;
}
if (!current_flag && bst == BotStanceUnknown) {
if (!current_flag && bst == EQEmu::constants::stanceUnknown) {
c->Message(m_fail, "A [current] argument or valid numeric [value] is required to use this command");
return;
}
@@ -5200,7 +5205,12 @@ void bot_subcommand_bot_stance(Client *c, const Seperator *sep)
bot_iter->Save();
}
Bot::BotGroupSay(bot_iter, "My current stance is '%s' (%u)", GetBotStanceName(bot_iter->GetBotStance()), bot_iter->GetBotStance());
Bot::BotGroupSay(
bot_iter,
"My current stance is '%s' (%i)",
EQEmu::constants::GetStanceName(bot_iter->GetBotStance()),
bot_iter->GetBotStance()
);
}
}
@@ -5273,7 +5283,7 @@ void bot_subcommand_bot_summon(Client *c, const Seperator *sep)
bot_iter->WipeHateList();
bot_iter->SetTarget(nullptr);
bot_iter->Warp(glm::vec3(c->GetPosition()));
bot_iter->Teleport(c->GetPosition());
bot_iter->DoAnim(0);
if (!bot_iter->HasPet())
@@ -5281,7 +5291,7 @@ void bot_subcommand_bot_summon(Client *c, const Seperator *sep)
bot_iter->GetPet()->WipeHateList();
bot_iter->GetPet()->SetTarget(nullptr);
bot_iter->GetPet()->Warp(glm::vec3(c->GetPosition()));
bot_iter->GetPet()->Teleport(c->GetPosition());
}
if (sbl.size() == 1)
@@ -7220,7 +7230,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
inst = my_bot->CastToBot()->GetBotItem(i);
if (!inst || !inst->GetItem()) {
c->Message(m_message, "I need something for my %s (slot %i)", GetBotEquipSlotName(i), i);
c->Message(m_message, "I need something for my %s (slot %i)", EQEmu::invslot::GetInvPossessionsSlotName(i), i);
continue;
}
@@ -7230,7 +7240,7 @@ void bot_subcommand_inventory_list(Client *c, const Seperator *sep)
}
linker.SetItemInst(inst);
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), GetBotEquipSlotName(i), i);
c->Message(m_message, "Using %s in my %s (slot %i)", linker.GenerateLink().c_str(), EQEmu::invslot::GetInvPossessionsSlotName(i), i);
++inventory_count;
}
@@ -7333,14 +7343,14 @@ void bot_subcommand_inventory_remove(Client *c, const Seperator *sep)
case EQEmu::invslot::slotWaist:
case EQEmu::invslot::slotPowerSource:
case EQEmu::invslot::slotAmmo:
c->Message(m_message, "My %s is %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
c->Message(m_message, "My %s is %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
break;
case EQEmu::invslot::slotShoulders:
case EQEmu::invslot::slotArms:
case EQEmu::invslot::slotHands:
case EQEmu::invslot::slotLegs:
case EQEmu::invslot::slotFeet:
c->Message(m_message, "My %s are %s unequipped", GetBotEquipSlotName(slotId), ((itm) ? ("now") : ("already")));
c->Message(m_message, "My %s are %s unequipped", EQEmu::invslot::GetInvPossessionsSlotName(slotId), ((itm) ? ("now") : ("already")));
break;
default:
c->Message(m_fail, "I'm soo confused...");
@@ -7383,7 +7393,7 @@ void bot_subcommand_inventory_window(Client *c, const Seperator *sep)
item = inst->GetItem();
window_text.append("<c \"#FFFFFF\">");
window_text.append(GetBotEquipSlotName(i));
window_text.append(EQEmu::invslot::GetInvPossessionsSlotName(i));
window_text.append(": ");
if (item) {
//window_text.append("</c>");
@@ -7707,16 +7717,7 @@ uint32 helper_bot_create(Client *bot_owner, std::string bot_name, uint8 bot_clas
return bot_id;
}
auto DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(
bot_name.c_str(),
"",
bot_owner->GetLevel(),
bot_race,
bot_class,
bot_gender
);
auto my_bot = new Bot(DefaultNPCTypeStruct, bot_owner);
auto my_bot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(bot_name.c_str(), "", bot_owner->GetLevel(), bot_race, bot_class, bot_gender), bot_owner);
if (!my_bot->Save()) {
bot_owner->Message(m_unknown, "Failed to create '%s' due to unknown cause", my_bot->GetCleanName());
@@ -7774,7 +7775,7 @@ bool helper_cast_standard_spell(Bot* casting_bot, Mob* target_mob, int spell_id,
if (annouce_cast)
Bot::BotGroupSay(casting_bot, "Attempting to cast '%s' on %s", spells[spell_id].name, target_mob->GetCleanName());
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::CastingSlot::Gem2, -1, -1, dont_root_before);
return casting_bot->CastSpell(spell_id, target_mob->GetID(), EQEmu::spells::CastingSlot::Gem2, -1, -1, dont_root_before);
}
bool helper_command_alias_fail(Client *bot_owner, const char* command_handler, const char *alias, const char *command)
+24 -28
View File
@@ -83,12 +83,8 @@ bool BotDatabase::LoadBotCommandSettings(std::map<std::string, std::pair<uint8,
return true;
}
static uint8 spell_casting_chances[MaxSpellTypes][PLAYER_CLASS_COUNT][MaxStances][cntHSND];
bool BotDatabase::LoadBotSpellCastingChances()
{
memset(spell_casting_chances, 0, sizeof(spell_casting_chances));
query =
"SELECT"
" `spell_type_index`,"
@@ -119,14 +115,14 @@ bool BotDatabase::LoadBotSpellCastingChances()
for (auto row = results.begin(); row != results.end(); ++row) {
uint8 spell_type_index = atoi(row[0]);
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
continue;
uint8 class_index = atoi(row[1]);
if (class_index < WARRIOR || class_index > BERSERKER)
continue;
--class_index;
uint8 stance_index = atoi(row[2]);
if (stance_index >= MaxStances)
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
continue;
for (uint8 conditional_index = nHSND; conditional_index < cntHSND; ++conditional_index) {
@@ -136,7 +132,7 @@ bool BotDatabase::LoadBotSpellCastingChances()
if (value > 100)
value = 100;
spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index] = value;
}
}
@@ -357,8 +353,8 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
// TODO: Consider removing resists and basic attributes from the load query above since we're using defaultNPCType values instead
auto row = results.begin();
NPCType defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7]));
NPCType tempNPCStruct = Bot::FillNPCTypeStruct(
auto defaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(std::string(row[2]), std::string(row[3]), atoi(row[10]), atoi(row[8]), atoi(row[9]), atoi(row[7]));
auto tempNPCStruct = Bot::FillNPCTypeStruct(
atoi(row[1]),
std::string(row[2]),
std::string(row[3]),
@@ -379,21 +375,21 @@ bool BotDatabase::LoadBot(const uint32 bot_id, Bot*& loaded_bot)
atoi(row[25]),
atoi(row[28]),
atoi(row[29]),
defaultNPCTypeStruct.MR,
defaultNPCTypeStruct.CR,
defaultNPCTypeStruct.DR,
defaultNPCTypeStruct.FR,
defaultNPCTypeStruct.PR,
defaultNPCTypeStruct.Corrup,
defaultNPCTypeStruct.AC,
defaultNPCTypeStruct.STR,
defaultNPCTypeStruct.STA,
defaultNPCTypeStruct.DEX,
defaultNPCTypeStruct.AGI,
defaultNPCTypeStruct.INT,
defaultNPCTypeStruct.WIS,
defaultNPCTypeStruct.CHA,
defaultNPCTypeStruct.ATK
defaultNPCTypeStruct->MR,
defaultNPCTypeStruct->CR,
defaultNPCTypeStruct->DR,
defaultNPCTypeStruct->FR,
defaultNPCTypeStruct->PR,
defaultNPCTypeStruct->Corrup,
defaultNPCTypeStruct->AC,
defaultNPCTypeStruct->STR,
defaultNPCTypeStruct->STA,
defaultNPCTypeStruct->DEX,
defaultNPCTypeStruct->AGI,
defaultNPCTypeStruct->INT,
defaultNPCTypeStruct->WIS,
defaultNPCTypeStruct->CHA,
defaultNPCTypeStruct->ATK
);
loaded_bot = new Bot(bot_id, atoi(row[0]), atoi(row[1]), atof(row[14]), atoi(row[6]), tempNPCStruct);
@@ -877,7 +873,7 @@ bool BotDatabase::LoadStance(Bot* bot_inst, bool& stance_flag)
return true;
auto row = results.begin();
bot_inst->SetBotStance((BotStanceType)atoi(row[0]));
bot_inst->SetBotStance((EQEmu::constants::StanceType)atoi(row[0]));
stance_flag = true;
return true;
@@ -2853,16 +2849,16 @@ bool BotDatabase::DeleteAllHealRotations(const uint32 owner_id)
/* Bot miscellaneous functions */
uint8 BotDatabase::GetSpellCastingChance(uint8 spell_type_index, uint8 class_index, uint8 stance_index, uint8 conditional_index) // class_index is 0-based
{
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= Bot::SPELL_TYPE_COUNT)
return 0;
if (class_index >= PLAYER_CLASS_COUNT)
return 0;
if (stance_index >= MaxStances)
if (stance_index >= EQEmu::constants::STANCE_TYPE_COUNT)
return 0;
if (conditional_index >= cntHSND)
return 0;
return spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
return Bot::spell_casting_chances[spell_type_index][class_index][stance_index][conditional_index];
}
+82 -83
View File
@@ -192,25 +192,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
else {
float hpRatioToCast = 0.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
case BotStanceAggressive:
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
break;
case BotStanceBalanced:
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
break;
case BotStanceReactive:
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
break;
default:
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
case EQEmu::constants::stanceAggressive:
hpRatioToCast = isPrimaryHealer?90.0f:50.0f;
break;
case EQEmu::constants::stanceBalanced:
hpRatioToCast = isPrimaryHealer?95.0f:75.0f;
break;
case EQEmu::constants::stanceReactive:
hpRatioToCast = isPrimaryHealer?100.0f:90.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
hpRatioToCast = isPrimaryHealer?75.0f:25.0f;
break;
default:
hpRatioToCast = isPrimaryHealer?100.0f:0.0f;
break;
}
//If we're at specified mana % or below, don't heal as hybrid
@@ -381,23 +380,22 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
{
float manaRatioToCast = 75.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
manaRatioToCast = 90.0f;
break;
case BotStanceBalanced:
case BotStanceAggressive:
manaRatioToCast = 75.0f;
break;
case BotStanceReactive:
case BotStanceBurn:
case BotStanceBurnAE:
manaRatioToCast = 50.0f;
break;
default:
manaRatioToCast = 75.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
manaRatioToCast = 90.0f;
break;
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceAggressive:
manaRatioToCast = 75.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
manaRatioToCast = 50.0f;
break;
default:
manaRatioToCast = 75.0f;
break;
}
//If we're at specified mana % or below, don't rune as enchanter
@@ -461,25 +459,24 @@ bool Bot::AICastSpell(Mob* tar, uint8 iChance, uint32 iSpellTypes) {
{
float manaRatioToCast = 75.0f;
switch(this->GetBotStance())
{
case BotStanceEfficient:
manaRatioToCast = 90.0f;
break;
case BotStanceBalanced:
manaRatioToCast = 75.0f;
break;
case BotStanceReactive:
case BotStanceAggressive:
manaRatioToCast = 50.0f;
break;
case BotStanceBurn:
case BotStanceBurnAE:
manaRatioToCast = 25.0f;
break;
default:
manaRatioToCast = 50.0f;
break;
switch(this->GetBotStance()) {
case EQEmu::constants::stanceEfficient:
manaRatioToCast = 90.0f;
break;
case EQEmu::constants::stanceBalanced:
manaRatioToCast = 75.0f;
break;
case EQEmu::constants::stanceReactive:
case EQEmu::constants::stanceAggressive:
manaRatioToCast = 50.0f;
break;
case EQEmu::constants::stanceBurn:
case EQEmu::constants::stanceBurnAE:
manaRatioToCast = 25.0f;
break;
default:
manaRatioToCast = 50.0f;
break;
}
//If we're at specified mana % or below, don't nuke as cleric or enchanter
@@ -1310,7 +1307,7 @@ bool Bot::AI_EngagedCastCheck() {
AIautocastspell_timer->Disable(); //prevent the timer from going off AGAIN while we are casting.
uint8 botClass = GetClass();
BotStanceType botStance = GetBotStance();
EQEmu::constants::StanceType botStance = GetBotStance();
bool mayGetAggro = HasOrMayGetAggro();
Log(Logs::Detail, Logs::AI, "Engaged autocast check triggered (BOTS). Trying to cast healing spells then maybe offensive spells.");
@@ -2573,79 +2570,79 @@ bool Bot::CheckDisciplineRecastTimers(Bot *caster, int timer_index) {
uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
{
uint8 spell_type_index = MaxSpellTypes;
uint8 spell_type_index = SPELL_TYPE_COUNT;
switch (spellType) {
case SpellType_Nuke:
spell_type_index = SpellType_NukeIndex;
spell_type_index = spellTypeIndexNuke;
break;
case SpellType_Heal:
spell_type_index = SpellType_HealIndex;
spell_type_index = spellTypeIndexHeal;
break;
case SpellType_Root:
spell_type_index = SpellType_RootIndex;
spell_type_index = spellTypeIndexRoot;
break;
case SpellType_Buff:
spell_type_index = SpellType_BuffIndex;
spell_type_index = spellTypeIndexBuff;
break;
case SpellType_Escape:
spell_type_index = SpellType_EscapeIndex;
spell_type_index = spellTypeIndexEscape;
break;
case SpellType_Pet:
spell_type_index = SpellType_PetIndex;
spell_type_index = spellTypeIndexPet;
break;
case SpellType_Lifetap:
spell_type_index = SpellType_LifetapIndex;
spell_type_index = spellTypeIndexLifetap;
break;
case SpellType_Snare:
spell_type_index = SpellType_SnareIndex;
spell_type_index = spellTypeIndexSnare;
break;
case SpellType_DOT:
spell_type_index = SpellType_DOTIndex;
spell_type_index = spellTypeIndexDot;
break;
case SpellType_Dispel:
spell_type_index = SpellType_DispelIndex;
spell_type_index = spellTypeIndexDispel;
break;
case SpellType_InCombatBuff:
spell_type_index = SpellType_InCombatBuffIndex;
spell_type_index = spellTypeIndexInCombatBuff;
break;
case SpellType_Mez:
spell_type_index = SpellType_MezIndex;
spell_type_index = spellTypeIndexMez;
break;
case SpellType_Charm:
spell_type_index = SpellType_CharmIndex;
spell_type_index = spellTypeIndexCharm;
break;
case SpellType_Slow:
spell_type_index = SpellType_SlowIndex;
spell_type_index = spellTypeIndexSlow;
break;
case SpellType_Debuff:
spell_type_index = SpellType_DebuffIndex;
spell_type_index = spellTypeIndexDebuff;
break;
case SpellType_Cure:
spell_type_index = SpellType_CureIndex;
spell_type_index = spellTypeIndexCure;
break;
case SpellType_Resurrect:
spell_type_index = SpellType_ResurrectIndex;
spell_type_index = spellTypeIndexResurrect;
break;
case SpellType_HateRedux:
spell_type_index = SpellType_HateReduxIndex;
spell_type_index = spellTypeIndexHateRedux;
break;
case SpellType_InCombatBuffSong:
spell_type_index = SpellType_InCombatBuffSongIndex;
spell_type_index = spellTypeIndexInCombatBuffSong;
break;
case SpellType_OutOfCombatBuffSong:
spell_type_index = SpellType_OutOfCombatBuffSongIndex;
spell_type_index = spellTypeIndexOutOfCombatBuffSong;
break;
case SpellType_PreCombatBuff:
spell_type_index = SpellType_PreCombatBuffIndex;
spell_type_index = spellTypeIndexPreCombatBuff;
break;
case SpellType_PreCombatBuffSong:
spell_type_index = SpellType_PreCombatBuffSongIndex;
spell_type_index = spellTypeIndexPreCombatBuffSong;
break;
default:
spell_type_index = MaxSpellTypes;
spell_type_index = SPELL_TYPE_COUNT;
break;
}
if (spell_type_index >= MaxSpellTypes)
if (spell_type_index >= SPELL_TYPE_COUNT)
return 0;
uint8 class_index = GetClass();
@@ -2653,11 +2650,13 @@ uint8 Bot::GetChanceToCastBySpellType(uint32 spellType)
return 0;
--class_index;
uint8 stance_index = (uint8)GetBotStance();
if (stance_index >= MaxStances)
EQEmu::constants::StanceType stance_type = GetBotStance();
if (stance_type < EQEmu::constants::stancePassive || stance_type > EQEmu::constants::stanceBurnAE)
return 0;
uint8 stance_index = EQEmu::constants::ConvertStanceTypeToIndex(stance_type);
uint8 type_index = nHSND;
if (HasGroup()) {
if (IsGroupHealer()/* || IsRaidHealer()*/)
type_index |= pH;
+248 -417
View File
@@ -38,6 +38,8 @@ extern volatile bool RunLoops;
#include "../common/rulesys.h"
#include "../common/string_util.h"
#include "../common/data_verification.h"
#include "../common/profanity_manager.h"
#include "data_bucket.h"
#include "position.h"
#include "net.h"
#include "worldserver.h"
@@ -53,6 +55,7 @@ extern volatile bool RunLoops;
#include "guild_mgr.h"
#include "quest_parser_collection.h"
#include "queryserv.h"
#include "mob_movement_manager.h"
extern QueryServ* QServ;
extern EntityList entity_list;
@@ -118,7 +121,6 @@ Client::Client(EQStreamInterface* ieqs)
0,
0
),
position_timer(250),
hpupdate_timer(2000),
camp_timer(29000),
process_timer(100),
@@ -166,6 +168,7 @@ Client::Client(EQStreamInterface* ieqs)
for (int client_filter = 0; client_filter < _FilterCount; client_filter++)
ClientFilters[client_filter] = FilterShow;
mMovementManager->AddClient(this);
character_id = 0;
conn_state = NoPacketsReceived;
client_data_loaded = false;
@@ -220,7 +223,6 @@ Client::Client(EQStreamInterface* ieqs)
npcflag = false;
npclevel = 0;
pQueuedSaveWorkID = 0;
position_timer_counter = 0;
position_update_same_count = 0;
fishing_timer.Disable();
shield_timer.Disable();
@@ -254,7 +256,6 @@ Client::Client(EQStreamInterface* ieqs)
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:
memset(&m_pp, 0, sizeof(m_pp));
@@ -287,14 +288,6 @@ Client::Client(EQStreamInterface* ieqs)
XPRate = 100;
current_endurance = 0;
m_TimeSinceLastPositionCheck = 0;
m_DistanceSinceLastPositionCheck = 0.0f;
m_ShadowStepExemption = 0;
m_KnockBackExemption = 0;
m_PortExemption = 0;
m_SenseExemption = 0;
m_AssistExemption = 0;
m_CheatDetectMoved = false;
CanUseReport = true;
aa_los_them_mob = nullptr;
los_status = false;
@@ -345,6 +338,12 @@ Client::Client(EQStreamInterface* ieqs)
temp_pvp = false;
is_client_moving = false;
/**
* GM
*/
display_mob_info_window = true;
dev_tools_window_enabled = true;
#ifdef BOTS
bot_owner_options = DefaultBotOwnerOptions;
#endif
@@ -353,6 +352,8 @@ Client::Client(EQStreamInterface* ieqs)
}
Client::~Client() {
mMovementManager->RemoveClient(this);
#ifdef BOTS
Bot::ProcessBotOwnerRefDelete(this);
#endif
@@ -895,6 +896,10 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
language = 0; // No need for language when drunk
}
// Censor the message
if (EQEmu::ProfanityManager::IsCensorshipActive() && (chan_num != 8))
EQEmu::ProfanityManager::RedactMessage(message);
switch(chan_num)
{
case 0: { /* Guild Chat */
@@ -1092,6 +1097,9 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
break;
}
if (EQEmu::ProfanityManager::IsCensorshipActive())
EQEmu::ProfanityManager::RedactMessage(message);
#ifdef BOTS
if (message[0] == BOT_COMMAND_CHAR) {
if (bot_command_dispatch(this, message) == -2) {
@@ -1129,7 +1137,7 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
CheckLDoNHail(GetTarget());
CheckEmoteHail(GetTarget(), message);
if(DistanceSquaredNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
if(DistanceNoZ(m_Position, GetTarget()->GetPosition()) <= RuleI(Range, Say)) {
NPC *tar = GetTarget()->CastToNPC();
parse->EventNPC(EVENT_SAY, tar->CastToNPC(), this, message, language);
@@ -1658,7 +1666,6 @@ void Client::FriendsWho(char *FriendsString) {
}
}
void Client::UpdateAdmin(bool iFromDB) {
int16 tmp = admin;
if (iFromDB)
@@ -1900,9 +1907,9 @@ void Client::CheckManaEndUpdate() {
mana_update->cur_mana = GetMana();
mana_update->max_mana = GetMaxMana();
mana_update->spawn_id = GetID();
if ((ClientVersionBit() & EQEmu::versions::ClientVersionBit::bit_SoDAndLater) != 0)
if ((ClientVersionBit() & EQEmu::versions::ClientVersionBitmask::maskSoDAndLater) != 0)
QueuePacket(mana_packet); // do we need this with the OP_ManaChange packet above?
entity_list.QueueClientsByXTarget(this, mana_packet, false, EQEmu::versions::ClientVersionBit::bit_SoDAndLater);
entity_list.QueueClientsByXTarget(this, mana_packet, false, EQEmu::versions::ClientVersionBitmask::maskSoDAndLater);
safe_delete(mana_packet);
last_reported_mana_percent = this->GetManaPercent();
@@ -1925,9 +1932,9 @@ void Client::CheckManaEndUpdate() {
endurance_update->cur_end = GetEndurance();
endurance_update->max_end = GetMaxEndurance();
endurance_update->spawn_id = GetID();
if ((ClientVersionBit() & EQEmu::versions::ClientVersionBit::bit_SoDAndLater) != 0)
if ((ClientVersionBit() & EQEmu::versions::ClientVersionBitmask::maskSoDAndLater) != 0)
QueuePacket(endurance_packet); // do we need this with the OP_ManaChange packet above?
entity_list.QueueClientsByXTarget(this, endurance_packet, false, EQEmu::versions::ClientVersionBit::bit_SoDAndLater);
entity_list.QueueClientsByXTarget(this, endurance_packet, false, EQEmu::versions::ClientVersionBitmask::maskSoDAndLater);
safe_delete(endurance_packet);
last_reported_endurance_percent = this->GetEndurancePercent();
@@ -2078,6 +2085,7 @@ bool Client::ChangeFirstName(const char* in_firstname, const char* gmname)
void Client::SetGM(bool toggle) {
m_pp.gm = toggle ? 1 : 0;
m_inv.SetGMInventory((bool)m_pp.gm);
Message(13, "You are %s a GM.", m_pp.gm ? "now" : "no longer");
SendAppearancePacket(AT_GM, m_pp.gm);
Save();
@@ -2640,6 +2648,11 @@ bool Client::CheckAccess(int16 iDBLevel, int16 iDefaultLevel) {
}
void Client::MemorizeSpell(uint32 slot,uint32 spellid,uint32 scribing){
if (slot < 0 || slot >= EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize)
return;
if ((spellid < 3 || spellid > EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellIdMax) && spellid != 0xFFFFFFFF)
return;
auto outapp = new EQApplicationPacket(OP_MemorizeSpell, sizeof(MemorizeSpell_Struct));
MemorizeSpell_Struct* mss=(MemorizeSpell_Struct*)outapp->pBuffer;
mss->scribing=scribing;
@@ -2966,13 +2979,12 @@ bool Client::BindWound(Mob *bindmob, bool start, bool fail)
return true;
}
void Client::SetMaterial(int16 in_slot, uint32 item_id) {
const EQEmu::ItemData* item = database.GetItem(item_id);
if (item && item->IsClassCommon())
{
void Client::SetMaterial(int16 in_slot, uint32 item_id)
{
const EQEmu::ItemData *item = database.GetItem(item_id);
if (item && item->IsClassCommon()) {
uint8 matslot = EQEmu::InventoryProfile::CalcMaterialFromSlot(in_slot);
if (matslot != EQEmu::textures::materialInvalid)
{
if (matslot != EQEmu::textures::materialInvalid) {
m_pp.item_material.Slot[matslot].Material = GetEquipmentMaterial(matslot);
}
}
@@ -3056,7 +3068,7 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
Filter0(FilterMissedMe);
Filter1(FilterDamageShields);
if (ClientVersionBit() & EQEmu::versions::bit_SoDAndLater) {
if (ClientVersionBit() & EQEmu::versions::maskSoDAndLater) {
if (filter->filters[FilterDOT] == 0)
ClientFilters[FilterDOT] = FilterShow;
else if (filter->filters[FilterDOT] == 1)
@@ -3077,7 +3089,7 @@ void Client::ServerFilter(SetServerFilter_Struct* filter){
Filter1(FilterFocusEffects);
Filter1(FilterPetSpells);
if (ClientVersionBit() & EQEmu::versions::bit_SoDAndLater) {
if (ClientVersionBit() & EQEmu::versions::maskSoDAndLater) {
if (filter->filters[FilterHealOverTime] == 0)
ClientFilters[FilterHealOverTime] = FilterShow;
// This is called 'Show Mine Only' in the clients, but functions the same as show
@@ -3458,7 +3470,7 @@ float Client::CalcPriceMod(Mob* other, bool reverse)
float chaformula = 0;
if (other)
{
int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
int factionlvl = GetFactionLevel(CharacterID(), other->CastToNPC()->GetNPCTypeID(), GetFactionRace(), GetClass(), GetDeity(), other->CastToNPC()->GetPrimaryFaction(), other);
if (factionlvl >= FACTION_APPREHENSIVE) // Apprehensive or worse.
{
if (GetCHA() > 103)
@@ -4041,9 +4053,9 @@ void Client::SetHoTT(uint32 mobid) {
void Client::SendPopupToClient(const char *Title, const char *Text, uint32 PopupID, uint32 Buttons, uint32 Duration)
{
auto outapp = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *)outapp->pBuffer;
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) outapp->pBuffer;
if ((strlen(Title) > (sizeof(olms->Title) - 1)) || (strlen(Text) > (sizeof(olms->Text) - 1))) {
safe_delete(outapp);
@@ -4055,12 +4067,14 @@ void Client::SendPopupToClient(const char *Title, const char *Text, uint32 Popup
olms->Buttons = Buttons;
if (Duration > 0)
if (Duration > 0) {
olms->Duration = Duration * 1000;
else
}
else {
olms->Duration = 0xffffffff;
}
olms->PopupID = PopupID;
olms->PopupID = PopupID;
olms->NegativeID = 0;
sprintf(olms->ButtonName0, "%s", "Yes");
@@ -4069,16 +4083,29 @@ void Client::SendPopupToClient(const char *Title, const char *Text, uint32 Popup
safe_delete(outapp);
}
void Client::SendFullPopup(const char *Title, const char *Text, uint32 PopupID, uint32 NegativeID, uint32 Buttons, uint32 Duration, const char *ButtonName0, const char *ButtonName1, uint32 SoundControls) {
void Client::SendFullPopup(
const char *Title,
const char *Text,
uint32 PopupID,
uint32 NegativeID,
uint32 Buttons,
uint32 Duration,
const char *ButtonName0,
const char *ButtonName1,
uint32 SoundControls
)
{
auto outapp = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *)outapp->pBuffer;
if((strlen(Text) > (sizeof(olms->Text)-1)) || (strlen(Title) > (sizeof(olms->Title) - 1)) ) {
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) outapp->pBuffer;
if ((strlen(Text) > (sizeof(olms->Text) - 1)) || (strlen(Title) > (sizeof(olms->Title) - 1))) {
safe_delete(outapp);
return;
}
if (ButtonName0 && ButtonName1 && ( (strlen(ButtonName0) > (sizeof(olms->ButtonName0) - 1)) || (strlen(ButtonName1) > (sizeof(olms->ButtonName1) - 1)) ) ) {
if (ButtonName0 && ButtonName1 && ((strlen(ButtonName0) > (sizeof(olms->ButtonName0) - 1)) ||
(strlen(ButtonName1) > (sizeof(olms->ButtonName1) - 1)))) {
safe_delete(outapp);
return;
}
@@ -4087,31 +4114,47 @@ void Client::SendFullPopup(const char *Title, const char *Text, uint32 PopupID,
strcpy(olms->Text, Text);
olms->Buttons = Buttons;
if (ButtonName0 == NULL || ButtonName1 == NULL) {
if (ButtonName0 == nullptr || ButtonName1 == nullptr) {
sprintf(olms->ButtonName0, "%s", "Yes");
sprintf(olms->ButtonName1, "%s", "No");
} else {
}
else {
strcpy(olms->ButtonName0, ButtonName0);
strcpy(olms->ButtonName1, ButtonName1);
}
if(Duration > 0)
if (Duration > 0) {
olms->Duration = Duration * 1000;
else
}
else {
olms->Duration = 0xffffffff;
}
olms->PopupID = PopupID;
olms->NegativeID = NegativeID;
olms->PopupID = PopupID;
olms->NegativeID = NegativeID;
olms->SoundControls = SoundControls;
QueuePacket(outapp);
safe_delete(outapp);
}
void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const char *ButtonName0, const char *ButtonName1, uint32 Duration, int title_type, Client* target, const char *Title, const char *Text, ...) {
void Client::SendWindow(
uint32 PopupID,
uint32 NegativeID,
uint32 Buttons,
const char *ButtonName0,
const char *ButtonName1,
uint32 Duration,
int title_type,
Client *target,
const char *Title,
const char *Text,
...
)
{
va_list argptr;
char buffer[4096];
char buffer[4096];
va_start(argptr, Text);
vsnprintf(buffer, sizeof(buffer), Text, argptr);
@@ -4119,23 +4162,23 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
size_t len = strlen(buffer);
auto app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct* olms=(OnLevelMessage_Struct*)app->pBuffer;
auto app = new EQApplicationPacket(OP_OnLevelMessage, sizeof(OnLevelMessage_Struct));
OnLevelMessage_Struct *olms = (OnLevelMessage_Struct *) app->pBuffer;
if(strlen(Text) > (sizeof(olms->Text)-1)) {
if (strlen(Text) > (sizeof(olms->Text) - 1)) {
safe_delete(app);
return;
}
if(!target)
if (!target) {
title_type = 0;
}
switch (title_type)
{
switch (title_type) {
case 1: {
char name[64] = "";
strcpy(name, target->GetName());
if(target->GetLastName()) {
if (target->GetLastName()) {
char last_name[64] = "";
strcpy(last_name, target->GetLastName());
strcat(name, " ");
@@ -4145,8 +4188,8 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
break;
}
case 2: {
if(target->GuildID()) {
char *guild_name = (char*)guild_mgr.GetGuildName(target->GuildID());
if (target->GuildID()) {
char *guild_name = (char *) guild_mgr.GetGuildName(target->GuildID());
strcpy(olms->Title, guild_name);
}
else {
@@ -4160,19 +4203,21 @@ void Client::SendWindow(uint32 PopupID, uint32 NegativeID, uint32 Buttons, const
}
}
memcpy(olms->Text, buffer, len+1);
memcpy(olms->Text, buffer, len + 1);
olms->Buttons = Buttons;
sprintf(olms->ButtonName0, "%s", ButtonName0);
sprintf(olms->ButtonName1, "%s", ButtonName1);
if(Duration > 0)
if (Duration > 0) {
olms->Duration = Duration * 1000;
else
}
else {
olms->Duration = 0xffffffff;
}
olms->PopupID = PopupID;
olms->PopupID = PopupID;
olms->NegativeID = NegativeID;
FastQueuePacket(&app);
@@ -4918,7 +4963,7 @@ void Client::HandleLDoNOpen(NPC *target)
if(target->GetLDoNTrapSpellID() != 0)
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -5040,7 +5085,7 @@ void Client::HandleLDoNDisarm(NPC *target, uint16 skill, uint8 type)
break;
case -1:
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -5059,7 +5104,7 @@ void Client::HandleLDoNPickLock(NPC *target, uint16 skill, uint8 type)
if(target->IsLDoNTrapped())
{
Message_StringID(13, LDON_ACCIDENT_SETOFF2);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SpellFinished(target->GetLDoNTrapSpellID(), this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[target->GetLDoNTrapSpellID()].ResistDiff);
target->SetLDoNTrapSpellID(0);
target->SetLDoNTrapped(false);
target->SetLDoNTrapDetected(false);
@@ -5292,177 +5337,6 @@ void Client::ShowSkillsWindow()
this->SendPopupToClient(WindowTitle, WindowText.c_str());
}
void Client::SetShadowStepExemption(bool v)
{
if(v == true)
{
uint32 cur_time = Timer::GetCurrentTime();
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed);
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
{
if(IsShadowStepExempted())
{
if(m_DistanceSinceLastPositionCheck > 800)
{
CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ());
}
}
else if(IsKnockBackExempted())
{
//still potential to trigger this if you're knocked back off a
//HUGE fall that takes > 2.5 seconds
if(speed > 30.0f)
{
CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ());
}
}
else if(!IsPortExempted())
{
if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ()))
{
if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
CheatDetected(MQWarp, GetX(), GetY(), GetZ());
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
//Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT);
}
else
{
CheatDetected(MQWarpLight, GetX(), GetY(), GetZ());
}
}
}
}
}
}
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
}
m_ShadowStepExemption = v;
}
void Client::SetKnockBackExemption(bool v)
{
if(v == true)
{
uint32 cur_time = Timer::GetCurrentTime();
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
{
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed);
if(IsShadowStepExempted())
{
if(m_DistanceSinceLastPositionCheck > 800)
{
CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ());
}
}
else if(IsKnockBackExempted())
{
//still potential to trigger this if you're knocked back off a
//HUGE fall that takes > 2.5 seconds
if(speed > 30.0f)
{
CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ());
}
}
else if(!IsPortExempted())
{
if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ()))
{
if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
CheatDetected(MQWarp, GetX(), GetY(), GetZ());
//Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT);
}
else
{
CheatDetected(MQWarpLight, GetX(), GetY(), GetZ());
}
}
}
}
}
}
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
}
m_KnockBackExemption = v;
}
void Client::SetPortExemption(bool v)
{
if(v == true)
{
uint32 cur_time = Timer::GetCurrentTime();
if((cur_time - m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - m_TimeSinceLastPositionCheck);
int runs = GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!GetGMSpeed() && (runs >= GetBaseRunspeed() || (speed > (GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
{
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
m_DistanceSinceLastPositionCheck, (cur_time - m_TimeSinceLastPositionCheck), speed);
if(IsShadowStepExempted())
{
if(m_DistanceSinceLastPositionCheck > 800)
{
CheatDetected(MQWarpShadowStep, GetX(), GetY(), GetZ());
}
}
else if(IsKnockBackExempted())
{
//still potential to trigger this if you're knocked back off a
//HUGE fall that takes > 2.5 seconds
if(speed > 30.0f)
{
CheatDetected(MQWarpKnockBack, GetX(), GetY(), GetZ());
}
}
else if(!IsPortExempted())
{
if(!IsMQExemptedArea(zone->GetZoneID(), GetX(), GetY(), GetZ()))
{
if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
CheatDetected(MQWarp, GetX(), GetY(), GetZ());
//Death(this, 10000000, SPELL_UNKNOWN, _1H_BLUNT);
}
else
{
CheatDetected(MQWarpLight, GetX(), GetY(), GetZ());
}
}
}
}
}
}
m_TimeSinceLastPositionCheck = cur_time;
m_DistanceSinceLastPositionCheck = 0.0f;
}
m_PortExemption = v;
}
void Client::Signal(uint32 data)
{
char buf[32];
@@ -5471,158 +5345,6 @@ void Client::Signal(uint32 data)
parse->EventPlayer(EVENT_SIGNAL, this, buf, 0);
}
const bool Client::IsMQExemptedArea(uint32 zoneID, float x, float y, float z) const
{
float max_dist = 90000;
switch(zoneID)
{
case 2:
{
float delta = (x-(-713.6));
delta *= delta;
float distance = delta;
delta = (y-(-160.2));
delta *= delta;
distance += delta;
delta = (z-(-12.8));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
delta = (x-(-153.8));
delta *= delta;
distance = delta;
delta = (y-(-30.3));
delta *= delta;
distance += delta;
delta = (z-(8.2));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
break;
}
case 9:
{
float delta = (x-(-682.5));
delta *= delta;
float distance = delta;
delta = (y-(147.0));
delta *= delta;
distance += delta;
delta = (z-(-9.9));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
delta = (x-(-655.4));
delta *= delta;
distance = delta;
delta = (y-(10.5));
delta *= delta;
distance += delta;
delta = (z-(-51.8));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
break;
}
case 62:
case 75:
case 114:
case 209:
{
//The portals are so common in paineel/felwitheb that checking
//distances wouldn't be worth it cause unless you're porting to the
//start field you're going to be triggering this and that's a level of
//accuracy I'm willing to sacrifice
return true;
break;
}
case 24:
{
float delta = (x-(-183.0));
delta *= delta;
float distance = delta;
delta = (y-(-773.3));
delta *= delta;
distance += delta;
delta = (z-(54.1));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
delta = (x-(-8.8));
delta *= delta;
distance = delta;
delta = (y-(-394.1));
delta *= delta;
distance += delta;
delta = (z-(41.1));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
delta = (x-(-310.3));
delta *= delta;
distance = delta;
delta = (y-(-1411.6));
delta *= delta;
distance += delta;
delta = (z-(-42.8));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
delta = (x-(-183.1));
delta *= delta;
distance = delta;
delta = (y-(-1409.8));
delta *= delta;
distance += delta;
delta = (z-(37.1));
delta *= delta;
distance += delta;
if(distance < max_dist)
return true;
break;
}
case 110:
case 34:
case 96:
case 93:
case 68:
case 84:
{
if(GetBoatID() != 0)
return true;
break;
}
default:
break;
}
return false;
}
void Client::SendRewards()
{
std::vector<ClientReward> rewards;
@@ -5914,7 +5636,7 @@ void Client::SuspendMinion()
memset(&m_suspendedminion, 0, sizeof(struct PetInfo));
// TODO: These pet command states need to be synced ...
// Will just fix them for now
if (m_ClientVersionBit & EQEmu::versions::bit_UFAndLater) {
if (m_ClientVersionBit & EQEmu::versions::maskUFAndLater) {
SetPetCommandState(PET_BUTTON_SIT, 0);
SetPetCommandState(PET_BUTTON_STOP, 0);
SetPetCommandState(PET_BUTTON_REGROUP, 0);
@@ -6455,7 +6177,7 @@ void Client::LocateCorpse()
SetHeading(CalculateHeadingToTarget(ClosestCorpse->GetX(), ClosestCorpse->GetY()));
SetTarget(ClosestCorpse);
SendTargetCommand(ClosestCorpse->GetID());
SendPositionUpdate(2);
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0, true);
}
else if(!GetTarget())
Message_StringID(clientMessageError, SENSE_CORPSE_NONE);
@@ -6620,7 +6342,7 @@ void Client::Doppelganger(uint16 spell_id, Mob *target, const char *name_overrid
(npc_dup!=nullptr)?npc_dup:npc_type, //make sure we give the NPC the correct data pointer
0,
GetPosition() + glm::vec4(swarmPetLocations[summon_count], 0.0f, 0.0f),
FlyMode3);
GravityBehavior::Water);
if(!swarm_pet_npc->GetSwarmInfo()){
auto nSI = new SwarmPet;
@@ -6679,30 +6401,13 @@ void Client::SendStatsWindow(Client* client, bool use_window)
std::string class_Name = itoa(GetClass());
std::string class_List[] = { "WAR", "CLR", "PAL", "RNG", "SHD", "DRU", "MNK", "BRD", "ROG", "SHM", "NEC", "WIZ", "MAG", "ENC", "BST", "BER" };
if(GetClass() < 17 && GetClass() > 0) { class_Name = class_List[GetClass()-1]; }
if (GetClass() < 17 && GetClass() > 0) {
class_Name = class_List[GetClass() - 1];
}
// Race
std::string race_Name = itoa(GetRace());
switch(GetRace())
{
case 1: race_Name = "Human"; break;
case 2: race_Name = "Barbarian"; break;
case 3: race_Name = "Erudite"; break;
case 4: race_Name = "Wood Elf"; break;
case 5: race_Name = "High Elf"; break;
case 6: race_Name = "Dark Elf"; break;
case 7: race_Name = "Half Elf"; break;
case 8: race_Name = "Dwarf"; break;
case 9: race_Name = "Troll"; break;
case 10: race_Name = "Ogre"; break;
case 11: race_Name = "Halfing"; break;
case 12: race_Name = "Gnome"; break;
case 128: race_Name = "Iksar"; break;
case 130: race_Name = "Vah Shir"; break;
case 330: race_Name = "Froglok"; break;
case 522: race_Name = "Drakkin"; break;
default: break;
}
std::string race_name = GetRaceIDName(GetRace());
/*##########################################################
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
H/M/E String
@@ -7131,7 +6836,7 @@ void Client::SendStatsWindow(Client* client, bool use_window)
std::ostringstream final_string;
final_string <<
/* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast<int>(GetLevel()) << indS << "Race: " << race_Name << "<br>" <<
/* C/L/R */ indP << "Class: " << class_Name << indS << "Level: " << static_cast<int>(GetLevel()) << indS << "Race: " << race_name << "<br>" <<
/* Runes */ indP << "Rune: " << rune_number << indL << indS << "Spell Rune: " << magic_rune_number << "<br>" <<
/* HP/M/E */ HME_row <<
/* DS */ indP << "DS: " << (itembonuses.DamageShield + spellbonuses.DamageShield*-1) << " (Spell: " << (spellbonuses.DamageShield*-1) << " + Item: " << itembonuses.DamageShield << " / " << RuleI(Character, ItemDamageShieldCap) << ")<br>" <<
@@ -7940,7 +7645,7 @@ void Client::SendClearMercInfo()
void Client::DuplicateLoreMessage(uint32 ItemID)
{
if (!(m_ClientVersionBit & EQEmu::versions::bit_RoFAndLater))
if (!(m_ClientVersionBit & EQEmu::versions::maskRoFAndLater))
{
Message_StringID(0, PICK_LORE);
return;
@@ -8000,7 +7705,7 @@ FACTION_VALUE Client::GetReverseFactionCon(Mob* iOther) {
if (iOther->GetPrimaryFaction() == 0)
return FACTION_INDIFFERENT;
return GetFactionLevel(CharacterID(), 0, GetRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther);
return GetFactionLevel(CharacterID(), 0, GetFactionRace(), GetClass(), GetDeity(), iOther->GetPrimaryFaction(), iOther);
}
//o--------------------------------------------------------------
@@ -8087,7 +7792,7 @@ void Client::SetFactionLevel(uint32 char_id, uint32 npc_id, uint8 char_class, ui
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(),
faction_id[i]);
if (quest)
@@ -8137,7 +7842,7 @@ void Client::SetFactionLevel2(uint32 char_id, int32 faction_id, uint8 char_class
// Find out starting faction for this faction
// It needs to be used to adj max and min personal
// The range is still the same, 1200-3000(4200), but adjusted for base
database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(),
database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(),
faction_id);
// Adjust the amount you can go up or down so the resulting range
@@ -8238,9 +7943,15 @@ return;
int32 Client::GetModCharacterFactionLevel(int32 faction_id) {
int32 Modded = GetCharacterFactionLevel(faction_id);
FactionMods fm;
if (database.GetFactionData(&fm, GetClass(), GetRace(), GetDeity(), faction_id))
if (database.GetFactionData(&fm, GetClass(), GetFactionRace(), GetDeity(), faction_id))
{
Modded += fm.base + fm.class_mod + fm.race_mod + fm.deity_mod;
//Tack on any bonuses from Alliance type spell effects
Modded += GetFactionBonus(faction_id);
Modded += GetItemFactionBonus(faction_id);
}
return Modded;
}
@@ -8253,7 +7964,7 @@ void Client::MerchantRejectMessage(Mob *merchant, int primaryfaction)
// If a faction is involved, get the data.
if (primaryfaction > 0) {
if (database.GetFactionData(&fmod, GetClass(), GetRace(), GetDeity(), primaryfaction)) {
if (database.GetFactionData(&fmod, GetClass(), GetFactionRace(), GetDeity(), primaryfaction)) {
tmpFactionValue = GetCharacterFactionLevel(primaryfaction);
lowestvalue = std::min(std::min(tmpFactionValue, fmod.deity_mod),
std::min(fmod.class_mod, fmod.race_mod));
@@ -8835,11 +8546,11 @@ void Client::QuestReward(Mob* target, uint32 copper, uint32 silver, uint32 gold,
}
void Client::SendHPUpdateMarquee(){
if (!this || !this->IsClient() || !this->cur_hp || !this->max_hp)
if (!this || !this->IsClient() || !this->current_hp || !this->max_hp)
return;
/* Health Update Marquee Display: Custom*/
uint8 health_percentage = (uint8)(this->cur_hp * 100 / this->max_hp);
uint8 health_percentage = (uint8)(this->current_hp * 100 / this->max_hp);
if (health_percentage >= 100)
return;
@@ -9315,3 +9026,123 @@ void Client::InitInnates()
}
}
bool Client::GetDisplayMobInfoWindow() const
{
return display_mob_info_window;
}
void Client::SetDisplayMobInfoWindow(bool display_mob_info_window)
{
Client::display_mob_info_window = display_mob_info_window;
}
bool Client::IsDevToolsWindowEnabled() const
{
return dev_tools_window_enabled;
}
/**
* @param in_dev_tools_window_enabled
*/
void Client::SetDevToolsWindowEnabled(bool in_dev_tools_window_enabled)
{
Client::dev_tools_window_enabled = in_dev_tools_window_enabled;
}
/**
* @param model_id
*/
void Client::SetPrimaryWeaponOrnamentation(uint32 model_id)
{
auto primary_item = m_inv.GetItem(EQEmu::invslot::slotPrimary);
if (primary_item) {
database.QueryDatabase(
StringFormat(
"UPDATE `inventory` SET `ornamentidfile` = %i WHERE `charid` = %i AND `slotid` = %i",
model_id,
character_id,
EQEmu::invslot::slotPrimary
));
primary_item->SetOrnamentationIDFile(model_id);
SendItemPacket(EQEmu::invslot::slotPrimary, primary_item, ItemPacketTrade);
WearChange(EQEmu::textures::weaponPrimary, static_cast<uint16>(model_id), 0);
Message(15, "Your primary weapon appearance has been modified");
}
}
/**
* @param model_id
*/
void Client::SetSecondaryWeaponOrnamentation(uint32 model_id)
{
auto secondary_item = m_inv.GetItem(EQEmu::invslot::slotSecondary);
if (secondary_item) {
database.QueryDatabase(
StringFormat(
"UPDATE `inventory` SET `ornamentidfile` = %i WHERE `charid` = %i AND `slotid` = %i",
model_id,
character_id,
EQEmu::invslot::slotSecondary
));
secondary_item->SetOrnamentationIDFile(model_id);
SendItemPacket(EQEmu::invslot::slotSecondary, secondary_item, ItemPacketTrade);
WearChange(EQEmu::textures::weaponSecondary, static_cast<uint16>(model_id), 0);
Message(15, "Your secondary weapon appearance has been modified");
}
}
/**
* Used in #goto <player_name>
*
* @param player_name
*/
bool Client::GotoPlayer(std::string player_name)
{
std::string query = StringFormat(
"SELECT"
" character_data.zone_id,"
" character_data.zone_instance,"
" character_data.x,"
" character_data.y,"
" character_data.z,"
" character_data.heading "
"FROM"
" character_data "
"WHERE"
" TRUE"
" AND character_data.name = '%s'"
" AND character_data.last_login > (UNIX_TIMESTAMP() - 600) LIMIT 1", player_name.c_str());
auto results = database.QueryDatabase(query);
if (!results.Success()) {
return false;
}
for (auto row = results.begin(); row != results.end(); ++row) {
auto zone_id = static_cast<uint32>(atoi(row[0]));
auto instance_id = static_cast<uint16>(atoi(row[1]));
auto x = static_cast<float>(atof(row[2]));
auto y = static_cast<float>(atof(row[3]));
auto z = static_cast<float>(atof(row[4]));
auto heading = static_cast<float>(atof(row[5]));
if (instance_id > 0 && !database.CheckInstanceExists(instance_id)) {
this->Message(15, "Instance no longer exists...");
return false;
}
if (instance_id > 0) {
database.AddClientToInstance(instance_id, this->CharacterID());
}
this->MovePC(zone_id, instance_id, x, y, z, heading);
return true;
}
return false;
}
+22 -35
View File
@@ -255,6 +255,17 @@ public:
std::unordered_map<Mob *, float> close_mobs;
bool is_client_moving;
void SetDisplayMobInfoWindow(bool display_mob_info_window);
bool GetDisplayMobInfoWindow() const;
bool IsDevToolsWindowEnabled() const;
void SetDevToolsWindowEnabled(bool dev_tools_window_enabled);
void SetPrimaryWeaponOrnamentation(uint32 model_id);
void SetSecondaryWeaponOrnamentation(uint32 model_id);
bool GotoPlayer(std::string player_name);
//abstract virtual function implementations required by base abstract class
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill);
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill, bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None);
@@ -355,7 +366,7 @@ public:
void SetHideMe(bool hm);
inline uint16 GetPort() const { return port; }
bool IsDead() const { return(dead); }
bool IsUnconscious() const { return ((cur_hp <= 0) ? true : false); }
bool IsUnconscious() const { return ((current_hp <= 0) ? true : false); }
inline bool IsLFP() { return LFP; }
void UpdateLFP();
@@ -557,10 +568,10 @@ public:
virtual int GetCurrentBuffSlots() const;
virtual int GetCurrentSongSlots() const;
virtual int GetCurrentDiscSlots() const { return 1; }
virtual int GetMaxBuffSlots() const { return EQEmu::constants::LongBuffs; }
virtual int GetMaxSongSlots() const { return EQEmu::constants::ShortBuffs; }
virtual int GetMaxDiscSlots() const { return EQEmu::constants::DiscBuffs; }
virtual int GetMaxTotalSlots() const { return EQEmu::constants::TotalBuffs; }
virtual int GetMaxBuffSlots() const { return EQEmu::spells::LONG_BUFFS; }
virtual int GetMaxSongSlots() const { return EQEmu::spells::SHORT_BUFFS; }
virtual int GetMaxDiscSlots() const { return EQEmu::spells::DISC_BUFFS; }
virtual int GetMaxTotalSlots() const { return EQEmu::spells::TOTAL_BUFFS; }
virtual uint32 GetFirstBuffSlot(bool disc, bool song);
virtual uint32 GetLastBuffSlot(bool disc, bool song);
virtual void InitializeBuffSlots();
@@ -801,7 +812,7 @@ public:
#ifdef PACKET_PROFILER
void DumpPacketProfile() { if(eqs) eqs->DumpPacketProfile(); }
#endif
uint32 GetEquipment(uint8 material_slot) const; // returns item id
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id
uint32 GetEquipmentColor(uint8 material_slot) const;
virtual void UpdateEquipmentLight() { m_Light.Type[EQEmu::lightsource::LightEquipment] = m_inv.FindBrightestLightType(); m_Light.Level[EQEmu::lightsource::LightEquipment] = EQEmu::lightsource::TypeToLevel(m_Light.Type[EQEmu::lightsource::LightEquipment]); }
@@ -864,7 +875,6 @@ public:
void SetAATitle(const char *Title);
void SetTitleSuffix(const char *txt);
void MemorizeSpell(uint32 slot, uint32 spellid, uint32 scribing);
int32 acmod();
// Item methods
void EVENT_ITEM_ScriptStopReturn();
@@ -972,23 +982,7 @@ public:
void SendRules(Client* client);
std::list<std::string> consent_list;
//Anti-Cheat Stuff
uint32 m_TimeSinceLastPositionCheck;
float m_DistanceSinceLastPositionCheck;
bool m_CheatDetectMoved;
void SetShadowStepExemption(bool v);
void SetKnockBackExemption(bool v);
void SetPortExemption(bool v);
void SetSenseExemption(bool v) { m_SenseExemption = v; }
void SetAssistExemption(bool v) { m_AssistExemption = v; }
const bool IsShadowStepExempted() const { return m_ShadowStepExemption; }
const bool IsKnockBackExempted() const { return m_KnockBackExemption; }
const bool IsPortExempted() const { return m_PortExemption; }
const bool IsSenseExempted() const { return m_SenseExemption; }
const bool IsAssistExempted() const { return m_AssistExemption; }
const bool GetGMSpeed() const { return (gmspeed > 0); }
void CheatDetected(CheatTypes CheatType, float x, float y, float z);
const bool IsMQExemptedArea(uint32 zoneID, float x, float y, float z) const;
bool CanUseReport;
//This is used to later set the buff duration of the spell, in slot to duration.
@@ -1177,7 +1171,6 @@ public:
inline bool IsDraggingCorpse() { return (DraggedCorpses.size() > 0); }
void DragCorpses();
inline void ClearDraggedCorpses() { DraggedCorpses.clear(); }
inline void ResetPositionTimer() { position_timer_counter = 0; }
void SendAltCurrencies();
void SetAlternateCurrencyValue(uint32 currency_id, uint32 new_amount);
void AddAlternateCurrencyValue(uint32 currency_id, int32 amount, int8 method = 0);
@@ -1189,7 +1182,7 @@ public:
void HandleLFGuildResponse(ServerPacket *pack);
void SendLFGuildStatus();
void SendGuildLFGuildStatus();
inline bool XTargettingAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::bit_UFAndLater) && RuleB(Character, EnableXTargetting)); }
inline bool XTargettingAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::maskUFAndLater) && RuleB(Character, EnableXTargetting)); }
inline uint8 GetMaxXTargets() const { return MaxXTargets; }
void SetMaxXTargets(uint8 NewMax);
bool IsXTarget(const Mob *m) const;
@@ -1213,7 +1206,7 @@ public:
bool GroupFollow(Client* inviter);
inline bool GetRunMode() const { return runmode; }
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::bit_RoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
inline bool AggroMeterAvailable() const { return ((m_ClientVersionBit & EQEmu::versions::maskRoF2AndLater)) && RuleB(Character, EnableAggroMeter); } // RoF untested
inline void SetAggroMeterLock(int in) { m_aggrometer.set_lock_id(in); }
void ProcessAggroMeter(); // builds packet and sends
@@ -1483,6 +1476,9 @@ private:
int Haste; //precalced value
uint32 tmSitting; // time stamp started sitting, used for HP regen bonus added on MAY 5, 2004
bool display_mob_info_window;
bool dev_tools_window_enabled;
int32 max_end;
int32 current_endurance;
@@ -1521,9 +1517,6 @@ private:
WaterRegionType last_region_type;
Timer position_timer;
uint8 position_timer_counter;
// this is used to try to cut back on position update reflections
int position_update_same_count;
@@ -1564,7 +1557,6 @@ private:
Timer position_update_timer; /* Timer used when client hasn't updated within a 10 second window */
glm::vec3 m_Proximity;
glm::vec4 last_major_update_position;
void BulkSendInventoryItems();
@@ -1609,11 +1601,6 @@ private:
int XPRate;
bool m_ShadowStepExemption;
bool m_KnockBackExemption;
bool m_PortExemption;
bool m_SenseExemption;
bool m_AssistExemption;
bool alternate_currency_loaded;
std::map<uint32, uint32> alternate_currency;
std::queue<std::pair<uint32, int32>> alternate_currency_queued_operations;
+113 -641
View File
@@ -275,8 +275,9 @@ int32 Client::CalcHPRegen(bool bCombat)
if (!skip_innate && m_pp.InnateSkills[InnateRegen] != InnateDisabled) {
if (level >= 50) {
++base;
if (level >= 55)
if (level >= 55) {
++base;
}
}
base *= 2;
}
@@ -324,14 +325,15 @@ int32 Client::CalcMaxHP()
max_hp += spellbonuses.HP + aabonuses.HP;
max_hp += GroupLeadershipAAHealthEnhancement();
max_hp += max_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f);
if (cur_hp > max_hp) {
cur_hp = max_hp;
if (current_hp > max_hp) {
current_hp = max_hp;
}
int hp_perc_cap = spellbonuses.HPPercCap[0];
if (hp_perc_cap) {
int curHP_cap = (max_hp * hp_perc_cap) / 100;
if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1])) {
cur_hp = curHP_cap;
if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1])) {
current_hp = curHP_cap;
}
}
return max_hp;
@@ -340,136 +342,137 @@ int32 Client::CalcMaxHP()
uint32 Mob::GetClassLevelFactor()
{
uint32 multiplier = 0;
uint8 mlevel = GetLevel();
uint8 mlevel = GetLevel();
switch (GetClass()) {
case WARRIOR: {
if (mlevel < 20) {
multiplier = 220;
}
else if (mlevel < 30) {
multiplier = 230;
}
else if (mlevel < 40) {
multiplier = 250;
}
else if (mlevel < 53) {
multiplier = 270;
}
else if (mlevel < 57) {
multiplier = 280;
}
else if (mlevel < 60) {
multiplier = 290;
}
else if (mlevel < 70) {
multiplier = 300;
}
else {
multiplier = 311;
}
break;
if (mlevel < 20) {
multiplier = 220;
}
else if (mlevel < 30) {
multiplier = 230;
}
else if (mlevel < 40) {
multiplier = 250;
}
else if (mlevel < 53) {
multiplier = 270;
}
else if (mlevel < 57) {
multiplier = 280;
}
else if (mlevel < 60) {
multiplier = 290;
}
else if (mlevel < 70) {
multiplier = 300;
}
else {
multiplier = 311;
}
break;
}
case DRUID:
case CLERIC:
case SHAMAN: {
if (mlevel < 70) {
multiplier = 150;
}
else {
multiplier = 157;
}
break;
if (mlevel < 70) {
multiplier = 150;
}
else {
multiplier = 157;
}
break;
}
case BERSERKER:
case PALADIN:
case SHADOWKNIGHT: {
if (mlevel < 35) {
multiplier = 210;
}
else if (mlevel < 45) {
multiplier = 220;
}
else if (mlevel < 51) {
multiplier = 230;
}
else if (mlevel < 56) {
multiplier = 240;
}
else if (mlevel < 60) {
multiplier = 250;
}
else if (mlevel < 68) {
multiplier = 260;
}
else {
multiplier = 270;
}
break;
if (mlevel < 35) {
multiplier = 210;
}
else if (mlevel < 45) {
multiplier = 220;
}
else if (mlevel < 51) {
multiplier = 230;
}
else if (mlevel < 56) {
multiplier = 240;
}
else if (mlevel < 60) {
multiplier = 250;
}
else if (mlevel < 68) {
multiplier = 260;
}
else {
multiplier = 270;
}
break;
}
case MONK:
case BARD:
case ROGUE:
case BEASTLORD: {
if (mlevel < 51) {
multiplier = 180;
}
else if (mlevel < 58) {
multiplier = 190;
}
else if (mlevel < 70) {
multiplier = 200;
}
else {
multiplier = 210;
}
break;
if (mlevel < 51) {
multiplier = 180;
}
else if (mlevel < 58) {
multiplier = 190;
}
else if (mlevel < 70) {
multiplier = 200;
}
else {
multiplier = 210;
}
break;
}
case RANGER: {
if (mlevel < 58) {
multiplier = 200;
}
else if (mlevel < 70) {
multiplier = 210;
}
else {
multiplier = 220;
}
break;
if (mlevel < 58) {
multiplier = 200;
}
else if (mlevel < 70) {
multiplier = 210;
}
else {
multiplier = 220;
}
break;
}
case MAGICIAN:
case WIZARD:
case NECROMANCER:
case ENCHANTER: {
if (mlevel < 70) {
multiplier = 120;
}
else {
multiplier = 127;
}
break;
if (mlevel < 70) {
multiplier = 120;
}
else {
multiplier = 127;
}
break;
}
default: {
if (mlevel < 35) {
multiplier = 210;
}
else if (mlevel < 45) {
multiplier = 220;
}
else if (mlevel < 51) {
multiplier = 230;
}
else if (mlevel < 56) {
multiplier = 240;
}
else if (mlevel < 60) {
multiplier = 250;
}
else {
multiplier = 260;
}
break;
if (mlevel < 35) {
multiplier = 210;
}
else if (mlevel < 45) {
multiplier = 220;
}
else if (mlevel < 51) {
multiplier = 230;
}
else if (mlevel < 56) {
multiplier = 240;
}
else if (mlevel < 60) {
multiplier = 250;
}
else {
multiplier = 260;
}
break;
}
}
return multiplier;
}
@@ -560,537 +563,6 @@ int32 Client::GetRawItemAC()
return Total;
}
int32 Client::acmod()
{
int agility = GetAGI();
int level = GetLevel();
if (agility < 1 || level < 1) {
return (0);
}
if (agility <= 74) {
if (agility == 1) {
return -24;
}
else if (agility <= 3) {
return -23;
}
else if (agility == 4) {
return -22;
}
else if (agility <= 6) {
return -21;
}
else if (agility <= 8) {
return -20;
}
else if (agility == 9) {
return -19;
}
else if (agility <= 11) {
return -18;
}
else if (agility == 12) {
return -17;
}
else if (agility <= 14) {
return -16;
}
else if (agility <= 16) {
return -15;
}
else if (agility == 17) {
return -14;
}
else if (agility <= 19) {
return -13;
}
else if (agility == 20) {
return -12;
}
else if (agility <= 22) {
return -11;
}
else if (agility <= 24) {
return -10;
}
else if (agility == 25) {
return -9;
}
else if (agility <= 27) {
return -8;
}
else if (agility == 28) {
return -7;
}
else if (agility <= 30) {
return -6;
}
else if (agility <= 32) {
return -5;
}
else if (agility == 33) {
return -4;
}
else if (agility <= 35) {
return -3;
}
else if (agility == 36) {
return -2;
}
else if (agility <= 38) {
return -1;
}
else if (agility <= 65) {
return 0;
}
else if (agility <= 70) {
return 1;
}
else if (agility <= 74) {
return 5;
}
}
else if (agility <= 137) {
if (agility == 75) {
if (level <= 6) {
return 9;
}
else if (level <= 19) {
return 23;
}
else if (level <= 39) {
return 33;
}
else {
return 39;
}
}
else if (agility >= 76 && agility <= 79) {
if (level <= 6) {
return 10;
}
else if (level <= 19) {
return 23;
}
else if (level <= 39) {
return 33;
}
else {
return 40;
}
}
else if (agility == 80) {
if (level <= 6) {
return 11;
}
else if (level <= 19) {
return 24;
}
else if (level <= 39) {
return 34;
}
else {
return 41;
}
}
else if (agility >= 81 && agility <= 85) {
if (level <= 6) {
return 12;
}
else if (level <= 19) {
return 25;
}
else if (level <= 39) {
return 35;
}
else {
return 42;
}
}
else if (agility >= 86 && agility <= 90) {
if (level <= 6) {
return 12;
}
else if (level <= 19) {
return 26;
}
else if (level <= 39) {
return 36;
}
else {
return 42;
}
}
else if (agility >= 91 && agility <= 95) {
if (level <= 6) {
return 13;
}
else if (level <= 19) {
return 26;
}
else if (level <= 39) {
return 36;
}
else {
return 43;
}
}
else if (agility >= 96 && agility <= 99) {
if (level <= 6) {
return 14;
}
else if (level <= 19) {
return 27;
}
else if (level <= 39) {
return 37;
}
else {
return 44;
}
}
else if (agility == 100 && level >= 7) {
if (level <= 19) {
return 28;
}
else if (level <= 39) {
return 38;
}
else {
return 45;
}
}
else if (level <= 6) {
return 15;
}
//level is >6
else if (agility >= 101 && agility <= 105) {
if (level <= 19) {
return 29;
}
else if (level <= 39) {
return 39; // not verified
}
else {
return 45;
}
}
else if (agility >= 106 && agility <= 110) {
if (level <= 19) {
return 29;
}
else if (level <= 39) {
return 39; // not verified
}
else {
return 46;
}
}
else if (agility >= 111 && agility <= 115) {
if (level <= 19) {
return 30;
}
else if (level <= 39) {
return 40; // not verified
}
else {
return 47;
}
}
else if (agility >= 116 && agility <= 119) {
if (level <= 19) {
return 31;
}
else if (level <= 39) {
return 41;
}
else {
return 47;
}
}
else if (level <= 19) {
return 32;
}
//level is > 19
else if (agility == 120) {
if (level <= 39) {
return 42;
}
else {
return 48;
}
}
else if (agility <= 125) {
if (level <= 39) {
return 42;
}
else {
return 49;
}
}
else if (agility <= 135) {
if (level <= 39) {
return 42;
}
else {
return 50;
}
}
else {
if (level <= 39) {
return 42;
}
else {
return 51;
}
}
}
else if (agility <= 300) {
if (level <= 6) {
if (agility <= 139) {
return (21);
}
else if (agility == 140) {
return (22);
}
else if (agility <= 145) {
return (23);
}
else if (agility <= 150) {
return (23);
}
else if (agility <= 155) {
return (24);
}
else if (agility <= 159) {
return (25);
}
else if (agility == 160) {
return (26);
}
else if (agility <= 165) {
return (26);
}
else if (agility <= 170) {
return (27);
}
else if (agility <= 175) {
return (28);
}
else if (agility <= 179) {
return (28);
}
else if (agility == 180) {
return (29);
}
else if (agility <= 185) {
return (30);
}
else if (agility <= 190) {
return (31);
}
else if (agility <= 195) {
return (31);
}
else if (agility <= 199) {
return (32);
}
else if (agility <= 219) {
return (33);
}
else if (agility <= 239) {
return (34);
}
else {
return (35);
}
}
else if (level <= 19) {
if (agility <= 139) {
return (34);
}
else if (agility == 140) {
return (35);
}
else if (agility <= 145) {
return (36);
}
else if (agility <= 150) {
return (37);
}
else if (agility <= 155) {
return (37);
}
else if (agility <= 159) {
return (38);
}
else if (agility == 160) {
return (39);
}
else if (agility <= 165) {
return (40);
}
else if (agility <= 170) {
return (40);
}
else if (agility <= 175) {
return (41);
}
else if (agility <= 179) {
return (42);
}
else if (agility == 180) {
return (43);
}
else if (agility <= 185) {
return (43);
}
else if (agility <= 190) {
return (44);
}
else if (agility <= 195) {
return (45);
}
else if (agility <= 199) {
return (45);
}
else if (agility <= 219) {
return (46);
}
else if (agility <= 239) {
return (47);
}
else {
return (48);
}
}
else if (level <= 39) {
if (agility <= 139) {
return (44);
}
else if (agility == 140) {
return (45);
}
else if (agility <= 145) {
return (46);
}
else if (agility <= 150) {
return (47);
}
else if (agility <= 155) {
return (47);
}
else if (agility <= 159) {
return (48);
}
else if (agility == 160) {
return (49);
}
else if (agility <= 165) {
return (50);
}
else if (agility <= 170) {
return (50);
}
else if (agility <= 175) {
return (51);
}
else if (agility <= 179) {
return (52);
}
else if (agility == 180) {
return (53);
}
else if (agility <= 185) {
return (53);
}
else if (agility <= 190) {
return (54);
}
else if (agility <= 195) {
return (55);
}
else if (agility <= 199) {
return (55);
}
else if (agility <= 219) {
return (56);
}
else if (agility <= 239) {
return (57);
}
else {
return (58);
}
}
else { //lvl >= 40
if (agility <= 139) {
return (51);
}
else if (agility == 140) {
return (52);
}
else if (agility <= 145) {
return (53);
}
else if (agility <= 150) {
return (53);
}
else if (agility <= 155) {
return (54);
}
else if (agility <= 159) {
return (55);
}
else if (agility == 160) {
return (56);
}
else if (agility <= 165) {
return (56);
}
else if (agility <= 170) {
return (57);
}
else if (agility <= 175) {
return (58);
}
else if (agility <= 179) {
return (58);
}
else if (agility == 180) {
return (59);
}
else if (agility <= 185) {
return (60);
}
else if (agility <= 190) {
return (61);
}
else if (agility <= 195) {
return (61);
}
else if (agility <= 199) {
return (62);
}
else if (agility <= 219) {
return (63);
}
else if (agility <= 239) {
return (64);
}
else {
return (65);
}
}
}
else {
//seems about 21 agil per extra AC pt over 300...
return (65 + ((agility - 300) / 21));
}
Log(Logs::Detail, Logs::Error, "Error in Client::acmod(): Agility: %i, Level: %i", agility, level);
return 0;
};
int32 Client::CalcMaxMana()
{
switch (GetCasterClass()) {
@@ -1366,7 +838,7 @@ uint32 Client::CalcCurrentWeight()
This is the ONLY instance I have seen where the client is hard coded to particular Item IDs to set a certain property for an item. It is very odd.
*/
// SoD+ client has no weight for coin
if (EQEmu::behavior::Lookup(EQEmu::versions::ConvertClientVersionToMobVersion(ClientVersion()))->CoinHasWeight) {
if (EQEmu::behavior::StaticLookup(EQEmu::versions::ConvertClientVersionToMobVersion(ClientVersion()))->CoinHasWeight) {
Total += (m_pp.platinum + m_pp.gold + m_pp.silver + m_pp.copper) / 4;
}
float Packrat = (float)spellbonuses.Packrat + (float)aabonuses.Packrat + (float)itembonuses.Packrat;
+180 -361
View File
File diff suppressed because it is too large Load Diff
+23 -77
View File
@@ -122,7 +122,7 @@ bool Client::Process() {
/* I haven't naturally updated my position in 10 seconds, updating manually */
if (!is_client_moving && position_update_timer.Check()) {
SendPositionUpdate();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
}
if (mana_timer.Check())
@@ -193,10 +193,6 @@ bool Client::Process() {
if (IsStunned() && stunned_timer.Check())
Mob::UnStun();
if (!m_CheatDetectMoved) {
m_TimeSinceLastPositionCheck = Timer::GetCurrentTime();
}
if (bardsong_timer.Check() && bardsong != 0) {
//NOTE: this is kinda a heavy-handed check to make sure the mob still exists before
//doing the next pulse on them...
@@ -263,20 +259,13 @@ bool Client::Process() {
/* Build a close range list of NPC's */
if (npc_close_scan_timer.Check()) {
close_mobs.clear();
/* Force spawn updates when traveled far */
//Force spawn updates when traveled far
bool force_spawn_updates = false;
float client_update_range = (RuleI(Range, ClientForceSpawnUpdateRange) * RuleI(Range, ClientForceSpawnUpdateRange));
if (DistanceSquared(last_major_update_position, m_Position) >= client_update_range) {
last_major_update_position = m_Position;
force_spawn_updates = true;
}
float scan_range = (RuleI(Range, ClientNPCScan) * RuleI(Range, ClientNPCScan));
auto &mob_list = entity_list.GetMobList();
for (auto itr = mob_list.begin(); itr != mob_list.end(); ++itr) {
Mob* mob = itr->second;
float distance = DistanceSquared(m_Position, mob->GetPosition());
if (mob->IsNPC()) {
if (distance <= scan_range) {
@@ -286,21 +275,9 @@ bool Client::Process() {
close_mobs.insert(std::pair<Mob *, float>(mob, distance));
}
}
if (force_spawn_updates && mob != this) {
if (mob->is_distance_roamer) {
mob->SendPositionUpdateToClient(this);
continue;
}
if (distance <= client_update_range)
mob->SendPositionUpdateToClient(this);
}
}
}
bool may_use_attacks = false;
/*
Things which prevent us from attacking:
@@ -373,25 +350,22 @@ bool Client::Process() {
}
Mob *auto_attack_target = GetTarget();
if (auto_attack && auto_attack_target != nullptr && may_use_attacks && attack_timer.Check())
{
if (auto_attack && auto_attack_target != nullptr && may_use_attacks && attack_timer.Check()) {
//check if change
//only check on primary attack.. sorry offhand you gotta wait!
if (aa_los_them_mob)
{
if (aa_los_them_mob) {
if (auto_attack_target != aa_los_them_mob ||
m_AutoAttackPosition.x != GetX() ||
m_AutoAttackPosition.y != GetY() ||
m_AutoAttackPosition.z != GetZ() ||
m_AutoAttackTargetLocation.x != aa_los_them_mob->GetX() ||
m_AutoAttackTargetLocation.y != aa_los_them_mob->GetY() ||
m_AutoAttackTargetLocation.z != aa_los_them_mob->GetZ())
{
aa_los_them_mob = auto_attack_target;
m_AutoAttackPosition = GetPosition();
m_AutoAttackTargetLocation.z != aa_los_them_mob->GetZ()) {
aa_los_them_mob = auto_attack_target;
m_AutoAttackPosition = GetPosition();
m_AutoAttackTargetLocation = glm::vec3(aa_los_them_mob->GetPosition());
los_status = CheckLosFN(auto_attack_target);
los_status_facing = IsFacingMob(aa_los_them_mob);
los_status = CheckLosFN(auto_attack_target);
los_status_facing = IsFacingMob(aa_los_them_mob);
}
// If only our heading changes, we can skip the CheckLosFN call
// but above we still need to update los_status_facing
@@ -400,25 +374,21 @@ bool Client::Process() {
los_status_facing = IsFacingMob(aa_los_them_mob);
}
}
else
{
aa_los_them_mob = auto_attack_target;
m_AutoAttackPosition = GetPosition();
else {
aa_los_them_mob = auto_attack_target;
m_AutoAttackPosition = GetPosition();
m_AutoAttackTargetLocation = glm::vec3(aa_los_them_mob->GetPosition());
los_status = CheckLosFN(auto_attack_target);
los_status_facing = IsFacingMob(aa_los_them_mob);
los_status = CheckLosFN(auto_attack_target);
los_status_facing = IsFacingMob(aa_los_them_mob);
}
if (!CombatRange(auto_attack_target))
{
if (!CombatRange(auto_attack_target)) {
Message_StringID(MT_TooFarAway, TARGET_TOO_FAR);
}
else if (auto_attack_target == this)
{
else if (auto_attack_target == this) {
Message_StringID(MT_TooFarAway, TRY_ATTACKING_SOMEONE);
}
else if (!los_status || !los_status_facing)
{
else if (!los_status || !los_status_facing) {
//you can't see your target
}
else if (auto_attack_target->GetHP() > -10) // -10 so we can watch people bleed in PvP
@@ -428,8 +398,9 @@ bool Client::Process() {
TriggerDefensiveProcs(auto_attack_target, EQEmu::invslot::slotPrimary, false);
DoAttackRounds(auto_attack_target, EQEmu::invslot::slotPrimary);
if (CheckAATimer(aaTimerRampage))
if (CheckAATimer(aaTimerRampage)) {
entity_list.AEAttack(this, 30);
}
}
}
@@ -471,31 +442,6 @@ bool Client::Process() {
}
}
if (position_timer.Check()) {
if (IsAIControlled())
{
if (!IsMoving())
{
animation = 0;
m_Delta = glm::vec4(0.0f, 0.0f, 0.0f, m_Delta.w);
SendPositionUpdate(2);
}
}
// Send a position packet every 8 seconds - if not done, other clients
// see this char disappear after 10-12 seconds of inactivity
if (position_timer_counter >= 36) { // Approx. 4 ticks per second
entity_list.SendPositionUpdates(this, pLastUpdateWZ, RuleI(Range, MobPositionUpdates), GetTarget(), true);
pLastUpdate = Timer::GetCurrentTime();
pLastUpdateWZ = pLastUpdate;
position_timer_counter = 0;
}
else {
pLastUpdate = Timer::GetCurrentTime();
position_timer_counter++;
}
}
if (HasVirus()) {
if (viral_timer.Check()) {
viral_timer_counter++;
@@ -871,7 +817,7 @@ void Client::BulkSendInventoryItems()
void Client::BulkSendMerchantInventory(int merchant_id, int npcid) {
const EQEmu::ItemData* handyitem = nullptr;
uint32 numItemSlots = 80; //The max number of items passed in the transaction.
if (m_ClientVersionBit & EQEmu::versions::bit_RoFAndLater) { // RoF+ can send 200 items
if (m_ClientVersionBit & EQEmu::versions::maskRoFAndLater) { // RoF+ can send 200 items
numItemSlots = 200;
}
const EQEmu::ItemData *item = nullptr;
@@ -1191,7 +1137,7 @@ void Client::CancelSneakHide()
// The later clients send back a OP_Hide (this has a size but data is 0)
// as well as OP_SpawnAppearance with AT_Invis and one with AT_Sneak
// So we don't have to handle any of those flags
if (ClientVersionBit() & EQEmu::versions::bit_SoFAndEarlier)
if (ClientVersionBit() & EQEmu::versions::maskSoFAndEarlier)
sneaking = false;
}
}
@@ -2188,7 +2134,7 @@ void Client::ClearHover()
entity_list.QueueClients(this, outapp, false);
safe_delete(outapp);
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater)
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::maskUFAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket(false);
CastToClient()->FastQueuePacket(&outapp);
+1389 -212
View File
File diff suppressed because it is too large Load Diff
+11
View File
@@ -84,6 +84,7 @@ void command_deletegraveyard(Client *c, const Seperator *sep);
void command_delpetition(Client *c, const Seperator *sep);
void command_depop(Client *c, const Seperator *sep);
void command_depopzone(Client *c, const Seperator *sep);
void command_devtools(Client *c, const Seperator *sep);
void command_details(Client *c, const Seperator *sep);
void command_disablerecipe(Client *c, const Seperator *sep);
void command_disarmtrap(Client *c, const Seperator *sep);
@@ -116,6 +117,7 @@ void command_givemoney(Client *c, const Seperator *sep);
void command_globalview(Client* c, const Seperator *sep);
void command_gm(Client *c, const Seperator *sep);
void command_gmspeed(Client *c, const Seperator *sep);
void command_gmzone(Client *c, const Seperator *sep);
void command_goto(Client *c, const Seperator *sep);
void command_grid(Client *c, const Seperator *sep);
void command_guild(Client *c, const Seperator *sep);
@@ -148,10 +150,12 @@ void command_iteminfo(Client *c, const Seperator *sep);
void command_itemsearch(Client *c, const Seperator *sep);
void command_itemtest(Client *c, const Seperator *sep);
void command_kick(Client *c, const Seperator *sep);
void command_killallnpcs(Client *c, const Seperator *sep);
void command_kill(Client *c, const Seperator *sep);
void command_lastname(Client *c, const Seperator *sep);
void command_level(Client *c, const Seperator *sep);
void command_listnpcs(Client *c, const Seperator *sep);
void command_list(Client *c, const Seperator *sep);
void command_listpetition(Client *c, const Seperator *sep);
void command_load_shared_memory(Client *c, const Seperator *sep);
void command_loc(Client *c, const Seperator *sep);
@@ -168,12 +172,14 @@ void command_merchantopenshop(Client *c, const Seperator *sep);
void command_modifynpcstat(Client *c, const Seperator *sep);
void command_motd(Client *c, const Seperator *sep);
void command_movechar(Client *c, const Seperator *sep);
void command_movement(Client *c, const Seperator *sep);
void command_myskills(Client *c, const Seperator *sep);
void command_mysql(Client *c, const Seperator *sep);
void command_mysqltest(Client *c, const Seperator *sep);
void command_mystats(Client *c, const Seperator *sep);
void command_name(Client *c, const Seperator *sep);
void command_netstats(Client *c, const Seperator *sep);
void command_network(Client *c, const Seperator *sep);
void command_npccast(Client *c, const Seperator *sep);
void command_npcedit(Client *c, const Seperator *sep);
void command_npcemote(Client *c, const Seperator *sep);
@@ -205,12 +211,14 @@ void command_permagender(Client *c, const Seperator *sep);
void command_permarace(Client *c, const Seperator *sep);
void command_petitioninfo(Client *c, const Seperator *sep);
void command_picklock(Client *c, const Seperator *sep);
void command_profanity(Client *c, const Seperator *sep);
#ifdef EQPROFILE
void command_profiledump(Client *c, const Seperator *sep);
void command_profilereset(Client *c, const Seperator *sep);
#endif
void command_proximity(Client *c, const Seperator *sep);
void command_push(Client *c, const Seperator *sep);
void command_pvp(Client *c, const Seperator *sep);
void command_qglobal(Client *c, const Seperator *sep);
@@ -241,6 +249,7 @@ void command_resetaa_timer(Client *c, const Seperator *sep);
void command_revoke(Client *c, const Seperator *sep);
void command_rules(Client *c, const Seperator *sep);
void command_save(Client *c, const Seperator *sep);
void command_scale(Client *c, const Seperator *sep);
void command_scribespell(Client *c, const Seperator *sep);
void command_scribespells(Client *c, const Seperator *sep);
void command_sendop(Client *c, const Seperator *sep);
@@ -292,6 +301,7 @@ 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_test(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);
@@ -318,6 +328,7 @@ void command_viewnpctype(Client *c, const Seperator *sep);
void command_viewpetition(Client *c, const Seperator *sep);
void command_wc(Client *c, const Seperator *sep);
void command_weather(Client *c, const Seperator *sep);
void command_who(Client *c, const Seperator *sep);
void command_worldshutdown(Client *c, const Seperator *sep);
void command_wp(Client *c, const Seperator *sep);
void command_wpadd(Client *c, const Seperator *sep);
-2
View File
@@ -206,8 +206,6 @@ typedef enum { //fear states
fearStateStuck //I cannot move somehow...
} FearState;
enum { FlyMode0 = 0, FlyMode1 = 1, Flymode2 = 2, FlyMode3 = 3 };
// This is actually FlyMode, from MQ2
enum GravityBehavior {
Ground,
+3 -3
View File
@@ -1374,7 +1374,7 @@ void Corpse::QueryLoot(Client* to) {
cur = itemlist.begin();
end = itemlist.end();
int corpselootlimit = EQEmu::inventory::Lookup(EQEmu::versions::ConvertClientVersionToMobVersion(to->ClientVersion()))->InventoryTypeSize[EQEmu::invtype::typeCorpse];
int corpselootlimit = to->GetInv().GetLookup()->InventoryTypeSize.Corpse;
for(; cur != end; ++cur) {
ServerLootItem_Struct* sitem = *cur;
@@ -1479,7 +1479,7 @@ void Corpse::Spawn() {
safe_delete(app);
}
uint32 Corpse::GetEquipment(uint8 material_slot) const {
uint32 Corpse::GetEquippedItemFromTextureSlot(uint8 material_slot) const {
int16 invslot;
if (material_slot > EQEmu::textures::LastTexture) {
@@ -1500,7 +1500,7 @@ uint32 Corpse::GetEquipmentColor(uint8 material_slot) const {
return 0;
}
item = database.GetItem(GetEquipment(material_slot));
item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
if(item != 0) {
return (item_tint.Slot[material_slot].UseTint ? item_tint.Slot[material_slot].Color : item->Color);
}
+1 -1
View File
@@ -126,7 +126,7 @@ class Corpse : public Mob {
void Spawn();
char corpse_name[64];
uint32 GetEquipment(uint8 material_slot) const;
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const;
uint32 GetEquipmentColor(uint8 material_slot) const;
inline int GetRezExp() { return rez_experience; }
+28 -14
View File
@@ -496,7 +496,7 @@ void Doors::HandleClick(Client* sender, uint8 trigger) {
}
}
void Doors::NPCOpen(NPC* sender, bool alt_mode)
void Doors::Open(Mob* sender, bool alt_mode)
{
if (sender) {
if (GetTriggerType() == 255 || GetTriggerDoorID() > 0 || GetLockpick() != 0 || GetKeyItem() != 0 || open_type == 59 || open_type == 58 || !sender->IsNPC()) { // this object isnt triggered or door is locked - NPCs should not open locked doors!
@@ -820,7 +820,7 @@ void Doors::SetDisableTimer(bool flag) {
void Doors::CreateDatabaseEntry()
{
if(database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) {
if (database.GetDoorsDBCountPlusOne(zone->GetShortName(), zone->GetInstanceVersion()) - 1 >= 255) {
return;
}
@@ -828,18 +828,32 @@ void Doors::CreateDatabaseEntry()
* Persist
*/
database.InsertDoor(
GetDoorDBID(),
GetDoorID(),
GetDoorName(),
m_Position,
GetOpenType(),
static_cast<uint16>(GetGuildID()),
GetLockpick(),
GetKeyItem(),
static_cast<uint8>(GetDoorParam()),
static_cast<uint8>(GetInvertState()),
GetIncline(),
GetSize()
GetDoorDBID(),
GetDoorID(),
GetDoorName(),
m_Position,
GetOpenType(),
static_cast<uint16>(GetGuildID()),
GetLockpick(),
GetKeyItem(),
static_cast<uint8>(GetDoorParam()),
static_cast<uint8>(GetInvertState()),
GetIncline(),
GetSize()
);
}
float Doors::GetX()
{
return m_Position.x;
}
float Doors::GetY()
{
return m_Position.y;
}
float Doors::GetZ()
{
return m_Position.z;
}
+5 -1
View File
@@ -49,7 +49,7 @@ public:
void ForceClose(Mob *sender, bool alt_mode = false);
void ForceOpen(Mob *sender, bool alt_mode = false);
void HandleClick(Client *sender, uint8 trigger);
void NPCOpen(NPC *sender, bool alt_mode = false);
void Open(Mob *sender, bool alt_mode = false);
void SetDisableTimer(bool flag);
void SetDoorName(const char *name);
void SetEntityID(uint32 entity) { entity_id = entity; }
@@ -64,6 +64,10 @@ public:
void SetSize(uint16 size);
void ToggleState(Mob *sender);
float GetX();
float GetY();
float GetZ();
private:
uint32 database_id;
+3 -3
View File
@@ -643,9 +643,9 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
if (reduced_recast > 0)
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
CastSpell(spell_id, target, EQEmu::spells::CastingSlot::Discipline, -1, -1, 0, -1, (uint32)DiscTimer, reduced_recast);
else{
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
CastSpell(spell_id, target, EQEmu::spells::CastingSlot::Discipline);
return true;
}
@@ -653,7 +653,7 @@ bool Client::UseDiscipline(uint32 spell_id, uint32 target) {
}
else
{
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
CastSpell(spell_id, target, EQEmu::spells::CastingSlot::Discipline);
}
return(true);
}
+1 -1
View File
@@ -1046,7 +1046,7 @@ void PerlembParser::ExportMobVariables(bool isPlayerQuest, bool isGlobalPlayerQu
if (mob && npcmob && mob->IsClient()) {
Client* client = mob->CastToClient();
fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetRace(),
fac = client->GetFactionLevel(client->CharacterID(), npcmob->GetID(), client->GetFactionRace(),
client->GetClass(), client->GetDeity(), npcmob->GetPrimaryFaction(), npcmob);
}
}
+40 -7
View File
@@ -3082,10 +3082,10 @@ XS(XS__saylink) {
Perl_croak(aTHX_ "Usage: quest::saylink(string message, [bool silent = false], [link_name = message])");
dXSTARG;
Const_char *RETVAL;
char message[250];
char link_name[250];
bool silent = false;
std::string RETVAL;
char message[250];
char link_name[250];
bool silent = false;
strcpy(message, (char *) SvPV_nolen(ST(0)));
if (items >= 2) {
silent = ((int) SvIV(ST(1))) == 0 ? false : true;
@@ -3096,7 +3096,8 @@ XS(XS__saylink) {
strcpy(link_name, message);
RETVAL = quest_manager.saylink(message, silent, link_name);
sv_setpv(TARG, RETVAL);
sv_setpv(TARG, RETVAL.c_str());
XSprePUSH;
PUSHTARG;
XSRETURN(1);
@@ -3219,9 +3220,9 @@ XS(XS__FlyMode);
XS(XS__FlyMode) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: quest::FlyMode(uint8 mode [0-3])");
Perl_croak(aTHX_ "Usage: quest::FlyMode(uint8 mode [0-5])");
uint8 flymode = (int) SvUV(ST(0));
GravityBehavior flymode = (GravityBehavior) SvUV(ST(0));
quest_manager.FlyMode(flymode);
XSRETURN_EMPTY;
@@ -3638,6 +3639,36 @@ XS(XS__UpdateZoneHeader) {
XSRETURN_EMPTY;
}
XS(XS__set_rule);
XS(XS__set_rule) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: quest::set_rule(string rule_name, string rule_value)");
std::string rule_name = (std::string) SvPV_nolen(ST(0));
std::string rule_value = (std::string) SvPV_nolen(ST(1));
RuleManager::Instance()->SetRule(rule_name.c_str(), rule_value.c_str());
XSRETURN_EMPTY;
}
XS(XS__get_rule);
XS(XS__get_rule) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: quest::get_rule(string rule_name)");
dXSTARG;
std::string rule_name = (std::string) SvPV_nolen(ST(0));
std::string rule_value;
RuleManager::Instance()->GetRule(rule_name.c_str(), rule_value);
sv_setpv(TARG, rule_value.c_str());
XSprePUSH;
PUSHTARG;
XSRETURN(1);
}
XS(XS__get_data);
XS(XS__get_data) {
dXSARGS;
@@ -3750,6 +3781,8 @@ EXTERN_C XS(boot_quest) {
newXS(strcpy(buf, "GetTimeSeconds"), XS__GetTimeSeconds, file);
newXS(strcpy(buf, "GetZoneID"), XS__GetZoneID, file);
newXS(strcpy(buf, "GetZoneLongName"), XS__GetZoneLongName, file);
newXS(strcpy(buf, "set_rule"), XS__set_rule, file);
newXS(strcpy(buf, "get_rule"), XS__get_rule, file);
newXS(strcpy(buf, "get_data"), XS__get_data, file);
newXS(strcpy(buf, "get_data_expires"), XS__get_data_expires, file);
newXS(strcpy(buf, "set_data"), XS__set_data, file);
+54 -149
View File
@@ -40,6 +40,8 @@
#include "string_ids.h"
#include "worldserver.h"
#include "water_map.h"
#include "npc_scale_manager.h"
#include "../common/say_link.h"
#ifdef _WINDOWS
#define snprintf _snprintf
@@ -648,8 +650,6 @@ void EntityList::AddNPC(NPC *npc, bool SendSpawnPacket, bool dontqueue)
parse->EventNPC(EVENT_SPAWN, npc, nullptr, "", 0);
npc->FixZ();
uint16 emoteid = npc->GetEmoteID();
if (emoteid != 0)
npc->DoNPCEmote(ONSPAWN, emoteid);
@@ -1272,32 +1272,35 @@ void EntityList::SendZoneSpawns(Client *client)
void EntityList::SendZoneSpawnsBulk(Client *client)
{
NewSpawn_Struct ns;
Mob *spawn;
uint32 maxspawns = 100;
NewSpawn_Struct ns{};
Mob *spawn;
EQApplicationPacket *app;
if (maxspawns > mob_list.size())
maxspawns = mob_list.size();
auto bzsp = new BulkZoneSpawnPacket(client, maxspawns);
uint32 max_spawns = 100;
if (max_spawns > mob_list.size()) {
max_spawns = static_cast<uint32>(mob_list.size());
}
auto bulk_zone_spawn_packet = new BulkZoneSpawnPacket(client, max_spawns);
const glm::vec4 &client_position = client->GetPosition();
const float distance_max = (600.0 * 600.0);
bool delaypkt = false;
const glm::vec4& cpos = client->GetPosition();
const float dmax = 600.0 * 600.0;
for (auto it = mob_list.begin(); it != mob_list.end(); ++it) {
spawn = it->second;
if (spawn && spawn->GetID() > 0 && spawn->Spawned()) {
if (!spawn->ShouldISpawnFor(client))
if (!spawn->ShouldISpawnFor(client)) {
continue;
}
#if 1
const glm::vec4& spos = spawn->GetPosition();
const glm::vec4 &spawn_position = spawn->GetPosition();
delaypkt = false;
if (DistanceSquared(cpos, spos) > dmax || (spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE)))
delaypkt = true;
bool is_delayed_packet = (
DistanceSquared(client_position, spawn_position) > distance_max ||
(spawn->IsClient() && (spawn->GetRace() == MINOR_ILL_OBJ || spawn->GetRace() == TREE))
);
if (delaypkt) {
if (is_delayed_packet) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
@@ -1306,35 +1309,38 @@ void EntityList::SendZoneSpawnsBulk(Client *client)
else {
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns);
bulk_zone_spawn_packet->AddSpawn(&ns);
}
spawn->SendArmorAppearance(client);
#else
/* original code kept for spawn packet research */
int32 race = spawn->GetRace();
// Illusion races on PCs don't work as a mass spawn
// But they will work as an add_spawn AFTER CLIENT_CONNECTED.
if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
app = new EQApplicationPacket;
spawn->CreateSpawnPacket(app);
client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
safe_delete(app);
}
else {
memset(&ns, 0, sizeof(NewSpawn_Struct));
spawn->FillSpawnStruct(&ns, client);
bzsp->AddSpawn(&ns);
}
// Despite being sent in the OP_ZoneSpawns packet, the client
// does not display worn armor correctly so display it.
spawn->SendArmorAppearance(client);
#endif
/**
* Original code kept for spawn packet research
*
* int32 race = spawn->GetRace();
*
* Illusion races on PCs don't work as a mass spawn
* But they will work as an add_spawn AFTER CLIENT_CONNECTED.
* if (spawn->IsClient() && (race == MINOR_ILL_OBJ || race == TREE)) {
* app = new EQApplicationPacket;
* spawn->CreateSpawnPacket(app);
* client->QueuePacket(app, true, Client::CLIENT_CONNECTED);
* safe_delete(app);
* }
* else {
* memset(&ns, 0, sizeof(NewSpawn_Struct));
* spawn->FillSpawnStruct(&ns, client);
* bzsp->AddSpawn(&ns);
* }
*
* Despite being sent in the OP_ZoneSpawns packet, the client
* does not display worn armor correctly so display it.
* spawn->SendArmorAppearance(client);
*/
}
}
safe_delete(bzsp);
safe_delete(bulk_zone_spawn_packet);
}
//this is a hack to handle a broken spawn struct
@@ -1556,7 +1562,7 @@ void EntityList::QueueClientsByTarget(Mob *sender, const EQApplicationPacket *ap
}
}
void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *app, bool iSendToSender, EQEmu::versions::ClientVersionBit client_version_bits)
void EntityList::QueueClientsByXTarget(Mob *sender, const EQApplicationPacket *app, bool iSendToSender, EQEmu::versions::ClientVersionBitmask client_version_bits)
{
auto it = client_list.begin();
while (it != client_list.end()) {
@@ -2650,56 +2656,6 @@ void EntityList::RemoveDebuffs(Mob *caster)
}
}
// Currently, a new packet is sent per entity.
// @todo: Come back and use FLAG_COMBINED to pack
// all updates into one packet.
void EntityList::SendPositionUpdates(Client *client, uint32 cLastUpdate, float update_range, Entity *always_send, bool iSendEvenIfNotChanged)
{
update_range = (update_range * update_range);
EQApplicationPacket *outapp = 0;
PlayerPositionUpdateServer_Struct *ppu = 0;
Mob *mob = 0;
auto it = mob_list.begin();
while (it != mob_list.end()) {
mob = it->second;
if (
mob && !mob->IsCorpse()
&& (it->second != client)
&& (mob->IsClient() || iSendEvenIfNotChanged || (mob->LastChange() >= cLastUpdate))
&& (it->second->ShouldISpawnFor(client))
) {
if (
update_range == 0
|| (it->second == always_send)
|| mob->IsClient()
|| (DistanceSquared(mob->GetPosition(), client->GetPosition()) <= update_range)
) {
if (mob && mob->IsClient() && mob->GetID() > 0) {
client->QueuePacket(outapp, false, Client::CLIENT_CONNECTED);
if (outapp == 0) {
outapp = new EQApplicationPacket(OP_ClientUpdate, sizeof(PlayerPositionUpdateServer_Struct));
ppu = (PlayerPositionUpdateServer_Struct*)outapp->pBuffer;
}
mob->MakeSpawnUpdate(ppu);
safe_delete(outapp);
outapp = 0;
}
}
}
++it;
}
safe_delete(outapp);
}
char *EntityList::MakeNameUnique(char *name)
{
bool used[300];
@@ -2753,58 +2709,6 @@ char *EntityList::RemoveNumbers(char *name)
return name;
}
void EntityList::ListNPCs(Client* client, const char *arg1, const char *arg2, uint8 searchtype)
{
if (arg1 == 0)
searchtype = 0;
else if (arg2 == 0 && searchtype >= 2)
searchtype = 0;
uint32 x = 0;
uint32 z = 0;
char sName[36];
auto it = npc_list.begin();
client->Message(0, "NPCs in the zone:");
if (searchtype == 0) {
while (it != npc_list.end()) {
NPC *n = it->second;
client->Message(0, " %5d: %s (%.0f, %0.f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ());
x++;
z++;
++it;
}
} else if (searchtype == 1) {
client->Message(0, "Searching by name method. (%s)",arg1);
auto tmp = new char[strlen(arg1) + 1];
strcpy(tmp, arg1);
strupr(tmp);
while (it != npc_list.end()) {
z++;
strcpy(sName, it->second->GetName());
strupr(sName);
if (strstr(sName, tmp)) {
NPC *n = it->second;
client->Message(0, " %5d: %s (%.0f, %.0f, %.0f)", n->GetID(), n->GetName(), n->GetX(), n->GetY(), n->GetZ());
x++;
}
++it;
}
safe_delete_array(tmp);
} else if (searchtype == 2) {
client->Message(0, "Searching by number method. (%s %s)",arg1,arg2);
while (it != npc_list.end()) {
z++;
if ((it->second->GetID() >= atoi(arg1)) && (it->second->GetID() <= atoi(arg2)) && (atoi(arg1) <= atoi(arg2))) {
client->Message(0, " %5d: %s", it->second->GetID(), it->second->GetName());
x++;
}
++it;
}
}
client->Message(0, "%d npcs listed. There is a total of %d npcs in this zone.", x, z);
}
void EntityList::ListNPCCorpses(Client *client)
{
uint32 x = 0;
@@ -3263,7 +3167,7 @@ void EntityList::AddHealAggro(Mob *target, Mob *caster, uint16 hate)
}
}
void EntityList::OpenDoorsNear(NPC *who)
void EntityList::OpenDoorsNear(Mob *who)
{
for (auto it = door_list.begin();it != door_list.end(); ++it) {
@@ -3276,7 +3180,7 @@ void EntityList::OpenDoorsNear(NPC *who)
float curdist = diff.x * diff.x + diff.y * diff.y;
if (diff.z * diff.z < 10 && curdist <= 100)
cdoor->NPCOpen(who);
cdoor->Open(who);
}
}
@@ -4067,8 +3971,9 @@ Mob *EntityList::GetTargetForMez(Mob *caster)
void EntityList::SendZoneAppearance(Client *c)
{
if (!c)
if (!c) {
return;
}
auto it = mob_list.begin();
while (it != mob_list.end()) {
@@ -4083,7 +3988,7 @@ void EntityList::SendZoneAppearance(Client *c)
cur->SendAppearancePacket(AT_Anim, cur->GetAppearanceValue(cur->GetAppearance()), false, true, c);
}
if (cur->GetSize() != cur->GetBaseSize()) {
cur->SendAppearancePacket(AT_Size, (uint32)cur->GetSize(), false, true, c);
cur->SendAppearancePacket(AT_Size, (uint32) cur->GetSize(), false, true, c);
}
}
++it;
@@ -4273,7 +4178,7 @@ void EntityList::ZoneWho(Client *c, Who_All_Struct *Who)
WAPP2->RankMSGID = 12315;
else if (ClientEntry->IsBuyer())
WAPP2->RankMSGID = 6056;
else if (ClientEntry->Admin() >= 10)
else if (ClientEntry->Admin() >= 10 && ClientEntry->GetGM())
WAPP2->RankMSGID = 12312;
else
WAPP2->RankMSGID = 0xFFFFFFFF;
+2 -4
View File
@@ -353,7 +353,7 @@ public:
void QueueClientsByTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, Mob* SkipThisMob = 0, bool ackreq = true,
bool HoTT = true, uint32 ClientVersionBits = 0xFFFFFFFF, bool inspect_buffs = false);
void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, EQEmu::versions::ClientVersionBit client_version_bits = EQEmu::versions::ClientVersionBit::bit_AllClients);
void QueueClientsByXTarget(Mob* sender, const EQApplicationPacket* app, bool iSendToSender = true, EQEmu::versions::ClientVersionBitmask client_version_bits = EQEmu::versions::ClientVersionBitmask::maskAllClients);
void QueueToGroupsForNPCHealthAA(Mob* sender, const EQApplicationPacket* app);
void QueueManaged(Mob* sender, const EQApplicationPacket* app, bool ignore_sender=false, bool ackreq = true);
@@ -370,9 +370,8 @@ public:
void AddHealAggro(Mob* target, Mob* caster, uint16 hate);
Mob* FindDefenseNPC(uint32 npcid);
void OpenDoorsNear(NPC* opener);
void OpenDoorsNear(Mob* opener);
void UpdateWho(bool iSendFullUpdate = false);
void SendPositionUpdates(Client* client, uint32 cLastUpdate = 0, float update_range = 0, Entity* always_send = 0, bool iSendEvenIfNotChanged = false);
char* MakeNameUnique(char* name);
static char* RemoveNumbers(char* name);
void SignalMobsByNPCID(uint32 npc_type, int signal_id);
@@ -382,7 +381,6 @@ public:
void SendPetitionToAdmins();
void AddLootToNPCS(uint32 item_id, uint32 count);
void ListNPCs(Client* client, const char* arg1 = 0, const char* arg2 = 0, uint8 searchtype = 0);
void ListNPCCorpses(Client* client);
void ListPlayerCorpses(Client* client);
int32 DeleteNPCCorpses();
+1 -1
View File
@@ -1133,7 +1133,7 @@ uint32 Client::GetCharMaxLevelFromQGlobal() {
uint32 Client::GetCharMaxLevelFromBucket() {
uint32 char_id = this->CharacterID();
std::string query = StringFormat("SELECT value FROM data_buckets WHERE key = '%i-CharMaxLevel'", char_id);
std::string query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-CharMaxLevel'", char_id);
auto results = database.QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Data bucket for CharMaxLevel for char ID %i failed.", char_id);
+2 -26
View File
@@ -157,10 +157,8 @@ void Mob::ProcessFlee()
void Mob::CalculateNewFearpoint() {
if (RuleB(Pathing, Fear) && zone->pathing) {
auto Node = zone->pathing->GetRandomLocation();
auto Node = zone->pathing->GetRandomLocation(glm::vec3(GetX(), GetY(), GetZ()));
if (Node.x != 0.0f || Node.y != 0.0f || Node.z != 0.0f) {
++Node.z;
m_FearWalkTarget = Node;
currently_fleeing = true;
@@ -169,29 +167,7 @@ void Mob::CalculateNewFearpoint() {
Log(Logs::Detail,
Logs::Pathing,
"No path found to selected node. Falling through to old fear point selection.");
"No path found to selected node during CalculateNewFearpoint.");
}
int loop = 0;
float ranx, rany, ranz;
currently_fleeing = true;
while (loop < 100) //Max 100 tries
{
int ran = 250 - (loop * 2);
loop++;
ranx = GetX() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
rany = GetY() + zone->random.Int(0, ran - 1) - zone->random.Int(0, ran - 1);
ranz = FindGroundZ(ranx, rany);
if (ranz == BEST_Z_INVALID)
continue;
float fdist = ranz - GetZ();
if (fdist >= -12 && fdist <= 12 && CheckCoordLosNoZLeaps(GetX(), GetY(), GetZ(), ranx, rany, ranz)) {
break;
}
}
if (currently_fleeing)
m_FearWalkTarget = glm::vec3(ranx, rany, ranz);
}
+1 -1
View File
@@ -284,7 +284,7 @@ void Client::GoFish()
if (tmp != nullptr) {
auto positionNPC = GetPosition();
positionNPC.x = positionNPC.x + 3;
auto npc = new NPC(tmp, nullptr, positionNPC, FlyMode3);
auto npc = new NPC(tmp, nullptr, positionNPC, GravityBehavior::Water);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
+1 -1
View File
@@ -691,7 +691,7 @@ void GuildBankManager::SendGuildBank(Client *c)
auto &guild_bank = *Iterator;
// RoF+ uses a bulk list packet -- This is also how the Action 0 of older clients basically works
if (c->ClientVersionBit() & EQEmu::versions::bit_RoFAndLater) {
if (c->ClientVersionBit() & EQEmu::versions::maskRoFAndLater) {
auto outapp = new EQApplicationPacket(OP_GuildBankItemList, sizeof(GuildBankItemListEntry_Struct) * 240);
for (int i = 0; i < GUILD_BANK_DEPOSIT_AREA_SIZE; ++i) {
const EQEmu::ItemData *Item = database.GetItem(guild_bank->Items.DepositArea[i].ItemID);
-17
View File
@@ -335,15 +335,6 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
continue;
}
auto hateEntryPosition = glm::vec3(cur->entity_on_hatelist->GetX(), cur->entity_on_hatelist->GetY(), cur->entity_on_hatelist->GetZ());
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
if (!zone->watermap->InLiquid(hateEntryPosition)) {
skipped_count++;
++iterator;
continue;
}
}
if (cur->entity_on_hatelist->Sanctuary()) {
if (hate == -1)
{
@@ -474,14 +465,6 @@ Mob *HateList::GetEntWithMostHateOnList(Mob *center, Mob *skip)
continue;
}
if (center->IsNPC() && center->CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
if(!zone->watermap->InLiquid(glm::vec3(cur->entity_on_hatelist->GetPosition()))) {
skipped_count++;
++iterator;
continue;
}
}
if (cur->entity_on_hatelist != nullptr && ((cur->stored_hate_amount > hate) || cur->is_entity_frenzy))
{
top_hate = cur->entity_on_hatelist;
+2 -2
View File
@@ -30,7 +30,7 @@ std::map<uint16, const NPCType *> Horse::horse_types;
LinkedList<NPCType *> horses_auto_delete;
Horse::Horse(Client *_owner, uint16 spell_id, const glm::vec4& position)
: NPC(GetHorseType(spell_id), nullptr, position, FlyMode3)
: NPC(GetHorseType(spell_id), nullptr, position, GravityBehavior::Water)
{
//give the horse its proper name.
strn0cpy(name, _owner->GetCleanName(), 55);
@@ -90,7 +90,7 @@ const NPCType *Horse::BuildHorseType(uint16 spell_id) {
strcpy(npc_type->name, "Unclaimed_Mount"); // this should never get used
strcpy(npc_type->special_abilities, "19,1^20,1^24,1");
npc_type->cur_hp = 1;
npc_type->current_hp = 1;
npc_type->max_hp = 1;
npc_type->race = atoi(row[0]);
npc_type->gender = atoi(row[1]); // Drogmor's are female horses. Yuck.
+25 -27
View File
@@ -113,7 +113,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
x++;
}
DeleteItemInInventory(i, 0, ((i - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]));
DeleteItemInInventory(i, 0, ((i - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank));
}
}
@@ -126,7 +126,7 @@ uint32 Client::NukeItem(uint32 itemnum, uint8 where_to_check) {
x++;
}
DeleteItemInInventory(i, 0, (((i - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]));
DeleteItemInInventory(i, 0, (((i - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT) >= GetInv().GetLookup()->InventoryTypeSize.Bank));
}
}
}
@@ -781,12 +781,12 @@ int32 Client::GetItemIDAt(int16 slot_id) {
return INVALID_ID;
}
else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return INVALID_ID;
}
else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) {
auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return INVALID_ID;
}
@@ -811,12 +811,12 @@ int32 Client::GetAugmentIDAt(int16 slot_id, uint8 augslot) {
return INVALID_ID;
}
else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return INVALID_ID;
}
else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) {
auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return INVALID_ID;
}
@@ -1474,11 +1474,11 @@ bool Client::IsValidSlot(uint32 slot) {
return ((((uint64)1 << temp_slot) & GetInv().GetLookup()->PossessionsBitmask) != 0);
}
else if (slot <= EQEmu::invslot::BANK_END && slot >= EQEmu::invslot::BANK_BEGIN) {
return ((slot - EQEmu::invslot::BANK_BEGIN) < GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]);
return ((slot - EQEmu::invslot::BANK_BEGIN) < GetInv().GetLookup()->InventoryTypeSize.Bank);
}
else if (slot <= EQEmu::invbag::BANK_BAGS_END && slot >= EQEmu::invbag::BANK_BAGS_BEGIN) {
auto temp_slot = (slot - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
return (temp_slot < GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank]);
return (temp_slot < GetInv().GetLookup()->InventoryTypeSize.Bank);
}
else if (
(slot == (uint32)INVALID_INDEX) ||
@@ -2500,7 +2500,7 @@ void Client::DisenchantSummonedBags(bool client_update)
}
for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv[slot_id];
@@ -2627,7 +2627,7 @@ void Client::RemoveNoRent(bool client_update)
}
for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv[slot_id];
@@ -2639,7 +2639,7 @@ void Client::RemoveNoRent(bool client_update)
for (auto slot_id = EQEmu::invbag::BANK_BAGS_BEGIN; slot_id <= EQEmu::invbag::BANK_BAGS_END; ++slot_id) {
auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv[slot_id];
@@ -2745,7 +2745,7 @@ void Client::RemoveDuplicateLore(bool client_update)
}
for (auto slot_id = EQEmu::invslot::BANK_BEGIN; slot_id <= EQEmu::invslot::BANK_END; ++slot_id) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv.PopItem(slot_id);
@@ -2762,7 +2762,7 @@ void Client::RemoveDuplicateLore(bool client_update)
for (auto slot_id = EQEmu::invbag::BANK_BAGS_BEGIN; slot_id <= EQEmu::invbag::BANK_BAGS_END; ++slot_id) {
auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
continue;
auto inst = m_inv.PopItem(slot_id);
@@ -2858,26 +2858,24 @@ void Client::MoveSlotNotAllowed(bool client_update)
}
// these functions operate with a material slot, which is from 0 to 8
uint32 Client::GetEquipment(uint8 material_slot) const
uint32 Client::GetEquippedItemFromTextureSlot(uint8 material_slot) const
{
int16 invslot;
int16 inventory_slot;
const EQEmu::ItemInstance *item;
if(material_slot > EQEmu::textures::LastTexture)
{
if (material_slot > EQEmu::textures::LastTexture) {
return 0;
}
invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (invslot == INVALID_INDEX)
{
inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (inventory_slot == INVALID_INDEX) {
return 0;
}
item = m_inv.GetItem(invslot);
item = m_inv.GetItem(inventory_slot);
if(item && item->GetItem())
{
if (item && item->GetItem()) {
return item->GetItem()->ID;
}
@@ -2889,7 +2887,7 @@ int32 Client::GetEquipmentMaterial(uint8 material_slot)
{
const ItemData *item;
item = database.GetItem(GetEquipment(material_slot));
item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
if(item != 0)
{
return item->Material;
@@ -2904,7 +2902,7 @@ uint32 Client::GetEquipmentColor(uint8 material_slot) const
if (material_slot > EQEmu::textures::LastTexture)
return 0;
const EQEmu::ItemData *item = database.GetItem(GetEquipment(material_slot));
const EQEmu::ItemData *item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
if(item != nullptr)
return ((m_pp.item_tint.Slot[material_slot].UseTint) ? m_pp.item_tint.Slot[material_slot].Color : item->Color);
@@ -2927,12 +2925,12 @@ void Client::SendItemPacket(int16 slot_id, const EQEmu::ItemInstance* inst, Item
return;
}
else if (slot_id <= EQEmu::invslot::BANK_END && slot_id >= EQEmu::invslot::BANK_BEGIN) {
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if ((slot_id - EQEmu::invslot::BANK_BEGIN) >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return;
}
else if (slot_id <= EQEmu::invbag::BANK_BAGS_END && slot_id >= EQEmu::invbag::BANK_BAGS_BEGIN) {
auto temp_slot = (slot_id - EQEmu::invbag::BANK_BAGS_BEGIN) / EQEmu::invbag::SLOT_COUNT;
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize[EQEmu::invtype::typeBank])
if (temp_slot >= GetInv().GetLookup()->InventoryTypeSize.Bank)
return;
}
+12
View File
@@ -1486,6 +1486,16 @@ void Lua_Client::DisableAreaRegens()
self->DisableAreaRegens();
}
void Lua_Client::SetPrimaryWeaponOrnamentation(uint32 model_id) {
Lua_Safe_Call_Void();
self->SetPrimaryWeaponOrnamentation(model_id);
}
void Lua_Client::SetSecondaryWeaponOrnamentation(uint32 model_id) {
Lua_Safe_Call_Void();
self->SetSecondaryWeaponOrnamentation(model_id);
}
luabind::scope lua_register_client() {
return luabind::class_<Lua_Client, Lua_Mob>("Client")
.def(luabind::constructor<>())
@@ -1548,6 +1558,8 @@ luabind::scope lua_register_client() {
.def("GetBindHeading", (float(Lua_Client::*)(int))&Lua_Client::GetBindHeading)
.def("GetBindZoneID", (uint32(Lua_Client::*)(void))&Lua_Client::GetBindZoneID)
.def("GetBindZoneID", (uint32(Lua_Client::*)(int))&Lua_Client::GetBindZoneID)
.def("SetPrimaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetPrimaryWeaponOrnamentation)
.def("SetSecondaryWeaponOrnamentation", (void(Lua_Client::*)(uint32))&Lua_Client::SetSecondaryWeaponOrnamentation)
.def("MovePC", (void(Lua_Client::*)(int,float,float,float,float))&Lua_Client::MovePC)
.def("MovePCInstance", (void(Lua_Client::*)(int,int,float,float,float,float))&Lua_Client::MovePCInstance)
.def("ChangeLastName", (void(Lua_Client::*)(const char *in))&Lua_Client::ChangeLastName)
+4
View File
@@ -309,6 +309,10 @@ public:
void DisableAreaEndRegen();
void EnableAreaRegens(int value);
void DisableAreaRegens();
void SetPrimaryWeaponOrnamentation(uint32 model_id);
void SetSecondaryWeaponOrnamentation(uint32 model_id);
};
#endif
+2 -2
View File
@@ -231,7 +231,7 @@ void Lua_EntityList::ReplaceWithTarget(Lua_Mob target, Lua_Mob new_target) {
self->ReplaceWithTarget(target, new_target);
}
void Lua_EntityList::OpenDoorsNear(Lua_NPC opener) {
void Lua_EntityList::OpenDoorsNear(Lua_Mob opener) {
Lua_Safe_Call_Void();
self->OpenDoorsNear(opener);
}
@@ -479,7 +479,7 @@ luabind::scope lua_register_entity_list() {
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::RemoveFromTargets)
.def("RemoveFromTargets", (void(Lua_EntityList::*)(Lua_Mob, bool))&Lua_EntityList::RemoveFromTargets)
.def("ReplaceWithTarget", (void(Lua_EntityList::*)(Lua_Mob, Lua_Mob))&Lua_EntityList::ReplaceWithTarget)
.def("OpenDoorsNear", (void(Lua_EntityList::*)(Lua_NPC))&Lua_EntityList::OpenDoorsNear)
.def("OpenDoorsNear", (void(Lua_EntityList::*)(Lua_Mob))&Lua_EntityList::OpenDoorsNear)
.def("MakeNameUnique", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::MakeNameUnique)
.def("RemoveNumbers", (std::string(Lua_EntityList::*)(const char*))&Lua_EntityList::RemoveNumbers)
.def("SignalMobsByNPCID", (void(Lua_EntityList::*)(uint32, int))&Lua_EntityList::SignalMobsByNPCID)
+1 -1
View File
@@ -84,7 +84,7 @@ public:
void RemoveFromTargets(Lua_Mob mob);
void RemoveFromTargets(Lua_Mob mob, bool RemoveFromXTargets);
void ReplaceWithTarget(Lua_Mob target, Lua_Mob new_target);
void OpenDoorsNear(Lua_NPC opener);
void OpenDoorsNear(Lua_Mob opener);
std::string MakeNameUnique(const char *name);
std::string RemoveNumbers(const char *name);
void SignalMobsByNPCID(uint32 npc_id, int signal);
+32 -24
View File
@@ -815,33 +815,39 @@ int lua_merchant_count_item(uint32 npc_id, uint32 item_id) {
std::string lua_item_link(int item_id) {
char text[250] = { 0 };
quest_manager.varlink(text, item_id);
return std::string(text);
return quest_manager.varlink(text, item_id);
}
std::string lua_say_link(const char *phrase, bool silent, const char *link_name) {
char text[256] = { 0 };
strncpy(text, phrase, 255);
quest_manager.saylink(text, silent, link_name);
return std::string(text);
return quest_manager.saylink(text, silent, link_name);
}
std::string lua_say_link(const char *phrase, bool silent) {
char text[256] = { 0 };
strncpy(text, phrase, 255);
quest_manager.saylink(text, silent, text);
return std::string(text);
return quest_manager.saylink(text, silent, text);
}
std::string lua_say_link(const char *phrase) {
char text[256] = { 0 };
strncpy(text, phrase, 255);
quest_manager.saylink(text, false, text);
return std::string(text);
return quest_manager.saylink(text, false, text);
}
void lua_set_rule(std::string rule_name, std::string rule_value) {
RuleManager::Instance()->SetRule(rule_name.c_str(), rule_value.c_str());
}
std::string lua_get_rule(std::string rule_name) {
std::string rule_value;
RuleManager::Instance()->GetRule(rule_name.c_str(), rule_value);
return rule_value;
}
std::string lua_get_data(std::string bucket_key) {
@@ -933,7 +939,7 @@ void lua_flag_instance_by_raid_leader(uint32 zone, uint32 version) {
}
void lua_fly_mode(int flymode) {
quest_manager.FlyMode(flymode);
quest_manager.FlyMode(static_cast<GravityBehavior>(flymode));
}
int lua_faction_value() {
@@ -1443,7 +1449,7 @@ void lua_create_npc(luabind::adl::object table, float x, float y, float z, float
luabind::adl::index_proxy<luabind::adl::object> cur = table["name"];
LuaCreateNPCParseString(name, 64, "_");
LuaCreateNPCParseString(lastname, 64, "");
LuaCreateNPCParse(cur_hp, int32, 30);
LuaCreateNPCParse(current_hp, int32, 30);
LuaCreateNPCParse(max_hp, int32, 30);
LuaCreateNPCParse(size, float, 6.0f);
LuaCreateNPCParse(runspeed, float, 1.25f);
@@ -1468,19 +1474,19 @@ void lua_create_npc(luabind::adl::object table, float x, float y, float z, float
LuaCreateNPCParse(AC, uint32, 0);
LuaCreateNPCParse(Mana, uint32, 0);
LuaCreateNPCParse(ATK, uint32, 0);
LuaCreateNPCParse(STR, uint32, 75);
LuaCreateNPCParse(STA, uint32, 75);
LuaCreateNPCParse(DEX, uint32, 75);
LuaCreateNPCParse(AGI, uint32, 75);
LuaCreateNPCParse(INT, uint32, 75);
LuaCreateNPCParse(WIS, uint32, 75);
LuaCreateNPCParse(CHA, uint32, 75);
LuaCreateNPCParse(MR, int32, 25);
LuaCreateNPCParse(FR, int32, 25);
LuaCreateNPCParse(CR, int32, 25);
LuaCreateNPCParse(PR, int32, 25);
LuaCreateNPCParse(DR, int32, 25);
LuaCreateNPCParse(Corrup, int32, 25);
LuaCreateNPCParse(STR, uint32, 0);
LuaCreateNPCParse(STA, uint32, 0);
LuaCreateNPCParse(DEX, uint32, 0);
LuaCreateNPCParse(AGI, uint32, 0);
LuaCreateNPCParse(INT, uint32, 0);
LuaCreateNPCParse(WIS, uint32, 0);
LuaCreateNPCParse(CHA, uint32, 0);
LuaCreateNPCParse(MR, int32, 0);
LuaCreateNPCParse(FR, int32, 0);
LuaCreateNPCParse(CR, int32, 0);
LuaCreateNPCParse(PR, int32, 0);
LuaCreateNPCParse(DR, int32, 0);
LuaCreateNPCParse(Corrup, int32, 0);
LuaCreateNPCParse(PhR, int32, 0);
LuaCreateNPCParse(haircolor, uint8, 0);
LuaCreateNPCParse(beardcolor, uint8, 0);
@@ -1541,7 +1547,7 @@ void lua_create_npc(luabind::adl::object table, float x, float y, float z, float
LuaCreateNPCParse(no_target_hotkey, bool, false);
LuaCreateNPCParse(raid_target, bool, false);
NPC* npc = new NPC(npc_type, nullptr, glm::vec4(x, y, z, heading), FlyMode3);
NPC* npc = new NPC(npc_type, nullptr, glm::vec4(x, y, z, heading), GravityBehavior::Water);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc);
}
@@ -1718,6 +1724,8 @@ luabind::scope lua_register_general() {
luabind::def("say_link", (std::string(*)(const char*,bool,const char*))&lua_say_link),
luabind::def("say_link", (std::string(*)(const char*,bool))&lua_say_link),
luabind::def("say_link", (std::string(*)(const char*))&lua_say_link),
luabind::def("set_rule", (void(*)(std::string, std::string))&lua_set_rule),
luabind::def("get_rule", (std::string(*)(std::string))&lua_get_rule),
luabind::def("get_data", (std::string(*)(std::string))&lua_get_data),
luabind::def("get_data_expires", (std::string(*)(std::string))&lua_get_data_expires),
luabind::def("set_data", (void(*)(std::string, std::string))&lua_set_data),
+32 -21
View File
@@ -770,28 +770,28 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id) {
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot));
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot));
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time);
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot), cast_time);
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost);
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot), cast_time, mana_cost);
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot));
}
bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, int mana_cost, int item_slot, int timer,
int timer_duration) {
Lua_Safe_Call_Bool();
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
static_cast<uint32>(timer), static_cast<uint32>(timer_duration));
}
@@ -800,7 +800,7 @@ bool Lua_Mob::CastSpell(int spell_id, int target_id, int slot, int cast_time, in
Lua_Safe_Call_Bool();
int16 res = resist_adjust;
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
return self->CastSpell(spell_id, target_id, static_cast<EQEmu::spells::CastingSlot>(slot), cast_time, mana_cost, nullptr, static_cast<uint32>(item_slot),
static_cast<uint32>(timer), static_cast<uint32>(timer_duration), &res);
}
@@ -811,27 +811,27 @@ bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target) {
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot));
return self->SpellFinished(spell_id, target, static_cast<EQEmu::spells::CastingSlot>(slot));
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::spells::CastingSlot>(slot), mana_used);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::spells::CastingSlot>(slot), mana_used, inventory_slot);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::spells::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust);
}
bool Lua_Mob::SpellFinished(int spell_id, Lua_Mob target, int slot, int mana_used, uint32 inventory_slot, int resist_adjust, bool proc) {
Lua_Safe_Call_Bool();
return self->SpellFinished(spell_id, target, static_cast<EQEmu::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc);
return self->SpellFinished(spell_id, target, static_cast<EQEmu::spells::CastingSlot>(slot), mana_used, inventory_slot, resist_adjust, proc);
}
void Lua_Mob::SendBeginCast(int spell_id, int cast_time) {
@@ -1124,15 +1124,24 @@ double Lua_Mob::CalculateHeadingToTarget(double in_x, double in_y) {
return self->CalculateHeadingToTarget(static_cast<float>(in_x), static_cast<float>(in_y));
}
bool Lua_Mob::CalculateNewPosition(double x, double y, double z, double speed) {
Lua_Safe_Call_Bool();
return self->CalculateNewPosition(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z), static_cast<float>(speed));
void Lua_Mob::RunTo(double x, double y, double z) {
Lua_Safe_Call_Void();
self->RunTo(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
}
bool Lua_Mob::CalculateNewPosition(double x, double y, double z, double speed, bool check_z) {
Lua_Safe_Call_Bool();
return self->CalculateNewPosition(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z), static_cast<float>(speed),
check_z);
void Lua_Mob::WalkTo(double x, double y, double z) {
Lua_Safe_Call_Void();
self->WalkTo(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
}
void Lua_Mob::NavigateTo(double x, double y, double z) {
Lua_Safe_Call_Void();
self->NavigateTo(static_cast<float>(x), static_cast<float>(y), static_cast<float>(z));
}
void Lua_Mob::StopNavigation() {
Lua_Safe_Call_Void();
self->StopNavigation();
}
float Lua_Mob::CalculateDistance(double x, double y, double z) {
@@ -1430,7 +1439,7 @@ void Lua_Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uin
void Lua_Mob::SetFlyMode(int in) {
Lua_Safe_Call_Void();
self->SetFlyMode(in);
self->SetFlyMode(static_cast<GravityBehavior>(in));
}
void Lua_Mob::SetTexture(int in) {
@@ -2361,8 +2370,10 @@ luabind::scope lua_register_mob() {
.def("FaceTarget", (void(Lua_Mob::*)(Lua_Mob))&Lua_Mob::FaceTarget)
.def("SetHeading", (void(Lua_Mob::*)(double))&Lua_Mob::SetHeading)
.def("CalculateHeadingToTarget", (double(Lua_Mob::*)(double,double))&Lua_Mob::CalculateHeadingToTarget)
.def("CalculateNewPosition", (bool(Lua_Mob::*)(double,double,double,double))&Lua_Mob::CalculateNewPosition)
.def("CalculateNewPosition", (bool(Lua_Mob::*)(double,double,double,double,bool))&Lua_Mob::CalculateNewPosition)
.def("RunTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::RunTo)
.def("WalkTo", (void(Lua_Mob::*)(double, double, double))&Lua_Mob::WalkTo)
.def("NavigateTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::NavigateTo)
.def("StopNavigation", (void(Lua_Mob::*)(void))&Lua_Mob::StopNavigation)
.def("CalculateDistance", (float(Lua_Mob::*)(double,double,double))&Lua_Mob::CalculateDistance)
.def("SendTo", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendTo)
.def("SendToFixZ", (void(Lua_Mob::*)(double,double,double))&Lua_Mob::SendToFixZ)
+4 -2
View File
@@ -240,8 +240,10 @@ public:
void FaceTarget(Lua_Mob target);
void SetHeading(double in);
double CalculateHeadingToTarget(double in_x, double in_y);
bool CalculateNewPosition(double x, double y, double z, double speed);
bool CalculateNewPosition(double x, double y, double z, double speed, bool check_z);
void RunTo(double x, double y, double z);
void WalkTo(double x, double y, double z);
void NavigateTo(double x, double y, double z);
void StopNavigation();
float CalculateDistance(double x, double y, double z);
void SendTo(double x, double y, double z);
void SendToFixZ(double x, double y, double z);
+3 -9
View File
@@ -308,14 +308,9 @@ void Lua_NPC::NextGuardPosition() {
self->NextGuardPosition();
}
void Lua_NPC::SaveGuardSpot() {
void Lua_NPC::SaveGuardSpot(float x, float y, float z, float heading) {
Lua_Safe_Call_Void();
self->SaveGuardSpot();
}
void Lua_NPC::SaveGuardSpot(bool clear) {
Lua_Safe_Call_Void();
self->SaveGuardSpot(clear);
self->SaveGuardSpot(glm::vec4(x, y, z, heading));
}
bool Lua_NPC::IsGuarding() {
@@ -597,8 +592,7 @@ luabind::scope lua_register_npc() {
.def("PauseWandering", (void(Lua_NPC::*)(int))&Lua_NPC::PauseWandering)
.def("MoveTo", (void(Lua_NPC::*)(float,float,float,float,bool))&Lua_NPC::MoveTo)
.def("NextGuardPosition", (void(Lua_NPC::*)(void))&Lua_NPC::NextGuardPosition)
.def("SaveGuardSpot", (void(Lua_NPC::*)(void))&Lua_NPC::SaveGuardSpot)
.def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot)
.def("SaveGuardSpot", (void(Lua_NPC::*)(float,float,float,float))&Lua_NPC::SaveGuardSpot)
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox)
+1 -2
View File
@@ -87,8 +87,7 @@ public:
void PauseWandering(int pause_time);
void MoveTo(float x, float y, float z, float h, bool save);
void NextGuardPosition();
void SaveGuardSpot();
void SaveGuardSpot(bool clear);
void SaveGuardSpot(float x, float y, float z, float heading);
bool IsGuarding();
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y);
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay);
+50 -80
View File
@@ -29,7 +29,7 @@ extern volatile bool is_zone_loaded;
#endif
Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
: NPC(d, nullptr, glm::vec4(x, y, z, heading), 0, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000)
: NPC(d, nullptr, glm::vec4(x, y, z, heading), GravityBehavior::Water, false), endupkeep_timer(1000), rest_timer(1), confidence_timer(6000), check_target_timer(2000)
{
base_hp = d->max_hp;
base_mana = d->Mana;
@@ -66,7 +66,7 @@ Merc::Merc(const NPCType* d, float x, float y, float z, float heading)
memset(equipment, 0, sizeof(equipment));
SetMercID(0);
SetStance(MercStanceBalanced);
SetStance(EQEmu::constants::stanceBalanced);
rest_timer.Disable();
if (GetClass() == ROGUE)
@@ -859,14 +859,14 @@ int32 Merc::CalcMaxHP() {
max_hp += max_hp * ((spellbonuses.MaxHPChange + itembonuses.MaxHPChange) / 10000.0f);
if (cur_hp > max_hp)
cur_hp = max_hp;
if (current_hp > max_hp)
current_hp = max_hp;
int hp_perc_cap = spellbonuses.HPPercCap[0];
if(hp_perc_cap) {
int curHP_cap = (max_hp * hp_perc_cap) / 100;
if (cur_hp > curHP_cap || (spellbonuses.HPPercCap[1] && cur_hp > spellbonuses.HPPercCap[1]))
cur_hp = curHP_cap;
if (current_hp > curHP_cap || (spellbonuses.HPPercCap[1] && current_hp > spellbonuses.HPPercCap[1]))
current_hp = curHP_cap;
}
return max_hp;
@@ -1271,7 +1271,7 @@ bool Merc::Process()
//6 seconds, or whatever the rule is set to has passed, send this position to everyone to avoid ghosting
if(!IsMoving() && !IsEngaged())
{
SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
if(IsSitting()) {
if(!rest_timer.Enabled()) {
rest_timer.Start(RuleI(Character, RestRegenTimeToActivate) * 1000);
@@ -1465,35 +1465,15 @@ void Merc::AI_Process() {
if(moved) {
moved = false;
SetCurrentSpeed(0);
StopNavigation();
}
}
return;
}
else if (!CheckLosFN(GetTarget())) {
if (RuleB(Mercs, MercsUsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(
GetTarget()->GetX(),
GetTarget()->GetY(),
GetTarget()->GetZ(),
GetRunspeed(),
WaypointChanged,
NodeReached
);
if (WaypointChanged)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetRunspeed());
}
else {
Mob* follow = entity_list.GetMob(GetFollowID());
if (follow)
CalculateNewPosition(follow->GetX(), follow->GetY(), follow->GetZ(), GetRunspeed());
}
auto Goal = GetTarget()->GetPosition();
RunTo(Goal.x, Goal.y, Goal.z);
return;
}
@@ -1527,7 +1507,7 @@ void Merc::AI_Process() {
SetRunAnimSpeed(0);
if(moved) {
SetCurrentSpeed(0);
StopNavigation();
}
}
@@ -1559,7 +1539,7 @@ void Merc::AI_Process() {
float newZ = 0;
FaceTarget(GetTarget());
if (PlotPositionAroundTarget(this, newX, newY, newZ)) {
CalculateNewPosition(newX, newY, newZ, GetRunspeed());
RunTo(newX, newY, newZ);
return;
}
}
@@ -1571,7 +1551,7 @@ void Merc::AI_Process() {
float newY = 0;
float newZ = 0;
if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ)) {
CalculateNewPosition(newX, newY, newZ, GetRunspeed());
RunTo(newX, newY, newZ);
return;
}
}
@@ -1582,16 +1562,16 @@ void Merc::AI_Process() {
float newY = 0;
float newZ = 0;
if (PlotPositionAroundTarget(GetTarget(), newX, newY, newZ, false) && GetArchetype() != ARCHETYPE_CASTER) {
CalculateNewPosition(newX, newY, newZ, GetRunspeed());
RunTo(newX, newY, newZ);
return;
}
}
}
if (IsMoving())
SendPositionUpdate();
else
SendPosition();
//if (IsMoving())
// SendPositionUpdate();
//else
// SendPosition();
}
if(!IsMercCaster() && GetTarget() && !IsStunned() && !IsMezzed() && (GetAppearance() != eaDead))
@@ -1709,14 +1689,14 @@ void Merc::AI_Process() {
{
if(!IsRooted()) {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", GetTarget()->GetCleanName());
CalculateNewPosition(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), GetRunspeed());
RunTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ());
return;
}
if(IsMoving())
SendPositionUpdate();
else
SendPosition();
//if(IsMoving())
// SendPositionUpdate();
//else
// SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
}
} // end not in combat range
@@ -1764,27 +1744,19 @@ void Merc::AI_Process() {
if (follow) {
float dist = DistanceSquared(m_Position, follow->GetPosition());
int speed = GetRunspeed();
bool running = true;
if (dist < GetFollowDistance() + 1000)
speed = GetWalkspeed();
running = false;
SetRunAnimSpeed(0);
if (dist > GetFollowDistance()) {
if (RuleB(Mercs, MercsUsePathing) && zone->pathing) {
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(follow->GetX(), follow->GetY(), follow->GetZ(),
speed, WaypointChanged, NodeReached);
if (WaypointChanged)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed);
if (running) {
RunTo(follow->GetX(), follow->GetY(), follow->GetZ());
}
else {
CalculateNewPosition(follow->GetX(), follow->GetY(), follow->GetZ(), speed);
WalkTo(follow->GetX(), follow->GetY(), follow->GetZ());
}
if (rest_timer.Enabled())
@@ -1793,7 +1765,7 @@ void Merc::AI_Process() {
else {
if (moved) {
moved = false;
SetCurrentSpeed(0);
StopNavigation();
}
}
}
@@ -1819,8 +1791,7 @@ void Merc::AI_Start(int32 iMoveDelay) {
}
SendTo(GetX(), GetY(), GetZ());
SetChanged();
SaveGuardSpot();
SaveGuardSpot(GetPosition());
}
void Merc::AI_Stop() {
@@ -1937,7 +1908,7 @@ bool Merc::AI_IdleCastCheck() {
bool EntityList::Merc_AICheckCloseBeneficialSpells(Merc* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes&SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
//according to live, you can buff and heal through walls...
//now with PCs, this only applies if you can TARGET the target, but
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
@@ -2019,10 +1990,10 @@ bool Merc::AIDoSpellCast(uint16 spellid, Mob* tar, int32 mana_cost, uint32* oDon
|| dist2 <= GetActSpellRange(spellid, spells[spellid].range)*GetActSpellRange(spellid, spells[spellid].range)) && (mana_cost <= GetMana() || GetMana() == GetMaxMana()))
{
SetRunAnimSpeed(0);
SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
SetMoving(false);
result = CastSpell(spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
result = CastSpell(spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, 0);
if(IsCasting() && IsSitting())
Stand();
@@ -3698,13 +3669,13 @@ MercSpell Merc::GetBestMercSpellForAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
initialCastChance = 50;
break;
case MercStanceBalanced:
case EQEmu::constants::stanceBalanced:
initialCastChance = 25;
break;
case MercStanceBurn:
case EQEmu::constants::stanceBurn:
initialCastChance = 0;
break;
}
@@ -3746,11 +3717,11 @@ MercSpell Merc::GetBestMercSpellForTargetedAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 2;
break;
}
@@ -3798,11 +3769,11 @@ MercSpell Merc::GetBestMercSpellForPBAENuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 2;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 3;
break;
}
@@ -3849,11 +3820,11 @@ MercSpell Merc::GetBestMercSpellForAERainNuke(Merc* caster, Mob* tar) {
switch(caster->GetStance())
{
case MercStanceBurnAE:
case EQEmu::constants::stanceBurnAE:
numTargetsCheck = 1;
break;
case MercStanceBalanced:
case MercStanceBurn:
case EQEmu::constants::stanceBalanced:
case EQEmu::constants::stanceBurn:
numTargetsCheck = 2;
break;
}
@@ -4074,7 +4045,7 @@ bool Merc::UseDiscipline(int32 spell_id, int32 target) {
if(IsCasting())
InterruptSpell();
CastSpell(spell_id, target, EQEmu::CastingSlot::Discipline);
CastSpell(spell_id, target, EQEmu::spells::CastingSlot::Discipline);
return(true);
}
@@ -4389,9 +4360,8 @@ void Merc::Sit() {
if(IsMoving()) {
moved = false;
// SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
SetMoving(false);
tar_ndx = 0;
}
SetAppearance(eaSitting);
@@ -4979,7 +4949,7 @@ void Merc::ScaleStats(int scalepercent, bool setmax) {
max_hp = (int)((float)base_hp * scalerate);
base_hp = max_hp;
if (setmax)
cur_hp = max_hp;
current_hp = max_hp;
}
if (base_mana)
@@ -5158,7 +5128,7 @@ bool Merc::Spawn(Client *owner) {
entity_list.AddMerc(this, true, true);
SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
Log(Logs::General, Logs::Mercenaries, "Spawn Mercenary %s.", GetName());
@@ -5679,7 +5649,7 @@ void Client::SpawnMerc(Merc* merc, bool setMaxStats) {
merc->SetSuspended(false);
SetMerc(merc);
merc->Unsuspend(setMaxStats);
merc->SetStance(GetMercInfo().Stance);
merc->SetStance((EQEmu::constants::StanceType)GetMercInfo().Stance);
Log(Logs::General, Logs::Mercenaries, "SpawnMerc Success for %s.", GetName());
+3 -15
View File
@@ -30,18 +30,6 @@ namespace EQEmu
const int MercAISpellRange = 100; // TODO: Write a method that calcs what the merc's spell range is based on spell, equipment, AA, whatever and replace this
enum MercStanceType {
MercStancePassive = 1,
MercStanceBalanced,
MercStanceEfficient,
MercStanceReactive,
MercStanceAggressive,
MercStanceAssist,
MercStanceBurn,
MercStanceEfficient2,
MercStanceBurnAE
};
struct MercSpell {
uint16 spellid; // <= 0 = no spell
uint32 type; // 0 = never, must be one (and only one) of the defined values
@@ -175,7 +163,7 @@ public:
uint8 GetTierID() { return _TierID; }
uint32 GetCostFormula() { return _CostFormula; }
uint32 GetMercNameType() { return _NameType; }
uint32 GetStance() { return _currentStance; }
EQEmu::constants::StanceType GetStance() { return _currentStance; }
int GetHatedCount() { return _hatedCount; }
inline const uint8 GetClientVersion() const { return _OwnerClientVersion; }
@@ -265,7 +253,7 @@ public:
void SetMercNameType( uint8 nametype ) { _NameType = nametype; }
void SetClientVersion(uint8 clientVersion) { _OwnerClientVersion = clientVersion; }
void SetSuspended(bool suspended) { _suspended = suspended; }
void SetStance( uint32 stance ) { _currentStance = stance; }
void SetStance( EQEmu::constants::StanceType stance ) { _currentStance = stance; }
void SetHatedCount( int count ) { _hatedCount = count; }
void Sit();
@@ -385,7 +373,7 @@ private:
uint8 _CostFormula;
uint8 _NameType;
uint8 _OwnerClientVersion;
uint32 _currentStance;
EQEmu::constants::StanceType _currentStance;
EQEmu::InventoryProfile m_inv;
int32 max_end;
+611 -884
View File
File diff suppressed because it is too large Load Diff
+152 -145
View File
@@ -49,6 +49,7 @@ class Aura;
struct AuraRecord;
struct NewSpawn_Struct;
struct PlayerPositionUpdateServer_Struct;
class MobMovementManager;
const int COLLISION_BOX_SIZE = 8;
@@ -106,65 +107,66 @@ public:
AuraMgr() : count(0) { }
};
Mob(const char* in_name,
const char* in_lastname,
int32 in_cur_hp,
int32 in_max_hp,
uint8 in_gender,
uint16 in_race,
uint8 in_class,
bodyType in_bodytype,
uint8 in_deity,
uint8 in_level,
uint32 in_npctype_id,
float in_size,
float in_runspeed,
const glm::vec4& position,
uint8 in_light,
uint8 in_texture,
uint8 in_helmtexture,
uint16 in_ac,
uint16 in_atk,
uint16 in_str,
uint16 in_sta,
uint16 in_dex,
uint16 in_agi,
uint16 in_int,
uint16 in_wis,
uint16 in_cha,
uint8 in_haircolor,
uint8 in_beardcolor,
uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye?
uint8 in_eyecolor2,
uint8 in_hairstyle,
uint8 in_luclinface,
uint8 in_beard,
uint32 in_drakkin_heritage,
uint32 in_drakkin_tattoo,
uint32 in_drakkin_details,
EQEmu::TintProfile in_armor_tint,
uint8 in_aa_title,
uint8 in_see_invis, // see through invis
uint8 in_see_invis_undead, // see through invis vs. undead
uint8 in_see_hide,
uint8 in_see_improved_hide,
int32 in_hp_regen,
int32 in_mana_regen,
uint8 in_qglobal,
uint8 in_maxlevel,
uint32 in_scalerate,
uint8 in_armtexture,
uint8 in_bracertexture,
uint8 in_handtexture,
uint8 in_legtexture,
uint8 in_feettexture
Mob(
const char *in_name,
const char *in_lastname,
int32 in_cur_hp,
int32 in_max_hp,
uint8 in_gender,
uint16 in_race,
uint8 in_class,
bodyType in_bodytype,
uint8 in_deity,
uint8 in_level,
uint32 in_npctype_id,
float in_size,
float in_runspeed,
const glm::vec4 &position,
uint8 in_light,
uint8 in_texture,
uint8 in_helmtexture,
uint16 in_ac,
uint16 in_atk,
uint16 in_str,
uint16 in_sta,
uint16 in_dex,
uint16 in_agi,
uint16 in_int,
uint16 in_wis,
uint16 in_cha,
uint8 in_haircolor,
uint8 in_beardcolor,
uint8 in_eyecolor1, // the eyecolors always seem to be the same, maybe left and right eye?
uint8 in_eyecolor2,
uint8 in_hairstyle,
uint8 in_luclinface,
uint8 in_beard,
uint32 in_drakkin_heritage,
uint32 in_drakkin_tattoo,
uint32 in_drakkin_details,
EQEmu::TintProfile in_armor_tint,
uint8 in_aa_title,
uint8 in_see_invis, // see through invis
uint8 in_see_invis_undead, // see through invis vs. undead
uint8 in_see_hide,
uint8 in_see_improved_hide,
int32 in_hp_regen,
int32 in_mana_regen,
uint8 in_qglobal,
uint8 in_maxlevel,
uint32 in_scalerate,
uint8 in_armtexture,
uint8 in_bracertexture,
uint8 in_handtexture,
uint8 in_legtexture,
uint8 in_feettexture
);
virtual ~Mob();
inline virtual bool IsMob() const { return true; }
inline virtual bool InZone() const { return true; }
bool is_distance_roamer;
void DisplayInfo(Mob *mob);
//Somewhat sorted: needs documenting!
@@ -236,34 +238,47 @@ public:
return;
}
//Appearance
void SendLevelAppearance();
void SendStunAppearance();
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5,
Client *specific_target=nullptr);
void SendTargetable(bool on, Client *specific_target = nullptr);
virtual void SendArmorAppearance(Client *one_client = nullptr);
virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr);
virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0,
uint32 unknown06 = 0, uint32 unknown18 = 0);
virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint);
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 hero_forge_model = 0);
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0,
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr, EQEmu::skills::SkillType skillInUse = EQEmu::skills::SkillArchery);
void ChangeSize(float in_size, bool bNoRestriction = false);
inline uint8 SeeInvisible() const { return see_invis; }
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
/**
************************************************
* Appearance
************************************************
*/
EQEmu::InternalTextureProfile mob_texture_profile = {};
bool IsInvisible(Mob* other = 0) const;
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt);
inline bool GetSeeInvisible(uint8 see_invis);
inline bool SeeHide() const { return see_hide; }
inline bool SeeImprovedHide() const { return see_improved_hide; }
inline bool GetSeeInvisible(uint8 see_invis);
bool IsInvisible(Mob* other = 0) const;
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
inline uint8 SeeInvisible() const { return see_invis; }
int32 GetTextureProfileMaterial(uint8 material_slot) const;
int32 GetTextureProfileColor(uint8 material_slot) const;
int32 GetTextureProfileHeroForgeModel(uint8 material_slot) const;
virtual void SendArmorAppearance(Client *one_client = nullptr);
virtual void SendTextureWC(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0, uint32 unknown06 = 0, uint32 unknown18 = 0);
virtual void SendWearChange(uint8 material_slot, Client *one_client = nullptr);
virtual void SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint);
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 hero_forge_model = 0);
void ChangeSize(float in_size, bool bNoRestriction = false);
void DoAnim(const int animnum, int type=0, bool ackreq = true, eqFilterType filter = FilterNone);
void ProjectileAnimation(Mob* to, int item_id, bool IsArrow = false, float speed = 0, float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr, EQEmu::skills::SkillType skillInUse = EQEmu::skills::SkillArchery);
void SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32 parm4, uint32 parm5, Client *specific_target=nullptr);
void SendLevelAppearance();
void SendStunAppearance();
void SendTargetable(bool on, Client *specific_target = nullptr);
void SetInvisible(uint8 state);
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt);
void SetMobTextureProfile(uint8 material_slot, uint16 texture, uint32 color = 0, uint32 hero_forge_model = 0);
//Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, EQEmu::CastingSlot slot);
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, EQEmu::spells::CastingSlot slot);
void BardPulse(uint16 spell_id, Mob *caster);
//Spell
@@ -294,24 +309,24 @@ public:
void SendSpellBarEnable(uint16 spellid);
void ZeroCastingVars();
virtual void SpellProcess();
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
virtual bool CastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot = EQEmu::spells::CastingSlot::Item, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 *resist_adjust = nullptr,
uint32 aa_id = 0);
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, int32 casttime = -1,
virtual bool DoCastSpell(uint16 spell_id, uint16 target_id, EQEmu::spells::CastingSlot slot = EQEmu::spells::CastingSlot::Item, int32 casttime = -1,
int32 mana_cost = -1, uint32* oSpellWillFinish = 0, uint32 item_slot = 0xFFFFFFFF,
uint32 timer = 0xFFFFFFFF, uint32 timer_duration = 0, int16 resist_adjust = 0,
uint32 aa_id = 0);
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQEmu::CastingSlot slot, uint16 mana_used,
void CastedSpellFinished(uint16 spell_id, uint32 target_id, EQEmu::spells::CastingSlot slot, uint16 mana_used,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0);
bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::CastingSlot slot = EQEmu::CastingSlot::Item, uint16 mana_used = 0,
bool SpellFinished(uint16 spell_id, Mob *target, EQEmu::spells::CastingSlot slot = EQEmu::spells::CastingSlot::Item, uint16 mana_used = 0,
uint32 inventory_slot = 0xFFFFFFFF, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
void SendBeginCast(uint16 spell_id, uint32 casttime);
virtual bool SpellOnTarget(uint16 spell_id, Mob* spelltar, bool reflect = false,
bool use_resist_adjust = false, int16 resist_adjust = 0, bool isproc = false, int level_override = -1);
virtual bool SpellEffect(Mob* caster, uint16 spell_id, float partial = 100, int level_override = -1);
virtual bool DetermineSpellTargets(uint16 spell_id, Mob *&spell_target, Mob *&ae_center,
CastAction_type &CastAction, EQEmu::CastingSlot slot, bool isproc = false);
CastAction_type &CastAction, EQEmu::spells::CastingSlot slot, bool isproc = false);
virtual bool CheckFizzle(uint16 spell_id);
virtual bool CheckSpellLevelRestriction(uint16 spell_id);
virtual bool IsImmuneToSpell(uint16 spell_id, Mob *caster);
@@ -409,7 +424,7 @@ public:
bool CanFacestab() { return can_facestab; }
void SetFacestab(bool val) { can_facestab = val; }
virtual uint16 GetSkill(EQEmu::skills::SkillType skill_num) const { return 0; }
virtual uint32 GetEquipment(uint8 material_slot) const { return(0); }
virtual uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const { return(0); }
virtual int32 GetEquipmentMaterial(uint8 material_slot) const;
virtual int32 GetHerosForgeModel(uint8 material_slot) const;
virtual uint32 GetEquipmentColor(uint8 material_slot) const;
@@ -419,14 +434,15 @@ public:
virtual bool Death(Mob* killerMob, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill) = 0;
virtual void Damage(Mob* from, int32 damage, uint16 spell_id, EQEmu::skills::SkillType attack_skill,
bool avoidable = true, int8 buffslot = -1, bool iBuffTic = false, eSpecialAttacks special = eSpecialAttacks::None) = 0;
inline virtual void SetHP(int32 hp) { if (hp >= max_hp) cur_hp = max_hp; else cur_hp = hp;}
inline virtual void SetHP(int32 hp) { if (hp >= max_hp) current_hp = max_hp; else current_hp = hp;}
bool ChangeHP(Mob* other, int32 amount, uint16 spell_id = 0, int8 buffslot = -1, bool iBuffTic = false);
inline void SetOOCRegen(int32 newoocregen) {ooc_regen = newoocregen;}
virtual void Heal();
virtual void HealDamage(uint32 ammount, Mob* caster = nullptr, uint16 spell_id = SPELL_UNKNOWN);
virtual void SetMaxHP() { cur_hp = max_hp; }
virtual void SetMaxHP() { current_hp = max_hp; }
virtual inline uint16 GetBaseRace() const { return base_race; }
virtual inline uint8 GetBaseGender() const { return base_gender; }
virtual uint16 GetFactionRace();
virtual inline uint16 GetDeity() const { return deity; }
virtual EQEmu::deity::DeityTypeBit GetDeityBit() { return EQEmu::deity::ConvertDeityTypeToDeityTypeBit((EQEmu::deity::DeityType)deity); }
inline uint16 GetRace() const { return race; }
@@ -470,8 +486,8 @@ public:
inline Mob* GetTarget() const { return target; }
virtual void SetTarget(Mob* mob);
inline bool HasTargetReflection() const { return (target && target != this && target->target == this); }
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)cur_hp/max_hp*100); }
virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast<int>(cur_hp * 100 / max_hp); }
virtual inline float GetHPRatio() const { return max_hp == 0 ? 0 : ((float)current_hp/max_hp*100); }
virtual inline int GetIntHPRatio() const { return max_hp == 0 ? 0 : static_cast<int>(current_hp * 100 / max_hp); }
inline int32 GetAC() const { return AC; }
inline virtual int32 GetATK() const { return ATK + itembonuses.ATK + spellbonuses.ATK; }
inline virtual int32 GetATKBonus() const { return itembonuses.ATK + spellbonuses.ATK; }
@@ -482,6 +498,11 @@ public:
inline virtual int32 GetINT() const { return INT + itembonuses.INT + spellbonuses.INT; }
inline virtual int32 GetWIS() const { return WIS + itembonuses.WIS + spellbonuses.WIS; }
inline virtual int32 GetCHA() const { return CHA + itembonuses.CHA + spellbonuses.CHA; }
inline virtual int32 GetHeroicMR() const { return 0; }
inline virtual int32 GetHeroicFR() const { return 0; }
inline virtual int32 GetHeroicDR() const { return 0; }
inline virtual int32 GetHeroicPR() const { return 0; }
inline virtual int32 GetHeroicCR() const { return 0; }
inline virtual int32 GetMR() const { return MR + itembonuses.MR + spellbonuses.MR; }
inline virtual int32 GetFR() const { return FR + itembonuses.FR + spellbonuses.FR; }
inline virtual int32 GetDR() const { return DR + itembonuses.DR + spellbonuses.DR; }
@@ -495,6 +516,13 @@ public:
inline StatBonuses* GetItemBonusesPtr() { return &itembonuses; }
inline StatBonuses* GetSpellBonusesPtr() { return &spellbonuses; }
inline StatBonuses* GetAABonusesPtr() { return &aabonuses; }
inline virtual int32 GetHeroicSTR() const { return 0; }
inline virtual int32 GetHeroicSTA() const { return 0; }
inline virtual int32 GetHeroicDEX() const { return 0; }
inline virtual int32 GetHeroicAGI() const { return 0; }
inline virtual int32 GetHeroicINT() const { return 0; }
inline virtual int32 GetHeroicWIS() const { return 0; }
inline virtual int32 GetHeroicCHA() const { return 0; }
inline virtual int32 GetMaxSTR() const { return GetSTR(); }
inline virtual int32 GetMaxSTA() const { return GetSTA(); }
inline virtual int32 GetMaxDEX() const { return GetDEX(); }
@@ -508,12 +536,13 @@ public:
inline virtual int32 GetMaxCR() const { return 255; }
inline virtual int32 GetMaxFR() const { return 255; }
inline virtual int32 GetDelayDeath() const { return 0; }
inline int32 GetHP() const { return cur_hp; }
inline int32 GetHP() const { return current_hp; }
inline int32 GetMaxHP() const { return max_hp; }
virtual int32 CalcMaxHP();
inline int32 GetMaxMana() const { return max_mana; }
inline int32 GetMana() const { return current_mana; }
virtual int32 GetEndurance() const { return 0; }
virtual int32 GetMaxEndurance() const { return 0; }
virtual void SetEndurance(int32 newEnd) { return; }
int32 GetItemHPBonuses();
int32 GetSpellHPBonuses();
@@ -524,21 +553,14 @@ public:
uint32 GetNPCTypeID() const { return npctype_id; }
void SetNPCTypeID(uint32 npctypeid) { npctype_id = npctypeid; }
inline const glm::vec4& GetPosition() const { return m_Position; }
inline void SetPosition(const float x, const float y, const float z) { m_Position.x = x; m_Position.y = y; m_Position.z = z; }
inline const float GetX() const { return m_Position.x; }
inline const float GetY() const { return m_Position.y; }
inline const float GetZ() const { return m_Position.z; }
inline const float GetHeading() const { return m_Position.w; }
inline const float GetSize() const { return size; }
inline const float GetBaseSize() const { return base_size; }
inline const float GetTarX() const { return m_TargetLocation.x; }
inline const float GetTarY() const { return m_TargetLocation.y; }
inline const float GetTarZ() const { return m_TargetLocation.z; }
inline const float GetTarVX() const { return m_TargetV.x; }
inline const float GetTarVY() const { return m_TargetV.y; }
inline const float GetTarVZ() const { return m_TargetV.z; }
inline const float GetTarVector() const { return tar_vector; }
inline const uint8 GetTarNDX() const { return tar_ndx; }
inline const int8 GetFlyMode() const { return static_cast<const int8>(flymode); }
inline const GravityBehavior GetFlyMode() const { return flymode; }
bool IsBoat() const;
//Group
@@ -551,14 +573,12 @@ public:
virtual inline int32 GetPrimaryFaction() const { return 0; }
//Movement
void Warp(const glm::vec3& location);
inline bool IsMoving() const { return moving; }
virtual void SetMoving(bool move) { moving = move; m_Delta = glm::vec4(); }
virtual void GoToBind(uint8 bindnum = 0) { }
virtual void Gate(uint8 bindnum = 0);
int GetWalkspeed() const { return(_GetWalkSpeed()); }
int GetRunspeed() const { return(_GetRunSpeed()); }
void SetCurrentSpeed(int in);
virtual int GetWalkspeed() const { return(_GetWalkSpeed()); }
virtual int GetRunspeed() const { return(_GetRunSpeed()); }
int GetBaseRunspeed() const { return base_runspeed; }
int GetBaseWalkspeed() const { return base_walkspeed; }
int GetBaseFearSpeed() const { return base_fearspeed; }
@@ -567,26 +587,24 @@ public:
void SetRunning(bool val) { m_is_running = val; }
virtual void GMMove(float x, float y, float z, float heading = 0.01, bool SendUpdate = true);
void SetDelta(const glm::vec4& delta);
void SetTargetDestSteps(uint8 target_steps) { tar_ndx = target_steps; }
void SendPositionUpdateToClient(Client *client);
void SendPositionUpdate(uint8 iSendToSelf = 0);
void MakeSpawnUpdateNoDelta(PlayerPositionUpdateServer_Struct* spu);
void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu);
void SendPosition();
void SentPositionPacket(float dx, float dy, float dz, float dh, int anim, bool send_to_self = false);
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 SetFlyMode(GravityBehavior flymode);
void Teleport(const glm::vec3 &pos);
void Teleport(const glm::vec4 &pos);
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; }
void SetHeading(float iHeading) { m_Position.w = iHeading; }
//AI
static uint32 GetLevelCon(uint8 mylevel, uint8 iOtherLevel);
@@ -613,8 +631,6 @@ public:
void SetAssistAggro(bool value) { AssistAggro = value; if (PrimaryAggro) AssistAggro = false; }
bool HateSummon();
void FaceTarget(Mob* mob_to_face = 0);
void SetHeading(float iHeading) { if(m_Position.w != iHeading) { pLastChange = Timer::GetCurrentTime();
m_Position.w = iHeading; } }
void WipeHateList();
void AddFeignMemory(Client* attacker);
void RemoveFromFeignMemory(Client* attacker);
@@ -625,8 +641,6 @@ public:
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; }
inline bool CheckLastLosState() const { return last_los_check; }
@@ -816,8 +830,10 @@ public:
void SendAppearancePacket(uint32 type, uint32 value, bool WholeZone = true, bool iIgnoreSelf = false, Client *specific_target=nullptr);
void SetAppearance(EmuAppearance app, bool iIgnoreSelf = true);
inline EmuAppearance GetAppearance() const { return _appearance; }
inline const int GetAnimation() const { return animation; }
inline void SetAnimation(int a) { animation = a; }
inline const uint8 GetRunAnimSpeed() const { return pRunAnimSpeed; }
inline void SetRunAnimSpeed(int8 in) { if (pRunAnimSpeed != in) { pRunAnimSpeed = in; pLastChange = Timer::GetCurrentTime(); } }
inline void SetRunAnimSpeed(int8 in) { pRunAnimSpeed = in; }
bool IsDestructibleObject() { return destructibleobject; }
void SetDestructibleObject(bool in) { destructibleobject = in; }
@@ -983,7 +999,13 @@ public:
inline bool CheckAggro(Mob* other) {return hate_list.IsEntOnHateList(other);}
float CalculateHeadingToTarget(float in_x, float in_y) { return HeadingAngleToMob(in_x, in_y); }
virtual bool CalculateNewPosition(float x, float y, float z, float speed, bool check_z = true, bool calculate_heading = true);
virtual void WalkTo(float x, float y, float z);
virtual void RunTo(float x, float y, float z);
void NavigateTo(float x, float y, float z);
void RotateTo(float new_heading);
void RotateToWalking(float new_heading);
void RotateToRunning(float new_heading);
void StopNavigation();
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);
@@ -991,8 +1013,9 @@ public:
float GetZOffset() const;
float GetDefaultRaceSize() const;
void FixZ(int32 z_find_offset = 5, bool fix_client_z = false);
float GetFixedZ(glm::vec3 destination, int32 z_find_offset = 5);
float GetFixedZ(const glm::vec3 &destination, int32 z_find_offset = 5);
virtual int GetStuckBehavior() const { return 0; }
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; }
@@ -1049,6 +1072,7 @@ public:
inline bool Sanctuary() const { return spellbonuses.Sanctuary; }
bool HasNPCSpecialAtk(const char* parse);
bool HasSpecialAbilities();
int GetSpecialAbility(int ability);
int GetSpecialAbilityParam(int ability, int param);
void SetSpecialAbility(int ability, int level);
@@ -1070,6 +1094,7 @@ public:
void SetCurrentWP(uint16 waypoint) { cur_wp = waypoint; }
virtual FACTION_VALUE GetReverseFactionCon(Mob* iOther) { return FACTION_INDIFFERENT; }
virtual const bool IsUnderwaterOnly() const { return false; }
inline bool IsTrackable() const { return(trackable); }
Timer* GetAIThinkTimer() { return AI_think_timer.get(); }
Timer* GetAIMovementTimer() { return AI_movement_timer.get(); }
@@ -1146,6 +1171,10 @@ public:
int GetWeaponDamage(Mob *against, const EQEmu::ItemData *weapon_item);
int GetWeaponDamage(Mob *against, const EQEmu::ItemInstance *weapon_item, uint32 *hate = nullptr);
int32 GetHPRegen() const;
int32 GetManaRegen() const;
// Bots HealRotation methods
#ifdef BOTS
bool IsHealRotationTarget() { return (m_target_of_heal_rotation.use_count() && m_target_of_heal_rotation.get()); }
@@ -1167,7 +1196,6 @@ protected:
int _GetWalkSpeed() const;
int _GetRunSpeed() const;
int _GetFearSpeed() const;
virtual bool MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool check_z = true, bool calculate_heading = true);
virtual bool AI_EngagedCastCheck() { return(false); }
virtual bool AI_PursueCastCheck() { return(false); }
@@ -1218,7 +1246,7 @@ protected:
int targeted;
bool findable;
bool trackable;
int32 cur_hp;
int32 current_hp;
int32 max_hp;
int32 base_hp;
int32 current_mana;
@@ -1261,10 +1289,8 @@ protected:
uint8 level;
uint8 orig_level;
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 m_Position;
int animation; // this is really what MQ2 calls SpeedRun just packed like (int)(SpeedRun * 40.0f)
float base_size;
float size;
@@ -1277,7 +1303,6 @@ protected:
int current_speed;
eSpecialAttacks m_specialattacks;
uint32 pLastChange;
bool held;
bool gheld;
bool nocast;
@@ -1307,8 +1332,6 @@ protected:
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);
virtual float GetSympatheticProcChances(uint16 spell_id, int16 ProcRateMod, int32 ItemProcRate = 0);
int16 GetSympatheticSpellProcRate(uint16 spell_id);
@@ -1353,7 +1376,7 @@ protected:
int attacked_count;
bool delaytimer;
uint16 casting_spell_targetid;
EQEmu::CastingSlot casting_spell_slot;
EQEmu::spells::CastingSlot casting_spell_slot;
uint16 casting_spell_mana;
uint32 casting_spell_inventory_slot;
uint32 casting_spell_timer;
@@ -1363,7 +1386,7 @@ protected:
uint32 casting_spell_aa_id;
bool casting_spell_checks;
uint16 bardsong;
EQEmu::CastingSlot bardsong_slot;
EQEmu::spells::CastingSlot bardsong_slot;
uint32 bardsong_target_id;
bool ActiveProjectileATK;
@@ -1445,7 +1468,6 @@ protected:
std::unique_ptr<Timer> AI_think_timer;
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;
@@ -1469,14 +1491,11 @@ protected:
bool flee_mode;
Timer flee_timer;
Timer fix_z_timer;
Timer fix_z_timer_engaged;
Timer attack_anim_timer;
Timer position_update_melee_push_timer;
bool pAIControlled;
bool roamer;
bool logging_enabled;
int wandertype;
int pausetype;
@@ -1497,15 +1516,8 @@ protected:
glm::vec3 m_FearWalkTarget;
bool currently_fleeing;
// Pathing
//
glm::vec3 PathingDestination;
IPathfinder::IPath Route;
std::unique_ptr<Timer> PathRecalcTimer;
bool pause_timer_complete;
bool DistractedFromGrid;
glm::vec3 PathingLastPosition;
int PathingLoopCount;
uint32 pDontHealMeBefore;
uint32 pDontBuffMeBefore;
uint32 pDontDotMeBefore;
@@ -1524,18 +1536,11 @@ protected:
bool pet_owner_client; //Flags regular and pets as belonging to a client
uint32 pet_targetlock_id;
EGNode *_egnode; //the EG node we are in
glm::vec3 m_TargetLocation;
uint8 tar_ndx;
float tar_vector;
glm::vec3 m_TargetV;
float test_vector;
glm::vec3 m_TargetRing;
// we might want to do this differently, we gotta do max NPC buffs ... which is 97
uint32 m_spellHitsLeft[EQEmu::constants::TotalBuffs]; // Used to track which spells will have their numhits incremented when spell finishes casting
int flymode;
uint32 m_spellHitsLeft[EQEmu::spells::TOTAL_BUFFS]; // Used to track which spells will have their numhits incremented when spell finishes casting
GravityBehavior flymode;
bool m_targetable;
int QGVarDuration(const char *fmt);
void InsertQuestGlobal(int charid, int npcid, int zoneid, const char *name, const char *value, int expdate);
@@ -1553,6 +1558,8 @@ protected:
AuraMgr aura_mgr;
AuraMgr trap_mgr;
MobMovementManager *mMovementManager;
private:
void _StopSong(); //this is not what you think it is
Mob* target;
+147 -355
View File
@@ -375,16 +375,11 @@ bool NPC::AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgain
#endif
casting_spell_AIindex = i;
//stop moving if were casting a spell and were not a bard...
if(!IsBardSong(AIspells[i].spellid)) {
SetCurrentSpeed(0);
}
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
return CastSpell(AIspells[i].spellid, tar->GetID(), EQEmu::spells::CastingSlot::Gem2, AIspells[i].manacost == -2 ? 0 : -1, mana_cost, oDontDoAgainBefore, -1, -1, 0, &(AIspells[i].resist_adjust));
}
bool EntityList::AICheckCloseBeneficialSpells(NPC* caster, uint8 iChance, float iRange, uint32 iSpellTypes) {
if((iSpellTypes & SpellTypes_Detrimental) != 0) {
if((iSpellTypes & SPELL_TYPES_DETRIMENTAL) != 0) {
//according to live, you can buff and heal through walls...
//now with PCs, this only applies if you can TARGET the target, but
// according to Rogean, Live NPCs will just cast through walls/floors, no problem..
@@ -520,7 +515,6 @@ void Mob::AI_Start(uint32 iMoveDelay) {
m_Delta = glm::vec4();
pRunAnimSpeed = 0;
pLastChange = Timer::GetCurrentTime();
}
void Client::AI_Start(uint32 iMoveDelay) {
@@ -556,8 +550,7 @@ void NPC::AI_Start(uint32 iMoveDelay) {
}
SendTo(GetX(), GetY(), GetZ());
SetChanged();
SaveGuardSpot();
SaveGuardSpot(GetPosition());
}
void Mob::AI_Stop() {
@@ -695,11 +688,11 @@ void Client::AI_SpellCast()
}
uint32 spell_to_cast = 0xFFFFFFFF;
EQEmu::CastingSlot slot_to_use = EQEmu::CastingSlot::Item;
EQEmu::spells::CastingSlot slot_to_use = EQEmu::spells::CastingSlot::Item;
if(valid_spells.size() == 1)
{
spell_to_cast = valid_spells[0];
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[0]);
slot_to_use = static_cast<EQEmu::spells::CastingSlot>(slots[0]);
}
else if(valid_spells.empty())
{
@@ -709,7 +702,7 @@ void Client::AI_SpellCast()
{
uint32 idx = zone->random.Int(0, (valid_spells.size()-1));
spell_to_cast = valid_spells[idx];
slot_to_use = static_cast<EQEmu::CastingSlot>(slots[idx]);
slot_to_use = static_cast<EQEmu::spells::CastingSlot>(slots[idx]);
}
if(IsMezSpell(spell_to_cast) || IsFearSpell(spell_to_cast))
@@ -722,7 +715,7 @@ void Client::AI_SpellCast()
{
if(!IsBardSong(spell_to_cast))
{
SetCurrentSpeed(0);
StopNavigation();
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@@ -736,7 +729,7 @@ void Client::AI_SpellCast()
{
if(!IsBardSong(spell_to_cast))
{
SetCurrentSpeed(0);
StopNavigation();
}
CastSpell(spell_to_cast, tar->GetID(), slot_to_use);
return;
@@ -788,50 +781,23 @@ void Client::AI_Process()
if (RuleB(Combat, EnableFearPathing)) {
if (currently_fleeing) {
if (fix_z_timer.Check())
this->FixZ(5, true);
if (IsRooted()) {
//make sure everybody knows were not moving, for appearance sake
if (IsMoving()) {
if (GetTarget())
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetCurrentSpeed(0);
FaceTarget();
StopNavigation();
}
//continue on to attack code, ensuring that we execute the engaged code
engaged = true;
}
else {
if (AI_movement_timer->Check()) {
int speed = GetFearSpeed();
animation = speed;
speed *= 2;
SetCurrentSpeed(speed);
// Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
// Calculate a new point to run to
if(IsPositionEqualWithinCertainZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget, 5.0f)) {
CalculateNewFearpoint();
}
if (!RuleB(Pathing, Fear) || !zone->pathing)
CalculateNewPosition(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z, speed, true);
else {
bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
speed,
waypoint_changed,
node_reached
);
if (waypoint_changed)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed);
RunTo(m_FearWalkTarget.x, m_FearWalkTarget.y, m_FearWalkTarget.z);
}
}
return;
@@ -866,6 +832,10 @@ void Client::AI_Process()
bool is_combat_range = CombatRange(GetTarget());
if (is_combat_range) {
if (IsMoving()) {
StopNavigation();
}
if (charm_class_attacks_timer.Check()) {
DoClassAttacks(GetTarget());
}
@@ -873,10 +843,8 @@ void Client::AI_Process()
if (AI_movement_timer->Check()) {
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) !=
m_Position.w) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
FaceTarget();
}
SetCurrentSpeed(0);
}
if (GetTarget() && !IsStunned() && !IsMezzed() && !GetFeigned()) {
if (attack_timer.Check()) {
@@ -898,29 +866,12 @@ void Client::AI_Process()
{
if(AI_movement_timer->Check())
{
int newspeed = GetRunspeed();
animation = newspeed;
newspeed *= 2;
SetCurrentSpeed(newspeed);
if(!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(), newspeed);
else
{
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached);
if(WaypointChanged)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, newspeed);
}
RunTo(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ());
}
}
else if(IsMoving())
{
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SetCurrentSpeed(0);
FaceTarget();
}
}
AI_SpellCast();
@@ -953,22 +904,18 @@ void Client::AI_Process()
float dist = DistanceSquared(m_Position, owner->GetPosition());
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
Teleport(owner->GetPosition());
} 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);
if (dist >= 1225) {
RunTo(owner->GetX(), owner->GetY(), owner->GetZ());
}
else {
WalkTo(owner->GetX(), owner->GetY(), owner->GetZ());
}
}
} else {
if (moved) {
SetCurrentSpeed(0);
moved = false;
}
StopNavigation();
}
}
}
@@ -982,49 +929,49 @@ void Mob::ProcessForcedMovement()
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;
@@ -1038,13 +985,12 @@ void Mob::ProcessForcedMovement()
}
}
}
if (bPassed) {
ForcedMovement = 0;
Teleport(m_Position + m_Delta);
m_Delta = glm::vec4();
SendPositionUpdate();
pLastChange = Timer::GetCurrentTime();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0, true);
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
@@ -1119,9 +1065,8 @@ void Mob::AI_Process() {
!IsPetRegroup()) {
//make sure everybody knows were not moving, for appearance sake
if (IsMoving()) {
if (target)
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetCurrentSpeed(0);
FaceTarget();
StopNavigation();
moved = false;
}
//continue on to attack code, ensuring that we execute the engaged code
@@ -1130,37 +1075,16 @@ void Mob::AI_Process() {
else {
if (AI_movement_timer->Check()) {
// Check if we have reached the last fear point
if ((std::abs(GetX() - m_FearWalkTarget.x) < 0.1) &&
(std::abs(GetY() - m_FearWalkTarget.y) < 0.1)) {
if (DistanceNoZ(glm::vec3(GetX(), GetY(), GetZ()), m_FearWalkTarget) <= 5.0f) {
// Calculate a new point to run to
StopNavigation();
CalculateNewFearpoint();
}
if (!RuleB(Pathing, Fear) || !zone->pathing) {
CalculateNewPosition(
m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
GetFearSpeed(),
true
);
}
else {
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(
m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z,
GetFearSpeed(),
WaypointChanged,
NodeReached
);
if (WaypointChanged)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetFearSpeed());
}
RunTo(
m_FearWalkTarget.x,
m_FearWalkTarget.y,
m_FearWalkTarget.z
);
}
return;
}
@@ -1173,26 +1097,6 @@ void Mob::AI_Process() {
}
if (engaged) {
/* Fix Z when following during pull, not when engaged and stationary */
if (moving && fix_z_timer_engaged.Check()) {
if (this->GetTarget()) {
/* If we are engaged, moving and following client, let's look for best Z more often */
float target_distance = DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition());
this->FixZ();
if (target_distance <= 15 && !this->CheckLosFN(this->GetTarget())) {
Mob *target = this->GetTarget();
m_Position.x = target->GetX();
m_Position.y = target->GetY();
m_Position.z = target->GetZ();
m_Position.w = target->GetHeading();
SendPosition();
}
}
}
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive);
@@ -1275,21 +1179,12 @@ void Mob::AI_Process() {
bool is_combat_range = CombatRange(target);
if (is_combat_range) {
if (AI_movement_timer->Check()) {
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
}
if (IsMoving()) {
if (CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()) != m_Position.w) {
SetHeading(CalculateHeadingToTarget(GetTarget()->GetX(), GetTarget()->GetY()));
SendPosition();
}
SetCurrentSpeed(0);
StopNavigation();
}
FaceTarget();
//casting checked above...
if (target && !IsStunned() && !IsMezzed() && GetAppearance() != eaDead && !IsMeleeDisabled()) {
@@ -1444,26 +1339,6 @@ void Mob::AI_Process() {
} //end is within combat rangepet
else {
//we cannot reach our target...
//underwater stuff only works with water maps in the zone!
if (IsNPC() && CastToNPC()->IsUnderwaterOnly() && zone->HasWaterMap()) {
auto targetPosition = glm::vec3(target->GetX(), target->GetY(), target->GetZ());
if (!zone->watermap->InLiquid(targetPosition)) {
Mob *tar = hate_list.GetEntWithMostHateOnList(this);
if (tar == target) {
WipeHateList();
Heal();
BuffFadeAll();
AI_walking_timer->Start(100);
time_until_can_move = Timer::GetCurrentTime();
return;
}
else if (tar != nullptr) {
SetTarget(tar);
return;
}
}
}
// See if we can summon the mob to us
if (!HateSummon()) {
@@ -1474,32 +1349,19 @@ void Mob::AI_Process() {
// Now pursue
// TODO: Check here for another person on hate list with close hate value
if (AI_PursueCastCheck()) {
//we did something, so do not process movement.
if (IsCasting() && GetClass() != BARD) {
StopNavigation();
FaceTarget();
}
}
else if (AI_movement_timer->Check() && target) {
if (!IsRooted()) {
Log(Logs::Detail, Logs::AI, "Pursuing %s while engaged.", target->GetName());
if (!RuleB(Pathing, Aggro) || !zone->pathing)
CalculateNewPosition(target->GetX(), target->GetY(), target->GetZ(), GetRunspeed());
else {
bool WaypointChanged, NodeReached;
glm::vec3 Goal = UpdatePath(
target->GetX(), target->GetY(), target->GetZ(),
GetRunspeed(), WaypointChanged, NodeReached
);
if (WaypointChanged)
tar_ndx = 20;
CalculateNewPosition(Goal.x, Goal.y, Goal.z, GetRunspeed());
}
RunTo(target->GetX(), target->GetY(), target->GetZ());
}
else if (IsMoving()) {
SetHeading(CalculateHeadingToTarget(target->GetX(), target->GetY()));
SetCurrentSpeed(0);
else {
FaceTarget();
}
}
}
@@ -1536,7 +1398,9 @@ void Mob::AI_Process() {
}
}
if (AI_IdleCastCheck()) {
//we processed a spell action, so do nothing else.
if (IsCasting() && GetClass() != BARD) {
StopNavigation();
}
}
else if (zone->CanDoCombat() && CastToNPC()->WillAggroNPCs() && AI_scan_area_timer->Check()) {
@@ -1572,44 +1436,38 @@ void Mob::AI_Process() {
if (distance_to_owner >= 400 || z_distance > 100) {
int pet_speed = GetWalkspeed();
bool running = false;
/**
* Distance: >= 35 (Run if far away)
*/
if (distance_to_owner >= 1225) {
pet_speed = GetRunspeed();
running = true;
}
/**
* Distance: >= 450 (Snap to owner)
*/
if (distance_to_owner >= 202500 || z_distance > 100) {
m_Position = pet_owner_position;
SendPositionUpdate();
moved = true;
if (running) {
RunTo(pet_owner_position.x, pet_owner_position.y, pet_owner_position.z);
}
else {
WalkTo(pet_owner_position.x, pet_owner_position.y, pet_owner_position.z);
}
}
else {
bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
owner->GetX(),
owner->GetY(),
owner->GetZ(),
pet_speed,
waypoint_changed,
node_reached
);
CalculateNewPosition(Goal.x, Goal.y, Goal.z, pet_speed, true);
if (running) {
RunTo(pet_owner_position.x, pet_owner_position.y, pet_owner_position.z);
}
else {
WalkTo(pet_owner_position.x, pet_owner_position.y, pet_owner_position.z);
}
}
}
else {
if (moved) {
this->FixZ();
SetCurrentSpeed(0);
moved = false;
}
StopNavigation();
}
break;
@@ -1647,29 +1505,24 @@ void Mob::AI_Process() {
* Default follow distance is 100
*/
if (distance >= follow_distance) {
int speed = GetWalkspeed();
bool running = false;
// maybe we want the NPC to only walk doing follow logic
if (GetFollowCanRun() && distance >= follow_distance + 150) {
speed = GetRunspeed();
running = true;
}
bool waypoint_changed, node_reached;
auto &Goal = follow->GetPosition();
glm::vec3 Goal = UpdatePath(
follow->GetX(),
follow->GetY(),
follow->GetZ(),
speed,
waypoint_changed,
node_reached
);
CalculateNewPosition(Goal.x, Goal.y, Goal.z, speed, true);
if (running) {
RunTo(Goal.x, Goal.y, Goal.z);
}
else {
WalkTo(Goal.x, Goal.y, Goal.z);
}
}
else {
moved = false;
SetCurrentSpeed(0);
StopNavigation();
}
}
}
@@ -1721,7 +1574,18 @@ void NPC::AI_DoMovement() {
*/
if (roambox_distance > 0) {
if (!IsMoving()) {
// Check if we're already moving to a WP
// If so, if we're not moving we have arrived and need to set delay
if (GetCWP() == EQEmu::WaypointStatus::RoamBoxPauseInProgress && !IsMoving()) {
// We have arrived
time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetCurrentWP(0);
return;
}
// Set a new destination
if (!IsMoving() && time_until_can_move < Timer::GetCurrentTime()) {
auto move_x = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
auto move_y = static_cast<float>(zone->random.Real(-roambox_distance, roambox_distance));
@@ -1776,7 +1640,7 @@ void NPC::AI_DoMovement() {
destination.x = roambox_destination_x;
destination.y = roambox_destination_y;
destination.z = m_Position.z;
roambox_destination_z = GetFixedZ(destination) + this->GetZOffset();
roambox_destination_z = zone->zonemap ? zone->zonemap->FindClosestZ(destination, nullptr) + this->GetZOffset() : 0;
Log(Logs::Detail,
Logs::NPCRoamBox,
@@ -1789,47 +1653,29 @@ void NPC::AI_DoMovement() {
roambox_min_y,
roambox_max_y,
roambox_destination_y);
}
Log(Logs::Detail, Logs::NPCRoamBox, "Dest Z is (%f)", roambox_destination_z);
/**
bool waypoint_changed, node_reached;
glm::vec3 Goal = UpdatePath(
roambox_destination_x,
roambox_destination_y,
roambox_destination_z,
move_speed,
waypoint_changed,
node_reached
);
**/
CalculateNewPosition(roambox_destination_x, roambox_destination_y, roambox_destination_z, move_speed, true);
if (m_Position.x == roambox_destination_x && m_Position.y == roambox_destination_y) {
time_until_can_move = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetMoving(false);
this->FixZ();
SendPosition();
SetCurrentWP(EQEmu::WaypointStatus::RoamBoxPauseInProgress);
NavigateTo(roambox_destination_x, roambox_destination_y, roambox_destination_z);
}
return;
}
else if (roamer) {
if (AI_walking_timer->Check()) {
movetimercompleted = true;
pause_timer_complete = true;
AI_walking_timer->Disable();
}
int32 gridno = CastToNPC()->GetGrid();
if (gridno > 0 || cur_wp == -2) {
if (movetimercompleted == true) { // time to pause at wp is over
if (gridno > 0 || cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
if (pause_timer_complete == true) { // time to pause at wp is over
AI_SetupNextWaypoint();
} // endif (movetimercompleted==true)
} // endif (pause_timer_complete==true)
else if (!(AI_walking_timer->Enabled())) { // currently moving
bool doMove = true;
if (m_CurrentWayPoint.x == GetX() && m_CurrentWayPoint.y == GetY()) { // are we there yet? then stop
if(IsPositionEqual(glm::vec2(m_CurrentWayPoint.x, m_CurrentWayPoint.y), glm::vec2(GetX(), GetY()))) {
Log(Logs::Detail,
Logs::AI,
"We have reached waypoint %d (%.3f,%.3f,%.3f) on grid %d",
@@ -1838,17 +1684,13 @@ void NPC::AI_DoMovement() {
GetY(),
GetZ(),
GetGrid());
SetWaypointPause();
SetAppearance(eaStanding, false);
SetMoving(false);
if (m_CurrentWayPoint.w >= 0.0) {
SetHeading(m_CurrentWayPoint.w);
if (cur_wp_pause > 0) {
RotateTo(m_CurrentWayPoint.w);
}
this->FixZ();
SendPosition();
//kick off event_waypoint arrive
char temp[16];
sprintf(temp, "%d", cur_wp);
@@ -1859,94 +1701,49 @@ void NPC::AI_DoMovement() {
// 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) {
if (cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
AI_SetupNextWaypoint();
}
// wipe feign memory since we reached our first waypoint
if (cur_wp == 1)
ClearFeignMemory();
}
if (doMove) { // not at waypoint yet or at 0 pause WP, so keep moving
if (!RuleB(Pathing, AggroReturnToGrid) || !zone->pathing || (DistractedFromGrid == 0))
CalculateNewPosition(
m_CurrentWayPoint.x,
m_CurrentWayPoint.y,
m_CurrentWayPoint.z,
move_speed,
true
);
else {
bool WaypointChanged;
bool NodeReached;
glm::vec3 Goal = UpdatePath(
m_CurrentWayPoint.x,
m_CurrentWayPoint.y,
m_CurrentWayPoint.z,
move_speed,
WaypointChanged,
NodeReached
);
if (WaypointChanged)
tar_ndx = 20;
if (NodeReached)
entity_list.OpenDoorsNear(CastToNPC());
CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed, true);
if (cur_wp_pause == 0) {
pause_timer_complete = true;
AI_SetupNextWaypoint();
doMove = true;
}
}
if (doMove) { // not at waypoint yet or at 0 pause WP, so keep moving
NavigateTo(
m_CurrentWayPoint.x,
m_CurrentWayPoint.y,
m_CurrentWayPoint.z
);
}
}
} // endif (gridno > 0)
// handle new quest grid command processing
else if (gridno < 0) { // this mob is under quest control
if (movetimercompleted == true) { // time to pause has ended
if (pause_timer_complete == true) { // time to pause has ended
SetGrid(0 - GetGrid()); // revert to AI control
Log(Logs::Detail, Logs::Pathing, "Quest pathing is finished. Resuming on grid %d", GetGrid());
SetAppearance(eaStanding, false);
CalculateNewWaypoint();
}
}
}
else if (IsGuarding()) {
bool CP2Moved;
if (!RuleB(Pathing, Guard) || !zone->pathing) {
CP2Moved = CalculateNewPosition(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, move_speed);
}
else {
if (!((m_Position.x == m_GuardPoint.x) && (m_Position.y == m_GuardPoint.y) &&
(m_Position.z == m_GuardPoint.z))) {
else if (IsGuarding()) {
bool at_gp = IsPositionEqualWithinCertainZ(m_Position, m_GuardPoint, 15.0f);
bool WaypointChanged, NodeReached;
if (at_gp) {
glm::vec3 Goal = UpdatePath(
m_GuardPoint.x,
m_GuardPoint.y,
m_GuardPoint.z,
move_speed,
WaypointChanged,
NodeReached
);
if (WaypointChanged) {
tar_ndx = 20;
}
if (NodeReached) {
entity_list.OpenDoorsNear(CastToNPC());
}
CP2Moved = CalculateNewPosition(Goal.x, Goal.y, Goal.z, move_speed);
}
else {
CP2Moved = false;
}
}
if (!CP2Moved) {
if (moved) {
Log(Logs::Detail,
Logs::AI,
@@ -1958,15 +1755,17 @@ void NPC::AI_DoMovement() {
ClearFeignMemory();
moved = false;
if (GetTarget() == nullptr || DistanceSquared(m_Position, GetTarget()->GetPosition()) >= 5 * 5) {
SetHeading(m_GuardPoint.w);
RotateTo(m_GuardPoint.w);
}
else {
FaceTarget(GetTarget());
}
SetCurrentSpeed(0);
SetAppearance(GetGuardPointAnim());
}
}
else {
NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z);
}
}
}
@@ -1998,27 +1797,26 @@ void NPC::AI_SetupNextWaypoint() {
found_spawn->SetNPCPointerNull();
}
else {
movetimercompleted = false;
pause_timer_complete = false;
Log(Logs::Detail, Logs::Pathing, "We are departing waypoint %d.", cur_wp);
//if we were under quest control (with no grid), we are done now..
if (cur_wp == -2) {
if (cur_wp == EQEmu::WaypointStatus::QuestControlNoGrid) {
Log(Logs::Detail, Logs::Pathing, "Non-grid quest mob has reached its quest ordered waypoint. Leaving pathing mode.");
roamer = false;
cur_wp = 0;
}
SetAppearance(eaStanding, false);
entity_list.OpenDoorsNear(CastToNPC());
entity_list.OpenDoorsNear(this);
if (!DistractedFromGrid) {
//kick off event_waypoint depart
char temp[16];
sprintf(temp, "%d", cur_wp);
parse->EventNPC(EVENT_WAYPOINT_DEPART, CastToNPC(), nullptr, temp, 0);
//setup our next waypoint, if we are still on our normal grid
//remember that the quest event above could have done anything it wanted with our grid
if (GetGrid() > 0) {
@@ -2083,20 +1881,14 @@ void Mob::AI_Event_Engaged(Mob* attacker, bool iYellForHelp) {
void Mob::AI_Event_NoLongerEngaged() {
if (!IsAIControlled())
return;
this->AI_walking_timer->Start(RandomTimer(3000,20000));
AI_walking_timer->Start(RandomTimer(3000,20000));
time_until_can_move = Timer::GetCurrentTime();
if (minLastFightingDelayMoving == maxLastFightingDelayMoving)
time_until_can_move += minLastFightingDelayMoving;
else
time_until_can_move += zone->random.Int(minLastFightingDelayMoving, maxLastFightingDelayMoving);
// So mobs don't keep running as a ghost until AIwalking_timer fires
// if they were moving prior to losing all hate
// except if we're a pet, then we might run into some issues with pets backing off when they should immediately be moving
if(!IsPet())
{
SetRunAnimSpeed(0);
SendPosition();
}
StopNavigation();
ClearRampage();
if(IsNPC())
@@ -3030,7 +2822,7 @@ DBnpcspells_Struct *ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID)
entry.max_hp = atoi(row[8]);
// some spell types don't make much since to be priority 0, so fix that
if (!(entry.type & SpellTypes_Innate) && entry.priority == 0)
if (!(entry.type & SPELL_TYPES_INNATE) && entry.priority == 0)
entry.priority = 1;
if (row[9])
+548
View File
@@ -0,0 +1,548 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "../common/eqemu_logsys.h"
#include "../common/misc_functions.h"
#include "../common/spdat.h"
#include "../common/string_util.h"
#include "mob.h"
#include "quest_parser_collection.h"
#include "zonedb.h"
#ifdef BOTS
#include "bot.h"
#endif
/**
* Stores internal representation of mob texture by material slot
*
* @param material_slot
* @param texture
* @param color
* @param hero_forge_model
*/
void Mob::SetMobTextureProfile(uint8 material_slot, uint16 texture, uint32 color, uint32 hero_forge_model)
{
Log(Logs::Detail, Logs::MobAppearance,
"Mob::SetMobTextureProfile [%s] material_slot: %u texture: %u color: %u hero_forge_model: %u",
this->GetCleanName(),
material_slot,
texture,
color,
hero_forge_model
);
switch (material_slot) {
case EQEmu::textures::armorHead:
mob_texture_profile.Head.Material = texture;
mob_texture_profile.Head.HerosForgeModel = hero_forge_model;
mob_texture_profile.Head.Color = color;
break;
case EQEmu::textures::armorChest:
mob_texture_profile.Chest.Material = texture;
mob_texture_profile.Chest.HerosForgeModel = hero_forge_model;
mob_texture_profile.Chest.Color = color;
break;
case EQEmu::textures::armorArms:
mob_texture_profile.Arms.Material = texture;
mob_texture_profile.Arms.HerosForgeModel = hero_forge_model;
mob_texture_profile.Arms.Color = color;
break;
case EQEmu::textures::armorWrist:
mob_texture_profile.Wrist.Material = texture;
mob_texture_profile.Wrist.HerosForgeModel = hero_forge_model;
mob_texture_profile.Wrist.Color = color;
break;
case EQEmu::textures::armorHands:
mob_texture_profile.Hands.Material = texture;
mob_texture_profile.Hands.HerosForgeModel = hero_forge_model;
mob_texture_profile.Hands.Color = color;
break;
case EQEmu::textures::armorLegs:
mob_texture_profile.Legs.Material = texture;
mob_texture_profile.Legs.HerosForgeModel = hero_forge_model;
mob_texture_profile.Legs.Color = color;
break;
case EQEmu::textures::armorFeet:
mob_texture_profile.Feet.Material = texture;
mob_texture_profile.Feet.HerosForgeModel = hero_forge_model;
mob_texture_profile.Feet.Color = color;
break;
case EQEmu::textures::weaponPrimary:
mob_texture_profile.Primary.Material = texture;
mob_texture_profile.Primary.HerosForgeModel = hero_forge_model;
mob_texture_profile.Primary.Color = color;
break;
case EQEmu::textures::weaponSecondary:
mob_texture_profile.Secondary.Material = texture;
mob_texture_profile.Secondary.HerosForgeModel = hero_forge_model;
mob_texture_profile.Secondary.Color = color;
break;
default:
return;
}
}
/**
* Returns internal representation of mob texture by material
*
* @param material_slot
* @return
*/
int32 Mob::GetTextureProfileMaterial(uint8 material_slot) const
{
switch (material_slot) {
case EQEmu::textures::armorHead:
return mob_texture_profile.Head.Material;
case EQEmu::textures::armorChest:
return mob_texture_profile.Chest.Material;
case EQEmu::textures::armorArms:
return mob_texture_profile.Arms.Material;
case EQEmu::textures::armorWrist:
return mob_texture_profile.Wrist.Material;
case EQEmu::textures::armorHands:
return mob_texture_profile.Hands.Material;
case EQEmu::textures::armorLegs:
return mob_texture_profile.Legs.Material;
case EQEmu::textures::armorFeet:
return mob_texture_profile.Feet.Material;
case EQEmu::textures::weaponPrimary:
return mob_texture_profile.Primary.Material;
case EQEmu::textures::weaponSecondary:
return mob_texture_profile.Secondary.Material;
default:
return 0;
}
}
/**
* Returns internal representation of mob texture by color
*
* @param material_slot
* @return
*/
int32 Mob::GetTextureProfileColor(uint8 material_slot) const
{
switch (material_slot) {
case EQEmu::textures::armorHead:
return mob_texture_profile.Head.Color;
case EQEmu::textures::armorChest:
return mob_texture_profile.Chest.Color;
case EQEmu::textures::armorArms:
return mob_texture_profile.Arms.Color;
case EQEmu::textures::armorWrist:
return mob_texture_profile.Wrist.Color;
case EQEmu::textures::armorHands:
return mob_texture_profile.Hands.Color;
case EQEmu::textures::armorLegs:
return mob_texture_profile.Legs.Color;
case EQEmu::textures::armorFeet:
return mob_texture_profile.Feet.Color;
case EQEmu::textures::weaponPrimary:
return mob_texture_profile.Primary.Color;
case EQEmu::textures::weaponSecondary:
return mob_texture_profile.Secondary.Color;
default:
return 0;
}
}
/**
* Returns internal representation of mob texture by HerosForgeModel
*
* @param material_slot
* @return
*/
int32 Mob::GetTextureProfileHeroForgeModel(uint8 material_slot) const
{
switch (material_slot) {
case EQEmu::textures::armorHead:
return mob_texture_profile.Head.HerosForgeModel;
case EQEmu::textures::armorChest:
return mob_texture_profile.Chest.HerosForgeModel;
case EQEmu::textures::armorArms:
return mob_texture_profile.Arms.HerosForgeModel;
case EQEmu::textures::armorWrist:
return mob_texture_profile.Wrist.HerosForgeModel;
case EQEmu::textures::armorHands:
return mob_texture_profile.Hands.HerosForgeModel;
case EQEmu::textures::armorLegs:
return mob_texture_profile.Legs.HerosForgeModel;
case EQEmu::textures::armorFeet:
return mob_texture_profile.Feet.HerosForgeModel;
case EQEmu::textures::weaponPrimary:
return mob_texture_profile.Primary.HerosForgeModel;
case EQEmu::textures::weaponSecondary:
return mob_texture_profile.Secondary.HerosForgeModel;
default:
return 0;
}
}
/**
* Gets the material or texture for a slot (leather / plate etc.)
*
* @param material_slot
* @return
*/
int32 Mob::GetEquipmentMaterial(uint8 material_slot) const
{
uint32 equipment_material = 0;
int32 ornamentation_augment_type = RuleI(Character, OrnamentationAugmentType);
int32 texture_profile_material = GetTextureProfileMaterial(material_slot);
Log(Logs::Detail, Logs::MobAppearance,
"Mob::GetEquipmentMaterial [%s] material_slot: %u texture_profile_material: %i",
this->clean_name,
material_slot,
texture_profile_material
);
if (texture_profile_material > 0) {
return texture_profile_material;
}
auto item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
if (item != nullptr) {
/**
* Handle primary / secondary texture
*/
bool is_primary_or_secondary_weapon =
material_slot == EQEmu::textures::weaponPrimary ||
material_slot == EQEmu::textures::weaponSecondary;
if (is_primary_or_secondary_weapon) {
if (this->IsClient()) {
int16 inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (inventory_slot == INVALID_INDEX) {
return 0;
}
const EQEmu::ItemInstance *item_instance = CastToClient()->m_inv[inventory_slot];
if (item_instance) {
if (item_instance->GetOrnamentationAug(ornamentation_augment_type)) {
item = item_instance->GetOrnamentationAug(ornamentation_augment_type)->GetItem();
if (item && strlen(item->IDFile) > 2) {
equipment_material = atoi(&item->IDFile[2]);
}
}
else if (item_instance->GetOrnamentationIDFile()) {
equipment_material = item_instance->GetOrnamentationIDFile();
}
}
}
if (equipment_material == 0 && strlen(item->IDFile) > 2) {
equipment_material = atoi(&item->IDFile[2]);
}
}
else {
equipment_material = item->Material;
}
}
return equipment_material;
}
/**
* @param material_slot
* @return
*/
uint32 Mob::GetEquipmentColor(uint8 material_slot) const
{
const EQEmu::ItemData *item = nullptr;
if (armor_tint.Slot[material_slot].Color) {
return armor_tint.Slot[material_slot].Color;
}
item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
if (item != nullptr) {
return item->Color;
}
return 0;
}
/**
* @param material_slot
* @return
*/
int32 Mob::GetHerosForgeModel(uint8 material_slot) const
{
uint32 hero_model = 0;
if (material_slot >= 0 && material_slot < EQEmu::textures::weaponPrimary) {
uint32 ornamentation_aug_type = RuleI(Character, OrnamentationAugmentType);
const EQEmu::ItemData *item = database.GetItem(GetEquippedItemFromTextureSlot(material_slot));
int16 invslot = EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (item != nullptr && invslot != INVALID_INDEX) {
if (IsClient()) {
const EQEmu::ItemInstance *inst = CastToClient()->m_inv[invslot];
if (inst) {
if (inst->GetOrnamentationAug(ornamentation_aug_type)) {
item = inst->GetOrnamentationAug(ornamentation_aug_type)->GetItem();
hero_model = item->HerosForgeModel;
}
else if (inst->GetOrnamentHeroModel()) {
hero_model = inst->GetOrnamentHeroModel();
}
}
}
if (hero_model == 0) {
hero_model = item->HerosForgeModel;
}
}
if (IsNPC()) {
hero_model = CastToNPC()->GetHeroForgeModel();
/**
* Robes require full model number, and should only be sent to chest, arms, wrists, and legs slots
*/
if (hero_model > 1000 && material_slot != 1 && material_slot != 2 && material_slot != 3 &&
material_slot != 5) {
hero_model = 0;
}
}
}
/**
* Auto-Convert Hero Model to match the slot
*
* Otherwise, use the exact Model if model is > 999
* Robes for example are 11607 to 12107 in RoF
*/
if (hero_model > 0 && hero_model < 1000) {
hero_model *= 100;
hero_model += material_slot;
}
return hero_model;
}
uint32 NPC::GetEquippedItemFromTextureSlot(uint8 material_slot) const
{
if (material_slot > 8) {
return 0;
}
int16 inventory_slot = EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot);
if (inventory_slot == INVALID_INDEX) {
return 0;
}
return equipment[inventory_slot];
}
/**
* NPCs typically use this function for sending appearance
* @param one_client
*/
void Mob::SendArmorAppearance(Client *one_client)
{
/**
* one_client of 0 means sent to all clients
*
* Despite the fact that OP_NewSpawn and OP_ZoneSpawns include the
* armor being worn and its mats, the client doesn't update the display
* on arrival of these packets reliably.
*
* Send Wear changes if mob is a PC race and item is an armor slot.
* The other packets work for primary/secondary.
*/
Log(Logs::Detail, Logs::MobAppearance, "Mob::SendArmorAppearance [%s]",
this->GetCleanName()
);
if (IsPlayerRace(race)) {
if (!IsClient()) {
for (uint8 i = 0; i <= EQEmu::textures::materialCount; ++i) {
const EQEmu::ItemData *item = database.GetItem(GetEquippedItemFromTextureSlot(i));
if (item != nullptr) {
SendWearChange(i, one_client);
}
}
}
}
for (uint8 i = 0; i <= EQEmu::textures::materialCount; ++i) {
if (GetTextureProfileMaterial(i)) {
SendWearChange(i, one_client);
}
}
}
/**
* @param material_slot
* @param one_client
*/
void Mob::SendWearChange(uint8 material_slot, Client *one_client)
{
auto packet = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
auto *wear_change = (WearChange_Struct *) packet->pBuffer;
Log(Logs::Detail, Logs::MobAppearance, "Mob::SendWearChange [%s]",
this->GetCleanName()
);
wear_change->spawn_id = GetID();
wear_change->material = static_cast<uint32>(GetEquipmentMaterial(material_slot));
wear_change->elite_material = IsEliteMaterialItem(material_slot);
wear_change->hero_forge_model = static_cast<uint32>(GetHerosForgeModel(material_slot));
#ifdef BOTS
if (IsBot()) {
auto item_inst = CastToBot()->GetBotItem(EQEmu::InventoryProfile::CalcSlotFromMaterial(material_slot));
if (item_inst)
wear_change->color.Color = item_inst->GetColor();
else
wear_change->color.Color = 0;
}
else {
wear_change->color.Color = GetEquipmentColor(material_slot);
}
#else
wear_change->color.Color = GetEquipmentColor(material_slot);
#endif
wear_change->wear_slot_id = material_slot;
if (!one_client) {
entity_list.QueueClients(this, packet);
}
else {
one_client->QueuePacket(packet, false, Client::CLIENT_CONNECTED);
}
safe_delete(packet);
}
/**
*
* @param slot
* @param texture
* @param hero_forge_model
* @param elite_material
* @param unknown06
* @param unknown18
*/
void Mob::SendTextureWC(
uint8 slot,
uint16 texture,
uint32 hero_forge_model,
uint32 elite_material,
uint32 unknown06,
uint32 unknown18
)
{
auto outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
auto *wear_change = (WearChange_Struct *) outapp->pBuffer;
if (this->IsClient()) {
wear_change->color.Color = GetEquipmentColor(slot);
}
else {
wear_change->color.Color = this->GetArmorTint(slot);
}
wear_change->spawn_id = this->GetID();
wear_change->material = texture;
wear_change->wear_slot_id = slot;
wear_change->unknown06 = unknown06;
wear_change->elite_material = elite_material;
wear_change->hero_forge_model = hero_forge_model;
wear_change->unknown18 = unknown18;
SetMobTextureProfile(slot, texture, wear_change->color.Color, hero_forge_model);
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
/**
* @param material_slot
* @param red_tint
* @param green_tint
* @param blue_tint
*/
void Mob::SetSlotTint(uint8 material_slot, uint8 red_tint, uint8 green_tint, uint8 blue_tint)
{
uint32 color;
color = (red_tint & 0xFF) << 16;
color |= (green_tint & 0xFF) << 8;
color |= (blue_tint & 0xFF);
color |= (color) ? (0xFF << 24) : 0;
armor_tint.Slot[material_slot].Color = color;
auto outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
auto *wc = (WearChange_Struct *) outapp->pBuffer;
wc->spawn_id = this->GetID();
wc->material = GetEquipmentMaterial(material_slot);
wc->hero_forge_model = GetHerosForgeModel(material_slot);
wc->color.Color = color;
wc->wear_slot_id = material_slot;
SetMobTextureProfile(material_slot, texture, color);
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
/**
* @param material_slot
* @param texture
* @param color
* @param hero_forge_model
*/
void Mob::WearChange(uint8 material_slot, uint16 texture, uint32 color, uint32 hero_forge_model)
{
armor_tint.Slot[material_slot].Color = color;
/**
* Change internal values
*/
SetMobTextureProfile(material_slot, texture, color, hero_forge_model);
/**
* Packet update
*/
auto outapp = new EQApplicationPacket(OP_WearChange, sizeof(WearChange_Struct));
auto *wear_change = (WearChange_Struct *) outapp->pBuffer;
wear_change->spawn_id = this->GetID();
wear_change->material = texture;
wear_change->hero_forge_model = hero_forge_model;
wear_change->color.Color = color;
wear_change->wear_slot_id = material_slot;
entity_list.QueueClients(this, outapp);
safe_delete(outapp);
}
+835
View File
@@ -0,0 +1,835 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "client.h"
#include "mob.h"
#include "../common/races.h"
#include "../common/say_link.h"
#include "npc_scale_manager.h"
std::string commify(const std::string &number)
{
std::string temp_string;
auto string_length = static_cast<int>(number.length());
int i = 0;
for (i = string_length - 3; i >= 0; i -= 3) {
if (i > 0) {
temp_string = "," + number.substr(static_cast<unsigned long>(i), 3) + temp_string;
}
else {
temp_string = number.substr(static_cast<unsigned long>(i), 3) + temp_string;
}
}
if (i < 0) {
temp_string = number.substr(0, static_cast<unsigned long>(3 + i)) + temp_string;
}
return temp_string;
}
inline std::string GetMobAttributeByString(Mob *mob, const std::string &attribute)
{
std::string entity_variable = "modify_stat_" + attribute;
std::string scaling_modified;
if (mob->GetEntityVariable(entity_variable.c_str())) {
scaling_modified = " *";
}
if (attribute == "ac") {
if (mob->GetEntityVariable(std::string("modify_stat_max_hp").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string(mob->GetAC())) + scaling_modified;
}
if (attribute == "atk") {
return std::to_string(mob->GetATK()) + scaling_modified;
}
if (attribute == "end") {
int endurance = 0;
if (mob->IsClient()) {
endurance = mob->CastToClient()->GetEndurance();
}
return commify(std::to_string(endurance));
}
if (attribute == "hp") {
return commify(std::to_string(mob->GetHP()));
}
if (attribute == "hp_min_max") {
if (mob->GetEntityVariable(std::string("modify_stat_max_hp").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string(mob->GetHP())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxHP())) + " (" +
std::to_string((int) mob->GetHPRatio()) + "%)";
}
if (attribute == "mana") {
return commify(std::to_string(mob->GetMana()));
}
if (attribute == "mp_min_max") {
return commify(std::to_string(mob->GetMana())) + " / " + commify(std::to_string(mob->GetMaxMana())) + " (" +
std::to_string((int) mob->GetManaPercent()) + "%)";
}
if (attribute == "end_min_max") {
return commify(std::to_string(mob->GetEndurance())) + " / " + commify(std::to_string(mob->GetMaxEndurance())) + " (" +
std::to_string((int)mob->GetEndurancePercent()) + "%)";
}
if (attribute == "str") {
return commify(std::to_string(mob->GetSTR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxSTR())) + " +" +
commify(std::to_string(mob->GetHeroicSTR()));
}
if (attribute == "sta") {
return commify(std::to_string(mob->GetSTA())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxSTA())) + " +" +
commify(std::to_string(mob->GetHeroicSTA()));
}
if (attribute == "dex") {
return commify(std::to_string(mob->GetDEX())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxDEX())) + " +" +
commify(std::to_string(mob->GetHeroicDEX()));
}
if (attribute == "agi") {
return commify(std::to_string(mob->GetAGI())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxAGI())) + " +" +
commify(std::to_string(mob->GetHeroicAGI()));
}
if (attribute == "int") {
return commify(std::to_string(mob->GetINT())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxINT())) + " +" +
commify(std::to_string(mob->GetHeroicINT()));
}
if (attribute == "wis") {
return commify(std::to_string(mob->GetWIS())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxWIS())) + " +" +
commify(std::to_string(mob->GetHeroicWIS()));
}
if (attribute == "cha") {
return commify(std::to_string(mob->GetCHA())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxCHA())) + " +" +
commify(std::to_string(mob->GetHeroicCHA()));
}
if (attribute == "mr") {
return commify(std::to_string(mob->GetMR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxMR())) + " +" +
commify(std::to_string(mob->GetHeroicMR()));
}
if (attribute == "cr") {
return commify(std::to_string(mob->GetCR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxCR())) + " +" +
commify(std::to_string(mob->GetHeroicCR()));
}
if (attribute == "fr") {
return commify(std::to_string(mob->GetFR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxFR())) + " +" +
commify(std::to_string(mob->GetHeroicFR()));
}
if (attribute == "pr") {
return commify(std::to_string(mob->GetPR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxPR())) + " +" +
commify(std::to_string(mob->GetHeroicPR()));
}
if (attribute == "dr") {
return commify(std::to_string(mob->GetDR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxDR())) + " +" +
commify(std::to_string(mob->GetHeroicDR()));
}
if (attribute == "cr") {
return commify(std::to_string(mob->GetCR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxCR())) + " +" +
commify(std::to_string(mob->GetHeroicCR()));
}
if (attribute == "pr") {
return commify(std::to_string(mob->GetPR())) + scaling_modified + " / " +
commify(std::to_string(mob->GetMaxPR())) + " +" +
commify(std::to_string(mob->GetHeroicPR()));
}
if (attribute == "cor") {
return std::to_string(mob->GetCorrup());
}
if (attribute == "phy") {
return std::to_string(mob->GetPhR());
}
if (attribute == "name") {
return mob->GetCleanName();
}
if (attribute == "surname") {
std::string last_name = mob->GetLastName();
return (last_name.length() > 0 ? mob->GetLastName() : " ");
}
if (attribute == "race") {
return GetRaceIDName(mob->GetRace());
}
if (attribute == "class") {
return GetClassIDName(mob->GetClass(), 0);
}
if (attribute == "level") {
return std::to_string(mob->GetLevel());
}
if (attribute == "flymode") {
return std::to_string(mob->GetFlyMode());
}
if (attribute == "maxbuffslots") {
return std::to_string(mob->GetMaxBuffSlots());
}
if (attribute == "curbuffslots") {
return std::to_string(mob->GetCurrentBuffSlots());
}
if (attribute == "tohit") {
return std::to_string(mob->compute_tohit(EQEmu::skills::SkillHandtoHand));
}
if (attribute == "total_to_hit") {
return std::to_string(mob->GetTotalToHit(EQEmu::skills::SkillHandtoHand, 0));
}
if (attribute == "defense") {
return std::to_string(mob->compute_defense());
}
if (attribute == "total_defense") {
return std::to_string(mob->GetTotalDefense());
}
if (attribute == "offense") {
return std::to_string(mob->offense(EQEmu::skills::SkillHandtoHand));
}
if (attribute == "mitigation_ac") {
return std::to_string(mob->GetMitigationAC());
}
if (mob->IsNPC()) {
NPC *npc = mob->CastToNPC();
if (attribute == "npcid") {
return std::to_string(npc->GetNPCTypeID());
}
if (attribute == "texture") {
return std::to_string(npc->GetTexture());
}
if (attribute == "bodytype") {
return std::to_string(npc->GetBodyType());
}
if (attribute == "gender") {
return std::to_string(npc->GetGender());
}
if (attribute == "size") {
return std::to_string((int)npc->GetSize());
}
if (attribute == "runspeed") {
return std::to_string((int)npc->GetRunspeed());
}
if (attribute == "walkspeed") {
return std::to_string((int)npc->GetWalkspeed());
}
if (attribute == "spawngroup") {
return std::to_string(npc->GetSp2());
}
if (attribute == "grid") {
return std::to_string(npc->GetGrid());
}
if (attribute == "emote") {
return std::to_string(npc->GetEmoteID());
}
if (attribute == "see_invis") {
return std::to_string(npc->SeeInvisible());
}
if (attribute == "see_invis_undead") {
return std::to_string(npc->SeeInvisibleUndead());
}
if (attribute == "faction") {
return std::to_string(npc->GetNPCFactionID());
}
if (attribute == "loottable") {
return std::to_string(npc->GetLoottableID());
}
if (attribute == "prim_skill") {
return std::to_string(npc->GetPrimSkill());
}
if (attribute == "sec_skill") {
return std::to_string(npc->GetSecSkill());
}
if (attribute == "melee_texture_1") {
return std::to_string(npc->GetMeleeTexture1());
}
if (attribute == "melee_texture_2") {
return std::to_string(npc->GetMeleeTexture2());
}
if (attribute == "aggrorange") {
return std::to_string((int)npc->GetAggroRange());
}
if (attribute == "assistrange") {
return std::to_string((int)npc->GetAssistRange());
}
if (attribute == "findable") {
return std::to_string(npc->IsFindable());
}
if (attribute == "trackable") {
return std::to_string(npc->IsTrackable());
}
if (attribute == "spells_id") {
return std::to_string(npc->GetNPCSpellsID());
}
if (attribute == "roambox_min_x") {
return std::to_string((int)npc->GetRoamboxMinX());
}
if (attribute == "roambox_max_x") {
return std::to_string((int)npc->GetRoamboxMaxX());
}
if (attribute == "roambox_min_y") {
return std::to_string((int)npc->GetRoamboxMinY());
}
if (attribute == "roambox_max_y") {
return std::to_string((int)npc->GetRoamboxMaxY());
}
if (attribute == "roambox_min_delay") {
return std::to_string((int)npc->GetRoamboxMinDelay());
}
if (attribute == "roambox_delay") {
return std::to_string((int)npc->GetRoamboxDelay());
}
if (attribute == "roambox_distance") {
return std::to_string((int)npc->GetRoamboxDistance());
}
if (attribute == "proximity_min_x") {
return std::to_string((int)npc->GetProximityMinX());
}
if (attribute == "proximity_max_x") {
return std::to_string((int)npc->GetProximityMaxX());
}
if (attribute == "proximity_min_y") {
return std::to_string((int)npc->GetProximityMinY());
}
if (attribute == "proximity_max_y") {
return std::to_string((int)npc->GetProximityMaxY());
}
if (attribute == "proximity_min_z") {
return std::to_string((int)npc->GetProximityMinZ());
}
if (attribute == "proximity_max_z") {
return std::to_string((int)npc->GetProximityMaxZ());
}
if (attribute == "accuracy") {
return std::to_string((int)npc->GetAccuracyRating()) + scaling_modified;
}
if (attribute == "slow_mitigation") {
if (mob->GetEntityVariable(std::string("modify_stat_slow_mitigation").c_str())) {
scaling_modified = " *";
}
return std::to_string((int)npc->GetSlowMitigation()) + scaling_modified;
}
if (attribute == "min_hit") {
if (mob->GetEntityVariable(std::string("modify_stat_min_hit").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string((int)npc->GetMinDMG())) + scaling_modified;
}
if (attribute == "max_hit") {
if (mob->GetEntityVariable(std::string("modify_stat_max_hit").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string((int)npc->GetMaxDMG())) + scaling_modified;
}
if (attribute == "hp_regen") {
if (mob->GetEntityVariable(std::string("modify_stat_hp_regen").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string((int)npc->GetHPRegen())) + scaling_modified;
}
if (attribute == "attack_delay") {
if (mob->GetEntityVariable(std::string("modify_stat_attack_delay").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string(npc->GetAttackDelay())) + scaling_modified;
}
if (attribute == "spell_scale") {
if (mob->GetEntityVariable(std::string("modify_stat_spell_scale").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string((int)npc->GetSpellScale())) + scaling_modified;
}
if (attribute == "heal_scale") {
if (mob->GetEntityVariable(std::string("modify_stat_heal_scale").c_str())) {
scaling_modified = " *";
}
return commify(std::to_string((int)npc->GetHealScale())) + scaling_modified;
}
if (attribute == "avoidance") {
return commify(std::to_string((int)npc->GetAvoidanceRating())) + scaling_modified;
}
npc->GetNPCEmote(npc->GetEmoteID(), 0);
}
if (mob->IsClient()) {
Client *client = mob->CastToClient();
if (attribute == "shielding") {
return commify(std::to_string((int)client->GetShielding())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemShieldingCap)));
}
if (attribute == "spell_shielding") {
return commify(std::to_string((int)client->GetSpellShield())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemSpellShieldingCap)));
}
if (attribute == "dot_shielding") {
return commify(std::to_string((int)client->GetDoTShield())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemDoTShieldingCap)));
}
if (attribute == "stun_resist") {
return commify(std::to_string((int)client->GetStunResist())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemStunResistCap)));
}
if (attribute == "damage_shield") {
return commify(std::to_string((int)client->GetDS())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemDamageShieldCap)));
}
if (attribute == "avoidance") {
return commify(std::to_string((int) client->GetAvoidance())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemAvoidanceCap)));
}
if (attribute == "strikethrough") {
return commify(std::to_string((int) client->GetStrikeThrough())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemStrikethroughCap)));
}
if (attribute == "accuracy") {
return commify(std::to_string((int) client->GetAccuracy())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemAccuracyCap)));
}
if (attribute == "combat_effects") {
return commify(std::to_string((int) client->GetCombatEffects())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemCombatEffectsCap)));
}
if (attribute == "heal_amount") {
return commify(std::to_string((int) client->GetHealAmt())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemHealAmtCap)));
}
if (attribute == "spell_dmg") {
return commify(std::to_string((int) client->GetSpellDmg())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemSpellDmgCap)));
}
if (attribute == "clairvoyance") {
return commify(std::to_string((int) client->GetClair())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemClairvoyanceCap)));
}
if (attribute == "ds_mitigation") {
return commify(std::to_string((int) client->GetDSMit())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemDSMitigationCap)));
}
if (attribute == "hp_regen") {
return commify(std::to_string((int) client->GetHPRegen())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemHealthRegenCap)));
}
if (attribute == "mana_regen") {
return commify(std::to_string((int) client->GetManaRegen())) + " / " +
commify(std::to_string((int) RuleI(Character, ItemManaRegenCap)));
}
if (attribute == "end_regen") {
return commify(std::to_string((int) client->CalcEnduranceRegen())) + " / " +
commify(std::to_string((int) client->CalcEnduranceRegenCap()));
}
}
if (attribute == "type") {
std::string entity_type = "Mob";
if (mob->IsCorpse()) {
entity_type = "Corpse";
}
if (mob->IsNPC()) {
entity_type = "NPC";
}
if (mob->IsClient()) {
entity_type = "Client";
}
return entity_type;
}
return "null";
}
inline std::string WriteDisplayInfoSection(
Mob *mob,
const std::string &section_name,
std::vector<std::string> attributes_list,
int column_count = 3,
bool display_section_name = false
)
{
std::string text;
if (display_section_name) {
text += "<c \"#FFFF66\">" + section_name + "</c><br>";
}
text += "<table><tbody>";
int index = 0;
bool first_row = true;
for (const auto &attribute : attributes_list) {
if (index == 0) {
if (first_row) {
text += "<tr>\n";
first_row = false;
}
else {
text += "</tr><tr>\n";
}
}
std::string attribute_name = attribute;
find_replace(attribute_name, "_min_max", std::string(""));
/**
* Translate attribute names with underscores
* "total_to_hit" = "Total To Hit"
*/
if (attribute_name.find('_') != std::string::npos) {
std::vector<std::string> split_string = split(attribute_name, '_');
std::string new_attribute_name;
for (std::string &string_value : split_string) {
new_attribute_name += ucfirst(string_value) + " ";
}
attribute_name = new_attribute_name;
}
/**
* Attribute names less than 4 characters get capitalized
* "hp" = "HP"
*/
if (attribute_name.length() <= 3) {
attribute_name = str_toupper(attribute_name);
}
/**
* Attribute names larger than 3 characters get capitalized first letter
* "avoidance" = "Avoidance"
*/
if (attribute_name.length() > 3) {
attribute_name = ucfirst(attribute_name);
}
find_replace(attribute_name, "Proximity", std::string(""));
find_replace(attribute_name, "Roambox", std::string(""));
std::string attribute_value = GetMobAttributeByString(mob, attribute);
if (attribute_value.length() <= 0) {
continue;
}
text += "<td>" + attribute_name + "</td><td>" + GetMobAttributeByString(mob, attribute) + "</td>";
if (index == column_count) {
index = 0;
continue;
}
index++;
}
text += "</tr></tbody></table>";
return text;
}
inline void NPCCommandsMenu(Client* client, NPC* npc)
{
std::string menu_commands;
if (npc->GetGrid() > 0) {
menu_commands += "[" + EQEmu::SayLinkEngine::GenerateQuestSaylink("#grid show", false, "Grid Points") + "] ";
}
if (npc->GetEmoteID() > 0) {
std::string saylink = StringFormat("#emotesearch %u", npc->GetEmoteID());
menu_commands += "[" + EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink, false, "Emotes") + "] ";
}
if (npc->GetLoottableID() > 0) {
menu_commands += "[" + EQEmu::SayLinkEngine::GenerateQuestSaylink("#npcloot show", false, "Loot") + "] ";
}
if (npc->IsProximitySet()) {
menu_commands += "[" + EQEmu::SayLinkEngine::GenerateQuestSaylink("#proximity show", false, "Proximity") + "] ";
}
if (menu_commands.length() > 0) {
std::string dev_menu = "[" + EQEmu::SayLinkEngine::GenerateQuestSaylink("#devtools", false, "DevTools") + "] ";;
client->Message(0, "| %s [Show Commands] %s", dev_menu.c_str(), menu_commands.c_str());
}
}
void Mob::DisplayInfo(Mob *mob)
{
if (!mob) {
return;
}
if (this->IsClient()) {
std::string window_text = "<c \"#FFFF66\">*Drag window open vertically to see all</c><br>";
Client *client = this->CastToClient();
if (!client->IsDevToolsWindowEnabled()) {
return;
}
std::vector<std::string> info_attributes = {
"name",
"race",
"surname",
"class",
};
window_text += WriteDisplayInfoSection(mob, "Info", info_attributes, 1, false);
std::vector<std::string> basic_attributes = {
"type",
"level",
"hp_min_max",
"ac",
"mp_min_max",
"atk",
"end_min_max",
};
window_text += WriteDisplayInfoSection(mob, "Main", basic_attributes, 1, false);
std::vector<std::string> stat_attributes = {
"str",
"sta",
"agi",
"dex",
"wis",
"int",
"cha",
};
window_text += WriteDisplayInfoSection(mob, "Statistics", stat_attributes, 1, false);
std::vector<std::string> resist_attributes = {
"pr",
"mr",
"dr",
"fr",
"cr",
"cor",
"phy",
};
window_text += WriteDisplayInfoSection(mob, "Resists", resist_attributes, 1, false);
std::vector<std::string> calculations = {
"tohit",
"total_to_hit",
"defense",
"total_defense",
"offense",
"mitigation_ac",
};
window_text += WriteDisplayInfoSection(mob, "Calculations", calculations, 1, true);
if (mob->IsClient()) {
std::vector<std::string> mods = {
"hp_regen",
"mana_regen",
"end_regen",
"heal_amount",
"spell_dmg",
"clairvoyance",
};
window_text += WriteDisplayInfoSection(mob, "Mods", mods, 1, true);
std::vector<std::string> mod_defensive = {
"shielding",
"spell_shielding",
"dot_shielding",
"stun_resist",
"damage_shield",
"ds_mitigation",
"avoidance",
};
window_text += WriteDisplayInfoSection(mob, "Mod Defensive", mod_defensive, 1, true);
std::vector<std::string> mod_offensive = {
"strikethrough",
"accuracy",
"combat_effects",
};
window_text += WriteDisplayInfoSection(mob, "Mod Offensive", mod_offensive, 1, true);
}
if (mob->IsNPC()) {
NPC *npc = mob->CastToNPC();
std::vector<std::string> npc_stats = {
"accuracy",
"slow_mitigation",
"min_hit",
"max_hit",
"hp_regen",
"attack_delay",
"spell_scale",
"heal_scale",
"avoidance",
};
window_text += WriteDisplayInfoSection(mob, "NPC Stats", npc_stats, 1, true);
std::vector<std::string> npc_attributes = {
"npcid",
"texture",
"bodytype",
"gender",
"size",
"runspeed",
"walkspeed",
"spawngroup",
"grid",
"emote",
"see_invis",
"see_invis_undead",
"faction",
"loottable",
"prim_skill",
"sec_skill",
"melee_texture_1",
"melee_texture_2",
"aggrorange",
"assistrange",
"findable",
"trackable",
"flymode",
"spells_id",
"curbuffslots",
"maxbuffslots",
};
window_text += WriteDisplayInfoSection(mob, "NPC Attributes", npc_attributes, 1, true);
/**
* Print Roambox
*/
if (npc->GetRoamboxMaxX() != 0 && npc->GetRoamboxMinX() != 0) {
std::vector<std::string> npc_roambox = {
"roambox_min_x",
"roambox_max_x",
"roambox_min_y",
"roambox_max_y",
"roambox_min_delay",
"roambox_delay",
"roambox_distance",
};
window_text += WriteDisplayInfoSection(mob, "Roambox", npc_roambox, 1, true);
}
if (npc->proximity != nullptr) {
std::vector<std::string> npc_proximity = {
"proximity_min_x",
"proximity_max_x",
"proximity_min_y",
"proximity_max_y",
"proximity_min_z",
"proximity_max_z",
};
window_text += WriteDisplayInfoSection(mob, "Proximity", npc_proximity, 1, true);
}
int8 npc_type = npc_scale_manager->GetNPCScalingType(npc);
std::string npc_type_string = npc_scale_manager->GetNPCScalingTypeName(npc);
client->Message(
0,
"| # Target: %s Type: %i (%s)",
npc->GetCleanName(),
npc_type,
npc_type_string.c_str());
NPCCommandsMenu(client, npc);
}
// std::cout << "Window Length: " << window_text.length() << std::endl;
if (client->GetDisplayMobInfoWindow()) {
client->SendFullPopup(
"GM: Entity Info",
window_text.c_str(),
EQEmu::popupresponse::MOB_INFO_DISMISS,
0,
100,
0,
"Snooze",
"OK"
);
}
}
}
File diff suppressed because it is too large Load Diff
+83
View File
@@ -0,0 +1,83 @@
#pragma once
#include <memory>
class Mob;
class Client;
struct RotateCommand;
struct MovementCommand;
struct MobMovementEntry;
struct PlayerPositionUpdateServer_Struct;
enum ClientRange : int
{
ClientRangeNone = 0,
ClientRangeClose = 1,
ClientRangeMedium = 2,
ClientRangeCloseMedium = 3,
ClientRangeLong = 4,
ClientRangeCloseLong = 5,
ClientRangeMediumLong = 6,
ClientRangeAny = 7
};
enum MobMovementMode : int
{
MovementWalking = 0,
MovementRunning = 1
};
enum MobStuckBehavior : int
{
RunToTarget,
WarpToTarget,
TakeNoAction,
EvadeCombat,
MaxStuckBehavior
};
class MobMovementManager
{
public:
~MobMovementManager();
void Process();
void AddMob(Mob *m);
void RemoveMob(Mob *m);
void AddClient(Client *c);
void RemoveClient(Client *c);
void RotateTo(Mob *who, float to, MobMovementMode mode = MovementRunning);
void Teleport(Mob *who, float x, float y, float z, float heading);
void NavigateTo(Mob *who, float x, float y, float z, MobMovementMode mode = MovementRunning);
void StopNavigation(Mob *who);
void SendCommandToClients(Mob *m, float dx, float dy, float dz, float dh, int anim, ClientRange range);
float FixHeading(float in);
void DumpStats(Client *to);
void ClearStats();
static MobMovementManager &Get() {
static MobMovementManager inst;
return inst;
}
private:
MobMovementManager();
MobMovementManager(const MobMovementManager&);
MobMovementManager& operator=(const MobMovementManager&);
void FillCommandStruct(PlayerPositionUpdateServer_Struct *spu, Mob *m, float dx, float dy, float dz, float dh, int anim);
void UpdatePath(Mob *who, float x, float y, float z, MobMovementMode mode);
void UpdatePathGround(Mob *who, float x, float y, float z, MobMovementMode mode);
void UpdatePathUnderwater(Mob *who, float x, float y, float z, MobMovementMode mode);
void UpdatePathBoat(Mob *who, float x, float y, float z, MobMovementMode mode);
void PushTeleportTo(MobMovementEntry &ent, float x, float y, float z, float heading);
void PushMoveTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode);
void PushSwimTo(MobMovementEntry &ent, float x, float y, float z, MobMovementMode mode);
void PushRotateTo(MobMovementEntry &ent, Mob *who, float to, MobMovementMode mode);
void PushStopMoving(MobMovementEntry &ent);
void PushEvadeCombat(MobMovementEntry &ent);
void HandleStuckBehavior(Mob *who, float x, float y, float z, MobMovementMode mode);
struct Implementation;
std::unique_ptr<Implementation> _impl;
};
+25 -7
View File
@@ -32,6 +32,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/eq_stream_ident.h"
#include "../common/patches/patches.h"
#include "../common/rulesys.h"
#include "../common/profanity_manager.h"
#include "../common/misc_functions.h"
#include "../common/string_util.h"
#include "../common/platform.h"
@@ -42,7 +43,6 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/spdat.h"
#include "../common/eqemu_logsys.h"
#include "zone_config.h"
#include "masterentity.h"
#include "worldserver.h"
@@ -62,6 +62,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "embparser.h"
#include "lua_parser.h"
#include "questmgr.h"
#include "npc_scale_manager.h"
#include "../common/event/event_loop.h"
#include "../common/event/timer.h"
@@ -104,12 +105,13 @@ npcDecayTimes_Struct npcCorpseDecayTimes[100];
TitleManager title_manager;
QueryServ *QServ = 0;
TaskManager *taskmanager = 0;
NpcScaleManager *npc_scale_manager;
QuestParserCollection *parse = 0;
EQEmuLogSys LogSys;
const SPDat_Spell_Struct* spells;
int32 SPDAT_RECORDS = -1;
const ZoneConfig *Config;
uint64_t frame_time = 0;
double frame_time = 0.0;
void Shutdown();
extern void MapOpcodes();
@@ -222,7 +224,6 @@ int main(int argc, char** argv) {
worldserver.SetLauncherName("NONE");
}
Log(Logs::General, Logs::Zone_Server, "Connecting to MySQL...");
if (!database.Connect(
Config->DatabaseHost.c_str(),
@@ -255,6 +256,12 @@ int main(int argc, char** argv) {
guild_mgr.SetDatabase(&database);
GuildBanks = nullptr;
/**
* NPC Scale Manager
*/
npc_scale_manager = new NpcScaleManager;
npc_scale_manager->LoadScaleData();
#ifdef _EQDEBUG
_CrtSetDbgFlag(_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
#endif
@@ -344,6 +351,10 @@ int main(int argc, char** argv) {
Log(Logs::General, Logs::Zone_Server, "Loading corpse timers");
database.GetDecayTimes(npcCorpseDecayTimes);
Log(Logs::General, Logs::Zone_Server, "Loading profanity list");
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
Log(Logs::General, Logs::Error, "Loading profanity list FAILED!");
Log(Logs::General, Logs::Zone_Server, "Loading commands");
int retval = command_init();
if (retval<0)
@@ -356,18 +367,21 @@ int main(int argc, char** argv) {
std::string tmp;
if (database.GetVariable("RuleSet", tmp)) {
Log(Logs::General, Logs::Zone_Server, "Loading rule set '%s'", tmp.c_str());
if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str())) {
if (!RuleManager::Instance()->LoadRules(&database, tmp.c_str(), false)) {
Log(Logs::General, Logs::Error, "Failed to load ruleset '%s', falling back to defaults.", tmp.c_str());
}
}
else {
if (!RuleManager::Instance()->LoadRules(&database, "default")) {
if (!RuleManager::Instance()->LoadRules(&database, "default", false)) {
Log(Logs::General, Logs::Zone_Server, "No rule set configured, using default rules");
}
else {
Log(Logs::General, Logs::Zone_Server, "Loaded default rule set 'default'", tmp.c_str());
}
}
EQEmu::InitializeDynamicLookups();
Log(Logs::General, Logs::Zone_Server, "Initialized dynamic dictionary entries");
}
#ifdef BOTS
@@ -447,13 +461,17 @@ int main(int argc, char** argv) {
//Calculate frame time
std::chrono::time_point<std::chrono::system_clock> frame_now = std::chrono::system_clock::now();
frame_time = std::chrono::duration_cast<std::chrono::milliseconds>(frame_now - frame_prev).count();
frame_time = std::chrono::duration_cast<std::chrono::duration<double>>(frame_now - frame_prev).count();
frame_prev = frame_now;
if (!eqsf_open && Config->ZonePort != 0) {
Log(Logs::General, Logs::Zone_Server, "Starting EQ Network server on port %d", Config->ZonePort);
EQ::Net::EQStreamManagerOptions opts(Config->ZonePort, false, true);
opts.daybreak_options.resend_delay_ms = RuleI(Network, ResendDelayBaseMS);
opts.daybreak_options.resend_delay_factor = RuleR(Network, ResendDelayFactor);
opts.daybreak_options.resend_delay_min = RuleI(Network, ResendDelayMinMS);
opts.daybreak_options.resend_delay_max = RuleI(Network, ResendDelayMaxMS);
eqsm.reset(new EQ::Net::EQStreamManager(opts));
eqsf_open = true;
@@ -674,4 +692,4 @@ void UpdateWindowTitle(char* iNewTitle) {
}
SetConsoleTitle(tmp);
#endif
}
}
+722 -643
View File
File diff suppressed because it is too large Load Diff
+50 -17
View File
@@ -51,6 +51,7 @@ typedef struct {
float min_z;
float max_z;
bool say;
bool proximity_set;
} NPCProximity;
struct AISpells_Struct {
@@ -106,10 +107,11 @@ public:
static bool SpawnZoneController();
static int8 GetAILevel(bool iForceReRead = false);
NPC(const NPCType* data, Spawn2* respawn, const glm::vec4& position, int iflymode, bool IsCorpse = false);
NPC(const NPCType* npc_type_data, Spawn2* respawn, const glm::vec4& position, GravityBehavior iflymode, bool IsCorpse = false);
virtual ~NPC();
static NPC *SpawnNodeNPC(std::string name, std::string last_name, const glm::vec4 &position);
static NPC *SpawnGridNodeNPC(std::string name, const glm::vec4 &position, uint32 grid_id, uint32 grid_number, uint32 pause);
//abstract virtual function implementations requird by base abstract class
@@ -142,9 +144,6 @@ public:
virtual void AI_Event_SpellCastFinished(bool iCastSucceeded, uint16 slot);
void LevelScale();
void CalcNPCResists();
void CalcNPCRegen();
void CalcNPCDamage();
virtual void SetTarget(Mob* mob);
virtual uint16 GetSkill(EQEmu::skills::SkillType skill_num) const { if (skill_num <= EQEmu::skills::HIGHEST_SKILL) { return skills[skill_num]; } return 0; }
@@ -229,9 +228,6 @@ public:
EmuAppearance GetGuardPointAnim() const { return guard_anim; }
void SaveGuardPointAnim(EmuAppearance anim) { guard_anim = anim; }
void SetFlyMode(uint8 FlyMode){ flymode=FlyMode; }
uint32 GetFlyMode() const { return flymode; }
uint8 GetPrimSkill() const { return prim_melee_type; }
uint8 GetSecSkill() const { return sec_melee_type; }
uint8 GetRangedSkill() const { return ranged_type; }
@@ -255,12 +251,23 @@ public:
void SignalNPC(int _signal_id);
inline int32 GetNPCFactionID() const { return npc_faction_id; }
inline int32 GetPrimaryFaction() const { return primary_faction; }
int32 GetNPCHate(Mob* in_ent) {return hate_list.GetEntHateAmount(in_ent);}
bool IsOnHatelist(Mob*p) { return hate_list.IsEntOnHateList(p);}
inline int32 GetNPCFactionID() const
{ return npc_faction_id; }
void SetNPCFactionID(int32 in) { npc_faction_id = in; database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction); }
inline int32 GetPrimaryFaction() const
{ return primary_faction; }
int32 GetNPCHate(Mob *in_ent)
{ return hate_list.GetEntHateAmount(in_ent); }
bool IsOnHatelist(Mob *p)
{ return hate_list.IsEntOnHateList(p); }
void SetNPCFactionID(int32 in)
{
npc_faction_id = in;
database.GetFactionIdsForNPC(npc_faction_id, &faction_list, &primary_faction);
}
glm::vec4 m_SpawnPoint;
@@ -304,14 +311,17 @@ public:
void MoveTo(const glm::vec4& position, bool saveguardspot);
void GetClosestWaypoint(std::list<wplist> &wp_list, int count, const glm::vec3& location);
uint32 GetEquipment(uint8 material_slot) const; // returns item id
uint32 GetEquippedItemFromTextureSlot(uint8 material_slot) const; // returns item id
int32 GetEquipmentMaterial(uint8 material_slot) const;
void NextGuardPosition();
void SaveGuardSpot(bool iClearGuardSpot = false);
void SaveGuardSpot(const glm::vec4 &pos);
inline bool IsGuarding() const { return(m_GuardPoint.w != 0); }
void SaveGuardSpotCharm();
uint16 GetMeleeTexture1() const;
uint16 GetMeleeTexture2() const;
void RestoreGuardSpotCharm();
void AI_SetRoambox(
@@ -341,6 +351,14 @@ public:
inline const uint32 GetNPCSpellsID() const { return npc_spells_id; }
inline const uint32 GetNPCSpellsEffectsID() const { return npc_spells_effects_id; }
float GetProximityMinX();
float GetProximityMaxX();
float GetProximityMinY();
float GetProximityMaxY();
float GetProximityMinZ();
float GetProximityMaxZ();
bool IsProximitySet();
ItemList itemlist; //kathgar - why is this public? Doing other things or I would check the code
NPCProximity* proximity;
@@ -357,7 +375,7 @@ public:
void SetAvoidanceRating(int32 d) { avoidance_rating = d;}
int32 GetRawAC() const { return AC; }
void ModifyNPCStat(const char *identifier, const char *newValue);
void ModifyNPCStat(const char *identifier, const char *new_value);
virtual void SetLevel(uint8 in_level, bool command = false);
bool IsLDoNTrapped() const { return (ldon_trapped); }
@@ -384,7 +402,7 @@ public:
/* Only allows players that killed corpse to loot */
const bool HasPrivateCorpse() const { return NPCTypedata->private_corpse; }
const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; }
virtual const bool IsUnderwaterOnly() const { return NPCTypedata->underwater; }
const char* GetRawNPCTypeName() const { return NPCTypedata->name; }
void ChangeLastName(const char* in_lastname);
@@ -435,8 +453,22 @@ public:
bool IgnoreDespawn() { return ignore_despawn; }
float GetRoamboxMaxX() const;
float GetRoamboxMaxY() const;
float GetRoamboxMinX() const;
float GetRoamboxMinY() const;
float GetRoamboxDistance() const;
float GetRoamboxDestinationX() const;
float GetRoamboxDestinationY() const;
float GetRoamboxDestinationZ() const;
uint32 GetRoamboxDelay() const;
uint32 GetRoamboxMinDelay() const;
std::unique_ptr<Timer> AIautocastspell_timer;
virtual int GetStuckBehavior() const { return NPCTypedata_ours ? NPCTypedata_ours->stuck_behavior : NPCTypedata->stuck_behavior; }
protected:
const NPCType* NPCTypedata;
@@ -549,7 +581,8 @@ protected:
uint32 equipment[EQEmu::invslot::EQUIPMENT_COUNT]; //this is an array of item IDs
uint32 herosforgemodel; //this is the Hero Forge Armor Model (i.e 63 or 84 or 203)
uint16 d_melee_texture1; //this is an item Material value
uint16 d_melee_texture1;
//this is an item Material value
uint16 d_melee_texture2; //this is an item Material value (offhand)
const char* ammo_idfile; //this determines projectile graphic "IT###" (see item field 'idfile')
uint8 prim_melee_type; //Sets the Primary Weapon attack message and animation
+624
View File
@@ -0,0 +1,624 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#include "npc_scale_manager.h"
#include "../common/string_util.h"
/**
* @param npc
*/
void NpcScaleManager::ScaleNPC(NPC * npc)
{
int8 npc_type = GetNPCScalingType(npc);
int npc_level = npc->GetLevel();
bool is_auto_scaled = IsAutoScaled(npc);
global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
if (!scale_data.level) {
Log(Logs::General, Logs::NPCScaling, "NPC: %s - scaling data not found for type: %i level: %i",
npc->GetCleanName(),
npc_type,
npc_level
);
return;
}
if (npc->GetAC() == 0) {
npc->ModifyNPCStat("ac", std::to_string(scale_data.ac).c_str());
}
if (npc->GetMaxHP() == 0) {
npc->ModifyNPCStat("max_hp", std::to_string(scale_data.hp).c_str());
npc->Heal();
}
if (npc->GetAccuracyRating() == 0) {
npc->ModifyNPCStat("accuracy", std::to_string(scale_data.accuracy).c_str());
}
if (npc->GetSlowMitigation() == 0) {
npc->ModifyNPCStat("slow_mitigation", std::to_string(scale_data.slow_mitigation).c_str());
}
if (npc->GetATK() == 0) {
npc->ModifyNPCStat("atk", std::to_string(scale_data.attack).c_str());
}
if (npc->GetSTR() == 0) {
npc->ModifyNPCStat("str", std::to_string(scale_data.strength).c_str());
}
if (npc->GetSTA() == 0) {
npc->ModifyNPCStat("sta", std::to_string(scale_data.stamina).c_str());
}
if (npc->GetDEX() == 0) {
npc->ModifyNPCStat("dex", std::to_string(scale_data.dexterity).c_str());
}
if (npc->GetAGI() == 0) {
npc->ModifyNPCStat("agi", std::to_string(scale_data.agility).c_str());
}
if (npc->GetINT() == 0) {
npc->ModifyNPCStat("int", std::to_string(scale_data.intelligence).c_str());
}
if (npc->GetWIS() == 0) {
npc->ModifyNPCStat("wis", std::to_string(scale_data.wisdom).c_str());
}
if (npc->GetCHA() == 0) {
npc->ModifyNPCStat("cha", std::to_string(scale_data.charisma).c_str());
}
if (npc->GetMR() == 0) {
npc->ModifyNPCStat("mr", std::to_string(scale_data.magic_resist).c_str());
}
if (npc->GetCR() == 0) {
npc->ModifyNPCStat("cr", std::to_string(scale_data.cold_resist).c_str());
}
if (npc->GetFR() == 0) {
npc->ModifyNPCStat("fr", std::to_string(scale_data.fire_resist).c_str());
}
if (npc->GetPR() == 0) {
npc->ModifyNPCStat("pr", std::to_string(scale_data.poison_resist).c_str());
}
if (npc->GetDR() == 0) {
npc->ModifyNPCStat("dr", std::to_string(scale_data.disease_resist).c_str());
}
if (npc->GetCorrup() == 0) {
npc->ModifyNPCStat("cr", std::to_string(scale_data.corruption_resist).c_str());
}
if (npc->GetPhR() == 0) {
npc->ModifyNPCStat("pr", std::to_string(scale_data.physical_resist).c_str());
}
if (npc->GetMinDMG() == 0 && npc->GetMaxDMG() == 0) {
int min_dmg = scale_data.min_dmg;
if (RuleB(Combat, UseNPCDamageClassLevelMods)) {
int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass());
min_dmg = (min_dmg * class_level_damage_mod) / 220;
Log(Logs::Moderate,
Logs::NPCScaling,
"ClassLevelDamageMod::min_dmg base: %i calc: %i",
scale_data.min_dmg,
min_dmg);
}
npc->ModifyNPCStat("min_hit", std::to_string(min_dmg).c_str());
}
if (npc->GetMaxDMG() == 0) {
int max_dmg = scale_data.max_dmg;
if (RuleB(Combat, UseNPCDamageClassLevelMods)) {
int32 class_level_damage_mod = GetClassLevelDamageMod(npc->GetLevel(), npc->GetClass());
max_dmg = (scale_data.max_dmg * class_level_damage_mod) / 220;
Log(Logs::Moderate,
Logs::NPCScaling,
"ClassLevelDamageMod::max_dmg base: %i calc: %i",
scale_data.max_dmg,
max_dmg
);
}
npc->ModifyNPCStat("max_hit", std::to_string(max_dmg).c_str());
}
if (npc->GetHPRegen() == 0) {
npc->ModifyNPCStat("hp_regen", std::to_string(scale_data.hp_regen_rate).c_str());
}
if (npc->GetAttackDelay() == 0) {
npc->ModifyNPCStat("attack_delay", std::to_string(scale_data.attack_delay).c_str());
}
if (npc->GetSpellScale() == 0) {
npc->ModifyNPCStat("spell_scale", std::to_string(scale_data.spell_scale).c_str());
}
if (npc->GetHealScale() == 0) {
npc->ModifyNPCStat("heal_scale", std::to_string(scale_data.heal_scale).c_str());
}
if (!npc->HasSpecialAbilities() && is_auto_scaled) {
npc->ModifyNPCStat("special_abilities", scale_data.special_abilities.c_str());
}
if (LogSys.log_settings[Logs::NPCScaling].is_category_enabled == 1) {
std::string scale_log;
for (const auto &stat : scaling_stats) {
std::string variable = StringFormat("modify_stat_%s", stat.c_str());
if (npc->EntityVariableExists(variable.c_str())) {
scale_log += stat + ": " + npc->GetEntityVariable(variable.c_str()) + " ";
}
}
Log(Logs::General,
Logs::NPCScaling,
"(%s) level: %i type: %i Auto: %s Setting: %s",
npc->GetCleanName(),
npc_level,
npc_type,
(is_auto_scaled ? "true" : "false"),
scale_log.c_str());
}
}
bool NpcScaleManager::LoadScaleData()
{
auto results = database.QueryDatabase(
"SELECT "
"type,"
"level,"
"ac,"
"hp,"
"accuracy,"
"slow_mitigation,"
"attack,"
"strength,"
"stamina,"
"dexterity,"
"agility,"
"intelligence,"
"wisdom,"
"charisma,"
"magic_resist,"
"cold_resist,"
"fire_resist,"
"poison_resist,"
"disease_resist,"
"corruption_resist,"
"physical_resist,"
"min_dmg,"
"max_dmg,"
"hp_regen_rate,"
"attack_delay,"
"spell_scale,"
"heal_scale,"
"special_abilities"
" FROM `npc_scale_global_base`"
);
for (auto row = results.begin(); row != results.end(); ++row) {
global_npc_scale scale_data;
scale_data.type = atoi(row[0]);
scale_data.level = atoi(row[1]);
scale_data.ac = atoi(row[2]);
scale_data.hp = atoi(row[3]);
scale_data.accuracy = atoi(row[4]);
scale_data.slow_mitigation = atoi(row[5]);
scale_data.attack = atoi(row[6]);
scale_data.strength = atoi(row[7]);
scale_data.stamina = atoi(row[8]);
scale_data.dexterity = atoi(row[9]);
scale_data.agility = atoi(row[10]);
scale_data.intelligence = atoi(row[11]);
scale_data.wisdom = atoi(row[12]);
scale_data.charisma = atoi(row[13]);
scale_data.magic_resist = atoi(row[14]);
scale_data.cold_resist = atoi(row[15]);
scale_data.fire_resist = atoi(row[16]);
scale_data.poison_resist = atoi(row[17]);
scale_data.disease_resist = atoi(row[18]);
scale_data.corruption_resist = atoi(row[19]);
scale_data.physical_resist = atoi(row[20]);
scale_data.min_dmg = atoi(row[21]);
scale_data.max_dmg = atoi(row[22]);
scale_data.hp_regen_rate = atoi(row[23]);
scale_data.attack_delay = atoi(row[24]);
scale_data.spell_scale = atoi(row[25]);
scale_data.heal_scale = atoi(row[26]);
if (row[25]) {
scale_data.special_abilities = row[27];
}
npc_global_base_scaling_data.insert(
std::make_pair(
std::make_pair(scale_data.type, scale_data.level),
scale_data
)
);
}
Log(Logs::General, Logs::NPCScaling, "Global Base Scaling Data Loaded...");
return true;
}
/**
* @param npc_type
* @param npc_level
* @return NpcScaleManager::global_npc_scale
*/
NpcScaleManager::global_npc_scale NpcScaleManager::GetGlobalScaleDataForTypeLevel(int8 npc_type, int npc_level)
{
auto iter = npc_global_base_scaling_data.find(std::make_pair(npc_type, npc_level));
if (iter != npc_global_base_scaling_data.end()) {
return iter->second;
}
return {};
}
/**
* @param level
* @param npc_class
* @return
*/
uint32 NpcScaleManager::GetClassLevelDamageMod(uint32 level, uint32 npc_class)
{
uint32 multiplier = 0;
switch (npc_class) {
case WARRIOR: {
if (level < 20) {
multiplier = 220;
}
else if (level < 30) {
multiplier = 230;
}
else if (level < 40) {
multiplier = 250;
}
else if (level < 53) {
multiplier = 270;
}
else if (level < 57) {
multiplier = 280;
}
else if (level < 60) {
multiplier = 290;
}
else if (level < 70) {
multiplier = 300;
}
else {
multiplier = 311;
}
break;
}
case DRUID:
case CLERIC:
case SHAMAN: {
if (level < 70) {
multiplier = 150;
}
else {
multiplier = 157;
}
break;
}
case BERSERKER:
case PALADIN:
case SHADOWKNIGHT: {
if (level < 35) {
multiplier = 210;
}
else if (level < 45) {
multiplier = 220;
}
else if (level < 51) {
multiplier = 230;
}
else if (level < 56) {
multiplier = 240;
}
else if (level < 60) {
multiplier = 250;
}
else if (level < 68) {
multiplier = 260;
}
else {
multiplier = 270;
}
break;
}
case MONK:
case BARD:
case ROGUE:
case BEASTLORD: {
if (level < 51) {
multiplier = 180;
}
else if (level < 58) {
multiplier = 190;
}
else if (level < 70) {
multiplier = 200;
}
else {
multiplier = 210;
}
break;
}
case RANGER: {
if (level < 58) {
multiplier = 200;
}
else if (level < 70) {
multiplier = 210;
}
else {
multiplier = 220;
}
break;
}
case MAGICIAN:
case WIZARD:
case NECROMANCER:
case ENCHANTER: {
if (level < 70) {
multiplier = 120;
}
else {
multiplier = 127;
}
break;
}
default: {
if (level < 35) {
multiplier = 210;
}
else if (level < 45) {
multiplier = 220;
}
else if (level < 51) {
multiplier = 230;
}
else if (level < 56) {
multiplier = 240;
}
else if (level < 60) {
multiplier = 250;
}
else {
multiplier = 260;
}
break;
}
}
return multiplier;
}
/**
* @param npc
* @return int8
*/
int8 NpcScaleManager::GetNPCScalingType(NPC *&npc)
{
std::string npc_name = npc->GetName();
if (npc->IsRareSpawn() || npc_name.find('#') != std::string::npos || isupper(npc_name[0])) {
return 1;
}
if (npc->IsRaidTarget()) {
return 2;
}
return 0;
}
/**
* @param npc
* @return std::string
*/
std::string NpcScaleManager::GetNPCScalingTypeName(NPC *&npc)
{
int8 scaling_type = GetNPCScalingType(npc);
if (scaling_type == 1) {
return "Named";
}
if (npc->IsRaidTarget()) {
return "Raid";
}
return "Trash";
}
/**
* Determines based on minimum criteria if NPC is auto scaled for certain things to be scaled like
* special abilities. We use this so we don't blindly assume we want things to be applied
*
* @param npc
* @return
*/
bool NpcScaleManager::IsAutoScaled(NPC *npc)
{
return
(npc->GetHP() == 0 &&
npc->GetMaxDMG() == 0 &&
npc->GetMinDMG() == 0 &&
npc->GetSTR() == 0 &&
npc->GetSTA() == 0 &&
npc->GetDEX() == 0 &&
npc->GetAGI() == 0 &&
npc->GetINT() == 0 &&
npc->GetWIS() == 0 &&
npc->GetCHA() == 0 &&
npc->GetMR() == 0 &&
npc->GetFR() == 0 &&
npc->GetCR() == 0 &&
npc->GetPR() == 0 &&
npc->GetDR() == 0);
}
/**
* Returns false if scaling data not found
* @param npc
* @return
*/
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically(NPC *&npc)
{
int8 npc_type = GetNPCScalingType(npc);
int npc_level = npc->GetLevel();
global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
if (!scale_data.level) {
Log(
Logs::General,
Logs::NPCScaling,
"NpcScaleManager::ApplyGlobalBaseScalingToNPCStatically NPC: %s - scaling data not found for type: %i level: %i",
npc->GetCleanName(),
npc_type,
npc_level
);
return false;
}
std::string query = StringFormat(
"UPDATE `npc_types` SET "
"AC = %i, "
"hp = %i, "
"Accuracy = %i, "
"slow_mitigation = %i, "
"ATK = %i, "
"STR = %i, "
"STA = %i, "
"DEX = %i, "
"AGI = %i, "
"_INT = %i, "
"WIS = %i, "
"CHA = %i, "
"MR = %i, "
"CR = %i, "
"FR = %i, "
"PR = %i, "
"DR = %i, "
"Corrup = %i, "
"PhR = %i, "
"mindmg = %i, "
"maxdmg = %i, "
"hp_regen_rate = %i, "
"attack_delay = %i, "
"spellscale = %i, "
"healscale = %i, "
"special_abilities = '%s' "
"WHERE `id` = %i",
scale_data.ac,
scale_data.hp,
scale_data.accuracy,
scale_data.slow_mitigation,
scale_data.attack,
scale_data.strength,
scale_data.stamina,
scale_data.dexterity,
scale_data.agility,
scale_data.intelligence,
scale_data.wisdom,
scale_data.charisma,
scale_data.magic_resist,
scale_data.cold_resist,
scale_data.fire_resist,
scale_data.poison_resist,
scale_data.disease_resist,
scale_data.corruption_resist,
scale_data.physical_resist,
scale_data.min_dmg,
scale_data.max_dmg,
scale_data.hp_regen_rate,
scale_data.attack_delay,
scale_data.spell_scale,
scale_data.heal_scale,
EscapeString(scale_data.special_abilities).c_str(),
npc->GetNPCTypeID()
);
auto results = database.QueryDatabase(query);
return results.Success();
}
/**
* Returns false if scaling data not found
* @param npc
* @return
*/
bool NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically(NPC *&npc)
{
int8 npc_type = GetNPCScalingType(npc);
int npc_level = npc->GetLevel();
global_npc_scale scale_data = GetGlobalScaleDataForTypeLevel(npc_type, npc_level);
if (!scale_data.level) {
Log(
Logs::General,
Logs::NPCScaling,
"NpcScaleManager::ApplyGlobalBaseScalingToNPCDynamically NPC: %s - scaling data not found for type: %i level: %i",
npc->GetCleanName(),
npc_type,
npc_level
);
return false;
}
std::string query = StringFormat(
"UPDATE `npc_types` SET "
"AC = 0, "
"hp = 0, "
"Accuracy = 0, "
"slow_mitigation = 0, "
"ATK = 0, "
"STR = 0, "
"STA = 0, "
"DEX = 0, "
"AGI = 0, "
"_INT = 0, "
"WIS = 0, "
"CHA = 0, "
"MR = 0, "
"CR = 0, "
"FR = 0, "
"PR = 0, "
"DR = 0, "
"Corrup = 0, "
"PhR = 0, "
"mindmg = 0, "
"maxdmg = 0, "
"hp_regen_rate = 0, "
"attack_delay = 0, "
"spellscale = 0, "
"healscale = 0, "
"special_abilities = '' "
"WHERE `id` = %i",
npc->GetNPCTypeID()
);
auto results = database.QueryDatabase(query);
return results.Success();
}
+107
View File
@@ -0,0 +1,107 @@
/**
* EQEmulator: Everquest Server Emulator
* Copyright (C) 2001-2018 EQEmulator Development Team (https://github.com/EQEmu/Server)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; version 2 of the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY except by those people which sell it, which
* are required to give you total support for your newly bought product;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
*/
#ifndef EQEMU_NPC_SCALE_MANAGER_H
#define EQEMU_NPC_SCALE_MANAGER_H
#include "npc.h"
class NpcScaleManager {
public:
struct global_npc_scale {
int type;
int level;
int ac;
int hp;
int accuracy;
int slow_mitigation;
int attack;
int strength;
int stamina;
int dexterity;
int agility;
int intelligence;
int wisdom;
int charisma;
int magic_resist;
int cold_resist;
int fire_resist;
int poison_resist;
int disease_resist;
int corruption_resist;
int physical_resist;
int min_dmg;
int max_dmg;
int hp_regen_rate;
int attack_delay;
int spell_scale;
int heal_scale;
std::string special_abilities;
};
std::vector<std::string> scaling_stats = {
"ac",
"max_hp",
"accuracy",
"slow_mitigation",
"atk",
"str",
"sta",
"dex",
"agi",
"int",
"wis",
"cha",
"mr",
"cr",
"fr",
"pr",
"dr",
"cr",
"pr",
"min_hit",
"max_hit",
"hp_regen",
"attack_delay",
"spell_scale",
"heal_scale",
"special_abilities"
};
void ScaleNPC(NPC * npc);
bool IsAutoScaled(NPC * npc);
bool LoadScaleData();
global_npc_scale GetGlobalScaleDataForTypeLevel(int8 npc_type, int npc_level);
std::map<std::pair<int, int>, global_npc_scale> npc_global_base_scaling_data;
int8 GetNPCScalingType(NPC * &npc);
std::string GetNPCScalingTypeName(NPC * &npc);
bool ApplyGlobalBaseScalingToNPCStatically(NPC * &npc);
bool ApplyGlobalBaseScalingToNPCDynamically(NPC * &npc);
uint32 GetClassLevelDamageMod(uint32 level, uint32 npc_class);
};
extern NpcScaleManager *npc_scale_manager;
#endif //EQEMU_NPC_SCALE_MANAGER_H
+14 -14
View File
@@ -179,20 +179,20 @@ protected:
void ResetState(); // Set state back to original
void RandomSpawn(bool send_packet = false); //spawn this ground spawn at a random place
Object_Struct m_data; // Packet data
EQEmu::ItemInstance* m_inst; // Item representing object
bool m_inuse; // Currently in use by a client?
uint32 m_id; // Database key, different than drop_id
uint32 m_type; // Object Type, ie, forge, oven, dropped item, etc (ref: ContainerUseTypes)
uint32 m_icon; // Icon to use for forge, oven, etc
float m_max_x;
float m_max_y;
float m_min_x;
float m_min_y;
float m_z;
float m_heading;
bool m_ground_spawn;
char m_display_name[64];
Object_Struct m_data; // Packet data
EQEmu::ItemInstance *m_inst; // Item representing object
bool m_inuse; // Currently in use by a client?
uint32 m_id; // Database key, different than drop_id
uint32 m_type; // Object Type, ie, forge, oven, dropped item, etc (ref: ContainerUseTypes)
uint32 m_icon; // Icon to use for forge, oven, etc
float m_max_x;
float m_max_y;
float m_min_x;
float m_min_y;
float m_z;
float m_heading;
bool m_ground_spawn;
char m_display_name[64];
std::map<std::string, std::string> o_EntityVariables;
-5
View File
@@ -8,15 +8,10 @@
IPathfinder *IPathfinder::Load(const std::string &zone) {
struct stat statbuffer;
std::string waypoint_path = fmt::format("maps/path/{0}.path", zone);
std::string navmesh_path = fmt::format("maps/nav/{0}.nav", zone);
if (stat(navmesh_path.c_str(), &statbuffer) == 0) {
return new PathfinderNavmesh(navmesh_path);
}
if (stat(waypoint_path.c_str(), &statbuffer) == 0) {
return new PathfinderWaypoint(waypoint_path);
}
return new PathfinderNull();
}
+47 -2
View File
@@ -6,6 +6,50 @@
class Client;
class Seperator;
enum PathingPolyFlags
{
PathingNormal = 1,
PathingWater = 2,
PathingLava = 4,
PathingZoneLine = 8,
PathingPvP = 16,
PathingSlime = 32,
PathingIce = 64,
PathingVWater = 128,
PathingGeneralArea = 256,
PathingPortal = 512,
PathingPrefer = 1024,
PathingDisabled = 2048,
PathingAll = 65535,
PathingNotDisabled = PathingAll ^ PathingDisabled
};
struct PathfinderOptions
{
PathfinderOptions() {
flags = PathingNotDisabled;
smooth_path = true;
step_size = 10.0f;
flag_cost[0] = 1.0f;
flag_cost[1] = 3.0f;
flag_cost[2] = 5.0f;
flag_cost[3] = 1.0f;
flag_cost[4] = 2.0f;
flag_cost[5] = 2.0f;
flag_cost[6] = 4.0f;
flag_cost[7] = 1.0f;
flag_cost[8] = 0.1f;
flag_cost[9] = 0.1f;
offset = 3.25f;
}
int flags;
bool smooth_path;
float step_size;
float flag_cost[10];
float offset;
};
class IPathfinder
{
public:
@@ -29,8 +73,9 @@ public:
IPathfinder() { }
virtual ~IPathfinder() { }
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck) = 0;
virtual glm::vec3 GetRandomLocation() = 0;
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled) = 0;
virtual IPath FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions& opts) = 0;
virtual glm::vec3 GetRandomLocation(const glm::vec3 &start) = 0;
virtual void DebugCommand(Client *c, const Seperator *sep) = 0;
static IPathfinder *Load(const std::string &zone);
+279 -25
View File
@@ -1,9 +1,9 @@
#include <memory>
#include <stdio.h>
#include <vector>
#include <DetourNavMesh.h>
#include <DetourNavMeshQuery.h>
#include "pathfinder_nav_mesh.h"
#include <DetourCommon.h>
#include <DetourNavMeshQuery.h>
#include "zone.h"
#include "water_map.h"
@@ -12,7 +12,7 @@
extern Zone *zone;
const int MaxNavmeshNodes = 4092;
const int MaxNavmeshNodes = 4096;
struct PathfinderNavmesh::Implementation
{
@@ -33,7 +33,7 @@ PathfinderNavmesh::~PathfinderNavmesh()
Clear();
}
IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck)
IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags)
{
partial = false;
@@ -43,14 +43,14 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl
if (!m_impl->query) {
m_impl->query = dtAllocNavMeshQuery();
m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes);
}
m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes);
glm::vec3 current_location(start.x, start.z, start.y);
glm::vec3 dest_location(end.x, end.z, end.y);
dtQueryFilter filter;
filter.setIncludeFlags(65535U ^ 2048);
filter.setIncludeFlags(flags);
filter.setAreaCost(0, 1.0f); //Normal
filter.setAreaCost(1, 3.0f); //Water
filter.setAreaCost(2, 5.0f); //Lava
@@ -131,10 +131,184 @@ IPathfinder::IPath PathfinderNavmesh::FindRoute(const glm::vec3 &start, const gl
return Route;
}
glm::vec3 PathfinderNavmesh::GetRandomLocation()
IPathfinder::IPath PathfinderNavmesh::FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions &opts)
{
partial = false;
if (!m_impl->nav_mesh) {
return glm::vec3();
return IPath();
}
if (!m_impl->query) {
m_impl->query = dtAllocNavMeshQuery();
}
m_impl->query->init(m_impl->nav_mesh, MaxNavmeshNodes);
glm::vec3 current_location(start.x, start.z, start.y);
glm::vec3 dest_location(end.x, end.z, end.y);
dtQueryFilter filter;
filter.setIncludeFlags(opts.flags);
filter.setAreaCost(0, opts.flag_cost[0]); //Normal
filter.setAreaCost(1, opts.flag_cost[1]); //Water
filter.setAreaCost(2, opts.flag_cost[2]); //Lava
filter.setAreaCost(4, opts.flag_cost[3]); //PvP
filter.setAreaCost(5, opts.flag_cost[4]); //Slime
filter.setAreaCost(6, opts.flag_cost[5]); //Ice
filter.setAreaCost(7, opts.flag_cost[6]); //V Water (Frigid Water)
filter.setAreaCost(8, opts.flag_cost[7]); //General Area
filter.setAreaCost(9, opts.flag_cost[8]); //Portal
filter.setAreaCost(10, opts.flag_cost[9]); //Prefer
static const int max_polys = 256;
dtPolyRef start_ref;
dtPolyRef end_ref;
glm::vec3 ext(5.0f, 100.0f, 5.0f);
m_impl->query->findNearestPoly(&current_location[0], &ext[0], &filter, &start_ref, 0);
m_impl->query->findNearestPoly(&dest_location[0], &ext[0], &filter, &end_ref, 0);
if (!start_ref || !end_ref) {
return IPath();
}
int npoly = 0;
dtPolyRef path[max_polys] = { 0 };
m_impl->query->findPath(start_ref, end_ref, &current_location[0], &dest_location[0], &filter, path, &npoly, max_polys);
if (npoly) {
glm::vec3 epos = dest_location;
if (path[npoly - 1] != end_ref) {
m_impl->query->closestPointOnPoly(path[npoly - 1], &dest_location[0], &epos[0], 0);
partial = true;
auto dist = DistanceSquared(epos, current_location);
if (dist < 10000.0f) {
stuck = true;
}
}
int n_straight_polys;
glm::vec3 straight_path[max_polys];
unsigned char straight_path_flags[max_polys];
dtPolyRef straight_path_polys[max_polys];
auto status = m_impl->query->findStraightPath(&current_location[0], &epos[0], path, npoly,
(float*)&straight_path[0], straight_path_flags,
straight_path_polys, &n_straight_polys, 2048, DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS);
if (dtStatusFailed(status)) {
return IPath();
}
if (n_straight_polys) {
if (opts.smooth_path) {
IPath Route;
//Add the first point
{
auto &flag = straight_path_flags[0];
if (flag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) {
auto &p = straight_path[0];
Route.push_back(glm::vec3(p.x, p.z, p.y));
}
else {
auto &p = straight_path[0];
float h = 0.0f;
if (dtStatusSucceed(GetPolyHeightOnPath(path, npoly, p, &h))) {
p.y = h + opts.offset;
}
Route.push_back(glm::vec3(p.x, p.z, p.y));
}
}
for (int i = 0; i < n_straight_polys - 1; ++i)
{
auto &flag = straight_path_flags[i];
if (flag & DT_STRAIGHTPATH_OFFMESH_CONNECTION) {
auto &poly = straight_path_polys[i];
auto &p2 = straight_path[i + 1];
glm::vec3 node(p2.x, p2.z, p2.y);
Route.push_back(node);
unsigned short pflag = 0;
if (dtStatusSucceed(m_impl->nav_mesh->getPolyFlags(straight_path_polys[i], &pflag))) {
if (pflag & 512) {
Route.push_back(true);
}
}
}
else {
auto &p1 = straight_path[i];
auto &p2 = straight_path[i + 1];
auto dist = glm::distance(p1, p2);
auto dir = glm::normalize(p2 - p1);
float total = 0.0f;
glm::vec3 previous_pt = p1;
while (total < dist) {
glm::vec3 current_pt;
float dist_to_move = opts.step_size;
float ff = opts.step_size / 2.0f;
if (total + dist_to_move + ff >= dist) {
current_pt = p2;
total = dist;
}
else {
total += dist_to_move;
current_pt = p1 + dir * total;
}
float h = 0.0f;
if (dtStatusSucceed(GetPolyHeightOnPath(path, npoly, current_pt, &h))) {
current_pt.y = h + opts.offset;
}
Route.push_back(glm::vec3(current_pt.x, current_pt.z, current_pt.y));
previous_pt = current_pt;
}
}
}
return Route;
}
else {
IPath Route;
for (int i = 0; i < n_straight_polys; ++i)
{
auto &current = straight_path[i];
glm::vec3 node(current.x, current.z, current.y);
Route.push_back(node);
unsigned short flag = 0;
if (dtStatusSucceed(m_impl->nav_mesh->getPolyFlags(straight_path_polys[i], &flag))) {
if (flag & 512) {
Route.push_back(true);
}
}
}
return Route;
}
}
}
return IPath();
}
glm::vec3 PathfinderNavmesh::GetRandomLocation(const glm::vec3 &start)
{
if (start.x == 0.0f && start.y == 0.0)
return glm::vec3(0.f);
if (!m_impl->nav_mesh) {
return glm::vec3(0.f);
}
if (!m_impl->query) {
@@ -143,19 +317,38 @@ glm::vec3 PathfinderNavmesh::GetRandomLocation()
}
dtQueryFilter filter;
filter.setIncludeFlags(65535U);
filter.setIncludeFlags(65535U ^ 2048);
filter.setAreaCost(0, 1.0f); //Normal
filter.setAreaCost(1, 3.0f); //Water
filter.setAreaCost(2, 5.0f); //Lava
filter.setAreaCost(4, 1.0f); //PvP
filter.setAreaCost(5, 2.0f); //Slime
filter.setAreaCost(6, 2.0f); //Ice
filter.setAreaCost(7, 4.0f); //V Water (Frigid Water)
filter.setAreaCost(8, 1.0f); //General Area
filter.setAreaCost(9, 0.1f); //Portal
filter.setAreaCost(10, 0.1f); //Prefer
dtPolyRef randomRef;
float point[3];
if (dtStatusSucceed(m_impl->query->findRandomPoint(&filter, []() {
return (float)zone->random.Real(0.0, 1.0);
}, &randomRef, point)))
dtPolyRef start_ref;
glm::vec3 current_location(start.x, start.z, start.y);
glm::vec3 ext(5.0f, 100.0f, 5.0f);
m_impl->query->findNearestPoly(&current_location[0], &ext[0], &filter, &start_ref, 0);
if (!start_ref)
{
return glm::vec3(0.f);
}
if (dtStatusSucceed(m_impl->query->findRandomPointAroundCircle(start_ref, &current_location[0], 100.f, &filter, []() { return (float)zone->random.Real(0.0, 1.0); }, &randomRef, point)))
{
return glm::vec3(point[0], point[2], point[1]);
}
return glm::vec3();
return glm::vec3(0.f);
}
void PathfinderNavmesh::DebugCommand(Client *c, const Seperator *sep)
@@ -288,24 +481,85 @@ void PathfinderNavmesh::Load(const std::string &path)
void PathfinderNavmesh::ShowPath(Client * c, const glm::vec3 &start, const glm::vec3 &end)
{
auto &list = entity_list.GetNPCList();
for (auto &iter : list) {
auto npc = iter.second;
auto name = npc->GetName();
if (strstr(name, "PathNode") != nullptr) {
npc->Depop();
}
}
PathfinderOptions opts;
opts.smooth_path = true;
opts.step_size = RuleR(Pathing, NavmeshStepSize);
bool partial = false;
bool stuck = false;
auto path = FindRoute(start, end, partial, stuck);
std::vector<FindPerson_Point> points;
auto path = FindPath(start, end, partial, stuck, opts);
if (!partial) {
FindPerson_Point p;
for (auto &node : path)
for (auto &node : path) {
if (!node.teleport) {
NPC::SpawnNPC("PathNode 2253 1 0 1 2 1", glm::vec4(node.pos, 1.0));
}
}
}
dtStatus PathfinderNavmesh::GetPolyHeightNoConnections(dtPolyRef ref, const float *pos, float *height) const
{
auto *m_nav = m_impl->nav_mesh;
if (!m_nav) {
return DT_FAILURE;
}
const dtMeshTile* tile = 0;
const dtPoly* poly = 0;
if (dtStatusFailed(m_nav->getTileAndPolyByRef(ref, &tile, &poly))) {
return DT_FAILURE | DT_INVALID_PARAM;
}
if (poly->getType() != DT_POLYTYPE_OFFMESH_CONNECTION) {
const unsigned int ip = (unsigned int)(poly - tile->polys);
const dtPolyDetail* pd = &tile->detailMeshes[ip];
for (int j = 0; j < pd->triCount; ++j)
{
if (!node.teleport) {
p.x = node.pos.x;
p.y = node.pos.y;
p.z = node.pos.z;
points.push_back(p);
const unsigned char* t = &tile->detailTris[(pd->triBase + j) * 4];
const float* v[3];
for (int k = 0; k < 3; ++k)
{
if (t[k] < poly->vertCount)
v[k] = &tile->verts[poly->verts[t[k]] * 3];
else
v[k] = &tile->detailVerts[(pd->vertBase + (t[k] - poly->vertCount)) * 3];
}
float h;
if (dtClosestHeightPointTriangle(pos, v[0], v[1], v[2], h))
{
if (height)
*height = h;
return DT_SUCCESS;
}
}
}
c->SendPathPacket(points);
return DT_FAILURE | DT_INVALID_PARAM;
}
dtStatus PathfinderNavmesh::GetPolyHeightOnPath(const dtPolyRef *path, const int path_len, const glm::vec3 &pos, float *h) const
{
if (!path || !path_len) {
return DT_FAILURE;
}
for (int i = 0; i < path_len; ++i) {
dtPolyRef ref = path[i];
if (dtStatusSucceed(GetPolyHeightNoConnections(ref, &pos[0], h))) {
return DT_SUCCESS;
}
}
return DT_FAILURE;
}
+7 -3
View File
@@ -2,6 +2,7 @@
#include "pathfinder_interface.h"
#include <string>
#include <DetourNavMesh.h>
class PathfinderNavmesh : public IPathfinder
{
@@ -9,15 +10,18 @@ public:
PathfinderNavmesh(const std::string &path);
virtual ~PathfinderNavmesh();
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck);
virtual glm::vec3 GetRandomLocation();
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled);
virtual IPath FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions& opts);
virtual glm::vec3 GetRandomLocation(const glm::vec3 &start);
virtual void DebugCommand(Client *c, const Seperator *sep);
private:
void Clear();
void Load(const std::string &path);
void ShowPath(Client *c, const glm::vec3 &start, const glm::vec3 &end);
dtStatus GetPolyHeightNoConnections(dtPolyRef ref, const float *pos, float *height) const;
dtStatus GetPolyHeightOnPath(const dtPolyRef *path, const int path_len, const glm::vec3 &pos, float *h) const;
struct Implementation;
std::unique_ptr<Implementation> m_impl;
};
};
+13 -3
View File
@@ -1,6 +1,6 @@
#include "pathfinder_null.h"
IPathfinder::IPath PathfinderNull::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck)
IPathfinder::IPath PathfinderNull::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags)
{
partial = false;
stuck = false;
@@ -10,7 +10,17 @@ IPathfinder::IPath PathfinderNull::FindRoute(const glm::vec3 &start, const glm::
return ret;
}
glm::vec3 PathfinderNull::GetRandomLocation()
IPathfinder::IPath PathfinderNull::FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions &opts)
{
return glm::vec3();
partial = false;
stuck = false;
IPath ret;
ret.push_back(start);
ret.push_back(end);
return ret;
}
glm::vec3 PathfinderNull::GetRandomLocation(const glm::vec3 &start)
{
return glm::vec3(0.0f);
}
+4 -3
View File
@@ -8,7 +8,8 @@ public:
PathfinderNull() { }
virtual ~PathfinderNull() { }
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck);
virtual glm::vec3 GetRandomLocation();
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled);
virtual IPath FindPath(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, const PathfinderOptions& opts);
virtual glm::vec3 GetRandomLocation(const glm::vec3 &start);
virtual void DebugCommand(Client *c, const Seperator *sep) { }
};
};
+4 -4
View File
@@ -114,7 +114,7 @@ PathfinderWaypoint::~PathfinderWaypoint()
{
}
IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck)
IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags)
{
stuck = false;
partial = false;
@@ -184,7 +184,7 @@ IPathfinder::IPath PathfinderWaypoint::FindRoute(const glm::vec3 &start, const g
return IPath();
}
glm::vec3 PathfinderWaypoint::GetRandomLocation()
glm::vec3 PathfinderWaypoint::GetRandomLocation(const glm::vec3 &start)
{
if (m_impl->Nodes.size() > 0) {
auto idx = zone->random.Int(0, (int)m_impl->Nodes.size() - 1);
@@ -529,7 +529,7 @@ void PathfinderWaypoint::ShowNode(const Node &n) {
sprintf(npc_type->name, "%s", DigitToWord(n.id).c_str());
sprintf(npc_type->lastname, "%i", n.id);
npc_type->cur_hp = 4000000;
npc_type->current_hp = 4000000;
npc_type->max_hp = 4000000;
npc_type->race = 2254;
npc_type->gender = 2;
@@ -557,7 +557,7 @@ void PathfinderWaypoint::ShowNode(const Node &n) {
npc_type->findable = 1;
auto position = glm::vec4(n.v.x, n.v.y, n.v.z, 0.0f);
auto npc = new NPC(npc_type, nullptr, position, FlyMode1);
auto npc = new NPC(npc_type, nullptr, position, GravityBehavior::Flying);
npc->GiveNPCTypeData(npc_type);
entity_list.AddNPC(npc, true, true);
+3 -3
View File
@@ -11,8 +11,8 @@ public:
PathfinderWaypoint(const std::string &path);
virtual ~PathfinderWaypoint();
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck);
virtual glm::vec3 GetRandomLocation();
virtual IPath FindRoute(const glm::vec3 &start, const glm::vec3 &end, bool &partial, bool &stuck, int flags = PathingNotDisabled);
virtual glm::vec3 GetRandomLocation(const glm::vec3 &start);
virtual void DebugCommand(Client *c, const Seperator *sep);
private:
@@ -28,4 +28,4 @@ private:
struct Implementation;
std::unique_ptr<Implementation> m_impl;
};
};
+18 -216
View File
@@ -1,5 +1,5 @@
#include "../common/global_define.h"
#include "../common/event/background_task.h"
#include "../common/event/task.h"
#include "client.h"
#include "zone.h"
@@ -7,207 +7,6 @@
extern Zone *zone;
void AdjustRoute(std::list<IPathfinder::IPathNode> &nodes, int flymode, float offset) {
if (!zone->HasMap() || !zone->HasWaterMap()) {
return;
}
for (auto &node : nodes) {
if (flymode == 0 || !zone->watermap->InLiquid(node.pos)) {
auto best_z = zone->zonemap->FindBestZ(node.pos, nullptr);
if (best_z != BEST_Z_INVALID) {
node.pos.z = best_z + offset;
}
}
}
}
glm::vec3 Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached)
{
glm::vec3 To(ToX, ToY, ToZ);
if (Speed <= 0) {
return To;
}
glm::vec3 From(GetX(), GetY(), GetZ());
if (DistanceSquared(To, From) < 1.0f) {
WaypointChanged = false;
NodeReached = true;
Route.clear();
return To;
}
if (Route.empty()) {
bool partial = false;
bool stuck = false;
Route = zone->pathing->FindRoute(From, To, partial, stuck);
AdjustRoute(Route, flymode, GetZOffset());
PathingDestination = To;
WaypointChanged = true;
NodeReached = false;
if (stuck) {
return HandleStuckPath(To, From);
}
if (Route.empty()) {
return To;
}
else {
return (*Route.begin()).pos;
}
}
else {
if (PathRecalcTimer->Check()) {
bool SameDestination = DistanceSquared(To, PathingDestination) < 100.0f;
if (!SameDestination) {
//We had a route but our target position moved too much
bool partial = false;
bool stuck = false;
Route = zone->pathing->FindRoute(From, To, partial, stuck);
AdjustRoute(Route, flymode, GetZOffset());
PathingDestination = To;
WaypointChanged = true;
NodeReached = false;
if (stuck) {
return HandleStuckPath(To, From);
}
if (Route.empty()) {
return To;
}
else {
return (*Route.begin()).pos;
}
}
}
if (!IsRooted()) {
bool AtPrevNode = DistanceSquared(From, PathingLastPosition) < 1.0f;
if (AtPrevNode) {
PathingLoopCount++;
auto front = (*Route.begin()).pos;
if (PathingLoopCount > 5) {
Teleport(front);
SendPosition();
Route.pop_front();
WaypointChanged = true;
NodeReached = true;
PathingLoopCount = 0;
}
return front;
}
else {
PathingLastPosition = From;
PathingLoopCount = 0;
}
}
else {
PathingLastPosition = From;
PathingLoopCount = 0;
}
bool AtNextNode = false;
if (flymode == 1) {
AtNextNode = DistanceSquared(From, (*Route.begin()).pos) < 4.0f;
}
else {
float z_dist = From.z - (*Route.begin()).pos.z;
z_dist *= z_dist;
AtNextNode = DistanceSquaredNoZ(From, (*Route.begin()).pos) < 4.0f && z_dist < 25.0f;
}
if (AtNextNode) {
WaypointChanged = false;
NodeReached = true;
Route.pop_front();
if (Route.empty()) {
bool partial = false;
bool stuck = false;
Route = zone->pathing->FindRoute(From, To, partial, stuck);
AdjustRoute(Route, flymode, GetZOffset());
PathingDestination = To;
WaypointChanged = true;
if (stuck) {
return HandleStuckPath(To, From);
}
if(Route.empty()) {
return To;
}
else {
return (*Route.begin()).pos;
}
}
else {
auto node = *Route.begin();
if (node.teleport) {
Route.pop_front();
if (Route.empty()) {
return To;
}
auto nextNode = *Route.begin();
Teleport(nextNode.pos);
Route.pop_front();
if (Route.empty()) {
return To;
}
return (*Route.begin()).pos;
}
return node.pos;
}
}
else {
WaypointChanged = false;
NodeReached = false;
return (*Route.begin()).pos;
}
}
return To;
}
glm::vec3 Mob::HandleStuckPath(const glm::vec3 &To, const glm::vec3 &From)
{
bool partial = false;
bool stuck = false;
auto r = zone->pathing->FindRoute(To, From, partial, stuck);
Route.clear();
if (r.size() < 1) {
Teleport(To);
return To;
}
auto iter = r.rbegin();
auto final_node = (*iter);
Teleport(final_node.pos);
if (r.size() < 2) {
return final_node.pos;
}
else {
iter++;
return (*iter).pos;
}
}
void CullPoints(std::vector<FindPerson_Point> &points) {
if (!zone->HasMap()) {
return;
@@ -241,53 +40,56 @@ void CullPoints(std::vector<FindPerson_Point> &points) {
}
void Client::SendPathPacket(const std::vector<FindPerson_Point> &points) {
EQ::BackgroundTask task([](EQEmu::Any &data) {
auto &points = EQEmu::any_cast<std::vector<FindPerson_Point>&>(data);
EQEmu::Any data(points);
EQ::Task([=](EQ::Task::ResolveFn resolve, EQ::Task::RejectFn reject) {
auto points = EQEmu::any_cast<std::vector<FindPerson_Point>>(data);
CullPoints(points);
}, [this](EQEmu::Any &data) {
auto &points = EQEmu::any_cast<std::vector<FindPerson_Point>&>(data);
resolve(points);
})
.Then([this](const EQEmu::Any &result) {
auto points = EQEmu::any_cast<std::vector<FindPerson_Point>>(result);
if (points.size() < 2) {
if (Admin() > 10) {
Message(MT_System, "Too few points");
}
EQApplicationPacket outapp(OP_FindPersonReply, 0);
QueuePacket(&outapp);
return;
}
if (points.size() > 36) {
if (Admin() > 10) {
Message(MT_System, "Too many points %u", points.size());
}
EQApplicationPacket outapp(OP_FindPersonReply, 0);
QueuePacket(&outapp);
return;
}
if (Admin() > 10) {
Message(MT_System, "Total points %u", points.size());
}
int len = sizeof(FindPersonResult_Struct) + (points.size() + 1) * sizeof(FindPerson_Point);
auto outapp = new EQApplicationPacket(OP_FindPersonReply, len);
FindPersonResult_Struct* fpr = (FindPersonResult_Struct*)outapp->pBuffer;
std::vector<FindPerson_Point>::iterator cur, end;
cur = points.begin();
end = points.end();
unsigned int r;
for (r = 0; cur != end; ++cur, r++) {
fpr->path[r] = *cur;
}
//put the last element into the destination field
--cur;
fpr->path[r] = *cur;
fpr->dest = *cur;
FastQueuePacket(&outapp);
}, points);
})
.Run();
}
+265 -211
View File
@@ -6126,6 +6126,59 @@ XS(XS_Client_Popup2) {
XSRETURN_EMPTY;
}
XS(XS_Client_SetPrimaryWeaponOrnamentation); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SetPrimaryWeaponOrnamentation)
{
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Client::SetPrimaryWeaponOrnamentation(THIS, model_id)");
}
{
Client *THIS;
uint32 model_id = (uint32) SvUV(ST(1));
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
}
else {
Perl_croak(aTHX_ "THIS is not of type Client");
}
if (THIS == nullptr) {
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
}
THIS->SetPrimaryWeaponOrnamentation(model_id);
}
XSRETURN_EMPTY;
}
XS(XS_Client_SetSecondaryWeaponOrnamentation); /* prototype to pass -Wmissing-prototypes */
XS(XS_Client_SetSecondaryWeaponOrnamentation)
{
dXSARGS;
if (items != 2) {
Perl_croak(aTHX_ "Usage: Client::SetSecondaryWeaponOrnamentation(THIS, model_id)");
}
{
Client *THIS;
uint32 model_id = (uint32) SvUV(ST(1));
if (sv_derived_from(ST(0), "Client")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
THIS = INT2PTR(Client *, tmp);
}
else {
Perl_croak(aTHX_ "THIS is not of type Client");
}
if (THIS == nullptr) {
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
}
THIS->SetSecondaryWeaponOrnamentation(model_id);
}
XSRETURN_EMPTY;
}
#ifdef __cplusplus
extern "C"
@@ -6147,240 +6200,241 @@ XS(boot_Client) {
XS_VERSION_BOOTCHECK;
newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$");
newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$");
newXSproto(strcpy(buf, "SaveBackup"), XS_Client_SaveBackup, file, "$");
newXSproto(strcpy(buf, "AccountID"), XS_Client_AccountID, file, "$");
newXSproto(strcpy(buf, "AccountName"), XS_Client_AccountName, file, "$");
newXSproto(strcpy(buf, "AddAAPoints"), XS_Client_AddAAPoints, file, "$$");
newXSproto(strcpy(buf, "AddAlternateCurrencyValue"), XS_Client_AddAlternateCurrencyValue, file, "$$$");
newXSproto(strcpy(buf, "AddCrystals"), XS_Client_AddCrystals, file, "$$");
newXSproto(strcpy(buf, "AddEXP"), XS_Client_AddEXP, file, "$$;$$");
newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$");
newXSproto(strcpy(buf, "AddMoneyToPP"), XS_Client_AddMoneyToPP, file, "$$$$$$");
newXSproto(strcpy(buf, "AddPVPPoints"), XS_Client_AddPVPPoints, file, "$$");
newXSproto(strcpy(buf, "AddSkill"), XS_Client_AddSkill, file, "$$$");
newXSproto(strcpy(buf, "Admin"), XS_Client_Admin, file, "$");
newXSproto(strcpy(buf, "AssignTask"), XS_Client_AssignTask, file, "$$$;$");
newXSproto(strcpy(buf, "AssignToInstance"), XS_Client_AssignToInstance, file, "$$");
newXSproto(strcpy(buf, "AutoSplitEnabled"), XS_Client_AutoSplitEnabled, file, "$");
newXSproto(strcpy(buf, "BreakInvis"), XS_Client_BreakInvis, file, "$");
newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$");
newXSproto(strcpy(buf, "CalcPriceMod"), XS_Client_CalcPriceMod, file, "$;$$");
newXSproto(strcpy(buf, "CanHaveSkill"), XS_Client_CanHaveSkill, file, "$$");
newXSproto(strcpy(buf, "ChangeLastName"), XS_Client_ChangeLastName, file, "$$");
newXSproto(strcpy(buf, "CharacterID"), XS_Client_CharacterID, file, "$");
newXSproto(strcpy(buf, "CheckIncreaseSkill"), XS_Client_CheckIncreaseSkill, file, "$$;$");
newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$");
newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$");
newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$");
newXSproto(strcpy(buf, "Connected"), XS_Client_Connected, file, "$");
newXSproto(strcpy(buf, "InZone"), XS_Client_InZone, file, "$");
newXSproto(strcpy(buf, "Kick"), XS_Client_Kick, file, "$");
newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$");
newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$");
newXSproto(strcpy(buf, "Disconnect"), XS_Client_Disconnect, file, "$");
newXSproto(strcpy(buf, "IsLD"), XS_Client_IsLD, file, "$");
newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$");
newXSproto(strcpy(buf, "GetAnon"), XS_Client_GetAnon, file, "$");
newXSproto(strcpy(buf, "DropItem"), XS_Client_DropItem, file, "$$");
newXSproto(strcpy(buf, "Duck"), XS_Client_Duck, file, "$");
newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$");
newXSproto(strcpy(buf, "SetGM"), XS_Client_SetGM, file, "$$");
newXSproto(strcpy(buf, "SetPVP"), XS_Client_SetPVP, file, "$$");
newXSproto(strcpy(buf, "GetPVP"), XS_Client_GetPVP, file, "$");
newXSproto(strcpy(buf, "GetGM"), XS_Client_GetGM, file, "$");
newXSproto(strcpy(buf, "SetBaseClass"), XS_Client_SetBaseClass, file, "$$");
newXSproto(strcpy(buf, "SetBaseRace"), XS_Client_SetBaseRace, file, "$$");
newXSproto(strcpy(buf, "SetBaseGender"), XS_Client_SetBaseGender, file, "$$");
newXSproto(strcpy(buf, "GetBaseFace"), XS_Client_GetBaseFace, file, "$");
newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$");
newXSproto(strcpy(buf, "GetLastName"), XS_Client_GetLastName, file, "$");
newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$");
newXSproto(strcpy(buf, "GetBaseSTR"), XS_Client_GetBaseSTR, file, "$");
newXSproto(strcpy(buf, "GetBaseSTA"), XS_Client_GetBaseSTA, file, "$");
newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$");
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$");
newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$");
newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$");
newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$");
newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$");
newXSproto(strcpy(buf, "GetAAPercent"), XS_Client_GetAAPercent, file, "$");
newXSproto(strcpy(buf, "GetAAPoints"), XS_Client_GetAAPoints, file, "$$");
newXSproto(strcpy(buf, "GetAccountAge"), XS_Client_GetAccountAge, file, "$");
newXSproto(strcpy(buf, "GetAccountFlag"), XS_Client_GetAccountFlag, file, "$$");
newXSproto(strcpy(buf, "GetAggroCount"), XS_Client_GetAggroCount, file, "$");
newXSproto(strcpy(buf, "GetAllMoney"), XS_Client_GetAllMoney, file, "$");
newXSproto(strcpy(buf, "GetAnon"), XS_Client_GetAnon, file, "$");
newXSproto(strcpy(buf, "GetAugmentAt"), XS_Client_GetAugmentAt, file, "$$$");
newXSproto(strcpy(buf, "GetAugmentIDAt"), XS_Client_GetAugmentIDAt, file, "$$$");
newXSproto(strcpy(buf, "GetBaseAGI"), XS_Client_GetBaseAGI, file, "$");
newXSproto(strcpy(buf, "GetBaseCHA"), XS_Client_GetBaseCHA, file, "$");
newXSproto(strcpy(buf, "GetBaseDEX"), XS_Client_GetBaseDEX, file, "$");
newXSproto(strcpy(buf, "GetBaseFace"), XS_Client_GetBaseFace, file, "$");
newXSproto(strcpy(buf, "GetBaseINT"), XS_Client_GetBaseINT, file, "$");
newXSproto(strcpy(buf, "GetBaseAGI"), XS_Client_GetBaseAGI, file, "$");
newXSproto(strcpy(buf, "GetBaseSTA"), XS_Client_GetBaseSTA, file, "$");
newXSproto(strcpy(buf, "GetBaseSTR"), XS_Client_GetBaseSTR, file, "$");
newXSproto(strcpy(buf, "GetBaseWIS"), XS_Client_GetBaseWIS, file, "$");
newXSproto(strcpy(buf, "GetWeight"), XS_Client_GetWeight, file, "$");
newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$");
newXSproto(strcpy(buf, "GetAAExp"), XS_Client_GetAAExp, file, "$");
newXSproto(strcpy(buf, "GetAAPercent"), XS_Client_GetAAPercent, file, "$");
newXSproto(strcpy(buf, "GetTotalSecondsPlayed"), XS_Client_GetTotalSecondsPlayed, file, "$");
newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$");
newXSproto(strcpy(buf, "SetDeity"), XS_Client_SetDeity, file, "$$");
newXSproto(strcpy(buf, "AddEXP"), XS_Client_AddEXP, file, "$$;$$");
newXSproto(strcpy(buf, "SetEXP"), XS_Client_SetEXP, file, "$$$;$");
newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$");
newXSproto(strcpy(buf, "GetBecomeNPCLevel"), XS_Client_GetBecomeNPCLevel, file, "$");
newXSproto(strcpy(buf, "GetBindHeading"), XS_Client_GetBindHeading, file, "$$");
newXSproto(strcpy(buf, "GetBindX"), XS_Client_GetBindX, file, "$$");
newXSproto(strcpy(buf, "GetBindY"), XS_Client_GetBindY, file, "$$");
newXSproto(strcpy(buf, "GetBindZ"), XS_Client_GetBindZ, file, "$$");
newXSproto(strcpy(buf, "GetBindHeading"), XS_Client_GetBindHeading, file, "$$");
newXSproto(strcpy(buf, "GetBindZoneID"), XS_Client_GetBindZoneID, file, "$$");
newXSproto(strcpy(buf, "MovePC"), XS_Client_MovePC, file, "$$$$$$");
newXSproto(strcpy(buf, "MovePCInstance"), XS_Client_MovePCInstance, file, "$$$$$$$");
newXSproto(strcpy(buf, "ChangeLastName"), XS_Client_ChangeLastName, file, "$$");
newXSproto(strcpy(buf, "GetFactionLevel"), XS_Client_GetFactionLevel, file, "$$$$$$$$");
newXSproto(strcpy(buf, "SetFactionLevel"), XS_Client_SetFactionLevel, file, "$$$$$$");
newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$");
newXSproto(strcpy(buf, "GetRawItemAC"), XS_Client_GetRawItemAC, file, "$");
newXSproto(strcpy(buf, "AccountID"), XS_Client_AccountID, file, "$");
newXSproto(strcpy(buf, "AccountName"), XS_Client_AccountName, file, "$");
newXSproto(strcpy(buf, "Admin"), XS_Client_Admin, file, "$");
newXSproto(strcpy(buf, "CharacterID"), XS_Client_CharacterID, file, "$");
newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$");
newXSproto(strcpy(buf, "UpdateWho"), XS_Client_UpdateWho, file, "$;$");
newXSproto(strcpy(buf, "GuildRank"), XS_Client_GuildRank, file, "$");
newXSproto(strcpy(buf, "GuildID"), XS_Client_GuildID, file, "$");
newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$");
newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$");
newXSproto(strcpy(buf, "AddMoneyToPP"), XS_Client_AddMoneyToPP, file, "$$$$$$");
newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$");
newXSproto(strcpy(buf, "GetSkillPoints"), XS_Client_GetSkillPoints, file, "$");
newXSproto(strcpy(buf, "SetSkillPoints"), XS_Client_SetSkillPoints, file, "$$");
newXSproto(strcpy(buf, "IncreaseSkill"), XS_Client_IncreaseSkill, file, "$$;$");
newXSproto(strcpy(buf, "IncreaseLanguageSkill"), XS_Client_IncreaseLanguageSkill, file, "$$;$");
newXSproto(strcpy(buf, "GetSkill"), XS_Client_GetSkill, file, "$$");
newXSproto(strcpy(buf, "GetRawSkill"), XS_Client_GetRawSkill, file, "$$");
newXSproto(strcpy(buf, "HasSkill"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "CanHaveSkill"), XS_Client_CanHaveSkill, file, "$$");
newXSproto(strcpy(buf, "SetSkill"), XS_Client_SetSkill, file, "$$$");
newXSproto(strcpy(buf, "AddSkill"), XS_Client_AddSkill, file, "$$$");
newXSproto(strcpy(buf, "CheckSpecializeIncrease"), XS_Client_CheckSpecializeIncrease, file, "$$");
newXSproto(strcpy(buf, "CheckIncreaseSkill"), XS_Client_CheckIncreaseSkill, file, "$$;$");
newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$");
newXSproto(strcpy(buf, "MaxSkill"), XS_Client_MaxSkill, file, "$$;$$");
newXSproto(strcpy(buf, "GMKill"), XS_Client_GMKill, file, "$");
newXSproto(strcpy(buf, "IsMedding"), XS_Client_IsMedding, file, "$");
newXSproto(strcpy(buf, "GetDuelTarget"), XS_Client_GetDuelTarget, file, "$");
newXSproto(strcpy(buf, "IsDueling"), XS_Client_IsDueling, file, "$");
newXSproto(strcpy(buf, "SetDuelTarget"), XS_Client_SetDuelTarget, file, "$$");
newXSproto(strcpy(buf, "SetDueling"), XS_Client_SetDueling, file, "$$");
newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$");
newXSproto(strcpy(buf, "MemSpell"), XS_Client_MemSpell, file, "$$$;$");
newXSproto(strcpy(buf, "UnmemSpell"), XS_Client_UnmemSpell, file, "$$;$");
newXSproto(strcpy(buf, "UnmemSpellBySpellID"), XS_Client_UnmemSpellBySpellID, file, "$$");
newXSproto(strcpy(buf, "UnmemSpellAll"), XS_Client_UnmemSpellAll, file, "$;$");
newXSproto(strcpy(buf, "ScribeSpell"), XS_Client_ScribeSpell, file, "$$$;$");
newXSproto(strcpy(buf, "UnscribeSpell"), XS_Client_UnscribeSpell, file, "$$;$");
newXSproto(strcpy(buf, "UnscribeSpellAll"), XS_Client_UnscribeSpellAll, file, "$;$");
newXSproto(strcpy(buf, "TrainDiscBySpellID"), XS_Client_TrainDiscBySpellID, file, "$$");
newXSproto(strcpy(buf, "GetDiscSlotBySpellID"), XS_Client_GetDiscSlotBySpellID, file, "$$");
newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$");
newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$");
newXSproto(strcpy(buf, "IsSitting"), XS_Client_IsSitting, file, "$");
newXSproto(strcpy(buf, "IsBecomeNPC"), XS_Client_IsBecomeNPC, file, "$");
newXSproto(strcpy(buf, "GetBecomeNPCLevel"), XS_Client_GetBecomeNPCLevel, file, "$");
newXSproto(strcpy(buf, "SetBecomeNPC"), XS_Client_SetBecomeNPC, file, "$$");
newXSproto(strcpy(buf, "SetBecomeNPCLevel"), XS_Client_SetBecomeNPCLevel, file, "$$");
newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$");
newXSproto(strcpy(buf, "GetFeigned"), XS_Client_GetFeigned, file, "$");
newXSproto(strcpy(buf, "AutoSplitEnabled"), XS_Client_AutoSplitEnabled, file, "$");
newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$");
newXSproto(strcpy(buf, "GetHorseId"), XS_Client_GetHorseId, file, "$");
newXSproto(strcpy(buf, "NukeItem"), XS_Client_NukeItem, file, "$$;$");
newXSproto(strcpy(buf, "SetTint"), XS_Client_SetTint, file, "$$$");
newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$");
newXSproto(strcpy(buf, "Undye"), XS_Client_Undye, file, "$");
newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$");
newXSproto(strcpy(buf, "GetAugmentIDAt"), XS_Client_GetAugmentIDAt, file, "$$$");
newXSproto(strcpy(buf, "DeleteItemInInventory"), XS_Client_DeleteItemInInventory, file, "$$;$$");
newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$");
newXSproto(strcpy(buf, "SetStats"), XS_Client_SetStats, file, "$$$");
newXSproto(strcpy(buf, "IncStats"), XS_Client_IncStats, file, "$$$");
newXSproto(strcpy(buf, "DropItem"), XS_Client_DropItem, file, "$$");
newXSproto(strcpy(buf, "BreakInvis"), XS_Client_BreakInvis, file, "$");
newXSproto(strcpy(buf, "GetGroup"), XS_Client_GetGroup, file, "$");
newXSproto(strcpy(buf, "LeaveGroup"), XS_Client_LeaveGroup, file, "$");
newXSproto(strcpy(buf, "GetRaid"), XS_Client_GetRaid, file, "$");
newXSproto(strcpy(buf, "IsGrouped"), XS_Client_IsGrouped, file, "$");
newXSproto(strcpy(buf, "IsRaidGrouped"), XS_Client_IsRaidGrouped, file, "$");
newXSproto(strcpy(buf, "Hungry"), XS_Client_Hungry, file, "$");
newXSproto(strcpy(buf, "Thirsty"), XS_Client_Thirsty, file, "$");
newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$");
newXSproto(strcpy(buf, "DecreaseByID"), XS_Client_DecreaseByID, file, "$$$");
newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$");
newXSproto(strcpy(buf, "Escape"), XS_Client_Escape, file, "$");
newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$");
newXSproto(strcpy(buf, "GoFish"), XS_Client_GoFish, file, "$");
newXSproto(strcpy(buf, "ForageItem"), XS_Client_ForageItem, file, "$");
newXSproto(strcpy(buf, "CalcPriceMod"), XS_Client_CalcPriceMod, file, "$;$$");
newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$");
newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$");
newXSproto(strcpy(buf, "GetCarriedMoney"), XS_Client_GetCarriedMoney, file, "$");
newXSproto(strcpy(buf, "GetCharacterFactionLevel"), XS_Client_GetCharacterFactionLevel, file, "$$");
newXSproto(strcpy(buf, "SetZoneFlag"), XS_Client_SetZoneFlag, file, "$$");
newXSproto(strcpy(buf, "ClearZoneFlag"), XS_Client_ClearZoneFlag, file, "$$");
newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$");
newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$");
newXSproto(strcpy(buf, "LoadZoneFlags"), XS_Client_LoadZoneFlags, file, "$");
newXSproto(strcpy(buf, "SetAATitle"), XS_Client_SetAATitle, file, "$$;$");
newXSproto(strcpy(buf, "GetClientVersion"), XS_Client_GetClientVersion, file, "$");
newXSproto(strcpy(buf, "GetClientVersionBit"), XS_Client_GetClientVersionBit, file, "$");
newXSproto(strcpy(buf, "SetTitleSuffix"), XS_Client_SetTitleSuffix, file, "$$;$");
newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$");
newXSproto(strcpy(buf, "GetAAPoints"), XS_Client_GetAAPoints, file, "$$");
newXSproto(strcpy(buf, "GetSpentAA"), XS_Client_GetSpentAA, file, "$$");
newXSproto(strcpy(buf, "AddAAPoints"), XS_Client_AddAAPoints, file, "$$");
newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$");
newXSproto(strcpy(buf, "GetModCharacterFactionLevel"), XS_Client_GetModCharacterFactionLevel, file, "$$");
newXSproto(strcpy(buf, "GetLDoNWins"), XS_Client_GetLDoNWins, file, "$");
newXSproto(strcpy(buf, "GetLDoNLosses"), XS_Client_GetLDoNLosses, file, "$");
newXSproto(strcpy(buf, "GetLDoNWinsTheme"), XS_Client_GetLDoNWinsTheme, file, "$$");
newXSproto(strcpy(buf, "GetLDoNLossesTheme"), XS_Client_GetLDoNLossesTheme, file, "$$");
newXSproto(strcpy(buf, "GetItemAt"), XS_Client_GetItemAt, file, "$$");
newXSproto(strcpy(buf, "GetAugmentAt"), XS_Client_GetAugmentAt, file, "$$$");
newXSproto(strcpy(buf, "GetStartZone"), XS_Client_GetStartZone, file, "$");
newXSproto(strcpy(buf, "SetStartZone"), XS_Client_SetStartZone, file, "$$");
newXSproto(strcpy(buf, "KeyRingAdd"), XS_Client_KeyRingAdd, file, "$$");
newXSproto(strcpy(buf, "KeyRingCheck"), XS_Client_KeyRingCheck, file, "$$");
newXSproto(strcpy(buf, "AddPVPPoints"), XS_Client_AddPVPPoints, file, "$$");
newXSproto(strcpy(buf, "AddCrystals"), XS_Client_AddCrystals, file, "$$");
newXSproto(strcpy(buf, "GetPVPPoints"), XS_Client_GetPVPPoints, file, "$");
newXSproto(strcpy(buf, "GetRadiantCrystals"), XS_Client_GetRadiantCrystals, file, "$");
newXSproto(strcpy(buf, "GetEbonCrystals"), XS_Client_GetEbonCrystals, file, "$");
newXSproto(strcpy(buf, "ReadBook"), XS_Client_ReadBook, file, "$$$");
newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$");
newXSproto(strcpy(buf, "GetGroupPoints"), XS_Client_GetGroupPoints, file, "$");
newXSproto(strcpy(buf, "GetRaidPoints"), XS_Client_GetRaidPoints, file, "$");
newXSproto(strcpy(buf, "LearnRecipe"), XS_Client_LearnRecipe, file, "$$");
newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$");
newXSproto(strcpy(buf, "GetMaxEndurance"), XS_Client_GetMaxEndurance, file, "$");
newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$");
newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$");
newXSproto(strcpy(buf, "SendOPTranslocateConfirm"), XS_Client_SendOPTranslocateConfirm, file, "$$$");
newXSproto(strcpy(buf, "NPCSpawn"), XS_Client_NPCSpawn, file, "$$$;$");
newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$");
newXSproto(strcpy(buf, "AddLevelBasedExp"), XS_Client_AddLevelBasedExp, file, "$$;$");
newXSproto(strcpy(buf, "IncrementAA"), XS_Client_IncrementAA, file, "$$");
newXSproto(strcpy(buf, "GrantAlternateAdvancementAbility"), XS_Client_GrantAlternateAdvancementAbility, file,
"$$$;$");
newXSproto(strcpy(buf, "GetAALevel"), XS_Client_GetAALevel, file, "$$");
newXSproto(strcpy(buf, "MarkCompassLoc"), XS_Client_MarkCompassLoc, file, "$$$$");
newXSproto(strcpy(buf, "ClearCompassMark"), XS_Client_ClearCompassMark, file, "$");
newXSproto(strcpy(buf, "GetFreeSpellBookSlot"), XS_Client_GetFreeSpellBookSlot, file, "$;$");
newXSproto(strcpy(buf, "GetSpellBookSlotBySpellID"), XS_Client_GetSpellBookSlotBySpellID, file, "$$");
newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$;$");
newXSproto(strcpy(buf, "AssignTask"), XS_Client_AssignTask, file, "$$$;$");
newXSproto(strcpy(buf, "FailTask"), XS_Client_FailTask, file, "$$");
newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$");
newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$");
newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$");
newXSproto(strcpy(buf, "GetTaskActivityDoneCount"), XS_Client_GetTaskActivityDoneCount, file, "$$$");
newXSproto(strcpy(buf, "GetCorpseCount"), XS_Client_GetCorpseCount, file, "$");
newXSproto(strcpy(buf, "GetCorpseID"), XS_Client_GetCorpseID, file, "$$");
newXSproto(strcpy(buf, "GetCorpseItemAt"), XS_Client_GetCorpseItemAt, file, "$$$");
newXSproto(strcpy(buf, "AssignToInstance"), XS_Client_AssignToInstance, file, "$$");
newXSproto(strcpy(buf, "Freeze"), XS_Client_Freeze, file, "$");
newXSproto(strcpy(buf, "UnFreeze"), XS_Client_UnFreeze, file, "$");
newXSproto(strcpy(buf, "GetAggroCount"), XS_Client_GetAggroCount, file, "$");
newXSproto(strcpy(buf, "GetCarriedMoney"), XS_Client_GetCarriedMoney, file, "$");
newXSproto(strcpy(buf, "GetAllMoney"), XS_Client_GetAllMoney, file, "$");
newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$");
newXSproto(strcpy(buf, "SetCustomItemData"), XS_Client_SetCustomItemData, file, "$$$$");
newXSproto(strcpy(buf, "GetCustomItemData"), XS_Client_GetCustomItemData, file, "$$$");
newXSproto(strcpy(buf, "OpenLFGuildWindow"), XS_Client_OpenLFGuildWindow, file, "$");
newXSproto(strcpy(buf, "SignalClient"), XS_Client_SignalClient, file, "$");
newXSproto(strcpy(buf, "AddAlternateCurrencyValue"), XS_Client_AddAlternateCurrencyValue, file, "$$$");
newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$");
newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$");
newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$");
newXSproto(strcpy(buf, "GetAccountFlag"), XS_Client_GetAccountFlag, file, "$$");
newXSproto(strcpy(buf, "GetDiscSlotBySpellID"), XS_Client_GetDiscSlotBySpellID, file, "$$");
newXSproto(strcpy(buf, "GetDuelTarget"), XS_Client_GetDuelTarget, file, "$");
newXSproto(strcpy(buf, "GetEbonCrystals"), XS_Client_GetEbonCrystals, file, "$");
newXSproto(strcpy(buf, "GetEndurance"), XS_Client_GetEndurance, file, "$");
newXSproto(strcpy(buf, "GetEnduranceRatio"), XS_Client_GetEnduranceRatio, file, "$");
newXSproto(strcpy(buf, "GetEXP"), XS_Client_GetEXP, file, "$");
newXSproto(strcpy(buf, "GetFace"), XS_Client_GetFace, file, "$");
newXSproto(strcpy(buf, "GetFactionLevel"), XS_Client_GetFactionLevel, file, "$$$$$$$$");
newXSproto(strcpy(buf, "GetFeigned"), XS_Client_GetFeigned, file, "$");
newXSproto(strcpy(buf, "GetFreeSpellBookSlot"), XS_Client_GetFreeSpellBookSlot, file, "$;$");
newXSproto(strcpy(buf, "GetGM"), XS_Client_GetGM, file, "$");
newXSproto(strcpy(buf, "GetGroup"), XS_Client_GetGroup, file, "$");
newXSproto(strcpy(buf, "GetGroupPoints"), XS_Client_GetGroupPoints, file, "$");
newXSproto(strcpy(buf, "GetHorseId"), XS_Client_GetHorseId, file, "$");
newXSproto(strcpy(buf, "GetHunger"), XS_Client_GetHunger, file, "$$");
newXSproto(strcpy(buf, "GetThirst"), XS_Client_GetThirst, file, "$$");
newXSproto(strcpy(buf, "SetHunger"), XS_Client_SetHunger, file, "$$");
newXSproto(strcpy(buf, "SetThirst"), XS_Client_SetThirst, file, "$$");
newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$");
newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$");
newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$");
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
newXSproto(strcpy(buf, "ExpeditionMessage"), XS_Client_ExpeditionMessage, file, "$$$");
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$");
newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$");
newXSproto(strcpy(buf, "GetInstanceID"), XS_Client_GetInstanceID, file, "$$");
newXSproto(strcpy(buf, "GetInstrumentMod"), XS_Client_GetInstrumentMod, file, "$$");
newXSproto(strcpy(buf, "GetIP"), XS_Client_GetIP, file, "$");
newXSproto(strcpy(buf, "GetItemAt"), XS_Client_GetItemAt, file, "$$");
newXSproto(strcpy(buf, "GetItemIDAt"), XS_Client_GetItemIDAt, file, "$$");
newXSproto(strcpy(buf, "GetItemInInventory"), XS_Client_GetItemInInventory, file, "$$");
newXSproto(strcpy(buf, "GetLanguageSkill"), XS_Client_GetLanguageSkill, file, "$$");
newXSproto(strcpy(buf, "GetLastName"), XS_Client_GetLastName, file, "$");
newXSproto(strcpy(buf, "GetLDoNLosses"), XS_Client_GetLDoNLosses, file, "$");
newXSproto(strcpy(buf, "GetLDoNLossesTheme"), XS_Client_GetLDoNLossesTheme, file, "$$");
newXSproto(strcpy(buf, "GetLDoNPointsTheme"), XS_Client_GetLDoNPointsTheme, file, "$");
newXSproto(strcpy(buf, "GetLDoNWins"), XS_Client_GetLDoNWins, file, "$");
newXSproto(strcpy(buf, "GetLDoNWinsTheme"), XS_Client_GetLDoNWinsTheme, file, "$$");
newXSproto(strcpy(buf, "GetMaxEndurance"), XS_Client_GetMaxEndurance, file, "$");
newXSproto(strcpy(buf, "GetModCharacterFactionLevel"), XS_Client_GetModCharacterFactionLevel, file, "$$");
newXSproto(strcpy(buf, "GetMoney"), XS_Client_GetMoney, file, "$$$");
newXSproto(strcpy(buf, "GetPVP"), XS_Client_GetPVP, file, "$");
newXSproto(strcpy(buf, "GetPVPPoints"), XS_Client_GetPVPPoints, file, "$");
newXSproto(strcpy(buf, "GetRadiantCrystals"), XS_Client_GetRadiantCrystals, file, "$");
newXSproto(strcpy(buf, "GetRaid"), XS_Client_GetRaid, file, "$");
newXSproto(strcpy(buf, "GetRaidPoints"), XS_Client_GetRaidPoints, file, "$");
newXSproto(strcpy(buf, "GetRawItemAC"), XS_Client_GetRawItemAC, file, "$");
newXSproto(strcpy(buf, "GetRawSkill"), XS_Client_GetRawSkill, file, "$$");
newXSproto(strcpy(buf, "GetSkill"), XS_Client_GetSkill, file, "$$");
newXSproto(strcpy(buf, "GetSkillPoints"), XS_Client_GetSkillPoints, file, "$");
newXSproto(strcpy(buf, "GetSpellBookSlotBySpellID"), XS_Client_GetSpellBookSlotBySpellID, file, "$$");
newXSproto(strcpy(buf, "GetSpentAA"), XS_Client_GetSpentAA, file, "$$");
newXSproto(strcpy(buf, "GetStartZone"), XS_Client_GetStartZone, file, "$");
newXSproto(strcpy(buf, "GetTargetRingX"), XS_Client_GetTargetRingX, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingY"), XS_Client_GetTargetRingY, file, "$$");
newXSproto(strcpy(buf, "GetTargetRingZ"), XS_Client_GetTargetRingZ, file, "$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "CalcEXP"), XS_Client_CalcEXP, file, "$");
newXSproto(strcpy(buf, "GetMoney"), XS_Client_GetMoney, file, "$$$");
newXSproto(strcpy(buf, "GetAccountAge"), XS_Client_GetAccountAge, file, "$");
newXSproto(strcpy(buf, "GetTaskActivityDoneCount"), XS_Client_GetTaskActivityDoneCount, file, "$$$");
newXSproto(strcpy(buf, "GetThirst"), XS_Client_GetThirst, file, "$$");
newXSproto(strcpy(buf, "GetTotalSecondsPlayed"), XS_Client_GetTotalSecondsPlayed, file, "$");
newXSproto(strcpy(buf, "GetWeight"), XS_Client_GetWeight, file, "$");
newXSproto(strcpy(buf, "GMKill"), XS_Client_GMKill, file, "$");
newXSproto(strcpy(buf, "GoFish"), XS_Client_GoFish, file, "$");
newXSproto(strcpy(buf, "GrantAlternateAdvancementAbility"), XS_Client_GrantAlternateAdvancementAbility, file, "$$$;$");
newXSproto(strcpy(buf, "GuildID"), XS_Client_GuildID, file, "$");
newXSproto(strcpy(buf, "GuildRank"), XS_Client_GuildRank, file, "$");
newXSproto(strcpy(buf, "HasSkill"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "HasSpellScribed"), XS_Client_HasSkill, file, "$$");
newXSproto(strcpy(buf, "HasZoneFlag"), XS_Client_HasZoneFlag, file, "$$");
newXSproto(strcpy(buf, "Hungry"), XS_Client_Hungry, file, "$");
newXSproto(strcpy(buf, "IncreaseLanguageSkill"), XS_Client_IncreaseLanguageSkill, file, "$$;$");
newXSproto(strcpy(buf, "IncreaseSkill"), XS_Client_IncreaseSkill, file, "$$;$");
newXSproto(strcpy(buf, "IncrementAA"), XS_Client_IncrementAA, file, "$$");
newXSproto(strcpy(buf, "IncStats"), XS_Client_IncStats, file, "$$$");
newXSproto(strcpy(buf, "InZone"), XS_Client_InZone, file, "$");
newXSproto(strcpy(buf, "IsBecomeNPC"), XS_Client_IsBecomeNPC, file, "$");
newXSproto(strcpy(buf, "IsDueling"), XS_Client_IsDueling, file, "$");
newXSproto(strcpy(buf, "IsGrouped"), XS_Client_IsGrouped, file, "$");
newXSproto(strcpy(buf, "IsLD"), XS_Client_IsLD, file, "$");
newXSproto(strcpy(buf, "IsMedding"), XS_Client_IsMedding, file, "$");
newXSproto(strcpy(buf, "IsRaidGrouped"), XS_Client_IsRaidGrouped, file, "$");
newXSproto(strcpy(buf, "IsSitting"), XS_Client_IsSitting, file, "$");
newXSproto(strcpy(buf, "IsTaskActive"), XS_Client_IsTaskActive, file, "$$");
newXSproto(strcpy(buf, "IsTaskActivityActive"), XS_Client_IsTaskActivityActive, file, "$$$");
newXSproto(strcpy(buf, "IsTaskCompleted"), XS_Client_IsTaskCompleted, file, "$$");
newXSproto(strcpy(buf, "KeyRingAdd"), XS_Client_KeyRingAdd, file, "$$");
newXSproto(strcpy(buf, "KeyRingCheck"), XS_Client_KeyRingCheck, file, "$$");
newXSproto(strcpy(buf, "Kick"), XS_Client_Kick, file, "$");
newXSproto(strcpy(buf, "LearnRecipe"), XS_Client_LearnRecipe, file, "$$");
newXSproto(strcpy(buf, "LeaveGroup"), XS_Client_LeaveGroup, file, "$");
newXSproto(strcpy(buf, "LoadZoneFlags"), XS_Client_LoadZoneFlags, file, "$");
newXSproto(strcpy(buf, "MarkCompassLoc"), XS_Client_MarkCompassLoc, file, "$$$$");
newXSproto(strcpy(buf, "MaxSkill"), XS_Client_MaxSkill, file, "$$;$$");
newXSproto(strcpy(buf, "MemSpell"), XS_Client_MemSpell, file, "$$$;$");
newXSproto(strcpy(buf, "MovePC"), XS_Client_MovePC, file, "$$$$$$");
newXSproto(strcpy(buf, "MovePCInstance"), XS_Client_MovePCInstance, file, "$$$$$$$");
newXSproto(strcpy(buf, "NPCSpawn"), XS_Client_NPCSpawn, file, "$$$;$");
newXSproto(strcpy(buf, "NukeItem"), XS_Client_NukeItem, file, "$$;$");
newXSproto(strcpy(buf, "OpenLFGuildWindow"), XS_Client_OpenLFGuildWindow, file, "$");
newXSproto(strcpy(buf, "PlayMP3"), XS_Client_PlayMP3, file, "$;$");
newXSproto(strcpy(buf, "Popup2"), XS_Client_Popup2, file, "$$$;$$$$$$$");
newXSproto(strcpy(buf, "QuestReward"), XS_Client_QuestReward, file, "$$;$$$$$$$");
newXSproto(strcpy(buf, "ReadBook"), XS_Client_ReadBook, file, "$$$");
newXSproto(strcpy(buf, "RefundAA"), XS_Client_RefundAA, file, "$$");
newXSproto(strcpy(buf, "RemoveNoRent"), XS_Client_RemoveNoRent, file, "$");
newXSproto(strcpy(buf, "ResetAA"), XS_Client_ResetAA, file, "$");
newXSproto(strcpy(buf, "ResetTrade"), XS_Client_ResetTrade, file, "$");
newXSproto(strcpy(buf, "Save"), XS_Client_Save, file, "$$");
newXSproto(strcpy(buf, "SaveBackup"), XS_Client_SaveBackup, file, "$");
newXSproto(strcpy(buf, "ScribeSpell"), XS_Client_ScribeSpell, file, "$$$;$");
newXSproto(strcpy(buf, "SendColoredText"), XS_Client_SendColoredText, file, "$$$");
newXSproto(strcpy(buf, "SendMarqueeMessage"), XS_Client_SendMarqueeMessage, file, "$$$$$$$");
newXSproto(strcpy(buf, "SendOPTranslocateConfirm"), XS_Client_SendOPTranslocateConfirm, file, "$$$");
newXSproto(strcpy(buf, "SendSound"), XS_Client_SendSound, file, "$");
newXSproto(strcpy(buf, "SendSpellAnim"), XS_Client_SendSpellAnim, file, "$$$");
newXSproto(strcpy(buf, "SendTargetCommand"), XS_Client_SendTargetCommand, file, "$$");
newXSproto(strcpy(buf, "SendWebLink"), XS_Client_SendWebLink, file, "$:$");
newXSproto(strcpy(buf, "SendZoneFlagInfo"), XS_Client_SendZoneFlagInfo, file, "$$");
newXSproto(strcpy(buf, "SetAAPoints"), XS_Client_SetAAPoints, file, "$$");
newXSproto(strcpy(buf, "SetAATitle"), XS_Client_SetAATitle, file, "$$;$");
newXSproto(strcpy(buf, "SetAccountFlag"), XS_Client_SetAccountFlag, file, "$$");
newXSproto(strcpy(buf, "SetBaseClass"), XS_Client_SetBaseClass, file, "$$");
newXSproto(strcpy(buf, "SetBaseGender"), XS_Client_SetBaseGender, file, "$$");
newXSproto(strcpy(buf, "SetBaseRace"), XS_Client_SetBaseRace, file, "$$");
newXSproto(strcpy(buf, "SetBecomeNPC"), XS_Client_SetBecomeNPC, file, "$$");
newXSproto(strcpy(buf, "SetBecomeNPCLevel"), XS_Client_SetBecomeNPCLevel, file, "$$");
newXSproto(strcpy(buf, "SetBindPoint"), XS_Client_SetBindPoint, file, "$;$$$$$");
newXSproto(strcpy(buf, "SetConsumption"), XS_Client_SetConsumption, file, "$$$");
newXSproto(strcpy(buf, "SetCustomItemData"), XS_Client_SetCustomItemData, file, "$$$$");
newXSproto(strcpy(buf, "SetDeity"), XS_Client_SetDeity, file, "$$");
newXSproto(strcpy(buf, "SetDueling"), XS_Client_SetDueling, file, "$$");
newXSproto(strcpy(buf, "SetDuelTarget"), XS_Client_SetDuelTarget, file, "$$");
newXSproto(strcpy(buf, "SetEndurance"), XS_Client_SetEndurance, file, "$$");
newXSproto(strcpy(buf, "SetEXP"), XS_Client_SetEXP, file, "$$$;$");
newXSproto(strcpy(buf, "SetFactionLevel"), XS_Client_SetFactionLevel, file, "$$$$$$");
newXSproto(strcpy(buf, "SetFactionLevel2"), XS_Client_SetFactionLevel2, file, "$$$$$$$");
newXSproto(strcpy(buf, "SetFeigned"), XS_Client_SetFeigned, file, "$$");
newXSproto(strcpy(buf, "SetGM"), XS_Client_SetGM, file, "$$");
newXSproto(strcpy(buf, "SetHorseId"), XS_Client_SetHorseId, file, "$$");
newXSproto(strcpy(buf, "SetHunger"), XS_Client_SetHunger, file, "$$");
newXSproto(strcpy(buf, "SetLanguageSkill"), XS_Client_SetLanguageSkill, file, "$$$");
newXSproto(strcpy(buf, "SetMaterial"), XS_Client_SetMaterial, file, "$$$");
newXSproto(strcpy(buf, "SetPrimaryWeaponOrnamentation"), XS_Client_SetPrimaryWeaponOrnamentation, file, "$$");
newXSproto(strcpy(buf, "SetPVP"), XS_Client_SetPVP, file, "$$");
newXSproto(strcpy(buf, "SetSecondaryWeaponOrnamentation"), XS_Client_SetSecondaryWeaponOrnamentation, file, "$$");
newXSproto(strcpy(buf, "SetSkill"), XS_Client_SetSkill, file, "$$$");
newXSproto(strcpy(buf, "SetSkillPoints"), XS_Client_SetSkillPoints, file, "$$");
newXSproto(strcpy(buf, "SetStartZone"), XS_Client_SetStartZone, file, "$$");
newXSproto(strcpy(buf, "SetStats"), XS_Client_SetStats, file, "$$$");
newXSproto(strcpy(buf, "SetThirst"), XS_Client_SetThirst, file, "$$");
newXSproto(strcpy(buf, "SetTint"), XS_Client_SetTint, file, "$$$");
newXSproto(strcpy(buf, "SetTitleSuffix"), XS_Client_SetTitleSuffix, file, "$$;$");
newXSproto(strcpy(buf, "SetZoneFlag"), XS_Client_SetZoneFlag, file, "$$");
newXSproto(strcpy(buf, "SignalClient"), XS_Client_SignalClient, file, "$");
newXSproto(strcpy(buf, "SilentMessage"), XS_Client_SilentMessage, file, "$$");
newXSproto(strcpy(buf, "SlotConvert2"), XS_Client_SlotConvert2, file, "$$");
newXSproto(strcpy(buf, "Stand"), XS_Client_Stand, file, "$");
newXSproto(strcpy(buf, "SummonItem"), XS_Client_SummonItem, file, "$$;$$$$$$$$");
newXSproto(strcpy(buf, "TakeMoneyFromPP"), XS_Client_TakeMoneyFromPP, file, "$$;$");
newXSproto(strcpy(buf, "TGB"), XS_Client_TGB, file, "$");
newXSproto(strcpy(buf, "Thirsty"), XS_Client_Thirsty, file, "$");
newXSproto(strcpy(buf, "TrainDiscBySpellID"), XS_Client_TrainDiscBySpellID, file, "$$");
newXSproto(strcpy(buf, "Undye"), XS_Client_Undye, file, "$");
newXSproto(strcpy(buf, "UnFreeze"), XS_Client_UnFreeze, file, "$");
newXSproto(strcpy(buf, "UnmemSpell"), XS_Client_UnmemSpell, file, "$$;$");
newXSproto(strcpy(buf, "UnmemSpellAll"), XS_Client_UnmemSpellAll, file, "$;$");
newXSproto(strcpy(buf, "UnmemSpellBySpellID"), XS_Client_UnmemSpellBySpellID, file, "$$");
newXSproto(strcpy(buf, "UnscribeSpell"), XS_Client_UnscribeSpell, file, "$$;$");
newXSproto(strcpy(buf, "UnscribeSpellAll"), XS_Client_UnscribeSpellAll, file, "$;$");
newXSproto(strcpy(buf, "UntrainDisc"), XS_Client_UntrainDisc, file, "$$;$");
newXSproto(strcpy(buf, "UntrainDiscAll"), XS_Client_UntrainDiscAll, file, "$;$");
newXSproto(strcpy(buf, "UpdateAdmin"), XS_Client_UpdateAdmin, file, "$;$");
newXSproto(strcpy(buf, "UpdateGroupAAs"), XS_Client_UpdateGroupAAs, file, "$$$");
newXSproto(strcpy(buf, "UpdateLDoNPoints"), XS_Client_UpdateLDoNPoints, file, "$$$");
newXSproto(strcpy(buf, "UpdateTaskActivity"), XS_Client_UpdateTaskActivity, file, "$$$$;$");
newXSproto(strcpy(buf, "UpdateWho"), XS_Client_UpdateWho, file, "$;$");
newXSproto(strcpy(buf, "UseDiscipline"), XS_Client_UseDiscipline, file, "$$$");
newXSproto(strcpy(buf, "WorldKick"), XS_Client_WorldKick, file, "$");
XSRETURN_YES;
}
+4 -4
View File
@@ -1355,7 +1355,7 @@ XS(XS_EntityList_OpenDoorsNear) {
Perl_croak(aTHX_ "Usage: EntityList::OpenDoorsNear(THIS, NPC* opener)");
{
EntityList *THIS;
NPC *opener;
Mob *opener;
if (sv_derived_from(ST(0), "EntityList")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
@@ -1365,11 +1365,11 @@ XS(XS_EntityList_OpenDoorsNear) {
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (sv_derived_from(ST(1), "NPC")) {
if (sv_derived_from(ST(1), "Mob")) {
IV tmp = SvIV((SV *) SvRV(ST(1)));
opener = INT2PTR(NPC *, tmp);
opener = INT2PTR(Mob *, tmp);
} else
Perl_croak(aTHX_ "opener is not of type NPC");
Perl_croak(aTHX_ "opener is not of type Mob");
if (opener == nullptr)
Perl_croak(aTHX_ "opener is nullptr, avoiding crash.");
+95 -98
View File
@@ -639,7 +639,7 @@ XS(XS_Mob_GetEquipment) {
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
RETVAL = THIS->GetEquipment(material_slot);
RETVAL = THIS->GetEquippedItemFromTextureSlot(material_slot);
XSprePUSH;
PUSHi((IV) RETVAL);
}
@@ -1166,55 +1166,6 @@ XS(XS_Mob_GMMove) {
XSRETURN_EMPTY;
}
XS(XS_Mob_SendPosUpdate); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SendPosUpdate) {
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: Mob::SendPosUpdate(THIS, [uint8 send_to_self = 0])");
{
Mob *THIS;
uint8 iSendToSelf;
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 < 2)
iSendToSelf = 0;
else {
iSendToSelf = (uint8) SvUV(ST(1));
}
THIS->SendPositionUpdate(iSendToSelf);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_SendPosition); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SendPosition) {
dXSARGS;
if (items != 1)
Perl_croak(aTHX_ "Usage: Mob::SendPosition(THIS)");
{
Mob *THIS;
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.");
THIS->SendPosition();
}
XSRETURN_EMPTY;
}
XS(XS_Mob_HasProcs); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_HasProcs) {
dXSARGS;
@@ -3786,7 +3737,7 @@ XS(XS_Mob_CastSpell) {
Mob *THIS;
uint16 spell_id = (uint16) SvUV(ST(1));
uint16 target_id = (uint16) SvUV(ST(2));
EQEmu::CastingSlot slot;
EQEmu::spells::CastingSlot slot;
int32 casttime;
int32 mana_cost;
int16 resist_adjust;
@@ -3800,9 +3751,9 @@ XS(XS_Mob_CastSpell) {
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 4)
slot = EQEmu::CastingSlot::Item;
slot = EQEmu::spells::CastingSlot::Item;
else {
slot = static_cast<EQEmu::CastingSlot>(SvUV(ST(3)));
slot = static_cast<EQEmu::spells::CastingSlot>(SvUV(ST(3)));
}
if (items < 5)
@@ -3876,7 +3827,7 @@ XS(XS_Mob_SpellFinished) {
resist_diff = spells[spell_id].ResistDiff;
}
THIS->SpellFinished(spell_id, spell_target, EQEmu::CastingSlot::Item, mana_cost, -1, resist_diff);
THIS->SpellFinished(spell_id, spell_target, EQEmu::spells::CastingSlot::Item, mana_cost, -1, resist_diff);
}
XSRETURN_EMPTY;
}
@@ -5526,20 +5477,71 @@ XS(XS_Mob_CalculateHeadingToTarget) {
XSRETURN(1);
}
XS(XS_Mob_CalculateNewPosition); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_CalculateNewPosition) {
XS(XS_Mob_RunTo); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_RunTo) {
dXSARGS;
if (items < 5 || items > 6)
if (items < 4 || items > 5)
Perl_croak(aTHX_
"Usage: Mob::CalculateNewPosition(THIS, float x, float y, float z, float speed, [bool check_z = false])");
"Usage: Mob::RunTo(THIS, float x, float y, float z)");
{
Mob *THIS;
float x = (float)SvNV(ST(1));
float y = (float)SvNV(ST(2));
float z = (float)SvNV(ST(3));
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.");
THIS->RunTo(x, y, z);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_WalkTo); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_WalkTo) {
dXSARGS;
if (items < 4 || items > 5)
Perl_croak(aTHX_
"Usage: Mob::WalkTo(THIS, float x, float y, float z)");
{
Mob *THIS;
float x = (float)SvNV(ST(1));
float y = (float)SvNV(ST(2));
float z = (float)SvNV(ST(3));
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.");
THIS->WalkTo(x, y, z);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_NavigateTo); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_NavigateTo) {
dXSARGS;
if (items < 4 || items > 5)
Perl_croak(aTHX_
"Usage: Mob::NavigateTo(THIS, float x, float y, float z)");
{
Mob *THIS;
bool RETVAL;
float x = (float) SvNV(ST(1));
float y = (float) SvNV(ST(2));
float z = (float) SvNV(ST(3));
float speed = (float) SvNV(ST(4));
bool checkZ;
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
@@ -5549,17 +5551,34 @@ XS(XS_Mob_CalculateNewPosition) {
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 6)
checkZ = false;
else {
checkZ = (bool) SvTRUE(ST(5));
}
RETVAL = THIS->CalculateNewPosition(x, y, z, speed, checkZ);
ST(0) = boolSV(RETVAL);
sv_2mortal(ST(0));
THIS->NavigateTo(x, y, z);
}
XSRETURN(1);
XSRETURN_EMPTY;
}
XS(XS_Mob_StopNavigation); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_StopNavigation) {
dXSARGS;
if (items < 5 || items > 6)
Perl_croak(aTHX_
"Usage: Mob::StopNavigation(THIS)");
{
Mob *THIS;
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.");
THIS->StopNavigation();
}
XSRETURN_EMPTY;
}
XS(XS_Mob_CalculateDistance); /* prototype to pass -Wmissing-prototypes */
@@ -6652,10 +6671,10 @@ XS(XS_Mob_SetFlyMode); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetFlyMode) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetFlyMode(THIS, uint8 flymode[0|1|2|3])");
Perl_croak(aTHX_ "Usage: Mob::SetFlyMode(THIS, uint8 flymode[0|1|2|3|4|5])");
{
Mob *THIS;
uint8 flymode = (uint8) SvIV(ST(1));
GravityBehavior flymode = (GravityBehavior) SvIV(ST(1));
if (sv_derived_from(ST(0), "Mob")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
@@ -7275,28 +7294,6 @@ XS(XS_Mob_SetLD) {
XSRETURN_EMPTY;
}
XS(XS_Mob_SetTargetDestSteps); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetTargetDestSteps) {
dXSARGS;
if (items != 2)
Perl_croak(aTHX_ "Usage: Mob::SetTargetDestSteps(THIS, uint8 target_steps)");
{
Mob *THIS;
uint8 target_steps = (uint8) SvIV(ST(1));
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.");
THIS->SetTargetDestSteps(target_steps);
}
XSRETURN_EMPTY;
}
XS(XS_Mob_SetTargetable); /* prototype to pass -Wmissing-prototypes */
XS(XS_Mob_SetTargetable) {
dXSARGS;
@@ -8556,8 +8553,6 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "DoAnim"), XS_Mob_DoAnim, file, "$$;$");
newXSproto(strcpy(buf, "ChangeSize"), XS_Mob_ChangeSize, file, "$$;$");
newXSproto(strcpy(buf, "GMMove"), XS_Mob_GMMove, file, "$$$$;$");
newXSproto(strcpy(buf, "SendPosUpdate"), XS_Mob_SendPosUpdate, file, "$;$");
newXSproto(strcpy(buf, "SendPosition"), XS_Mob_SendPosition, file, "$");
newXSproto(strcpy(buf, "HasProcs"), XS_Mob_HasProcs, file, "$");
newXSproto(strcpy(buf, "IsInvisible"), XS_Mob_IsInvisible, file, "$;$");
newXSproto(strcpy(buf, "SetInvisible"), XS_Mob_SetInvisible, file, "$$");
@@ -8718,7 +8713,10 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "WipeHateList"), XS_Mob_WipeHateList, file, "$");
newXSproto(strcpy(buf, "CheckAggro"), XS_Mob_CheckAggro, file, "$$");
newXSproto(strcpy(buf, "CalculateHeadingToTarget"), XS_Mob_CalculateHeadingToTarget, file, "$$$");
newXSproto(strcpy(buf, "CalculateNewPosition"), XS_Mob_CalculateNewPosition, file, "$$$$$;$");
newXSproto(strcpy(buf, "RunTo"), XS_Mob_RunTo, file, "$$$$");
newXSproto(strcpy(buf, "WalkTo"), XS_Mob_WalkTo, file, "$$$$");
newXSproto(strcpy(buf, "NavigateTo"), XS_Mob_NavigateTo, file, "$$$$");
newXSproto(strcpy(buf, "StopNavigation"), XS_Mob_StopNavigation, file, "$");
newXSproto(strcpy(buf, "CalculateDistance"), XS_Mob_CalculateDistance, file, "$$$$");
newXSproto(strcpy(buf, "SendTo"), XS_Mob_SendTo, file, "$$$$");
newXSproto(strcpy(buf, "SendToFixZ"), XS_Mob_SendToFixZ, file, "$$$$");
@@ -8781,7 +8779,6 @@ XS(boot_Mob) {
newXSproto(strcpy(buf, "SetBodyType"), XS_Mob_SetBodyType, file, "$$;$");
newXSproto(strcpy(buf, "SetDeltas"), XS_Mob_SetDeltas, file, "$$$$$");
newXSproto(strcpy(buf, "SetLD"), XS_Mob_SetLD, file, "$$");
newXSproto(strcpy(buf, "SetTargetDestSteps"), XS_Mob_SetTargetDestSteps, file, "$$");
newXSproto(strcpy(buf, "SetTargetable"), XS_Mob_SetTargetable, file, "$$");
newXSproto(strcpy(buf, "MakeTempPet"), XS_Mob_MakeTempPet, file, "$$;$$$$");
newXSproto(strcpy(buf, "ModSkillDmgTaken"), XS_Mob_ModSkillDmgTaken, file, "$$$");
+8 -11
View File
@@ -1341,11 +1341,14 @@ XS(XS_NPC_NextGuardPosition) {
XS(XS_NPC_SaveGuardSpot); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_SaveGuardSpot) {
dXSARGS;
if (items < 1 || items > 2)
Perl_croak(aTHX_ "Usage: NPC::SaveGuardSpot(THIS, [bool clear_guard_spot = false])");
if (items != 5)
Perl_croak(aTHX_ "Usage: NPC::SaveGuardSpot(THIS, x, y, z, heading)");
{
NPC *THIS;
bool iClearGuardSpot;
float x = (float)SvNV(ST(1));
float y = (float)SvNV(ST(2));
float z = (float)SvNV(ST(3));
float heading = (float)SvNV(ST(4));
if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV *) SvRV(ST(0)));
@@ -1355,13 +1358,7 @@ XS(XS_NPC_SaveGuardSpot) {
if (THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 2)
iClearGuardSpot = false;
else {
iClearGuardSpot = (bool) SvTRUE(ST(1));
}
THIS->SaveGuardSpot(iClearGuardSpot);
THIS->SaveGuardSpot(glm::vec4(x, y, z, heading));
}
XSRETURN_EMPTY;
}
@@ -2449,7 +2446,7 @@ XS(boot_NPC) {
newXSproto(strcpy(buf, "PauseWandering"), XS_NPC_PauseWandering, file, "$$");
newXSproto(strcpy(buf, "MoveTo"), XS_NPC_MoveTo, file, "$$$$");
newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$");
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$");
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$$$$$");
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$");
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$");
newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$");
+3 -3
View File
@@ -240,7 +240,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
if(scale_power > 0)
{
npc_type->max_hp *= (1 + scale_power);
npc_type->cur_hp = npc_type->max_hp;
npc_type->current_hp = npc_type->max_hp;
npc_type->AC *= (1 + scale_power);
npc_type->level += 1 + ((int)act_power / 25) > npc_type->level + RuleR(Pets, PetPowerLevelCap) ? RuleR(Pets, PetPowerLevelCap) : 1 + ((int)act_power / 25); // gains an additional level for every 25 pet power
npc_type->min_dmg = (npc_type->min_dmg * (1 + (scale_power / 2)));
@@ -255,7 +255,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
if (MaxHP){
npc_type->max_hp += (npc_type->max_hp*MaxHP)/100;
npc_type->cur_hp = npc_type->max_hp;
npc_type->current_hp = npc_type->max_hp;
}
//TODO: think about regen (engaged vs. not engaged)
@@ -425,7 +425,7 @@ void Mob::MakePoweredPet(uint16 spell_id, const char* pettype, int16 petpower,
into walls or objects (+10), this sometimes creates the "ghost" effect. I changed to +2 (as close as I
could get while it still looked good). I also noticed this can happen if an NPC is spawned on the same spot of another or in a related bad spot.*/
Pet::Pet(NPCType *type_data, Mob *owner, PetType type, uint16 spell_id, int16 power)
: NPC(type_data, 0, owner->GetPosition() + glm::vec4(2.0f, 2.0f, 0.0f, 0.0f), FlyMode3)
: NPC(type_data, 0, owner->GetPosition() + glm::vec4(2.0f, 2.0f, 0.0f, 0.0f), GravityBehavior::Water)
{
GiveNPCTypeData(type_data);
typeofpet = type;
+93
View File
@@ -5,6 +5,8 @@
#include <cmath>
#include "../common/string_util.h"
static const float position_eps = 0.0001f;
std::string to_string(const glm::vec4 &position) {
return StringFormat("(%.3f, %.3f, %.3f, %.3f)", position.x,position.y,position.z,position.w);
}
@@ -160,3 +162,94 @@ float GetReciprocalHeading(const float heading)
return result;
}
bool IsHeadingEqual(const float h1, const float h2)
{
return std::abs(h2 - h1) < 0.01f;
}
bool IsPositionEqual(const glm::vec2 &p1, const glm::vec2 &p2)
{
return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps;
}
bool IsPositionEqual(const glm::vec3 &p1, const glm::vec3 &p2)
{
return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < position_eps;
}
bool IsPositionEqual(const glm::vec4 &p1, const glm::vec4 &p2)
{
return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < position_eps;
}
bool IsPositionEqualWithinCertainZ(const glm::vec3 &p1, const glm::vec3 &p2, float z_eps) {
return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < z_eps;
}
bool IsPositionEqualWithinCertainZ(const glm::vec4 &p1, const glm::vec4 &p2, float z_eps) {
return std::abs(p1.x - p2.x) < position_eps && std::abs(p1.y - p2.y) < position_eps && std::abs(p1.z - p2.z) < z_eps;
}
bool IsPositionWithinSimpleCylinder(const glm::vec3 &p1, const glm::vec3 &cylinder_center, float cylinder_radius, float cylinder_height)
{
//If we're outside the height of cylinder then we're not in it (duh)
auto d = std::abs(p1.z - cylinder_center.z);
if (d > cylinder_height / 2.0) {
return false;
}
glm::vec2 p1d(p1.x, p1.y);
glm::vec2 ccd(cylinder_center.x, cylinder_center.y);
//If we're outside the radius of the cylinder then we're not in it (also duh)
d = Distance(p1d, ccd);
if (d > cylinder_radius) {
return false;
}
return true;
}
bool IsPositionWithinSimpleCylinder(const glm::vec4 &p1, const glm::vec4 &cylinder_center, float cylinder_radius, float cylinder_height)
{
//If we're outside the height of cylinder then we're not in it (duh)
auto d = std::abs(p1.z - cylinder_center.z);
if (d > cylinder_height / 2.0) {
return false;
}
glm::vec2 p1d(p1.x, p1.y);
glm::vec2 ccd(cylinder_center.x, cylinder_center.y);
//If we're outside the radius of the cylinder then we're not in it (also duh)
d = Distance(p1d, ccd);
if (d > cylinder_radius) {
return false;
}
return true;
}
float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float y2)
{
float y_diff = std::abs(y1 - y2);
float x_diff = std::abs(x1 - x2);
if (y_diff < 0.0000009999999974752427)
y_diff = 0.0000009999999974752427;
float angle = atan2(x_diff, y_diff) * 180.0f * 0.3183099014828645f; // angle, nice "pi"
// return the right thing based on relative quadrant
// I'm sure this could be improved for readability, but whatever
if (y1 >= y2) {
if (x2 >= x1)
return (90.0f - angle + 90.0f) * 511.5f * 0.0027777778f;
if (x2 <= x1)
return (angle + 180.0f) * 511.5f * 0.0027777778f;
}
if (y1 > y2 || x2 > x1)
return angle * 511.5f * 0.0027777778f;
else
return (90.0f - angle + 270.0f) * 511.5f * 0.0027777778f;
}
+13
View File
@@ -50,4 +50,17 @@ float DistanceSquaredNoZ(const glm::vec4& point1, const glm::vec4& point2);
float GetReciprocalHeading(const glm::vec4& point1);
float GetReciprocalHeading(const float heading);
bool IsHeadingEqual(const float h1, const float h2);
bool IsPositionEqual(const glm::vec2 &p1, const glm::vec2 &p2);
bool IsPositionEqual(const glm::vec3 &p1, const glm::vec3 &p2);
bool IsPositionEqual(const glm::vec4 &p1, const glm::vec4 &p2);
bool IsPositionEqualWithinCertainZ(const glm::vec3 &p1, const glm::vec3 &p2, float z_eps);
bool IsPositionEqualWithinCertainZ(const glm::vec4 &p1, const glm::vec4 &p2, float z_eps);
bool IsPositionWithinSimpleCylinder(const glm::vec3 &p1, const glm::vec3 &cylinder_center, float cylinder_radius, float cylinder_height);
bool IsPositionWithinSimpleCylinder(const glm::vec4 &p1, const glm::vec4 &cylinder_center, float cylinder_radius, float cylinder_height);
float CalculateHeadingAngleBetweenPositions(float x1, float y1, float x2, float y2);
#endif
+183 -168
View File
@@ -208,7 +208,7 @@ Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& p
const NPCType* tmp = 0;
if (tmp = database.LoadNPCTypesData(npc_type))
{
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
auto npc = new NPC(tmp, nullptr, position, GravityBehavior::Water);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
@@ -217,7 +217,7 @@ Mob* QuestManager::spawn2(int npc_type, int grid, int unused, const glm::vec4& p
{
npc->AssignWaypoints(grid);
}
npc->SendPositionUpdate();
return npc;
}
return nullptr;
@@ -232,7 +232,7 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
const NPCType* tmp = 0;
if (tmp = database.LoadNPCTypesData(npc_type))
{
auto npc = new NPC(tmp, nullptr, position, FlyMode3);
auto npc = new NPC(tmp, nullptr, position, GravityBehavior::Water);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
@@ -241,7 +241,7 @@ Mob* QuestManager::unique_spawn(int npc_type, int grid, int unused, const glm::v
{
npc->AssignWaypoints(grid);
}
npc->SendPositionUpdate();
return npc;
}
return nullptr;
@@ -308,22 +308,22 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
found_spawn->SetCurrentNPCID(npcid);
auto position = glm::vec4(found_spawn->GetX(), found_spawn->GetY(), found_spawn->GetZ(), found_spawn->GetHeading());
auto npc = new NPC(tmp, found_spawn, position, FlyMode3);
auto npc = new NPC(tmp, found_spawn, position, GravityBehavior::Water);
found_spawn->SetNPCPointer(npc);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
npc->SetSp2(found_spawn->SpawnGroupID());
entity_list.AddNPC(npc);
entity_list.LimitAddNPC(npc);
found_spawn->SetNPCPointer(npc);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
npc->SetSp2(found_spawn->SpawnGroupID());
entity_list.AddNPC(npc);
entity_list.LimitAddNPC(npc);
if (sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay &&
sg->min_delay)
npc->AI_SetRoambox(sg->roamdist, sg->roambox[0], sg->roambox[1], sg->roambox[2], sg->roambox[3],
sg->delay, sg->min_delay);
if (zone->InstantGrids()) {
found_spawn->LoadGrid();
if (sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay &&
sg->min_delay)
npc->AI_SetRoambox(sg->roamdist, sg->roambox[0], sg->roambox[1], sg->roambox[2], sg->roambox[3],
sg->delay, sg->min_delay);
if (zone->InstantGrids()) {
found_spawn->LoadGrid();
}
return npc;
@@ -371,14 +371,14 @@ void QuestManager::castspell(int spell_id, int target_id) {
if (owner) {
Mob *tgt = entity_list.GetMob(target_id);
if(tgt != nullptr)
owner->SpellFinished(spell_id, tgt, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
owner->SpellFinished(spell_id, tgt, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
void QuestManager::selfcast(int spell_id) {
QuestManagerCurrentQuestVars();
if (initiator)
initiator->SpellFinished(spell_id, initiator, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
initiator->SpellFinished(spell_id, initiator, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
void QuestManager::addloot(int item_id, int charges, bool equipitem, int aug1, int aug2, int aug3, int aug4, int aug5, int aug6) {
@@ -975,126 +975,181 @@ void QuestManager::permagender(int gender_id) {
uint16 QuestManager::scribespells(uint8 max_level, uint8 min_level) {
QuestManagerCurrentQuestVars();
uint16 book_slot, count;
uint16 spell_id;
int book_slot = initiator->GetNextAvailableSpellBookSlot();
int spell_id = 0;
int count = 0;
uint32 char_id = initiator->CharacterID();
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
bool SpellGlobalCheckResult = 0;
bool SpellBucketCheckResult = 0;
bool SpellGlobalCheckResult = false;
bool SpellBucketCheckResult = false;
for ( ; spell_id < SPDAT_RECORDS && book_slot < EQEmu::spells::SPELLBOOK_SIZE; ++spell_id) {
if (book_slot == -1) {
initiator->Message(
13,
"Unable to scribe spell %s (%i) to spellbook: no more spell book slots available.",
((spell_id >= 0 && spell_id < SPDAT_RECORDS) ? spells[spell_id].name : "Out-of-range"),
spell_id
);
break;
}
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return count;
}
if (book_slot < 0 || book_slot >= EQEmu::spells::SPELLBOOK_SIZE) {
initiator->Message(13, "FATAL ERROR: Book slot out-of-range (slot: %i, min: 0, max: %i)", book_slot, EQEmu::spells::SPELLBOOK_SIZE);
return count;
}
for(spell_id = 0, book_slot = initiator->GetNextAvailableSpellBookSlot(), count = 0; spell_id < SPDAT_RECORDS && book_slot < MAX_PP_SPELLBOOK; spell_id++, book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot))
{
if
(
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
spells[spell_id].skill != 52 &&
spells[spell_id].effectid[EFFECT_COUNT - 1] != 10
)
{
if (book_slot == -1) //no more book slots
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if(!IsDiscipline(spell_id) && !initiator->HasSpellScribed(spell_id)) { //isn't a discipline & we don't already have it scribed
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
if (spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return count;
}
if (!IsDiscipline(spell_id_) && !initiator->HasSpellScribed(spell_id)) { // isn't a discipline & we don't already have it scribed
if (SpellGlobalRule) {
// Bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
// bool to see if the character has the required QGlobal to scribe it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
if (SpellGlobalCheckResult) {
initiator->ScribeSpell(spell_id, book_slot);
count++;
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
} else if (SpellBucketRule) {
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
}
else if (SpellBucketRule) {
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
if (SpellBucketCheckResult) {
initiator->ScribeSpell(spell_id, book_slot);
count++;
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
} else {
initiator->ScribeSpell(spell_id, book_slot);
count++;
}
else {
initiator->ScribeSpell(spell_id_, book_slot);
++count;
}
}
break;
}
book_slot = initiator->GetNextAvailableSpellBookSlot(book_slot);
}
return count; //how many spells were scribed successfully
return count; // how many spells were scribed successfully
}
uint16 QuestManager::traindiscs(uint8 max_level, uint8 min_level) {
QuestManagerCurrentQuestVars();
uint16 count;
uint16 spell_id;
int spell_id = 0;
int count = 0;
uint32 char_id = initiator->CharacterID();
bool SpellGlobalRule = RuleB(Spells, EnableSpellGlobals);
bool SpellBucketRule = RuleB(Spells, EnableSpellBuckets);
bool SpellGlobalCheckResult = 0;
bool SpellBucketCheckResult = 0;
bool SpellGlobalCheckResult = false;
bool SpellBucketCheckResult = false;
for(spell_id = 0, count = 0; spell_id < SPDAT_RECORDS; spell_id++)
{
if
(
spells[spell_id].classes[WARRIOR] != 0 && //check if spell exists
spells[spell_id].classes[initiator->GetPP().class_-1] <= max_level && //maximum level
spells[spell_id].classes[initiator->GetPP().class_-1] >= min_level && //minimum level
spells[spell_id].skill != 52 &&
( !RuleB(Spells, UseCHAScribeHack) || spells[spell_id].effectid[EFFECT_COUNT - 1] != 10 )
)
{
if(IsDiscipline(spell_id)){
//we may want to come up with a function like Client::GetNextAvailableSpellBookSlot() to help speed this up a little
for(uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
if(initiator->GetPP().disciplines.values[r] == spell_id) {
initiator->Message(13, "You already know this discipline.");
break; //continue the 1st loop
}
else if(initiator->GetPP().disciplines.values[r] == 0) {
if (SpellGlobalRule) {
// Bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id, char_id);
if (SpellGlobalCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
}
break; //continue the 1st loop
} else if (SpellBucketRule) {
// Bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id, char_id);
if (SpellBucketCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
initiator->Message(0, "You have learned a new discipline!");
count++;
}
break;
}
else {
initiator->GetPP().disciplines.values[r] = spell_id;
database.SaveCharacterDisc(char_id, r, spell_id);
initiator->SendDisciplineUpdate();
bool change = false;
for( ; spell_id < SPDAT_RECORDS; ++spell_id) {
if (spell_id < 0 || spell_id >= SPDAT_RECORDS) {
initiator->Message(13, "FATAL ERROR: Spell id out-of-range (id: %i, min: 0, max: %i)", spell_id, SPDAT_RECORDS);
return count;
}
while (true) {
if (spells[spell_id].classes[WARRIOR] == 0) // check if spell exists
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] > max_level) // maximum level
break;
if (spells[spell_id].classes[initiator->GetPP().class_ - 1] < min_level) // minimum level
break;
if (spells[spell_id].skill == 52)
break;
if (RuleB(Spells, UseCHAScribeHack) && spells[spell_id].effectid[EFFECT_COUNT - 1] == 10)
break;
uint16 spell_id_ = (uint16)spell_id;
if ((spell_id_ != spell_id) || (spell_id != spell_id_)) {
initiator->Message(13, "FATAL ERROR: Type conversion data loss with spell_id (%i != %u)", spell_id, spell_id_);
return count;
}
if (!IsDiscipline(spell_id_))
break;
for (uint32 r = 0; r < MAX_PP_DISCIPLINES; r++) {
if (initiator->GetPP().disciplines.values[r] == spell_id_) {
initiator->Message(13, "You already know this discipline.");
break; // continue the 1st loop
}
else if (initiator->GetPP().disciplines.values[r] == 0) {
if (SpellGlobalRule) {
// bool to see if the character has the required QGlobal to train it if one exists in the Spell_Globals table
SpellGlobalCheckResult = initiator->SpellGlobalCheck(spell_id_, char_id);
if (SpellGlobalCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;
initiator->Message(0, "You have learned a new discipline!");
count++; //success counter
break; //continue the 1st loop
++count; // success counter
}
} //if we get to this point, there's already a discipline in this slot, so we skip it
break; // continue the 1st loop
}
else if (SpellBucketRule) {
// bool to see if the character has the required bucket to train it if one exists in the spell_buckets table
SpellBucketCheckResult = initiator->SpellBucketCheck(spell_id_, char_id);
if (SpellBucketCheckResult) {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;
initiator->Message(0, "You have learned a new discipline!");
++count;
}
break;
}
else {
initiator->GetPP().disciplines.values[r] = spell_id_;
database.SaveCharacterDisc(char_id, r, spell_id_);
change = true;;
initiator->Message(0, "You have learned a new discipline!");
++count; // success counter
break; // continue the 1st loop
}
}
}
break;
}
}
return count; //how many disciplines were learned successfully
if (change)
initiator->SendDisciplineUpdate();
return count; // how many disciplines were learned successfully
}
void QuestManager::unscribespells() {
QuestManagerCurrentQuestVars();
initiator->UnscribeSpellAll();
}
}
void QuestManager::untraindiscs() {
QuestManagerCurrentQuestVars();
@@ -1706,32 +1761,33 @@ void QuestManager::respawn(int npcTypeID, int grid) {
const NPCType* npcType = nullptr;
if ((npcType = database.LoadNPCTypesData(npcTypeID)))
{
owner = new NPC(npcType, nullptr, owner->GetPosition(), FlyMode3);
owner = new NPC(npcType, nullptr, owner->GetPosition(), GravityBehavior::Water);
owner->CastToNPC()->AddLootTable();
if (owner->CastToNPC()->DropsGlobalLoot())
owner->CastToNPC()->CheckGlobalLootTables();
entity_list.AddNPC(owner->CastToNPC(),true,true);
if(grid > 0)
owner->CastToNPC()->AssignWaypoints(grid);
owner->SendPositionUpdate();
}
}
void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz, bool bSay) {
void QuestManager::set_proximity(float minx, float maxx, float miny, float maxy, float minz, float maxz, bool bSay)
{
QuestManagerCurrentQuestVars();
if (!owner || !owner->IsNPC())
if (!owner || !owner->IsNPC()) {
return;
}
entity_list.AddProximity(owner->CastToNPC());
owner->CastToNPC()->proximity->min_x = minx;
owner->CastToNPC()->proximity->max_x = maxx;
owner->CastToNPC()->proximity->min_y = miny;
owner->CastToNPC()->proximity->max_y = maxy;
owner->CastToNPC()->proximity->min_z = minz;
owner->CastToNPC()->proximity->max_z = maxz;
owner->CastToNPC()->proximity->say = bSay;
owner->CastToNPC()->proximity->min_x = minx;
owner->CastToNPC()->proximity->max_x = maxx;
owner->CastToNPC()->proximity->min_y = miny;
owner->CastToNPC()->proximity->max_y = maxy;
owner->CastToNPC()->proximity->min_z = minz;
owner->CastToNPC()->proximity->max_z = maxz;
owner->CastToNPC()->proximity->say = bSay;
owner->CastToNPC()->proximity->proximity_set = true;
}
void QuestManager::clear_proximity() {
@@ -2134,8 +2190,7 @@ bool QuestManager::createBot(const char *name, const char *lastname, uint8 level
return false;
}
NPCType DefaultNPCTypeStruct = Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender);
Bot* NewBot = new Bot(DefaultNPCTypeStruct, initiator);
Bot* NewBot = new Bot(Bot::CreateDefaultNPCTypeStructForBot(name, lastname, level, race, botclass, gender), initiator);
if(NewBot)
{
@@ -2793,47 +2848,11 @@ void QuestManager::FlagInstanceByRaidLeader(uint32 zone, int16 version)
}
}
const char* QuestManager::saylink(char* Phrase, bool silent, const char* LinkName) {
std::string QuestManager::saylink(char *saylink_text, bool silent, const char *link_name)
{
QuestManagerCurrentQuestVars();
int sayid = 0;
int sz = strlen(Phrase);
auto escaped_string = new char[sz * 2];
database.DoEscapeString(escaped_string, Phrase, sz);
// Query for an existing phrase and id in the saylink table
std::string query = StringFormat("SELECT `id` FROM `saylink` WHERE `phrase` = '%s'", escaped_string);
auto results = database.QueryDatabase(query);
if (results.Success()) {
if (results.RowCount() >= 1) {
for (auto row = results.begin();row != results.end(); ++row)
sayid = atoi(row[0]);
} else {
std::string insert_query = StringFormat("INSERT INTO `saylink` (`phrase`) VALUES ('%s')", escaped_string);
results = database.QueryDatabase(insert_query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Error in saylink phrase queries", results.ErrorMessage().c_str());
}
else {
sayid = results.LastInsertedID();
}
}
}
safe_delete_array(escaped_string);
//Create the say link as an item link hash
EQEmu::SayLinkEngine linker;
linker.SetProxyItemID(SAYLINK_ITEM_ID);
if (silent)
linker.SetProxyAugment2ID(sayid);
else
linker.SetProxyAugment1ID(sayid);
linker.SetProxyText(LinkName);
strcpy(Phrase, linker.GenerateLink().c_str());
return Phrase;
return EQEmu::SayLinkEngine::GenerateQuestSaylink(saylink_text, silent, link_name);
}
const char* QuestManager::getguildnamebyid(int guild_id) {
@@ -2859,22 +2878,18 @@ bool QuestManager::IsRunning()
return owner->IsRunning();
}
void QuestManager::FlyMode(uint8 flymode)
void QuestManager::FlyMode(GravityBehavior flymode)
{
QuestManagerCurrentQuestVars();
if(initiator)
{
if (flymode >= 0 && flymode < 3) {
initiator->SendAppearancePacket(AT_Levitate, flymode);
return;
}
initiator->SendAppearancePacket(AT_Levitate, static_cast<int>(flymode));
initiator->SetFlyMode(flymode);
}
if(owner)
else if(owner)
{
if (flymode >= 0 && flymode < 3) {
owner->SendAppearancePacket(AT_Levitate, flymode);
return;
}
owner->SendAppearancePacket(AT_Levitate, static_cast<int>(flymode));
owner->SetFlyMode(flymode);
}
}
@@ -2884,7 +2899,7 @@ uint8 QuestManager::FactionValue()
FACTION_VALUE oldfac;
uint8 newfac = 0;
if(initiator && owner->IsNPC()) {
oldfac = initiator->GetFactionLevel(initiator->GetID(), owner->GetID(), initiator->GetRace(), initiator->GetClass(), initiator->GetDeity(), owner->GetPrimaryFaction(), owner);
oldfac = initiator->GetFactionLevel(initiator->GetID(), owner->GetID(), initiator->GetFactionRace(), initiator->GetClass(), initiator->GetDeity(), owner->GetPrimaryFaction(), owner);
// now, reorder the faction to have it make sense (higher values are better)
switch (oldfac) {
+2 -2
View File
@@ -248,11 +248,11 @@ public:
void FlagInstanceByGroupLeader(uint32 zone, int16 version);
void FlagInstanceByRaidLeader(uint32 zone, int16 version);
const char* varlink(char* perltext, int item_id);
const char* saylink(char* Phrase, bool silent, const char* LinkName);
std::string saylink(char *saylink_text, bool silent, const char *link_name);
const char* getguildnamebyid(int guild_id);
void SetRunning(bool val);
bool IsRunning();
void FlyMode(uint8 flymode);
void FlyMode(GravityBehavior flymode);
uint8 FactionValue();
void wearchange(uint8 slot, uint16 texture, uint32 hero_forge_model = 0, uint32 elite_material = 0);
void voicetell(const char *str, int macronum, int racenum, int gendernum);
+3 -3
View File
@@ -255,7 +255,7 @@ bool Spawn2::Process() {
}
currentnpcid = npcid;
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), FlyMode3);
NPC *npc = new NPC(tmp, this, glm::vec4(x, y, z, heading), GravityBehavior::Water);
npc->mod_prespawn(this);
@@ -488,7 +488,7 @@ bool ZoneDatabase::PopulateZoneSpawnListClose(uint32 zoneid, LinkedList<Spawn2*>
"animation "
"FROM "
"spawn2 "
"WHERE zone = '%s' AND version = %u",
"WHERE zone = '%s' AND (version = %u OR version = -1) ",
zone_name,
version
);
@@ -592,7 +592,7 @@ bool ZoneDatabase::PopulateZoneSpawnList(uint32 zoneid, LinkedList<Spawn2*> &spa
"animation "
"FROM "
"spawn2 "
"WHERE zone = '%s' AND version = %u",
"WHERE zone = '%s' AND (version = %u OR version = -1)",
zone_name,
version
);
+4 -4
View File
@@ -27,7 +27,7 @@
#include <string.h>
extern uint64_t frame_time;
extern double frame_time;
int Mob::GetBaseSkillDamage(EQEmu::skills::SkillType skill, Mob *target)
{
@@ -197,7 +197,7 @@ void Mob::DoSpecialAttackDamage(Mob *who, EQEmu::skills::SkillType skill, int32
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], who, EQEmu::CastingSlot::Item, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], who, EQEmu::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
@@ -1033,7 +1033,7 @@ void Mob::ProjectileAttack()
ProjectileAtk[i].skill = 0;
ProjectileAtk[i].speed_mod = 0.0f;
} else {
ProjectileAtk[i].increment += frame_time;
ProjectileAtk[i].increment += 1000 * frame_time;
}
}
@@ -2156,7 +2156,7 @@ void Mob::DoMeleeSkillAttackDmg(Mob *other, uint16 weapon_damage, EQEmu::skills:
IsValidSpell(aabonuses.SkillAttackProc[2])) {
float chance = aabonuses.SkillAttackProc[0] / 1000.0f;
if (zone->random.Roll(chance))
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::CastingSlot::Item, 0, -1,
SpellFinished(aabonuses.SkillAttackProc[2], other, EQEmu::spells::CastingSlot::Item, 0, -1,
spells[aabonuses.SkillAttackProc[2]].ResistDiff);
}
+16 -75
View File
@@ -149,7 +149,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (spells[spell_id].EndurUpkeep > 0)
SetEndurUpkeep(true);
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater)
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::maskUFAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket(false);
CastToClient()->FastQueuePacket(&outapp);
@@ -826,9 +826,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#endif
if(IsClient())
{
CastToClient()->SetSenseExemption(true);
if (CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoDAndLater)
if (CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoDAndLater)
{
bodyType bt = BT_Undead;
@@ -853,7 +851,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
SetHeading(CalculateHeadingToTarget(ClosestMob->GetX(), ClosestMob->GetY()));
SetTarget(ClosestMob);
CastToClient()->SendTargetCommand(ClosestMob->GetID());
SendPositionUpdate(2);
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0, true);
}
else
Message_StringID(clientMessageError, SENSE_NOTHING);
@@ -1246,7 +1244,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
// Will fix buttons for now
if (IsClient()) {
auto c = CastToClient();
if (c->ClientVersionBit() & EQEmu::versions::bit_UFAndLater) {
if (c->ClientVersionBit() & EQEmu::versions::maskUFAndLater) {
c->SetPetCommandState(PET_BUTTON_SIT, 0);
c->SetPetCommandState(PET_BUTTON_STOP, 0);
c->SetPetCommandState(PET_BUTTON_REGROUP, 0);
@@ -1665,7 +1663,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
#endif
// This is handled by the client prior to SoD.
//
if (IsClient() && (CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoDAndLater))
if (IsClient() && (CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoDAndLater))
CastToClient()->LocateCorpse();
break;
@@ -2472,7 +2470,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
case SE_FcTimerRefresh:
{
if(IsClient()) {
for(unsigned int i =0 ; i < MAX_PP_MEMSPELL; ++i) {
for (unsigned int i = 0; i < EQEmu::spells::SPELL_GEM_COUNT; ++i) {
if(IsValidSpell(CastToClient()->m_pp.mem_spells[i])) {
if (CalcFocusEffect(focusFcTimerRefresh, spell_id, CastToClient()->m_pp.mem_spells[i])){
CastToClient()->m_pp.spellSlotRefresh[i] = 1;
@@ -2741,7 +2739,7 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial, int level_ove
if (caster && IsValidSpell(spells[spell_id].base2[i])){
if(zone->random.Roll(spells[spell_id].base[i]))
caster->SpellFinished(spells[spell_id].base2[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
caster->SpellFinished(spells[spell_id].base2[i], this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
}
break;
}
@@ -3404,7 +3402,7 @@ void Mob::BuffProcess()
Log(Logs::Detail, Logs::Spells, "Buff %d in slot %d has %d tics remaining.", buffs[buffs_i].spellid, buffs_i, buffs[buffs_i].ticsremaining);
}
}
else if (IsClient() && !(CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoFAndLater))
else if (IsClient() && !(CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoFAndLater))
{
buffs[buffs_i].UpdateClient = true;
}
@@ -3664,14 +3662,14 @@ void Mob::DoBuffTic(const Buffs_Struct &buff, int slot, Mob *caster)
case SE_CastOnFadeEffectNPC:
case SE_CastOnFadeEffectAlways: {
if (buff.ticsremaining == 0) {
SpellFinished(spells[buff.spellid].base[i], this, EQEmu::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base[i]].ResistDiff);
SpellFinished(spells[buff.spellid].base[i], this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spells[buff.spellid].base[i]].ResistDiff);
}
break;
}
case SE_LocateCorpse: {
// This is handled by the client prior to SoD.
if (IsClient() && (CastToClient()->ClientVersionBit() & EQEmu::versions::bit_SoDAndLater))
if (IsClient() && (CastToClient()->ClientVersionBit() & EQEmu::versions::maskSoDAndLater))
CastToClient()->LocateCorpse();
}
@@ -4038,63 +4036,6 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
SetLevel(GetOrigLevel());
break;
}
case SE_MovementSpeed:
{
if(IsClient())
{
Client *my_c = CastToClient();
uint32 cur_time = Timer::GetCurrentTime();
if((cur_time - my_c->m_TimeSinceLastPositionCheck) > 1000)
{
float speed = (my_c->m_DistanceSinceLastPositionCheck * 100) / (float)(cur_time - my_c->m_TimeSinceLastPositionCheck);
float runs = my_c->GetRunspeed();
if(speed > (runs * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
if(!my_c->GetGMSpeed() && (runs >= my_c->GetBaseRunspeed() || (speed > (my_c->GetBaseRunspeed() * RuleR(Zone, MQWarpDetectionDistanceFactor)))))
{
printf("%s %i moving too fast! moved: %.2f in %ims, speed %.2f\n", __FILE__, __LINE__,
my_c->m_DistanceSinceLastPositionCheck, (cur_time - my_c->m_TimeSinceLastPositionCheck), speed);
if(my_c->IsShadowStepExempted())
{
if(my_c->m_DistanceSinceLastPositionCheck > 800)
{
my_c->CheatDetected(MQWarpShadowStep, my_c->GetX(), my_c->GetY(), my_c->GetZ());
}
}
else if(my_c->IsKnockBackExempted())
{
//still potential to trigger this if you're knocked back off a
//HUGE fall that takes > 2.5 seconds
if(speed > 30.0f)
{
my_c->CheatDetected(MQWarpKnockBack, my_c->GetX(), my_c->GetY(), my_c->GetZ());
}
}
else if(!my_c->IsPortExempted())
{
if(!my_c->IsMQExemptedArea(zone->GetZoneID(), my_c->GetX(), my_c->GetY(), my_c->GetZ()))
{
if(speed > (runs * 2 * RuleR(Zone, MQWarpDetectionDistanceFactor)))
{
my_c->m_TimeSinceLastPositionCheck = cur_time;
my_c->m_DistanceSinceLastPositionCheck = 0.0f;
my_c->CheatDetected(MQWarp, my_c->GetX(), my_c->GetY(), my_c->GetZ());
//my_c->Death(my_c, 10000000, SPELL_UNKNOWN, _1H_BLUNT);
}
else
{
my_c->CheatDetected(MQWarpLight, my_c->GetX(), my_c->GetY(), my_c->GetZ());
}
}
}
}
}
}
my_c->m_TimeSinceLastPositionCheck = cur_time;
my_c->m_DistanceSinceLastPositionCheck = 0.0f;
}
}
}
}
@@ -4144,7 +4085,7 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
{
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::bit_SoDAndLater);
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::maskSoDAndLater);
if(IsClient() && GetTarget() == this) {
CastToClient()->QueuePacket(outapp);
}
@@ -4154,11 +4095,11 @@ void Mob::BuffFadeBySlot(int slot, bool iRecalcBonuses)
if (IsNPC()) {
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::bit_SoDAndLater, true);
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::maskSoDAndLater, true);
safe_delete(outapp);
}
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::bit_UFAndLater)
if (IsClient() && CastToClient()->ClientVersionBit() & EQEmu::versions::maskUFAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket(false);
CastToClient()->FastQueuePacket(&outapp);
@@ -5159,7 +5100,7 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
if (Caston_spell_id) {
if (IsValidSpell(Caston_spell_id) && (Caston_spell_id != spell_id))
SpellFinished(Caston_spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff);
SpellFinished(Caston_spell_id, this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[Caston_spell_id].ResistDiff);
}
return (value * lvlModifier / 100);
@@ -6722,10 +6663,10 @@ void Mob::TryTriggerThreshHold(int32 damage, int effect_id, Mob* attacker){
if (IsValidSpell(spell_id)) {
if (IsBeneficialSpell(spell_id))
SpellFinished(spell_id, this, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, this, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
else if(attacker)
SpellFinished(spell_id, attacker, EQEmu::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
SpellFinished(spell_id, attacker, EQEmu::spells::CastingSlot::Item, 0, -1, spells[spell_id].ResistDiff);
}
}
}
+48 -70
View File
@@ -100,6 +100,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "bot.h"
#endif
#include "mob_movement_manager.h"
extern Zone* zone;
@@ -107,7 +108,7 @@ extern volatile bool is_zone_loaded;
extern WorldServer worldserver;
extern FastMath g_Math;
using EQEmu::CastingSlot;
using EQEmu::spells::CastingSlot;
// this is run constantly for every mob
void Mob::SpellProcess()
@@ -305,8 +306,9 @@ bool Mob::CastSpell(uint16 spell_id, uint16 target_id, CastingSlot slot,
}
//To prevent NPC ghosting when spells are cast from scripts
if (IsNPC() && IsMoving() && cast_time > 0)
SendPosition();
if (IsNPC() && IsMoving() && cast_time > 0) {
StopNavigation();
}
if(resist_adjust)
{
@@ -2665,23 +2667,6 @@ void Mob::BardPulse(uint16 spell_id, Mob *caster) {
action->effect_flag = 4;
if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
{
if(IsClient())
{
if(!IsBuffSpell(spell_id))
{
CastToClient()->SetKnockBackExemption(true);
}
}
}
if(IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep))
{
CastToClient()->SetShadowStepExemption(true);
}
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{
CastToClient()->QueuePacket(packet);
@@ -3021,6 +3006,10 @@ int Mob::CheckStackConflict(uint16 spellid1, int caster_level1, uint16 spellid2,
if(effect1 != effect2)
continue;
if (IsBardOnlyStackEffect(effect1) && GetSpellLevel(spellid1, BARD) != 255 &&
GetSpellLevel(spellid2, BARD) != 255)
continue;
// big ol' list according to the client, wasn't that nice!
if (IsEffectIgnoredInStacking(effect1))
continue;
@@ -3329,7 +3318,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
{
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::bit_SoDAndLater);
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::maskSoDAndLater);
if(IsClient() && GetTarget() == this)
CastToClient()->QueuePacket(outapp);
@@ -3339,7 +3328,7 @@ int Mob::AddBuff(Mob *caster, uint16 spell_id, int duration, int32 level_overrid
if (IsNPC()) {
EQApplicationPacket *outapp = MakeBuffsPacket();
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::bit_SoDAndLater, true);
entity_list.QueueClientsByTarget(this, outapp, false, nullptr, true, false, EQEmu::versions::maskSoDAndLater, true);
safe_delete(outapp);
}
@@ -3943,13 +3932,7 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
if(spells[spell_id].pushback != 0.0f || spells[spell_id].pushup != 0.0f)
{
if(spelltar->IsClient())
{
if(!IsBuffSpell(spell_id))
{
spelltar->CastToClient()->SetKnockBackExemption(true);
}
} else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) {
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;
@@ -3957,11 +3940,6 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r
}
}
if(spelltar->IsClient() && IsEffectInSpell(spell_id, SE_ShadowStep))
{
spelltar->CastToClient()->SetShadowStepExemption(true);
}
if(!IsEffectInSpell(spell_id, SE_BindAffinity))
{
if(spelltar != this && spelltar->IsClient()) // send to target
@@ -4841,7 +4819,22 @@ void Mob::Spin() {
safe_delete(outapp);
}
else {
GMMove(GetX(), GetY(), GetZ(), GetHeading()+5);
float x,y,z,h;
x=GetX();
y=GetY();
z=GetZ();
h=GetHeading()+5;
if (IsCorpse() || (IsClient() && !IsAIControlled())) {
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
mMovementManager->SendCommandToClients(this, 0.0, 0.0, 0.0, 0.0, 0, ClientRangeAny);
}
else {
Teleport(glm::vec4(x, y, z, h));
}
}
}
@@ -4925,12 +4918,11 @@ void Client::UnStun() {
void NPC::Stun(int duration) {
Mob::Stun(duration);
SetCurrentSpeed(0);
StopNavigation();
}
void NPC::UnStun() {
Mob::UnStun();
SetCurrentSpeed(GetRunspeed());
}
void Mob::Mesmerize()
@@ -4940,18 +4932,7 @@ void Mob::Mesmerize()
if (casting_spell_id)
InterruptSpell();
SendPosition();
/* this stuns the client for max time, with no way to break it
if (this->IsClient()){
EQApplicationPacket* outapp = new EQApplicationPacket(OP_Stun, sizeof(Stun_Struct));
Stun_Struct* stunon = (Stun_Struct*) outapp->pBuffer;
stunon->duration = 0xFFFF;
this->CastToClient()->QueuePacket(outapp);
safe_delete(outapp);
} else {
SetRunAnimSpeed(0);
}
*/
StopNavigation();
}
void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message)
@@ -5003,7 +4984,7 @@ void Client::MakeBuffFadePacket(uint16 spell_id, int slot_id, bool send_message)
void Client::MemSpell(uint16 spell_id, int slot, bool update_client)
{
if(slot >= MAX_PP_MEMSPELL || slot < 0)
if(slot >= EQEmu::spells::SPELL_GEM_COUNT || slot < 0)
return;
if(update_client)
@@ -5025,7 +5006,7 @@ void Client::MemSpell(uint16 spell_id, int slot, bool update_client)
void Client::UnmemSpell(int slot, bool update_client)
{
if(slot > MAX_PP_MEMSPELL || slot < 0)
if(slot > EQEmu::spells::SPELL_GEM_COUNT || slot < 0)
return;
Log(Logs::Detail, Logs::Spells, "Spell %d forgotten from slot %d", m_pp.mem_spells[slot], slot);
@@ -5041,7 +5022,7 @@ void Client::UnmemSpell(int slot, bool update_client)
void Client::UnmemSpellBySpellID(int32 spell_id)
{
for(int i = 0; i < MAX_PP_MEMSPELL; i++) {
for(int i = 0; i < EQEmu::spells::SPELL_GEM_COUNT; i++) {
if(m_pp.mem_spells[i] == spell_id) {
UnmemSpell(i, true);
break;
@@ -5053,14 +5034,14 @@ void Client::UnmemSpellAll(bool update_client)
{
int i;
for(i = 0; i < MAX_PP_MEMSPELL; i++)
for(i = 0; i < EQEmu::spells::SPELL_GEM_COUNT; i++)
if(m_pp.mem_spells[i] != 0xFFFFFFFF)
UnmemSpell(i, update_client);
}
void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client)
{
if(slot >= MAX_PP_SPELLBOOK || slot < 0)
if(slot >= EQEmu::spells::SPELLBOOK_SIZE || slot < 0)
return;
if(update_client)
@@ -5081,14 +5062,14 @@ void Client::ScribeSpell(uint16 spell_id, int slot, bool update_client)
void Client::UnscribeSpell(int slot, bool update_client)
{
if(slot >= MAX_PP_SPELLBOOK || slot < 0)
if(slot >= EQEmu::spells::SPELLBOOK_SIZE || slot < 0)
return;
Log(Logs::Detail, Logs::Spells, "Spell %d erased from spell book slot %d", m_pp.spell_book[slot], slot);
m_pp.spell_book[slot] = 0xFFFFFFFF;
database.DeleteCharacterSpell(this->CharacterID(), m_pp.spell_book[slot], slot);
if(update_client)
if(update_client && slot < EQEmu::spells::DynamicLookup(ClientVersion(), GetGM())->SpellbookSize)
{
auto outapp = new EQApplicationPacket(OP_DeleteSpell, sizeof(DeleteSpell_Struct));
DeleteSpell_Struct* del = (DeleteSpell_Struct*)outapp->pBuffer;
@@ -5101,9 +5082,7 @@ void Client::UnscribeSpell(int slot, bool update_client)
void Client::UnscribeSpellAll(bool update_client)
{
int i;
for(i = 0; i < MAX_PP_SPELLBOOK; i++)
for(int i = 0; i < EQEmu::spells::SPELLBOOK_SIZE; i++)
{
if(m_pp.spell_book[i] != 0xFFFFFFFF)
UnscribeSpell(i, update_client);
@@ -5137,7 +5116,7 @@ void Client::UntrainDiscAll(bool update_client)
}
int Client::GetNextAvailableSpellBookSlot(int starting_slot) {
for (int i = starting_slot; i < MAX_PP_SPELLBOOK; i++) { //using starting_slot should help speed this up when we're iterating through a bunch of spells
for (int i = starting_slot; i < EQEmu::spells::SPELLBOOK_SIZE; i++) { //using starting_slot should help speed this up when we're iterating through a bunch of spells
if (!IsValidSpell(GetSpellByBookSlot(i)))
return i;
}
@@ -5146,7 +5125,7 @@ int Client::GetNextAvailableSpellBookSlot(int starting_slot) {
}
int Client::FindSpellBookSlotBySpellID(uint16 spellid) {
for(int i = 0; i < MAX_PP_SPELLBOOK; i++) {
for(int i = 0; i < EQEmu::spells::SPELLBOOK_SIZE; i++) {
if(m_pp.spell_book[i] == spellid)
return i;
}
@@ -5204,7 +5183,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) {
std::string spell_bucket_name;
int spell_bucket_value;
int bucket_value;
std::string query = StringFormat("SELECT key, value FROM spell_buckets WHERE spellid = %i", spell_id);
std::string query = StringFormat("SELECT `key`, value FROM spell_buckets WHERE spellid = %i", spell_id);
auto results = database.QueryDatabase(query);
if (!results.Success())
return false;
@@ -5218,7 +5197,7 @@ bool Client::SpellBucketCheck(uint16 spell_id, uint32 char_id) {
if (spell_bucket_name.empty())
return true;
query = StringFormat("SELECT value FROM data_buckets WHERE key = '%i-%s'", char_id, spell_bucket_name.c_str());
query = StringFormat("SELECT value FROM data_buckets WHERE `key` = '%i-%s'", char_id, spell_bucket_name.c_str());
results = database.QueryDatabase(query);
if (!results.Success()) {
Log(Logs::General, Logs::Error, "Spell bucket %s for spell ID %i for char ID %i failed.", spell_bucket_name.c_str(), spell_id, char_id);
@@ -5445,7 +5424,7 @@ bool Mob::UseBardSpellLogic(uint16 spell_id, int slot)
spell_id != SPELL_UNKNOWN &&
slot != -1 &&
GetClass() == BARD &&
slot <= MAX_PP_MEMSPELL &&
slot <= EQEmu::spells::SPELL_GEM_COUNT &&
IsBardSong(spell_id)
);
}
@@ -5566,7 +5545,7 @@ void Mob::SendBuffsToClient(Client *c)
if(!c)
return;
if (c->ClientVersionBit() & EQEmu::versions::bit_SoDAndLater)
if (c->ClientVersionBit() & EQEmu::versions::maskSoDAndLater)
{
EQApplicationPacket *outapp = MakeBuffsPacket();
c->FastQueuePacket(&outapp);
@@ -5654,12 +5633,12 @@ int Client::GetCurrentBuffSlots() const
numbuffs++;
if (GetLevel() > 74)
numbuffs++;
return EQEmu::ClampUpper(numbuffs, EQEmu::constants::Lookup(m_ClientVersion)->LongBuffs);
return EQEmu::ClampUpper(numbuffs, EQEmu::spells::StaticLookup(m_ClientVersion)->LongBuffs);
}
int Client::GetCurrentSongSlots() const
{
return EQEmu::constants::Lookup(m_ClientVersion)->ShortBuffs; // AAs dont affect this
return EQEmu::spells::StaticLookup(m_ClientVersion)->ShortBuffs; // AAs dont affect this
}
void Client::InitializeBuffSlots()
@@ -5683,9 +5662,8 @@ void NPC::InitializeBuffSlots()
{
int max_slots = GetMaxTotalSlots();
buffs = new Buffs_Struct[max_slots];
for(int x = 0; x < max_slots; ++x)
{
buffs[x].spellid = SPELL_UNKNOWN;
for (int x = 0; x < max_slots; ++x) {
buffs[x].spellid = SPELL_UNKNOWN;
buffs[x].UpdateClient = false;
}
current_buff_count = 0;
+1 -1
View File
@@ -2745,7 +2745,7 @@ void TaskManager::SendTaskActivityShort(Client *c, int TaskID, int ActivityID, i
TaskActivityShort_Struct* tass;
if (c->ClientVersionBit() & EQEmu::versions::bit_RoFAndLater)
if (c->ClientVersionBit() & EQEmu::versions::maskRoFAndLater)
{
auto outapp = new EQApplicationPacket(OP_TaskActivity, 25);
outapp->WriteUInt32(ClientTaskIndex);
+5 -5
View File
@@ -135,7 +135,7 @@ void Trap::Trigger(Mob* trigger)
entity_list.MessageClose(trigger,false,100,13,"%s",message.c_str());
}
if(hiddenTrigger){
hiddenTrigger->SpellFinished(effectvalue, trigger, EQEmu::CastingSlot::Item, 0, -1, spells[effectvalue].ResistDiff);
hiddenTrigger->SpellFinished(effectvalue, trigger, EQEmu::spells::CastingSlot::Item, 0, -1, spells[effectvalue].ResistDiff);
}
break;
case trapTypeAlarm:
@@ -166,7 +166,7 @@ void Trap::Trigger(Mob* trigger)
{
auto randomOffset = glm::vec4(zone->random.Int(-5, 5),zone->random.Int(-5, 5),zone->random.Int(-5, 5), zone->random.Int(0, 249));
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
auto new_npc = new NPC(tmp, nullptr, spawnPosition, GravityBehavior::Flying);
new_npc->AddLootTable();
if (new_npc->DropsGlobalLoot())
new_npc->CheckGlobalLootTables();
@@ -191,7 +191,7 @@ void Trap::Trigger(Mob* trigger)
{
auto randomOffset = glm::vec4(zone->random.Int(-2, 2), zone->random.Int(-2, 2), zone->random.Int(-2, 2), zone->random.Int(0, 249));
auto spawnPosition = randomOffset + glm::vec4(m_Position, 0.0f);
auto new_npc = new NPC(tmp, nullptr, spawnPosition, FlyMode3);
auto new_npc = new NPC(tmp, nullptr, spawnPosition, GravityBehavior::Flying);
new_npc->AddLootTable();
if (new_npc->DropsGlobalLoot())
new_npc->CheckGlobalLootTables();
@@ -456,7 +456,7 @@ void Trap::CreateHiddenTrigger()
auto make_npc = new NPCType;
memcpy(make_npc, base_type, sizeof(NPCType));
make_npc->max_hp = 100000;
make_npc->cur_hp = 100000;
make_npc->current_hp = 100000;
strcpy(make_npc->name, "a_trap");
make_npc->runspeed = 0.0f;
make_npc->bodytype = BT_Special;
@@ -469,7 +469,7 @@ void Trap::CreateHiddenTrigger()
make_npc->trackable = 0;
make_npc->level = level;
strcpy(make_npc->special_abilities, "19,1^20,1^24,1^25,1");
NPC* npca = new NPC(make_npc, nullptr, glm::vec4(m_Position, 0.0f), FlyMode3);
NPC* npca = new NPC(make_npc, nullptr, glm::vec4(m_Position, 0.0f), GravityBehavior::Flying);
npca->GiveNPCTypeData(make_npc);
entity_list.AddNPC(npca);
+7
View File
@@ -14,25 +14,30 @@ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) {
std::transform(zone_name.begin(), zone_name.end(), zone_name.begin(), ::tolower);
std::string file_path = Config->MapDir + "water/" + zone_name + std::string(".wtr");
Log(Logs::General, Logs::Debug, "Attempting to load water map with path %s", file_path.c_str());
FILE *f = fopen(file_path.c_str(), "rb");
if(f) {
char magic[10];
uint32 version;
if(fread(magic, 10, 1, f) != 1) {
Log(Logs::General, Logs::Debug, "Failed to load water map, error reading magic string in header.");
fclose(f);
return nullptr;
}
if(strncmp(magic, "EQEMUWATER", 10)) {
Log(Logs::General, Logs::Debug, "Failed to load water map, bad magic string in header.");
fclose(f);
return nullptr;
}
if(fread(&version, sizeof(version), 1, f) != 1) {
Log(Logs::General, Logs::Debug, "Failed to load water map, error reading version.");
fclose(f);
return nullptr;
}
Log(Logs::General, Logs::Debug, "Attempting to V%u load water map %s.", version, file_path.c_str());
if(version == 1) {
auto wm = new WaterMapV1();
if(!wm->Load(f)) {
@@ -56,10 +61,12 @@ WaterMap* WaterMap::LoadWaterMapfile(std::string zone_name) {
fclose(f);
return wm;
} else {
Log(Logs::General, Logs::Debug, "Failed to load water map, unsupported version V%u.", version);
fclose(f);
return nullptr;
}
}
Log(Logs::General, Logs::Debug, "Failed to load water map, could not open file for reading %s.", file_path.c_str());
return nullptr;
}
+125 -240
View File
@@ -30,6 +30,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "quest_parser_collection.h"
#include "water_map.h"
#include "fastmath.h"
#include "mob_movement_manager.h"
#include <math.h>
#include <stdlib.h>
@@ -92,7 +93,7 @@ void NPC::StopWandering()
{ // stops a mob from wandering, takes him off grid and sends him back to spawn point
roamer = false;
CastToNPC()->SetGrid(0);
SendPosition();
StopNavigation();
Log(Logs::Detail, Logs::Pathing, "Stop Wandering requested.");
return;
}
@@ -107,7 +108,7 @@ void NPC::ResumeWandering()
{ // we were paused by a quest
AI_walking_timer->Disable();
SetGrid(0 - GetGrid());
if (cur_wp == -1)
if (cur_wp == EQEmu::WaypointStatus::QuestControlGrid)
{ // got here by a MoveTo()
cur_wp = save_wp;
UpdateWaypoint(cur_wp); // have him head to last destination from here
@@ -149,7 +150,7 @@ void NPC::PauseWandering(int pausetime)
moving = false;
DistractedFromGrid = true;
Log(Logs::Detail, Logs::Pathing, "Paused Wandering requested. Grid %d. Resuming in %d ms (0=not until told)", GetGrid(), pausetime);
SendPosition();
StopNavigation();
if (pausetime < 1) { // negative grid number stops him dead in his tracks until ResumeWandering()
SetGrid(0 - GetGrid());
}
@@ -163,28 +164,32 @@ void NPC::PauseWandering(int pausetime)
return;
}
void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
{ // makes mob walk to specified location
if (IsNPC() && GetGrid() != 0)
{ // he is on a grid
if (GetGrid() < 0)
{ // currently stopped by a quest command
SetGrid(0 - GetGrid()); // get him moving again
Log(Logs::Detail, Logs::AI, "MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.", GetGrid());
void NPC::MoveTo(const glm::vec4 &position, bool saveguardspot)
{ // makes mob walk to specified location
if (IsNPC() && GetGrid() != 0) { // he is on a grid
if (GetGrid() < 0) { // currently stopped by a quest command
SetGrid(0 - GetGrid()); // get him moving again
Log(Logs::Detail,
Logs::AI,
"MoveTo during quest wandering. Canceling quest wandering and going back to grid %d when MoveTo is done.",
GetGrid());
}
AI_walking_timer->Disable(); // disable timer in case he is paused at a wp
if (cur_wp >= 0)
{ // we've not already done a MoveTo()
save_wp = cur_wp; // save the current waypoint
cur_wp = -1; // flag this move as quest controlled
AI_walking_timer->Disable(); // disable timer in case he is paused at a wp
if (cur_wp >= 0) { // we've not already done a MoveTo()
save_wp = cur_wp; // save the current waypoint
cur_wp = EQEmu::WaypointStatus::QuestControlGrid;
}
Log(Logs::Detail, Logs::AI, "MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d", to_string(static_cast<glm::vec3>(position)).c_str(), -GetGrid(), save_wp);
Log(Logs::Detail,
Logs::AI,
"MoveTo %s, pausing regular grid wandering. Grid %d, save_wp %d",
to_string(static_cast<glm::vec3>(position)).c_str(),
-GetGrid(),
save_wp);
}
else
{ // not on a grid
roamer = true;
else { // not on a grid
roamer = true;
save_wp = 0;
cur_wp = -2; // flag as quest controlled w/no grid
cur_wp = EQEmu::WaypointStatus::QuestControlNoGrid;
Log(Logs::Detail, Logs::AI, "MoveTo %s without a grid.", to_string(static_cast<glm::vec3>(position)).c_str());
}
@@ -193,23 +198,27 @@ void NPC::MoveTo(const glm::vec4& position, bool saveguardspot)
m_CurrentWayPoint = position;
m_CurrentWayPoint.z = GetFixedZ(dest);
if (saveguardspot)
{
if (saveguardspot) {
m_GuardPoint = m_CurrentWayPoint;
if (m_GuardPoint.w == 0)
m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler
if (m_GuardPoint.w == 0) {
m_GuardPoint.w = 0.0001;
} //hack to make IsGuarding simpler
if (m_GuardPoint.w == -1)
m_GuardPoint.w = this->CalculateHeadingToTarget(position.x, position.y);
Log(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
Log(Logs::Detail,
Logs::AI,
"Setting guard position to %s",
to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
}
cur_wp_pause = 0;
cur_wp_pause = 0;
time_until_can_move = 0;
if (AI_walking_timer->Enabled())
if (AI_walking_timer->Enabled()) {
AI_walking_timer->Start(100);
}
}
void NPC::UpdateWaypoint(int wp_index)
@@ -332,8 +341,6 @@ void NPC::CalculateNewWaypoint()
}
}
tar_ndx = 52;
// Preserve waypoint setting for quest controlled NPCs
if (cur_wp < 0)
cur_wp = old_wp;
@@ -391,13 +398,8 @@ void NPC::SetWaypointPause()
{
//Declare time to wait on current WP
if (cur_wp_pause == 0) {
AI_walking_timer->Start(100);
AI_walking_timer->Trigger();
}
else
if(cur_wp_pause > 0)
{
switch (pausetype)
{
case 0: //Random Half
@@ -413,31 +415,22 @@ void NPC::SetWaypointPause()
}
}
void NPC::SaveGuardSpot(bool iClearGuardSpot) {
if (iClearGuardSpot) {
Log(Logs::Detail, Logs::AI, "Clearing guard order.");
m_GuardPoint = glm::vec4();
}
else {
m_GuardPoint = m_Position;
void NPC::SaveGuardSpot(const glm::vec4 &pos)
{
m_GuardPoint = pos;
if (m_GuardPoint.w == 0)
m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler
Log(Logs::Detail, Logs::AI, "Setting guard position to %s", to_string(static_cast<glm::vec3>(m_GuardPoint)).c_str());
}
if (m_GuardPoint.w == 0)
m_GuardPoint.w = 0.0001; //hack to make IsGuarding simpler
LogF(Logs::Detail, Logs::AI, "Setting guard position to {0}", to_string(static_cast<glm::vec3>(m_GuardPoint)));
}
void NPC::NextGuardPosition() {
if (!CalculateNewPosition(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z, GetMovespeed())) {
SetHeading(m_GuardPoint.w);
Log(Logs::Detail, Logs::AI, "Unable to move to next guard position. Probably rooted.");
}
else if ((m_Position.x == m_GuardPoint.x) && (m_Position.y == m_GuardPoint.y) && (m_Position.z == m_GuardPoint.z))
NavigateTo(m_GuardPoint.x, m_GuardPoint.y, m_GuardPoint.z);
if (IsPositionEqualWithinCertainZ(m_Position, m_GuardPoint, 5.0f))
{
if (moved)
{
moved = false;
SetCurrentSpeed(0);
}
}
}
@@ -446,179 +439,48 @@ 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)));
}
bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, bool check_z, bool calculate_heading) {
if (GetID() == 0)
return true;
if (speed <= 0) {
SetCurrentSpeed(0);
return true;
}
if ((m_Position.x - x == 0) && (m_Position.y - y == 0)) { //spawn is at target coords
return false;
}
else if ((std::abs(m_Position.x - x) < 0.1) && (std::abs(m_Position.y - y) < 0.1)) {
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), x, y, z);
}
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
return true;
}
int compare_steps = 20;
if (tar_ndx < compare_steps && m_TargetLocation.x == x && m_TargetLocation.y == y) {
float new_x = m_Position.x + m_TargetV.x * tar_vector;
float new_y = m_Position.y + m_TargetV.y * tar_vector;
float new_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
}
m_Position.x = new_x;
m_Position.y = new_y;
m_Position.z = new_z;
if (check_z && fix_z_timer.Check() && (!this->IsEngaged() || flee_mode || currently_fleeing)) {
this->FixZ();
}
tar_ndx++;
return true;
}
if (tar_ndx > 50) {
tar_ndx--;
}
else {
tar_ndx = 0;
}
m_TargetLocation = glm::vec3(x, y, z);
float nx = this->m_Position.x;
float ny = this->m_Position.y;
float nz = this->m_Position.z;
// float nh = this->heading;
m_TargetV.x = x - nx;
m_TargetV.y = y - ny;
m_TargetV.z = z - nz;
SetCurrentSpeed((int8)speed);
pRunAnimSpeed = speed;
#ifdef BOTS
if (IsClient() || IsBot())
#else
if (IsClient())
#endif
{
animation = speed / 2;
}
// --------------------------------------------------------------------------
// 2: get unit vector
// --------------------------------------------------------------------------
float mag = sqrtf(m_TargetV.x*m_TargetV.x + m_TargetV.y*m_TargetV.y + m_TargetV.z*m_TargetV.z);
tar_vector = (float)speed / mag;
// mob move fix
int numsteps = (int)(mag * 13.5f / (float)speed + 0.5f);
// mob move fix
if (numsteps < 20) {
if (numsteps > 1) {
tar_vector = 1.0f;
m_TargetV.x = m_TargetV.x / (float) numsteps;
m_TargetV.y = m_TargetV.y / (float) numsteps;
m_TargetV.z = m_TargetV.z / (float) numsteps;
float new_x = m_Position.x + m_TargetV.x;
float new_y = m_Position.y + m_TargetV.y;
float new_z = m_Position.z + m_TargetV.z;
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
}
m_Position.x = new_x;
m_Position.y = new_y;
m_Position.z = new_z;
if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y);
}
tar_ndx = 20 - numsteps;
}
else {
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), x, y, z);
}
m_Position.x = x;
m_Position.y = y;
m_Position.z = z;
}
}
else {
tar_vector /= 13.5f;
float dur = Timer::GetCurrentTime() - pLastChange;
if (dur < 0.0f) {
dur = 0.0f;
}
if (dur > 100.f) {
dur = 100.f;
}
tar_vector *= (dur / 100.0f);
float new_x = m_Position.x + m_TargetV.x * tar_vector;
float new_y = m_Position.y + m_TargetV.y * tar_vector;
float new_z = m_Position.z + m_TargetV.z * tar_vector;
if (IsNPC()) {
entity_list.ProcessMove(CastToNPC(), new_x, new_y, new_z);
}
m_Position.x = new_x;
m_Position.y = new_y;
m_Position.z = new_z;
if (calculate_heading) {
m_Position.w = CalculateHeadingToTarget(x, y);
}
}
if (check_z && fix_z_timer.Check() && !this->IsEngaged())
this->FixZ();
SetMoving(true);
moved = true;
m_Delta = glm::vec4(m_Position.x - nx, m_Position.y - ny, m_Position.z - nz, 0.0f);
if (IsClient()) {
SendPositionUpdate(1);
CastToClient()->ResetPositionTimer();
}
else {
SendPositionUpdate();
SetAppearance(eaStanding, false);
}
pLastChange = Timer::GetCurrentTime();
return true;
void Mob::WalkTo(float x, float y, float z)
{
mMovementManager->NavigateTo(this, x, y, z, MovementWalking);
}
bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool check_z, bool calculate_heading) {
return MakeNewPositionAndSendUpdate(x, y, z, speed, check_z);
void Mob::RunTo(float x, float y, float z)
{
mMovementManager->NavigateTo(this, x, y, z, MovementRunning);
}
void Mob::NavigateTo(float x, float y, float z)
{
if (IsRunning()) {
RunTo(x, y, z);
}
else {
WalkTo(x, y, z);
}
}
void Mob::RotateTo(float new_heading)
{
if (IsRunning()) {
RotateToRunning(new_heading);
}
else {
RotateToWalking(new_heading);
}
}
void Mob::RotateToWalking(float new_heading)
{
mMovementManager->RotateTo(this, new_heading, MovementWalking);
}
void Mob::RotateToRunning(float new_heading)
{
mMovementManager->RotateTo(this, new_heading, MovementRunning);
}
void Mob::StopNavigation() {
mMovementManager->StopNavigation(this);
}
void NPC::AssignWaypoints(int32 grid)
@@ -683,9 +545,6 @@ void NPC::AssignWaypoints(int32 grid)
if (wandertype == 1 || wandertype == 2 || wandertype == 5)
CalculateNewWaypoint();
if (wandertype == 1 || wandertype == 2 || wandertype == 5)
CalculateNewWaypoint();
}
void Mob::SendTo(float new_x, float new_y, float new_z) {
@@ -698,7 +557,7 @@ void Mob::SendTo(float new_x, float new_y, float new_z) {
m_Position.z = new_z;
Log(Logs::Detail, Logs::AI, "Sent To (%.3f, %.3f, %.3f)", new_x, new_y, new_z);
if (flymode == FlyMode1)
if (flymode == GravityBehavior::Flying)
return;
//fix up pathing Z, this shouldent be needed IF our waypoints
@@ -748,21 +607,18 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
}
}
float Mob::GetFixedZ(glm::vec3 destination, int32 z_find_offset) {
float Mob::GetFixedZ(const glm::vec3 &destination, int32 z_find_offset) {
BenchTimer timer;
timer.reset();
float new_z = destination.z;
if (zone->HasMap() && RuleB(Map, FixZWhenMoving)) {
if (zone->HasMap()) {
if (flymode == 1 || flymode == 2)
if (flymode == GravityBehavior::Flying)
return new_z;
if (this->IsBoat())
return new_z;
if (zone->HasWaterMap() && zone->watermap->InWater(glm::vec3(m_Position)))
if (zone->HasWaterMap() && zone->watermap->InLiquid(glm::vec3(m_Position)))
return new_z;
/*
@@ -794,11 +650,19 @@ float Mob::GetFixedZ(glm::vec3 destination, int32 z_find_offset) {
}
void Mob::FixZ(int32 z_find_offset /*= 5*/, bool fix_client_z /*= false*/) {
glm::vec3 current_loc(m_Position);
if (IsClient() && !fix_client_z)
if (IsClient() && !fix_client_z) {
return;
}
if (flymode == GravityBehavior::Flying) {
return;
}
if (zone->watermap && zone->watermap->InLiquid(m_Position)) {
return;
}
glm::vec3 current_loc(m_Position);
float new_z = GetFixedZ(current_loc, z_find_offset);
if (new_z == m_Position.z)
@@ -958,8 +822,6 @@ void Mob::TryMoveAlong(float distance, float angle, bool send)
new_pos.z = GetFixedZ(new_pos);
Teleport(new_pos);
if (send)
SendPositionUpdate();
}
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
@@ -1209,3 +1071,26 @@ void NPC::RestoreGuardSpotCharm()
{
m_GuardPoint = m_GuardPointSaved;
}
/******************
* Bot-specific overloads to make them play nice with the new movement system
*/
#ifdef BOTS
#include "bot.h"
void Bot::WalkTo(float x, float y, float z)
{
if (IsSitting())
Stand();
Mob::WalkTo(x, y, z);
}
void Bot::RunTo(float x, float y, float z)
{
if (IsSitting())
Stand();
Mob::RunTo(x, y, z);
}
#endif
+7 -1
View File
@@ -36,6 +36,7 @@ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#include "../common/misc_functions.h"
#include "../common/rulesys.h"
#include "../common/servertalk.h"
#include "../common/profanity_manager.h"
#include "client.h"
#include "corpse.h"
@@ -793,6 +794,11 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
}
break;
}
case ServerOP_RefreshCensorship: {
if (!EQEmu::ProfanityManager::LoadProfanityList(&database))
Log(Logs::General, Logs::Error, "Received request to refresh the profanity list..but, the action failed");
break;
}
case ServerOP_ChangeWID: {
if (pack->size != sizeof(ServerChangeWID_Struct)) {
std::cout << "Wrong size on ServerChangeWID_Struct. Got: " << pack->size << ", Expected: " << sizeof(ServerChangeWID_Struct) << std::endl;
@@ -1772,7 +1778,7 @@ void WorldServer::HandleMessage(uint16 opcode, const EQ::Net::Packet &p)
zone->GetLongName(),
zone->GetInstanceID()
);
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset());
RuleManager::Instance()->LoadRules(&database, RuleManager::Instance()->GetActiveRuleset(), true);
break;
}
case ServerOP_ReloadLogs: {
+17 -9
View File
@@ -53,6 +53,8 @@
#include "worldserver.h"
#include "zone.h"
#include "zone_config.h"
#include "mob_movement_manager.h"
#include "npc_scale_manager.h"
#include <time.h>
#include <ctime>
@@ -64,8 +66,6 @@
#define strcasecmp _stricmp
#endif
extern bool staticzone;
extern NetConnection net;
extern PetitionList petition_list;
@@ -73,6 +73,7 @@ extern QuestParserCollection* parse;
extern uint32 numclients;
extern WorldServer worldserver;
extern Zone* zone;
extern NpcScaleManager* npc_scale_manager;
Mutex MZoneShutdown;
@@ -861,6 +862,8 @@ Zone::Zone(uint32 in_zoneid, uint32 in_instanceid, const char* in_short_name)
m_ucss_available = false;
m_last_ucss_update = 0;
mMovementManager = &MobMovementManager::Get();
}
Zone::~Zone() {
@@ -901,7 +904,7 @@ bool Zone::Init(bool iStaticZone) {
std::string r_name = RuleManager::Instance()->GetRulesetName(&database, default_ruleset);
if(r_name.size() > 0)
{
RuleManager::Instance()->LoadRules(&database, r_name.c_str());
RuleManager::Instance()->LoadRules(&database, r_name.c_str(), false);
}
}
@@ -1296,6 +1299,8 @@ bool Zone::Process() {
if(hotzone_timer.Check()) { UpdateHotzone(); }
mMovementManager->Process();
return true;
}
@@ -1504,18 +1509,22 @@ void Zone::RepopClose(const glm::vec4& client_position, uint32 repop_distance)
mod_repop();
}
void Zone::Repop(uint32 delay) {
void Zone::Repop(uint32 delay)
{
if(!Depop())
if (!Depop()) {
return;
}
LinkedListIterator<Spawn2*> iterator(spawn2_list);
LinkedListIterator<Spawn2 *> iterator(spawn2_list);
iterator.Reset();
while (iterator.MoreElements()) {
iterator.RemoveCurrent();
}
npc_scale_manager->LoadScaleData();
entity_list.ClearTrapPointers();
quest_manager.ClearAllTimers();
@@ -1632,8 +1641,7 @@ ZonePoint* Zone::GetClosestZonePoint(const glm::vec3& location, uint32 to, Clien
// this shouldn't open up any exploits since those situations are detected later on
if ((zone->HasWaterMap() && !zone->watermap->InZoneLine(glm::vec3(client->GetPosition()))) || (!zone->HasWaterMap() && closest_dist > 400.0f && closest_dist < max_distance2))
{
if(client)
client->CheatDetected(MQZoneUnknownDest, location.x, location.y, location.z); // Someone is trying to use /zone
//TODO cheat detection
Log(Logs::General, Logs::Status, "WARNING: Closest zone point for zone id %d is %f, you might need to update your zone_points table if you dont arrive at the right spot.", to, closest_dist);
Log(Logs::General, Logs::Status, "<Real Zone Points>. %s", to_string(location).c_str());
}
@@ -2242,7 +2250,7 @@ void Zone::DoAdventureActions()
const NPCType* tmp = database.LoadNPCTypesData(ds->data_id);
if(tmp)
{
NPC* npc = new NPC(tmp, nullptr, glm::vec4(ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h), FlyMode3);
NPC* npc = new NPC(tmp, nullptr, glm::vec4(ds->assa_x, ds->assa_y, ds->assa_z, ds->assa_h), GravityBehavior::Water);
npc->AddLootTable();
if (npc->DropsGlobalLoot())
npc->CheckGlobalLootTables();
+3
View File
@@ -79,6 +79,7 @@ class WaterMap;
extern EntityList entity_list;
struct NPCType;
struct ServerZoneIncomingClient_Struct;
class MobMovementManager;
class Zone
{
@@ -362,6 +363,8 @@ private:
bool m_ucss_available;
uint32 m_last_ucss_update;
MobMovementManager *mMovementManager;
};
#endif
Regular → Executable
+55 -22
View File
@@ -1204,12 +1204,12 @@ bool ZoneDatabase::LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_
auto results = database.QueryDatabase(query);
int i = 0;
/* Initialize Spells */
for (i = 0; i < MAX_PP_MEMSPELL; i++){
for (i = 0; i < EQEmu::spells::SPELL_GEM_COUNT; i++){
pp->mem_spells[i] = 0xFFFFFFFF;
}
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
if (i < MAX_PP_MEMSPELL && atoi(row[1]) <= SPDAT_RECORDS){
if (i < EQEmu::spells::SPELL_GEM_COUNT && atoi(row[1]) <= SPDAT_RECORDS){
pp->mem_spells[i] = atoi(row[1]);
}
}
@@ -1225,17 +1225,28 @@ bool ZoneDatabase::LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Str
"`character_spells` "
"WHERE `id` = %u ORDER BY `slot_id`", character_id);
auto results = database.QueryDatabase(query);
int i = 0;
/* Initialize Spells */
for (i = 0; i < MAX_PP_SPELLBOOK; i++){
pp->spell_book[i] = 0xFFFFFFFF;
}
memset(pp->spell_book, 0xFF, (sizeof(uint32) * EQEmu::spells::SPELLBOOK_SIZE));
// We have the ability to block loaded spells by max id on a per-client basis..
// but, we do not have to ability to keep players from using older clients after
// they have scribed spells on a newer one that exceeds the older one's limit.
// Load them all so that server actions are valid..but, nix them in translators.
for (auto row = results.begin(); row != results.end(); ++row) {
i = atoi(row[0]);
if (i < MAX_PP_SPELLBOOK && atoi(row[1]) <= SPDAT_RECORDS){
pp->spell_book[i] = atoi(row[1]);
}
int idx = atoi(row[0]);
int id = atoi(row[1]);
if (idx < 0 || idx >= EQEmu::spells::SPELLBOOK_SIZE)
continue;
if (id < 3 || id > SPDAT_RECORDS) // 3 ("Summon Corpse") is the first scribable spell in spells_us.txt
continue;
pp->spell_book[idx] = id;
}
return true;
}
@@ -1535,7 +1546,7 @@ bool ZoneDatabase::SaveCharacterTribute(uint32 character_id, PlayerProfile_Struc
QueryDatabase(query);
/* Save Tributes only if we have values... */
for (int i = 0; i < EQEmu::invtype::TRIBUTE_SIZE; i++){
if (pp->tributes[i].tribute > 0 && pp->tributes[i].tribute != TRIBUTE_NONE){
if (pp->tributes[i].tribute >= 0 && pp->tributes[i].tribute != TRIBUTE_NONE){
std::string query = StringFormat("REPLACE INTO `character_tribute` (id, tier, tribute) VALUES (%u, %u, %u)", character_id, pp->tributes[i].tier, pp->tributes[i].tribute);
QueryDatabase(query);
Log(Logs::General, Logs::None, "ZoneDatabase::SaveCharacterTribute for character ID: %i, tier:%u tribute:%u done", character_id, pp->tributes[i].tier, pp->tributes[i].tribute);
@@ -2471,7 +2482,8 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
"npc_types.charm_avoidance_rating, "
"npc_types.charm_atk, "
"npc_types.skip_global_loot, "
"npc_types.rare_spawn "
"npc_types.rare_spawn, "
"npc_types.stuck_behavior "
"FROM npc_types %s",
where_condition.c_str()
);
@@ -2494,7 +2506,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
temp_npctype_data->race = atoi(row[3]);
temp_npctype_data->class_ = atoi(row[4]);
temp_npctype_data->max_hp = atoi(row[5]);
temp_npctype_data->cur_hp = temp_npctype_data->max_hp;
temp_npctype_data->current_hp = temp_npctype_data->max_hp;
temp_npctype_data->Mana = atoi(row[6]);
temp_npctype_data->gender = atoi(row[7]);
temp_npctype_data->texture = atoi(row[8]);
@@ -2637,7 +2649,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
temp_npctype_data->spellscale = atoi(row[86]);
temp_npctype_data->healscale = atoi(row[87]);
temp_npctype_data->no_target_hotkey = atoi(row[88]) == 1 ? true: false;
temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false: true;
temp_npctype_data->raid_target = atoi(row[89]) == 0 ? false : true;
temp_npctype_data->attack_delay = atoi(row[90]) * 100; // TODO: fix DB
temp_npctype_data->light = (atoi(row[91]) & 0x0F);
@@ -2660,6 +2672,7 @@ const NPCType* ZoneDatabase::LoadNPCTypesData(uint32 npc_type_id, bool bulk_load
temp_npctype_data->skip_global_loot = atoi(row[107]) != 0;
temp_npctype_data->rare_spawn = atoi(row[108]) != 0;
temp_npctype_data->stuck_behavior = atoi(row[109]);
// If NPC with duplicate NPC id already in table,
// free item we attempted to add.
@@ -2775,7 +2788,7 @@ const NPCType* ZoneDatabase::GetMercType(uint32 id, uint16 raceid, uint32 client
tmpNPCType->race = atoi(row[3]);
tmpNPCType->class_ = atoi(row[4]);
tmpNPCType->max_hp = atoi(row[5]);
tmpNPCType->cur_hp = tmpNPCType->max_hp;
tmpNPCType->current_hp = tmpNPCType->max_hp;
tmpNPCType->Mana = atoi(row[6]);
tmpNPCType->gender = atoi(row[7]);
tmpNPCType->texture = atoi(row[8]);
@@ -3644,7 +3657,7 @@ void ZoneDatabase::LoadBuffs(Client *client)
}
// We load up to the most our client supports
max_slots = EQEmu::constants::Lookup(client->ClientVersion())->LongBuffs;
max_slots = EQEmu::spells::StaticLookup(client->ClientVersion())->LongBuffs;
for (int index = 0; index < max_slots; ++index) {
if (!IsValidSpell(buffs[index].spellid))
continue;
@@ -3901,6 +3914,8 @@ bool ZoneDatabase::GetFactionData(FactionMods* fm, uint32 class_mod, uint32 race
}
fm->base = faction_array[faction_id]->base;
fm->min = faction_array[faction_id]->min; // The lowest your personal earned faction can go - before race/class/diety adjustments.
fm->max = faction_array[faction_id]->max; // The highest your personal earned faction can go - before race/class/diety adjustments.
if(class_mod > 0) {
char str[32];
@@ -4047,14 +4062,32 @@ bool ZoneDatabase::LoadFactionData()
faction_array[index] = new Faction;
strn0cpy(faction_array[index]->name, row[1], 50);
faction_array[index]->base = atoi(row[2]);
faction_array[index]->min = MIN_PERSONAL_FACTION;
faction_array[index]->max = MAX_PERSONAL_FACTION;
query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index);
auto modResults = QueryDatabase(query);
if (!modResults.Success())
continue;
// Load in the mimimum and maximum faction that can be earned for this faction
query = StringFormat("SELECT `min` , `max` FROM `faction_base_data` WHERE client_faction_id = %u", index);
auto baseResults = QueryDatabase(query);
if (!baseResults.Success() || baseResults.RowCount() == 0) {
Log(Logs::General, Logs::General, "Faction %d has no base data", (int)index);
}
else {
for (auto modRow = baseResults.begin(); modRow != baseResults.end(); ++modRow) {
faction_array[index]->min = atoi(modRow[0]);
faction_array[index]->max = atoi(modRow[1]);
Log(Logs::General, Logs::None, "Min(%d), Max(%d) for faction (%u)",faction_array[index]->min, faction_array[index]->max, index);
}
}
for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow)
faction_array[index]->mods[modRow[1]] = atoi(modRow[0]);
// Load in modifiers to the faction based on characters race, class and diety.
query = StringFormat("SELECT `mod`, `mod_name` FROM `faction_list_mod` WHERE faction_id = %u", index);
auto modResults = QueryDatabase(query);
if (!modResults.Success())
continue;
for (auto modRow = modResults.begin(); modRow != modResults.end(); ++modRow) {
faction_array[index]->mods[modRow[1]] = atoi(modRow[0]);
}
}
return true;
+41 -39
View File
@@ -271,7 +271,12 @@ public:
void DeleteBuyLines(uint32 CharID);
void UpdateBuyLine(uint32 CharID, uint32 BuySlot, uint32 Quantity);
/* General Character Related Stuff */
/**
************************************************
* Character
************************************************
*/
bool SetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
uint32 GetServerFilters(char* name, ServerSideFilters_Struct *ssfs);
@@ -284,46 +289,43 @@ public:
void RemoveTempFactions(Client *c);
void UpdateItemRecastTimestamps(uint32 char_id, uint32 recast_type, uint32 timestamp);
/* Character Data Loaders */
bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list);
bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
bool DeleteCharacterAAs(uint32 character_id);
bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);
bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id);
bool DeleteCharacterDye(uint32 character_id);
bool DeleteCharacterLeadershipAAs(uint32 character_id);
bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
/* Character Data Saves */
bool SaveCharacterBindPoint(uint32 character_id, const BindStruct &bind, uint32 bind_num);
bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges);
bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color);
bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value);
bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value);
bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id);
bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name);
bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon);
bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBandolier(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterBindPoint(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterData(uint32 character_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool LoadCharacterDisciplines(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterFactionValues(uint32 character_id, faction_map & val_list);
bool LoadCharacterLanguages(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMaterialColor(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterMemmedSpells(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterPotions(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterSkills(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterSpellBook(uint32 character_id, PlayerProfile_Struct* pp);
bool LoadCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
/* Character Data Deletes */
bool DeleteCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool DeleteCharacterDisc(uint32 character_id, uint32 slot_id);
bool DeleteCharacterBandolier(uint32 character_id, uint32 band_id);
bool DeleteCharacterLeadershipAAs(uint32 character_id);
bool DeleteCharacterAAs(uint32 character_id);
bool DeleteCharacterDye(uint32 character_id);
bool SaveCharacterAA(uint32 character_id, uint32 aa_id, uint32 current_level, uint32 charges);
bool SaveCharacterBandolier(uint32 character_id, uint8 bandolier_id, uint8 bandolier_slot, uint32 item_id, uint32 icon, const char* bandolier_name);
bool SaveCharacterBindPoint(uint32 character_id, const BindStruct &bind, uint32 bind_num);
bool SaveCharacterCurrency(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterData(uint32 character_id, uint32 account_id, PlayerProfile_Struct* pp, ExtendedProfile_Struct* m_epp);
bool SaveCharacterDisc(uint32 character_id, uint32 slot_id, uint32 disc_id);
bool SaveCharacterLanguage(uint32 character_id, uint32 lang_id, uint32 value);
bool SaveCharacterLeadershipAA(uint32 character_id, PlayerProfile_Struct* pp);
bool SaveCharacterMaterialColor(uint32 character_id, uint32 slot_id, uint32 color);
bool SaveCharacterMemorizedSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterPotionBelt(uint32 character_id, uint8 potion_id, uint32 item_id, uint32 icon);
bool SaveCharacterSkill(uint32 character_id, uint32 skill_id, uint32 value);
bool SaveCharacterSpell(uint32 character_id, uint32 spell_id, uint32 slot_id);
bool SaveCharacterTribute(uint32 character_id, PlayerProfile_Struct* pp);
/* Character Inventory */
bool NoRentExpired(const char* name);
+2 -1
View File
@@ -36,7 +36,7 @@ struct NPCType
{
char name[64];
char lastname[70];
int32 cur_hp;
int32 current_hp;
int32 max_hp;
float size;
float runspeed;
@@ -143,6 +143,7 @@ struct NPCType
bool untargetable;
bool skip_global_loot;
bool rare_spawn;
int8 stuck_behavior;
};
namespace player_lootitem {
+5 -10
View File
@@ -95,7 +95,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
//unable to find a zone point... is there anything else
//that can be a valid un-zolicited zone request?
CheatDetected(MQZone, zc->x, zc->y, zc->z);
//Todo cheat detection
Message(13, "Invalid unsolicited zone request.");
Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id);
SendZoneCancel(zc);
@@ -130,7 +130,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
//then we assume this is invalid.
if(!zone_point || zone_point->target_zone_id != target_zone_id) {
Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%d'.", GetName(), target_zone_id);
CheatDetected(MQGate, zc->x, zc->y, zc->z);
//todo cheat detection
SendZoneCancel(zc);
return;
}
@@ -252,7 +252,7 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
//for now, there are no other cases...
//could not find a valid reason for them to be zoning, stop it.
CheatDetected(MQZoneUnknownDest, 0.0, 0.0, 0.0);
//todo cheat detection
Log(Logs::General, Logs::Error, "Zoning %s: Invalid unsolicited zone request to zone id '%s'. Not near a zone point.", GetName(), target_zone_name);
SendZoneCancel(zc);
return;
@@ -296,7 +296,6 @@ void Client::Handle_OP_ZoneChange(const EQApplicationPacket *app) {
void Client::SendZoneCancel(ZoneChange_Struct *zc) {
//effectively zone them right back to where they were
//unless we find a better way to stop the zoning process.
SetPortExemption(true);
EQApplicationPacket *outapp = nullptr;
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
@@ -316,8 +315,6 @@ void Client::SendZoneError(ZoneChange_Struct *zc, int8 err)
{
Log(Logs::General, Logs::Error, "Zone %i is not available because target wasn't found or character insufficent level", zc->zoneID);
SetPortExemption(true);
EQApplicationPacket *outapp = nullptr;
outapp = new EQApplicationPacket(OP_ZoneChange, sizeof(ZoneChange_Struct));
ZoneChange_Struct *zc2 = (ZoneChange_Struct*)outapp->pBuffer;
@@ -488,8 +485,6 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
pShortZoneName = database.GetZoneName(zoneID);
database.GetZoneLongName(pShortZoneName, &pZoneName);
SetPortExemption(true);
if(!pZoneName) {
Message(13, "Invalid zone number specified");
safe_delete_array(pZoneName);
@@ -593,7 +588,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
// If we are SoF and later and are respawning from hover, we want the real zone ID, else zero to use the old hack.
//
if(zone->GetZoneID() == zoneID) {
if ((ClientVersionBit() & EQEmu::versions::bit_SoFAndLater) && (!RuleB(Character, RespawnFromHover) || !IsHoveringForRespawn()))
if ((ClientVersionBit() & EQEmu::versions::maskSoFAndLater) && (!RuleB(Character, RespawnFromHover) || !IsHoveringForRespawn()))
gmg->bind_zone_id = 0;
else
gmg->bind_zone_id = zoneID;
@@ -669,7 +664,7 @@ void Client::ZonePC(uint32 zoneID, uint32 instance_id, float x, float y, float z
m_Proximity = glm::vec3(m_Position);
//send out updates to people in zone.
SendPosition();
SentPositionPacket(0.0f, 0.0f, 0.0f, 0.0f, 0);
}
auto outapp =