From ae966e546b93b42193147cf87cf1f9d67e4877a6 Mon Sep 17 00:00:00 2001 From: "Michael Cook (mackal)" Date: Wed, 7 Mar 2018 16:50:24 -0500 Subject: [PATCH] WIP for NPC spell push, off by default for now --- common/ruletypes.h | 1 + zone/attack.cpp | 22 +++------------- zone/map.cpp | 8 ++++++ zone/map.h | 1 + zone/mob.cpp | 1 + zone/mob.h | 6 +++++ zone/mob_ai.cpp | 66 ++++++++++++++++++++++++++++++++++++++++++++++ zone/spells.cpp | 7 +++++ 8 files changed, 94 insertions(+), 18 deletions(-) diff --git a/common/ruletypes.h b/common/ruletypes.h index d80996184..6484a8586 100644 --- a/common/ruletypes.h +++ b/common/ruletypes.h @@ -402,6 +402,7 @@ RULE_BOOL(Spells, IgnoreSpellDmgLvlRestriction, false) // ignore the 5 level spr 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_BOOL(Spells, NPCSpellPush, false) // enable spell push on NPCs RULE_CATEGORY_END() RULE_CATEGORY(Combat) diff --git a/zone/attack.cpp b/zone/attack.cpp index 90a774550..26121134e 100644 --- a/zone/attack.cpp +++ b/zone/attack.cpp @@ -3622,24 +3622,10 @@ void Mob::CommonDamage(Mob* attacker, int &damage, const uint16 spell_id, const a->force = 0.0f; // 2013 change that disabled NPC vs NPC push else a->force *= 0.10f; // force against NPCs is divided by 10 I guess? ex bash is 0.3, parsed 0.03 against an NPC - } - // update NPC stuff - if (a->force != 0.0f) { - auto new_pos = glm::vec3( - m_Position.x + (a->force * g_Math.FastSin(a->hit_heading) + m_Delta.x), - m_Position.y + (a->force * g_Math.FastCos(a->hit_heading) + m_Delta.y), m_Position.z); - if ((!IsNPC() || position_update_melee_push_timer.Check()) && zone->zonemap && - zone->zonemap->CheckLoS( - glm::vec3(m_Position), - new_pos)) { // If we have LoS on the new loc it should be reachable. - if (IsNPC()) { - // Is this adequate? - - Teleport(new_pos); - SendPositionUpdate(); - } - } else { - a->force = 0.0f; // we couldn't move there, so lets not + if (ForcedMovement == 0 && a->force != 0.0f && position_update_melee_push_timer.Check()) { + m_Delta.x += a->force * g_Math.FastSin(a->hit_heading); + m_Delta.y += a->force * g_Math.FastCos(a->hit_heading); + ForcedMovement = 3; } } } diff --git a/zone/map.cpp b/zone/map.cpp index f198dc1de..487a9de0b 100644 --- a/zone/map.cpp +++ b/zone/map.cpp @@ -276,6 +276,14 @@ bool Map::FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) con return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, (RmReal *)&outloc, nullptr, nullptr); } +// returns true if a collision happens +bool Map::DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const { + if(!imp) + return false; + + return imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, (RmReal *)&outnorm, (RmReal *)&distance); +} + inline bool file_exists(const std::string& name) { std::ifstream f(name.c_str()); return f.good(); diff --git a/zone/map.h b/zone/map.h index 162985a36..34638eec1 100644 --- a/zone/map.h +++ b/zone/map.h @@ -43,6 +43,7 @@ public: bool LineIntersectsZoneNoZLeaps(glm::vec3 start, glm::vec3 end, float step_mag, glm::vec3 *result) const; bool CheckLoS(glm::vec3 myloc, glm::vec3 oloc) const; bool FindClosestLoS(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outloc) const; + bool DoCollisionCheck(glm::vec3 myloc, glm::vec3 oloc, glm::vec3 &outnorm, float &distance) const; #ifdef USE_MAP_MMFS bool Load(std::string filename, bool force_mmf_overwrite = false); diff --git a/zone/mob.cpp b/zone/mob.cpp index 6c3808549..d23aaf0b4 100644 --- a/zone/mob.cpp +++ b/zone/mob.cpp @@ -393,6 +393,7 @@ Mob::Mob(const char* in_name, permarooted = (runspeed > 0) ? false : true; movetimercompleted = false; + ForcedMovement = 0; roamer = false; rooted = false; charmed = false; diff --git a/zone/mob.h b/zone/mob.h index d19f4299c..5530e0cab 100644 --- a/zone/mob.h +++ b/zone/mob.h @@ -50,6 +50,8 @@ struct AuraRecord; struct NewSpawn_Struct; struct PlayerPositionUpdateServer_Struct; +const int COLLISION_BOX_SIZE = 4; + namespace EQEmu { struct ItemData; @@ -1314,6 +1316,9 @@ protected: char lastname[64]; glm::vec4 m_Delta; + // just locs around them to double check, if we do expand collision this should be cached on movement + // ideally we should use real models, but this should be quick and work mostly + glm::vec4 m_CollisionBox[COLLISION_BOX_SIZE]; EQEmu::LightSourceProfile m_Light; @@ -1424,6 +1429,7 @@ protected: std::unique_ptr AI_movement_timer; std::unique_ptr AI_target_check_timer; bool movetimercompleted; + int8 ForcedMovement; // push bool permarooted; std::unique_ptr AI_scan_area_timer; std::unique_ptr AI_walking_timer; diff --git a/zone/mob_ai.cpp b/zone/mob_ai.cpp index 4d213dc7d..0b77cb87e 100644 --- a/zone/mob_ai.cpp +++ b/zone/mob_ai.cpp @@ -29,12 +29,16 @@ #include "quest_parser_collection.h" #include "string_ids.h" #include "water_map.h" +#include "fastmath.h" +#include #include #include +#include #include extern EntityList entity_list; +extern FastMath g_Math; extern Zone *zone; @@ -953,6 +957,68 @@ void Mob::AI_Process() { if (!(AI_think_timer->Check() || attack_timer.Check(false))) return; + // we are being pushed, we will hijack this movement timer + // this also needs to be done before casting to have a chance to interrupt + // this flag won't be set if the mob can't be pushed (rooted etc) + if (ForcedMovement && AI_movement_timer->Check()) { + bool bPassed = true; + auto z_off = GetZOffset(); + glm::vec3 normal; + glm::vec3 new_pos = m_Position + m_Delta; + new_pos.z += z_off; + + // 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 + z_off; + + // to right + m_CollisionBox[1].x = m_Position.x + 3.0f * g_Math.FastSin(128.0f); + m_CollisionBox[1].y = m_Position.y + 3.0f * g_Math.FastCos(128.0f); + m_CollisionBox[1].z = m_Position.z + z_off; + + // behind + m_CollisionBox[2].x = m_Position.x + 3.0f * g_Math.FastSin(256.0f); + m_CollisionBox[2].y = m_Position.y + 3.0f * g_Math.FastCos(256.0f); + m_CollisionBox[2].z = m_Position.z + z_off; + + // to left + m_CollisionBox[3].x = m_Position.x + 3.0f * g_Math.FastSin(384.0f); + m_CollisionBox[3].y = m_Position.y + 3.0f * g_Math.FastCos(384.0f); + m_CollisionBox[3].z = m_Position.z + z_off; + + // collision happened, need to move along the wall + float distance = 0.0f, shortest = std::numeric_limits::infinity(); + glm::vec3 tmp_nrm; + for (auto &vec : m_CollisionBox) { + if (zone->zonemap->DoCollisionCheck(vec, new_pos, tmp_nrm, distance)) { + bPassed = false; // lets try with new projection next pass + if (distance < shortest) { + normal = tmp_nrm; + shortest = distance; + } + } + } + } + + if (bPassed) { + ForcedMovement = 0; + m_Delta = glm::vec4(); + Teleport(new_pos); + SendPositionUpdate(); + pLastChange = Timer::GetCurrentTime(); + } else if (--ForcedMovement) { + auto proj = glm::proj(static_cast(m_Delta), normal); + m_Delta.x -= proj.x; + m_Delta.y -= proj.y; + m_Delta.z -= proj.z; + } else { + m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up + } + } + if (IsCasting()) return; diff --git a/zone/spells.cpp b/zone/spells.cpp index d5da2a34f..1c17e80f4 100644 --- a/zone/spells.cpp +++ b/zone/spells.cpp @@ -81,6 +81,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) #include "quest_parser_collection.h" #include "string_ids.h" #include "worldserver.h" +#include "fastmath.h" #include #include @@ -104,6 +105,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org) extern Zone* zone; extern volatile bool is_zone_loaded; extern WorldServer worldserver; +extern FastMath g_Math; using EQEmu::CastingSlot; @@ -3945,6 +3947,11 @@ bool Mob::SpellOnTarget(uint16 spell_id, Mob *spelltar, bool reflect, bool use_r { spelltar->CastToClient()->SetKnockBackExemption(true); } + } else if (RuleB(Spells, NPCSpellPush) && !spelltar->IsRooted() && spelltar->ForcedMovement == 0) { + spelltar->m_Delta.x += action->force * g_Math.FastSin(action->hit_heading); + spelltar->m_Delta.y += action->force * g_Math.FastCos(action->hit_heading); + spelltar->m_Delta.z += action->hit_pitch; + spelltar->ForcedMovement = 6; } }