enderalse/source/scripts/critterbird.psc

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