Better flee runspeed calculation.

Added two new NPC special_abilities, ALWAYS_FLEE and FLEE_PERCENT.
Fixed an issue where a NPC could get stuck on a single coord in a rectangular roambox.
Added mindelay to spawngroup to allow for greater control of the roambox delay. SQL is required.
This commit is contained in:
cavedude00 2014-02-26 18:06:16 -08:00
parent ea31a29f8a
commit 1d6bd3cc5e
19 changed files with 118 additions and 56 deletions

View File

@ -4,12 +4,19 @@ EQEMu Changelog (Started on Sept 24, 2003 15:50)
== 02/26/2014 == == 02/26/2014 ==
Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells Kayen: Implemented SE_FrenziedDevestation - increase critical spell chacnce and 2x mana cost for DD spells
Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs Kayen: Fixed SE_SpellProcChance - Now works on spell dervived procs
cavedude: Added two new NPC special_abilities. ALWAYS_FLEE, which forces the NPC to always flee ignoring FleeIfNotAlone and FLEE_PERCENT which allows you to change the HP an individual NPC will flee at. If no value is set, the rule is used as normal.
cavedude: Fixed an issue where rectangular roamboxes could cause an NPC to get stuck on a single coord.
cavedude: Added a new roambox column, mindelay allowing you to have more control over the roambox delay.
Required SQL: utils/sql/git/required/2014_02_26_roambox_update.sql
== 02/24/2014 == == 02/24/2014 ==
cavedude: Better flee runspeed calculation. Added rule Combat:FleeMultiplier to alter this behavior.
Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535) Sorvani: Updated GetUnusedInstanceID to not recycle instance ID's unless it has reached max (65535)
== 02/23/2014 == == 02/23/2014 ==
Secrets: Exported the client object SendTargetCommand to Perl. Secrets: Exported the client object SendTargetCommand to Perl.
cavedude: Merchants will now keep better track of charges.
== 02/20/2014 == == 02/20/2014 ==
Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value Kayen: Implemented SE_MitigateDotDamage - dot spell mitigation rune with max value

View File

@ -310,8 +310,8 @@ RULE_INT ( Combat, ClientBaseCritChance, 0 ) //The base crit chance for all clie
RULE_BOOL ( Combat, UseIntervalAC, true) RULE_BOOL ( Combat, UseIntervalAC, true)
RULE_INT ( Combat, PetAttackMagicLevel, 30) RULE_INT ( Combat, PetAttackMagicLevel, 30)
RULE_BOOL ( Combat, EnableFearPathing, true) RULE_BOOL ( Combat, EnableFearPathing, true)
RULE_INT ( Combat, FleeHPRatio, 25) RULE_REAL ( Combat, FleeMultiplier, 2.0) // Determines how quickly a NPC will slow down while fleeing. Decrease multiplier to slow NPC down quicker.
RULE_INT ( Combat, FleeSnareHPRatio, 11) // HP at which snare will halt movement of a fleeing NPC. RULE_INT ( Combat, FleeHPRatio, 25) //HP % when a NPC begins to flee.
RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it. RULE_BOOL ( Combat, FleeIfNotAlone, false) // If false, mobs won't flee if other mobs are in combat with it.
RULE_BOOL ( Combat, AdjustProcPerMinute, true) RULE_BOOL ( Combat, AdjustProcPerMinute, true)
RULE_REAL ( Combat, AvgProcsPerMinute, 2.0) RULE_REAL ( Combat, AvgProcsPerMinute, 2.0)

View File

@ -0,0 +1,2 @@
alter table `spawngroup` add column `mindelay` int(11) not null default 15000 AFTER delay;
alter table `spawngroup` change `delay` `delay` int(11) not null default 45000;

View File

