enderalse/source/scripts/crittermoth.psc

349 lines
12 KiB
Plaintext

scriptName CritterMoth extends Critter
{Main Behavior script for moths and butterflies}
import Utility
import form
import debug
; Properties (set through the editor)
FormList property PlantTypes auto
{ The list of plant types this moth can be attracted to}
; Constants
float Property fTimeAtPlantMin = 5.0 auto
{The Minimum time a Moth stays at a plant}
float Property fTimeAtPlantMax = 10.0 auto
{The Maximum time a Moth stays at a plant}
float Property fActorDetectionDistance = 300.0 auto
{The Distance at which an actor will trigger a flee behavior}
float Property fTranslationSpeedMean = 150.0 auto
{The movement speed when going from plant to plant, mean value}
float Property fTranslationSpeedVariance = 50.0 auto
{The movement speed when going from plant to plant, variance}
float Property fFleeTranslationSpeed = 300.0 auto
{The movement speed when fleeing from the player}
float Property fBellShapePathHeight = 150.0 auto
{The height of the bell shaped path}
float Property fFlockPlayerXYDist = 100.0 auto
{When flocking the player, the XY random value to add to its location}
float Property fFlockPlayerZDistMin = 50.0 auto
{When flocking the player, the min Z value to add to its location}
float Property fFlockPlayerZDistMax = 200.0 auto
{When flocking the player, the max Z value to add to its location}
float Property fFlockTranslationSpeed = 300.0 auto
{When flocking the player, the speed at which to move}
float Property fMinScale = 0.5 auto
{Minimum initial scale of the Moth}
float Property fMaxScale = 1.2 auto
{Maximum initial scale of the Moth}
float property fMinTravel = 64.0 auto
{Minimum distance a wandering moth/butterfly will travel}
float property fMaxTravel = 512.0 auto
{Maximum distance a wandering moth/butterfly will travel}
string property LandingMarkerPrefix = "LandingSmall0" auto
{Prefix of landing markers on plants, default="LandingSmall0"}
float property fMaxRotationSpeed = 90.0 auto
{Max rotation speed while mocing, default = 90 deg/s}
; Variables
int iPlantTypeCount = 0
Actor closestActor = none
; Constants
float fWaitingToDieTimer = 10.0
; Called by the spawner to kick off the processing on this Moth
Event OnStart()
; Pick a plant type that we're attracted to
iPlantTypeCount = PlantTypes.GetSize()
; Vary size a bit
SetScale(RandomFloat(fMinScale, fMaxScale))
; Switch state and trigger a callback immediately
; ; Debug.TraceConditional("Moth " + self + " warping to state AtPlant", bCritterDebug)
; test moved to Critter [indent retained] [USKP 2.0.1]
WarpToNewPlant()
; ; Debug.TraceConditional("Moth " + self + " registering for update", bCritterDebug)
; Enable the critter
Enable()
if CheckFor3D(self)
; Switch to keyframe state
SetMotionType(Motion_Keyframed, false)
; Get ready to start moving
RegisterForSingleUpdate(0.0)
else
DisableAndDelete(false)
endIf
endEvent
; The Current plant object
ObjectReference currentPlant = none
;/ clear TargetObject [USKP 2.0.1]
/;
Function TargetClear()
currentPlant = none
endFunction
; Moth is at the plant
State AtPlant
Event OnUpdate()
; Is the player too far?
if CheckViableDistance()
;/// [USKP 2.0.1]
; Kill this critter
DisableAndDelete()
elseif (spawner.iCurrentCritterCount > spawner.iMaxCritterCount) || (spawner.iCurrentCritterCount < 0)
; something's up with the spawner. Kill critters until it recovers
; debug.trace(self+" updated, but spawner ("+spawner+") has bad iCurrentCritterCount ("+spawner.iCurrentCritterCount+")")
disableAndDelete()
else
///;
if (ShouldFlockAroundPlayer())
; Player is close enough and has the ingredient we're attracted to,
; If applicable, play takeoff animation
DoPathStartStuff()
; Flock to the player
FlockToPlayer()
else
if (Spawner && Spawner.IsActiveTime())
; Check whether we should flee and move faster
float fspeed = 0.0
if (closestActor != none)
; ;Debug.Trace(self + " Oh noes! there is an Actor " + closestActor + " nearby, Flee")
; Move fast
fspeed = fFleeTranslationSpeed
else
; Move at regular speed
fspeed = RandomFloat(fTranslationSpeedMean - fTranslationSpeedVariance, fTranslationSpeedMean + fTranslationSpeedVariance)
endIf
; Time to take off for another plant
GoToNewPlant(fspeed)
else
; Time to go to bed,
BellShapeTranslateToRefAtSpeedAndGotoState(Spawner, fBellShapePathHeight, fTranslationSpeedMean, fMaxRotationSpeed, "KillForTheNight")
endIf
endIf
bCalculating = False; [USKP 2.0.4]
endIf
closestActor = none; [USKP 2.0.4]
endEvent
Event OnCritterGoalReached()
; traceConditional(self + " reached goal", bCritterDebug)
if PlayerRef; interlock, but never delete during Translation [USKP 2.0.1]
closestActor = Game.FindClosestActorFromRef(self, fActorDetectionDistance)
if closestActor
; There is an actor right there, trigger the update right away, so we'll flee
; ; Debug.TraceConditional("Moth " + self + " registering for update", bCritterDebug)
RegisterForSingleUpdate(0.0)
else
; Wait at the plant, then take off again
; ; Debug.TraceConditional("Moth " + self + " registering for update", bCritterDebug)
RegisterForSingleUpdate(RandomFloat(fTimeAtPlantMin, fTimeAtPlantMax))
endIf
endIf
EndEvent
endState
; When the player has the ingredient we're interested in, follow him
State FollowingPlayer
;! Event OnCritterGoalReached()
Event OnUpdate(); [USKP 2.0.1]
; prevent repeat calculations [USKP 2.0.4]
If bCalculating
return
EndIf
bCalculating = True; [USKP 2.0.3]
; Are we too far from our spawner?
if (Spawner && Spawner.GetDistance(self) < fLeashLength && ShouldFlockAroundPlayer())
; Nope, flock to the player
FlockToPlayer()
else
; Go back to the plants
GoToNewPlant(fFlockTranslationSpeed)
endIf
bCalculating = False; [USKP 2.0.4]
endEvent
Event OnCritterGoalReached()
; traceConditional(self + " about to reach goal", bCritterDebug)
if PlayerRef; interlock, but never delete during Translation [USKP 2.0.1]
RegisterForSingleUpdate(0.0)
endIf
EndEvent
endState
; When the moths go to sleep, they get deleted
State KillForTheNight
;! Event OnCritterGoalReached()
Event OnUpdate(); [USKP 2.0.1]
; We've reached the nest, die
; debug.trace ("Killing for the night: "+self)
DisableAndDelete()
endEvent
Event OnCritterGoalReached()
; traceConditional(self + " about to reach goal", bCritterDebug)
if PlayerRef; interlock, but never delete during Translation [USKP 2.0.1]
RegisterForSingleUpdate(0.0)
endIf
EndEvent
endState
; Helper method to indicate whether the player has the ingredient
bool Function ShouldFlockAroundPlayer()
; if (PlayerRef.GetDistance(Spawner) > fRadius)
; return false
; endIf
; return (PlayerRef.GetItemCount(IngredientType) > 0)
return false
endFunction
; Utility method, makes a moth flock to a random point around the player
Function FlockToPlayer()
; Switch state
; ; Debug.TraceConditional("Moth " + self + " going to state FollowingPlayer", bCritterDebug)
gotoState("FollowingPlayer")
; re-check viability [USKP 2.0.3]
if !(PlayerRef ;/&& CheckCellAttached(self) && Spawner && CheckCellAttached(Spawner)/;)
RegisterForSingleUpdate(fWaitingToDieTimer)
return
endif
; Pick a random point around the player
float ftargetX = PlayerRef.X + RandomFloat(-fFlockPlayerXYDist, fFlockPlayerXYDist)
float ftargetY = PlayerRef.Y + RandomFloat(-fFlockPlayerXYDist, fFlockPlayerXYDist)
float ftargetZ = PlayerRef.Z + RandomFloat(fFlockPlayerZDistMin, fFlockPlayerZDistMax)
float ftargetAngleZ = RandomFloat(-180, 180)
float ftargetAngleX = RandomFloat(-20, 20)
float fpathCurve = RandomFloat(fPathCurveMean - fPathCurveVariance, fPathCurveMean + fPathCurveVariance)
; Travel to it
if CheckViability()
return
endIf
SplineTranslateTo(ftargetX, ftargetY, ftargetZ, ftargetAngleX, 0.0, ftargetAngleZ, fpathCurve, fFlockTranslationSpeed, fMaxRotationSpeed)
endFunction
; Finds a new plant to fly to
ObjectReference Function PickNextPlant()
; re-check viability [USKP 2.0.1]
if !(PlayerRef ;/&& CheckCellAttached(self) && Spawner && CheckCellAttached(Spawner)/;)
return none
endif
; Look for a random plant within the radius of the Spawner
int isafetyCheck = 10; was 5, match FireFly [USKP 2.0.1]
ObjectReference newPlant = CurrentPlant
while PlayerRef && isafetyCheck > 0; [USKP 2.0.1b]
; Grab a random plant from the list of valid plant types
newPlant = Game.FindRandomReferenceOfAnyTypeInList(PlantTypes, fSpawnerX, fSpawnerY, fSpawnerZ, fLeashLength)
; Check whether the new plant is valid (different from current)
; and 3D check because critters can attempt to pick disabled Nirnroots [USKP 2.0.1]
; and not too close to an actor
if (newPlant != none && newPlant != currentPlant && !newPlant.IsDisabled() \
&& Game.FindClosestActorFromRef(newPlant, fActorDetectionDistance) == none \
&& CheckCellAttached(newPlant) && CheckFor3D(newPlant))
return newPlant; [USKP 2.0.1b]
endIf
; Safety counter
isafetyCheck -= 1
endWhile
; [USKP 2.0.1]
; Debug.Trace("Moth " + self + " couldn't find a valid plant to go to", 1)
return none
endFunction
; Picks a new plant and fly to it if possible
Function GoToNewPlant(float afSpeed)
; Find a plant reference, trying to pick a different one than the current one
ObjectReference newPlant = PickNextPlant()
if (newPlant != none)
; Update the current plant to the new one
currentPlant = newPlant
; Pick random landing node
; And start moving towards it
string landingMarkerName = LandingMarkerPrefix + RandomInt(1, 3)
if (newPlant.HasNode(landingMarkerName))
BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, landingMarkerName, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
else
; traceConditional(self + " could not find landing marker " + landingMarkerName + " on plant " + newPlant, bCritterDebug)
string firstMarkerName = LandingMarkerPrefix + 1
if (newPlant.HasNode(firstMarkerName))
BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, firstMarkerName, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
else
; traceConditional(self + " could not find landing marker " + firstMarkerName + " on plant " + newPlant, bCritterDebug)
BellShapeTranslateToRefAtSpeedAndGotoState(CurrentPlant, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
endIf
endIf
else
; This moth is stuck, wait until the player is far enough away that it can delete itself
; Debug.Trace("Moth " + self + " is stuck and will wait to kill itself", 1)
gotoState("KillForTheNight"); [USKP 2.0.1]
RegisterForSingleUpdate(fWaitingToDieTimer)
endIf
endFunction
Function WarpToNewPlant()
; Find a plant reference, trying to pick a different one than the current one
ObjectReference newPlant = PickNextPlant()
if (newPlant != none)
; Update the current plant to the new one
currentPlant = newPlant
; Pick random landing node
; And start moving towards it
string landingMarkerName = LandingMarkerPrefix + RandomInt(1, 3)
if (newPlant.HasNode(landingMarkerName))
WarpToRefNodeAndGotoState(CurrentPlant, landingMarkerName, "AtPlant")
else
; traceConditional(self + " could not find landing marker " + landingMarkerName + " on plant " + newPlant, bCritterDebug)
string firstMarkerName = LandingMarkerPrefix + 1
if (newPlant.HasNode(firstMarkerName))
WarpToRefNodeAndGotoState(CurrentPlant, firstMarkerName, "AtPlant")
else
; traceConditional(self + " could not find landing marker " + firstMarkerName + " on plant " + newPlant, bCritterDebug)
WarpToRefAndGotoState(CurrentPlant, "AtPlant")
endIf
endIf
else
; This moth is stuck, wait until the player is far enough away that it can delete itself
; Debug.Trace("Moth " + self + " is stuck and will wait to kill itself", 1)
gotoState("KillForTheNight"); [USKP 2.0.1]
RegisterForSingleUpdate(fWaitingToDieTimer)
endIf
endFunction
Function DoPathStartStuff()
; Transition to the flight state
SetAnimationVariableFloat("fTakeOff", 1.0); [USKP 2.0.1]
endFunction
Function DoPathEndStuff()
; Transition to the hover/landed state
SetAnimationVariableFloat("fTakeOff", 0.0); [USKP 2.0.1]
endFunction