WIP for NPC spell push, off by default for now

This commit is contained in:
Michael Cook (mackal) 2018-03-07 16:50:24 -05:00
parent 0d63c8b9ef
commit ae966e546b
8 changed files with 94 additions and 18 deletions

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -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);

View File

@ -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;

View File

@ -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<Timer> AI_movement_timer;
std::unique_ptr<Timer> AI_target_check_timer;
bool movetimercompleted;
int8 ForcedMovement; // push
bool permarooted;
std::unique_ptr<Timer> AI_scan_area_timer;
std::unique_ptr<Timer> AI_walking_timer;

View File

@ -29,12 +29,16 @@
#include "quest_parser_collection.h"
#include "string_ids.h"
#include "water_map.h"
#include "fastmath.h"
#include <glm/gtx/projection.hpp>
#include <algorithm>
#include <iostream>
#include <limits>
#include <math.h>
extern EntityList entity_list;
extern FastMath g_Math;
extern Zone *zone;
@ -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<float>::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<glm::vec3>(m_Delta), normal);
m_Delta.x -= proj.x;
m_Delta.y -= proj.y;
m_Delta.z -= proj.z;
} else {
m_Delta = glm::vec4(); // well, we failed to find a spot to be forced to, lets give up
}
}
if (IsCasting())
return;

View File

@ -81,6 +81,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
#include "quest_parser_collection.h"
#include "string_ids.h"
#include "worldserver.h"
#include "fastmath.h"
#include <assert.h>
#include <math.h>
@ -104,6 +105,7 @@ Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
extern Zone* zone;
extern volatile bool is_zone_loaded;
extern WorldServer worldserver;
extern FastMath g_Math;
using EQEmu::CastingSlot;
@ -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;
}
}