@ -455,6 +455,7 @@ void NPC::AI_Init() {
roambox_distance = 0; roambox_distance = 0;
roambox_movingto_x = 0; roambox_movingto_x = 0;
roambox_movingto_y = 0; roambox_movingto_y = 0;
roambox_min_delay = 2500;
roambox_delay = 2500; roambox_delay = 2500;
} }
@ -1590,14 +1591,17 @@ void NPC::AI_DoMovement() {
movey *= MakeRandomInt(0, 1) ? 1 : -1; movey *= MakeRandomInt(0, 1) ? 1 : -1;
roambox_movingto_x = GetX() + movex; roambox_movingto_x = GetX() + movex;
roambox_movingto_y = GetY() + movey; roambox_movingto_y = GetY() + movey;
//Try to calculate new coord using distance.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x -= movex * 2; roambox_movingto_x -= movex * 2;
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y -= movey * 2; roambox_movingto_y -= movey * 2;
//New coord is still invalid, ignore distance and just pick a new random coord.
//If we're here we may have a roambox where one side is shorter than the specified distance. Commons, Wkarana, etc.
if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x) if (roambox_movingto_x > roambox_max_x || roambox_movingto_x < roambox_min_x)
roambox_movingto_x = roambox_max_x; roambox_movingto_x = MakeRandomFloat(roambox_min_x+1,roambox_max_x-1);
if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y) if (roambox_movingto_y > roambox_max_y || roambox_movingto_y < roambox_min_y)
roambox_movingto_y = roambox_max_y; roambox_movingto_y = MakeRandomFloat(roambox_min_y+1,roambox_max_y-1);
} }
mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)", mlog(AI__WAYPOINTS, "Roam Box: d=%.3f (%.3f->%.3f,%.3f->%.3f): Go To (%.3f,%.3f)",
@ -1605,7 +1609,7 @@ void NPC::AI_DoMovement() {
if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true)) if (!CalculateNewPosition2(roambox_movingto_x, roambox_movingto_y, GetZ(), walksp, true))
{ {
roambox_movingto_x = roambox_max_x + 1; // force update roambox_movingto_x = roambox_max_x + 1; // force update
pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_delay, roambox_delay + 5000); pLastFightingDelayMoving = Timer::GetCurrentTime() + RandomTimer(roambox_min_delay, roambox_delay);
SetMoving(false); SetMoving(false);
SendPosition(); // makes mobs stop clientside SendPosition(); // makes mobs stop clientside
} }

View File

@ -2085,21 +2085,27 @@ void command_ai(Client *c, const Seperator *sep)
} }
else if (strcasecmp(sep->arg[1], "roambox") == 0) { else if (strcasecmp(sep->arg[1], "roambox") == 0) {
if (target && target->IsAIControlled() && target->IsNPC()) { if (target && target->IsAIControlled() && target->IsNPC()) {
if ((sep->argnum == 6 || sep->argnum == 7) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) { if ((sep->argnum == 6 || sep->argnum == 7 || sep->argnum == 8) && sep->IsNumber(2) && sep->IsNumber(3) && sep->IsNumber(4) && sep->IsNumber(5) && sep->IsNumber(6)) {
uint32 tmp = 2500; uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(7)) if (sep->IsNumber(7))
tmp = atoi(sep->arg[7]); tmp = atoi(sep->arg[7]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp); if (sep->IsNumber(8))
tmp2 = atoi(sep->arg[8]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), atof(sep->arg[4]), atof(sep->arg[5]), atof(sep->arg[6]), tmp, tmp2);
} }
else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) { else if ((sep->argnum == 3 || sep->argnum == 4) && sep->IsNumber(2) && sep->IsNumber(3)) {
uint32 tmp = 2500; uint32 tmp = 2500;
uint32 tmp2 = 2500;
if (sep->IsNumber(4)) if (sep->IsNumber(4))
tmp = atoi(sep->arg[4]); tmp = atoi(sep->arg[4]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp); if (sep->IsNumber(5))
tmp2 = atoi(sep->arg[5]);
target->CastToNPC()->AI_SetRoambox(atof(sep->arg[2]), atof(sep->arg[3]), tmp, tmp2);
} }
else { else {
c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay]"); c->Message(0, "Usage: #ai roambox dist max_x min_x max_y min_y [delay] [mindelay]");
c->Message(0, "Usage: #ai roambox dist roamdist [delay]"); c->Message(0, "Usage: #ai roambox dist roamdist [delay] [mindelay]");
} }
} }
else else

