Merge branch 'master' of github.com:EQEmu/Server

This commit is contained in:
KimLS 2017-06-29 14:14:24 -07:00
commit 1110b284d8
20 changed files with 156 additions and 178 deletions

View File

@ -1,25 +1,36 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50)
-------------------------------------------------------
== 6/28/2017 ==
Akkadius: Fixed issues with Z correctness when NPCs are pathing on normal grids
Akkadius: Fixed issues with Z correctness when NPCs are engaged with players following
Akkadius: NPC corpses should fall into the ground far less
== 6/25/2017 ==
Akkadius: New rules made by developers are now automatically created when world boots up, this keeps
from having to issue schema SQL updates every time rules are added.
- Whenever a rule isn't present in the database, it will be automatically created
Akkadius: Sped up saylink retrieval x1000 helpful for dialogues, plugins with many saylinks
== 4/16/2017 ==
KLS: Merge eqstream branch
-UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
-TCP Server to Server connection stack completely rewritten.
-Server connections reconnect much more reliably and quickly now.
-Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
-Protocol behind the tcp connections has changed (see breaking changes section).
-API significantly changed and should be easier to write new servers or handlers for.
-Telnet console connection has been separated out from the current port (see breaking changes section).
-Because of changes to the TCP stack, lsreconnect and echo have been disabled.
-The server tic rate has been changed to be approx 30 fps from 500+ fps.
-Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
- UDP client stack completely rewritten should both have better throughput and recover better (peq has had far fewer reports of desyncs).
- TCP Server to Server connection stack completely rewritten.
- Server connections reconnect much more reliably and quickly now.
- Now supports optional packet encryption via libsodium (https://download.libsodium.org/doc/).
- Protocol behind the tcp connections has changed (see breaking changes section).
- API significantly changed and should be easier to write new servers or handlers for.
- Telnet console connection has been separated out from the current port (see breaking changes section).
- Because of changes to the TCP stack, lsreconnect and echo have been disabled.
- The server tic rate has been changed to be approx 30 fps from 500+ fps.
- Changed how missiles and movement were calculated slightly to account for this (Missiles in particular are not perfect but close enough).
-Breaking changes:
-Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
-To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
-You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
-The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
-Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
-To enable telnet you need to add a telnet tag in the world section of configuration such as:
- Breaking changes:
- Users who use the cmake install feature should be aware that the install directory is now %cmake_install_dir%/bin instead of just %cmake_install_dir%/
- To support new features such as encryption the underlying protocol had to change... however some servers such as the public login server will be slow to change so we've included a compatibility layer for legacy login connections:
- You should add <legacy>1</legacy> to the login section of your configuration file when connecting to a server that is using the old protocol.
- The central eqemu login server uses the old protocol and probably will for the forseeable future so if your server is connecting to it be sure to add that tag to your configuration file in that section.
- Telnet no longer uses the same port as the Server to Server connection and because of this the tcp tag no longer has any effect on telnet connections.
- To enable telnet you need to add a telnet tag in the world section of configuration such as:
<telnet ip="0.0.0.0" port="9001" enabled="true"/>
== 4/1/2017 ==

View File

@ -221,7 +221,7 @@ namespace EQ
resend_delay_ms = 150;
resend_delay_factor = 1.5;
resend_delay_min = 150;
resend_delay_max = 4000;
resend_delay_max = 1000;
connect_delay_ms = 500;
stale_connection_ms = 90000;
connect_stale_ms = 5000;

View File

@ -274,6 +274,7 @@ RULE_BOOL(Map, FixPathingZWhenLoading, true) //increases zone boot times a bit
RULE_BOOL(Map, FixPathingZAtWaypoints, false) //alternative to `WhenLoading`, accomplishes the same thing but does it at each waypoint instead of once at boot time.
RULE_BOOL(Map, FixPathingZWhenMoving, false) //very CPU intensive, but helps hopping with widely spaced waypoints.
RULE_BOOL(Map, FixPathingZOnSendTo, false) //try to repair Z coords in the SendTo routine as well.
RULE_BOOL(Map, FixZWhenMoving, true) // Automatically fix NPC Z coordinates when moving/pathing/engaged (Far less CPU intensive than its predecessor)
RULE_REAL(Map, FixPathingZMaxDeltaMoving, 20) //at runtime while pathing: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaWaypoint, 20) //at runtime at each waypoint: max change in Z to allow the BestZ code to apply.
RULE_REAL(Map, FixPathingZMaxDeltaSendTo, 20) //at runtime in SendTo: max change in Z to allow the BestZ code to apply.
@ -397,6 +398,7 @@ RULE_BOOL(Spells, FlatItemExtraSpellAmt, false) // allow SpellDmg stat to affect
RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spread on applying SpellDmg
RULE_BOOL(Spells, AllowItemTGB, false) // TGB doesn't work with items on live, custom servers want it though
RULE_BOOL(Spells, NPCInnateProcOverride, true) // NPC innate procs override the target type to single target.
RULE_BOOL(Spells, OldRainTargets, false) // use old incorrectly implemented max targets for rains
RULE_CATEGORY_END()
RULE_CATEGORY(Combat)

View File

@ -30,7 +30,7 @@
Manifest: https://github.com/EQEmu/Server/blob/master/utils/sql/db_update_manifest.txt
*/
#define CURRENT_BINARY_DATABASE_VERSION 9111
#define CURRENT_BINARY_DATABASE_VERSION 9112
#ifdef BOTS
#define CURRENT_BINARY_BOTS_DATABASE_VERSION 9017
#else

View File

@ -42,6 +42,7 @@ WeaponSkillFalloff = RuleR.Get(Rule.WeaponSkillFalloff);
ArcheryHitPenalty = RuleR.Get(Rule.ArcheryHitPenalty);
UseOldDamageIntervalRules = RuleB.Get(Rule.UseOldDamageIntervalRules);
CriticalMessageRange = RuleI.Get(Rule.CriticalDamage);
function MeleeMitigation(e)
e.IgnoreDefault = true;
@ -238,9 +239,9 @@ function TryCriticalHit(e)
e.hit.damage_done = (e.hit.damage_done * SlayDmgBonus * 2.25) / 100;
if (self:GetGender() == 1) then
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done));
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses her target! (%d)', self:GetCleanName(), e.hit.damage_done));
else
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done));
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s\'s holy blade cleanses his target! (%d)', self:GetCleanName(), e.hit.damage_done));
end
return e;
@ -326,15 +327,15 @@ function TryCriticalHit(e)
end
if (crip_success) then
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done));
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s lands a Crippling Blow! (%d)', self:GetCleanName(), e.hit.damage_done));
if (defender:GetLevel() <= 55 and not defender:GetSpecialAbility(SpecialAbility.unstunable)) then
defender:Emote("staggers.");
defender:Stun(0);
end
elseif (deadlySuccess) then
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done));
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a Deadly Strike! (%d)', self:GetCleanName(), e.hit.damage_done));
else
entity_list:FilteredMessageClose(self, false, 200, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done));
entity_list:FilteredMessageClose(self, false, CriticalMessageRange, MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)', self:GetCleanName(), e.hit.damage_done));
end
end
end
@ -379,9 +380,9 @@ function TryPetCriticalHit(self, defender, hit)
local entity_list = eq.get_entity_list();
critMod = critMod + GetCritDmgMod(self, hit.skill) * 2;
hit.damage_done = (hit.damage_done * critMod) / 100;
entity_list:FilteredMessageClose(this, false, 200,
entity_list:FilteredMessageClose(this, false, CriticalMessageRange,
MT.CritMelee, Filter.MeleeCrits, string.format('%s scores a critical hit! (%d)',
self:GetCleanName(), hit.damage_done));
self:GetCleanName(), e.hit.damage_done));
end
end
@ -750,4 +751,4 @@ function ApplyMeleeDamageBonus(e)
e.hit.damage_done = e.hit.damage_done + (e.hit.damage_done * dmgbonusmod / 100);
return e;
end
end

