Hour-based Waypoint Walker version 1.2
This is the second update to my Hour-based Waypoint Walker. It includes
a couple bug fixes, a change to help with "NPC getting stuck in a
PC-less area" situations, a small change to the numbering (now use 00-23
for the hours instead of 01-24--and use 24 for 24-hour waypoints instead
of 00), and support for ambient animations at posts. See the
introductory comments for details.
If you're installing this for the first time in your module, follow the
directions in the comments.
If you're upgrading from version 1.1, just replace your rh_inc_hrwpoints
and your heartbeat script with the new ones posted below, change any
existing 24-hour waypoints to 24 instead of 00 (e.g. what was
POST_00_NPCTag should now be POST_24_NPCTag), and change any existing
midnight waypoints from 24 to 00 (e.g. what was WP_24_NPCTag_01 should
now be WP_00_NPCTag_01).
If you're upgrading from version 1.0, follow the directions for
upgrading to 1.1 using the scripts in this post instead of the 1.1
scripts, and, as directed above, update any existing tags for 24-hour or
midnight waypoints/posts. The instructions for upgrading to version 1.1
can be found
here (third post from the top).
Check the "common issues" section of the comments for tips on dealing
with some tricky situations. This should be relatively bug free now, but
if you think you've found one, feel free to E-mail me or send me a
message a forum message.
Here are the scripts:
rh_inc_hrwpoints:
NWScript:
void HourWayPoints(int nRun = FALSE, float fPause = 1.0, int nCircle = FALSE);
void HourRunCircuit(string sPrefix, int nWPStart = 1, int nReverse = FALSE, int nRun=FALSE, float fPause = 1.0, int nCircle = FALSE);
void HourGoToPost(object oPost, int nRun = FALSE);
int GetEventForWP(object oWP, int nNum);
string PosIntTo2DigitString(int nNum);
float GetFacingCorrectly(object oTarget);
void HourWayPoints(int nRun = FALSE, float fPause = 1.0, int nCircle = FALSE)
{
if (GetLocalInt(OBJECT_SELF, "nDontWalkWP"))
return;
string sWalker = GetTag(OBJECT_SELF);
string sWPPrefix;
string sPostPrefix;
int nWPStart = GetLocalInt(OBJECT_SELF, "nNextWP");
int nDay;
object oWP;
object oPost;
ClearAllActions();
sWPPrefix = "WP_24_";
sPostPrefix = "POST_24_";
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
if (GetIsObjectValid(oWP)) {
if (!nWPStart)
nWPStart = 1;
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
} else if (GetIsObjectValid(oPost)) {
HourGoToPost(oPost, nRun);
return;
}
if (GetIsDay() || GetIsDawn()) {
sWPPrefix = "WP_DY_";
sPostPrefix = "POST_DY_";
nDay = 1;
} else {
sWPPrefix = "WP_NT_";
sPostPrefix = "POST_NT_";
nDay = 2;
}
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
if (GetIsObjectValid(oWP)) {
int nOldDay = GetLocalInt(OBJECT_SELF, "nDayPoints");
if (!(nWPStart && nOldDay && nOldDay == nDay)) {
SetLocalInt(OBJECT_SELF, "nDayPoints", nDay);
nWPStart = 1;
}
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
} else if (GetIsObjectValid(oPost)) {
SetLocalInt(OBJECT_SELF, "nDayPoints", nDay);
HourGoToPost(oPost);
return;
}
int nHour = GetTimeHour();
sWPPrefix = "WP_" + PosIntTo2DigitString(nHour) + "_";
sPostPrefix = "POST_" + PosIntTo2DigitString(nHour) + "_";
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
if (!GetLocalInt(OBJECT_SELF, "nStartedWaypoints")) {
if (nHour == 23)
SetLocalInt(OBJECT_SELF, "nLastHourWalked", 0);
else
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour + 1);
}
while (nHour != GetLocalInt(OBJECT_SELF, "nLastHourWalked")) {
if (GetIsObjectValid(oWP)) {
nWPStart = 1;
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
SetLocalInt(OBJECT_SELF, "nStartedWaypoints", TRUE);
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
} else if (GetIsObjectValid(oPost)) {
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
SetLocalInt(OBJECT_SELF, "nStartedWaypoints", TRUE);
HourGoToPost(oPost, nRun);
return;
} else {
nHour--;
if (nHour < 0)
nHour = 23;
sWPPrefix = "WP_" + PosIntTo2DigitString(nHour) + "_";
sPostPrefix = "POST_" + PosIntTo2DigitString(nHour) + "_";
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
}
}
if (GetIsObjectValid(oWP)) {
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
SetLocalInt(OBJECT_SELF, "nStartedWaypoints", TRUE);
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
}
if (GetIsObjectValid(oPost)) {
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
SetLocalInt(OBJECT_SELF, "nStartedWaypoints", TRUE);
HourGoToPost(oPost, nRun);
return;
}
}
void HourRunCircuit(string sPrefix, int nNext = 1, int nReverse=FALSE, int nRun=FALSE, float fPause = 1.0, int nCircle = FALSE)
{
string sWalker = GetTag(OBJECT_SELF);
object oWP = GetWaypointByTag(sPrefix + sWalker + "_" + PosIntTo2DigitString(nNext));
object oPost = GetWaypointByTag("POST_" + GetSubString(sPrefix, 3, 3) + sWalker);
object oArea = GetArea(OBJECT_SELF);
object oNearestPCArea = GetArea(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF));
if (GetIsObjectValid(oWP)) {
SetLocalInt(OBJECT_SELF, "nNextWP", nNext);
SetLocalInt(OBJECT_SELF, "nAmbientAnimations", 0);
if (oArea == oNearestPCArea)
ActionForceMoveToObject(oWP, nRun, 1.0, 60.0);
else
ActionForceMoveToObject(oWP, nRun, 1.0, 20.0);
ActionWait(fPause);
SignalEvent(OBJECT_SELF, EventUserDefined(GetEventForWP(oWP, nNext)));
if (!nReverse)
nNext++;
else
nNext--;
} else if (GetIsObjectValid(oPost)) {
ActionDoCommand(HourGoToPost(oPost, nRun));
return;
} else {
if (nCircle) {
nReverse = FALSE;
nNext = 1;
} else {
nReverse = !nReverse;
if (!nReverse)
nNext += 2;
else
nNext -= 2;
}
}
ActionDoCommand(HourRunCircuit(sPrefix, nNext, nReverse, nRun, fPause, nCircle));
}
void HourGoToPost(object oPost, int nRun = FALSE)
{
object oArea = GetArea(OBJECT_SELF);
object oNearestPCArea = GetArea(GetNearestCreature(CREATURE_TYPE_PLAYER_CHAR, PLAYER_CHAR_IS_PC, OBJECT_SELF));
SetLocalInt(OBJECT_SELF, "nNextWP", 100);
SetLocalInt(OBJECT_SELF, "nAmbientAnimations", 0);
if (oArea == oNearestPCArea)
ActionForceMoveToObject(oPost, nRun, 1.0, 60.0);
else
ActionForceMoveToObject(oPost, nRun, 1.0, 20.0);
float fFacing = GetFacingCorrectly(oPost);
ActionDoCommand(SetFacing(fFacing));
SignalEvent(OBJECT_SELF, EventUserDefined(GetEventForWP(oPost, 0)));
}
string PosIntTo2DigitString(int nNum)
{
string sString;
if (nNum > 9)
return IntToString(nNum);
else if (nNum < 10 && nNum >= 0)
return ("0" + IntToString(nNum));
else
return "This function is not for negative numbers.";
}
float GetFacingCorrectly(object oTarget)
{
float fFacing;
fFacing = GetFacing(oTarget);
if (fFacing >= 360.0)
fFacing = 720.0 - fFacing;
return fFacing;
}
int GetEventForWP(object oWP, int nNum)
{
string sTime;
string sNum;
string sEventNum;
int nEventNum;
if (!nNum) {
sNum = "00";
sTime = GetSubString(GetTag(oWP), 5, 2);
} else {
sNum = GetStringRight(GetTag(oWP), 2);
sTime = GetSubString(GetTag(oWP), 3, 2);
}
if (sTime == "DY")
sTime = "25";
else if (sTime == "NT")
sTime = "26";
sEventNum = "9" + sTime + sNum;
nEventNum = StringToInt(sEventNum);
return nEventNum;
}
The OnHeartbeat script:
NWScript:
#include "MY_I0_GENERIC"
void main()
{
if(GetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY))
{
if(TalentAdvancedBuff(40.0))
{
SetSpawnInCondition(NW_FLAG_FAST_BUFF_ENEMY, FALSE);
return;
}
}
if (GetLocalInt(OBJECT_SELF, "nDayPoints")) {
int nDay;
if (GetIsDay() || GetIsDawn())
nDay = 1;
else
nDay = 2;
if (GetLocalInt(OBJECT_SELF, "nDayPoints") != nDay)
SignalEvent(OBJECT_SELF, EventUserDefined(2000));
}
int nHour = GetTimeHour();
int nLastHourChecked = GetLocalInt(OBJECT_SELF, "nLastHourChecked");
if (GetLocalInt(OBJECT_SELF, "nStartedWaypoints") && nHour != nLastHourChecked
&& !GetIsInCombat(OBJECT_SELF) && !IsInConversation(OBJECT_SELF)) {
string sWalker = GetTag(OBJECT_SELF);
object oNewWP;
object oNewPost;
while (nHour != nLastHourChecked) {
oNewWP = GetObjectByTag("WP_" + PosIntTo2DigitString(nHour) + "_" + sWalker + "_01");
oNewPost = GetObjectByTag("POST_" + PosIntTo2DigitString(nHour) + "_" + sWalker);
if (GetIsObjectValid(oNewWP) || GetIsObjectValid(oNewPost)) {
nHour = GetTimeHour();
nLastHourChecked = nHour;
} else {
nHour--;
if (nHour < 0)
nHour = 23;
}
}
if (GetIsObjectValid(oNewWP) || GetIsObjectValid(oNewPost))
SignalEvent(OBJECT_SELF, EventUserDefined(2000));
else
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
}
if(!GetHasEffect(EFFECT_TYPE_SLEEP))
{
if(GetLocalInt(OBJECT_SELF, "nAmbientAnimations"))
{
if(!GetIsObjectValid(GetAttemptedAttackTarget()) && !GetIsObjectValid(GetAttemptedSpellTarget()))
{
if(!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN)))
{
if(!GetBehaviorState(NW_FLAG_BEHAVIOR_SPECIAL) && !IsInConversation(OBJECT_SELF))
{
if(GetLocalInt(OBJECT_SELF, "nAmbientAnimations") == 1)
{
PlayMobileAmbientAnimations();
}
else if(GetIsEncounterCreature() &&
!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN)))
{
PlayMobileAmbientAnimations();
}
else if(GetLocalInt(OBJECT_SELF, "nAmbientAnimations") == 2 &&
!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN)))
{
PlayImmobileAmbientAnimations();
}
}
else
{
DetermineSpecialBehavior();
}
}
else
{
}
}
}
}
else
{
if(GetSpawnInCondition(NW_FLAG_SLEEPING_AT_NIGHT))
{
effect eVis = EffectVisualEffect(VFX_IMP_SLEEP);
if(d10() > 6)
{
ApplyEffectToObject(DURATION_TYPE_INSTANT, eVis, OBJECT_SELF);
}
}
}
if(GetSpawnInCondition(NW_FLAG_HEARTBEAT_EVENT)) {
SignalEvent(OBJECT_SELF, EventUserDefined(1001));
}
}
Finally, the OnUserDefined script (add case statements for your own
userdefined events, of course):
NWScript:
#include "MY_I0_GENERIC"
void main()
{
int nEvent = GetUserDefinedEventNumber();
switch(nEvent)
{
case 2000:
HourWayPoints(FALSE, 1.0, FALSE);
break;
}
}
_________________
"In the global sense, bringing peace means either appeasing the needs
of--or, more likely, destroying--everyone." --Guardian of Helm
|