View File

@ -122,7 +122,10 @@ enum {
TETHER = 33, TETHER = 33,
DESTRUCTIBLE_OBJECT = 34, DESTRUCTIBLE_OBJECT = 34,
NO_HARM_FROM_CLIENT = 35, NO_HARM_FROM_CLIENT = 35,
MAX_SPECIAL_ATTACK = 36 ALWAYS_FLEE = 36,
FLEE_PERCENT = 37,
MAX_SPECIAL_ATTACK = 38
}; };
typedef enum { //fear states typedef enum { //fear states

View File

@ -53,7 +53,10 @@ void Mob::CheckFlee() {
//see if were possibly hurt enough //see if were possibly hurt enough
float ratio = GetHPRatio(); float ratio = GetHPRatio();
if(ratio >= RuleI(Combat, FleeHPRatio)) float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(ratio >= fleeratio)
return; return;
//we might be hurt enough, check con now.. //we might be hurt enough, check con now..
@ -77,25 +80,24 @@ void Mob::CheckFlee() {
switch(con) { switch(con) {
//these values are not 100% researched //these values are not 100% researched
case CON_GREEN: case CON_GREEN:
run_ratio = RuleI(Combat, FleeHPRatio); run_ratio = fleeratio;
break; break;
case CON_LIGHTBLUE: case CON_LIGHTBLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 8 / 10; run_ratio = fleeratio * 9 / 10;
break; break;
case CON_BLUE: case CON_BLUE:
run_ratio = RuleI(Combat, FleeHPRatio) * 6 / 10; run_ratio = fleeratio * 8 / 10;
break; break;
default: default:
run_ratio = RuleI(Combat, FleeHPRatio) * 4 / 10; run_ratio = fleeratio * 7 / 10;
break; break;
} }
if(ratio < run_ratio) if(ratio < run_ratio)
{ {
if (RuleB(Combat, FleeIfNotAlone) || if (RuleB(Combat, FleeIfNotAlone) ||
(!RuleB(Combat, FleeIfNotAlone) && GetSpecialAbility(ALWAYS_FLEE) ||
(entity_list.GetHatedCount(hate_top, this) == 0))) (!RuleB(Combat, FleeIfNotAlone) && (entity_list.GetHatedCount(hate_top, this) == 0)))
StartFleeing(); StartFleeing();
} }
} }
@ -110,7 +112,9 @@ void Mob::ProcessFlee() {
} }
//see if we are still dying, if so, do nothing //see if we are still dying, if so, do nothing
if(GetHPRatio() < (float)RuleI(Combat, FleeHPRatio)) float fleeratio = GetSpecialAbility(FLEE_PERCENT);
fleeratio = fleeratio > 0 ? fleeratio : RuleI(Combat, FleeHPRatio);
if(GetHPRatio() < fleeratio)
return; return;
//we are not dying anymore... see what we do next //we are not dying anymore... see what we do next
@ -128,18 +132,18 @@ void Mob::ProcessFlee() {
float Mob::GetFearSpeed() { float Mob::GetFearSpeed() {
if(flee_mode) { if(flee_mode) {
//we know ratio < FLEE_HP_RATIO //we know ratio < FLEE_HP_RATIO
float speed = GetRunspeed(); float speed = GetBaseRunspeed();
float ratio = GetHPRatio(); float ratio = GetHPRatio();
float multiplier = RuleR(Combat, FleeMultiplier);
// mob's movement will halt with a decent snare at HP specified by rule. if(GetSnaredAmount() > 40)
if (ratio <= RuleI(Combat, FleeSnareHPRatio) && GetSnaredAmount() > 40) { multiplier = multiplier / 6.0f;
return 0.0001f;
}
if (ratio < FLEE_HP_MINSPEED) speed = speed * ratio * multiplier / 100;
ratio = FLEE_HP_MINSPEED;
speed = speed * 0.5 * ratio / 100; //NPC will eventually stop. Snares speeds this up.
if(speed < 0.09)
speed = 0.0001f;
return(speed); return(speed);
} }

View File

@ -2166,7 +2166,9 @@ luabind::scope lua_register_special_abilities() {
luabind::value("leash", static_cast<int>(LEASH)), luabind::value("leash", static_cast<int>(LEASH)),
luabind::value("tether", static_cast<int>(TETHER)), luabind::value("tether", static_cast<int>(TETHER)),
luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)), luabind::value("destructible_object", static_cast<int>(DESTRUCTIBLE_OBJECT)),
luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT)) luabind::value("no_harm_from_client", static_cast<int>(NO_HARM_FROM_CLIENT)),
luabind::value("always_flee", static_cast<int>(ALWAYS_FLEE)),
luabind::value("flee_percent", static_cast<int>(FLEE_PERCENT))
]; ];
} }

