I’m working on a map inspired by Raft and I want to have drifting debris which can be picked up or harvested by damaging them. I’m using an invisible prop with a button and prop manipulator device attached to it, as well as an array of props which gets hidden/shown depending on which loot table index is selected during runtime.
However I encounter a few problems:
After hiding the prop for the first time the next prop doesn’t turn visible. Show/Hide transacts but aren’t failable, so I can’t tell if they fail/get rolled back.
MoveTo often fails, even though I have tested by setting the area in which the prop moves to a small zone with no other props which it might collide with. I also removed collision from the props, but I don’t know if that even works because it seems like I can still trigger damage events by shooting them (or maybe it’s the invisible parent-prop that receives the calls, but that one is also set to have no collision).
The button device (set to not visible during game) is sometimes visible at some point after I have damaged/harvested the prop.
I have done some debugging and noted that if I disable MoveProp() and HideProp() then eventually it will show both/all items once their indexes gets rolled on the table. But hiding a prop seems to disable any of the props to the shown again.
Does anyone have any idea what could be going wrong here with the Hide/Show bug and/or the TeleportTo/MoveTo bug?
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /Verse.org/Random }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /Fortnite.com/Characters }
using { raft_constants }
# This device handles drifting debris
debris_device := class(creative_device):
# A reference to the player manager
@editable
PlayerMan : player_manager_device = player_manager_device{}
# Interactables
# In order for loot/debris to be collectable in several ways we need to have several devices
@editable
PropMan : prop_manipulator_device = prop_manipulator_device{} # This allows us to listen to "damaged" events as loot trigger
@editable
Button : button_device = button_device{} # This allows a player to pick up an object by interacting with it
@editable
PropToMove : creative_prop = creative_prop{} # This is a dummy prop which only exists so that we can move the entire device rather than having moving each individual prop
# A loot table should first be defined by all possible items that might drop
# Since the code will break if we change this list during runtime we need to list them all
# However we can give a weight of 0 to all objects that we don't wish to spawn and then handle that in the logic
@editable
Props : []creative_prop = array{}
@editable
Loot : []raft_constants.LootObject = array{}
# Movement points
var StartPos : vector3 = vector3{X:=0.0, Y:=0.0, Z:= 0.0}
var EndPos : vector3 = vector3{X:=0.0, Y:=0.0, Z:= 0.0}
# Current loot index
var LootIndex : int = -1
# TRIGGERS
# Lets the player loot the current debris directly
# This triggers a respawn
OnButtonPressed(Agent:agent):void=
GiveLoot(Agent)
spawn{Stop()}
# Lets the player loot the current debris directly
# This triggers a respawn
OnDamaged(Agent:agent):void=
Print("Damaged!")
GiveLoot(Agent)
spawn{Stop()}
# LOGIC
# Determines which direction the debris should travel
SelectTravelVector() : void=
Print("Setting travel vectors")
# reset start and end pos
set StartPos = vector3{X:=0.0, Y:=0.0, Z:= 0.0}
set EndPos = vector3{X:=0.0, Y:=0.0, Z:= 0.0}
# loop comparison between start and end, continue if shorter than 100
loop:
var Count : int = 0
if (Res := Distance(StartPos, EndPos) > 100.0):
break
var _X : float = 0.0
var _Y : float = 0.0
var _Z : float = 0.0
# Should start be south or east?
if(GetRandomInt(0,100) > 50): # Start from east
set _X = raft_constants.SESpawn.X
set _Y = GetRandomFloat(raft_constants.SESpawn.Y,NESpawn.Y)
set _Z = raft_constants.SESpawn.Z
set StartPos = vector3{X:=_X,Y:= _Y,Z:= _Z}
else: # Start from south
set _X = GetRandomFloat(raft_constants.SWSpawn.X,SESpawn.X)
set _Y = raft_constants.SWSpawn.Y
set _Z = raft_constants.SWSpawn.Z
set StartPos = vector3{X:=_X,Y:= _Y,Z:= _Z}
# Should end be west or north?
if(GetRandomInt(0,100) > 50): # End in West
set _X = raft_constants.SWSpawn.X
set _Y = GetRandomFloat(raft_constants.SWSpawn.Y,NWSpawn.Y)
set _Z = raft_constants.SWSpawn.Z
set EndPos = vector3{X:=_X,Y:= _Y,Z:= _Z}
else: # End in North
set _X = GetRandomFloat(raft_constants.NWSpawn.X,NESpawn.X)
set _Y = raft_constants.NWSpawn.Y
set _Z = raft_constants.NWSpawn.Z
set EndPos = vector3{X:=_X,Y:= _Y,Z:= _Z}
if (Count > 100):
break
set Count += 1
return
# Gives random loot to a player
GiveLoot(Agent : agent) : void=
if (Player := player[Agent]): # The recipient is a player
if (LootItem := GetActiveLoot[]):
PlayerMan.GivePlayerLoot(Player, LootItem.LootAmount, LootItem.LootType)
# Failable function that tries to get a loot object
GetActiveLoot<public>()<decides><transacts>:raft_constants.LootObject =
return Loot[LootIndex]
# Public function, a collider can also call this
Stop<public>()<suspends>:void=
Print("Stopping")
HideActiveProp()
Sleep(GetRandomFloat(5.0, 10.0))
Print("Respawning")
Respawn()
# Respawns the debris
Respawn():void =
SelectTravelVector()
SetRandomLoot()
ShowActiveProp()
spawn{MoveProp()}
# Select random loot from the loot table and update the device variables
SetRandomLoot():void =
Print("Randomizing loot index")
ValidLoot := for(Index -> Item : Loot, Item.LootAmount > 0):
(Index, Item.LootWeight)
var TotalWeight : int = 0
var CurrentWeight : int = 0
set LootIndex = -1
for (Item : ValidLoot):
set TotalWeight += Item(1)
SelectedWeight := GetRandomInt(0, TotalWeight)
for (Item : ValidLoot):
set CurrentWeight += Item(1)
if (LootIndex < 0):
if (SelectedWeight <= CurrentWeight):
set LootIndex = Item(0)
return
# Hide the active prop
HideActiveProp():void=
Print("Attempting to hide the prop")
if (ActiveProp := Props[LootIndex]):
ActiveProp.Hide()
PropMan.HideProps()
# Show active prop
ShowActiveProp():void=
Print("Attempting to show the prop")
if (ActiveProp := Props[LootIndex]):
ActiveProp.Show()
# Estimate travel time
EstimateTime():float=
Res := Distance(StartPos, EndPos)
Base := Res / 256.0
return Base * GetRandomFloat(0.75, 1.25) # Randomize the movement speed
# Moves the parent prop
MoveProp()<suspends>:void=
Print("Start movement")
Rot := PropToMove.GetTransform().Rotation
if (res := PropToMove.TeleportTo[StartPos, Rot]):
Time := EstimateTime()
Res := PropToMove.MoveTo(EndPos, Rot, Time)
if(Res = move_to_result.WillNotReachDestination):
Print("MoveTo failed")
HideActiveProp()
Respawn()
else:
Print("Moveto not failed?")
Print("Moveto started")
else:
Print("Catastrophic failure")
# Runs when the device is started in a running game
OnBegin<override>()<suspends>:void=
Button.InteractedWithEvent.Subscribe(OnButtonPressed)
PropMan.DamagedEvent.Subscribe(OnDamaged)
for(Prop : Props):
Prop.Hide()
Respawn()