Fixed a zone crash in spawn_conditions.

spawn_events changes, fixes, and additions.
This commit is contained in:
cavedude00 2014-04-25 12:40:25 -07:00
parent 4d3ba5087d
commit d8ad337c0e
9 changed files with 140 additions and 74 deletions

View File

@ -1,5 +1,16 @@
EQEMu Changelog (Started on Sept 24, 2003 15:50) EQEMu Changelog (Started on Sept 24, 2003 15:50)
------------------------------------------------------- -------------------------------------------------------
== 04/25/2014 ==
cavedude: Corrected a crash in spawn_conditions caused by NPCs on a one way path.
cavedude: Added strict column to spawn_events which will prevent an event from enabling if it's mid-cycle.
cavedude: Prevented disabled or strict spawn_events from enabling when the zone first boots.
cavedude: Fixed the quest function toggle_spawn_event under Perl.
If you're using the quest function toggle_spawn_event (worked on Lua only) it has changed syntax to:
toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base)
Required SQL: utils/sql/git/required/2014_04_25_spawn_events.sql
== 04/23/2014 == == 04/23/2014 ==
Kayen: Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc. Kayen: Improved SE_LimitCombatSkills will now more accurately determine if a spell is a combat proc.
Kayen: SE_LimitInstant will now also work when set to include instant spells. Kayen: SE_LimitInstant will now also work when set to include instant spells.

View File

@ -0,0 +1 @@
alter table spawn_events add column `strict` tinyint(4) not null default 0;

View File