View File

@ -297,9 +297,9 @@ void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, f
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y); self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y);
} }
void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay) { void Lua_NPC::AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay) {
Lua_Safe_Call_Void(); Lua_Safe_Call_Void();
self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay); self->AI_SetRoambox(dist, max_x, min_x, max_y, min_y, delay, mindelay);
} }
int Lua_NPC::GetNPCSpellsID() { int Lua_NPC::GetNPCSpellsID() {
@ -494,7 +494,7 @@ luabind::scope lua_register_npc() {
.def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot) .def("SaveGuardSpot", (void(Lua_NPC::*)(bool))&Lua_NPC::SaveGuardSpot)
.def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding) .def("IsGuarding", (bool(Lua_NPC::*)(void))&Lua_NPC::IsGuarding)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox) .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float))&Lua_NPC::AI_SetRoambox)
.def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32))&Lua_NPC::AI_SetRoambox) .def("AI_SetRoambox", (void(Lua_NPC::*)(float,float,float,float,float,uint32,uint32))&Lua_NPC::AI_SetRoambox)
.def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID) .def("GetNPCSpellsID", (int(Lua_NPC::*)(void))&Lua_NPC::GetNPCSpellsID)
.def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID) .def("GetSpawnPointID", (int(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointID)
.def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX) .def("GetSpawnPointX", (float(Lua_NPC::*)(void))&Lua_NPC::GetSpawnPointX)

View File

@ -85,7 +85,7 @@ public:
void SaveGuardSpot(bool clear); void SaveGuardSpot(bool clear);
bool IsGuarding(); bool IsGuarding();
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y); void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y);
void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay); void AI_SetRoambox(float dist, float max_x, float min_x, float max_y, float min_y, uint32 delay, uint32 mindelay);
int GetNPCSpellsID(); int GetNPCSpellsID();
int GetSpawnPointID(); int GetSpawnPointID();
float GetSpawnPointX(); float GetSpawnPointX();

View File

@ -381,6 +381,14 @@ int main(int argc, char** argv) {
entity_list.AddClient(client); entity_list.AddClient(client);
} }
uint8 IDLEZONETIME = 200;
if ( numclients < 1 && temp_timer.GetDuration() != IDLEZONETIME )
temp_timer.SetTimer(IDLEZONETIME);
else if ( numclients > 0 && temp_timer.GetDuration() == IDLEZONETIME )
{
temp_timer.SetTimer(10);
temp_timer.Trigger();
}
//check for timeouts in other threads //check for timeouts in other threads
timeout_manager.CheckTimeouts(); timeout_manager.CheckTimeouts();

View File