View File

@ -365,6 +365,7 @@
9109|2017_04_08_doors_disable_timer.sql|SHOW COLUMNS FROM `doors` LIKE 'disable_timer'|empty|
9110|2017_04_10_graveyard.sql|show index from graveyard WHERE key_name = 'zone_id_nonunique'|empty|
9111|2017_06_24_saylink_index.sql|SHOW INDEX FROM `saylink` WHERE `key_name` = 'phrase_index'|empty|
9112|2017_06_24_rule_values_expand.sql|SHOW COLUMNS FROM rule_values WHERE Field = 'rule_value' and Type = 'varchar(30)'|empty|
# Upgrade conditions:
# This won't be needed after this system is implemented, but it is used database that are not

View File

@ -0,0 +1,2 @@
ALTER TABLE `rule_values`
MODIFY COLUMN `rule_value` varchar(30) CHARACTER SET latin1 COLLATE latin1_swedish_ci NOT NULL DEFAULT '' AFTER `rule_name`;

View File

@ -332,6 +332,8 @@ int main(int argc, char** argv) {
database.ClearMerchantTemp();
}
RuleManager::Instance()->SaveRules(&database);
Log(Logs::General, Logs::World_Server, "Loading EQ time of day..");
TimeOfDay_Struct eqTime;
time_t realtime;

