Added Butterflies Unchained 1.1 by runesick

This commit is contained in:
Eddoursul 2023-12-04 12:33:00 +01:00
parent 46a4d3ce4a
commit ea329209ee
5 changed files with 181 additions and 8 deletions

View File

@ -763,6 +763,7 @@ XP32 Maximum Skeleton Extended - XPMSE by Groovtama
Vanilla Script (micro)Optimizations by subhuman0100 Vanilla Script (micro)Optimizations by subhuman0100
Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage
Precision Creatures by NickaNak (werewolf behaviors) Precision Creatures by NickaNak (werewolf behaviors)
Butterflies Unchained by runesick
Japanese Fonts Japanese Fonts
"Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku "Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku

View File

@ -759,6 +759,7 @@ XP32 Maximum Skeleton Extended - XPMSE by Groovtama
Vanilla Script (micro)Optimizations by subhuman0100 Vanilla Script (micro)Optimizations by subhuman0100
Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage
Precision Creatures by NickaNak (werewolf behaviors) Precision Creatures by NickaNak (werewolf behaviors)
Butterflies Unchained by runesick
Japanese Fonts Japanese Fonts
"Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku "Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku

View File

@ -759,6 +759,7 @@ XP32 Maximum Skeleton Extended - XPMSE by Groovtama
Vanilla Script (micro)Optimizations by subhuman0100 Vanilla Script (micro)Optimizations by subhuman0100
Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage Unnecessarily Fixed Fixed Dragon Stalking Fix by tarlazo and KirbonatedBeverage
Precision Creatures by NickaNak (werewolf behaviors) Precision Creatures by NickaNak (werewolf behaviors)
Butterflies Unchained by runesick
Japanese Fonts Japanese Fonts
"Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku "Corporate Mingcho" made by LOGOTYPE.JP Jimukyoku

View File

@ -310,6 +310,13 @@ Function SetSpawnerProperties()
PlayerRef = Game.GetPlayer() PlayerRef = Game.GetPlayer()
endFunction endFunction
Function SetPositionVariance(float xVar, float yVar, float minZVar, float maxZVar)
fPositionVarianceX = xVar
fPositionVarianceY = yVar
fPositionVarianceZMin = minZVar
fPositionVarianceZMax = maxZVar
EndFunction
; Checks that the critter is all initialized, and if so, kick off the behavior ; Checks that the critter is all initialized, and if so, kick off the behavior
Function CheckStateAndStart() Function CheckStateAndStart()
; ; Debug.TraceConditional("Critter " + self + "bDefaultPropertiesInitialized=" + bDefaultPropertiesInitialized + ", bSpawnerVariablesInitialized=" + bSpawnerVariablesInitialized, bCritterDebug) ; ; Debug.TraceConditional("Critter " + self + "bDefaultPropertiesInitialized=" + bDefaultPropertiesInitialized + ", bSpawnerVariablesInitialized=" + bSpawnerVariablesInitialized, bCritterDebug)

View File