@ -216,6 +216,7 @@ NPC::NPC(const NPCType* d, Spawn2* in_respawn, float x, float y, float z, float
roambox_min_y = -2; roambox_min_y = -2;
roambox_movingto_x = -2; roambox_movingto_x = -2;
roambox_movingto_y = -2; roambox_movingto_y = -2;
roambox_min_delay = 1000;
roambox_delay = 1000; roambox_delay = 1000;
org_heading = heading; org_heading = heading;
p_depop = false; p_depop = false;
@ -1526,6 +1527,12 @@ void Mob::NPCSpecialAttacks(const char* parse, int permtag, bool reset, bool rem
case 'i': case 'i':
SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1); SetSpecialAbility(IMMUNE_TAUNT, remove ? 0 : 1);
break; break;
case 'e':
SetSpecialAbility(ALWAYS_FLEE, remove ? 0 : 1);
break;
case 'h':
SetSpecialAbility(FLEE_PERCENT, remove ? 0 : 1);
break;
default: default:
break; break;
@ -1686,7 +1693,14 @@ bool Mob::HasNPCSpecialAtk(const char* parse) {
HasAllAttacks = false; HasAllAttacks = false;
} }
break; break;
case 'e':
if(!GetSpecialAbility(ALWAYS_FLEE))
HasAllAttacks = false;
break;
case 'h':
if(!GetSpecialAbility(FLEE_PERCENT))
HasAllAttacks = false;
break;
default: default:
HasAllAttacks = false; HasAllAttacks = false;
break; break;

View File

@ -265,8 +265,8 @@ public:
inline bool IsGuarding() const { return(guard_heading != 0); } inline bool IsGuarding() const { return(guard_heading != 0); }
void SaveGuardSpotCharm(); void SaveGuardSpotCharm();
void RestoreGuardSpotCharm(); void RestoreGuardSpotCharm();
void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500); void AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500); void AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay = 2500, uint32 iMinDelay = 2500);
//mercenary stuff //mercenary stuff
void LoadMercTypes(); void LoadMercTypes();
@ -430,6 +430,7 @@ protected:
float roambox_movingto_x; float roambox_movingto_x;
float roambox_movingto_y; float roambox_movingto_y;
uint32 roambox_delay; uint32 roambox_delay;
uint32 roambox_min_delay;
uint16 skills[HIGHEST_SKILL+1]; uint16 skills[HIGHEST_SKILL+1];
uint32 equipment[MAX_WORN_INVENTORY]; //this is an array of item IDs uint32 equipment[MAX_WORN_INVENTORY]; //this is an array of item IDs

View File

@ -1433,8 +1433,8 @@ XS(XS_NPC_AI_SetRoambox); /* prototype to pass -Wmissing-prototypes */
XS(XS_NPC_AI_SetRoambox) XS(XS_NPC_AI_SetRoambox)
{ {
dXSARGS; dXSARGS;
if (items < 6 || items > 7) if (items < 6 || items > 8)
Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500)"); Perl_croak(aTHX_ "Usage: NPC::AI_SetRoambox(THIS, iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay= 2500, iMinDelay= 2500)");
{ {
NPC * THIS; NPC * THIS;
float iDist = (float)SvNV(ST(1)); float iDist = (float)SvNV(ST(1));
@ -1443,6 +1443,7 @@ XS(XS_NPC_AI_SetRoambox)
float iMaxY = (float)SvNV(ST(4)); float iMaxY = (float)SvNV(ST(4));
float iMinY = (float)SvNV(ST(5)); float iMinY = (float)SvNV(ST(5));
uint32 iDelay; uint32 iDelay;
uint32 iMinDelay;
if (sv_derived_from(ST(0), "NPC")) { if (sv_derived_from(ST(0), "NPC")) {
IV tmp = SvIV((SV*)SvRV(ST(0))); IV tmp = SvIV((SV*)SvRV(ST(0)));
@ -1453,13 +1454,20 @@ XS(XS_NPC_AI_SetRoambox)
if(THIS == nullptr) if(THIS == nullptr)
Perl_croak(aTHX_ "THIS is nullptr, avoiding crash."); Perl_croak(aTHX_ "THIS is nullptr, avoiding crash.");
if (items < 7) if (items < 7){
iMinDelay = 2500;
iDelay = 2500; iDelay = 2500;
else { }
else if (items < 8){
iMinDelay = 2500;
iDelay = (uint32)SvUV(ST(6)); iDelay = (uint32)SvUV(ST(6));
} }
else {
iDelay = (uint32)SvUV(ST(6));
iMinDelay = (uint32)SvUV(ST(7));
}
THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay); THIS->AI_SetRoambox(iDist, iMaxX, iMinX, iMaxY, iMinY, iDelay, iMinDelay);
} }
XSRETURN_EMPTY; XSRETURN_EMPTY;
} }
@ -2208,7 +2216,7 @@ XS(boot_NPC)
newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$"); newXSproto(strcpy(buf, "NextGuardPosition"), XS_NPC_NextGuardPosition, file, "$");
newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$"); newXSproto(strcpy(buf, "SaveGuardSpot"), XS_NPC_SaveGuardSpot, file, "$;$");
newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$"); newXSproto(strcpy(buf, "IsGuarding"), XS_NPC_IsGuarding, file, "$");
newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$"); newXSproto(strcpy(buf, "AI_SetRoambox"), XS_NPC_AI_SetRoambox, file, "$$$$$$;$$");
newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$"); newXSproto(strcpy(buf, "GetNPCSpellsID"), XS_NPC_GetNPCSpellsID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointID"), XS_NPC_GetSpawnPointID, file, "$");
newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$"); newXSproto(strcpy(buf, "GetSpawnPointX"), XS_NPC_GetSpawnPointX, file, "$");

