308 lines
8.3 KiB
Plaintext
308 lines
8.3 KiB
Plaintext
scriptname critterBird extends critter
|
|
{behavior script for basic bird};[USKP 2.0.1]
|
|
;!{behavior script for basic fish}
|
|
;======================================================================================;
|
|
; IMPORTS /
|
|
;=============/
|
|
; include scripts for brevity
|
|
import utility
|
|
import form
|
|
import math
|
|
import debug
|
|
;======================================================================================;
|
|
; PROPERTIES /
|
|
;=============/
|
|
FormList property perchTypeList auto
|
|
{formlist of perches I look for}
|
|
float property fMinScale = 0.5 auto
|
|
{how small can this thing get? Default=0.5}
|
|
float property fMaxScale = 1.5 auto
|
|
{how big can it get? Default = 1.5}
|
|
float property fMinHop = 8.0 auto
|
|
{minimum distance to hop. Default: 8}
|
|
float property fMaxHop = 32.0 auto
|
|
{max distance to hop. Default:32}
|
|
float property leashLength = 256.0 auto
|
|
{how far from home can I roam? Treat like a radius. Default = 256.0}
|
|
float property fSpeed = 150.0 auto
|
|
{Speed of base movement. Default= 150}
|
|
string property sState auto hidden
|
|
{current behavior state of the bird. NOT a papyrus state}
|
|
; Valid State Values:
|
|
; inFlight
|
|
; onPerch ; perch basically anywhere I've landed but cannot hop along.
|
|
; onGround
|
|
int property iEnergy = 50 auto hidden
|
|
{internal energy value 1-100. Default at spawn: 50}
|
|
objectReference property goalPerch auto hidden
|
|
{Internally track reserved perch reference}
|
|
|
|
;======================================================================================;
|
|
; VARIABLES /
|
|
;=============/
|
|
objectReference player
|
|
; save player reference for later brevity.
|
|
int iDice
|
|
; integer we'll use and re-use for quick random decision rolls
|
|
|
|
;======================================================================================;
|
|
; EVENTS /
|
|
;=============/
|
|
EVENT onStart() ; critter event called when spawned
|
|
;trace("startup bird: " + self)
|
|
; scale a little bit for visual variance
|
|
setScale((randomFloat(fMinScale, fMaxScale)))
|
|
|
|
; enable after scale, matching other critters [USKP 2.0.1]
|
|
Enable()
|
|
|
|
; need 3D before keyframed [USKP 2.0.1]
|
|
if !CheckFor3D(self)
|
|
DisableAndDelete(false)
|
|
return
|
|
endif
|
|
|
|
; use keyframed motion
|
|
setMotionType(Motion_Keyframed, false)
|
|
|
|
;check for Spawner override of leash length
|
|
if spawner.fLeashOverride != 0
|
|
leashLength = spawner.fLeashOverride
|
|
endif
|
|
|
|
;save off the player reference for brevity
|
|
player = game.getPlayer()
|
|
|
|
; go to idle swim state and poll
|
|
sState = "onGround"
|
|
if sState == ""
|
|
;trace(self + " has sState of " + sState + ", something is wrong")
|
|
endif
|
|
gotoState("idling")
|
|
registerForSingleUpdate(0.0)
|
|
endEVENT
|
|
|
|
STATE idling
|
|
EVENT onUpdate()
|
|
; begin main loop of bird behavior
|
|
if CheckViableDistance()
|
|
if (spawner && spawner.isActiveTime())
|
|
; spawner is active this time of day
|
|
; first things first - call home if too far away.
|
|
if getDistance(spawner) > leashLength
|
|
;trace(self + " is too far from spawner. Fly Home")
|
|
if sState == "inFlight"
|
|
gotoState("Flying")
|
|
flyAwayHome()
|
|
else
|
|
takeFlight()
|
|
endif
|
|
endif
|
|
|
|
; if we were trying to reach a perch point, land at it now
|
|
if goalPerch != NONE
|
|
;trace(self + " has a goal perch: " + goalPerch)
|
|
if getDistance(goalPerch) < 32
|
|
gotoState("Flying")
|
|
landAtPerch(goalPerch)
|
|
endif
|
|
endif
|
|
|
|
; if nothing higher priority, then proceed with regular behavior decision
|
|
if sState == "inFlight"
|
|
;trace(self + " is in flight, choosing relevant behavior")
|
|
; choose from in-flight behaviors
|
|
iDice == randomInt(1,2)
|
|
if iDice == 1
|
|
; look for a perch object/node
|
|
ObjectReference goal = findPerch()
|
|
if goal != NONE
|
|
flyToPerch(goal)
|
|
endif
|
|
elseif iDice == 2
|
|
;trace(self + " is in flight and wants to fly more")
|
|
; fly some more
|
|
flyTo(player)
|
|
endif
|
|
elseif sState == "perched"
|
|
;trace(self + " is perched, choosing relevant behavior")
|
|
; choose from perch behaviors
|
|
iDice = randomInt(1,3)
|
|
if iDice == 1
|
|
playIdle()
|
|
elseif iDice == 2
|
|
takeFlight()
|
|
elseif iDice == 3
|
|
groundHop()
|
|
endif
|
|
elseif sState == "onGround"
|
|
;trace(self + " is on the ground, choosing relevant behavior")
|
|
; choose from ground behaviors
|
|
iDice = randomInt(1,3)
|
|
if iDice == 1
|
|
playIdle()
|
|
elseif iDice == 2
|
|
takeFlight()
|
|
elseif iDice == 3
|
|
groundHop()
|
|
endif
|
|
endif
|
|
endif
|
|
bCalculating = False; [USKP 2.0.4]
|
|
endif
|
|
endEVENT
|
|
endSTATE
|
|
|
|
STATE Flying
|
|
EVENT onTranslationComplete()
|
|
;trace(self + " done with translation. Idle now.")
|
|
gotoState("idling")
|
|
registerForSingleUpdate(0.0)
|
|
endEVENT
|
|
endSTATE
|
|
|
|
;======================================================================================;
|
|
; FUNCTIONS /
|
|
;=============/
|
|
FUNCTION groundHop()
|
|
;trace(self + " beginning groundHop()")
|
|
;/// [USKP]
|
|
; Set up variables to solve for right triangle ABC
|
|
; c is the hypotenuse - think of it as the path along which the bird hops
|
|
; the a and c values are going to be added to current XY to determine goal XY
|
|
float aA = getAngleZ() ; B
|
|
float aB ; /|
|
|
float aC = 90 ; c/ |b ; we know angle C is 90 because it's a right triangleukhj/p-
|
|
float a ; A/____|C
|
|
float b ; a
|
|
float c = randomFloat(fMinHop, fMaxHop)
|
|
|
|
; use an int to track the quadrant into which we're hopping (global)
|
|
; I:(+,+) II:(-,+) III:(-,-) IV:(+,-)
|
|
int quadrant
|
|
if (aA > 0 && aA <= 90) || aA > 360
|
|
quadrant = 1
|
|
elseif aA > 90 && aA <= 180
|
|
quadrant = 4
|
|
elseif aA > 180 && aA <= 270
|
|
quadrant = 3
|
|
elseif aA > 270 && aA <= 360
|
|
quadrant = 2
|
|
endif
|
|
|
|
a = c*(cos(aB))
|
|
b = c*(cos(aA))
|
|
|
|
float newX
|
|
float newY
|
|
|
|
if quadrant == 1
|
|
newX = (self.x + a)
|
|
newY = (self.y + b)
|
|
elseif quadrant == 2
|
|
newX = (self.x - a)
|
|
newY = (self.y + b)
|
|
elseif quadrant == 3
|
|
newX = (self.x - a)
|
|
newY = (self.y - b)
|
|
elseif quadrant == 4
|
|
newX = (self.x + a)
|
|
newY = (self.y - b)
|
|
endif
|
|
///;
|
|
; Sclerocephalus replacement [USKP 2.0.3]
|
|
Float AngleZ = self.GetAngleZ()
|
|
Float HopDistance = RandomFloat(fMinHop, fMaxHop)
|
|
|
|
Float NewX = self.X + HopDistance * Cos(AngleZ)
|
|
Float NewY = self.Y + HopDistance * Sin(AngleZ)
|
|
|
|
if CheckViability()
|
|
return
|
|
endIf
|
|
|
|
; now use a spline to "hop" there quicky.
|
|
;! SplineTranslateTo(newX, newY, self.z, 0, 0, self.getAngleZ(), 300, fSpeed)
|
|
SplineTranslateTo(newX, newY, self.Z, 0, 0, AngleZ, 300, fSpeed)
|
|
|
|
;trace(self + "performing a short hop to: " + newX + ", " + newY + ".")
|
|
; test, but a little pause between each seems proper.
|
|
wait(randomFloat(0.1, 2.0))
|
|
endFUNCTION
|
|
|
|
FUNCTION takeFlight()
|
|
if CheckViability()
|
|
return
|
|
endIf
|
|
|
|
;trace(self + " is taking flight")
|
|
playAnimationAndWait("takeOff","end")
|
|
sState = "inFlight"
|
|
SplinetranslateTo(self.X, self.Y, self.Z + 64, 0, 0, self.getAngleZ(), 50, fSpeed/2)
|
|
endFUNCTION
|
|
|
|
FUNCTION FlyTo(objectReference goal)
|
|
if CheckViability()
|
|
return
|
|
endIf
|
|
|
|
;trace(self + " is flying to point: " + goal)
|
|
; quick and dirty - just go right to the point.
|
|
SplinetranslateTo(goal.X, goal.Y, goal.Z + 64, 0, 0, self.getAngleZ(), 200, fSpeed)
|
|
endFUNCTION
|
|
|
|
FUNCTION playIdle()
|
|
; just have to be aware of the valid behaviors we can choose from.
|
|
; long-term may want to use an array/list and be able to choose dynamically
|
|
iDice = randomInt(1,2)
|
|
if iDice == 1
|
|
playAnimationAndWait("StartGrndPeck","StartGrndLook")
|
|
elseif iDice == 2
|
|
playAnimationAndWait("startGrndFlap","StartGrndLook")
|
|
endif
|
|
endFUNCTION
|
|
|
|
FUNCTION landAtPerch(objectReference goal)
|
|
if CheckViability()
|
|
return
|
|
endIf
|
|
|
|
SplineTranslateTo(goal.x, goal.y, goal.z, 0, 0, randomFloat(0.0, 360.0), 300, fSpeed/2)
|
|
playAnimationAndWait("startGrndFlap","StartGrndLook")
|
|
endFUNCTION
|
|
|
|
objectReference FUNCTION findPerch()
|
|
int size = perchTypeList.getSize()
|
|
int i = 0
|
|
bool checking = true
|
|
while i < size && checking == true
|
|
form perchType = perchTypeList.getAt(i)
|
|
i += 1
|
|
objectReference goal = game.FindRandomReferenceOfTypeFromRef(perchType, spawner, leashLength)
|
|
if goal != NONE
|
|
checking = false
|
|
;trace(self + "found a perch: " + goal)
|
|
critterPerch perchScript = goal as critterPerch
|
|
if perchScript.reserved != true
|
|
perchScript.reserved = true
|
|
perchScript.incoming = self
|
|
return goal
|
|
else
|
|
;trace(self + " found a perch, but it was reserved!")
|
|
return NONE
|
|
endif
|
|
;trace(self + " couldn't find a perch")
|
|
return NONE
|
|
endif
|
|
endWhile
|
|
endFUNCTION
|
|
|
|
FUNCTION flyToPerch(objectReference goal)
|
|
; quick and dirty
|
|
flyTo(goal)
|
|
endFUNCTION
|
|
|
|
FUNCTION flyAwayHome()
|
|
flyTo(spawner)
|
|
endFUNCTION
|