1. Name: Hour-based Waypoint Walker
2. What it does: This is a complete rewrite of WalkWayPoints with many
features, the most notable being the ability to create daily schedules
for NPCs by using waypoints and posts for different hours of the day.
You can assign scripts for each waypoint (they all go in the NPC's
OnUserDefined script, so you don't have to make a script for every
waypoint/post). You can also use circular waypoints, and terminal
waypoints (i.e. NPC goes through his waypoints once and then stops at a
post). It currently uses a GetFacingCorrectly function to wrap GetFacing
as a workaround for the latter function's bug, so NPCs will face the
right direction at posts. See the introductory comments for more
details, including instructions for setting it up in your module.
3. Notes: I've also included an OnHeartbeat script which is the same as
the default OnHeartbeat, with the exception that the previous
waypoint-related code is changed with code for my waypoint system.
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);
int FindNearestWPNum(string sWPPrefix, string sPostPrefix);
string PosIntTo2DigitString(int nNum);
float GetFacingCorrectly(object oTarget);
void HourWayPoints(int nRun = FALSE, float fPause = 1.0, int nCircle = 0)
{
if (GetLocalInt(OBJECT_SELF, "nDontWalkWP"))
return;
string sWalker = GetTag(OBJECT_SELF);
string sWPPrefix;
string sPostPrefix;
int nWPStart;
int nDay;
object oWP;
object oPost;
ClearAllActions();
sWPPrefix = "WP_00_";
sPostPrefix = "POST_00_";
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
if (GetIsObjectValid(oWP)) {
nWPStart = FindNearestWPNum(sWPPrefix, sPostPrefix);
if (nWPStart == 0)
HourGoToPost(oPost);
else
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
}
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)) {
nWPStart = FindNearestWPNum(sWPPrefix, sPostPrefix);
if (nWPStart == 0) {
SetLocalInt(OBJECT_SELF, "nDayPoints", nDay);
HourGoToPost(oPost);
} else {
SetLocalInt(OBJECT_SELF, "nDayPoints", nDay);
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
}
return;
}
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, "nLastHourWalked"))
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour + 1);
while (nHour != GetLocalInt(OBJECT_SELF, "nLastHourWalked")) {
if (GetIsObjectValid(oWP)) {
nWPStart = FindNearestWPNum(sWPPrefix, sPostPrefix);
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
if (nWPStart == 0)
HourGoToPost(oPost, nRun);
else
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
} else if (GetIsObjectValid(oPost)) {
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
HourGoToPost(oPost, nRun);
return;
} else {
nHour--;
if (nHour == 0)
nHour = 24;
sWPPrefix = "WP_" + PosIntTo2DigitString(nHour) + "_";
sPostPrefix = "POST_" + PosIntTo2DigitString(nHour) + "_";
oWP = GetObjectByTag(sWPPrefix + sWalker + "_01");
oPost = GetObjectByTag(sPostPrefix + sWalker);
}
}
if (GetIsObjectValid(oWP)) {
nWPStart = FindNearestWPNum(sWPPrefix, sPostPrefix);
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
if (nWPStart == 0)
HourGoToPost(oPost, nRun);
else
HourRunCircuit(sWPPrefix, nWPStart, FALSE, nRun, fPause, nCircle);
return;
}
if (GetIsObjectValid(oPost)) {
SetLocalInt(OBJECT_SELF, "nLastHourWalked", nHour);
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
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);
if (GetIsObjectValid(oWP)) {
ActionMoveToObject(oWP, nRun);
SignalEvent(OBJECT_SELF, EventUserDefined(GetEventForWP(oWP, nNext)));
ActionWait(fPause);
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)
{
ActionForceMoveToObject(oPost, nRun, 1.0, 60.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 FindNearestWPNum(string sWPPrefix, string sPostPrefix)
{
string sWalker = GetTag(OBJECT_SELF);
int nWPStart = 1;
int nNth = 1;
object oNearest = GetNearestObject(OBJECT_TYPE_WAYPOINT, OBJECT_SELF, nNth);
string sNearest = GetTag(oNearest);
while (GetIsObjectValid(oNearest)) {
if (GetStringLeft(sNearest, GetStringLength(sNearest) - 3) == (sWPPrefix + sWalker)) {
nWPStart = StringToInt(GetStringRight(sNearest, 2));
oNearest = OBJECT_INVALID;
} else if (sNearest == sPostPrefix + sWalker) {
nWPStart = 0;
oNearest = OBJECT_INVALID;
}
else {
nNth++;
oNearest = GetNearestObject(OBJECT_TYPE_WAYPOINT, OBJECT_SELF, nNth);
sNearest = GetTag(oNearest);
}
}
return nWPStart;
}
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;
}
And here is 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)
HourWayPoints();
}
int nHour = GetTimeHour();
int nLastHourChecked = GetLocalInt(OBJECT_SELF, "nLastHourChecked");
if (nLastHourChecked && nHour != nLastHourChecked) {
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 = 24;
}
}
if (GetIsObjectValid(oNewWP) || GetIsObjectValid(oNewPost))
HourWayPoints();
else
SetLocalInt(OBJECT_SELF, "nLastHourChecked", GetTimeHour());
}
if(!GetHasEffect(EFFECT_TYPE_SLEEP))
{
if(!GetIsPostOrWalking())
{
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(GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS) || GetSpawnInCondition(NW_FLAG_AMBIENT_ANIMATIONS_AVIAN))
{
PlayMobileAmbientAnimations();
}
else if(GetIsEncounterCreature() &&
!GetIsObjectValid(GetNearestCreature(CREATURE_TYPE_REPUTATION, REPUTATION_TYPE_ENEMY, OBJECT_SELF, 1, CREATURE_TYPE_PERCEPTION, PERCEPTION_SEEN)))
{
PlayMobileAmbientAnimations();
}
else if(GetSpawnInCondition(NW_FLAG_IMMOBILE_AMBIENT_ANIMATIONS) &&
!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));
}
}
|