@ -1627,11 +1627,32 @@ void NPC::AI_DoMovement() {
if (gridno > 0 || cur_wp==-2) { if (gridno > 0 || cur_wp==-2) {
if (movetimercompleted==true) { // time to pause at wp is over 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()) { if (wandertype == 4 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(true); //depop and resart spawn timer CastToNPC()->Depop(true); //depop and resart spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
} }
else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) { else if (wandertype == 6 && cur_wp == CastToNPC()->GetMaxWp()) {
CastToNPC()->Depop(false);//depop without spawn timer CastToNPC()->Depop(false);//depop without spawn timer
if(found_spawn)
found_spawn->SetNPCPointerNull();
} }
else { else {
movetimercompleted=false; movetimercompleted=false;

View File

@ -1678,14 +1678,15 @@ XS(XS__toggle_spawn_event);
XS(XS__toggle_spawn_event) XS(XS__toggle_spawn_event)
{ {
dXSARGS; dXSARGS;
if (items != 2) if (items != 4)
Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, reset_base)"); Perl_croak(aTHX_ "Usage: toggle_spawn_event(event_id, enabled?, strict, reset_base)");
uint32 event_id = (int)SvIV(ST(0)); uint32 event_id = (int)SvIV(ST(0));
bool enabled = ((int)SvIV(ST(1))) == 0?false:true; 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; XSRETURN_EMPTY;
} }

View File

@ -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); return quest_manager.get_spawn_condition(zone, instance_id, condition_id);
} }
void lua_toggle_spawn_event(int event_id, bool enable, bool reset) { void lua_toggle_spawn_event(int event_id, bool enable, bool strict, bool reset) {
quest_manager.toggle_spawn_event(event_id, enable, 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) { void lua_summon_burried_player_corpse(uint32 char_id, float x, float y, float z, float h) {

View File

@ -1733,8 +1733,8 @@ short QuestManager::get_spawn_condition(const char *zone_short, uint32 instance_
} }
//toggle a spawn event //toggle a spawn event
void QuestManager::toggle_spawn_event(int event_id, bool enable, bool reset_base) { void QuestManager::toggle_spawn_event(int event_id, bool enable, bool strict, bool reset_base) {
zone->spawn_conditions.ToggleEvent(event_id, enable, reset_base); zone->spawn_conditions.ToggleEvent(event_id, enable, strict, reset_base);
} }
bool QuestManager::has_zone_flag(int zone_id) { bool QuestManager::has_zone_flag(int zone_id) {

View File

@ -150,7 +150,7 @@ public:
void showgrid(int gridid); void showgrid(int gridid);
void spawn_condition(const char *zone_short, uint32 instance_id, uint16 condition_id, short new_value); 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); 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); bool has_zone_flag(int zone_id);
void set_zone_flag(int zone_id); void set_zone_flag(int zone_id);
void clear_zone_flag(int zone_id); void clear_zone_flag(int zone_id);

View File

@ -303,11 +303,13 @@ void Spawn2::ForceDespawn()
{ {
npcthis->Depop(true); npcthis->Depop(true);
IsDespawned = true; IsDespawned = true;
npcthis = nullptr;
return; return;
} }
else else
{ {
npcthis->Depop(false); npcthis->Depop(false);
npcthis = nullptr;
} }
} }
} }
@ -555,6 +557,7 @@ SpawnEvent::SpawnEvent() {
action = ActionSet; action = ActionSet;
argument = 0; argument = 0;
period = 0xFFFFFFFF; period = 0xFFFFFFFF;
strict = false;
memset(&next, 0, sizeof(next)); memset(&next, 0, sizeof(next));
} }
@ -586,13 +589,14 @@ void SpawnConditionManager::Process() {
for(; cur != end; ++cur) { for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur; SpawnEvent &cevent = *cur;
if(!cevent.enabled) if(cevent.enabled)
continue; {
if(EQTime::IsTimeBefore(&tod, &cevent.next)) { if(EQTime::IsTimeBefore(&tod, &cevent.next)) {
//this event has been triggered. //this event has been triggered.
//execute the event //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); ExecEvent(cevent, true);
//add the period of the event to the trigger time //add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next); EQTime::AddMinutes(cevent.period, &cevent.next);
std::string t; std::string t;
@ -610,6 +614,7 @@ void SpawnConditionManager::Process() {
} }
} }
} }
}
void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) { void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
std::map<uint16, SpawnCondition>::iterator condi; std::map<uint16, SpawnCondition>::iterator condi;
@ -619,6 +624,14 @@ void SpawnConditionManager::ExecEvent(SpawnEvent &event, bool send_update) {
return; //unable to find the spawn condition to operate on 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; SpawnCondition &cond = condi->second;
int16 new_value = cond.value; int16 new_value = cond.value;
@ -666,10 +679,10 @@ void SpawnConditionManager::UpdateDBEvent(SpawnEvent &event) {
len = MakeAnyLenString(&query, len = MakeAnyLenString(&query,
"UPDATE spawn_events SET " "UPDATE spawn_events SET "
"next_minute=%d, next_hour=%d, next_day=%d, next_month=%d, " "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", "WHERE id=%d",
event.next.minute, event.next.hour, event.next.day, event.next.month, 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)) { if(!database.RunQuery(query, len, errbuf)) {
LogFile->write(EQEMuLog::Error, "Unable to update spawn event '%s': %s\n", query, 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; bool ret = false;
len = MakeAnyLenString(&query, 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); "FROM spawn_events WHERE id=%d", event_id);
if (database.RunQuery(query, len, errbuf, &result)) { if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query); 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.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]); event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]); event.argument = atoi(row[10]);
zone_name = row[11]; event.strict = atoi(row[11])==0?false:true;
zone_name = row[12];
std::string t; std::string t;
EQTime::ToString(&event.next, 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", _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, t.c_str()); event.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict, t.c_str());
ret = true; ret = true;
} }
@ -794,7 +808,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
//load spawn events //load spawn events
SpawnEvent event; SpawnEvent event;
len = MakeAnyLenString(&query, 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); "FROM spawn_events WHERE zone='%s'", zone_name);
if (database.RunQuery(query, len, errbuf, &result)) { if (database.RunQuery(query, len, errbuf, &result)) {
safe_delete_array(query); 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.enabled = atoi(row[8])==0?false:true;
event.action = (SpawnEvent::Action) atoi(row[9]); event.action = (SpawnEvent::Action) atoi(row[9]);
event.argument = atoi(row[10]); event.argument = atoi(row[10]);
event.strict = atoi(row[11])==0?false:true;
spawn_events.push_back(event); spawn_events.push_back(event);
_log(SPAWNS__CONDITIONS, "Loaded %s spawn event %d on condition %d with period %d, action %d, argument %d", _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.enabled?"enabled":"disabled", event.id, event.condition_id, event.period, event.action, event.argument, event.strict);
} }
mysql_free_result(result); mysql_free_result(result);
} else { } else {
@ -847,9 +862,20 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
for(; cur != end; ++cur) { for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur; SpawnEvent &cevent = *cur;
if(!cevent.enabled) bool StrictCheck = false;
continue; 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;
//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);
if(cevent.enabled)
{
//watch for special case of all 0s, which means to reset next to now //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) { 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); _log(SPAWNS__CONDITIONS, "Initial next trigger time set for spawn event %d", cevent.id);
@ -866,7 +892,9 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
_log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id); _log(SPAWNS__CONDITIONS, "Catch up triggering on event %d", cevent.id);
//this event has been triggered. //this event has been triggered.
//execute the event //execute the event
if(!cevent.strict || StrictCheck)
ExecEvent(cevent, false); ExecEvent(cevent, false);
//add the period of the event to the trigger time //add the period of the event to the trigger time
EQTime::AddMinutes(cevent.period, &cevent.next); EQTime::AddMinutes(cevent.period, &cevent.next);
ran = true; ran = true;
@ -877,6 +905,7 @@ bool SpawnConditionManager::LoadSpawnConditions(const char* zone_name, uint32 in
UpdateDBEvent(cevent); UpdateDBEvent(cevent);
} }
} }
}
//now our event timers are all up to date, find our closest event. //now our event timers are all up to date, find our closest event.
FindNearestEvent(); FindNearestEvent();
@ -894,16 +923,16 @@ void SpawnConditionManager::FindNearestEvent() {
int next_id = -1; int next_id = -1;
for(; cur != end; ++cur) { for(; cur != end; ++cur) {
SpawnEvent &cevent = *cur; SpawnEvent &cevent = *cur;
if(cevent.enabled)
if(!cevent.enabled) {
continue;
//see if this event is before our last nearest //see if this event is before our last nearest
if(EQTime::IsTimeBefore(&next_event, &cevent.next)) { if(EQTime::IsTimeBefore(&next_event, &cevent.next))
{
memcpy(&next_event, &cevent.next, sizeof(next_event)); memcpy(&next_event, &cevent.next, sizeof(next_event));
next_id = cevent.id; next_id = cevent.id;
} }
} }
}
if(next_id == -1) if(next_id == -1)
_log(SPAWNS__CONDITIONS, "No spawn events enabled. Disabling next event."); _log(SPAWNS__CONDITIONS, "No spawn events enabled. Disabling next event.");
else else
@ -1035,7 +1064,7 @@ void SpawnConditionManager::ReloadEvent(uint32 event_id) {
FindNearestEvent(); 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 "); _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) { if(cevent.id == event_id) {
//make sure were actually changing something //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.enabled = enabled;
cevent.strict = strict;
if(reset_base) { if(reset_base) {
_log(SPAWNS__CONDITIONS, "Spawn event %d located in this zone. State set. Trigger time reset (period %d).", event_id, cevent.period); _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 //start with the time now

View File

@ -67,6 +67,7 @@ public:
bool NPCPointerValid() { return (npcthis!=nullptr); } bool NPCPointerValid() { return (npcthis!=nullptr); }
void SetNPCPointer(NPC* n) { npcthis = n; } void SetNPCPointer(NPC* n) { npcthis = n; }
void SetNPCPointerNull() { npcthis = nullptr; }
void SetTimer(uint32 duration) { timer.Start(duration); } void SetTimer(uint32 duration) { timer.Start(duration); }
uint32 GetKillCount() { return killcount; } uint32 GetKillCount() { return killcount; }
protected: protected:
@ -134,6 +135,7 @@ public:
bool enabled; bool enabled;
Action action; Action action;
int16 argument; int16 argument;
bool strict;
uint32 period; //eq minutes (3 seconds) between events uint32 period; //eq minutes (3 seconds) between events
TimeOfDay_Struct next; //next time this event triggers 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); 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 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); bool Check(uint16 condition, int16 min_value);
void ReloadEvent(uint32 event_id); void ReloadEvent(uint32 event_id);