mirror of
https://github.com/EQEmu/Server.git
synced 2026-05-31 04:56:20 +00:00
Merge remote-tracking branch 'upstream/master' into Warning_Cleanup
Conflicts: zone/Map.cpp
This commit is contained in:
+6
-2
@@ -66,7 +66,7 @@ SET(zone_sources
|
||||
horse.cpp
|
||||
inventory.cpp
|
||||
loottables.cpp
|
||||
Map.cpp
|
||||
map.cpp
|
||||
merc.cpp
|
||||
mob.cpp
|
||||
MobAI.cpp
|
||||
@@ -95,6 +95,7 @@ SET(zone_sources
|
||||
questmgr.cpp
|
||||
QuestParserCollection.cpp
|
||||
raids.cpp
|
||||
RaycastMesh.cpp
|
||||
spawn2.cpp
|
||||
spawn2.h
|
||||
spawngroup.cpp
|
||||
@@ -183,6 +184,7 @@ SET(zone_headers
|
||||
QuestParserCollection.h
|
||||
raid.h
|
||||
raids.h
|
||||
RaycastMesh.h
|
||||
skills.h
|
||||
spawn2.cpp
|
||||
spawn2.h
|
||||
@@ -230,7 +232,9 @@ IF(UNIX)
|
||||
TARGET_LINK_LIBRARIES(zone "${CMAKE_DL_LIBS}")
|
||||
TARGET_LINK_LIBRARIES(zone "z")
|
||||
TARGET_LINK_LIBRARIES(zone "m")
|
||||
TARGET_LINK_LIBRARIES(zone "rt")
|
||||
IF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(zone "rt")
|
||||
ENDIF(NOT DARWIN)
|
||||
TARGET_LINK_LIBRARIES(zone "pthread")
|
||||
ADD_DEFINITIONS(-fPIC)
|
||||
ENDIF(UNIX)
|
||||
|
||||
-1026
File diff suppressed because it is too large
Load Diff
+233
-7
@@ -530,6 +530,7 @@ void NPC::AI_Start(uint32 iMoveDelay) {
|
||||
if (NPCTypedata) {
|
||||
AI_AddNPCSpells(NPCTypedata->npc_spells_id);
|
||||
ProcessSpecialAbilities(NPCTypedata->special_abilities);
|
||||
AI_AddNPCSpellsEffects(NPCTypedata->npc_spells_effects_id);
|
||||
}
|
||||
|
||||
SendTo(GetX(), GetY(), GetZ());
|
||||
@@ -766,7 +767,7 @@ void Client::AI_Process()
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z,
|
||||
Map::Vertex Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z,
|
||||
GetFearSpeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if(WaypointChanged)
|
||||
@@ -922,7 +923,7 @@ void Client::AI_Process()
|
||||
else
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
VERTEX Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
|
||||
Map::Vertex Goal = UpdatePath(GetTarget()->GetX(), GetTarget()->GetY(), GetTarget()->GetZ(),
|
||||
GetRunspeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if(WaypointChanged)
|
||||
@@ -1034,7 +1035,7 @@ void Mob::AI_Process() {
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
VERTEX Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z,
|
||||
Map::Vertex Goal = UpdatePath(fear_walkto_x, fear_walkto_y, fear_walkto_z,
|
||||
GetFearSpeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if(WaypointChanged)
|
||||
@@ -1372,7 +1373,7 @@ void Mob::AI_Process() {
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
|
||||
VERTEX Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(),
|
||||
Map::Vertex Goal = UpdatePath(target->GetX(), target->GetY(), target->GetZ(),
|
||||
GetRunspeed(), WaypointChanged, NodeReached);
|
||||
|
||||
if(WaypointChanged)
|
||||
@@ -1627,11 +1628,32 @@ void NPC::AI_DoMovement() {
|
||||
|
||||
if (gridno > 0 || cur_wp==-2) {
|
||||
if (movetimercompleted==true) { // time to pause at wp is over
|
||||
|
||||
int32 spawn_id = this->GetSpawnPointID();
|
||||
LinkedListIterator<Spawn2*> iterator(zone->spawn2_list);
|
||||
iterator.Reset();
|
||||
Spawn2 *found_spawn = nullptr;
|
||||
|
||||
while(iterator.MoreElements())
|
||||
{
|
||||
Spawn2* cur = iterator.GetData();
|
||||
iterator.Advance();
|
||||
if(cur->GetID() == spawn_id)
|
||||
{
|
||||
found_spawn = cur;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(true); //depop and resart spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
|
||||
CastToNPC()->Depop(false);//depop without spawn timer
|
||||
if(found_spawn)
|
||||
found_spawn->SetNPCPointerNull();
|
||||
}
|
||||
else {
|
||||
movetimercompleted=false;
|
||||
@@ -1698,7 +1720,7 @@ void NPC::AI_DoMovement() {
|
||||
{
|
||||
bool WaypointChanged;
|
||||
bool NodeReached;
|
||||
VERTEX Goal = UpdatePath(cur_wp_x, cur_wp_y, cur_wp_z, walksp, WaypointChanged, NodeReached);
|
||||
Map::Vertex Goal = UpdatePath(cur_wp_x, cur_wp_y, cur_wp_z, walksp, WaypointChanged, NodeReached);
|
||||
if(WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
@@ -1737,7 +1759,7 @@ void NPC::AI_DoMovement() {
|
||||
if(!((x_pos == guard_x) && (y_pos == guard_y) && (z_pos == guard_z)))
|
||||
{
|
||||
bool WaypointChanged, NodeReached;
|
||||
VERTEX Goal = UpdatePath(guard_x, guard_y, guard_z, walksp, WaypointChanged, NodeReached);
|
||||
Map::Vertex Goal = UpdatePath(guard_x, guard_y, guard_z, walksp, WaypointChanged, NodeReached);
|
||||
if(WaypointChanged)
|
||||
tar_ndx = 20;
|
||||
|
||||
@@ -2277,6 +2299,7 @@ create table npc_spells_entries (
|
||||
*/
|
||||
|
||||
bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID);
|
||||
bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
|
||||
bool Compare_AI_Spells(AISpells_Struct i, AISpells_Struct j);
|
||||
|
||||
bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
@@ -2351,6 +2374,105 @@ bool NPC::AI_AddNPCSpells(uint32 iDBSpellsID) {
|
||||
return true;
|
||||
}
|
||||
|
||||
bool NPC::AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
|
||||
|
||||
npc_spells_effects_id = iDBSpellsEffectsID;
|
||||
AIspellsEffects.clear();
|
||||
|
||||
if (iDBSpellsEffectsID == 0)
|
||||
return false;
|
||||
|
||||
DBnpcspellseffects_Struct* spell_effects_list = database.GetNPCSpellsEffects(iDBSpellsEffectsID);
|
||||
|
||||
if (!spell_effects_list) {
|
||||
return false;
|
||||
}
|
||||
|
||||
DBnpcspellseffects_Struct* parentlist = database.GetNPCSpellsEffects(spell_effects_list->parent_list);
|
||||
|
||||
uint32 i;
|
||||
#if MobAI_DEBUG_Spells >= 10
|
||||
std::cout << "Loading NPCSpellsEffects onto " << this->GetName() << ": dbspellseffectsid=" << iDBSpellsEffectsID;
|
||||
if (spell_effects_list) {
|
||||
std::cout << " (found, " << spell_effects_list->numentries << "), parentlist=" << spell_effects)list->parent_list;
|
||||
if (spell_effects_list->parent_list) {
|
||||
if (parentlist) {
|
||||
std::cout << " (found, " << parentlist->numentries << ")";
|
||||
}
|
||||
else
|
||||
std::cout << " (not found)";
|
||||
}
|
||||
}
|
||||
else
|
||||
std::cout << " (not found)";
|
||||
std::cout << std::endl;
|
||||
#endif
|
||||
|
||||
if (parentlist) {
|
||||
for (i=0; i<parentlist->numentries; i++) {
|
||||
if (GetLevel() >= parentlist->entries[i].minlevel && GetLevel() <= parentlist->entries[i].maxlevel && parentlist->entries[i].spelleffectid > 0) {
|
||||
if (!IsSpellEffectInList(spell_effects_list, parentlist->entries[i].spelleffectid, parentlist->entries[i].base,
|
||||
parentlist->entries[i].limit, parentlist->entries[i].max))
|
||||
{
|
||||
AddSpellEffectToNPCList(parentlist->entries[i].spelleffectid,
|
||||
parentlist->entries[i].base, parentlist->entries[i].limit,
|
||||
parentlist->entries[i].max);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (i=0; i<spell_effects_list->numentries; i++) {
|
||||
if (GetLevel() >= spell_effects_list->entries[i].minlevel && GetLevel() <= spell_effects_list->entries[i].maxlevel && spell_effects_list->entries[i].spelleffectid > 0) {
|
||||
AddSpellEffectToNPCList(spell_effects_list->entries[i].spelleffectid,
|
||||
spell_effects_list->entries[i].base, spell_effects_list->entries[i].limit,
|
||||
spell_effects_list->entries[i].max);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void NPC::ApplyAISpellEffects(StatBonuses* newbon)
|
||||
{
|
||||
if (!AI_HasSpellsEffects())
|
||||
return;
|
||||
|
||||
for(int i=0; i < AIspellsEffects.size(); i++)
|
||||
{
|
||||
ApplySpellsBonuses(0, 0, newbon, 0, false, 0,-1,
|
||||
true, AIspellsEffects[i].spelleffectid, AIspellsEffects[i].base, AIspellsEffects[i].limit,AIspellsEffects[i].max);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// adds a spell to the list, taking into account priority and resorting list as needed.
|
||||
void NPC::AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max)
|
||||
{
|
||||
|
||||
if(!iSpellEffectID)
|
||||
return;
|
||||
|
||||
HasAISpellEffects = true;
|
||||
AISpellsEffects_Struct t;
|
||||
|
||||
t.spelleffectid = iSpellEffectID;
|
||||
t.base = base;
|
||||
t.limit = limit;
|
||||
t.max = max;
|
||||
AIspellsEffects.push_back(t);
|
||||
}
|
||||
|
||||
bool IsSpellEffectInList(DBnpcspellseffects_Struct* spelleffect_list, uint16 iSpellEffectID, int32 base, int32 limit, int32 max) {
|
||||
for (uint32 i=0; i < spelleffect_list->numentries; i++) {
|
||||
if (spelleffect_list->entries[i].spelleffectid == iSpellEffectID && spelleffect_list->entries[i].base == base
|
||||
&& spelleffect_list->entries[i].limit == limit && spelleffect_list->entries[i].max == max)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool IsSpellInList(DBnpcspells_Struct* spell_list, int16 iSpellID) {
|
||||
for (uint32 i=0; i < spell_list->numentries; i++) {
|
||||
if (spell_list->entries[i].spellid == iSpellID)
|
||||
@@ -2415,6 +2537,7 @@ void NPC::AISpellsList(Client *c)
|
||||
DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
if (iDBSpellsID == 0)
|
||||
return 0;
|
||||
|
||||
if (!npc_spells_cache) {
|
||||
npc_spells_maxid = GetMaxNPCSpellsID();
|
||||
npc_spells_cache = new DBnpcspells_Struct*[npc_spells_maxid+1];
|
||||
@@ -2424,11 +2547,13 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
npc_spells_loadtried[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (iDBSpellsID > npc_spells_maxid)
|
||||
return 0;
|
||||
if (npc_spells_cache[iDBSpellsID]) { // it's in the cache, easy =)
|
||||
return npc_spells_cache[iDBSpellsID];
|
||||
}
|
||||
|
||||
else if (!npc_spells_loadtried[iDBSpellsID]) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spells_loadtried[iDBSpellsID] = true;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
@@ -2439,7 +2564,7 @@ DBnpcspells_Struct* ZoneDatabase::GetNPCSpells(uint32 iDBSpellsID) {
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list, attack_proc, proc_chance from npc_spells where id=%d", iDBSpellsID), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
row = mysql_fetch_row(result);
|
||||
uint32 tmpparent_list = atoi(row[1]);
|
||||
int16 tmpattack_proc = atoi(row[2]);
|
||||
uint8 tmpproc_chance = atoi(row[3]);
|
||||
@@ -2527,3 +2652,104 @@ uint32 ZoneDatabase::GetMaxNPCSpellsID() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
DBnpcspellseffects_Struct* ZoneDatabase::GetNPCSpellsEffects(uint32 iDBSpellsEffectsID) {
|
||||
if (iDBSpellsEffectsID == 0)
|
||||
return 0;
|
||||
|
||||
if (!npc_spellseffects_cache) {
|
||||
npc_spellseffects_maxid = GetMaxNPCSpellsEffectsID();
|
||||
npc_spellseffects_cache = new DBnpcspellseffects_Struct*[npc_spellseffects_maxid+1];
|
||||
npc_spellseffects_loadtried = new bool[npc_spellseffects_maxid+1];
|
||||
for (uint32 i=0; i<=npc_spellseffects_maxid; i++) {
|
||||
npc_spellseffects_cache[i] = 0;
|
||||
npc_spellseffects_loadtried[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (iDBSpellsEffectsID > npc_spellseffects_maxid)
|
||||
return 0;
|
||||
if (npc_spellseffects_cache[iDBSpellsEffectsID]) { // it's in the cache, easy =)
|
||||
return npc_spellseffects_cache[iDBSpellsEffectsID];
|
||||
}
|
||||
|
||||
else if (!npc_spellseffects_loadtried[iDBSpellsEffectsID]) { // no reason to ask the DB again if we have failed once already
|
||||
npc_spellseffects_loadtried[iDBSpellsEffectsID] = true;
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT id, parent_list from npc_spells_effects where id=%d", iDBSpellsEffectsID), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
uint32 tmpparent_list = atoi(row[1]);
|
||||
mysql_free_result(result);
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT spell_effect_id, minlevel, maxlevel,se_base, se_limit, se_max from npc_spells_effects_entries where npc_spells_effects_id=%d ORDER BY minlevel", iDBSpellsEffectsID), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
uint32 tmpSize = sizeof(DBnpcspellseffects_Struct) + (sizeof(DBnpcspellseffects_entries_Struct) * mysql_num_rows(result));
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID] = (DBnpcspellseffects_Struct*) new uchar[tmpSize];
|
||||
memset(npc_spellseffects_cache[iDBSpellsEffectsID], 0, tmpSize);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->parent_list = tmpparent_list;
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->numentries = mysql_num_rows(result);
|
||||
int j = 0;
|
||||
while ((row = mysql_fetch_row(result))) {
|
||||
int spell_effect_id = atoi(row[0]);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].spelleffectid = spell_effect_id;
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].minlevel = atoi(row[1]);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].maxlevel = atoi(row[2]);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].base = atoi(row[3]);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].limit = atoi(row[4]);
|
||||
npc_spellseffects_cache[iDBSpellsEffectsID]->entries[j].max = atoi(row[5]);
|
||||
j++;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
return npc_spellseffects_cache[iDBSpellsEffectsID];
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else {
|
||||
mysql_free_result(result);
|
||||
}
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error in AddNPCSpells query1 '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32 ZoneDatabase::GetMaxNPCSpellsEffectsID() {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char *query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (RunQuery(query, MakeAnyLenString(&query, "SELECT max(id) from npc_spells_effects"), errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
if (mysql_num_rows(result) == 1) {
|
||||
row = mysql_fetch_row(result);
|
||||
uint32 ret = 0;
|
||||
if (row[0])
|
||||
ret = atoi(row[0]);
|
||||
mysql_free_result(result);
|
||||
return ret;
|
||||
}
|
||||
mysql_free_result(result);
|
||||
}
|
||||
else {
|
||||
std::cerr << "Error in GetMaxNPCSpellsEffectsID query '" << query << "' " << errbuf << std::endl;
|
||||
safe_delete_array(query);
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,941 @@
|
||||
#include "RaycastMesh.h"
|
||||
#include <math.h>
|
||||
#include <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
|
||||
// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do
|
||||
// high-speed raycasting.
|
||||
//
|
||||
// There are much better implementations of this available on the internet. In particular I recommend that you use
|
||||
// OPCODE written by Pierre Terdiman.
|
||||
// @see: http://www.codercorner.com/Opcode.htm
|
||||
//
|
||||
// OPCODE does a whole lot more than just raycasting, and is a rather significant amount of source code.
|
||||
//
|
||||
// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting.
|
||||
// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However,
|
||||
// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders
|
||||
// of magnitude; so this implementation should work fine for simple tools and utilities.
|
||||
//
|
||||
// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees.
|
||||
// AABB = Axis Aligned Bounding Volume trees.
|
||||
//
|
||||
// http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/AABB_tree/Chapter_main.html
|
||||
//
|
||||
//
|
||||
// This code snippet was written by John W. Ratcliff on August 18, 2011 and released under the MIT. license.
|
||||
//
|
||||
// mailto:jratcliffscarab@gmail.com
|
||||
//
|
||||
// The official source can be found at: http://code.google.com/p/raycastmesh/
|
||||
//
|
||||
//
|
||||
|
||||
#pragma warning(disable:4100)
|
||||
|
||||
namespace RAYCAST_MESH
|
||||
{
|
||||
|
||||
typedef std::vector< RmUint32 > TriVector;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
/**
|
||||
* A method to compute a ray-AABB intersection.
|
||||
* Original code by Andrew Woo, from "Graphics Gems", Academic Press, 1990
|
||||
* Optimized code by Pierre Terdiman, 2000 (~20-30% faster on my Celeron 500)
|
||||
* Epsilon value added by Klaus Hartmann. (discarding it saves a few cycles only)
|
||||
*
|
||||
* Hence this version is faster as well as more robust than the original one.
|
||||
*
|
||||
* Should work provided:
|
||||
* 1) the integer representation of 0.0f is 0x00000000
|
||||
* 2) the sign bit of the RmReal is the most significant one
|
||||
*
|
||||
* Report bugs: p.terdiman@codercorner.com
|
||||
*
|
||||
* \param aabb [in] the axis-aligned bounding box
|
||||
* \param origin [in] ray origin
|
||||
* \param dir [in] ray direction
|
||||
* \param coord [out] impact coordinates
|
||||
* \return true if ray intersects AABB
|
||||
*/
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
#define RAYAABB_EPSILON 0.00001f
|
||||
//! Integer representation of a RmRealing-point value.
|
||||
#define IR(x) ((RmUint32&)x)
|
||||
|
||||
bool intersectRayAABB(const RmReal MinB[3],const RmReal MaxB[3],const RmReal origin[3],const RmReal dir[3],RmReal coord[3])
|
||||
{
|
||||
bool Inside = true;
|
||||
RmReal MaxT[3];
|
||||
MaxT[0]=MaxT[1]=MaxT[2]=-1.0f;
|
||||
|
||||
// Find candidate planes.
|
||||
for(RmUint32 i=0;i<3;i++)
|
||||
{
|
||||
if(origin[i] < MinB[i])
|
||||
{
|
||||
coord[i] = MinB[i];
|
||||
Inside = false;
|
||||
|
||||
// Calculate T distances to candidate planes
|
||||
if(IR(dir[i])) MaxT[i] = (MinB[i] - origin[i]) / dir[i];
|
||||
}
|
||||
else if(origin[i] > MaxB[i])
|
||||
{
|
||||
coord[i] = MaxB[i];
|
||||
Inside = false;
|
||||
|
||||
// Calculate T distances to candidate planes
|
||||
if(IR(dir[i])) MaxT[i] = (MaxB[i] - origin[i]) / dir[i];
|
||||
}
|
||||
}
|
||||
|
||||
// Ray origin inside bounding box
|
||||
if(Inside)
|
||||
{
|
||||
coord[0] = origin[0];
|
||||
coord[1] = origin[1];
|
||||
coord[2] = origin[2];
|
||||
return true;
|
||||
}
|
||||
|
||||
// Get largest of the maxT's for final choice of intersection
|
||||
RmUint32 WhichPlane = 0;
|
||||
if(MaxT[1] > MaxT[WhichPlane]) WhichPlane = 1;
|
||||
if(MaxT[2] > MaxT[WhichPlane]) WhichPlane = 2;
|
||||
|
||||
// Check final candidate actually inside box
|
||||
if(IR(MaxT[WhichPlane])&0x80000000) return false;
|
||||
|
||||
for(RmUint32 i=0;i<3;i++)
|
||||
{
|
||||
if(i!=WhichPlane)
|
||||
{
|
||||
coord[i] = origin[i] + MaxT[WhichPlane] * dir[i];
|
||||
#ifdef RAYAABB_EPSILON
|
||||
if(coord[i] < MinB[i] - RAYAABB_EPSILON || coord[i] > MaxB[i] + RAYAABB_EPSILON) return false;
|
||||
#else
|
||||
if(coord[i] < MinB[i] || coord[i] > MaxB[i]) return false;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return true; // ray hits box
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
bool intersectLineSegmentAABB(const RmReal bmin[3],const RmReal bmax[3],const RmReal p1[3],const RmReal dir[3],RmReal &dist,RmReal intersect[3])
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
if ( dist > RAYAABB_EPSILON )
|
||||
{
|
||||
ret = intersectRayAABB(bmin,bmax,p1,dir,intersect);
|
||||
if ( ret )
|
||||
{
|
||||
RmReal dx = p1[0]-intersect[0];
|
||||
RmReal dy = p1[1]-intersect[1];
|
||||
RmReal dz = p1[2]-intersect[2];
|
||||
RmReal d = dx*dx+dy*dy+dz*dz;
|
||||
if ( d < dist*dist )
|
||||
{
|
||||
dist = sqrtf(d);
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* a = b - c */
|
||||
#define vector(a,b,c) \
|
||||
(a)[0] = (b)[0] - (c)[0]; \
|
||||
(a)[1] = (b)[1] - (c)[1]; \
|
||||
(a)[2] = (b)[2] - (c)[2];
|
||||
|
||||
#define innerProduct(v,q) \
|
||||
((v)[0] * (q)[0] + \
|
||||
(v)[1] * (q)[1] + \
|
||||
(v)[2] * (q)[2])
|
||||
|
||||
#define crossProduct(a,b,c) \
|
||||
(a)[0] = (b)[1] * (c)[2] - (c)[1] * (b)[2]; \
|
||||
(a)[1] = (b)[2] * (c)[0] - (c)[2] * (b)[0]; \
|
||||
(a)[2] = (b)[0] * (c)[1] - (c)[0] * (b)[1];
|
||||
|
||||
|
||||
static inline bool rayIntersectsTriangle(const RmReal *p,const RmReal *d,const RmReal *v0,const RmReal *v1,const RmReal *v2,RmReal &t)
|
||||
{
|
||||
RmReal e1[3],e2[3],h[3],s[3],q[3];
|
||||
RmReal a,f,u,v;
|
||||
|
||||
vector(e1,v1,v0);
|
||||
vector(e2,v2,v0);
|
||||
crossProduct(h,d,e2);
|
||||
a = innerProduct(e1,h);
|
||||
|
||||
if (a > -0.00001 && a < 0.00001)
|
||||
return(false);
|
||||
|
||||
f = 1/a;
|
||||
vector(s,p,v0);
|
||||
u = f * (innerProduct(s,h));
|
||||
|
||||
if (u < 0.0 || u > 1.0)
|
||||
return(false);
|
||||
|
||||
crossProduct(q,s,e1);
|
||||
v = f * innerProduct(d,q);
|
||||
if (v < 0.0 || u + v > 1.0)
|
||||
return(false);
|
||||
// at this stage we can compute t to find out where
|
||||
// the intersection point is on the line
|
||||
t = f * innerProduct(e2,q);
|
||||
if (t > 0) // ray intersection
|
||||
return(true);
|
||||
else // this means that there is a line intersection
|
||||
// but not a ray intersection
|
||||
return (false);
|
||||
}
|
||||
|
||||
static RmReal computePlane(const RmReal *A,const RmReal *B,const RmReal *C,RmReal *n) // returns D
|
||||
{
|
||||
RmReal vx = (B[0] - C[0]);
|
||||
RmReal vy = (B[1] - C[1]);
|
||||
RmReal vz = (B[2] - C[2]);
|
||||
|
||||
RmReal wx = (A[0] - B[0]);
|
||||
RmReal wy = (A[1] - B[1]);
|
||||
RmReal wz = (A[2] - B[2]);
|
||||
|
||||
RmReal vw_x = vy * wz - vz * wy;
|
||||
RmReal vw_y = vz * wx - vx * wz;
|
||||
RmReal vw_z = vx * wy - vy * wx;
|
||||
|
||||
RmReal mag = sqrt((vw_x * vw_x) + (vw_y * vw_y) + (vw_z * vw_z));
|
||||
|
||||
if ( mag < 0.000001f )
|
||||
{
|
||||
mag = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
mag = 1.0f/mag;
|
||||
}
|
||||
|
||||
RmReal x = vw_x * mag;
|
||||
RmReal y = vw_y * mag;
|
||||
RmReal z = vw_z * mag;
|
||||
|
||||
|
||||
RmReal D = 0.0f - ((x*A[0])+(y*A[1])+(z*A[2]));
|
||||
|
||||
n[0] = x;
|
||||
n[1] = y;
|
||||
n[2] = z;
|
||||
|
||||
return D;
|
||||
}
|
||||
|
||||
|
||||
#define TRI_EOF 0xFFFFFFFF
|
||||
|
||||
enum AxisAABB
|
||||
{
|
||||
AABB_XAXIS,
|
||||
AABB_YAXIS,
|
||||
AABB_ZAXIS
|
||||
};
|
||||
|
||||
enum ClipCode
|
||||
{
|
||||
CC_MINX = (1<<0),
|
||||
CC_MAXX = (1<<1),
|
||||
CC_MINY = (1<<2),
|
||||
CC_MAXY = (1<<3),
|
||||
CC_MINZ = (1<<4),
|
||||
CC_MAXZ = (1<<5),
|
||||
};
|
||||
|
||||
|
||||
class BoundsAABB
|
||||
{
|
||||
public:
|
||||
|
||||
|
||||
void setMin(const RmReal *v)
|
||||
{
|
||||
mMin[0] = v[0];
|
||||
mMin[1] = v[1];
|
||||
mMin[2] = v[2];
|
||||
}
|
||||
|
||||
void setMax(const RmReal *v)
|
||||
{
|
||||
mMax[0] = v[0];
|
||||
mMax[1] = v[1];
|
||||
mMax[2] = v[2];
|
||||
}
|
||||
|
||||
void setMin(RmReal x,RmReal y,RmReal z)
|
||||
{
|
||||
mMin[0] = x;
|
||||
mMin[1] = y;
|
||||
mMin[2] = z;
|
||||
}
|
||||
|
||||
void setMax(RmReal x,RmReal y,RmReal z)
|
||||
{
|
||||
mMax[0] = x;
|
||||
mMax[1] = y;
|
||||
mMax[2] = z;
|
||||
}
|
||||
|
||||
void include(const RmReal *v)
|
||||
{
|
||||
if ( v[0] < mMin[0] ) mMin[0] = v[0];
|
||||
if ( v[1] < mMin[1] ) mMin[1] = v[1];
|
||||
if ( v[2] < mMin[2] ) mMin[2] = v[2];
|
||||
|
||||
if ( v[0] > mMax[0] ) mMax[0] = v[0];
|
||||
if ( v[1] > mMax[1] ) mMax[1] = v[1];
|
||||
if ( v[2] > mMax[2] ) mMax[2] = v[2];
|
||||
}
|
||||
|
||||
void getCenter(RmReal *center) const
|
||||
{
|
||||
center[0] = (mMin[0]+mMax[0])*0.5f;
|
||||
center[1] = (mMin[1]+mMax[1])*0.5f;
|
||||
center[2] = (mMin[2]+mMax[2])*0.5f;
|
||||
}
|
||||
|
||||
bool intersects(const BoundsAABB &b) const
|
||||
{
|
||||
if ((mMin[0] > b.mMax[0]) || (b.mMin[0] > mMax[0])) return false;
|
||||
if ((mMin[1] > b.mMax[1]) || (b.mMin[1] > mMax[1])) return false;
|
||||
if ((mMin[2] > b.mMax[2]) || (b.mMin[2] > mMax[2])) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool containsTriangle(const RmReal *p1,const RmReal *p2,const RmReal *p3) const
|
||||
{
|
||||
BoundsAABB b;
|
||||
b.setMin(p1);
|
||||
b.setMax(p1);
|
||||
b.include(p2);
|
||||
b.include(p3);
|
||||
return intersects(b);
|
||||
}
|
||||
|
||||
bool containsTriangleExact(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &orCode) const
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
RmUint32 andCode;
|
||||
orCode = getClipCode(p1,p2,p3,andCode);
|
||||
if ( andCode == 0 )
|
||||
{
|
||||
ret = true;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline RmUint32 getClipCode(const RmReal *p1,const RmReal *p2,const RmReal *p3,RmUint32 &andCode) const
|
||||
{
|
||||
andCode = 0xFFFFFFFF;
|
||||
RmUint32 c1 = getClipCode(p1);
|
||||
RmUint32 c2 = getClipCode(p2);
|
||||
RmUint32 c3 = getClipCode(p3);
|
||||
andCode&=c1;
|
||||
andCode&=c2;
|
||||
andCode&=c3;
|
||||
return c1|c2|c3;
|
||||
}
|
||||
|
||||
inline RmUint32 getClipCode(const RmReal *p) const
|
||||
{
|
||||
RmUint32 ret = 0;
|
||||
|
||||
if ( p[0] < mMin[0] )
|
||||
{
|
||||
ret|=CC_MINX;
|
||||
}
|
||||
else if ( p[0] > mMax[0] )
|
||||
{
|
||||
ret|=CC_MAXX;
|
||||
}
|
||||
|
||||
if ( p[1] < mMin[1] )
|
||||
{
|
||||
ret|=CC_MINY;
|
||||
}
|
||||
else if ( p[1] > mMax[1] )
|
||||
{
|
||||
ret|=CC_MAXY;
|
||||
}
|
||||
|
||||
if ( p[2] < mMin[2] )
|
||||
{
|
||||
ret|=CC_MINZ;
|
||||
}
|
||||
else if ( p[2] > mMax[2] )
|
||||
{
|
||||
ret|=CC_MAXZ;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline void clamp(const BoundsAABB &aabb)
|
||||
{
|
||||
if ( mMin[0] < aabb.mMin[0] ) mMin[0] = aabb.mMin[0];
|
||||
if ( mMin[1] < aabb.mMin[1] ) mMin[1] = aabb.mMin[1];
|
||||
if ( mMin[2] < aabb.mMin[2] ) mMin[2] = aabb.mMin[2];
|
||||
if ( mMax[0] > aabb.mMax[0] ) mMax[0] = aabb.mMax[0];
|
||||
if ( mMax[1] > aabb.mMax[1] ) mMax[1] = aabb.mMax[1];
|
||||
if ( mMax[2] > aabb.mMax[2] ) mMax[2] = aabb.mMax[2];
|
||||
}
|
||||
|
||||
RmReal mMin[3];
|
||||
RmReal mMax[3];
|
||||
};
|
||||
|
||||
|
||||
class NodeAABB;
|
||||
|
||||
class NodeInterface
|
||||
{
|
||||
public:
|
||||
virtual NodeAABB * getNode(void) = 0;
|
||||
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal) = 0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class NodeAABB
|
||||
{
|
||||
public:
|
||||
NodeAABB(void)
|
||||
{
|
||||
mLeft = NULL;
|
||||
mRight = NULL;
|
||||
mLeafTriangleIndex= TRI_EOF;
|
||||
}
|
||||
|
||||
NodeAABB(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,RmUint32 *indices,
|
||||
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
||||
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
||||
RmReal minAxisSize,
|
||||
NodeInterface *callback,
|
||||
TriVector &leafTriangles) // once a particular axis is less than this size, stop sub-dividing.
|
||||
|
||||
{
|
||||
mLeft = NULL;
|
||||
mRight = NULL;
|
||||
mLeafTriangleIndex = TRI_EOF;
|
||||
TriVector triangles;
|
||||
triangles.reserve(tcount);
|
||||
for (RmUint32 i=0; i<tcount; i++)
|
||||
{
|
||||
triangles.push_back(i);
|
||||
}
|
||||
mBounds.setMin( vertices );
|
||||
mBounds.setMax( vertices );
|
||||
const RmReal *vtx = vertices+3;
|
||||
for (RmUint32 i=1; i<vcount; i++)
|
||||
{
|
||||
mBounds.include( vtx );
|
||||
vtx+=3;
|
||||
}
|
||||
split(triangles,vcount,vertices,tcount,indices,0,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
||||
}
|
||||
|
||||
NodeAABB(const BoundsAABB &aabb)
|
||||
{
|
||||
mBounds = aabb;
|
||||
mLeft = NULL;
|
||||
mRight = NULL;
|
||||
mLeafTriangleIndex = TRI_EOF;
|
||||
}
|
||||
|
||||
~NodeAABB(void)
|
||||
{
|
||||
}
|
||||
|
||||
// here is where we split the mesh..
|
||||
void split(const TriVector &triangles,
|
||||
RmUint32 vcount,
|
||||
const RmReal *vertices,
|
||||
RmUint32 tcount,
|
||||
const RmUint32 *indices,
|
||||
RmUint32 depth,
|
||||
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
||||
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
||||
RmReal minAxisSize,
|
||||
NodeInterface *callback,
|
||||
TriVector &leafTriangles) // once a particular axis is less than this size, stop sub-dividing.
|
||||
|
||||
{
|
||||
// Find the longest axis of the bounding volume of this node
|
||||
RmReal dx = mBounds.mMax[0] - mBounds.mMin[0];
|
||||
RmReal dy = mBounds.mMax[1] - mBounds.mMin[1];
|
||||
RmReal dz = mBounds.mMax[2] - mBounds.mMin[2];
|
||||
|
||||
AxisAABB axis = AABB_XAXIS;
|
||||
RmReal laxis = dx;
|
||||
|
||||
if ( dy > dx )
|
||||
{
|
||||
axis = AABB_YAXIS;
|
||||
laxis = dy;
|
||||
}
|
||||
|
||||
if ( dz > dx && dz > dy )
|
||||
{
|
||||
axis = AABB_ZAXIS;
|
||||
laxis = dz;
|
||||
}
|
||||
|
||||
RmUint32 count = triangles.size();
|
||||
|
||||
// if the number of triangles is less than the minimum allowed for a leaf node or...
|
||||
// we have reached the maximum recursion depth or..
|
||||
// the width of the longest axis is less than the minimum axis size then...
|
||||
// we create the leaf node and copy the triangles into the leaf node triangle array.
|
||||
if ( count < minLeafSize || depth >= maxDepth || laxis < minAxisSize )
|
||||
{
|
||||
// Copy the triangle indices into the leaf triangles array
|
||||
mLeafTriangleIndex = leafTriangles.size(); // assign the array start location for these leaf triangles.
|
||||
leafTriangles.push_back(count);
|
||||
for (TriVector::const_iterator i=triangles.begin(); i!=triangles.end(); ++i)
|
||||
{
|
||||
RmUint32 tri = *i;
|
||||
leafTriangles.push_back(tri);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RmReal center[3];
|
||||
mBounds.getCenter(center);
|
||||
BoundsAABB b1,b2;
|
||||
splitRect(axis,mBounds,b1,b2,center);
|
||||
|
||||
// Compute two bounding boxes based upon the split of the longest axis
|
||||
|
||||
BoundsAABB leftBounds,rightBounds;
|
||||
|
||||
TriVector leftTriangles;
|
||||
TriVector rightTriangles;
|
||||
|
||||
|
||||
// Create two arrays; one of all triangles which intersect the 'left' half of the bounding volume node
|
||||
// and another array that includes all triangles which intersect the 'right' half of the bounding volume node.
|
||||
for (TriVector::const_iterator i=triangles.begin(); i!=triangles.end(); ++i)
|
||||
{
|
||||
|
||||
RmUint32 tri = (*i);
|
||||
|
||||
{
|
||||
RmUint32 i1 = indices[tri*3+0];
|
||||
RmUint32 i2 = indices[tri*3+1];
|
||||
RmUint32 i3 = indices[tri*3+2];
|
||||
|
||||
const RmReal *p1 = &vertices[i1*3];
|
||||
const RmReal *p2 = &vertices[i2*3];
|
||||
const RmReal *p3 = &vertices[i3*3];
|
||||
|
||||
RmUint32 addCount = 0;
|
||||
RmUint32 orCode=0xFFFFFFFF;
|
||||
if ( b1.containsTriangleExact(p1,p2,p3,orCode))
|
||||
{
|
||||
addCount++;
|
||||
if ( leftTriangles.empty() )
|
||||
{
|
||||
leftBounds.setMin(p1);
|
||||
leftBounds.setMax(p1);
|
||||
}
|
||||
leftBounds.include(p1);
|
||||
leftBounds.include(p2);
|
||||
leftBounds.include(p3);
|
||||
leftTriangles.push_back(tri); // Add this triangle to the 'left triangles' array and revise the left triangles bounding volume
|
||||
}
|
||||
// if the orCode is zero; meaning the triangle was fully self-contiained int he left bounding box; then we don't need to test against the right
|
||||
if ( orCode && b2.containsTriangleExact(p1,p2,p3,orCode))
|
||||
{
|
||||
addCount++;
|
||||
if ( rightTriangles.empty() )
|
||||
{
|
||||
rightBounds.setMin(p1);
|
||||
rightBounds.setMax(p1);
|
||||
}
|
||||
rightBounds.include(p1);
|
||||
rightBounds.include(p2);
|
||||
rightBounds.include(p3);
|
||||
rightTriangles.push_back(tri); // Add this triangle to the 'right triangles' array and revise the right triangles bounding volume.
|
||||
}
|
||||
assert( addCount );
|
||||
}
|
||||
}
|
||||
|
||||
if ( !leftTriangles.empty() ) // If there are triangles in the left half then...
|
||||
{
|
||||
leftBounds.clamp(b1); // we have to clamp the bounding volume so it stays inside the parent volume.
|
||||
mLeft = callback->getNode(); // get a new AABB node
|
||||
new ( mLeft ) NodeAABB(leftBounds); // initialize it to default constructor values.
|
||||
// Then recursively split this node.
|
||||
mLeft->split(leftTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
||||
}
|
||||
|
||||
if ( !rightTriangles.empty() ) // If there are triangles in the right half then..
|
||||
{
|
||||
rightBounds.clamp(b2); // clamps the bounding volume so it stays restricted to the size of the parent volume.
|
||||
mRight = callback->getNode(); // allocate and default initialize a new node
|
||||
new ( mRight ) NodeAABB(rightBounds);
|
||||
// Recursively split this node.
|
||||
mRight->split(rightTriangles,vcount,vertices,tcount,indices,depth+1,maxDepth,minLeafSize,minAxisSize,callback,leafTriangles);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void splitRect(AxisAABB axis,const BoundsAABB &source,BoundsAABB &b1,BoundsAABB &b2,const RmReal *midpoint)
|
||||
{
|
||||
switch ( axis )
|
||||
{
|
||||
case AABB_XAXIS:
|
||||
{
|
||||
b1.setMin( source.mMin );
|
||||
b1.setMax( midpoint[0], source.mMax[1], source.mMax[2] );
|
||||
|
||||
b2.setMin( midpoint[0], source.mMin[1], source.mMin[2] );
|
||||
b2.setMax(source.mMax);
|
||||
}
|
||||
break;
|
||||
case AABB_YAXIS:
|
||||
{
|
||||
b1.setMin(source.mMin);
|
||||
b1.setMax(source.mMax[0], midpoint[1], source.mMax[2]);
|
||||
|
||||
b2.setMin(source.mMin[0], midpoint[1], source.mMin[2]);
|
||||
b2.setMax(source.mMax);
|
||||
}
|
||||
break;
|
||||
case AABB_ZAXIS:
|
||||
{
|
||||
b1.setMin(source.mMin);
|
||||
b1.setMax(source.mMax[0], source.mMax[1], midpoint[2]);
|
||||
|
||||
b2.setMin(source.mMin[0], source.mMin[1], midpoint[2]);
|
||||
b2.setMax(source.mMax);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void raycast(bool &hit,
|
||||
const RmReal *from,
|
||||
const RmReal *to,
|
||||
const RmReal *dir,
|
||||
RmReal *hitLocation,
|
||||
RmReal *hitNormal,
|
||||
RmReal *hitDistance,
|
||||
const RmReal *vertices,
|
||||
const RmUint32 *indices,
|
||||
RmReal &nearestDistance,
|
||||
NodeInterface *callback,
|
||||
RmUint32 *raycastTriangles,
|
||||
RmUint32 raycastFrame,
|
||||
const TriVector &leafTriangles,
|
||||
RmUint32 &nearestTriIndex)
|
||||
{
|
||||
RmReal sect[3];
|
||||
RmReal nd = nearestDistance;
|
||||
if ( !intersectLineSegmentAABB(mBounds.mMin,mBounds.mMax,from,dir,nd,sect) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
if ( mLeafTriangleIndex != TRI_EOF )
|
||||
{
|
||||
const RmUint32 *scan = &leafTriangles[mLeafTriangleIndex];
|
||||
RmUint32 count = *scan++;
|
||||
for (RmUint32 i=0; i<count; i++)
|
||||
{
|
||||
RmUint32 tri = *scan++;
|
||||
if ( raycastTriangles[tri] != raycastFrame )
|
||||
{
|
||||
raycastTriangles[tri] = raycastFrame;
|
||||
RmUint32 i1 = indices[tri*3+0];
|
||||
RmUint32 i2 = indices[tri*3+1];
|
||||
RmUint32 i3 = indices[tri*3+2];
|
||||
|
||||
const RmReal *p1 = &vertices[i1*3];
|
||||
const RmReal *p2 = &vertices[i2*3];
|
||||
const RmReal *p3 = &vertices[i3*3];
|
||||
|
||||
RmReal t;
|
||||
if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t))
|
||||
{
|
||||
bool accept = false;
|
||||
if ( t == nearestDistance && tri < nearestTriIndex )
|
||||
{
|
||||
accept = true;
|
||||
}
|
||||
if ( t < nearestDistance || accept )
|
||||
{
|
||||
nearestDistance = t;
|
||||
if ( hitLocation )
|
||||
{
|
||||
hitLocation[0] = from[0]+dir[0]*t;
|
||||
hitLocation[1] = from[1]+dir[1]*t;
|
||||
hitLocation[2] = from[2]+dir[2]*t;
|
||||
}
|
||||
if ( hitNormal )
|
||||
{
|
||||
callback->getFaceNormal(tri,hitNormal);
|
||||
}
|
||||
if ( hitDistance )
|
||||
{
|
||||
*hitDistance = t;
|
||||
}
|
||||
nearestTriIndex = tri;
|
||||
hit = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( mLeft )
|
||||
{
|
||||
mLeft->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,vertices,indices,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex);
|
||||
}
|
||||
if ( mRight )
|
||||
{
|
||||
mRight->raycast(hit,from,to,dir,hitLocation,hitNormal,hitDistance,vertices,indices,nearestDistance,callback,raycastTriangles,raycastFrame,leafTriangles,nearestTriIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
NodeAABB *mLeft; // left node
|
||||
NodeAABB *mRight; // right node
|
||||
BoundsAABB mBounds; // bounding volume of node
|
||||
RmUint32 mLeafTriangleIndex; // if it is a leaf node; then these are the triangle indices.
|
||||
};
|
||||
|
||||
class MyRaycastMesh : public RaycastMesh, public NodeInterface
|
||||
{
|
||||
public:
|
||||
|
||||
MyRaycastMesh(RmUint32 vcount,const RmReal *vertices,RmUint32 tcount,const RmUint32 *indices,RmUint32 maxDepth,RmUint32 minLeafSize,RmReal minAxisSize)
|
||||
{
|
||||
mRaycastFrame = 0;
|
||||
if ( maxDepth < 2 )
|
||||
{
|
||||
maxDepth = 2;
|
||||
}
|
||||
if ( maxDepth > 15 )
|
||||
{
|
||||
maxDepth = 15;
|
||||
}
|
||||
RmUint32 pow2Table[16] = { 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 65536 };
|
||||
mMaxNodeCount = 0;
|
||||
for (RmUint32 i=0; i<=maxDepth; i++)
|
||||
{
|
||||
mMaxNodeCount+=pow2Table[i];
|
||||
}
|
||||
mNodes = new NodeAABB[mMaxNodeCount];
|
||||
mNodeCount = 0;
|
||||
mVcount = vcount;
|
||||
mVertices = (RmReal *)::malloc(sizeof(RmReal)*3*vcount);
|
||||
memcpy(mVertices,vertices,sizeof(RmReal)*3*vcount);
|
||||
mTcount = tcount;
|
||||
mIndices = (RmUint32 *)::malloc(sizeof(RmUint32)*tcount*3);
|
||||
memcpy(mIndices,indices,sizeof(RmUint32)*tcount*3);
|
||||
mRaycastTriangles = (RmUint32 *)::malloc(tcount*sizeof(RmUint32));
|
||||
memset(mRaycastTriangles,0,tcount*sizeof(RmUint32));
|
||||
mRoot = getNode();
|
||||
mFaceNormals = NULL;
|
||||
new ( mRoot ) NodeAABB(mVcount,mVertices,mTcount,mIndices,maxDepth,minLeafSize,minAxisSize,this,mLeafTriangles);
|
||||
}
|
||||
|
||||
~MyRaycastMesh(void)
|
||||
{
|
||||
delete []mNodes;
|
||||
::free(mVertices);
|
||||
::free(mIndices);
|
||||
::free(mFaceNormals);
|
||||
::free(mRaycastTriangles);
|
||||
}
|
||||
|
||||
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
RmReal dir[3];
|
||||
dir[0] = to[0] - from[0];
|
||||
dir[1] = to[1] - from[1];
|
||||
dir[2] = to[2] - from[2];
|
||||
RmReal distance = sqrtf( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] );
|
||||
if ( distance < 0.0000000001f ) return false;
|
||||
RmReal recipDistance = 1.0f / distance;
|
||||
dir[0]*=recipDistance;
|
||||
dir[1]*=recipDistance;
|
||||
dir[2]*=recipDistance;
|
||||
mRaycastFrame++;
|
||||
RmUint32 nearestTriIndex=TRI_EOF;
|
||||
mRoot->raycast(ret,from,to,dir,hitLocation,hitNormal,hitDistance,mVertices,mIndices,distance,this,mRaycastTriangles,mRaycastFrame,mLeafTriangles,nearestTriIndex);
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void release(void)
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
virtual const RmReal * getBoundMin(void) const // return the minimum bounding box
|
||||
{
|
||||
return mRoot->mBounds.mMin;
|
||||
}
|
||||
virtual const RmReal * getBoundMax(void) const // return the maximum bounding box.
|
||||
{
|
||||
return mRoot->mBounds.mMax;
|
||||
}
|
||||
|
||||
virtual NodeAABB * getNode(void)
|
||||
{
|
||||
assert( mNodeCount < mMaxNodeCount );
|
||||
NodeAABB *ret = &mNodes[mNodeCount];
|
||||
mNodeCount++;
|
||||
return ret;
|
||||
}
|
||||
|
||||
virtual void getFaceNormal(RmUint32 tri,RmReal *faceNormal)
|
||||
{
|
||||
if ( mFaceNormals == NULL )
|
||||
{
|
||||
mFaceNormals = (RmReal *)::malloc(sizeof(RmReal)*3*mTcount);
|
||||
for (RmUint32 i=0; i<mTcount; i++)
|
||||
{
|
||||
RmUint32 i1 = mIndices[i*3+0];
|
||||
RmUint32 i2 = mIndices[i*3+1];
|
||||
RmUint32 i3 = mIndices[i*3+2];
|
||||
const RmReal*p1 = &mVertices[i1*3];
|
||||
const RmReal*p2 = &mVertices[i2*3];
|
||||
const RmReal*p3 = &mVertices[i3*3];
|
||||
RmReal *dest = &mFaceNormals[i*3];
|
||||
computePlane(p3,p2,p1,dest);
|
||||
}
|
||||
}
|
||||
const RmReal *src = &mFaceNormals[tri*3];
|
||||
faceNormal[0] = src[0];
|
||||
faceNormal[1] = src[1];
|
||||
faceNormal[2] = src[2];
|
||||
}
|
||||
|
||||
virtual bool bruteForceRaycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance)
|
||||
{
|
||||
bool ret = false;
|
||||
|
||||
RmReal dir[3];
|
||||
|
||||
dir[0] = to[0] - from[0];
|
||||
dir[1] = to[1] - from[1];
|
||||
dir[2] = to[2] - from[2];
|
||||
|
||||
RmReal distance = sqrtf( dir[0]*dir[0] + dir[1]*dir[1]+dir[2]*dir[2] );
|
||||
if ( distance < 0.0000000001f ) return false;
|
||||
RmReal recipDistance = 1.0f / distance;
|
||||
dir[0]*=recipDistance;
|
||||
dir[1]*=recipDistance;
|
||||
dir[2]*=recipDistance;
|
||||
const RmUint32 *indices = mIndices;
|
||||
const RmReal *vertices = mVertices;
|
||||
RmReal nearestDistance = distance;
|
||||
|
||||
for (RmUint32 tri=0; tri<mTcount; tri++)
|
||||
{
|
||||
RmUint32 i1 = indices[tri*3+0];
|
||||
RmUint32 i2 = indices[tri*3+1];
|
||||
RmUint32 i3 = indices[tri*3+2];
|
||||
|
||||
const RmReal *p1 = &vertices[i1*3];
|
||||
const RmReal *p2 = &vertices[i2*3];
|
||||
const RmReal *p3 = &vertices[i3*3];
|
||||
|
||||
RmReal t;
|
||||
if ( rayIntersectsTriangle(from,dir,p1,p2,p3,t))
|
||||
{
|
||||
if ( t < nearestDistance )
|
||||
{
|
||||
nearestDistance = t;
|
||||
if ( hitLocation )
|
||||
{
|
||||
hitLocation[0] = from[0]+dir[0]*t;
|
||||
hitLocation[1] = from[1]+dir[1]*t;
|
||||
hitLocation[2] = from[2]+dir[2]*t;
|
||||
}
|
||||
|
||||
if ( hitNormal )
|
||||
{
|
||||
getFaceNormal(tri,hitNormal);
|
||||
}
|
||||
|
||||
if ( hitDistance )
|
||||
{
|
||||
*hitDistance = t;
|
||||
}
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
RmUint32 mRaycastFrame;
|
||||
RmUint32 *mRaycastTriangles;
|
||||
RmUint32 mVcount;
|
||||
RmReal *mVertices;
|
||||
RmReal *mFaceNormals;
|
||||
RmUint32 mTcount;
|
||||
RmUint32 *mIndices;
|
||||
NodeAABB *mRoot;
|
||||
RmUint32 mNodeCount;
|
||||
RmUint32 mMaxNodeCount;
|
||||
NodeAABB *mNodes;
|
||||
TriVector mLeafTriangles;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
using namespace RAYCAST_MESH;
|
||||
|
||||
|
||||
RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in the source triangle mesh
|
||||
const RmReal *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
|
||||
RmUint32 tcount, // The number of triangles in the source triangle mesh
|
||||
const RmUint32 *indices, // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
|
||||
RmUint32 maxDepth, // Maximum recursion depth for the triangle mesh.
|
||||
RmUint32 minLeafSize, // minimum triangles to treat as a 'leaf' node.
|
||||
RmReal minAxisSize // once a particular axis is less than this size, stop sub-dividing.
|
||||
)
|
||||
{
|
||||
MyRaycastMesh *m = new MyRaycastMesh(vcount,vertices,tcount,indices,maxDepth,minLeafSize,minAxisSize);
|
||||
return static_cast< RaycastMesh * >(m);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
#ifndef RAYCAST_MESH_H
|
||||
|
||||
#define RAYCAST_MESH_H
|
||||
|
||||
// This code snippet allows you to create an axis aligned bounding volume tree for a triangle mesh so that you can do
|
||||
// high-speed raycasting.
|
||||
//
|
||||
// There are much better implementations of this available on the internet. In particular I recommend that you use
|
||||
// OPCODE written by Pierre Terdiman.
|
||||
// @see: http://www.codercorner.com/Opcode.htm
|
||||
//
|
||||
// OPCODE does a whole lot more than just raycasting, and is a rather significant amount of source code.
|
||||
//
|
||||
// I am providing this code snippet for the use case where you *only* want to do quick and dirty optimized raycasting.
|
||||
// I have not done performance testing between this version and OPCODE; so I don't know how much slower it is. However,
|
||||
// anytime you switch to using a spatial data structure for raycasting, you increase your performance by orders and orders
|
||||
// of magnitude; so this implementation should work fine for simple tools and utilities.
|
||||
//
|
||||
// It also serves as a nice sample for people who are trying to learn the algorithm of how to implement AABB trees.
|
||||
// AABB = Axis Aligned Bounding Volume trees.
|
||||
//
|
||||
// http://www.cgal.org/Manual/3.5/doc_html/cgal_manual/AABB_tree/Chapter_main.html
|
||||
//
|
||||
//
|
||||
// This code snippet was written by John W. Ratcliff on August 18, 2011 and released under the MIT. license.
|
||||
//
|
||||
// mailto:jratcliffscarab@gmail.com
|
||||
//
|
||||
// The official source can be found at: http://code.google.com/p/raycastmesh/
|
||||
//
|
||||
//
|
||||
|
||||
typedef float RmReal;
|
||||
typedef unsigned int RmUint32;
|
||||
|
||||
class RaycastMesh
|
||||
{
|
||||
public:
|
||||
virtual bool raycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) = 0;
|
||||
virtual bool bruteForceRaycast(const RmReal *from,const RmReal *to,RmReal *hitLocation,RmReal *hitNormal,RmReal *hitDistance) = 0;
|
||||
|
||||
virtual const RmReal * getBoundMin(void) const = 0; // return the minimum bounding box
|
||||
virtual const RmReal * getBoundMax(void) const = 0; // return the maximum bounding box.
|
||||
virtual void release(void) = 0;
|
||||
protected:
|
||||
virtual ~RaycastMesh(void) { };
|
||||
};
|
||||
|
||||
|
||||
RaycastMesh * createRaycastMesh(RmUint32 vcount, // The number of vertices in the source triangle mesh
|
||||
const RmReal *vertices, // The array of vertex positions in the format x1,y1,z1..x2,y2,z2.. etc.
|
||||
RmUint32 tcount, // The number of triangles in the source triangle mesh
|
||||
const RmUint32 *indices, // The triangle indices in the format of i1,i2,i3 ... i4,i5,i6, ...
|
||||
RmUint32 maxDepth=15, // Maximum recursion depth for the triangle mesh.
|
||||
RmUint32 minLeafSize=4, // minimum triangles to treat as a 'leaf' node.
|
||||
RmReal minAxisSize=0.01f // once a particular axis is less than this size, stop sub-dividing.
|
||||
);
|
||||
|
||||
|
||||
#endif
|
||||
+10
-5
@@ -243,12 +243,12 @@
|
||||
#define TRADESKILL_LEARN_RECIPE 3457 //You have learned the recipe %1!
|
||||
#define EXPEDITION_MIN_REMAIN 3551 //You only have %1 minutes remaining before this expedition comes to an end.
|
||||
#define REWIND_WAIT 4059 //You must wait a bit longer before using the rewind command again.
|
||||
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
|
||||
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
|
||||
#define CORPSEDRAG_LIMIT 4061 //You are already dragging as much as you can!
|
||||
#define CORPSEDRAG_ALREADY 4062 //You are already dragging %1.
|
||||
#define CORPSEDRAG_SOMEONE_ELSE 4063 //Someone else is dragging %1.
|
||||
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
|
||||
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
||||
#define CORPSEDRAG_BEGIN 4064 //You begin to drag %1.
|
||||
#define CORPSEDRAG_STOPALL 4065 //You stop dragging the corpses.
|
||||
#define CORPSEDRAG_STOP 4066 //You stop dragging the corpse.
|
||||
#define WHOALL_NO_RESULTS 5029 //There are no players in EverQuest that match those who filters.
|
||||
#define PETITION_NO_DELETE 5053 //You do not have a petition in the queue.
|
||||
#define PETITION_DELETED 5054 //Your petition was successfully deleted.
|
||||
@@ -300,6 +300,11 @@
|
||||
#define GAIN_RAID_LEADERSHIP_EXP 8789 //
|
||||
#define BUFF_MINUTES_REMAINING 8799 //%1 (%2 minutes remaining)
|
||||
#define FEAR_TOO_HIGH 9035 //Your target is too high of a level for your fear spell.
|
||||
#define SLOW_MOSTLY_SUCCESSFUL 9029 //Your spell was mostly successful.
|
||||
#define SLOW_PARTIALLY_SUCCESSFUL 9030 // Your spell was partially successful.
|
||||
#define SLOW_SLIGHTLY_SUCCESSFUL 9031 //Your spell was slightly successful.
|
||||
#define SPELL_OPPOSITE_EFFECT 9032 //Your spell may have had the opposite effect of what you desired.
|
||||
#define HAS_BEEN_AWAKENED 9037 //%1 has been awakened by %2.
|
||||
#define YOU_HEAL 9068 //You have healed %1 for %2 points of damage.
|
||||
#define YOUR_HIT_DOT 9072 //%1 has taken %2 damage from your %3.
|
||||
#define HIT_NON_MELEE 9073 //%1 hit %2 for %3 points of non-melee damage.
|
||||
|
||||
+3
-195
@@ -880,133 +880,6 @@ bool Mob::CombatRange(Mob* other)
|
||||
return false;
|
||||
}
|
||||
|
||||
//Old LOS function, prolly not used anymore
|
||||
//Not removed because I havent looked it over to see if anything
|
||||
//useful is in here before we delete it.
|
||||
bool Mob::CheckLos(Mob* other) {
|
||||
if (zone->zonemap == 0)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
float tmp_x = GetX();
|
||||
float tmp_y = GetY();
|
||||
float tmp_z = GetZ();
|
||||
float trg_x = other->GetX();
|
||||
float trg_y = other->GetY();
|
||||
float trg_z = other->GetZ();
|
||||
float perwalk_x = 0.5;
|
||||
float perwalk_y = 0.5;
|
||||
float perwalk_z = 0.5;
|
||||
float dist_x = tmp_x - trg_x;
|
||||
if (dist_x < 0)
|
||||
dist_x *= -1;
|
||||
float dist_y = tmp_y - trg_y;
|
||||
if (dist_y < 0)
|
||||
dist_y *= -1;
|
||||
float dist_z = tmp_z - trg_z;
|
||||
if (dist_z < 0)
|
||||
dist_z *= -1;
|
||||
if (dist_x < dist_y && dist_z < dist_y)
|
||||
{
|
||||
perwalk_x /= (dist_y/dist_x);
|
||||
perwalk_z /= (dist_y/dist_z);
|
||||
}
|
||||
else if (dist_y < dist_x && dist_z < dist_x)
|
||||
{
|
||||
perwalk_y /= (dist_x/dist_y);
|
||||
perwalk_z /= (dist_x/dist_z);
|
||||
}
|
||||
else if (dist_x < dist_z && dist_y < dist_z)
|
||||
{
|
||||
perwalk_x /= (dist_z/dist_x);
|
||||
perwalk_y /= (dist_z/dist_y);
|
||||
}
|
||||
float steps = (dist_x/perwalk_x + dist_y/perwalk_y + dist_z/perwalk_z)*10; //Just a safety check to prevent endless loops.
|
||||
while (steps > 0) {
|
||||
steps--;
|
||||
if (tmp_x < trg_x)
|
||||
{
|
||||
if (tmp_x + perwalk_x < trg_x)
|
||||
tmp_x += perwalk_x;
|
||||
else
|
||||
tmp_x = trg_x;
|
||||
}
|
||||
if (tmp_y < trg_y)
|
||||
{
|
||||
if (tmp_y + perwalk_y < trg_y)
|
||||
tmp_y += perwalk_y;
|
||||
else
|
||||
tmp_y = trg_y;
|
||||
}
|
||||
if (tmp_z < trg_z)
|
||||
{
|
||||
if (tmp_z + perwalk_z < trg_z)
|
||||
tmp_z += perwalk_z;
|
||||
else
|
||||
tmp_z = trg_z;
|
||||
}
|
||||
if (tmp_x > trg_x)
|
||||
{
|
||||
if (tmp_x - perwalk_x > trg_x)
|
||||
tmp_x -= perwalk_x;
|
||||
else
|
||||
tmp_x = trg_x;
|
||||
}
|
||||
if (tmp_y > trg_y)
|
||||
{
|
||||
if (tmp_y - perwalk_y > trg_y)
|
||||
tmp_y -= perwalk_y;
|
||||
else
|
||||
tmp_y = trg_y;
|
||||
}
|
||||
if (tmp_z > trg_z)
|
||||
{
|
||||
if (tmp_z - perwalk_z > trg_z)
|
||||
tmp_z -= perwalk_z;
|
||||
else
|
||||
tmp_z = trg_z;
|
||||
}
|
||||
if (tmp_y == trg_y && tmp_x == trg_x && tmp_z == trg_z)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//I believe this is contributing to breaking mob spawns when a map is loaded
|
||||
// NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), tmp_x, tmp_y );
|
||||
NodeRef pnode = NODE_NONE;
|
||||
if (pnode != NODE_NONE)
|
||||
{
|
||||
const int *iface = zone->zonemap->SeekFace( pnode, tmp_x, tmp_y );
|
||||
if (*iface == -1) {
|
||||
return false;
|
||||
}
|
||||
float temp_z = 0;
|
||||
float best_z = 999999;
|
||||
while(*iface != -1)
|
||||
{
|
||||
temp_z = zone->zonemap->GetFaceHeight( *iface, x_pos, y_pos );
|
||||
//UMM.. OMG... sqrtf(pow(x, 2)) == x.... retards
|
||||
float best_dist = sqrtf((float)(pow(best_z-tmp_z, 2)));
|
||||
float tmp_dist = sqrtf((float)(pow(tmp_z-tmp_z, 2)));
|
||||
if (tmp_dist < best_dist)
|
||||
{
|
||||
best_z = temp_z;
|
||||
}
|
||||
iface++;
|
||||
}
|
||||
/* solar: our aggro code isn't using this right now, just spells, so i'm
|
||||
taking out the +-10 check for now to make it work right on hills
|
||||
if (best_z - 10 > trg_z || best_z + 10 < trg_z)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//Father Nitwit's LOS code
|
||||
bool Mob::CheckLosFN(Mob* other) {
|
||||
bool Result = false;
|
||||
@@ -1028,8 +901,8 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) {
|
||||
#endif
|
||||
}
|
||||
|
||||
VERTEX myloc;
|
||||
VERTEX oloc;
|
||||
Map::Vertex myloc;
|
||||
Map::Vertex oloc;
|
||||
|
||||
#define LOS_DEFAULT_HEIGHT 6.0f
|
||||
|
||||
@@ -1044,72 +917,7 @@ bool Mob::CheckLosFN(float posX, float posY, float posZ, float mobSize) {
|
||||
#if LOSDEBUG>=5
|
||||
LogFile->write(EQEMuLog::Debug, "LOS from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f) sizes: (%.2f, %.2f)", myloc.x, myloc.y, myloc.z, oloc.x, oloc.y, oloc.z, GetSize(), mobSize);
|
||||
#endif
|
||||
|
||||
FACE *onhit;
|
||||
NodeRef mynode;
|
||||
NodeRef onode;
|
||||
|
||||
VERTEX hit;
|
||||
//see if anything in our node is in the way
|
||||
mynode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), myloc.x, myloc.y);
|
||||
if(mynode != NODE_NONE) {
|
||||
if(zone->zonemap->LineIntersectsNode(mynode, myloc, oloc, &hit, &onhit)) {
|
||||
#if LOSDEBUG>=5
|
||||
LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, cannot see.", GetName());
|
||||
LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n",
|
||||
onhit->a.x, onhit->a.y, onhit->a.z,
|
||||
onhit->b.x, onhit->b.y, onhit->b.z,
|
||||
onhit->c.x, onhit->c.y, onhit->c.z);
|
||||
#endif
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
#if LOSDEBUG>=5
|
||||
else {
|
||||
LogFile->write(EQEMuLog::Debug, "WTF, I have no node, what am I standing on??? (%.2f, %.2f).", myloc.x, myloc.y);
|
||||
}
|
||||
#endif
|
||||
|
||||
//see if they are in a different node.
|
||||
//if so, see if anything in their node is blocking me.
|
||||
if(! zone->zonemap->LocWithinNode(mynode, oloc.x, oloc.y)) {
|
||||
onode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), oloc.x, oloc.y);
|
||||
if(onode != NODE_NONE && onode != mynode) {
|
||||
if(zone->zonemap->LineIntersectsNode(onode, myloc, oloc, &hit, &onhit)) {
|
||||
#if LOSDEBUG>=5
|
||||
LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, cannot see (2).", GetName());
|
||||
LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n",
|
||||
onhit->a.x, onhit->a.y, onhit->a.z,
|
||||
onhit->b.x, onhit->b.y, onhit->b.z,
|
||||
onhit->c.x, onhit->c.y, onhit->c.z);
|
||||
#endif
|
||||
return(false);
|
||||
}
|
||||
}
|
||||
#if LOSDEBUG>=5
|
||||
else if(onode == NODE_NONE) {
|
||||
LogFile->write(EQEMuLog::Debug, "WTF, They have no node, what are they standing on??? (%.2f, %.2f).", myloc.x, myloc.y);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
if(zone->zonemap->LineIntersectsZone(myloc, oloc, CHECK_LOS_STEP, &onhit)) {
|
||||
#if LOSDEBUG>=5
|
||||
LogFile->write(EQEMuLog::Debug, "Check LOS for %s target %s, cannot see.", GetName(), other->GetName() );
|
||||
LogFile->write(EQEMuLog::Debug, "\tPoly: (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f) (%.2f, %.2f, %.2f)\n",
|
||||
onhit->a.x, onhit->a.y, onhit->a.z,
|
||||
onhit->b.x, onhit->b.y, onhit->b.z,
|
||||
onhit->c.x, onhit->c.y, onhit->c.z);
|
||||
#endif
|
||||
return(false);
|
||||
}*/
|
||||
|
||||
#if LOSDEBUG>=5
|
||||
LogFile->write(EQEMuLog::Debug, "Check LOS for %s target position, CAN SEE.", GetName());
|
||||
#endif
|
||||
|
||||
return(true);
|
||||
return zone->zonemap->CheckLoS(myloc, oloc);
|
||||
}
|
||||
|
||||
//offensive spell aggro
|
||||
|
||||
+4
-1
@@ -2120,7 +2120,8 @@ bool NPC::Death(Mob* killerMob, int32 damage, uint16 spell, SkillUseTypes attack
|
||||
|
||||
if(p_depop == true)
|
||||
return false;
|
||||
|
||||
|
||||
HasAISpellEffects = false;
|
||||
BuffFadeAll();
|
||||
uint8 killed_level = GetLevel();
|
||||
|
||||
@@ -3616,6 +3617,8 @@ void Mob::CommonDamage(Mob* attacker, int32 &damage, const uint16 spell_id, cons
|
||||
//fade mez if we are mezzed
|
||||
if (IsMezzed()) {
|
||||
mlog(COMBAT__HITS, "Breaking mez due to attack.");
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_WornOff,
|
||||
HAS_BEEN_AWAKENED, GetCleanName(), attacker->GetCleanName());
|
||||
BuffFadeByEffect(SE_Mez);
|
||||
}
|
||||
|
||||
|
||||
+124
-101
@@ -1158,14 +1158,20 @@ void Client::ApplyAABonuses(uint32 aaid, uint32 slots, StatBonuses* newbon)
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
case SE_SpellEffectResistChance:
|
||||
{
|
||||
for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2)
|
||||
{
|
||||
if(!newbon->SEResist[e] || ((newbon->SEResist[e] = base2) && (newbon->SEResist[e+1] < base1)) ){
|
||||
newbon->SEResist[e] = base2;
|
||||
newbon->SEResist[e+1] = base1;
|
||||
break;
|
||||
if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < base1)){
|
||||
newbon->SEResist[e] = base2; //Spell Effect ID
|
||||
newbon->SEResist[e+1] = base1; //Resist Chance
|
||||
break;
|
||||
}
|
||||
else if (!newbon->SEResist[e+1]){
|
||||
newbon->SEResist[e] = base2; //Spell Effect ID
|
||||
newbon->SEResist[e+1] = base1; //Resist Chance
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -1258,6 +1264,10 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
|
||||
}
|
||||
}
|
||||
|
||||
//Applies any perma NPC spell bonuses from npc_spells_effects table.
|
||||
if (IsNPC())
|
||||
CastToNPC()->ApplyAISpellEffects(newbon);
|
||||
|
||||
//Removes the spell bonuses that are effected by a 'negate' debuff.
|
||||
if (spellbonuses.NegateEffects){
|
||||
for(i = 0; i < buff_count; i++) {
|
||||
@@ -1270,12 +1280,13 @@ void Mob::CalcSpellBonuses(StatBonuses* newbon)
|
||||
if (GetClass() == BARD) newbon->ManaRegen = 0; // Bards do not get mana regen from spells.
|
||||
}
|
||||
|
||||
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot)
|
||||
void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterId, bool item_bonus, uint32 ticsremaining, int buffslot,
|
||||
bool IsAISpellEffect, uint16 effect_id, int32 se_base, int32 se_limit, int32 se_max)
|
||||
{
|
||||
int i, effect_value;
|
||||
int i, effect_value, base2, max, effectid;
|
||||
Mob *caster = nullptr;
|
||||
|
||||
if(!IsValidSpell(spell_id))
|
||||
if(!IsAISpellEffect && !IsValidSpell(spell_id))
|
||||
return;
|
||||
|
||||
if(casterId > 0)
|
||||
@@ -1283,19 +1294,35 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
for (i = 0; i < EFFECT_COUNT; i++)
|
||||
{
|
||||
if(IsBlankSpellEffect(spell_id, i))
|
||||
continue;
|
||||
//Buffs/Item effects
|
||||
if (!IsAISpellEffect) {
|
||||
|
||||
uint8 focus = IsFocusEffect(spell_id, i);
|
||||
if (focus)
|
||||
{
|
||||
newbon->FocusEffects[focus] = spells[spell_id].effectid[i];
|
||||
continue;
|
||||
if(IsBlankSpellEffect(spell_id, i))
|
||||
continue;
|
||||
|
||||
uint8 focus = IsFocusEffect(spell_id, i);
|
||||
if (focus)
|
||||
{
|
||||
newbon->FocusEffects[focus] = spells[spell_id].effectid[i];
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
effectid = spells[spell_id].effectid[i];
|
||||
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
|
||||
base2 = spells[spell_id].base2[i];
|
||||
max = spells[spell_id].max[i];
|
||||
}
|
||||
//Use AISpellEffects
|
||||
else {
|
||||
effectid = effect_id;
|
||||
effect_value = se_base;
|
||||
base2 = se_limit;
|
||||
max = se_max;
|
||||
i = EFFECT_COUNT; //End the loop
|
||||
}
|
||||
|
||||
effect_value = CalcSpellEffectValue(spell_id, i, casterlevel, caster, ticsremaining);
|
||||
|
||||
switch (spells[spell_id].effectid[i])
|
||||
switch (effectid)
|
||||
{
|
||||
case SE_CurrentHP: //regens
|
||||
if(effect_value > 0) {
|
||||
@@ -1338,19 +1365,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
}
|
||||
}
|
||||
else if ((effect_value - 100) < 0) { // Slow
|
||||
//Slow Mitigation works by taking the amount that would be slowed, and adding a multiplied version of the difference.
|
||||
int real_slow_value = (100 - effect_value) * -1;
|
||||
if (slow_mitigation){
|
||||
int new_effect_value = SlowMitigation(false,caster,real_slow_value);
|
||||
if (new_effect_value < newbon->haste) {
|
||||
newbon->haste = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (real_slow_value < newbon->haste)
|
||||
newbon->haste = real_slow_value;
|
||||
}
|
||||
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
|
||||
if (real_slow_value < newbon->haste)
|
||||
newbon->haste = real_slow_value;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1365,6 +1383,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
}
|
||||
else if ((effect_value - 100) < 0) { // Slow
|
||||
int real_slow_value = (100 - effect_value) * -1;
|
||||
real_slow_value -= ((real_slow_value * GetSlowMitigation()/100));
|
||||
if (real_slow_value < newbon->hastetype2)
|
||||
newbon->hastetype2 = real_slow_value;
|
||||
}
|
||||
@@ -1374,6 +1393,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_AttackSpeed3:
|
||||
{
|
||||
if (effect_value < 0){ //Slow
|
||||
effect_value -= ((effect_value * GetSlowMitigation()/100));
|
||||
if (effect_value < newbon->hastetype3)
|
||||
newbon->hastetype3 = effect_value;
|
||||
}
|
||||
@@ -1392,18 +1412,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
effect_value = effect_value * -1;
|
||||
|
||||
if (effect_value > 0 && effect_value > newbon->inhibitmelee) {
|
||||
|
||||
if (slow_mitigation){
|
||||
int new_effect_value = SlowMitigation(false,caster,effect_value);
|
||||
if (new_effect_value > newbon->inhibitmelee) {
|
||||
newbon->inhibitmelee = new_effect_value;
|
||||
SlowMitigation(true,caster);
|
||||
}
|
||||
}
|
||||
|
||||
else if (effect_value > newbon->inhibitmelee) {
|
||||
effect_value -= ((effect_value * GetSlowMitigation()/100));
|
||||
if (effect_value > newbon->inhibitmelee)
|
||||
newbon->inhibitmelee = effect_value;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1647,27 +1658,27 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_CriticalHitChance:
|
||||
{
|
||||
|
||||
if (RuleB(Spells, AdditiveBonusValues) && item_bonus) {
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->CriticalHitChance[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->CriticalHitChance[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->CriticalHitChance[base2] += effect_value;
|
||||
}
|
||||
|
||||
else if(effect_value < 0) {
|
||||
|
||||
if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value)
|
||||
if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] > effect_value)
|
||||
newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value;
|
||||
else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] > effect_value)
|
||||
newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value;
|
||||
else if(base2 != -1 && newbon->CriticalHitChance[base2] > effect_value)
|
||||
newbon->CriticalHitChance[base2] = effect_value;
|
||||
}
|
||||
|
||||
|
||||
else if(spells[spell_id].base2[i] == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value)
|
||||
else if(base2 == -1 && newbon->CriticalHitChance[HIGHEST_SKILL+1] < effect_value)
|
||||
newbon->CriticalHitChance[HIGHEST_SKILL+1] = effect_value;
|
||||
else if(spells[spell_id].base2[i] != -1 && newbon->CriticalHitChance[spells[spell_id].base2[i]] < effect_value)
|
||||
newbon->CriticalHitChance[spells[spell_id].base2[i]] = effect_value;
|
||||
else if(base2 != -1 && newbon->CriticalHitChance[base2] < effect_value)
|
||||
newbon->CriticalHitChance[base2] = effect_value;
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1786,10 +1797,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
newbon->MeleeLifetap += spells[spell_id].base[i];
|
||||
|
||||
else if((effect_value < 0) && (newbon->MeleeLifetap > effect_value))
|
||||
newbon->MeleeLifetap = spells[spell_id].base[i];
|
||||
newbon->MeleeLifetap = effect_value;
|
||||
|
||||
else if(newbon->MeleeLifetap < spells[spell_id].base[i])
|
||||
newbon->MeleeLifetap = spells[spell_id].base[i];
|
||||
else if(newbon->MeleeLifetap < effect_value)
|
||||
newbon->MeleeLifetap = effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1828,7 +1839,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_HundredHands:
|
||||
{
|
||||
if (RuleB(Spells, AdditiveBonusValues) && item_bonus)
|
||||
newbon->HundredHands += spells[spell_id].base[i];
|
||||
newbon->HundredHands += effect_value;
|
||||
|
||||
if (effect_value > 0 && effect_value > newbon->HundredHands)
|
||||
newbon->HundredHands = effect_value; //Increase Weapon Delay
|
||||
@@ -1841,7 +1852,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
{
|
||||
if(newbon->MeleeSkillCheck < effect_value) {
|
||||
newbon->MeleeSkillCheck = effect_value;
|
||||
newbon->MeleeSkillCheckSkill = spells[spell_id].base2[i]==-1?255:spells[spell_id].base2[i];
|
||||
newbon->MeleeSkillCheckSkill = base2==-1?255:base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1850,13 +1861,13 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
{
|
||||
|
||||
if (RuleB(Spells, AdditiveBonusValues) && item_bonus){
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->HitChanceEffect[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->HitChanceEffect[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->HitChanceEffect[base2] += effect_value;
|
||||
}
|
||||
|
||||
else if(spells[spell_id].base2[i] == -1){
|
||||
else if(base2 == -1){
|
||||
|
||||
if ((effect_value < 0) && (newbon->HitChanceEffect[HIGHEST_SKILL+1] > effect_value))
|
||||
newbon->HitChanceEffect[HIGHEST_SKILL+1] = effect_value;
|
||||
@@ -1868,12 +1879,12 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
else {
|
||||
|
||||
if ((effect_value < 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] > effect_value))
|
||||
newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value;
|
||||
if ((effect_value < 0) && (newbon->HitChanceEffect[base2] > effect_value))
|
||||
newbon->HitChanceEffect[base2] = effect_value;
|
||||
|
||||
else if (!newbon->HitChanceEffect[spells[spell_id].base2[i]] ||
|
||||
((newbon->HitChanceEffect[spells[spell_id].base2[i]] > 0) && (newbon->HitChanceEffect[spells[spell_id].base2[i]] < effect_value)))
|
||||
newbon->HitChanceEffect[spells[spell_id].base2[i]] = effect_value;
|
||||
else if (!newbon->HitChanceEffect[base2] ||
|
||||
((newbon->HitChanceEffect[base2] > 0) && (newbon->HitChanceEffect[base2] < effect_value)))
|
||||
newbon->HitChanceEffect[base2] = effect_value;
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1882,19 +1893,19 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_DamageModifier:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->DamageModifier[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->DamageModifier[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->DamageModifier[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_MinDamageModifier:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->MinDamageModifier[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->MinDamageModifier[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->MinDamageModifier[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1937,8 +1948,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
newbon->DeathSave[0] = effect_value; //1='Partial' 2='Full'
|
||||
newbon->DeathSave[1] = buffslot;
|
||||
//These are used in later expansion spell effects.
|
||||
newbon->DeathSave[2] = spells[spell_id].base2[i];//Min level for HealAmt
|
||||
newbon->DeathSave[3] = spells[spell_id].max[i];//HealAmt
|
||||
newbon->DeathSave[2] = base2;//Min level for HealAmt
|
||||
newbon->DeathSave[3] = max;//HealAmt
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1953,7 +1964,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
else if(newbon->DivineSaveChance[0] < effect_value)
|
||||
{
|
||||
newbon->DivineSaveChance[0] = effect_value;
|
||||
newbon->DivineSaveChance[1] = spells[spell_id].base2[i];
|
||||
newbon->DivineSaveChance[1] = base2;
|
||||
//SetDeathSaveChance(true);
|
||||
}
|
||||
break;
|
||||
@@ -1988,10 +1999,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_SkillDamageTaken:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->SkillDmgTaken[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->SkillDmgTaken[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->SkillDmgTaken[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2016,8 +2027,8 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
{
|
||||
newbon->CriticalSpellChance += effect_value;
|
||||
|
||||
if (spells[spell_id].base2[i] > newbon->SpellCritDmgIncNoStack)
|
||||
newbon->SpellCritDmgIncNoStack = spells[spell_id].base2[i];
|
||||
if (base2 > newbon->SpellCritDmgIncNoStack)
|
||||
newbon->SpellCritDmgIncNoStack = base2;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2069,9 +2080,9 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
if(!newbon->SpellOnKill[e])
|
||||
{
|
||||
// Base2 = Spell to fire | Base1 = % chance | Base3 = min level
|
||||
newbon->SpellOnKill[e] = spells[spell_id].base2[i];
|
||||
newbon->SpellOnKill[e] = base2;
|
||||
newbon->SpellOnKill[e+1] = effect_value;
|
||||
newbon->SpellOnKill[e+2] = spells[spell_id].max[i];
|
||||
newbon->SpellOnKill[e+2] = max;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2085,7 +2096,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
if(!newbon->SpellOnDeath[e])
|
||||
{
|
||||
// Base2 = Spell to fire | Base1 = % chance
|
||||
newbon->SpellOnDeath[e] = spells[spell_id].base2[i];
|
||||
newbon->SpellOnDeath[e] = base2;
|
||||
newbon->SpellOnDeath[e+1] = effect_value;
|
||||
break;
|
||||
}
|
||||
@@ -2095,26 +2106,26 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_CriticalDamageMob:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->CritDmgMob[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->CritDmgMob[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->CritDmgMob[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_ReduceSkillTimer:
|
||||
{
|
||||
if(newbon->SkillReuseTime[spells[spell_id].base2[i]] < effect_value)
|
||||
newbon->SkillReuseTime[spells[spell_id].base2[i]] = effect_value;
|
||||
if(newbon->SkillReuseTime[base2] < effect_value)
|
||||
newbon->SkillReuseTime[base2] = effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_SkillDamageAmount:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->SkillDamageAmount[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->SkillDamageAmount[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->SkillDamageAmount[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2204,10 +2215,10 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_SkillDamageAmount2:
|
||||
{
|
||||
if(spells[spell_id].base2[i] == -1)
|
||||
if(base2 == -1)
|
||||
newbon->SkillDamageAmount2[HIGHEST_SKILL+1] += effect_value;
|
||||
else
|
||||
newbon->SkillDamageAmount2[spells[spell_id].base2[i]] += effect_value;
|
||||
newbon->SkillDamageAmount2[base2] += effect_value;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2235,7 +2246,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
if (newbon->MeleeThresholdGuard[0] < effect_value){
|
||||
newbon->MeleeThresholdGuard[0] = effect_value;
|
||||
newbon->MeleeThresholdGuard[1] = buffslot;
|
||||
newbon->MeleeThresholdGuard[2] = spells[spell_id].base2[i];
|
||||
newbon->MeleeThresholdGuard[2] = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2245,7 +2256,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
if (newbon->SpellThresholdGuard[0] < effect_value){
|
||||
newbon->SpellThresholdGuard[0] = effect_value;
|
||||
newbon->SpellThresholdGuard[1] = buffslot;
|
||||
newbon->SpellThresholdGuard[2] = spells[spell_id].base2[i];
|
||||
newbon->SpellThresholdGuard[2] = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2279,20 +2290,20 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_TriggerMeleeThreshold:
|
||||
{
|
||||
if (newbon->TriggerMeleeThreshold[2] < spells[spell_id].base2[i]){
|
||||
if (newbon->TriggerMeleeThreshold[2] < base2){
|
||||
newbon->TriggerMeleeThreshold[0] = effect_value;
|
||||
newbon->TriggerMeleeThreshold[1] = buffslot;
|
||||
newbon->TriggerMeleeThreshold[2] = spells[spell_id].base2[i];
|
||||
newbon->TriggerMeleeThreshold[2] = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_TriggerSpellThreshold:
|
||||
{
|
||||
if (newbon->TriggerSpellThreshold[2] < spells[spell_id].base2[i]){
|
||||
if (newbon->TriggerSpellThreshold[2] < base2){
|
||||
newbon->TriggerSpellThreshold[0] = effect_value;
|
||||
newbon->TriggerSpellThreshold[1] = buffslot;
|
||||
newbon->TriggerSpellThreshold[2] = spells[spell_id].base2[i];
|
||||
newbon->TriggerSpellThreshold[2] = base2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -2307,7 +2318,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
|
||||
case SE_ShieldEquipDmgMod:
|
||||
newbon->ShieldEquipDmgMod[0] += effect_value;
|
||||
newbon->ShieldEquipDmgMod[1] += spells[spell_id].base2[i];
|
||||
newbon->ShieldEquipDmgMod[1] += base2;
|
||||
break;
|
||||
|
||||
case SE_BlockBehind:
|
||||
@@ -2396,7 +2407,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
break;
|
||||
|
||||
case SE_AddSingingMod:
|
||||
switch (spells[spell_id].base2[i])
|
||||
switch (base2)
|
||||
{
|
||||
case ItemTypeWindInstrument:
|
||||
newbon->windMod += effect_value;
|
||||
@@ -2489,10 +2500,14 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
{
|
||||
for(int e = 0; e < MAX_RESISTABLE_EFFECTS*2; e+=2)
|
||||
{
|
||||
if(!newbon->SEResist[e] &&
|
||||
((newbon->SEResist[e] = spells[spell_id].base2[i]) && (newbon->SEResist[e+1] < effect_value)) ){
|
||||
newbon->SEResist[e] = spells[spell_id].base2[i];
|
||||
newbon->SEResist[e+1] = effect_value;
|
||||
if(newbon->SEResist[e+1] && (newbon->SEResist[e] == base2) && (newbon->SEResist[e+1] < effect_value)){
|
||||
newbon->SEResist[e] = base2; //Spell Effect ID
|
||||
newbon->SEResist[e+1] = effect_value; //Resist Chance
|
||||
break;
|
||||
}
|
||||
else if (!newbon->SEResist[e+1]){
|
||||
newbon->SEResist[e] = base2; //Spell Effect ID
|
||||
newbon->SEResist[e+1] = effect_value; //Resist Chance
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -2509,7 +2524,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_GiveDoubleRiposte:
|
||||
{
|
||||
//Only allow for regular double riposte chance.
|
||||
if(newbon->GiveDoubleRiposte[spells[spell_id].base2[i]] == 0){
|
||||
if(newbon->GiveDoubleRiposte[base2] == 0){
|
||||
if(newbon->GiveDoubleRiposte[0] < effect_value)
|
||||
newbon->GiveDoubleRiposte[0] = effect_value;
|
||||
}
|
||||
@@ -2520,7 +2535,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
{
|
||||
if(newbon->SlayUndead[1] < effect_value)
|
||||
newbon->SlayUndead[0] = effect_value; // Rate
|
||||
newbon->SlayUndead[1] = spells[spell_id].base2[i]; // Damage Modifier
|
||||
newbon->SlayUndead[1] = base2; // Damage Modifier
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2536,7 +2551,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_ImprovedTaunt:
|
||||
if (newbon->ImprovedTaunt[0] < effect_value) {
|
||||
newbon->ImprovedTaunt[0] = effect_value;
|
||||
newbon->ImprovedTaunt[1] = spells[spell_id].base2[i];
|
||||
newbon->ImprovedTaunt[1] = base2;
|
||||
newbon->ImprovedTaunt[2] = buffslot;
|
||||
}
|
||||
break;
|
||||
@@ -2547,7 +2562,7 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
break;
|
||||
|
||||
case SE_FrenziedDevastation:
|
||||
newbon->FrenziedDevastation += spells[spell_id].base2[i];
|
||||
newbon->FrenziedDevastation += base2;
|
||||
break;
|
||||
|
||||
case SE_Root:
|
||||
@@ -2593,7 +2608,15 @@ void Mob::ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* ne
|
||||
case SE_Screech:
|
||||
newbon->Screech = effect_value;
|
||||
break;
|
||||
|
||||
|
||||
//Special custom cases for loading effects on to NPC from 'npc_spels_effects' table
|
||||
if (IsAISpellEffect) {
|
||||
|
||||
//Non-Focused Effect to modify incomming spell damage by resist type.
|
||||
case SE_FcSpellVulnerability:
|
||||
ModVulnerability(base2, effect_value);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+32
-4
@@ -1022,11 +1022,12 @@ void Client::ChannelMessageReceived(uint8 chan_num, uint8 language, uint8 lang_s
|
||||
if(command_dispatch(this, message) == -2) {
|
||||
if(parse->PlayerHasQuestSub(EVENT_COMMAND)) {
|
||||
int i = parse->EventPlayer(EVENT_COMMAND, this, message, 0);
|
||||
if(i == 0) {
|
||||
if(i == 0 && !RuleB(Chat, SuppressCommandErrors)) {
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
}
|
||||
} else {
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
if(!RuleB(Chat, SuppressCommandErrors))
|
||||
Message(13, "Command '%s' not recognized.", message);
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -8167,7 +8168,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, EATING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
#if EQDEBUG >= 1
|
||||
#if EQDEBUG >= 5
|
||||
LogFile->write(EQEMuLog::Debug, "Eating from slot:%i", (int)slot);
|
||||
#endif
|
||||
}
|
||||
@@ -8184,7 +8185,7 @@ void Client::Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_
|
||||
if(!auto_consume) //no message if the client consumed for us
|
||||
entity_list.MessageClose_StringID(this, true, 50, 0, DRINKING_MESSAGE, GetName(), item->Name);
|
||||
|
||||
#if EQDEBUG >= 1
|
||||
#if EQDEBUG >= 5
|
||||
LogFile->write(EQEMuLog::Debug, "Drinking from slot:%i", (int)slot);
|
||||
#endif
|
||||
}
|
||||
@@ -8219,3 +8220,30 @@ void Client::PlayMP3(const char* fname)
|
||||
QueuePacket(outapp);
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Client::ExpeditionSay(const char *str, int ExpID) {
|
||||
char errbuf[MYSQL_ERRMSG_SIZE];
|
||||
char* query = 0;
|
||||
MYSQL_RES *result;
|
||||
MYSQL_ROW row;
|
||||
|
||||
if (!database.RunQuery(query,MakeAnyLenString(&query, "SELECT `player_name` FROM `cust_inst_players` WHERE `inst_id` = %i", ExpID),errbuf,&result)){
|
||||
safe_delete_array(query);
|
||||
return;
|
||||
}
|
||||
|
||||
safe_delete_array(query);
|
||||
|
||||
if(result)
|
||||
this->Message(14, "You say to the expedition, '%s'", str);
|
||||
|
||||
while((row = mysql_fetch_row(result))) {
|
||||
const char* CharName = row[0];
|
||||
if(strcmp(CharName, this->GetCleanName()) != 0)
|
||||
worldserver.SendEmoteMessage(CharName, 0, 0, 14, "%s says to the expedition, '%s'", this->GetCleanName(), str);
|
||||
// ChannelList->CreateChannel(ChannelName, ChannelOwner, ChannelPassword, true, atoi(row[3]));
|
||||
}
|
||||
|
||||
mysql_free_result(result);
|
||||
|
||||
}
|
||||
|
||||
+3
-2
@@ -1170,6 +1170,7 @@ public:
|
||||
std::string GetAccountFlag(std::string flag); float GetDamageMultiplier(SkillUseTypes);
|
||||
void Consume(const Item_Struct *item, uint8 type, int16 slot, bool auto_consume);
|
||||
void PlayMP3(const char* fname);
|
||||
void ExpeditionSay(const char *str, int ExpID);
|
||||
int mod_client_damage(int damage, SkillUseTypes skillinuse, int hand, const ItemInst* weapon, Mob* other);
|
||||
bool mod_client_message(char* message, uint8 chan_num);
|
||||
bool mod_can_increase_skill(SkillUseTypes skillid, Mob* against_who);
|
||||
@@ -1209,8 +1210,8 @@ protected:
|
||||
|
||||
Mob* bind_sight_target;
|
||||
|
||||
VERTEX aa_los_me;
|
||||
VERTEX aa_los_them;
|
||||
Map::Vertex aa_los_me;
|
||||
Map::Vertex aa_los_them;
|
||||
Mob *aa_los_them_mob;
|
||||
bool los_status;
|
||||
float aa_los_me_heading;
|
||||
|
||||
+44
-29
@@ -8560,10 +8560,10 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app)
|
||||
}
|
||||
else
|
||||
{
|
||||
VERTEX Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION);
|
||||
VERTEX End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION);
|
||||
Map::Vertex Start(GetX(), GetY(), GetZ() + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION);
|
||||
Map::Vertex End(target->GetX(), target->GetY(), target->GetZ() + (target->GetSize() < 6.0 ? 6 : target->GetSize()) * HEAD_POSITION);
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr, nullptr) && zone->pathing->NoHazards(Start, End))
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, End, 1.0f, nullptr) && zone->pathing->NoHazards(Start, End))
|
||||
{
|
||||
points.resize(2);
|
||||
points[0].x = Start.x;
|
||||
@@ -8604,7 +8604,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app)
|
||||
|
||||
bool LeadsToTeleporter = false;
|
||||
|
||||
VERTEX v = zone->pathing->GetPathNodeCoordinates(pathlist.back());
|
||||
Map::Vertex v = zone->pathing->GetPathNodeCoordinates(pathlist.back());
|
||||
|
||||
p.x = v.x;
|
||||
p.y = v.y;
|
||||
@@ -8624,7 +8624,7 @@ void Client::Handle_OP_FindPersonRequest(const EQApplicationPacket *app)
|
||||
break;
|
||||
}
|
||||
|
||||
VERTEX v = zone->pathing->GetPathNodeCoordinates((*Iterator), false);
|
||||
Map::Vertex v = zone->pathing->GetPathNodeCoordinates((*Iterator), false);
|
||||
p.x = v.x;
|
||||
p.y = v.y;
|
||||
p.z = v.z;
|
||||
@@ -8795,33 +8795,32 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) {
|
||||
//if we zone in with invalid Z, fix it.
|
||||
if (zone->zonemap != nullptr) {
|
||||
|
||||
//for whatever reason, LineIntersectsNode is giving better results than FindBestZ
|
||||
|
||||
NodeRef pnode;
|
||||
VERTEX me;
|
||||
Map::Vertex me;
|
||||
me.x = GetX();
|
||||
me.y = GetY();
|
||||
me.z = GetZ() + (GetSize()==0.0?6:GetSize());
|
||||
pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), me.x, me.y );
|
||||
me.z = GetZ() + (GetSize() == 0.0 ? 6 : GetSize());
|
||||
|
||||
VERTEX hit;
|
||||
VERTEX below_me(me);
|
||||
below_me.z -= 500;
|
||||
if(!zone->zonemap->LineIntersectsNode(pnode, me, below_me, &hit, nullptr) || hit.z < -5000) {
|
||||
Map::Vertex hit;
|
||||
|
||||
if (zone->zonemap->FindBestZ(me, &hit) == BEST_Z_INVALID)
|
||||
{
|
||||
#if EQDEBUG >= 5
|
||||
LogFile->write(EQEMuLog::Debug, "Player %s started below the zone trying to fix! (%.3f, %.3f, %.3f)", GetName(), me.x, me.y, me.z);
|
||||
#endif
|
||||
//theres nothing below us... try to find something to stand on
|
||||
me.z += 200; //arbitrary #
|
||||
if(zone->zonemap->LineIntersectsNode(pnode, me, below_me, &hit, nullptr)) {
|
||||
if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID)
|
||||
{
|
||||
//+10 so they dont stick in the ground
|
||||
SendTo(me.x, me.y, hit.z + 10);
|
||||
m_pp.z = hit.z + 10;
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
//one more, desperate try
|
||||
me.z += 2000;
|
||||
if(zone->zonemap->LineIntersectsNode(pnode, me, below_me, &hit, nullptr)) {
|
||||
//+10 so they dont stick in the ground
|
||||
if (zone->zonemap->FindBestZ(me, &hit) != BEST_Z_INVALID)
|
||||
{
|
||||
//+10 so they dont stick in the ground
|
||||
SendTo(me.x, me.y, hit.z + 10);
|
||||
m_pp.z = hit.z + 10;
|
||||
}
|
||||
@@ -8829,14 +8828,6 @@ bool Client::FinishConnState2(DBAsyncWork* dbaw) {
|
||||
}
|
||||
}
|
||||
|
||||
//m_pp.hunger_level = 6000;
|
||||
//m_pp.thirst_level = 6000;
|
||||
|
||||
//aa_title = m_pp.aa_title;
|
||||
//m_pp.timeplayed=64;
|
||||
//m_pp.birthday=1057434792;
|
||||
//m_pp.lastlogin=1057464792;
|
||||
|
||||
if (m_pp.gm && admin < minStatusToBeGM)
|
||||
m_pp.gm = 0;
|
||||
|
||||
@@ -12483,7 +12474,15 @@ void Client::Handle_OP_GuildCreate(const EQApplicationPacket *app)
|
||||
//
|
||||
|
||||
char *GuildName = (char *)app->pBuffer;
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(GuildName) > 60)
|
||||
#else
|
||||
if(strnlen(GuildName, 64) > 60)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(GuildName, 64) > 60)
|
||||
#endif // DARWIN
|
||||
{
|
||||
Message(clientMessageError, "Guild name too long.");
|
||||
return;
|
||||
@@ -12941,7 +12940,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
|
||||
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_PlayerToggle_Struct);
|
||||
LFGuild_PlayerToggle_Struct *pts = (LFGuild_PlayerToggle_Struct *)app->pBuffer;
|
||||
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(pts->Comment) > 256)
|
||||
#else
|
||||
if(strnlen(pts->Comment, 256) > 256)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(pts->Comment, 256) > 256)
|
||||
#endif // DARWIN
|
||||
return;
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(pts->Comment) + 38);
|
||||
@@ -12968,7 +12975,15 @@ void Client::Handle_OP_LFGuild(const EQApplicationPacket *app)
|
||||
VERIFY_PACKET_LENGTH(OP_LFGuild, app, LFGuild_GuildToggle_Struct);
|
||||
LFGuild_GuildToggle_Struct *gts = (LFGuild_GuildToggle_Struct *)app->pBuffer;
|
||||
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#ifdef DARWIN
|
||||
#if __DARWIN_C_LEVEL < 200809L
|
||||
if (strlen(gts->Comment) > 256)
|
||||
#else
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#endif // __DARWIN_C_LEVEL
|
||||
#else
|
||||
if(strnlen(gts->Comment, 256) > 256)
|
||||
#endif // __DARWIN
|
||||
return;
|
||||
|
||||
ServerPacket* pack = new ServerPacket(ServerOP_QueryServGeneric, strlen(GetName()) + strlen(gts->Comment) + strlen(guild_mgr.GetGuildName(GuildID())) + 43);
|
||||
|
||||
+12
-28
@@ -7497,8 +7497,8 @@ void command_path(Client *c, const Seperator *sep)
|
||||
|
||||
if(zone->zonemap)
|
||||
{
|
||||
VERTEX loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, nullptr, nullptr);
|
||||
Map::Vertex loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(loc, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7524,8 +7524,8 @@ void command_path(Client *c, const Seperator *sep)
|
||||
|
||||
if(zone->zonemap)
|
||||
{
|
||||
VERTEX loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, nullptr, nullptr);
|
||||
Map::Vertex loc(px, py, pz);
|
||||
best_z = zone->zonemap->FindBestZ(loc, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -7657,8 +7657,8 @@ void command_path(Client *c, const Seperator *sep)
|
||||
{
|
||||
if(c && c->GetTarget())
|
||||
{
|
||||
if(zone->pathing->NoHazardsAccurate(VERTEX(c->GetX(),c->GetY(),c->GetZ()),
|
||||
VERTEX(c->GetTarget()->GetX(),c->GetTarget()->GetY(),c->GetTarget()->GetZ())))
|
||||
if (zone->pathing->NoHazardsAccurate(Map::Vertex(c->GetX(), c->GetY(), c->GetZ()),
|
||||
Map::Vertex(c->GetTarget()->GetX(), c->GetTarget()->GetY(), c->GetTarget()->GetZ())))
|
||||
{
|
||||
c->Message(0, "No hazards.");
|
||||
}
|
||||
@@ -7734,7 +7734,7 @@ void command_path(Client *c, const Seperator *sep)
|
||||
{
|
||||
Mob *m = c->GetTarget();
|
||||
|
||||
VERTEX Position(m->GetX(), m->GetY(), m->GetZ());
|
||||
Map::Vertex Position(m->GetX(), m->GetY(), m->GetZ());
|
||||
|
||||
int Node = zone->pathing->FindNearestPathNode(Position);
|
||||
|
||||
@@ -7867,35 +7867,19 @@ void command_bestz(Client *c, const Seperator *sep) {
|
||||
return;
|
||||
}
|
||||
|
||||
NodeRef pnode;
|
||||
if(c->GetTarget()) {
|
||||
pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), c->GetTarget()->GetX(), c->GetTarget()->GetY() );
|
||||
} else {
|
||||
pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), c->GetX(), c->GetY() );
|
||||
}
|
||||
if (pnode == NODE_NONE) {
|
||||
c->Message(0,"Unable to find your node.");
|
||||
return;
|
||||
}
|
||||
|
||||
VERTEX me;
|
||||
Map::Vertex me;
|
||||
me.x = c->GetX();
|
||||
me.y = c->GetY();
|
||||
me.z = c->GetZ() + (c->GetSize()==0.0?6:c->GetSize()) * HEAD_POSITION;
|
||||
VERTEX hit;
|
||||
VERTEX bme(me);
|
||||
Map::Vertex hit;
|
||||
Map::Vertex bme(me);
|
||||
bme.z -= 500;
|
||||
|
||||
float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, nullptr);
|
||||
|
||||
float best_z2 = -999990;
|
||||
if(zone->zonemap->LineIntersectsNode(pnode, me, bme, &hit, nullptr)) {
|
||||
best_z2 = hit.z;
|
||||
}
|
||||
float best_z = zone->zonemap->FindBestZ(me, &hit);
|
||||
|
||||
if (best_z != -999999)
|
||||
{
|
||||
c->Message(0,"Z is %.3f or %.3f at (%.3f, %.3f).", best_z, best_z2, me.x, me.y);
|
||||
c->Message(0,"Z is %.3f at (%.3f, %.3f).", best_z, me.x, me.y);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
+45
-3
@@ -55,9 +55,36 @@ int32 NPC::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
else
|
||||
value -= target->GetFcDamageAmtIncoming(this, spell_id)/spells[spell_id].buffduration;
|
||||
}
|
||||
|
||||
|
||||
value += dmg*SpellFocusDMG/100;
|
||||
|
||||
if (AI_HasSpellsEffects()){
|
||||
int16 chance = 0;
|
||||
int ratio = 0;
|
||||
|
||||
if (spells[spell_id].buffduration == 0) {
|
||||
|
||||
chance += spellbonuses.CriticalSpellChance + spellbonuses.FrenziedDevastation;
|
||||
|
||||
if (chance && MakeRandomInt(1,100) <= chance){
|
||||
|
||||
ratio += spellbonuses.SpellCritDmgIncrease + spellbonuses.SpellCritDmgIncNoStack;
|
||||
value += (value*ratio)/100;
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_BLAST, GetCleanName(), itoa(-value));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
chance += spellbonuses.CriticalDoTChance;
|
||||
|
||||
if (chance && MakeRandomInt(1,100) <= chance){
|
||||
|
||||
ratio += spellbonuses.DotCritDmgIncrease;
|
||||
value += (value*ratio)/100;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -79,7 +106,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
if (spell_id == SPELL_IMP_HARM_TOUCH) //Improved Harm Touch
|
||||
value -= GetAA(aaUnholyTouch) * 450; //Unholy Touch
|
||||
|
||||
int chance = RuleI(Spells, BaseCritChance);
|
||||
int chance = RuleI(Spells, BaseCritChance); //Wizard base critical chance is 2% (Does not scale with level)
|
||||
chance += itembonuses.CriticalSpellChance + spellbonuses.CriticalSpellChance + aabonuses.CriticalSpellChance;
|
||||
|
||||
chance += itembonuses.FrenziedDevastation + spellbonuses.FrenziedDevastation + aabonuses.FrenziedDevastation;
|
||||
@@ -99,7 +126,7 @@ int32 Client::GetActSpellDamage(uint16 spell_id, int32 value, Mob* target) {
|
||||
}
|
||||
|
||||
else if (GetClass() == WIZARD && (GetLevel() >= RuleI(Spells, WizCritLevel)) && (MakeRandomInt(1,100) <= RuleI(Spells, WizCritChance))) {
|
||||
ratio += MakeRandomInt(1,100); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio.
|
||||
ratio += MakeRandomInt(20,70); //Wizard innate critical chance is calculated seperately from spell effect and is not a set ratio. (20-70 is parse confirmed)
|
||||
Critical = true;
|
||||
}
|
||||
|
||||
@@ -254,6 +281,21 @@ int32 NPC::GetActSpellHealing(uint16 spell_id, int32 value, Mob* target) {
|
||||
value += target->GetFocusIncoming(focusFcHealAmtIncoming, SE_FcHealAmtIncoming, this, spell_id);
|
||||
value += value*target->GetHealRate(spell_id, this)/100;
|
||||
}
|
||||
|
||||
//Allow for critical heal chance if NPC is loading spell effect bonuses.
|
||||
if (AI_HasSpellsEffects()){
|
||||
|
||||
if(spells[spell_id].buffduration < 1) {
|
||||
|
||||
if(spellbonuses.CriticalHealChance && (MakeRandomInt(0,99) < spellbonuses.CriticalHealChance)) {
|
||||
value = value*2;
|
||||
entity_list.MessageClose_StringID(this, true, 100, MT_SpellCrits, OTHER_CRIT_HEAL, GetCleanName(), itoa(value));
|
||||
}
|
||||
}
|
||||
else if(spellbonuses.CriticalHealOverTime && (MakeRandomInt(0,99) < spellbonuses.CriticalHealOverTime)) {
|
||||
value = value*2;
|
||||
}
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -1678,14 +1678,15 @@ XS(XS__toggle_spawn_event);
|
||||
XS(XS__toggle_spawn_event)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 2)
|
||||
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)");
|
||||
if (items != 4)
|
||||
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, strict, reset_base)");
|
||||
|
||||
uint32 event_id = (int)SvIV(ST(0));
|
||||
bool enabled = ((int)SvIV(ST(1))) == 0?false:true;
|
||||
bool reset_base = ((int)SvIV(ST(1))) == 0?false:true;
|
||||
bool strict = ((int)SvIV(ST(2))) == 0?false:true;
|
||||
bool reset_base = ((int)SvIV(ST(3))) == 0?false:true;
|
||||
|
||||
quest_manager.toggle_spawn_event(event_id, enabled, reset_base);
|
||||
quest_manager.toggle_spawn_event(event_id, enabled, strict, reset_base);
|
||||
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
+6
-8
@@ -2606,8 +2606,8 @@ void EntityList::FindPathsToAllNPCs()
|
||||
|
||||
auto it = npc_list.begin();
|
||||
while (it != npc_list.end()) {
|
||||
VERTEX Node0 = zone->pathing->GetPathNodeCoordinates(0, false);
|
||||
VERTEX Dest(it->second->GetX(), it->second->GetY(), it->second->GetZ());
|
||||
Map::Vertex Node0 = zone->pathing->GetPathNodeCoordinates(0, false);
|
||||
Map::Vertex Dest(it->second->GetX(), it->second->GetY(), it->second->GetZ());
|
||||
std::list<int> Route = zone->pathing->FindRoute(Node0, Dest);
|
||||
if (Route.size() == 0)
|
||||
printf("Unable to find a route to %s\n", it->second->GetName());
|
||||
@@ -3611,9 +3611,9 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
|
||||
if (zone->zonemap == nullptr)
|
||||
return true;
|
||||
|
||||
VERTEX myloc;
|
||||
VERTEX oloc;
|
||||
VERTEX hit;
|
||||
Map::Vertex myloc;
|
||||
Map::Vertex oloc;
|
||||
Map::Vertex hit;
|
||||
|
||||
myloc.x = cur_x;
|
||||
myloc.y = cur_y;
|
||||
@@ -3626,9 +3626,7 @@ bool Entity::CheckCoordLosNoZLeaps(float cur_x, float cur_y, float cur_z,
|
||||
if (myloc.x == oloc.x && myloc.y == oloc.y && myloc.z == oloc.z)
|
||||
return true;
|
||||
|
||||
FACE *onhit;
|
||||
|
||||
if (!zone->zonemap->LineIntersectsZoneNoZLeaps(myloc,oloc,perwalk,&hit,&onhit))
|
||||
if (!zone->zonemap->LineIntersectsZoneNoZLeaps(myloc,oloc,perwalk,&hit))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
+2
-425
@@ -156,11 +156,11 @@ void Mob::CalculateNewFearpoint()
|
||||
{
|
||||
int Node = zone->pathing->GetRandomPathNode();
|
||||
|
||||
VERTEX Loc = zone->pathing->GetPathNodeCoordinates(Node);
|
||||
Map::Vertex Loc = zone->pathing->GetPathNodeCoordinates(Node);
|
||||
|
||||
++Loc.z;
|
||||
|
||||
VERTEX CurrentPosition(GetX(), GetY(), GetZ());
|
||||
Map::Vertex CurrentPosition(GetX(), GetY(), GetZ());
|
||||
|
||||
std::list<int> Route = zone->pathing->FindRoute(CurrentPosition, Loc);
|
||||
|
||||
@@ -209,429 +209,6 @@ void Mob::CalculateNewFearpoint()
|
||||
}
|
||||
}
|
||||
|
||||
//we need to start acting scared...
|
||||
//old fear function, kept for ref.
|
||||
/*void Mob::SetFeared(Mob *caster, uint32 duration, bool flee) {
|
||||
//special args to stop fear
|
||||
if(caster == nullptr && duration == 0) {
|
||||
fear_state = fearStateNotFeared;
|
||||
#ifdef FLEE_HP_RATIO
|
||||
flee_mode = false;
|
||||
#endif
|
||||
safe_delete(fear_path_state);
|
||||
return;
|
||||
}
|
||||
|
||||
flee_mode = flee;
|
||||
|
||||
//fear dosent work without at least maps
|
||||
if(zone->zonemap == nullptr) {
|
||||
fear_state = fearStateStuck;
|
||||
return; //just stand there
|
||||
}
|
||||
|
||||
//if we are allready feared, and we are on a fear grid..
|
||||
//then just stay happy on the grid...
|
||||
if(fear_path_state != nullptr) {
|
||||
if(fear_state != fearStateGrid) {
|
||||
LogFile->write(EQEMuLog::Debug, "Umm... %s has a fear path state, but is not in a grid state. Wtf?", GetName());
|
||||
fear_state = fearStateGrid;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//try to run straight away from the caster
|
||||
VERTEX hit, fear_vector;
|
||||
if(FearTryStraight(caster, duration, flee, hit, fear_vector)) {
|
||||
return;
|
||||
}
|
||||
|
||||
//OK, so if we just run, we are going to hit something...
|
||||
//now we have to think a little more.
|
||||
|
||||
//first, try to find a fear node that we can see.
|
||||
if(zone->pathing != nullptr) {
|
||||
fear_path_state = new MobFearState();
|
||||
if(zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ())) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: found path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z);
|
||||
#endif
|
||||
//we found a fear node... were on our way..
|
||||
cur_wp_x = fear_path_state->x;
|
||||
cur_wp_y = fear_path_state->y;
|
||||
cur_wp_z = fear_path_state->z;
|
||||
fear_state = fearStateGrid;
|
||||
return;
|
||||
}
|
||||
|
||||
//we have failed to find a path, so we dont need this..
|
||||
safe_delete(fear_path_state);
|
||||
}
|
||||
|
||||
//if we cannot just run, and we cannot see any paths, then
|
||||
//we will give one last ditch effort to find a legit path. We
|
||||
//will run as far as we can away from the player, and hope we
|
||||
//can see a path from there if not, we will start breaking rules
|
||||
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: Hope run from (%.2f, %.2f, %.2f), hit at (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), hit.x, hit.y, hit.z);
|
||||
#endif
|
||||
//use the hit point - a little + a little Z as the first waypoint.
|
||||
cur_wp_x = hit.x - fear_vector.x * 2;
|
||||
cur_wp_y = hit.y - fear_vector.y * 2;
|
||||
cur_wp_z = GetZ();
|
||||
fear_state = fearStateRunning;
|
||||
}
|
||||
//old fear function, kept for ref.
|
||||
bool Mob::FearTryStraight(Mob *caster, uint32 duration, bool flee, VERTEX &hit, VERTEX &fear_vector) {
|
||||
//gotta have somebody to run from
|
||||
if(caster == nullptr)
|
||||
return(false);
|
||||
|
||||
//our goal is to run along this vector...
|
||||
fear_vector.x = GetX() - caster->GetX();
|
||||
fear_vector.y = GetY() - caster->GetY();
|
||||
fear_vector.z = 0; //I dont see any reason to use Z
|
||||
float mag = sqrtf(fear_vector.x*fear_vector.x + fear_vector.y*fear_vector.y);
|
||||
fear_vector.x /= mag;
|
||||
fear_vector.y /= mag;
|
||||
|
||||
//now see if we can just run without hitting anything...
|
||||
VERTEX start, end;
|
||||
start.x = GetX();
|
||||
start.y = GetY();
|
||||
start.z = GetZ() + 5.0; //raise up a little over small bumps
|
||||
|
||||
//distance moved per movement tic.
|
||||
float distance = NPC_SPEED_MULTIPLIER * GetFearSpeed();
|
||||
//times number of movement tics in the spell.
|
||||
distance *= float(duration) / float(AImovement_duration);
|
||||
|
||||
end.x = start.x + fear_vector.x * distance;
|
||||
end.y = start.y + fear_vector.y * distance;
|
||||
end.z = start.z;
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, nullptr)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing Start: can run entire vector from (%.2f, %.2f, %.2f) to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), end.x, end.y, end.z);
|
||||
#endif
|
||||
//no hit, we can run this whole vector.
|
||||
cur_wp_x = end.x;
|
||||
cur_wp_y = end.y;
|
||||
cur_wp_z = GetZ();
|
||||
fear_state = fearStateRunningForever;
|
||||
return(true); //were done, nothing difficult needed.
|
||||
}
|
||||
|
||||
return(false);
|
||||
}
|
||||
|
||||
//old fear function, kept for ref.
|
||||
void Mob::CalculateFearPosition() {
|
||||
if(zone->zonemap == nullptr || fear_state == fearStateStuck) {
|
||||
return; //just stand there
|
||||
}
|
||||
|
||||
//This is the entire movement section, right here:
|
||||
if (cur_wp_x != GetX() && cur_wp_y != GetY()) {
|
||||
// not at waypoint yet, so keep moving
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//we have reached our waypoint, now what?
|
||||
//figure out a new waypoint to run at...
|
||||
|
||||
if(fear_state == fearStateRunningForever) {
|
||||
if(flee_mode) {
|
||||
//a fleeing mob may run away again
|
||||
VERTEX hit, fear_vector;
|
||||
if(FearTryStraight(GetHateTop(), FLEE_RUN_DURATION, true, hit, fear_vector))
|
||||
return; //we are running again
|
||||
//else, we need to find a grid, so act like we were on a hope run
|
||||
fear_state = fearStateRunning;
|
||||
}
|
||||
#ifndef FORCE_FEAR_TO_RUN
|
||||
else {
|
||||
//we were supposed to run forever, but we did not...
|
||||
//should re-fear ourself or something??
|
||||
fear_state = fearStateStuck;
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
//first see if we are on a path. if so our life is easy
|
||||
if(fear_state == fearStateGrid && fear_path_state) {
|
||||
//assume that we have zone->pathing since we got to this state.
|
||||
if(!zone->pathing->NextFearPath(fear_path_state)) {
|
||||
//this is bad, we were on a path and now its giving us
|
||||
//an error... we dont have a good way to deal with this
|
||||
fear_state = fearStateStuck;
|
||||
return;
|
||||
}
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: on path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z);
|
||||
#endif
|
||||
//we found a fear node... were on our way..
|
||||
cur_wp_x = fear_path_state->x;
|
||||
cur_wp_y = fear_path_state->y;
|
||||
cur_wp_z = fear_path_state->z;
|
||||
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
//the only valid state left is fearStateRunning, where we try to
|
||||
//find a grid once we reach our waypoint, which we have..
|
||||
if(fear_state != fearStateRunning) {
|
||||
//wtf... unknown state
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: Reached our fear waypoint, but we are in an unknown state %d... stopping.", fear_state);
|
||||
fear_state = fearStateStuck;
|
||||
return;
|
||||
}
|
||||
|
||||
//we wanted to try to find a waypoint now, so lets try..
|
||||
if(zone->pathing != nullptr) {
|
||||
fear_path_state = new MobFearState();
|
||||
|
||||
if(zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ())) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: ran to find path, moving from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z);
|
||||
#endif
|
||||
//we found a fear node... were on our way..
|
||||
cur_wp_x = fear_path_state->x;
|
||||
cur_wp_y = fear_path_state->y;
|
||||
cur_wp_z = fear_path_state->z;
|
||||
fear_state = fearStateGrid;
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
//if we get here... all valid methods have failed
|
||||
|
||||
#ifdef FORCE_FEAR_TO_RUN
|
||||
//ok, now we start making shit up
|
||||
|
||||
//for now, we will limit our bullshitting to ignoring LOS
|
||||
//when finding a pathing node, we SHOULD always get something..
|
||||
//do not force a path if we are fleeing
|
||||
if(!flee_mode && zone->pathing->FindNearestFear(fear_path_state, GetX(), GetY(), GetZ(), false)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: Bullshit Path from (%.2f, %.2f, %.2f) to path node (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), fear_path_state->x, fear_path_state->y, fear_path_state->z);
|
||||
#endif
|
||||
//we found a fear node... were on our way..
|
||||
cur_wp_x = fear_path_state->x;
|
||||
cur_wp_y = fear_path_state->y;
|
||||
cur_wp_z = fear_path_state->z;
|
||||
fear_state = fearStateGrid;
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetFearSpeed(), true);
|
||||
return;
|
||||
}
|
||||
#endif //FORCE_FEAR_TO_RUN
|
||||
|
||||
//we have failed to find a path once again, so we dont need this..
|
||||
safe_delete(fear_path_state);
|
||||
}
|
||||
|
||||
//if we get HERE... then NOTHING worked... just stick
|
||||
fear_state = fearStateStuck;
|
||||
|
||||
//end of function, everything else is #ifdef'd out
|
||||
//}
|
||||
|
||||
//I dont wanna get rid of this right now because it was a lot of hard
|
||||
//work to write... but it dosent work reliably, so oh well..
|
||||
#ifdef OLD_FEAR_PATHING
|
||||
/*
|
||||
The idea...
|
||||
|
||||
try to run along fear vector.
|
||||
If we can see along it, run
|
||||
otherwise, try to walk up a hill along the same vector
|
||||
then try to move along a wall along largest component of FV
|
||||
if cant move, change stae to stuck.
|
||||
|
||||
once we know a place to run, use the waypoint code to do it
|
||||
then if combat ends, we will reach the waypoint and
|
||||
|
||||
|
||||
*/
|
||||
/*
|
||||
//first try our original fear vector again...
|
||||
VERTEX start, end, hit, normalhit;
|
||||
start.x = GetX() - fear_vector.x * 0.4;
|
||||
start.y = GetY() - fear_vector.y * 0.4;
|
||||
start.z = GetZ() + 6.0; //raise up a little over small bumps
|
||||
|
||||
end.x = start.x + fear_vector.x * 10;
|
||||
end.y = start.y + fear_vector.y * 10;
|
||||
end.z = start.z;
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &normalhit, nullptr)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) normal run to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), end.x, end.y, end.z);
|
||||
#endif
|
||||
//we can run along this vector without hitting anything...
|
||||
cur_wp_x = end.x;
|
||||
cur_wp_y = end.y;
|
||||
cur_wp_z = end.z - 6.0;
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true);
|
||||
return;
|
||||
}
|
||||
//see if we can make ANY useful progress along that vector
|
||||
|
||||
//first, adjust normalhit to back up a little bit
|
||||
//so we dont run through the wall
|
||||
normalhit.x -= 0.4 * fear_vector.x;
|
||||
normalhit.y -= 0.4 * fear_vector.y;
|
||||
|
||||
float xd = normalhit.x - start.x;
|
||||
if(xd < 0)
|
||||
xd = 0 - xd;
|
||||
float yd = normalhit.y - start.y;
|
||||
if(yd < 0)
|
||||
yd = 0 - yd;
|
||||
|
||||
//this 2 is arbitrary
|
||||
if((xd+yd) > 2.0) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) small run to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), cur_wp_x, cur_wp_y, cur_wp_z);
|
||||
#endif
|
||||
cur_wp_x = normalhit.x;
|
||||
cur_wp_y = normalhit.y;
|
||||
cur_wp_z = GetZ();
|
||||
|
||||
//try and fix up the Z coord if possible
|
||||
//not sure if this is worth it, since it prolly isnt up much
|
||||
|
||||
NodeRef c = zone->zonemap->SeekNode(zone->zonemap->GetRoot(), end.x, end.y);
|
||||
if(c != NODE_NONE) {
|
||||
cur_wp_z = zone->zonemap->FindBestZ(c, end, &hit, nullptr);
|
||||
if(cur_wp_z < start.z)
|
||||
cur_wp_z = end.z; //revert on error
|
||||
}
|
||||
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) normal hit at (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), normalhit.x, normalhit.y, normalhit.z);
|
||||
#endif
|
||||
|
||||
//if we get here, we cannot run along our normal vector...
|
||||
//try up hill first
|
||||
|
||||
/*
|
||||
while this uphill stuff works great in outdoor zones,
|
||||
it totally breaks dungeons...
|
||||
|
||||
float speed = GetRunspeed();
|
||||
end.x = start.x + fear_vector.x * speed;
|
||||
end.y = start.y + fear_vector.y * speed;
|
||||
end.z = start.z + speed + speed;
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, nullptr)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) up hill run to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), end.x, end.y, end.z);
|
||||
#endif
|
||||
//we can run along this vector without hitting anything...
|
||||
cur_wp_x = end.x - 0.4 * fear_vector.x;
|
||||
cur_wp_y = end.y - 0.4 * fear_vector.y;
|
||||
cur_wp_z = end.z;
|
||||
|
||||
//try and fix up the Z coord if possible
|
||||
//not sure if this is worth it, since it prolly isnt up much
|
||||
|
||||
NodeRef c = zone->zonemap->SeekNode(zone->zonemap->GetRoot(), end.x, end.y);
|
||||
if(c != NODE_NONE) {
|
||||
cur_wp_z = zone->zonemap->FindBestZ(c, end, &hit, nullptr);
|
||||
if(cur_wp_z < start.z)
|
||||
cur_wp_z = end.z; //revert on error
|
||||
}
|
||||
|
||||
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true);
|
||||
return;
|
||||
}
|
||||
*/
|
||||
/*
|
||||
//cant run along our vector at all....
|
||||
//one last ditch effort... try to move to the side a little
|
||||
//along the minor component of the fear vector.
|
||||
//try it in one direction first...
|
||||
if(fear_vector.x < fear_vector.y) {
|
||||
end.x = start.x + fear_vector.x * 3;
|
||||
end.y = start.y;
|
||||
} else {
|
||||
end.x = start.x;
|
||||
end.y = start.y + fear_vector.y * 3;
|
||||
}
|
||||
end.z = start.z + 3; //a little lift as always
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, nullptr)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) strafe 1 to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), end.x, end.y, end.z);
|
||||
#endif
|
||||
//we can run along this vector without hitting anything...
|
||||
cur_wp_x = end.x - 0.4 * fear_vector.x;
|
||||
cur_wp_y = end.y - 0.4 * fear_vector.y;
|
||||
cur_wp_z = end.z - 3;
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
//now the other...
|
||||
if(fear_vector.x < fear_vector.y) {
|
||||
end.x = start.x + fear_vector.x * 3;
|
||||
end.y = start.y;
|
||||
} else {
|
||||
end.x = start.x;
|
||||
end.y = start.y + fear_vector.y * 3;
|
||||
}
|
||||
end.z = start.z + 3; //a little lift as always
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(start, end, 0.5, &hit, nullptr)) {
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) strafe 2 to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), end.x, end.y, end.z);
|
||||
#endif
|
||||
//we can run along this vector without hitting anything...
|
||||
cur_wp_x = end.x - 0.4 * fear_vector.x;
|
||||
cur_wp_y = end.y - 0.4 * fear_vector.y;
|
||||
cur_wp_z = end.z - 3;
|
||||
CalculateNewPosition2(cur_wp_x, cur_wp_y, cur_wp_z, GetRunspeed(), true);
|
||||
return;
|
||||
}
|
||||
|
||||
//if we get here... we have wasted enough CPU cycles
|
||||
//just call it quits on fear pathing...
|
||||
|
||||
//send them to normalhit and then stop
|
||||
cur_wp_x = normalhit.x;
|
||||
cur_wp_y = normalhit.y;
|
||||
cur_wp_z = GetZ();
|
||||
fear_state = fearStateRunningToStick;
|
||||
#ifdef FEAR_PATHING_DEBUG
|
||||
LogFile->write(EQEMuLog::Debug, "Fear Pathing: From (%.2f, %.2f, %.2f) final move to (%.2f, %.2f, %.2f)",
|
||||
GetX(), GetY(), GetZ(), normalhit.x, normalhit.y, normalhit.z);
|
||||
#endif
|
||||
#endif //OLD_FEAR_PATHING
|
||||
}*/
|
||||
|
||||
|
||||
|
||||
|
||||
+14
-16
@@ -225,7 +225,7 @@ bool Client::CanFish() {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(zone->zonemap!=nullptr && zone->watermap != nullptr && RuleB(Watermap, CheckForWaterWhenFishing)) {
|
||||
if(zone->zonemap != nullptr && zone->watermap != nullptr && RuleB(Watermap, CheckForWaterWhenFishing)) {
|
||||
float RodX, RodY, RodZ;
|
||||
// Tweak Rod and LineLength if required
|
||||
const float RodLength = RuleR(Watermap, FishingRodLength);
|
||||
@@ -240,24 +240,22 @@ bool Client::CanFish() {
|
||||
|
||||
// Do BestZ to find where the line hanging from the rod intersects the water (if it is water).
|
||||
// and go 1 unit into the water.
|
||||
VERTEX dest;
|
||||
Map::Vertex dest;
|
||||
dest.x = RodX;
|
||||
dest.y = RodY;
|
||||
dest.z = z_pos+10;
|
||||
NodeRef n = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), dest.x, dest.y);
|
||||
if(n != NODE_NONE) {
|
||||
RodZ = zone->zonemap->FindBestZ(n, dest, nullptr, nullptr) - 1;
|
||||
bool in_lava = zone->watermap->InLava(RodX, RodY, RodZ);
|
||||
bool in_water = zone->watermap->InWater(RodX, RodY, RodZ) || zone->watermap->InVWater(RodX, RodY, RodZ);
|
||||
//Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava);
|
||||
if (in_lava) {
|
||||
Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something?
|
||||
return false;
|
||||
}
|
||||
if((!in_water) || (z_pos-RodZ)>LineLength) { //Didn't hit the water OR the water is too far below us
|
||||
Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps?
|
||||
return false;
|
||||
}
|
||||
|
||||
RodZ = zone->zonemap->FindBestZ(dest, nullptr) - 1;
|
||||
bool in_lava = zone->watermap->InLava(RodX, RodY, RodZ);
|
||||
bool in_water = zone->watermap->InWater(RodX, RodY, RodZ) || zone->watermap->InVWater(RodX, RodY, RodZ);
|
||||
//Message(0, "Rod is at %4.3f, %4.3f, %4.3f, InWater says %d, InLava says %d", RodX, RodY, RodZ, in_water, in_lava);
|
||||
if (in_lava) {
|
||||
Message_StringID(MT_Skills, FISHING_LAVA); //Trying to catch a fire elemental or something?
|
||||
return false;
|
||||
}
|
||||
if((!in_water) || (z_pos-RodZ)>LineLength) { //Didn't hit the water OR the water is too far below us
|
||||
Message_StringID(MT_Skills, FISHING_LAND); //Trying to catch land sharks perhaps?
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
+27
-8
@@ -60,14 +60,16 @@ void HateList::Wipe()
|
||||
while(iterator != list.end())
|
||||
{
|
||||
Mob* m = (*iterator)->ent;
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
|
||||
//iterator
|
||||
if(m)
|
||||
{
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), m, "0", 0);
|
||||
|
||||
if(m->IsClient())
|
||||
m->CastToClient()->DecrementAggroCount();
|
||||
}
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
|
||||
if(m->IsClient())
|
||||
m->CastToClient()->DecrementAggroCount();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +205,9 @@ void HateList::Add(Mob *ent, int32 in_hate, int32 in_dam, bool bFrenzy, bool iAd
|
||||
|
||||
bool HateList::RemoveEnt(Mob *ent)
|
||||
{
|
||||
if (!ent)
|
||||
return false;
|
||||
|
||||
bool found = false;
|
||||
auto iterator = list.begin();
|
||||
|
||||
@@ -210,15 +215,18 @@ bool HateList::RemoveEnt(Mob *ent)
|
||||
{
|
||||
if((*iterator)->ent == ent)
|
||||
{
|
||||
if(ent)
|
||||
parse->EventNPC(EVENT_HATE_LIST, owner->CastToNPC(), ent, "0", 0);
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
found = true;
|
||||
|
||||
if(ent->IsClient())
|
||||
|
||||
if(ent && ent->IsClient())
|
||||
ent->CastToClient()->DecrementAggroCount();
|
||||
|
||||
}
|
||||
delete (*iterator);
|
||||
iterator = list.erase(iterator);
|
||||
|
||||
}
|
||||
else
|
||||
++iterator;
|
||||
}
|
||||
@@ -445,6 +453,17 @@ Mob *HateList::GetMostHate(){
|
||||
Mob *HateList::GetRandom()
|
||||
{
|
||||
int count = list.size();
|
||||
if(count == 0) //If we don't have any entries it'll crash getting a random 0, -1 position.
|
||||
return NULL;
|
||||
|
||||
if(count == 1) //No need to do all that extra work if we only have one hate entry
|
||||
{
|
||||
if(*list.begin()) // Just in case tHateEntry is invalidated somehow...
|
||||
return (*list.begin())->ent;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
auto iterator = list.begin();
|
||||
int random = MakeRandomInt(0, count - 1);
|
||||
for (int i = 0; i < random; i++)
|
||||
|
||||
@@ -372,8 +372,8 @@ int lua_get_spawn_condition(const char *zone, uint32 instance_id, int condition_
|
||||
return quest_manager.get_spawn_condition(zone, instance_id, condition_id);
|
||||
}
|
||||
|
||||
void lua_toggle_spawn_event(int event_id, bool enable, bool reset) {
|
||||
quest_manager.toggle_spawn_event(event_id, enable, reset);
|
||||
void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset) {
|
||||
quest_manager.toggle_spawn_event(event_id, enable, strict, reset);
|
||||
}
|
||||
|
||||
void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) {
|
||||
|
||||
+2
-2
@@ -408,7 +408,7 @@ void Lua_NPC::SetSpellFocusHeal(int focus) {
|
||||
}
|
||||
|
||||
float Lua_NPC::GetSlowMitigation() {
|
||||
Lua_Safe_Call_Real();
|
||||
Lua_Safe_Call_Int();
|
||||
return self->GetSlowMitigation();
|
||||
}
|
||||
|
||||
@@ -526,7 +526,7 @@ luabind::scope lua_register_npc() {
|
||||
.def("RemoveAISpell", (void(Lua_NPC::*)(int))&Lua_NPC::RemoveAISpell)
|
||||
.def("SetSpellFocusDMG", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusDMG)
|
||||
.def("SetSpellFocusHeal", (void(Lua_NPC::*)(int))&Lua_NPC::SetSpellFocusHeal)
|
||||
.def("GetSlowMitigation", (float(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
|
||||
.def("GetSlowMitigation", (int(Lua_NPC::*)(void))&Lua_NPC::GetSlowMitigation)
|
||||
.def("GetAttackSpeed", (float(Lua_NPC::*)(void))&Lua_NPC::GetAttackSpeed)
|
||||
.def("GetAccuracyRating", (int(Lua_NPC::*)(void))&Lua_NPC::GetAccuracyRating)
|
||||
.def("GetSpawnKillCount", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnKillCount)
|
||||
|
||||
+268
@@ -0,0 +1,268 @@
|
||||
#include "../common/debug.h"
|
||||
#include "../common/MiscFunctions.h"
|
||||
#include "map.h"
|
||||
#include "RaycastMesh.h"
|
||||
#include "zone.h"
|
||||
#include <stdint.h>
|
||||
#include <algorithm>
|
||||
#include <locale>
|
||||
#include <vector>
|
||||
|
||||
struct Map::impl
|
||||
{
|
||||
RaycastMesh *rm;
|
||||
};
|
||||
|
||||
Map::Map() {
|
||||
imp = nullptr;
|
||||
}
|
||||
|
||||
Map::~Map() {
|
||||
if(imp) {
|
||||
imp->rm->release();
|
||||
}
|
||||
}
|
||||
|
||||
float Map::FindBestZ(Vertex &start, Vertex *result) const {
|
||||
if (!imp)
|
||||
return false;
|
||||
|
||||
Vertex tmp;
|
||||
if(!result)
|
||||
result = &tmp;
|
||||
|
||||
start.z += RuleI(Map, FindBestZHeightAdjust);
|
||||
Vertex from(start.x, start.y, start.z);
|
||||
Vertex to(start.x, start.y, BEST_Z_INVALID);
|
||||
float hit_distance;
|
||||
bool hit = false;
|
||||
|
||||
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
|
||||
if(hit) {
|
||||
return result->z;
|
||||
}
|
||||
|
||||
// Find nearest Z above us
|
||||
|
||||
to.z = -BEST_Z_INVALID;
|
||||
hit = imp->rm->raycast((const RmReal*)&from, (const RmReal*)&to, (RmReal*)result, nullptr, &hit_distance);
|
||||
if (hit)
|
||||
{
|
||||
return result->z;
|
||||
}
|
||||
|
||||
return BEST_Z_INVALID;
|
||||
}
|
||||
|
||||
bool Map::LineIntersectsZone(Vertex start, Vertex end, float step, Vertex *result) const {
|
||||
if(!imp)
|
||||
return false;
|
||||
return imp->rm->raycast((const RmReal*)&start, (const RmReal*)&end, (RmReal*)result, nullptr, nullptr);
|
||||
}
|
||||
|
||||
bool Map::LineIntersectsZoneNoZLeaps(Vertex start, Vertex end, float step_mag, Vertex *result) const {
|
||||
if (!imp)
|
||||
return false;
|
||||
|
||||
float z = BEST_Z_INVALID;
|
||||
Vertex step;
|
||||
Vertex cur;
|
||||
cur.x = start.x;
|
||||
cur.y = start.y;
|
||||
cur.z = start.z;
|
||||
|
||||
step.x = end.x - start.x;
|
||||
step.y = end.y - start.y;
|
||||
step.z = end.z - start.z;
|
||||
float factor = step_mag / sqrt(step.x*step.x + step.y*step.y + step.z*step.z);
|
||||
|
||||
step.x *= factor;
|
||||
step.y *= factor;
|
||||
step.z *= factor;
|
||||
|
||||
int steps = 0;
|
||||
|
||||
if (step.x > 0 && step.x < 0.001f)
|
||||
step.x = 0.001f;
|
||||
if (step.y > 0 && step.y < 0.001f)
|
||||
step.y = 0.001f;
|
||||
if (step.z > 0 && step.z < 0.001f)
|
||||
step.z = 0.001f;
|
||||
if (step.x < 0 && step.x > -0.001f)
|
||||
step.x = -0.001f;
|
||||
if (step.y < 0 && step.y > -0.001f)
|
||||
step.y = -0.001f;
|
||||
if (step.z < 0 && step.z > -0.001f)
|
||||
step.z = -0.001f;
|
||||
|
||||
//while we are not past end
|
||||
//always do this once, even if start == end.
|
||||
while(cur.x != end.x || cur.y != end.y || cur.z != end.z)
|
||||
{
|
||||
steps++;
|
||||
Vertex me;
|
||||
me.x = cur.x;
|
||||
me.y = cur.y;
|
||||
me.z = cur.z;
|
||||
Vertex hit;
|
||||
|
||||
float best_z = FindBestZ(me, &hit);
|
||||
float diff = best_z - z;
|
||||
diff = diff < 0 ? -diff : diff;
|
||||
|
||||
if (z == -999999 || best_z == -999999 || diff < 12.0)
|
||||
z = best_z;
|
||||
else
|
||||
return true;
|
||||
|
||||
//look at current location
|
||||
if(LineIntersectsZone(start, end, step_mag, result))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
//move 1 step
|
||||
if (cur.x != end.x)
|
||||
cur.x += step.x;
|
||||
if (cur.y != end.y)
|
||||
cur.y += step.y;
|
||||
if (cur.z != end.z)
|
||||
cur.z += step.z;
|
||||
|
||||
//watch for end conditions
|
||||
if ( (cur.x > end.x && end.x >= start.x) || (cur.x < end.x && end.x <= start.x) || (step.x == 0) ) {
|
||||
cur.x = end.x;
|
||||
}
|
||||
if ( (cur.y > end.y && end.y >= start.y) || (cur.y < end.y && end.y <= start.y) || (step.y == 0) ) {
|
||||
cur.y = end.y;
|
||||
}
|
||||
if ( (cur.z > end.z && end.z >= start.z) || (cur.z < end.z && end.z < start.z) || (step.z == 0) ) {
|
||||
cur.z = end.z;
|
||||
}
|
||||
}
|
||||
|
||||
//walked entire line and didnt run into anything...
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::CheckLoS(Vertex myloc, Vertex oloc) const {
|
||||
if(!imp)
|
||||
return false;
|
||||
|
||||
return !imp->rm->raycast((const RmReal*)&myloc, (const RmReal*)&oloc, nullptr, nullptr, nullptr);
|
||||
}
|
||||
|
||||
Map *Map::LoadMapFile(std::string file) {
|
||||
std::string filename = MAP_DIR;
|
||||
filename += "/";
|
||||
std::transform(file.begin(), file.end(), file.begin(), ::tolower);
|
||||
filename += file;
|
||||
filename += ".map";
|
||||
|
||||
Map *m = new Map();
|
||||
if (m->Load(filename)) {
|
||||
return m;
|
||||
}
|
||||
|
||||
delete m;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Map::Load(std::string filename) {
|
||||
FILE *f = fopen(filename.c_str(), "rb");
|
||||
if(f) {
|
||||
uint32_t version;
|
||||
if(fread(&version, sizeof(version), 1, f) != 1) {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
if(version == 0x01000000) {
|
||||
bool v = LoadV1(f);
|
||||
fclose(f);
|
||||
return v;
|
||||
} else if(version == 0x02000000) {
|
||||
bool v = LoadV2(f);
|
||||
fclose(f);
|
||||
return v;
|
||||
} else {
|
||||
fclose(f);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Map::LoadV1(FILE *f) {
|
||||
uint32_t face_count;
|
||||
uint16_t node_count;
|
||||
uint32_t facelist_count;
|
||||
|
||||
if(fread(&face_count, sizeof(face_count), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(&node_count, sizeof(node_count), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(&facelist_count, sizeof(facelist_count), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<Vertex> verts;
|
||||
std::vector<uint32_t> indices;
|
||||
for(uint32_t i = 0; i < face_count; ++i) {
|
||||
Vertex a;
|
||||
Vertex b;
|
||||
Vertex c;
|
||||
float normals[4];
|
||||
if(fread(&a, sizeof(Vertex), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(&b, sizeof(Vertex), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(&c, sizeof(Vertex), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(fread(normals, sizeof(normals), 1, f) != 1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t sz = verts.size();
|
||||
verts.push_back(a);
|
||||
indices.push_back((uint32_t)sz);
|
||||
|
||||
verts.push_back(b);
|
||||
indices.push_back((uint32_t)sz + 1);
|
||||
|
||||
verts.push_back(c);
|
||||
indices.push_back((uint32_t)sz + 2);
|
||||
}
|
||||
|
||||
if(imp) {
|
||||
imp->rm->release();
|
||||
imp->rm = nullptr;
|
||||
} else {
|
||||
imp = new impl;
|
||||
}
|
||||
|
||||
imp->rm = createRaycastMesh((RmUint32)verts.size(), (const RmReal*)&verts[0], face_count, &indices[0]);
|
||||
|
||||
if(!imp->rm) {
|
||||
delete imp;
|
||||
imp = nullptr;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::LoadV2(FILE *f) {
|
||||
return false;
|
||||
}
|
||||
+42
-165
@@ -1,5 +1,7 @@
|
||||
/* EQEMu: Everquest Server Emulator
|
||||
Copyright (C) 2001-2002 EQEMu Development Team (http://eqemu.org)
|
||||
/*
|
||||
EQEMu: Everquest Server Emulator
|
||||
|
||||
Copyright (C) 2001-2014 EQEMu Development Team (http://eqemulator.net)
|
||||
|
||||
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
|
||||
@@ -9,182 +11,57 @@
|
||||
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.
|
||||
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
|
||||
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
*/
|
||||
#ifndef MAP_H
|
||||
#define MAP_H
|
||||
|
||||
#ifndef ZONE_MAP_H
|
||||
#define ZONE_MAP_H
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string>
|
||||
|
||||
//this is the current version number to expect from the map header
|
||||
#define MAP_VERSION 0x01000000
|
||||
#define BEST_Z_INVALID -99999
|
||||
|
||||
#define BEST_Z_INVALID -999999
|
||||
|
||||
#pragma pack(1)
|
||||
|
||||
typedef struct _vertex{
|
||||
// unsigned long order;
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
|
||||
_vertex()
|
||||
{
|
||||
x = y = z = 0.0f;
|
||||
};
|
||||
|
||||
_vertex(float ix, float iy, float iz)
|
||||
{
|
||||
x = ix;
|
||||
y = iy;
|
||||
z = iz;
|
||||
}
|
||||
bool operator==(const _vertex &v1) const
|
||||
{
|
||||
return((v1.x == x) && (v1.y == y) && (v1.z ==z));
|
||||
}
|
||||
|
||||
}VERTEX, *PVERTEX;
|
||||
|
||||
typedef struct _face{
|
||||
// unsigned long a, b, c; //vertexs
|
||||
VERTEX a;
|
||||
VERTEX b;
|
||||
VERTEX c;
|
||||
float nx, ny, nz, nd;
|
||||
}FACE, *PFACE;
|
||||
|
||||
typedef struct _mapHeader {
|
||||
uint32 version;
|
||||
uint32 face_count;
|
||||
uint16 node_count;
|
||||
uint32 facelist_count;
|
||||
} mapHeader;
|
||||
|
||||
/*
|
||||
This is used for the recursive node structure
|
||||
unsigned shorts are adequate because, worst case
|
||||
even in a zone that is 6000x6000 with a small node
|
||||
size of 30x30, there are only 40000 nodes.
|
||||
|
||||
quadrent definitions:
|
||||
quad 1 (nodes[0]):
|
||||
x>=0, y>=0
|
||||
quad 2 (nodes[1]):
|
||||
x<0, y>=0
|
||||
quad 3 (nodes[2]):
|
||||
x<0, y<0
|
||||
quad 4 (nodes[3]):
|
||||
x>=0, y<0
|
||||
|
||||
*/
|
||||
enum { //node flags
|
||||
nodeFinal = 0x01
|
||||
//7 more bits if theres something to use them for...
|
||||
};
|
||||
typedef struct _nodeHeader {
|
||||
//bounding box of this node
|
||||
//there is no reason that these could not be unsigned
|
||||
//shorts other than we have to compare them to floats
|
||||
//all over the place, so they stay floats for now.
|
||||
//changing it could save 8 bytes per node record (~320k for huge maps)
|
||||
float minx;
|
||||
float miny;
|
||||
float maxx;
|
||||
float maxy;
|
||||
|
||||
uint8 flags;
|
||||
union {
|
||||
uint16 nodes[4]; //index 0 means nullptr, not root
|
||||
struct {
|
||||
uint32 count;
|
||||
uint32 offset;
|
||||
} faces;
|
||||
};
|
||||
} nodeHeader, NODE, *PNODE;
|
||||
|
||||
#pragma pack()
|
||||
|
||||
//special value returned as 'not found'
|
||||
#define NODE_NONE 65534
|
||||
#define MAP_ROOT_NODE 0
|
||||
|
||||
typedef uint16 NodeRef;
|
||||
|
||||
/*typedef struct _node {
|
||||
nodeHeader head;
|
||||
unsigned int * pfaces;
|
||||
char mask;
|
||||
struct _node * node1, *node2, *node3, *node4;
|
||||
}NODE, *PNODE;*/
|
||||
|
||||
class Map {
|
||||
class Map
|
||||
{
|
||||
public:
|
||||
static Map* LoadMapfile(const char* in_zonename, const char *directory = nullptr);
|
||||
#pragma pack(1)
|
||||
struct Vertex
|
||||
{
|
||||
Vertex() : x(0.0f), y(0.0f), z(0.0f) { }
|
||||
Vertex(float _x, float _y, float _z) : x(_x), y(_y), z(_z) { }
|
||||
~Vertex() { }
|
||||
bool operator==(const Vertex &v) const
|
||||
{
|
||||
return((v.x == x) && (v.y == y) && (v.z == z));
|
||||
}
|
||||
|
||||
float x;
|
||||
float y;
|
||||
float z;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
Map();
|
||||
~Map();
|
||||
|
||||
bool loadMap(FILE *fp);
|
||||
|
||||
//the result is always final, except special NODE_NONE
|
||||
NodeRef SeekNode( NodeRef _node, float x, float y ) const;
|
||||
|
||||
//these are untested since rewrite:
|
||||
int *SeekFace( NodeRef _node, float x, float y );
|
||||
float GetFaceHeight( int _idx, float x, float y ) const;
|
||||
|
||||
bool LocWithinNode( NodeRef _node, float x, float y ) const;
|
||||
|
||||
//nodes to these functions must be final
|
||||
bool LineIntersectsNode( NodeRef _node, VERTEX start, VERTEX end, VERTEX *result, FACE **on = nullptr) const;
|
||||
bool LineIntersectsFace( PFACE cface, VERTEX start, VERTEX end, VERTEX *result) const;
|
||||
float FindBestZ( NodeRef _node, VERTEX start, VERTEX *result, FACE **on = nullptr) const;
|
||||
bool LineIntersectsZone(VERTEX start, VERTEX end, float step, VERTEX *result, FACE **on = nullptr) const;
|
||||
|
||||
// inline unsigned int GetVertexNumber( ) {return m_Vertex; }
|
||||
inline uint32 GetFacesNumber( ) const { return m_Faces; }
|
||||
// inline PVERTEX GetVertex( int _idx ) {return mFinalVertex + _idx; }
|
||||
inline PFACE GetFace( int _idx) {return mFinalFaces + _idx; }
|
||||
inline PFACE GetFaceFromlist( int _idx) {return &mFinalFaces[ mFaceLists[_idx] ]; }
|
||||
inline NodeRef GetRoot( ) const { return MAP_ROOT_NODE; }
|
||||
inline PNODE GetNode( NodeRef r ) { return( mNodes + r ); }
|
||||
|
||||
inline float GetMinX() const { return(_minx); }
|
||||
inline float GetMaxX() const { return(_maxx); }
|
||||
inline float GetMinY() const { return(_miny); }
|
||||
inline float GetMaxY() const { return(_maxy); }
|
||||
inline float GetMinZ() const { return(_minz); }
|
||||
inline float GetMaxZ() const { return(_maxz); }
|
||||
bool LineIntersectsZoneNoZLeaps(VERTEX start, VERTEX end, float step_mag, VERTEX *result, FACE **on);
|
||||
float FindClosestZ(VERTEX p ) const;
|
||||
|
||||
|
||||
float FindBestZ(Vertex &start, Vertex *result) const;
|
||||
bool LineIntersectsZone(Vertex start, Vertex end, float step, Vertex *result) const;
|
||||
bool LineIntersectsZoneNoZLeaps(Vertex start, Vertex end, float step_mag, Vertex *result) const;
|
||||
bool CheckLoS(Vertex myloc, Vertex oloc) const;
|
||||
bool Load(std::string filename);
|
||||
static Map *LoadMapFile(std::string file);
|
||||
private:
|
||||
// unsigned long m_Vertex;
|
||||
uint32 m_Faces;
|
||||
uint32 m_Nodes;
|
||||
uint32 m_FaceLists;
|
||||
// PVERTEX mFinalVertex;
|
||||
PFACE mFinalFaces;
|
||||
PNODE mNodes;
|
||||
uint32 *mFaceLists;
|
||||
|
||||
|
||||
int mCandFaces[100];
|
||||
|
||||
float _minz, _maxz;
|
||||
float _minx, _miny, _maxx, _maxy;
|
||||
|
||||
static void Normalize(VERTEX *p);
|
||||
|
||||
// void RecLoadNode( PNODE _node, FILE *l_f );
|
||||
// void RecFreeNode( PNODE _node );
|
||||
bool LoadV1(FILE *f);
|
||||
bool LoadV2(FILE *f);
|
||||
|
||||
struct impl;
|
||||
impl *imp;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
+44
-65
@@ -891,7 +891,8 @@ void Mob::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
|
||||
ns->spawn.invis = (invisible || hidden) ? 1 : 0; // TODO: load this before spawning players
|
||||
ns->spawn.NPC = IsClient() ? 0 : 1;
|
||||
ns->spawn.IsMercenary = IsMerc() ? 1 : 0;
|
||||
ns->spawn.IsMercenary = (IsMerc() || no_target_hotkey) ? 1 : 0;
|
||||
|
||||
ns->spawn.petOwnerId = ownerid;
|
||||
|
||||
ns->spawn.haircolor = haircolor;
|
||||
@@ -1626,7 +1627,7 @@ void Mob::SendAppearanceEffect(uint32 parm1, uint32 parm2, uint32 parm3, uint32
|
||||
la->parm4 = parm4;
|
||||
la->parm5 = parm5;
|
||||
// Note that setting the b values to 0 will disable the related effect from the corresponding parameter.
|
||||
// Setting the a value appears to have no affect at all.
|
||||
// Setting the a value appears to have no affect at all.s
|
||||
la->value1a = 1;
|
||||
la->value1b = 1;
|
||||
la->value2a = 1;
|
||||
@@ -2948,22 +2949,17 @@ void Mob::SetTarget(Mob* mob) {
|
||||
float Mob::FindGroundZ(float new_x, float new_y, float z_offset)
|
||||
{
|
||||
float ret = -999999;
|
||||
if (zone->zonemap != 0)
|
||||
if (zone->zonemap != nullptr)
|
||||
{
|
||||
NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), new_x, new_y );
|
||||
if (pnode != NODE_NONE)
|
||||
Map::Vertex me;
|
||||
me.x = new_x;
|
||||
me.y = new_y;
|
||||
me.z = z_pos+z_offset;
|
||||
Map::Vertex hit;
|
||||
float best_z = zone->zonemap->FindBestZ(me, &hit);
|
||||
if (best_z != -999999)
|
||||
{
|
||||
VERTEX me;
|
||||
me.x = new_x;
|
||||
me.y = new_y;
|
||||
me.z = z_pos+z_offset;
|
||||
VERTEX hit;
|
||||
FACE *onhit;
|
||||
float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, &onhit);
|
||||
if (best_z != -999999)
|
||||
{
|
||||
ret = best_z;
|
||||
}
|
||||
ret = best_z;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -2975,20 +2971,15 @@ float Mob::GetGroundZ(float new_x, float new_y, float z_offset)
|
||||
float ret = -999999;
|
||||
if (zone->zonemap != 0)
|
||||
{
|
||||
NodeRef pnode = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), new_x, new_y );
|
||||
if (pnode != NODE_NONE)
|
||||
Map::Vertex me;
|
||||
me.x = new_x;
|
||||
me.y = new_y;
|
||||
me.z = z_pos+z_offset;
|
||||
Map::Vertex hit;
|
||||
float best_z = zone->zonemap->FindBestZ(me, &hit);
|
||||
if (best_z != -999999)
|
||||
{
|
||||
VERTEX me;
|
||||
me.x = new_x;
|
||||
me.y = new_y;
|
||||
me.z = z_pos+z_offset;
|
||||
VERTEX hit;
|
||||
FACE *onhit;
|
||||
float best_z = zone->zonemap->FindBestZ(pnode, me, &hit, &onhit);
|
||||
if (best_z != -999999)
|
||||
{
|
||||
ret = best_z;
|
||||
}
|
||||
ret = best_z;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
@@ -4149,10 +4140,10 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
|
||||
for (int i = 0; i < EFFECT_COUNT; i++) {
|
||||
if (spells[spell_id].effectid[i] == SE_SpellOnKill2)
|
||||
{
|
||||
if (spells[spell_id].max[i] <= level)
|
||||
if (IsValidSpell(spells[spell_id].base2[i]) && spells[spell_id].max[i] <= level)
|
||||
{
|
||||
if(MakeRandomInt(0,99) < spells[spell_id].base[i])
|
||||
SpellFinished(spells[spell_id].base2[i], this);
|
||||
SpellFinished(spells[spell_id].base2[i], this, 10, 0, -1, spells[spells[spell_id].base2[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4165,19 +4156,19 @@ void Mob::TrySpellOnKill(uint8 level, uint16 spell_id)
|
||||
// Allow to check AA, items and buffs in all cases. Base2 = Spell to fire | Base1 = % chance | Base3 = min level
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER*3; i+=3) {
|
||||
|
||||
if(aabonuses.SpellOnKill[i] && (level >= aabonuses.SpellOnKill[i + 2])) {
|
||||
if(aabonuses.SpellOnKill[i] && IsValidSpell(aabonuses.SpellOnKill[i]) && (level >= aabonuses.SpellOnKill[i + 2])) {
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(aabonuses.SpellOnKill[i + 1]))
|
||||
SpellFinished(aabonuses.SpellOnKill[i], this);
|
||||
SpellFinished(aabonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
if(itembonuses.SpellOnKill[i] && (level >= itembonuses.SpellOnKill[i + 2])){
|
||||
if(itembonuses.SpellOnKill[i] && IsValidSpell(itembonuses.SpellOnKill[i]) && (level >= itembonuses.SpellOnKill[i + 2])){
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(itembonuses.SpellOnKill[i + 1]))
|
||||
SpellFinished(itembonuses.SpellOnKill[i], this);
|
||||
SpellFinished(itembonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
if(spellbonuses.SpellOnKill[i] && (level >= spellbonuses.SpellOnKill[i + 2])) {
|
||||
if(spellbonuses.SpellOnKill[i] && IsValidSpell(spellbonuses.SpellOnKill[i]) && (level >= spellbonuses.SpellOnKill[i + 2])) {
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(spellbonuses.SpellOnKill[i + 1]))
|
||||
SpellFinished(spellbonuses.SpellOnKill[i], this);
|
||||
SpellFinished(spellbonuses.SpellOnKill[i], this, 10, 0, -1, spells[aabonuses.SpellOnKill[i]].ResistDiff);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4192,21 +4183,21 @@ bool Mob::TrySpellOnDeath()
|
||||
return false;
|
||||
|
||||
for(int i = 0; i < MAX_SPELL_TRIGGER*2; i+=2) {
|
||||
if(IsClient() && aabonuses.SpellOnDeath[i]) {
|
||||
if(IsClient() && aabonuses.SpellOnDeath[i] && IsValidSpell(aabonuses.SpellOnDeath[i])) {
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(aabonuses.SpellOnDeath[i + 1])) {
|
||||
SpellFinished(aabonuses.SpellOnDeath[i], this);
|
||||
SpellFinished(aabonuses.SpellOnDeath[i], this, 10, 0, -1, spells[aabonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if(itembonuses.SpellOnDeath[i]) {
|
||||
if(itembonuses.SpellOnDeath[i] && IsValidSpell(itembonuses.SpellOnDeath[i])) {
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(itembonuses.SpellOnDeath[i + 1])) {
|
||||
SpellFinished(itembonuses.SpellOnDeath[i], this);
|
||||
SpellFinished(itembonuses.SpellOnDeath[i], this, 10, 0, -1, spells[itembonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
|
||||
if(spellbonuses.SpellOnDeath[i]) {
|
||||
if(spellbonuses.SpellOnDeath[i] && IsValidSpell(spellbonuses.SpellOnDeath[i])) {
|
||||
if(MakeRandomInt(0, 99) < static_cast<int>(spellbonuses.SpellOnDeath[i + 1])) {
|
||||
SpellFinished(spellbonuses.SpellOnDeath[i], this);
|
||||
SpellFinished(spellbonuses.SpellOnDeath[i], this, 10, 0, -1, spells[spellbonuses.SpellOnDeath[i]].ResistDiff);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4650,33 +4641,21 @@ void Mob::CastOnNumHitFade(uint32 spell_id)
|
||||
}
|
||||
}
|
||||
|
||||
int Mob::SlowMitigation(bool slow_msg, Mob *caster, int slow_value)
|
||||
void Mob::SlowMitigation(Mob* caster)
|
||||
{
|
||||
float int_slow_mitigation = slow_mitigation * 100.0f;
|
||||
|
||||
if (int_slow_mitigation > 100.0f)
|
||||
return 0;
|
||||
|
||||
if (slow_msg)
|
||||
if (GetSlowMitigation() && caster && caster->IsClient())
|
||||
{
|
||||
if (caster && caster->IsClient())
|
||||
{
|
||||
if ((int_slow_mitigation > 0.0f) && (int_slow_mitigation < 26.0f))
|
||||
caster->Message(262, "Your spell was mostly successful");
|
||||
if ((GetSlowMitigation() > 0) && (GetSlowMitigation() < 26))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_MOSTLY_SUCCESSFUL);
|
||||
|
||||
else if ((int_slow_mitigation >= 26.0f) && (int_slow_mitigation < 74.0f))
|
||||
caster->Message(262, "Your spell was partially successful");
|
||||
else if ((GetSlowMitigation() >= 26) && (GetSlowMitigation() < 74))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_PARTIALLY_SUCCESSFUL);
|
||||
|
||||
else if ((int_slow_mitigation >= 74.0f) && (int_slow_mitigation < 101.0f))
|
||||
caster->Message(262, "Your spell was slightly successful");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
else if ((GetSlowMitigation() >= 74) && (GetSlowMitigation() < 101))
|
||||
caster->Message_StringID(MT_SpellFailure, SLOW_SLIGHTLY_SUCCESSFUL);
|
||||
|
||||
else
|
||||
{
|
||||
slow_value -= (slow_value * static_cast<int>(int_slow_mitigation) / 100);
|
||||
return slow_value;
|
||||
else if (GetSlowMitigation() > 100)
|
||||
caster->Message_StringID(MT_SpellFailure, SPELL_OPPOSITE_EFFECT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+13
-10
@@ -161,7 +161,7 @@ public:
|
||||
virtual void WearChange(uint8 material_slot, uint16 texture, uint32 color);
|
||||
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);
|
||||
float angle = 0, float tilt = 0, float arc = 0, const char *IDFile = nullptr);
|
||||
void ChangeSize(float in_size, bool bNoRestriction = false);
|
||||
inline uint8 SeeInvisible() const { return see_invis; }
|
||||
inline bool SeeInvisibleUndead() const { return see_invis_undead; }
|
||||
@@ -170,7 +170,7 @@ public:
|
||||
bool IsInvisible(Mob* other = 0) const;
|
||||
void SetInvisible(uint8 state);
|
||||
bool AttackAnimation(SkillUseTypes &skillinuse, int Hand, const ItemInst* weapon);
|
||||
|
||||
|
||||
//Song
|
||||
bool UseBardSpellLogic(uint16 spell_id = 0xffff, int slot = -1);
|
||||
bool ApplyNextBardPulse(uint16 spell_id, Mob *spell_target, uint16 slot);
|
||||
@@ -182,7 +182,8 @@ public:
|
||||
bool IsBeneficialAllowed(Mob *target);
|
||||
virtual int GetCasterLevel(uint16 spell_id);
|
||||
void ApplySpellsBonuses(uint16 spell_id, uint8 casterlevel, StatBonuses* newbon, uint16 casterID = 0,
|
||||
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1);
|
||||
bool item_bonus = false, uint32 ticsremaining = 0, int buffslot = -1,
|
||||
bool IsAISpellEffect = false, uint16 effect_id = 0, int32 se_base = 0, int32 se_limit = 0, int32 se_max = 0);
|
||||
void NegateSpellsBonuses(uint16 spell_id);
|
||||
virtual float GetActSpellRange(uint16 spell_id, float range, bool IsBard = false) { return range;}
|
||||
virtual int32 GetActSpellDamage(uint16 spell_id, int32 value, Mob* target = nullptr) { return value; }
|
||||
@@ -414,7 +415,7 @@ public:
|
||||
void MakeSpawnUpdate(PlayerPositionUpdateServer_Struct* spu);
|
||||
void SendPosition();
|
||||
void SetFlyMode(uint8 flymode);
|
||||
inline void Teleport(VERTEX NewPosition) { x_pos = NewPosition.x; y_pos = NewPosition.y;
|
||||
inline void Teleport(Map::Vertex NewPosition) { x_pos = NewPosition.x; y_pos = NewPosition.y;
|
||||
z_pos = NewPosition.z; };
|
||||
|
||||
//AI
|
||||
@@ -444,7 +445,6 @@ public:
|
||||
void ClearFeignMemory();
|
||||
void PrintHateListToClient(Client *who) { hate_list.PrintToClient(who); }
|
||||
std::list<tHateEntry*>& GetHateList() { return hate_list.GetHateList(); }
|
||||
bool CheckLos(Mob* other);
|
||||
bool CheckLosFN(Mob* other);
|
||||
bool CheckLosFN(float posX, float posY, float posZ, float mobSize);
|
||||
inline void SetChanged() { pLastChange = Timer::GetCurrentTime(); }
|
||||
@@ -500,6 +500,7 @@ public:
|
||||
bool AddProcToWeapon(uint16 spell_id, bool bPerma = false, uint16 iChance = 3, uint16 base_spell_id = SPELL_UNKNOWN);
|
||||
bool RemoveProcFromWeapon(uint16 spell_id, bool bAll = false);
|
||||
bool HasProcs() const;
|
||||
bool IsCombatProc(uint16 spell_id);
|
||||
|
||||
//Logging
|
||||
bool IsLoggingEnabled() const { return(logging_enabled); }
|
||||
@@ -580,7 +581,7 @@ public:
|
||||
void CastOnCurer(uint32 spell_id);
|
||||
void CastOnCure(uint32 spell_id);
|
||||
void CastOnNumHitFade(uint32 spell_id);
|
||||
int SlowMitigation(bool slow_msg=false, Mob *caster = nullptr,int slow_value = 0);
|
||||
void SlowMitigation(Mob* caster);
|
||||
int16 GetCritDmgMob(uint16 skill);
|
||||
int16 GetMeleeDamageMod_SE(uint16 skill);
|
||||
int16 GetMeleeMinDamageMod_SE(uint16 skill);
|
||||
@@ -598,6 +599,7 @@ public:
|
||||
bool PassCastRestriction(bool UseCastRestriction = true, int16 value = 0, bool IsDamage = true);
|
||||
bool ImprovedTaunt();
|
||||
bool TryRootFadeByDamage(int buffslot, Mob* attacker);
|
||||
int16 GetSlowMitigation() const {return slow_mitigation;}
|
||||
|
||||
void ModSkillDmgTaken(SkillUseTypes skill_num, int value);
|
||||
int16 GetModSkillDmgTaken(const SkillUseTypes skill_num);
|
||||
@@ -945,6 +947,7 @@ protected:
|
||||
int16 petpower;
|
||||
uint32 follow;
|
||||
uint32 follow_dist;
|
||||
bool no_target_hotkey;
|
||||
|
||||
uint8 gender;
|
||||
uint16 race;
|
||||
@@ -988,7 +991,7 @@ protected:
|
||||
bool HasDied();
|
||||
void CalculateNewFearpoint();
|
||||
float FindGroundZ(float new_x, float new_y, float z_offset=0.0);
|
||||
VERTEX UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
Map::Vertex UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChange, bool &NodeReached);
|
||||
void PrintRoute();
|
||||
|
||||
virtual float GetSympatheticProcChances(float &ProcBonus, float &ProcChance, int32 cast_time, int16 ProcRateMod);
|
||||
@@ -1022,7 +1025,7 @@ protected:
|
||||
Timer attack_dw_timer;
|
||||
Timer ranged_timer;
|
||||
float attack_speed; //% increase/decrease in attack speed (not haste)
|
||||
float slow_mitigation; // Allows for a slow mitigation based on a % in decimal form. IE, 1 = 100% mitigation, .5 is 50%
|
||||
float slow_mitigation; // Allows for a slow mitigation (100 = 100%, 50% = 50%)
|
||||
Timer tic_timer;
|
||||
Timer mana_timer;
|
||||
|
||||
@@ -1157,8 +1160,8 @@ protected:
|
||||
|
||||
// Pathing
|
||||
//
|
||||
VERTEX PathingDestination;
|
||||
VERTEX PathingLastPosition;
|
||||
Map::Vertex PathingDestination;
|
||||
Map::Vertex PathingLastPosition;
|
||||
int PathingLoopCount;
|
||||
int PathingLastNodeVisited;
|
||||
std::list<int> Route;
|
||||
|
||||
+26
-1
@@ -224,11 +224,14 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
|
||||
p_depop = false;
|
||||
loottable_id = d->loottable_id;
|
||||
|
||||
no_target_hotkey = d->no_target_hotkey;
|
||||
|
||||
primary_faction = 0;
|
||||
SetNPCFactionID(d->npc_faction_id);
|
||||
|
||||
npc_spells_id = 0;
|
||||
HasAISpell = false;
|
||||
HasAISpellEffects = false;
|
||||
|
||||
if(GetClass() == MERCERNARY_MASTER && RuleB(Mercs, AllowMercs))
|
||||
{
|
||||
@@ -1719,6 +1722,22 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
|
||||
void NPC::FillSpawnStruct(NewSpawn_Struct* ns, Mob* ForWho)
|
||||
{
|
||||
Mob::FillSpawnStruct(ns, ForWho);
|
||||
|
||||
//Basic settings to make sure swarm pets work properly.
|
||||
if (GetSwarmOwner()) {
|
||||
Client *c = entity_list.GetClientByID(GetSwarmOwner());
|
||||
if(c) {
|
||||
SetAllowBeneficial(1); //Allow client cast swarm pets to be heal/buffed.
|
||||
//This is a hack to allow CLIENT swarm pets NOT to be targeted with F8. Warning: Will turn name 'Yellow'!
|
||||
if (RuleB(Pets, SwarmPetNotTargetableWithHotKey))
|
||||
ns->spawn.IsMercenary = 1;
|
||||
}
|
||||
//NPC cast swarm pets should still be targetable with F8.
|
||||
else
|
||||
ns->spawn.IsMercenary = 0;
|
||||
}
|
||||
|
||||
//Not recommended if using above (However, this will work better on older clients).
|
||||
if (RuleB(Pets, UnTargetableSwarmPet)) {
|
||||
if(GetOwnerID() || GetSwarmOwner()) {
|
||||
ns->spawn.is_pet = 1;
|
||||
@@ -1867,6 +1886,12 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "PhR")
|
||||
{
|
||||
PhR = atoi(val.c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if(id == "runspeed")
|
||||
{
|
||||
runspeed = (float)atof(val.c_str());
|
||||
@@ -1980,7 +2005,7 @@ void NPC::ModifyNPCStat(const char *identifier, const char *newValue)
|
||||
|
||||
if(id == "slow_mitigation")
|
||||
{
|
||||
slow_mitigation = atof(val.c_str());
|
||||
slow_mitigation = atoi(val.c_str());
|
||||
return;
|
||||
}
|
||||
if(id == "loottable_id")
|
||||
|
||||
+19
-3
@@ -66,6 +66,13 @@ struct AISpells_Struct {
|
||||
int16 resist_adjust;
|
||||
};
|
||||
|
||||
struct AISpellsEffects_Struct {
|
||||
uint16 spelleffectid;
|
||||
int32 base;
|
||||
int32 limit;
|
||||
int32 max;
|
||||
};
|
||||
|
||||
class AA_SwarmPetInfo;
|
||||
|
||||
class NPC : public Mob
|
||||
@@ -96,8 +103,11 @@ public:
|
||||
virtual void AI_Stop();
|
||||
void AI_DoMovement();
|
||||
bool AI_AddNPCSpells(uint32 iDBSpellsID);
|
||||
bool AI_AddNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||
virtual bool AI_EngagedCastCheck();
|
||||
bool AI_HasSpells() { return HasAISpell; }
|
||||
bool AI_HasSpellsEffects() { return HasAISpellEffects; }
|
||||
void ApplyAISpellEffects(StatBonuses* newbon);
|
||||
|
||||
virtual bool AI_PursueCastCheck();
|
||||
virtual bool AI_IdleCastCheck();
|
||||
@@ -233,7 +243,7 @@ public:
|
||||
|
||||
uint32 GetMaxDMG() const {return max_dmg;}
|
||||
uint32 GetMinDMG() const {return min_dmg;}
|
||||
float GetSlowMitigation() const {return slow_mitigation;}
|
||||
int16 GetSlowMitigation() const {return slow_mitigation;}
|
||||
float GetAttackSpeed() const {return attack_speed;}
|
||||
bool IsAnimal() const { return(bodytype == BT_Animal); }
|
||||
uint16 GetPetSpellID() const {return pet_spell_id;}
|
||||
@@ -245,6 +255,7 @@ public:
|
||||
void AddLootDrop(const Item_Struct*dbitem, ItemList* itemlistconst, int16 charges, uint8 minlevel, uint8 maxlevel, bool equipit, bool wearchange = false);
|
||||
virtual void DoClassAttacks(Mob *target);
|
||||
void CheckSignal();
|
||||
inline bool IsTargetableWithHotkey() const { return no_target_hotkey; }
|
||||
|
||||
//waypoint crap
|
||||
int GetMaxWp() const { return max_wp; }
|
||||
@@ -288,6 +299,7 @@ public:
|
||||
|
||||
inline void GiveNPCTypeData(NPCType *ours) { NPCTypedata_ours = ours; }
|
||||
inline const uint32 GetNPCSpellsID() const { return npc_spells_id; }
|
||||
inline const uint32 GetNPCSpellsEffectsID() const { return npc_spells_effects_id; }
|
||||
|
||||
ItemList itemlist; //kathgar - why is this public? Doing other things or I would check the code
|
||||
|
||||
@@ -338,6 +350,7 @@ public:
|
||||
|
||||
uint32 GetAdventureTemplate() const { return adventure_template_id; }
|
||||
void AddSpellToNPCList(int16 iPriority, int16 iSpellID, uint16 iType, int16 iManaCost, int32 iRecastDelay, int16 iResistAdjust);
|
||||
void AddSpellEffectToNPCList(uint16 iSpellEffectID, int32 base, int32 limit, int32 max);
|
||||
void RemoveSpellFromNPCList(int16 spell_id);
|
||||
Timer *GetRefaceTimer() const { return reface_timer; }
|
||||
const uint32 GetAltCurrencyType() const { return NPCTypedata->alt_currency_type; }
|
||||
@@ -399,8 +412,11 @@ protected:
|
||||
bool HasAISpell;
|
||||
virtual bool AICastSpell(Mob* tar, uint8 iChance, uint16 iSpellTypes);
|
||||
virtual bool AIDoSpellCast(uint8 i, Mob* tar, int32 mana_cost, uint32* oDontDoAgainBefore = 0);
|
||||
|
||||
|
||||
|
||||
uint32 npc_spells_effects_id;
|
||||
std::vector<AISpellsEffects_Struct> AIspellsEffects;
|
||||
bool HasAISpellEffects;
|
||||
|
||||
uint32 max_dmg;
|
||||
uint32 min_dmg;
|
||||
int32 accuracy_rating;
|
||||
|
||||
+74
-79
@@ -22,7 +22,7 @@
|
||||
|
||||
extern Zone *zone;
|
||||
|
||||
float VertexDistance(VERTEX a, VERTEX b)
|
||||
float VertexDistance(Map::Vertex a, Map::Vertex b)
|
||||
{
|
||||
float xdist = a.x - b.x;
|
||||
float ydist = a.y - b.y;
|
||||
@@ -30,7 +30,7 @@ float VertexDistance(VERTEX a, VERTEX b)
|
||||
return sqrtf(xdist * xdist + ydist * ydist + zdist * zdist);
|
||||
}
|
||||
|
||||
float VertexDistanceNoRoot(VERTEX a, VERTEX b)
|
||||
float VertexDistanceNoRoot(Map::Vertex a, Map::Vertex b)
|
||||
{
|
||||
float xdist = a.x - b.x;
|
||||
float ydist = a.y - b.y;
|
||||
@@ -188,9 +188,9 @@ void PathManager::PrintPathing()
|
||||
}
|
||||
}
|
||||
|
||||
VERTEX PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ)
|
||||
Map::Vertex PathManager::GetPathNodeCoordinates(int NodeNumber, bool BestZ)
|
||||
{
|
||||
VERTEX Result;
|
||||
Map::Vertex Result;
|
||||
|
||||
if(NodeNumber < Head.PathNodeCount)
|
||||
{
|
||||
@@ -336,12 +336,12 @@ std::list<int> PathManager::FindRoute(int startID, int endID)
|
||||
|
||||
}
|
||||
|
||||
bool CheckLOSBetweenPoints(VERTEX start, VERTEX end) {
|
||||
bool CheckLOSBetweenPoints(Map::Vertex start, Map::Vertex end) {
|
||||
|
||||
VERTEX hit;
|
||||
FACE *face;
|
||||
Map::Vertex hit;
|
||||
|
||||
if((zone->zonemap) && (zone->zonemap->LineIntersectsZone(start, end, 1, &hit, &face))) return false;
|
||||
if((zone->zonemap) && (zone->zonemap->LineIntersectsZone(start, end, 1, &hit)))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -351,7 +351,7 @@ bool SortPathNodesByDistance(PathNodeSortStruct n1, PathNodeSortStruct n2)
|
||||
return n1.Distance < n2.Distance;
|
||||
}
|
||||
|
||||
std::list<int> PathManager::FindRoute(VERTEX Start, VERTEX End)
|
||||
std::list<int> PathManager::FindRoute(Map::Vertex Start, Map::Vertex End)
|
||||
{
|
||||
_log(PATHING__DEBUG, "FindRoute(%8.3f, %8.3f, %8.3f, %8.3f, %8.3f, %8.3f)", Start.x, Start.y, Start.z, End.x, End.y, End.z);
|
||||
|
||||
@@ -389,7 +389,7 @@ std::list<int> PathManager::FindRoute(VERTEX Start, VERTEX End)
|
||||
{
|
||||
_log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id);
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Iterator).id].v, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
|
||||
{
|
||||
ClosestPathNodeToStart = (*Iterator).id;
|
||||
break;
|
||||
@@ -430,7 +430,7 @@ std::list<int> PathManager::FindRoute(VERTEX Start, VERTEX End)
|
||||
End.x, End.y, End.z,
|
||||
PathNodes[(*Iterator).id].v.x, PathNodes[(*Iterator).id].v.y, PathNodes[(*Iterator).id].v.z);
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Iterator).id].v, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
|
||||
{
|
||||
ClosestPathNodeToEnd = (*Iterator).id;
|
||||
break;
|
||||
@@ -470,7 +470,7 @@ std::list<int> PathManager::FindRoute(VERTEX Start, VERTEX End)
|
||||
if((*Second) < 0)
|
||||
break;
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Second)].v, 1.0f, nullptr, nullptr)
|
||||
if(!zone->zonemap->LineIntersectsZone(Start, PathNodes[(*Second)].v, 1.0f, nullptr)
|
||||
&& zone->pathing->NoHazards(Start, PathNodes[(*Second)].v))
|
||||
{
|
||||
noderoute.erase(First);
|
||||
@@ -503,7 +503,7 @@ std::list<int> PathManager::FindRoute(VERTEX Start, VERTEX End)
|
||||
if((*Second) < 0)
|
||||
break;
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Second)].v, 1.0f, nullptr, nullptr)
|
||||
if(!zone->zonemap->LineIntersectsZone(End, PathNodes[(*Second)].v, 1.0f, nullptr)
|
||||
&& zone->pathing->NoHazards(End, PathNodes[(*Second)].v))
|
||||
{
|
||||
noderoute.erase(First);
|
||||
@@ -653,19 +653,19 @@ void PathManager::SimpleMeshTest()
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached)
|
||||
Map::Vertex Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &WaypointChanged, bool &NodeReached)
|
||||
{
|
||||
WaypointChanged = false;
|
||||
|
||||
NodeReached = false;
|
||||
|
||||
VERTEX NodeLoc;
|
||||
Map::Vertex NodeLoc;
|
||||
|
||||
VERTEX From(GetX(), GetY(), GetZ());
|
||||
Map::Vertex From(GetX(), GetY(), GetZ());
|
||||
|
||||
VERTEX HeadPosition(From.x, From.y, From.z + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION);
|
||||
Map::Vertex HeadPosition(From.x, From.y, From.z + (GetSize() < 6.0 ? 6 : GetSize()) * HEAD_POSITION);
|
||||
|
||||
VERTEX To(ToX, ToY, ToZ);
|
||||
Map::Vertex To(ToX, ToY, ToZ);
|
||||
|
||||
bool SameDestination = (To == PathingDestination);
|
||||
|
||||
@@ -762,7 +762,7 @@ VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Waypo
|
||||
if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckShort))
|
||||
&& (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold)))
|
||||
{
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr))
|
||||
PathingLOSState = HaveLOS;
|
||||
else
|
||||
PathingLOSState = NoLOS;
|
||||
@@ -855,7 +855,7 @@ VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Waypo
|
||||
if((Distance <= RuleR(Pathing, MinDistanceForLOSCheckShort))
|
||||
&& (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold)))
|
||||
{
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr))
|
||||
PathingLOSState = HaveLOS;
|
||||
else
|
||||
PathingLOSState = NoLOS;
|
||||
@@ -894,7 +894,7 @@ VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Waypo
|
||||
&& (ABS(From.z - To.z) <= RuleR(Pathing, ZDiffThreshold)))
|
||||
{
|
||||
mlog(PATHING__DEBUG, " Checking for short LOS at distance %8.3f.", Distance);
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr))
|
||||
PathingLOSState = HaveLOS;
|
||||
else
|
||||
PathingLOSState = NoLOS;
|
||||
@@ -1047,7 +1047,7 @@ VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Waypo
|
||||
{
|
||||
mlog(PATHING__DEBUG, " Checking for long LOS at distance %8.3f.", Distance);
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(HeadPosition, To, 1.0f, nullptr))
|
||||
PathingLOSState = HaveLOS;
|
||||
else
|
||||
PathingLOSState = NoLOS;
|
||||
@@ -1091,7 +1091,7 @@ VERTEX Mob::UpdatePath(float ToX, float ToY, float ToZ, float Speed, bool &Waypo
|
||||
|
||||
}
|
||||
|
||||
int PathManager::FindNearestPathNode(VERTEX Position)
|
||||
int PathManager::FindNearestPathNode(Map::Vertex Position)
|
||||
{
|
||||
|
||||
// Find the nearest PathNode we have LOS to.
|
||||
@@ -1127,7 +1127,7 @@ int PathManager::FindNearestPathNode(VERTEX Position)
|
||||
{
|
||||
_log(PATHING__DEBUG, "Checking Reachability of Node %i from Start Position.", PathNodes[(*Iterator).id].id);
|
||||
|
||||
if(!zone->zonemap->LineIntersectsZone(Position, PathNodes[(*Iterator).id].v, 1.0f, nullptr, nullptr))
|
||||
if(!zone->zonemap->LineIntersectsZone(Position, PathNodes[(*Iterator).id].v, 1.0f, nullptr))
|
||||
{
|
||||
ClosestPathNodeToStart = (*Iterator).id;
|
||||
break;
|
||||
@@ -1141,13 +1141,13 @@ int PathManager::FindNearestPathNode(VERTEX Position)
|
||||
return ClosestPathNodeToStart;
|
||||
}
|
||||
|
||||
bool PathManager::NoHazards(VERTEX From, VERTEX To)
|
||||
bool PathManager::NoHazards(Map::Vertex From, Map::Vertex To)
|
||||
{
|
||||
// Test the Z coordinate at the mid point.
|
||||
//
|
||||
VERTEX MidPoint((From.x + To.x) / 2, (From.y + To.y) / 2, From.z);
|
||||
Map::Vertex MidPoint((From.x + To.x) / 2, (From.y + To.y) / 2, From.z);
|
||||
|
||||
float NewZ = zone->zonemap->FindBestZ(MAP_ROOT_NODE, MidPoint, nullptr, nullptr);
|
||||
float NewZ = zone->zonemap->FindBestZ(MidPoint, nullptr);
|
||||
|
||||
if(ABS(NewZ - From.z) > RuleR(Pathing, ZDiffThreshold))
|
||||
{
|
||||
@@ -1165,10 +1165,10 @@ bool PathManager::NoHazards(VERTEX From, VERTEX To)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PathManager::NoHazardsAccurate(VERTEX From, VERTEX To)
|
||||
bool PathManager::NoHazardsAccurate(Map::Vertex From, Map::Vertex To)
|
||||
{
|
||||
float stepx, stepy, stepz, curx, cury, curz;
|
||||
VERTEX cur = From;
|
||||
Map::Vertex cur = From;
|
||||
float last_z = From.z;
|
||||
float step_size = 1.0;
|
||||
|
||||
@@ -1182,13 +1182,13 @@ bool PathManager::NoHazardsAccurate(VERTEX From, VERTEX To)
|
||||
stepy = (float)To.y - cury;
|
||||
stepz = (float)To.z - curz;
|
||||
float factor = sqrt(stepx*stepx + stepy*stepy + stepz*stepz);
|
||||
stepx = (stepx/factor)*step_size;
|
||||
stepy = (stepy/factor)*step_size;
|
||||
stepz = (stepz/factor)*step_size;
|
||||
stepx = (stepx / factor)*step_size;
|
||||
stepy = (stepy / factor)*step_size;
|
||||
stepz = (stepz / factor)*step_size;
|
||||
|
||||
VERTEX TestPoint(curx, cury, curz);
|
||||
float NewZ = zone->zonemap->FindBestZ(MAP_ROOT_NODE, TestPoint, nullptr, nullptr);
|
||||
if(ABS(NewZ - last_z) > 5.0)
|
||||
Map::Vertex TestPoint(curx, cury, curz);
|
||||
float NewZ = zone->zonemap->FindBestZ(TestPoint, nullptr);
|
||||
if (ABS(NewZ - last_z) > 5.0f)
|
||||
{
|
||||
_log(PATHING__DEBUG, " HAZARD DETECTED moving from %8.3f, %8.3f, %8.3f to %8.3f, %8.3f, %8.3f. Best Z %8.3f, Z Change is %8.3f",
|
||||
From.x, From.y, From.z, TestPoint.x, TestPoint.y, TestPoint.z, NewZ, NewZ - From.z);
|
||||
@@ -1196,49 +1196,45 @@ bool PathManager::NoHazardsAccurate(VERTEX From, VERTEX To)
|
||||
}
|
||||
last_z = NewZ;
|
||||
|
||||
if(zone->watermap)
|
||||
if (zone->watermap)
|
||||
{
|
||||
NodeRef n = zone->zonemap->SeekNode( zone->zonemap->GetRoot(), TestPoint.x, TestPoint.y);
|
||||
if(n != NODE_NONE)
|
||||
if (zone->watermap->InLiquid(From.x, From.y, From.z) || zone->watermap->InLiquid(To.x, To.y, To.z))
|
||||
{
|
||||
if(zone->watermap->InLiquid(From.x, From.y, From.z) || zone->watermap->InLiquid(To.x, To.y, To.z))
|
||||
{
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(zone->watermap->InLiquid(TestPoint.x, TestPoint.y, NewZ))
|
||||
if (zone->watermap->InLiquid(TestPoint.x, TestPoint.y, NewZ))
|
||||
{
|
||||
Map::Vertex TestPointWater(TestPoint.x, TestPoint.y, NewZ - 0.5f);
|
||||
Map::Vertex TestPointWaterDest = TestPointWater;
|
||||
Map::Vertex hit;
|
||||
TestPointWaterDest.z -= 500;
|
||||
float best_z2 = -999990;
|
||||
if (zone->zonemap->LineIntersectsZone(TestPointWater, TestPointWaterDest, 1.0f, &hit))
|
||||
{
|
||||
VERTEX TestPointWater(TestPoint.x, TestPoint.y, NewZ-0.5);
|
||||
VERTEX TestPointWaterDest(TestPointWater);
|
||||
VERTEX hit;
|
||||
TestPointWaterDest.z -= 500;
|
||||
float best_z2 = -999990;
|
||||
if(zone->zonemap->LineIntersectsNode(n, TestPointWater, TestPointWaterDest, &hit, nullptr))
|
||||
best_z2 = hit.z;
|
||||
}
|
||||
if (best_z2 == -999990)
|
||||
{
|
||||
_log(PATHING__DEBUG, " HAZARD DETECTED, really deep water/lava!");
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ABS(NewZ - best_z2) > RuleR(Pathing, ZDiffThreshold))
|
||||
{
|
||||
best_z2 = hit.z;
|
||||
}
|
||||
if(best_z2 == -999990)
|
||||
{
|
||||
_log(PATHING__DEBUG, " HAZARD DETECTED, really deep water/lava!");
|
||||
_log(PATHING__DEBUG, " HAZARD DETECTED, water is fairly deep at %8.3f units deep", ABS(NewZ - best_z2));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(ABS(NewZ - best_z2) > RuleR(Pathing, ZDiffThreshold))
|
||||
{
|
||||
_log(PATHING__DEBUG, " HAZARD DETECTED, water is fairly deep at %8.3f units deep", ABS(NewZ - best_z2));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
_log(PATHING__DEBUG, " HAZARD NOT DETECTED, water is shallow at %8.3f units deep", ABS(NewZ - best_z2));
|
||||
}
|
||||
_log(PATHING__DEBUG, " HAZARD NOT DETECTED, water is shallow at %8.3f units deep", ABS(NewZ - best_z2));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_log(PATHING__DEBUG, "Hazard point not in water or lava!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_log(PATHING__DEBUG, "Hazard point not in water or lava!");
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1254,12 +1250,11 @@ bool PathManager::NoHazardsAccurate(VERTEX From, VERTEX To)
|
||||
cur.y = cury;
|
||||
cur.z = curz;
|
||||
|
||||
if(ABS(curx - To.x) < step_size) cur.x = To.x;
|
||||
if(ABS(cury - To.y) < step_size) cur.y = To.y;
|
||||
if(ABS(curz - To.z) < step_size) cur.z = To.z;
|
||||
if (ABS(curx - To.x) < step_size) cur.x = To.x;
|
||||
if (ABS(cury - To.y) < step_size) cur.y = To.y;
|
||||
if (ABS(curz - To.z) < step_size) cur.z = To.z;
|
||||
|
||||
}
|
||||
while(cur.x != To.x || cur.y != To.y || cur.z != To.z);
|
||||
} while (cur.x != To.x || cur.y != To.y || cur.z != To.z);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2002,8 +1997,8 @@ void PathManager::MoveNode(Client *c)
|
||||
|
||||
if(zone->zonemap)
|
||||
{
|
||||
VERTEX loc(c->GetX(), c->GetY(), c->GetZ());
|
||||
Node->bestz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, loc, nullptr, nullptr);
|
||||
Map::Vertex loc(c->GetX(), c->GetY(), c->GetZ());
|
||||
Node->bestz = zone->zonemap->FindBestZ(loc, nullptr);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -2073,14 +2068,14 @@ bool PathManager::NodesConnected(PathNode *a, PathNode *b)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PathManager::CheckLosFN(VERTEX a, VERTEX b)
|
||||
bool PathManager::CheckLosFN(Map::Vertex a, Map::Vertex b)
|
||||
{
|
||||
if(zone->zonemap)
|
||||
{
|
||||
VERTEX hit;
|
||||
Map::Vertex hit;
|
||||
|
||||
VERTEX myloc;
|
||||
VERTEX oloc;
|
||||
Map::Vertex myloc;
|
||||
Map::Vertex oloc;
|
||||
|
||||
myloc.x = a.x;
|
||||
myloc.y = a.y;
|
||||
@@ -2091,7 +2086,7 @@ bool PathManager::CheckLosFN(VERTEX a, VERTEX b)
|
||||
oloc.z = b.z;
|
||||
|
||||
|
||||
if(zone->zonemap->LineIntersectsZone(myloc, oloc, 1.0f, nullptr, nullptr))
|
||||
if(zone->zonemap->LineIntersectsZone(myloc, oloc, 1.0f, nullptr))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
+7
-7
@@ -31,7 +31,7 @@ struct NeighbourNode {
|
||||
|
||||
struct PathNode {
|
||||
uint16 id;
|
||||
VERTEX v;
|
||||
Map::Vertex v;
|
||||
float bestz;
|
||||
NeighbourNode Neighbours[PATHNODENEIGHBOURS];
|
||||
};
|
||||
@@ -61,17 +61,17 @@ public:
|
||||
static PathManager *LoadPathFile(const char *ZoneName);
|
||||
bool loadPaths(FILE *fp);
|
||||
void PrintPathing();
|
||||
std::list<int> FindRoute(VERTEX Start, VERTEX End);
|
||||
std::list<int> FindRoute(Map::Vertex Start, Map::Vertex End);
|
||||
std::list<int> FindRoute(int startID, int endID);
|
||||
|
||||
VERTEX GetPathNodeCoordinates(int NodeNumber, bool BestZ = true);
|
||||
bool CheckLosFN(VERTEX a, VERTEX b);
|
||||
Map::Vertex GetPathNodeCoordinates(int NodeNumber, bool BestZ = true);
|
||||
bool CheckLosFN(Map::Vertex a, Map::Vertex b);
|
||||
void SpawnPathNodes();
|
||||
void MeshTest();
|
||||
void SimpleMeshTest();
|
||||
int FindNearestPathNode(VERTEX Position);
|
||||
bool NoHazards(VERTEX From, VERTEX To);
|
||||
bool NoHazardsAccurate(VERTEX From, VERTEX To);
|
||||
int FindNearestPathNode(Map::Vertex Position);
|
||||
bool NoHazards(Map::Vertex From, Map::Vertex To);
|
||||
bool NoHazardsAccurate(Map::Vertex From, Map::Vertex To);
|
||||
void OpenDoors(int Node1, int Node2, Mob* ForWho);
|
||||
|
||||
PathNode* FindPathNodeByCoordinates(float x, float y, float z);
|
||||
|
||||
@@ -5898,6 +5898,64 @@ XS(XS_Client_PlayMP3)
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
XS(XS_Client_ExpeditionMessage); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_ExpeditionMessage)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 3)
|
||||
Perl_croak(aTHX_ "Usage: Client::ExpeditionMessage(THIS, ExpdID, Message)");
|
||||
{
|
||||
Client * THIS;
|
||||
int ExpdID = (int)SvUV(ST(1));
|
||||
const char * Message = (const char *)SvPV_nolen(ST(2));
|
||||
dXSTARG;
|
||||
|
||||
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 == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->ExpeditionSay(Message, ExpdID);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
//Client::SendMarqueeMessage(uint32 type, uint32 priority, uint32 fade_in, uint32 fade_out, uint32 duration, std::string msg)
|
||||
|
||||
XS(XS_Client_SendMarqueeMessage); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Client_SendMarqueeMessage)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items != 7)
|
||||
Perl_croak(aTHX_ "Usage: Client::SendMarqueeMessage(THIS, type, priority, fade_in, fade_out, duration, msg)");
|
||||
{
|
||||
Client * THIS;
|
||||
uint32 type = (uint32)SvUV(ST(1));
|
||||
uint32 priority = (uint32)SvUV(ST(2));
|
||||
uint32 fade_in = (uint32)SvUV(ST(3));
|
||||
uint32 fade_out = (uint32)SvUV(ST(4));
|
||||
uint32 duration = (uint32)SvUV(ST(5));
|
||||
std::string msg = (std::string)SvPV_nolen(ST(6));
|
||||
dXSTARG;
|
||||
|
||||
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 == NULL)
|
||||
Perl_croak(aTHX_ "THIS is NULL, avoiding crash.");
|
||||
|
||||
THIS->SendMarqueeMessage(type, priority, fade_in, fade_out, duration, msg);
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
}
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
#endif
|
||||
@@ -6134,6 +6192,8 @@ XS(boot_Client)
|
||||
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, "$$$$$$$");
|
||||
XSRETURN_YES;
|
||||
}
|
||||
|
||||
|
||||
+6
-3
@@ -6856,7 +6856,7 @@ XS(XS_Mob_ProjectileAnim); /* prototype to pass -Wmissing-prototypes */
|
||||
XS(XS_Mob_ProjectileAnim)
|
||||
{
|
||||
dXSARGS;
|
||||
if (items < 3 || items > 8)
|
||||
if (items < 3 || items > 9)
|
||||
Perl_croak(aTHX_ "Usage: Mob::ProjectileAnim(THIS, mob, item_id, IsArrow?, speed, angle, tilt, arc)");
|
||||
|
||||
{
|
||||
@@ -6868,6 +6868,7 @@ XS(XS_Mob_ProjectileAnim)
|
||||
float angle = 0;
|
||||
float tilt = 0;
|
||||
float arc = 0;
|
||||
char * IDFile = nullptr;
|
||||
|
||||
if (sv_derived_from(ST(0), "Mob")) {
|
||||
IV tmp = SvIV((SV*)SvRV(ST(0)));
|
||||
@@ -6903,7 +6904,9 @@ XS(XS_Mob_ProjectileAnim)
|
||||
arc = (float)SvNV(ST(7));
|
||||
}
|
||||
|
||||
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc);
|
||||
if (items > 8) { IDFile = (char *)SvPV_nolen(ST(8)); }
|
||||
|
||||
THIS->ProjectileAnimation(mob, item_id, IsArrow, speed, angle, tilt, arc, IDFile);
|
||||
|
||||
}
|
||||
XSRETURN_EMPTY;
|
||||
@@ -8389,7 +8392,7 @@ XS(boot_Mob)
|
||||
newXSproto(strcpy(buf, "CheckLoS"), XS_Mob_CheckLoS, file, "$$");
|
||||
newXSproto(strcpy(buf, "CheckLoSToLoc"), XS_Mob_CheckLoSToLoc, file, "$$$$;$");
|
||||
newXSproto(strcpy(buf, "FindGroundZ"), XS_Mob_FindGroundZ, file, "$$$;$");
|
||||
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$");
|
||||
newXSproto(strcpy(buf, "ProjectileAnim"), XS_Mob_ProjectileAnim, file, "$$$;$$$$$$");
|
||||
newXSproto(strcpy(buf, "HasNPCSpecialAtk"), XS_Mob_HasNPCSpecialAtk, file, "$$");
|
||||
newXSproto(strcpy(buf, "SendAppearanceEffect"), XS_Mob_SendAppearanceEffect, file, "$$;$$$$");
|
||||
newXSproto(strcpy(buf, "SetFlyMode"), XS_Mob_SetFlyMode, file, "$$");
|
||||
|
||||
+2
-2
@@ -2023,7 +2023,7 @@ XS(XS_NPC_GetSlowMitigation)
|
||||
Perl_croak(aTHX_ "Usage: NPC::GetSlowMitigation(THIS)");
|
||||
{
|
||||
NPC * THIS;
|
||||
float RETVAL;
|
||||
int16 RETVAL;
|
||||
dXSTARG;
|
||||
|
||||
if (sv_derived_from(ST(0), "NPC")) {
|
||||
@@ -2036,7 +2036,7 @@ XS(XS_NPC_GetSlowMitigation)
|
||||
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
|
||||
|
||||
RETVAL = THIS->GetSlowMitigation();
|
||||
XSprePUSH; PUSHn((double)RETVAL);
|
||||
XSprePUSH; PUSHn((UV)RETVAL);
|
||||
}
|
||||
XSRETURN(1);
|
||||
}
|
||||
|
||||
+5
-12
@@ -1277,16 +1277,9 @@ void QuestManager::signalwith(int npc_id, int signal_id, int wait_ms) {
|
||||
if(wait_ms > 0) {
|
||||
STimerList.push_back(SignalTimer(wait_ms, npc_id, signal_id));
|
||||
return;
|
||||
}
|
||||
|
||||
if (npc_id<1)
|
||||
{
|
||||
printf("signal() bad npcid=%i\n",npc_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
//initiator* signalnpc=0;
|
||||
entity_list.SignalMobsByNPCID(npc_id, signal_id);
|
||||
} else {
|
||||
STimerList.push_back(SignalTimer(0, npc_id, signal_id));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1740,8 +1733,8 @@ short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_
|
||||
}
|
||||
|
||||
//toggle a spawn event
|
||||
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) {
|
||||
zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base);
|
||||
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) {
|
||||
zone->spawn_conditions.ToggleEvent(event_id, enable, strict, reset_base);
|
||||
}
|
||||
|
||||
bool QuestManager::has_zone_flag(int zone_id) {
|
||||
|
||||
+1
-1
@@ -150,7 +150,7 @@ public:
|
||||
void showgrid(int gridid);
|
||||
void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value);
|
||||
short get_spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id);
|
||||
void toggle_spawn_event(int event_id, bool enable, bool reset_base);
|
||||
void toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base);
|
||||
bool has_zone_flag(int zone_id);
|
||||
void set_zone_flag(int zone_id);
|
||||
void clear_zone_flag(int zone_id);
|
||||
|
||||
+94
-64
@@ -303,11 +303,13 @@ void Spawn2::ForceDespawn()
|
||||
{
|
||||
npcthis->Depop(true);
|
||||
IsDespawned = true;
|
||||
npcthis = nullptr;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
npcthis->Depop(false);
|
||||
npcthis = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -555,6 +557,7 @@ SpawnEvent::SpawnEvent() {
|
||||
action = ActionSet;
|
||||
argument = 0;
|
||||
period = 0xFFFFFFFF;
|
||||
strict = false;
|
||||
memset(&next, 0, sizeof(next));
|
||||
}
|
||||
|
||||
@@ -586,26 +589,28 @@ void SpawnConditionManager::Process() {
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
if(cevent.enabled)
|
||||
{
|
||||
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
if(!cevent.strict || (cevent.strict && cevent.next.hour == tod.hour && cevent.next.day == tod.day && cevent.next.month == tod.month && cevent.next.year == tod.year))
|
||||
ExecEvent(cevent, true);
|
||||
|
||||
if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
ExecEvent(cevent, true);
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
std::string t;
|
||||
EQTime::ToString(&cevent.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
|
||||
//save the next event time in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
//find the next closest event timer.
|
||||
FindNearestEvent();
|
||||
//minor optimization, if theres no more possible events,
|
||||
//then stop trying... I dunno how worth while this is.
|
||||
if(EQTime::IsTimeBefore(&next_event, &tod))
|
||||
return;
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
std::string t;
|
||||
EQTime::ToString(&cevent.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Will trigger again in %d EQ minutes at %s.", cevent.id, cevent.period, t.c_str());
|
||||
//save the next event time in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
//find the next closest event timer.
|
||||
FindNearestEvent();
|
||||
//minor optimization, if theres no more possible events,
|
||||
//then stop trying... I dunno how worth while this is.
|
||||
if(EQTime::IsTimeBefore(&next_event, &tod))
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -619,6 +624,14 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
|
||||
return; //unable to find the spawn condition to operate on
|
||||
}
|
||||
|
||||
TimeOfDay_Struct tod;
|
||||
zone->zone_time.getEQTimeOfDay(&tod);
|
||||
if(event.strict && (event.next.hour != tod.hour || event.next.day != tod.day || event.next.month != tod.month || event.next.year != tod.year))
|
||||
{
|
||||
_log(SPAWNS__CONDITIONS, "Event %d: Unable to execute. Condition is strict, and event time has already passed.", event.id);
|
||||
return;
|
||||
}
|
||||
|
||||
SpawnCondition &cond = condi->second;
|
||||
|
||||
int16 new_value = cond.value;
|
||||
@@ -666,10 +679,10 @@ void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) {
|
||||
len = MakeAnyLenString(&query,
|
||||
"UPDATE spawn_events SET "
|
||||
"next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, "
|
||||
"next_year=%d, enabled=%d "
|
||||
"next_year=%d, enabled=%d, strict=%d "
|
||||
"WHERE id=%d",
|
||||
event.next.minute, event.next.hour, event.next.day, event.next.month,
|
||||
event.next.year, event.enabled?1:0, event.id
|
||||
event.next.year, event.enabled?1:0, event.strict?1:0,event.id
|
||||
);
|
||||
if(!database.RunQuery(query, len, errbuf)) {
|
||||
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, errbuf);
|
||||
@@ -703,7 +716,7 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
|
||||
bool ret = false;
|
||||
|
||||
len = MakeAnyLenString(&query,
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,zone "
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict,zone "
|
||||
"FROM spawn_events WHERE id=%d", event_id);
|
||||
if (database.RunQuery(query, len, errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
@@ -721,12 +734,13 @@ bool SpawnConditionManager::LoadDBEvent(uint32 event_id, SpawnEvent &event, std:
|
||||
event.enabled = atoi(row[8])==0?false:true;
|
||||
event.action = (SpawnEvent::Action) atoi(row[9]);
|
||||
event.argument = atoi(row[10]);
|
||||
zone_name = row[11];
|
||||
event.strict = atoi(row[11])==0?false:true;
|
||||
zone_name = row[12];
|
||||
|
||||
std::string t;
|
||||
EQTime::ToString(&event.next, t);
|
||||
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d. Will trigger at %s",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, t.c_str());
|
||||
_log(SPAWNS__CONDITIONS, "(LoadDBEvent) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d. Will trigger at %s",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str());
|
||||
|
||||
ret = true;
|
||||
}
|
||||
@@ -794,7 +808,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
//load spawn events
|
||||
SpawnEvent event;
|
||||
len = MakeAnyLenString(&query,
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument "
|
||||
"SELECT id,cond_id,period,next_minute,next_hour,next_day,next_month,next_year,enabled,action,argument,strict "
|
||||
"FROM spawn_events WHERE zone='%s'", zone_name);
|
||||
if (database.RunQuery(query, len, errbuf, &result)) {
|
||||
safe_delete_array(query);
|
||||
@@ -816,10 +830,11 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
event.enabled = atoi(row[8])==0?false:true;
|
||||
event.action = (SpawnEvent::Action) atoi(row[9]);
|
||||
event.argument = atoi(row[10]);
|
||||
event.strict = atoi(row[11])==0?false:true;
|
||||
spawn_events.push_back(event);
|
||||
|
||||
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument);
|
||||
_log(SPAWNS__CONDITIONS, "(LoadSpawnConditions) Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d, strict %d",
|
||||
event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
|
||||
}
|
||||
mysql_free_result(result);
|
||||
} else {
|
||||
@@ -847,34 +862,48 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
bool StrictCheck = false;
|
||||
if(cevent.strict &&
|
||||
cevent.next.hour == tod.hour &&
|
||||
cevent.next.day == tod.day &&
|
||||
cevent.next.month == tod.month &&
|
||||
cevent.next.year == tod.year)
|
||||
StrictCheck = true;
|
||||
|
||||
//watch for special case of all 0s, which means to reset next to now
|
||||
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
|
||||
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
|
||||
memcpy(&cevent.next, &tod, sizeof(cevent.next));
|
||||
//add one period
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
//save it in the db.
|
||||
UpdateDBEvent(cevent);
|
||||
continue; //were done with this event.
|
||||
}
|
||||
//If event is disabled, or we failed the strict check, set initial spawn_condition to 0.
|
||||
if(!cevent.enabled || !StrictCheck)
|
||||
SetCondition(zone->GetShortName(), zone->GetInstanceID(),cevent.condition_id,0);
|
||||
|
||||
ran = false;
|
||||
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
ExecEvent(cevent, false);
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
ran = true;
|
||||
}
|
||||
//only write it out if the event actually ran
|
||||
if(ran) {
|
||||
//save the event in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
if(cevent.enabled)
|
||||
{
|
||||
//watch for special case of all 0s, which means to reset next to now
|
||||
if(cevent.next.year == 0 && cevent.next.month == 0 && cevent.next.day == 0 && cevent.next.hour == 0 && cevent.next.minute == 0) {
|
||||
_log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
|
||||
memcpy(&cevent.next, &tod, sizeof(cevent.next));
|
||||
//add one period
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
//save it in the db.
|
||||
UpdateDBEvent(cevent);
|
||||
continue; //were done with this event.
|
||||
}
|
||||
|
||||
ran = false;
|
||||
while(EQTime::IsTimeBefore(&tod, &cevent.next)) {
|
||||
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
|
||||
//this event has been triggered.
|
||||
//execute the event
|
||||
if(!cevent.strict || StrictCheck)
|
||||
ExecEvent(cevent, false);
|
||||
|
||||
//add the period of the event to the trigger time
|
||||
EQTime::AddMinutes(cevent.period, &cevent.next);
|
||||
ran = true;
|
||||
}
|
||||
//only write it out if the event actually ran
|
||||
if(ran) {
|
||||
//save the event in the DB
|
||||
UpdateDBEvent(cevent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -894,14 +923,14 @@ void SpawnConditionManager::FindNearestEvent() {
|
||||
int next_id = -1;
|
||||
for(; cur != end; ++cur) {
|
||||
SpawnEvent &cevent = *cur;
|
||||
|
||||
if(!cevent.enabled)
|
||||
continue;
|
||||
|
||||
//see if this event is before our last nearest
|
||||
if(EQTime::IsTimeBefore(&next_event, &cevent.next)) {
|
||||
memcpy(&next_event, &cevent.next, sizeof(next_event));
|
||||
next_id = cevent.id;
|
||||
if(cevent.enabled)
|
||||
{
|
||||
//see if this event is before our last nearest
|
||||
if(EQTime::IsTimeBefore(&next_event, &cevent.next))
|
||||
{
|
||||
memcpy(&next_event, &cevent.next, sizeof(next_event));
|
||||
next_id = cevent.id;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(next_id == -1)
|
||||
@@ -1035,7 +1064,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) {
|
||||
FindNearestEvent();
|
||||
}
|
||||
|
||||
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool reset_base) {
|
||||
void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base) {
|
||||
|
||||
_log(SPAWNS__CONDITIONS, "Request to %s spawn event %d %sresetting trigger time", enabled?"enable":"disable", event_id, reset_base?"":"without ");
|
||||
|
||||
@@ -1048,8 +1077,9 @@ void SpawnConditionManager::ToggleEvent(uint32 event_id, bool enabled, bool rese
|
||||
|
||||
if(cevent.id == event_id) {
|
||||
//make sure were actually changing something
|
||||
if(cevent.enabled != enabled || reset_base) {
|
||||
if(cevent.enabled != enabled || reset_base || cevent.strict != strict) {
|
||||
cevent.enabled = enabled;
|
||||
cevent.strict = strict;
|
||||
if(reset_base) {
|
||||
_log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period);
|
||||
//start with the time now
|
||||
|
||||
+3
-1
@@ -67,6 +67,7 @@ public:
|
||||
|
||||
bool NPCPointerValid() { return (npcthis!=nullptr); }
|
||||
void SetNPCPointer(NPC* n) { npcthis = n; }
|
||||
void SetNPCPointerNull() { npcthis = nullptr; }
|
||||
void SetTimer(uint32 duration) { timer.Start(duration); }
|
||||
uint32 GetKillCount() { return killcount; }
|
||||
protected:
|
||||
@@ -134,6 +135,7 @@ public:
|
||||
bool enabled;
|
||||
Action action;
|
||||
int16 argument;
|
||||
bool strict;
|
||||
|
||||
uint32 period; //eq minutes (3 seconds) between events
|
||||
TimeOfDay_Struct next; //next time this event triggers
|
||||
@@ -148,7 +150,7 @@ public:
|
||||
|
||||
int16 GetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id);
|
||||
void SetCondition(const char *zone_short, uint32 instance_id, uint16 condition_id, int16 new_value, bool world_update = false);
|
||||
void ToggleEvent(uint32 event_id, bool enabled, bool reset_base);
|
||||
void ToggleEvent(uint32 event_id, bool enabled, bool strict, bool reset_base);
|
||||
bool Check(uint16 condition, int16 min_value);
|
||||
void ReloadEvent(uint32 event_id);
|
||||
|
||||
|
||||
@@ -1346,7 +1346,7 @@ void Mob::SendItemAnimation(Mob *to, const Item_Struct *item, SkillUseTypes skil
|
||||
safe_delete(outapp);
|
||||
}
|
||||
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc) {
|
||||
void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, float angle, float tilt, float arc, const char *IDFile) {
|
||||
|
||||
const Item_Struct* item = nullptr;
|
||||
uint8 item_type = 0;
|
||||
@@ -1380,6 +1380,10 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
arc = 50;
|
||||
}
|
||||
|
||||
const char *item_IDFile = item->IDFile;
|
||||
|
||||
if (IDFile && (strncmp(IDFile, "IT", 2) == 0))
|
||||
item_IDFile = IDFile;
|
||||
|
||||
// See SendItemAnimation() for some notes on this struct
|
||||
EQApplicationPacket *outapp = new EQApplicationPacket(OP_SomeItemPacketMaybe, sizeof(Arrow_Struct));
|
||||
@@ -1393,7 +1397,7 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
as->item_id = item->ID;
|
||||
as->item_type = item_type;
|
||||
as->skill = 0; // Doesn't seem to have any effect
|
||||
strn0cpy(as->model_name, item->IDFile, 16);
|
||||
strn0cpy(as->model_name, item_IDFile, 16);
|
||||
as->velocity = speed;
|
||||
as->launch_angle = angle;
|
||||
as->tilt = tilt;
|
||||
@@ -1406,7 +1410,6 @@ void Mob::ProjectileAnimation(Mob* to, int item_id, bool IsArrow, float speed, f
|
||||
|
||||
}
|
||||
|
||||
|
||||
void NPC::DoClassAttacks(Mob *target) {
|
||||
if(target == nullptr)
|
||||
return; //gotta have a target for all these
|
||||
|
||||
+104
-48
@@ -284,20 +284,31 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
#ifdef SPELL_EFFECT_SPAM
|
||||
snprintf(effect_desc, _EDLEN, "Percental Heal: %+i (%d%% max)", spell.max[i], effect_value);
|
||||
#endif
|
||||
//im not 100% sure about this implementation.
|
||||
//the spell value forumula dosent work for these... at least spell 3232 anyways
|
||||
int32 val = spell.max[i];
|
||||
int32 val = GetMaxHP() * spell.base[i] / 100;
|
||||
|
||||
if(caster)
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
//This effect can also do damage by percent.
|
||||
if (val < 0) {
|
||||
|
||||
int32 mhp = GetMaxHP();
|
||||
int32 cap = mhp * spell.base[i] / 100;
|
||||
if (-val > spell.max[i])
|
||||
val = -spell.max[i];
|
||||
|
||||
if(cap < val)
|
||||
val = cap;
|
||||
if (caster)
|
||||
val = caster->GetActSpellDamage(spell_id, val, this);
|
||||
|
||||
if(val > 0)
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
if (val > spell.max[i])
|
||||
val = spell.max[i];
|
||||
|
||||
if(caster)
|
||||
val = caster->GetActSpellHealing(spell_id, val, this);
|
||||
}
|
||||
|
||||
if (val < 0)
|
||||
Damage(caster, -val, spell_id, spell.skill, false, buffslot, false);
|
||||
else
|
||||
HealDamage(val, caster);
|
||||
|
||||
break;
|
||||
@@ -1672,6 +1683,8 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
{
|
||||
CastToClient()->SummonHorse(spell_id);
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -2624,14 +2637,37 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
HealDamage(dmg, caster);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_Taunt:
|
||||
{
|
||||
if (IsNPC())
|
||||
caster->Taunt(this->CastToNPC(), false, spell.base[i]);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case SE_AttackSpeed:
|
||||
if (spell.base[i] < 100)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed2:
|
||||
if (spell.base[i] < 100)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed3:
|
||||
if (spell.base[i] < 0)
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
case SE_AttackSpeed4:
|
||||
SlowMitigation(caster);
|
||||
break;
|
||||
|
||||
// Handled Elsewhere
|
||||
case SE_ImmuneFleeing:
|
||||
case SE_NegateSpellEffect:
|
||||
@@ -2717,10 +2753,6 @@ bool Mob::SpellEffect(Mob* caster, uint16 spell_id, float partial)
|
||||
case SE_DivineSave:
|
||||
case SE_Accuracy:
|
||||
case SE_Flurry:
|
||||
case SE_AttackSpeed:
|
||||
case SE_AttackSpeed2:
|
||||
case SE_AttackSpeed3:
|
||||
case SE_AttackSpeed4:
|
||||
case SE_ImprovedDamage:
|
||||
case SE_ImprovedHeal:
|
||||
case SE_IncreaseSpellHaste:
|
||||
@@ -3351,10 +3383,7 @@ void Mob::DoBuffTic(uint16 spell_id, int slot, uint32 ticsremaining, uint8 caste
|
||||
case SE_Root: {
|
||||
|
||||
/* Root formula derived from extensive personal live parses - Kayen
|
||||
ROOT has a 40% chance to do a resist check to break.
|
||||
Resist check has NO LOWER bounds.
|
||||
If multiple roots on target. Root in first slot will be checked first to break from nukes.
|
||||
If multiple roots on target and broken by spell. Roots are removed ONE at a time in order of buff slot.
|
||||
ROOT has a 70% chance to do a resist check to break.
|
||||
*/
|
||||
|
||||
if (MakeRandomInt(0, 99) < RuleI(Spells, RootBreakCheckChance)){
|
||||
@@ -3970,6 +3999,15 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
|
||||
bool LimitFailure = false;
|
||||
bool LimitInclude[MaxLimitInclude] = { false };
|
||||
/* Certain limits require only one of several Include conditions to be true. Ie. Add damage to fire OR ice spells.
|
||||
0/1 SE_LimitResist
|
||||
2/3 SE_LimitSpell
|
||||
4/5 SE_LimitEffect
|
||||
6/7 SE_LimitTarget
|
||||
8/9 SE_LimitSpellGroup:
|
||||
10/11 SE_LimitCastingSkill:
|
||||
Remember: Update MaxLimitInclude in spdat.h if adding new limits that require Includes
|
||||
*/
|
||||
int FocusCount = 0;
|
||||
|
||||
std::map<uint32, std::map<uint32, AA_Ability> >::const_iterator find_iter = aa_effects.find(aa_ID);
|
||||
@@ -3984,7 +4022,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
base1 = iter->second.base1;
|
||||
base2 = iter->second.base2;
|
||||
slot = iter->second.slot;
|
||||
|
||||
|
||||
/*
|
||||
AA Foci's can contain multiple focus effects within the same AA.
|
||||
To handle this we will not automatically return zero if a limit is found.
|
||||
@@ -4038,8 +4076,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_LimitInstant:
|
||||
if(spell.buffduration)
|
||||
if(base1 == 1 && spell.buffduration) //Fail if not instant
|
||||
LimitFailure = true;
|
||||
if(base1 == 0 && (spell.buffduration == 0)) //Fail if instant
|
||||
LimitFailure = true;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitMaxLevel:
|
||||
@@ -4087,13 +4128,14 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
case SE_LimitMinDur:
|
||||
if (base1 > CalcBuffDuration_formula(GetLevel(), spell.buffdurationformula, spell.buffduration))
|
||||
LimitFailure = true;
|
||||
break;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitEffect:
|
||||
if(base1 < 0){
|
||||
if(IsEffectInSpell(spell_id,-base1)) //Exclude
|
||||
LimitFailure = true;
|
||||
}
|
||||
}
|
||||
else{
|
||||
LimitInclude[4] = true;
|
||||
if(IsEffectInSpell(spell_id,base1)) //Include
|
||||
@@ -4138,10 +4180,11 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
break;
|
||||
|
||||
case SE_LimitCombatSkills:
|
||||
if (base1 == 0 && IsCombatSkill(spell_id)) //Exclude Discs
|
||||
if (base1 == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
|
||||
LimitFailure = true;
|
||||
else if (base1 == 1 && !IsCombatSkill(spell_id)) //Exclude Spells
|
||||
else if (base1 == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
|
||||
LimitFailure = true;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitSpellGroup:
|
||||
@@ -4172,7 +4215,7 @@ int16 Client::CalcAAFocus(focusType type, uint32 aa_ID, uint16 spell_id)
|
||||
//Do not use this limit more then once per spell. If multiple class, treat value like items would.
|
||||
if (!PassLimitClass(base1, GetClass()))
|
||||
LimitFailure = true;
|
||||
break;
|
||||
break;
|
||||
|
||||
case SE_LimitRace:
|
||||
if (base1 != GetRace())
|
||||
@@ -4433,8 +4476,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_LimitInstant:
|
||||
if(spell.buffduration)
|
||||
if(focus_spell.base[i] == 1 && spell.buffduration) //Fail if not instant
|
||||
return 0;
|
||||
if(focus_spell.base[i] == 0 && (spell.buffduration == 0)) //Fail if instant
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitMaxLevel:
|
||||
@@ -4539,10 +4585,11 @@ int16 Mob::CalcFocusEffect(focusType type, uint16 focus_id, uint16 spell_id, boo
|
||||
break;
|
||||
|
||||
case SE_LimitCombatSkills:
|
||||
if (focus_spell.base[i] == 0 && IsCombatSkill(spell_id)) //Exclude Disc
|
||||
return 0;
|
||||
else if (focus_spell.base[i] == 1 && !IsCombatSkill(spell_id)) //Include Spells
|
||||
if (focus_spell.base[i] == 0 && (IsCombatSkill(spell_id) || IsCombatProc(spell_id))) //Exclude Discs / Procs
|
||||
return 0;
|
||||
else if (focus_spell.base[i] == 1 && (!IsCombatSkill(spell_id) || !IsCombatProc(spell_id))) //Exclude Spells
|
||||
return 0;
|
||||
|
||||
break;
|
||||
|
||||
case SE_LimitSpellGroup:
|
||||
@@ -5628,7 +5675,7 @@ uint16 Mob::GetSpellEffectResistChance(uint16 spell_id)
|
||||
if(!IsValidSpell(spell_id))
|
||||
return 0;
|
||||
|
||||
if (!aabonuses.SEResist[0] && !spellbonuses.SEResist[0] && !itembonuses.SEResist[0])
|
||||
if (!aabonuses.SEResist[1] && !spellbonuses.SEResist[1] && !itembonuses.SEResist[1])
|
||||
return 0;
|
||||
|
||||
uint16 resist_chance = 0;
|
||||
@@ -6064,26 +6111,35 @@ bool Mob::TrySpellProjectile(Mob* spell_target, uint16 spell_id){
|
||||
projectile_timer.Start(250);
|
||||
}
|
||||
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
//This will use the correct graphic as defined in the player_1 field of spells_new table. Found in UF+ spell files.
|
||||
if (RuleB(Spells, UseLiveSpellProjectileGFX)) {
|
||||
ProjectileAnimation(spell_target,0, false, 1.5,0,0,0, spells[spell_id].player_1);
|
||||
}
|
||||
|
||||
//Pending other types of projectile graphics. (They will function but with a default arrow graphic for now)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
//This allows limited support for server using older spell files that do not contain data for bolt graphics.
|
||||
else {
|
||||
//Only use fire graphic for fire spells.
|
||||
if (spells[spell_id].resisttype == RESIST_FIRE) {
|
||||
|
||||
if (IsClient()){
|
||||
if (CastToClient()->GetClientVersionBit() <= 4) //Titanium needs alternate graphic.
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_Titanium)), false, 1.5);
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_SOF)), false, 1.5);
|
||||
}
|
||||
|
||||
else
|
||||
ProjectileAnimation(spell_target,(RuleI(Spells, FRProjectileItem_NPC)), false, 1.5);
|
||||
|
||||
}
|
||||
|
||||
//Default to an arrow if not using a mage bolt (Use up to date spell file and enable above rules for best results)
|
||||
else
|
||||
ProjectileAnimation(spell_target,0, 1, 1.5);
|
||||
}
|
||||
|
||||
if (spells[spell_id].CastingAnim == 64)
|
||||
anim = 44; //Corrects for animation error.
|
||||
|
||||
DoAnim(anim, 0, true, IsClient() ? FilterPCSpells : FilterNPCSpells); //Override the default projectile animation.
|
||||
return true;
|
||||
|
||||
@@ -4966,6 +4966,28 @@ bool Mob::FindType(uint16 type, bool bOffensive, uint16 threshold) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::IsCombatProc(uint16 spell_id) {
|
||||
|
||||
if (RuleB(Spells, FocusCombatProcs))
|
||||
return false;
|
||||
|
||||
if(spell_id == SPELL_UNKNOWN)
|
||||
return(false);
|
||||
|
||||
if ((spells[spell_id].cast_time == 0) && (spells[spell_id].recast_time == 0) && (spells[spell_id].recovery_time == 0))
|
||||
{
|
||||
|
||||
for (int i = 0; i < MAX_PROCS; i++){
|
||||
if (PermaProcs[i].spellID == spell_id || SpellProcs[i].spellID == spell_id
|
||||
|| SkillProcs[i].spellID == spell_id || RangedProcs[i].spellID == spell_id){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mob::AddProcToWeapon(uint16 spell_id, bool bPerma, uint16 iChance, uint16 base_spell_id) {
|
||||
if(spell_id == SPELL_UNKNOWN)
|
||||
return(false);
|
||||
|
||||
+14
-14
@@ -241,9 +241,9 @@ void NPC::UpdateWaypoint(int wp_index)
|
||||
if(!RuleB(Watermap, CheckForWaterAtWaypoints) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(cur_wp_x, cur_wp_y, cur_wp_z)))
|
||||
{
|
||||
VERTEX dest(cur_wp_x, cur_wp_y, cur_wp_z);
|
||||
Map::Vertex dest(cur_wp_x, cur_wp_y, cur_wp_z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
if( (newz > -2000) && ABS(newz - dest.z) < RuleR(Map, FixPathingZMaxDeltaWaypoint))
|
||||
cur_wp_z = newz + 1;
|
||||
@@ -571,9 +571,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
{
|
||||
VERTEX dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -700,9 +700,9 @@ bool Mob::MakeNewPositionAndSendUpdate(float x, float y, float z, float speed, b
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
{
|
||||
VERTEX dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -825,9 +825,9 @@ bool Mob::CalculateNewPosition(float x, float y, float z, float speed, bool chec
|
||||
if(!RuleB(Watermap, CheckForWaterWhenMoving) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
{
|
||||
VERTEX dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -933,9 +933,9 @@ void NPC::AssignWaypoints(int32 grid) {
|
||||
if(!RuleB(Watermap, CheckWaypointsInWaterWhenLoading) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(newwp.x, newwp.y, newwp.z)))
|
||||
{
|
||||
VERTEX dest(newwp.x, newwp.y, newwp.z);
|
||||
Map::Vertex dest(newwp.x, newwp.y, newwp.z);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
if( (newz > -2000) && ABS(newz-dest.z) < RuleR(Map, FixPathingZMaxDeltaLoading))
|
||||
newwp.z = newz + 1;
|
||||
@@ -988,9 +988,9 @@ void Mob::SendTo(float new_x, float new_y, float new_z) {
|
||||
if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
{
|
||||
VERTEX dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
@@ -1019,9 +1019,9 @@ void Mob::SendToFixZ(float new_x, float new_y, float new_z) {
|
||||
if(!RuleB(Watermap, CheckForWaterOnSendTo) || !zone->HasWaterMap() ||
|
||||
(zone->HasWaterMap() && !zone->watermap->InWater(x_pos, y_pos, z_pos)))
|
||||
{
|
||||
VERTEX dest(x_pos, y_pos, z_pos);
|
||||
Map::Vertex dest(x_pos, y_pos, z_pos);
|
||||
|
||||
float newz = zone->zonemap->FindBestZ(MAP_ROOT_NODE, dest, nullptr, nullptr);
|
||||
float newz = zone->zonemap->FindBestZ(dest, nullptr);
|
||||
|
||||
mlog(AI__WAYPOINTS, "BestZ returned %4.3f at %4.3f, %4.3f, %4.3f", newz,x_pos,y_pos,z_pos);
|
||||
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ bool Zone::Bootup(uint32 iZoneID, uint32 iInstanceID, bool iStaticZone) {
|
||||
worldserver.SetZone(0);
|
||||
return false;
|
||||
}
|
||||
zone->zonemap = Map::LoadMapfile(zone->map_name);
|
||||
zone->zonemap = Map::LoadMapFile(zone->map_name);
|
||||
zone->watermap = WaterMap::LoadWaterMapfile(zone->map_name);
|
||||
zone->pathing = PathManager::LoadPathFile(zone->map_name);
|
||||
|
||||
|
||||
+19
-4
@@ -33,8 +33,11 @@ ZoneDatabase::ZoneDatabase(const char* host, const char* user, const char* passw
|
||||
void ZoneDatabase::ZDBInitVars() {
|
||||
memset(door_isopen_array, 0, sizeof(door_isopen_array));
|
||||
npc_spells_maxid = 0;
|
||||
npc_spellseffects_maxid = 0;
|
||||
npc_spells_cache = 0;
|
||||
npc_spellseffects_cache = 0;
|
||||
npc_spells_loadtried = 0;
|
||||
npc_spellseffects_loadtried = 0;
|
||||
max_faction = 0;
|
||||
faction_array = nullptr;
|
||||
}
|
||||
@@ -49,6 +52,14 @@ ZoneDatabase::~ZoneDatabase() {
|
||||
}
|
||||
safe_delete_array(npc_spells_loadtried);
|
||||
|
||||
if (npc_spellseffects_cache) {
|
||||
for (x=0; x<=npc_spellseffects_maxid; x++) {
|
||||
safe_delete_array(npc_spellseffects_cache[x]);
|
||||
}
|
||||
safe_delete_array(npc_spellseffects_cache);
|
||||
}
|
||||
safe_delete_array(npc_spellseffects_loadtried);
|
||||
|
||||
if (faction_array != nullptr) {
|
||||
for (x=0; x <= max_faction; x++) {
|
||||
if (faction_array[x] != 0)
|
||||
@@ -1053,6 +1064,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.attack_count,"
|
||||
"npc_types.special_abilities,"
|
||||
"npc_types.npc_spells_id,"
|
||||
"npc_types.npc_spells_effects_id,"
|
||||
"npc_types.d_meele_texture1,"
|
||||
"npc_types.d_meele_texture2,"
|
||||
"npc_types.prim_melee_type,"
|
||||
@@ -1099,7 +1111,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
"npc_types.underwater,"
|
||||
"npc_types.emoteid,"
|
||||
"npc_types.spellscale,"
|
||||
"npc_types.healscale";
|
||||
"npc_types.healscale,"
|
||||
"npc_types.no_target_hotkey";
|
||||
|
||||
MakeAnyLenString(&query, "%s FROM npc_types WHERE id=%d", basic_query, id);
|
||||
|
||||
@@ -1150,6 +1163,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->attack_count = atoi(row[r++]);
|
||||
strn0cpy(tmpNPCType->special_abilities, row[r++], 512);
|
||||
tmpNPCType->npc_spells_id = atoi(row[r++]);
|
||||
tmpNPCType->npc_spells_effects_id = atoi(row[r++]);
|
||||
tmpNPCType->d_meele_texture1 = atoi(row[r++]);
|
||||
tmpNPCType->d_meele_texture2 = atoi(row[r++]);
|
||||
tmpNPCType->prim_melee_type = atoi(row[r++]);
|
||||
@@ -1191,7 +1205,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF) << 8;
|
||||
tmpNPCType->armor_tint[0] |= (atoi(row[r++]) & 0xFF);
|
||||
tmpNPCType->armor_tint[0] |= (tmpNPCType->armor_tint[0]) ? (0xFF << 24) : 0;
|
||||
|
||||
|
||||
int i;
|
||||
if (armor_tint_id > 0)
|
||||
{
|
||||
@@ -1272,7 +1286,7 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->see_improved_hide = atoi(row[r++])==0?false:true;
|
||||
tmpNPCType->ATK = atoi(row[r++]);
|
||||
tmpNPCType->accuracy_rating = atoi(row[r++]);
|
||||
tmpNPCType->slow_mitigation = atof(row[r++]);
|
||||
tmpNPCType->slow_mitigation = atoi(row[r++]);
|
||||
tmpNPCType->maxlevel = atoi(row[r++]);
|
||||
tmpNPCType->scalerate = atoi(row[r++]);
|
||||
tmpNPCType->private_corpse = atoi(row[r++]) == 1 ? true : false;
|
||||
@@ -1281,7 +1295,8 @@ const NPCType* ZoneDatabase::GetNPCType (uint32 id) {
|
||||
tmpNPCType->emoteid = atoi(row[r++]);
|
||||
tmpNPCType->spellscale = atoi(row[r++]);
|
||||
tmpNPCType->healscale = atoi(row[r++]);
|
||||
|
||||
tmpNPCType->no_target_hotkey = atoi(row[r++]) == 1 ? true : false;
|
||||
|
||||
// If NPC with duplicate NPC id already in table,
|
||||
// free item we attempted to add.
|
||||
if (zone->npctable.find(tmpNPCType->npc_id) != zone->npctable.end())
|
||||
|
||||
@@ -30,6 +30,17 @@ struct DBnpcspells_entries_Struct {
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
#pragma pack(1)
|
||||
struct DBnpcspellseffects_entries_Struct {
|
||||
int16 spelleffectid;
|
||||
uint8 minlevel;
|
||||
uint8 maxlevel;
|
||||
int32 base;
|
||||
int32 limit;
|
||||
int32 max;
|
||||
};
|
||||
#pragma pack()
|
||||
|
||||
struct DBnpcspells_Struct {
|
||||
uint32 parent_list;
|
||||
int16 attack_proc;
|
||||
@@ -38,6 +49,12 @@ struct DBnpcspells_Struct {
|
||||
DBnpcspells_entries_Struct entries[0];
|
||||
};
|
||||
|
||||
struct DBnpcspellseffects_Struct {
|
||||
uint32 parent_list;
|
||||
uint32 numentries;
|
||||
DBnpcspellseffects_entries_Struct entries[0];
|
||||
};
|
||||
|
||||
struct DBTradeskillRecipe_Struct {
|
||||
SkillUseTypes tradeskill;
|
||||
int16 skill_needed;
|
||||
@@ -345,7 +362,9 @@ public:
|
||||
void AddLootTableToNPC(NPC* npc,uint32 loottable_id, ItemList* itemlist, uint32* copper, uint32* silver, uint32* gold, uint32* plat);
|
||||
void AddLootDropToNPC(NPC* npc,uint32 lootdrop_id, ItemList* itemlist, uint8 droplimit, uint8 mindrop);
|
||||
uint32 GetMaxNPCSpellsID();
|
||||
uint32 GetMaxNPCSpellsEffectsID();
|
||||
DBnpcspells_Struct* GetNPCSpells(uint32 iDBSpellsID);
|
||||
DBnpcspellseffects_Struct* GetNPCSpellsEffects(uint32 iDBSpellsEffectsID);
|
||||
|
||||
/*
|
||||
* Mercs
|
||||
@@ -475,8 +494,11 @@ protected:
|
||||
uint32 max_faction;
|
||||
Faction** faction_array;
|
||||
uint32 npc_spells_maxid;
|
||||
uint32 npc_spellseffects_maxid;
|
||||
DBnpcspells_Struct** npc_spells_cache;
|
||||
bool* npc_spells_loadtried;
|
||||
DBnpcspellseffects_Struct** npc_spellseffects_cache;
|
||||
bool* npc_spellseffects_loadtried;
|
||||
uint8 door_isopen_array[255];
|
||||
};
|
||||
|
||||
|
||||
+3
-1
@@ -53,6 +53,7 @@ struct NPCType
|
||||
uint8 helmtexture;
|
||||
uint32 loottable_id;
|
||||
uint32 npc_spells_id;
|
||||
uint32 npc_spells_effects_id;
|
||||
int32 npc_faction_id;
|
||||
uint32 merchanttype;
|
||||
uint32 alt_currency_type;
|
||||
@@ -111,7 +112,7 @@ struct NPCType
|
||||
int accuracy_rating; //10 = 1% accuracy
|
||||
bool findable; //can be found with find command
|
||||
bool trackable;
|
||||
float slow_mitigation; // Slow mitigation % in decimal form.
|
||||
int16 slow_mitigation;
|
||||
uint8 maxlevel;
|
||||
uint32 scalerate;
|
||||
bool private_corpse;
|
||||
@@ -120,6 +121,7 @@ struct NPCType
|
||||
uint32 emoteid;
|
||||
float spellscale;
|
||||
float healscale;
|
||||
bool no_target_hotkey;
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user