View File

@ -347,8 +347,8 @@ Mob* QuestManager::spawn_from_spawn2(uint32 spawn2_id)
entity_list.AddNPC(npc); entity_list.AddNPC(npc);
entity_list.LimitAddNPC(npc); entity_list.LimitAddNPC(npc);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
if(zone->InstantGrids()) if(zone->InstantGrids())
{ {
found_spawn->LoadGrid(); found_spawn->LoadGrid();

View File

@ -229,8 +229,8 @@ bool Spawn2::Process() {
entity_list.AddNPC(npc); entity_list.AddNPC(npc);
//this limit add must be done after the AddNPC since we need the entity ID. //this limit add must be done after the AddNPC since we need the entity ID.
entity_list.LimitAddNPC(npc); entity_list.LimitAddNPC(npc);
if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay) if(sg->roamdist && sg->roambox[0] && sg->roambox[1] && sg->roambox[2] && sg->roambox[3] && sg->delay && sg->min_delay)
npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay); npc->AI_SetRoambox(sg->roamdist,sg->roambox[0],sg->roambox[1],sg->roambox[2],sg->roambox[3],sg->delay,sg->min_delay);
if(zone->InstantGrids()) { if(zone->InstantGrids()) {
_log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z); _log(SPAWNS__MAIN, "Spawn2 %d: Group %d spawned %s (%d) at (%.3f, %.3f, %.3f).", spawn2_id, spawngroup_id_, npc->GetName(), npcid, x, y, z);
LoadGrid(); LoadGrid();

View File

@ -35,7 +35,7 @@ SpawnEntry::SpawnEntry( uint32 in_NPCType, int in_chance, uint8 in_npc_spawn_lim
npc_spawn_limit = in_npc_spawn_limit; npc_spawn_limit = in_npc_spawn_limit;
} }
SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ) { SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in ) {
id = in_id; id = in_id;
strn0cpy( name_, name, 120); strn0cpy( name_, name, 120);
group_spawn_limit = in_group_spawn_limit; group_spawn_limit = in_group_spawn_limit;
@ -44,6 +44,7 @@ SpawnGroup::SpawnGroup( uint32 in_id, char* name, int in_group_spawn_limit, floa
roambox[2]=maxy; roambox[2]=maxy;
roambox[3]=miny; roambox[3]=miny;
roamdist=dist; roamdist=dist;
min_delay=min_delay_in;
delay=delay_in; delay=delay_in;
despawn=despawn_in; despawn=despawn_in;
despawn_timer=despawn_timer_in; despawn_timer=despawn_timer_in;
@ -150,11 +151,11 @@ bool ZoneDatabase::LoadSpawnGroups(const char* zone_name, uint16 version, SpawnG
// CODER new spawn code // CODER new spawn code
query = 0; query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result)) if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT(spawngroupID), spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawn2,spawngroup WHERE spawn2.spawngroupID=spawngroup.ID and spawn2.version=%u and zone='%s'", version, zone_name), errbuf, &result))
{ {
safe_delete_array(query); safe_delete_array(query);
while((row = mysql_fetch_row(result))) { while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10])); SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup); spawn_group_list->AddSpawnGroup(newSpawnGroup);
} }
mysql_free_result(result); mysql_free_result(result);
@ -205,11 +206,11 @@ bool ZoneDatabase::LoadSpawnGroupsByID(int spawngroupid, SpawnGroupList* spawn_g
// CODER new spawn code // CODER new spawn code
query = 0; query = 0;
if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result)) if (RunQuery(query, MakeAnyLenString(&query, "SELECT DISTINCT spawngroup.id, spawngroup.name, spawngroup.spawn_limit, spawngroup.dist, spawngroup.max_x, spawngroup.min_x, spawngroup.max_y, spawngroup.min_y, spawngroup.delay, spawngroup.despawn, spawngroup.despawn_timer, spawngroup.mindelay FROM spawngroup WHERE spawngroup.ID='%i'", spawngroupid), errbuf, &result))
{ {
safe_delete_array(query); safe_delete_array(query);
while((row = mysql_fetch_row(result))) { while((row = mysql_fetch_row(result))) {
SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10])); SpawnGroup* newSpawnGroup = new SpawnGroup( atoi(row[0]), row[1], atoi(row[2]), atof(row[3]), atof(row[4]), atof(row[5]), atof(row[6]), atof(row[7]), atoi(row[8]), atoi(row[9]), atoi(row[10]), atoi(row[11]));
spawn_group_list->AddSpawnGroup(newSpawnGroup); spawn_group_list->AddSpawnGroup(newSpawnGroup);
} }
mysql_free_result(result); mysql_free_result(result);