View File

@ -53,9 +53,8 @@ extern WorldServer worldserver;
extern EntityList entity_list;
extern Zone* zone;
EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon)
EQEmu::skills::SkillType Mob::AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse)
{
EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt;
// Determine animation
int type = 0;
if (weapon && weapon->IsClassCommon()) {
@ -1924,7 +1923,7 @@ bool NPC::Attack(Mob* other, int Hand, bool bRiposte, bool IsStrikethrough, bool
//do attack animation regardless of whether or not we can hit below
int16 charges = 0;
EQEmu::ItemInstance weapon_inst(weapon, charges);
my_hit.skill = AttackAnimation(Hand, &weapon_inst);
my_hit.skill = AttackAnimation(Hand, &weapon_inst, my_hit.skill);
//basically "if not immune" then do the attack
if (weapon_damage > 0) {
@ -2388,6 +2387,13 @@ bool NPC::Death(Mob* killer_mob, int32 damage, uint16 spell, EQEmu::skills::Skil
entity_list.UnMarkNPC(GetID());
entity_list.RemoveNPC(GetID());
/* Fix Z on Corpse Creation */
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float new_z = zone->zonemap->FindBestZ(dest, nullptr);
corpse->SetFlyMode(1);
corpse->GMMove(m_Position.x, m_Position.y, new_z + 5, m_Position.w);
this->SetID(0);
if (killer != 0 && emoteid != 0)

View File

@ -68,6 +68,7 @@ Beacon::Beacon(Mob *at_mob, int lifetime)
resist_adjust = 0;
spell_iterations = 0;
caster_id = 0;
max_targets = 4; // default
if(lifetime)
remove_timer.Start();
@ -93,10 +94,10 @@ bool Beacon::Process()
)
{
Mob *caster = entity_list.GetMob(caster_id);
if(caster && spell_iterations--)
if(caster && spell_iterations-- && max_targets)
{
bool affect_caster = (!caster->IsNPC() && !caster->IsAIControlled()); //NPC AE spells do not affect the NPC caster
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust);
entity_list.AESpell(caster, this, spell_id, affect_caster, resist_adjust, &max_targets);
}
else
{
@ -126,6 +127,8 @@ void Beacon::AELocationSpell(Mob *caster, uint16 cast_spell_id, int16 resist_adj
this->resist_adjust = resist_adjust;
spell_iterations = spells[spell_id].AEDuration / 2500;
spell_iterations = spell_iterations < 1 ? 1 : spell_iterations; // at least 1
if (spells[spell_id].aemaxtargets)
max_targets = spells[spell_id].aemaxtargets;
spell_timer.Start(2500);
spell_timer.Trigger();
}

View File

@ -56,6 +56,7 @@ protected:
int16 resist_adjust;
int spell_iterations;
Timer spell_timer;
int max_targets;
uint16 caster_id;
private:

View File

@ -694,7 +694,7 @@ void EntityList::AETaunt(Client* taunter, float range, int32 bonus_hate)
// causes caster to hit every mob within dist range of center with
// spell_id.
// NPC spells will only affect other NPCs with compatible faction
void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust)
void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster, int16 resist_adjust, int *max_targets)
{
Mob *curmob = nullptr;
@ -703,12 +703,25 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
float min_range2 = spells[spell_id].min_range * spells[spell_id].min_range;
float dist_targ = 0;
const auto &position = spells[spell_id].targettype == ST_Ring ? caster->GetTargetRingLocation() : static_cast<glm::vec3>(center->GetPosition());
glm::vec2 min = { position.x - dist, position.y - dist };
glm::vec2 max = { position.x + dist, position.y + dist };
bool bad = IsDetrimentalSpell(spell_id);
bool isnpc = caster->IsNPC();
int MAX_TARGETS_ALLOWED = 4;
if (spells[spell_id].aemaxtargets)
MAX_TARGETS_ALLOWED = spells[spell_id].aemaxtargets;
if (RuleB(Spells, OldRainTargets))
max_targets = nullptr; // ignore it!
// if we have a passed in value, use it, otherwise default to data
// detrimental Target AEs have a default value of 4 for PCs and unlimited for NPCs
int max_targets_allowed = 0; // unlimited
if (max_targets) // rains pass this in since they need to preserve the count through waves
max_targets_allowed = *max_targets;
else if (spells[spell_id].aemaxtargets)
max_targets_allowed = spells[spell_id].aemaxtargets;
else if (IsTargetableAESpell(spell_id) && bad && !isnpc)
max_targets_allowed = 4;
int iCounter = 0;
@ -732,13 +745,10 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
continue;
if (spells[spell_id].pcnpc_only_flag == 2 && (curmob->IsClient() || curmob->IsMerc()))
continue;
if (!IsWithinAxisAlignedBox(static_cast<glm::vec2>(curmob->GetPosition()), min, max))
continue;
if (spells[spell_id].targettype == ST_Ring) {
dist_targ = DistanceSquared(static_cast<glm::vec3>(curmob->GetPosition()), caster->GetTargetRingLocation());
}
else if (center) {
dist_targ = DistanceSquared(curmob->GetPosition(), center->GetPosition());
}
dist_targ = DistanceSquared(curmob->GetPosition(), position);
if (dist_targ > dist2) //make sure they are in range
continue;
@ -761,7 +771,7 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
if (bad) {
if (!caster->IsAttackAllowed(curmob, true))
continue;
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
if (center && !spells[spell_id].npc_no_los && !center->CheckLosFN(curmob))
continue;
if (!center && !spells[spell_id].npc_no_los && !caster->CheckLosFN(caster->GetTargetRingX(), caster->GetTargetRingY(), caster->GetTargetRingZ(), curmob->GetSize()))
continue;
@ -776,22 +786,17 @@ void EntityList::AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_
}
curmob->CalcSpellPowerDistanceMod(spell_id, dist_targ);
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
//if we get here... cast the spell.
if (IsTargetableAESpell(spell_id) && bad) {
if (iCounter < MAX_TARGETS_ALLOWED) {
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
} else {
if (spells[spell_id].aemaxtargets && iCounter < spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
if (!spells[spell_id].aemaxtargets)
caster->SpellOnTarget(spell_id, curmob, false, true, resist_adjust);
}
if (!isnpc || spells[spell_id].aemaxtargets) //npcs are not target limited (unless casting a spell with a target limit)...
if (max_targets_allowed) { // if we have a limit, increment count
iCounter++;
if (iCounter >= max_targets_allowed) // we done
break;
}
}
if (max_targets && max_targets_allowed)
*max_targets = *max_targets - iCounter;
}
void EntityList::MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster)

View File

@ -357,7 +357,7 @@ public:
void AEAttack(Mob *attacker, float dist, int Hand = EQEmu::inventory::slotPrimary, int count = 0, bool IsFromSpell = false);
void AETaunt(Client *caster, float range=0, int32 bonus_hate=0);
void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0);
void AESpell(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true, int16 resist_adjust = 0, int *max_targets = nullptr);
void MassGroupBuff(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);
void AEBardPulse(Mob *caster, Mob *center, uint16 spell_id, bool affect_caster = true);

View File

@ -112,7 +112,9 @@ Mob::Mob(const char* in_name,
m_Position(position),
tmHidden(-1),
mitigation_ac(0),
m_specialattacks(eSpecialAttacks::None)
m_specialattacks(eSpecialAttacks::None),
fix_z_timer(1000),
fix_z_timer_engaged(100)
{
targeted = 0;
tar_ndx=0;

View File

@ -233,7 +233,7 @@ public:
inline bool SeeImprovedHide() const { return see_improved_hide; }
bool IsInvisible(Mob* other = 0) const;
void SetInvisible(uint8 state);
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon);
EQEmu::skills::SkillType AttackAnimation(int Hand, const EQEmu::ItemInstance* weapon, EQEmu::skills::SkillType skillinuse = EQEmu::skills::Skill1HBlunt);
//Song
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
@ -913,6 +913,7 @@ public:
float GetGroundZ(float new_x, float new_y, float z_offset=0.0);
void SendTo(float new_x, float new_y, float new_z);
void SendToFixZ(float new_x, float new_y, float new_z);
void FixZ();
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; }
@ -1373,6 +1374,8 @@ protected:
bool flee_mode;
Timer flee_timer;
Timer fix_z_timer;
Timer fix_z_timer_engaged;
bool pAIControlled;
bool roamer;

View File

@ -743,6 +743,10 @@ void Client::AI_Process()
if(RuleB(Combat, EnableFearPathing)){
if(currently_fleeing) {
if (fix_z_timer_engaged.Check())
this->FixZ();
if(IsRooted()) {
//make sure everybody knows were not moving, for appearance sake
if(IsMoving())
@ -782,6 +786,7 @@ void Client::AI_Process()
}
return;
}
}
}
@ -991,6 +996,12 @@ 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(DistanceNoZ(this->GetPosition(), this->GetTarget()->GetPosition()) > 50)
this->FixZ();
if (!(m_PlayerState & static_cast<uint32>(PlayerState::Aggressive)))
SendAddPlayerState(PlayerState::Aggressive);
// we are prevented from getting here if we are blind and don't have a target in range

View File

@ -541,6 +541,9 @@ int main(int argc, char** argv) {
if (previous_loaded && !current_loaded) {
process_timer.Stop();
process_timer.Start(1000, true);
uint32 shutdown_timer = database.getZoneShutDownDelay(zone->GetZoneID(), zone->GetInstanceVersion());
zone->StartShutdownTimer(shutdown_timer);
}
else if (!previous_loaded && current_loaded) {
process_timer.Stop();

View File

@ -2237,21 +2237,7 @@ bool Mob::SpellFinished(uint16 spell_id, Mob *spell_target, CastingSlot slot, ui
// special ae duration spell
ae_center->CastToBeacon()->AELocationSpell(this, spell_id, resist_adjust);
} else {
// regular PB AE or targeted AE spell - spell_target is null if PB
if(spell_target) // this must be an AETarget spell
{
bool cast_on_target = true;
if (spells[spell_id].targettype == ST_TargetAENoPlayersPets && spell_target->IsPetOwnerClient())
cast_on_target = false;
if (spells[spell_id].targettype == ST_AreaClientOnly && !spell_target->IsClient())
cast_on_target = false;
if (spells[spell_id].targettype == ST_AreaNPCOnly && !spell_target->IsNPC())
cast_on_target = false;
// affect the target too
if (cast_on_target)
SpellOnTarget(spell_id, spell_target, false, true, resist_adjust);
}
// unsure if we actually need this? Need to find some spell examples
if(ae_center && ae_center == this && IsBeneficialSpell(spell_id))
SpellOnTarget(spell_id, this);

View File

@ -513,39 +513,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.y = new_y;
m_Position.z = new_z;
uint8 NPCFlyMode = 0;
if (IsNPC()) {
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
NPCFlyMode = 1;
}
//fix up pathing Z
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
{
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
{
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
if ((newz > -2000) &&
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
{
if ((std::abs(x - m_Position.x) < 0.5) &&
(std::abs(y - m_Position.y) < 0.5)) {
if (std::abs(z - m_Position.z) <=
RuleR(Map, FixPathingZMaxDeltaMoving))
m_Position.z = z;
else
m_Position.z = newz + 1;
}
else
m_Position.z = newz + 1;
}
}
}
if(fix_z_timer.Check())
this->FixZ();
tar_ndx++;
return true;
@ -651,37 +620,8 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, int speed, boo
m_Position.w = CalculateHeadingToTarget(x, y);
}
uint8 NPCFlyMode = 0;
if (IsNPC()) {
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
NPCFlyMode = 1;
}
//fix up pathing Z
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving)) {
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
{
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr);
if ((newz > -2000) &&
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
{
if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) {
if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
m_Position.z = z;
else
m_Position.z = newz + 1;
}
else
m_Position.z = newz + 1;
}
}
}
if (fix_z_timer.Check())
this->FixZ();
SetMoving(true);
moved = true;
@ -769,39 +709,8 @@ bool Mob::CalculateNewPosition(float x, float y, float z, int speed, bool checkZ
Log(Logs::Detail, Logs::AI, "Next position (%.3f, %.3f, %.3f)", m_Position.x, m_Position.y, m_Position.z);
}
uint8 NPCFlyMode = 0;
if (IsNPC()) {
if (CastToNPC()->GetFlyMode() == 1 || CastToNPC()->GetFlyMode() == 2)
NPCFlyMode = 1;
}
//fix up pathing Z
if (!NPCFlyMode && checkZ && zone->HasMap() && RuleB(Map, FixPathingZWhenMoving))
{
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
{
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float newz = zone->zonemap->FindBestZ(dest, nullptr) + 2.0f;
Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
if ((newz > -2000) &&
std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving)) // Sanity check.
{
if (std::abs(x - m_Position.x) < 0.5 && std::abs(y - m_Position.y) < 0.5) {
if (std::abs(z - m_Position.z) <= RuleR(Map, FixPathingZMaxDeltaMoving))
m_Position.z = z;
else
m_Position.z = newz + 1;
}
else
m_Position.z = newz + 1;
}
}
}
if (fix_z_timer.Check())
this->FixZ();
//OP_MobUpdate
if ((old_test_vector != test_vector) || tar_ndx>20) { //send update
@ -943,9 +852,6 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
m_Position.y = new_y;
m_Position.z = new_z + 0.1;
//fix up pathing Z, this shouldent be needed IF our waypoints
//are corrected instead
if (zone->HasMap() && RuleB(Map, FixPathingZOnSendTo))
{
if (!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
@ -955,7 +861,7 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
float newz = zone->zonemap->FindBestZ(dest, nullptr);
Log(Logs::Detail, Logs::AI, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
Log(Logs::Moderate, Logs::Pathing, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz, m_Position.x, m_Position.y, m_Position.z);
if ((newz > -2000) && std::abs(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaSendTo)) // Sanity check.
m_Position.z = newz + 1;
@ -963,6 +869,39 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
}
}
void Mob::FixZ() {
BenchTimer timer;
timer.reset();
if (zone->HasMap() && RuleB(Map, FixZWhenMoving) && (flymode != 1 && flymode != 2))
{
if (!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
(zone->HasWaterMap() && !zone->watermap->InWater(glm::vec3(m_Position))))
{
glm::vec3 dest(m_Position.x, m_Position.y, m_Position.z);
float new_z = zone->zonemap->FindBestZ(dest, nullptr);
auto duration = timer.elapsed();
Log(
Logs::Moderate,
Logs::Pathing,
"Mob::FixZ() (%s) returned %4.3f at %4.3f, %4.3f, %4.3f - Took %lf",
this->GetCleanName(),
new_z,
m_Position.x,
m_Position.y,
m_Position.z,
duration
);
if ((new_z > -2000) && std::abs(new_z - dest.z) < RuleR(Map, FixPathingZMaxDeltaMoving))
m_Position.z = new_z + 1;
}
}
}
int ZoneDatabase::GetHighestGrid(uint32 zoneid) {
std::string query = StringFormat("SELECT COALESCE(MAX(id), 0) FROM grid WHERE zoneid = %i", zoneid);

View File

@ -1414,11 +1414,11 @@ bool Zone::HasWeather()
void Zone::StartShutdownTimer(uint32 set_time) {
if (set_time > autoshutdown_timer.GetRemainingTime()) {
if (set_time == (RuleI(Zone, AutoShutdownDelay)))
{
if (set_time == (RuleI(Zone, AutoShutdownDelay))) {
set_time = database.getZoneShutDownDelay(GetZoneID(), GetInstanceVersion());
}
autoshutdown_timer.Start(set_time, false);
autoshutdown_timer.SetTimer(set_time);
Log(Logs::General, Logs::Zone_Server, "Zone::StartShutdownTimer set to %u", set_time);
}
}