@ -8,11 +8,20 @@ import debug
; Properties (set through the editor) ; Properties (set through the editor)
FormList property PlantTypes auto FormList property PlantTypes auto
{ The list of plant types this moth can be attracted to} { The list of plant types this moth can be attracted to}
FormList property IgnoreHarvestedPlants auto
{ plants we want to ignore when they're harvested }
FormList property GroundPlants auto ; jazbay
FormList property VeryShortPlants auto ; cabbage, leek, nirnroot, canis root, hanging moss 3, spiky grass
FormList property ShortPlants auto ; mountain flowers, cotton, dragon's tongue, hanging moss 2
FormList property TallPlants auto ; snowberry
FormList property VeryTallPlants auto ; juniper
ObjectReference fLastPlant
; Constants ; Constants
float Property fTimeAtPlantMin = 5.0 auto float Property fTimeAtPlantMin = 5.0 auto
{The Minimum time a Moth stays at a plant} {The Minimum time a Moth stays at a plant}
float Property fTimeAtPlantMax = 10.0 auto float Property fTimeAtPlantMax = 15.0 auto
{The Maximum time a Moth stays at a plant} {The Maximum time a Moth stays at a plant}
float Property fActorDetectionDistance = 300.0 auto float Property fActorDetectionDistance = 300.0 auto
{The Distance at which an actor will trigger a flee behavior} {The Distance at which an actor will trigger a flee behavior}
@ -23,7 +32,7 @@ float Property fTranslationSpeedVariance = 50.0 auto
float Property fFleeTranslationSpeed = 300.0 auto float Property fFleeTranslationSpeed = 300.0 auto
{The movement speed when fleeing from the player} {The movement speed when fleeing from the player}
float Property fBellShapePathHeight = 150.0 auto float Property fBellShapePathHeight = 150.0 auto
{The height of the bell shaped path} {The maximum height of the bell shaped path}
float Property fFlockPlayerXYDist = 100.0 auto float Property fFlockPlayerXYDist = 100.0 auto
{When flocking the player, the XY random value to add to its location} {When flocking the player, the XY random value to add to its location}
float Property fFlockPlayerZDistMin = 50.0 auto float Property fFlockPlayerZDistMin = 50.0 auto
@ -44,8 +53,20 @@ string property LandingMarkerPrefix = "LandingSmall0" auto
{Prefix of landing markers on plants, default="LandingSmall0"} {Prefix of landing markers on plants, default="LandingSmall0"}
float property fMaxRotationSpeed = 90.0 auto float property fMaxRotationSpeed = 90.0 auto
{Max rotation speed while mocing, default = 90 deg/s} {Max rotation speed while mocing, default = 90 deg/s}
float property fLeashMultiplier = 4.0 auto
{Increase the spawning range from the intial spawn point}
float property fSearchDistance = 250.0 auto
{Butterflies will search this distance for their next perch, increasing by fSearchIncrease each time it fails to find a plant.}
float property fSearchIncrease = 250.0 auto
float property fMinWander = 500.0 auto
float property fMaxWander = 1500.0 auto
{ How far away will butterflies wander from their current perch before searching for a perch}
; Variables ; Variables
; Averages of x, y, z positions to track butterfly movement and keep them moving from old areas
float avgX = 0.0
float avgY = 0.0
float avgZ = 0.0
int iPlantTypeCount = 0 int iPlantTypeCount = 0
Actor closestActor = none Actor closestActor = none
@ -55,6 +76,8 @@ float fWaitingToDieTimer = 10.0
; Called by the spawner to kick off the processing on this Moth ; Called by the spawner to kick off the processing on this Moth
Event OnStart() Event OnStart()
LoadPlantLists()
; Pick a plant type that we're attracted to ; Pick a plant type that we're attracted to
iPlantTypeCount = PlantTypes.GetSize() iPlantTypeCount = PlantTypes.GetSize()
@ -82,6 +105,14 @@ Event OnStart()
endIf endIf
endEvent endEvent
Function LoadPlantLists()
;Form cabbage = Game.GetFormFromFile(0x000BCF48, "Skyrim.esm")
;if !VeryShortPlants.HasForm(cabbage)
; debug.trace("[Butterflies Unchained] Loading cabbage form into form list..")
; VeryShortPlants.AddForm(cabbage)
;endif
endFunction
; The Current plant object ; The Current plant object
ObjectReference currentPlant = none ObjectReference currentPlant = none
@ -241,8 +272,111 @@ Function FlockToPlayer()
SplineTranslateTo(ftargetX, ftargetY, ftargetZ, ftargetAngleX, 0.0, ftargetAngleZ, fpathCurve, fFlockTranslationSpeed, fMaxRotationSpeed) SplineTranslateTo(ftargetX, ftargetY, ftargetZ, ftargetAngleX, 0.0, ftargetAngleZ, fpathCurve, fFlockTranslationSpeed, fMaxRotationSpeed)
endFunction endFunction
ObjectReference Function PickPlant(float x, float y, float z, float searchDistance, float searchIncrease)
; 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]
int distanceScale = 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, x, y, z, searchDistance + (searchIncrease * distanceScale))
; 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) && newPlant != fLastPlant)
float plantX = newPlant.GetPositionX()
float plantY = newPlant.GetPositionY()
float plantZ = newPlant.GetPositionZ()
float distanceFromAvg = Math.sqrt(Math.pow(plantX - avgX, 2) + Math.pow(plantY - avgY, 2) + Math.pow(plantZ - avgZ, 2))
; Enforce distance from average position, unless we are on our last safety check.
; In which case we allow it to pass to stop the butterfly from despawning
if (distanceFromAvg >= fMinWander || isafetyCheck == 1)
Form plantForm
; Fixes butterfly hovering by setting the height according to the plant
if (GroundPlants == none || VeryShortPlants == none \
|| ShortPlants == none || TallPlants == none \
|| VeryTallPlants == none)
Debug.Trace("[Butterflies Unchained] One or more of your mods adds a butterfly that requires a compatibility patch for optimal performance. You may encounter hovering butterflies in the meantime.")
else
plantForm = newPlant.GetBaseObject()
if (GroundPlants.Find(plantForm) != -1)
SetPositionVariance(10.0, 10.0, 5.0, 10.0)
elseif (VeryShortPlants.Find(plantForm) != -1)
SetPositionVariance(10.0, 10.0, 20.0, 30.0)
elseif (ShortPlants.Find(plantForm) != -1)
SetPositionVariance(15.0, 15.0, 35.0, 50.0)
elseif (TallPlants.Find(plantForm) != -1)
SetPositionVariance(15.0, 15.0, 50.0, 90.0)
elseif (VeryTallPlants.Find(plantForm) != -1)
SetPositionVariance(15.0, 15.0, 115.0, 140.0)
else
SetPositionVariance(15.0, 15.0, 50.0, 100.0)
endif
endif
if (!newPlant.IsHarvested() || IgnoreHarvestedPlants.Find(plantForm) == -1)
return newPlant; [USKP 2.0.1b]
endif
endif
endIf
if ((fSearchDistance + (100 * distanceScale)) < fMaxPlayerDistance)
distanceScale += 1
endif
; Safety counter
isafetyCheck -= 1
endWhile
; [USKP 2.0.1]
;Debug.Trace("Moth ( " + fSpawnerX + ", " + fSpawnerY + ", " + fSpawnerZ + ") " + self + " couldn't find a valid plant to go to", 1)
return none
endFunction
; Finds a new plant to fly to ; Finds a new plant to fly to
ObjectReference Function PickNextPlant() ObjectReference Function PickNextPlant()
float wanderRangeX = 0
float wanderRangeY = 0
float posX = self.GetPositionX()
float posY = self.GetPositionY()
float posZ = self.GetPositionZ()
; create a vector in the direction of the butterfly's movement to encourage exploration
if (fLastPlant != none)
float lastPlantX = fLastPlant.GetPositionX()
float lastPlantY = fLastPlant.GetPositionY()
float distanceTraveledX = posX - lastPlantX
float distanceTraveledY = posY - lastPlantY
float distanceTraveled = Math.sqrt(Math.pow(distanceTraveledX, 2) + Math.pow(distanceTraveledY, 2))
wanderRangeX = RandomFloat(fMinWander, fMaxWander) * Math.cos(distanceTraveledX / distanceTraveled)
if (distanceTraveledX < 0) ; more efficient than calling math.abs
wanderRangeX *= -1
endif
wanderRangeY = RandomFloat(fMinWander, fMaxWander) * Math.sin(distanceTraveledY / distanceTraveled)
else
wanderRangeX = RandomFloat(fMinWander, fMaxWander) * RandomInt(-1, 1)
wanderRangeY = RandomFloat(fMinWander, fMaxWander) * RandomInt(-1, 1)
endif
return PickPlant(posX + wanderRangeX, posY + wanderRangeY, posZ, fSearchDistance, fSearchIncrease)
endFunction
;; To maintain optimal compatibility with other mods, new function was created rather than having parameters added
ObjectReference Function PickSpawnPlant()
; re-check viability [USKP 2.0.1] ; re-check viability [USKP 2.0.1]
if !(PlayerRef ;/&& CheckCellAttached(self) && Spawner && CheckCellAttached(Spawner)/;) if !(PlayerRef ;/&& CheckCellAttached(self) && Spawner && CheckCellAttached(Spawner)/;)
return none return none
@ -255,7 +389,7 @@ ObjectReference Function PickNextPlant()
while PlayerRef && isafetyCheck > 0; [USKP 2.0.1b] while PlayerRef && isafetyCheck > 0; [USKP 2.0.1b]
; Grab a random plant from the list of valid plant types ; Grab a random plant from the list of valid plant types
newPlant = Game.FindRandomReferenceOfAnyTypeInList(PlantTypes, fSpawnerX, fSpawnerY, fSpawnerZ, fLeashLength) newPlant = Game.FindRandomReferenceOfAnyTypeInList(PlantTypes, fSpawnerX, fSpawnerY, fSpawnerZ, fLeashLength * fLeashMultiplier)
; Check whether the new plant is valid (different from current) ; 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 3D check because critters can attempt to pick disabled Nirnroots [USKP 2.0.1]
@ -280,22 +414,51 @@ Function GoToNewPlant(float afSpeed)
ObjectReference newPlant = PickNextPlant() ObjectReference newPlant = PickNextPlant()
if (newPlant != none) if (newPlant != none)
; Update the last plant to the current one
fLastPlant = currentPlant
; Update the current plant to the new one ; Update the current plant to the new one
currentPlant = newPlant currentPlant = newPlant
float newPlantX = newPlant.GetPositionX()
float newPlantY = newPlant.GetPositionY()
float newPlantZ = newPlant.GetPositionZ()
; Update avg coord vals
if (avgX == 0)
avgX = newPlantX
else
avgX = (avgX + newPlantX) / 2
endif
if (avgY == 0)
avgY = newPlantY
else
avgY = (avgY + newPlantY) / 2
endif
if (avgZ == 0)
avgZ = newPlantZ
else
avgZ = (avgX + newPlantZ) / 2
endif
; Pick random landing node ; Pick random landing node
; And start moving towards it ; And start moving towards it
string landingMarkerName = LandingMarkerPrefix + RandomInt(1, 3) string landingMarkerName = LandingMarkerPrefix + RandomInt(1, 3)
float newPlantDistance = Math.sqrt(Math.pow(newPlantX - self.GetPositionX(), 2) + Math.pow(newPlantY - self.GetPositionY(), 2) + Math.pow(newPlantZ - self.GetPositionZ(), 2))
float adjustedBellShapePathHeight = fBellShapePathHeight - (fBellShapePathHeight * (Math.pow(0.995, newPlantDistance)))
if (newPlant.HasNode(landingMarkerName)) if (newPlant.HasNode(landingMarkerName))
BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, landingMarkerName, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant") BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, landingMarkerName, adjustedBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
else else
; traceConditional(self + " could not find landing marker " + landingMarkerName + " on plant " + newPlant, bCritterDebug) ; traceConditional(self + " could not find landing marker " + landingMarkerName + " on plant " + newPlant, bCritterDebug)
string firstMarkerName = LandingMarkerPrefix + 1 string firstMarkerName = LandingMarkerPrefix + 1
if (newPlant.HasNode(firstMarkerName)) if (newPlant.HasNode(firstMarkerName))
BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, firstMarkerName, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant") BellShapeTranslateToRefNodeAtSpeedAndGotoState(CurrentPlant, firstMarkerName, adjustedBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
else else
; traceConditional(self + " could not find landing marker " + firstMarkerName + " on plant " + newPlant, bCritterDebug) ; traceConditional(self + " could not find landing marker " + firstMarkerName + " on plant " + newPlant, bCritterDebug)
BellShapeTranslateToRefAtSpeedAndGotoState(CurrentPlant, fBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant") BellShapeTranslateToRefAtSpeedAndGotoState(CurrentPlant, adjustedBellShapePathHeight, afSpeed, fMaxRotationSpeed, "AtPlant")
endIf endIf
endIf endIf
else else
@ -308,7 +471,7 @@ endFunction
Function WarpToNewPlant() Function WarpToNewPlant()
; Find a plant reference, trying to pick a different one than the current one ; Find a plant reference, trying to pick a different one than the current one
ObjectReference newPlant = PickNextPlant() ObjectReference newPlant = PickSpawnPlant()
if (newPlant != none) if (newPlant != none)
; Update the current plant to the new one ; Update the current plant to the new one