View File

@ -39,13 +39,14 @@ public:
class SpawnGroup class SpawnGroup
{ {
public: public:
SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in ); SpawnGroup(uint32 in_id, char* name, int in_group_spawn_limit, float dist, float maxx, float minx, float maxy, float miny, int delay_in, int despawn_in, uint32 despawn_timer_in, int min_delay_in );
~SpawnGroup(); ~SpawnGroup();
uint32 GetNPCType(); uint32 GetNPCType();
void AddSpawnEntry( SpawnEntry* newEntry ); void AddSpawnEntry( SpawnEntry* newEntry );
uint32 id; uint32 id;
float roamdist; float roamdist;
float roambox[4]; float roambox[4];
int min_delay;
int delay; int delay;
int despawn; int despawn;
uint32 despawn_timer; uint32 despawn_timer;

View File

@ -47,11 +47,11 @@ static inline float ABS(float x) {
return(x); return(x);
} }
void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay) { void NPC::AI_SetRoambox(float iDist, float iRoamDist, uint32 iDelay, uint32 iMinDelay) {
AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay); AI_SetRoambox(iDist, GetX()+iRoamDist, GetX()-iRoamDist, GetY()+iRoamDist, GetY()-iRoamDist, iDelay, iMinDelay);
} }
void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay) { void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, float iMinY, uint32 iDelay, uint32 iMinDelay) {
roambox_distance = iDist; roambox_distance = iDist;
roambox_max_x = iMaxX; roambox_max_x = iMaxX;
roambox_min_x = iMinX; roambox_min_x = iMinX;
@ -59,6 +59,7 @@ void NPC::AI_SetRoambox(float iDist, float iMaxX, float iMinX, float iMaxY, floa
roambox_min_y = iMinY; roambox_min_y = iMinY;
roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc roambox_movingto_x = roambox_max_x + 1; // this will trigger a recalc
roambox_delay = iDelay; roambox_delay = iDelay;
roambox_min_delay = iMinDelay;
} }
void NPC::DisplayWaypointInfo(Client *c) { void NPC::DisplayWaypointInfo(Client *c) {