How to call methods on an array of Button devices?

So the following code works to set the text on all of the buttons with my tag just fine so I know the array is full. They also all grant the item but it doesn’t do the cool down.

I know the problem is in my GrantItem() function. The disable call is for a single button device but I want it to run on any button device that is interacted with. They shouldn’t all go on cooldown, just the specific one that was pressed and actually just for that player or agent. I’m not sure how to pass in the button or what to do there. So how would I do that?

I’d greatly appreciate your help!

unreal-editor-for-fortnite Programming & Scripting question

using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# using { /Verse.org/Simulation/Text }


# A Verse-authored creative device that can be placed in a level
lucky_loot_device := class(creative_device):
    # A button that the player can interact with to grant an item - this is a reference to a button device
    @editable
    var LuckyButton: button_device = button_device{}

    # Empty array of button devices
    LuckyButtons: []button_device = array{}

    # An item granter that will grant an item to the player when the button is interacted with - this is chosen in the editor
    @editable
    LuckyGranter: item_granter_device = item_granter_device{}
  
    # The amount of time to wait before the button can be interacted with again
    @editable
    CoolDownTime: float = 2.0

    # The message to show when the player can interact with the button
    @editable
    StringToShow: string = ""
    
    OnBegin<override>()<suspends>:void=
        # Convert the string to a message
        messageToShow: message = StringToMessage(StringToShow)
        # Find all the buttons in the level that have the snowButton tag
        FoundButtons := FindCreativeObjectsWithTag(snowButton{})
        # For each button, subscribe to the InteractedWithEvent and set the interaction text
        for (Button : FoundButtons, ButtonDevice := button_device[Button]):
            ButtonDevice.InteractedWithEvent.Subscribe(GrantItem)
            # Set the LuckyButtons to the buttons with the snowButton tag
            ButtonDevice.SetInteractionText(messageToShow)
            set LuckyButtons = LuckyButtons + array{ButtonDevice}
            
    # Function to convert a string to a 'message'
    StringToMessage<localizes>(value:string) : message = "{value}"
            
    GrantItem(Agent:agent):void=     
        # Grant a random item to the player
        LuckyGranter.CycleToRandomItem(Agent)
        # Disable the button
        # LuckyButtons.Disable()
        
        
        # Wait for the cooldown time
        spawn:
            Cooldown()

    Cooldown()<suspends>:void=
        # Wait for the cooldown time
        Sleep(CoolDownTime)
        # Enable the button
        LuckyButton.Enable()

I think I see what you’re trying to do. Your problem is that you need to access the specific button pressed in the “GrantItem” function you subscribed to, but it’s only giving you an agent that pressed that button?

This is a pattern I’ve been using in my code to achieve something similar, but I’m not sure if it’s the sanctioned best practice.

First you will want to define some kind of handler class above your lucky_loot_device class definition.

ButtonHandler := class():
    MyLootDevice : lucky_loot_device
    MyButton : button_device
    
    HandleInteractedEvent(InAgent:agent) : void = 
        MyLootDevice .GrantItem(InAgent, MyButton )

What does this do? It gives you a valid function to subscribe to (HandleInteractedEvent), but also allows the class you constructed to hold additional data (MyButton).

Now you just need to call the handler in your code where you’re subscribing to the event. Your subscribe would look something like this (I’m doing this off the top of my head to it might be a little wrong):

ButtonDevice.InteractedWithEvent.Subscribe(ButtonHandler{MyLootDevice := Self, MyButton := Button}.HandleInteractedEvent)

Now your button knows to use the HandleInteractedEvent inside ButtonHandler, but you’ve also passed along Button

The last step is to modify GrantItem and Cooldown:

GrantItem(InAgent : agent, InButton : button_device) : void=
        # Grant a random item to the player
        LuckyGranter.CycleToRandomItem(InAgent)

        # Disable the button
        InButton.Disable()
        
        # Wait for the cooldown time
        spawn{Cooldown(InButton)}

    Cooldown(InButton : button_device)<suspends>:void=
        # Wait for the cooldown time
        Sleep(CoolDownTime)
        # Enable the button
        InButton.Enable()

This way you’re telling Cooldown to keep track of the button you want to re-enable at some point. Each button would now have its own cooldown and use the same item granter to grant items.

Is that what you were going for?

1 Like

That’s a very interesting approach. I will try it out and let you know. I had a few things wrong with that code (like the empty array of buttons not marked with ‘var’). This is how I corrected it:

       
    # An item granter that will grant an item to the player when the button is interacted with - this is chosen in the editor
    @editable
    LuckyGranter: item_granter_device = item_granter_device{}
  
    # The amount of time to wait before the button can be interacted with again
    @editable
    CoolDownTime: float = 2.0

    # The message to show when the player can interact with the button
    @editable
    StringToShow: string = ""

    OnBegin<override>()<suspends>:void=
        # Convert the string to a message
        messageToShow: message = StringToMessage(StringToShow)
        # Find all the buttons in the level that have the snowButton tag
        FoundButtons := FindCreativeObjectsWithTag(snowButton{})
        # For each button, subscribe to the InteractedWithEvent and set the interaction text
        for (Button : FoundButtons, ButtonDevice := button_device[Button]):
            ButtonDevice.SetInteractionText(messageToShow)
            spawn:
                HandleButton(ButtonDevice)
                
    # Function to convert a string to a 'message'
    StringToMessage<localizes>(value:string) : message = "{value}"
            
    HandleButton(ButtonDevice:button_device)<suspends>:void=
        # Loop to handle the button being interacted with - essentially a 'while true' loop
        loop:
            Agent := ButtonDevice.InteractedWithEvent.Await()
            GrantItem(Agent)
            # set snowAmount = 1.0
            ButtonDevice.Disable()
            Cooldown()
            ButtonDevice.Enable()
    
    GrantItem(Agent:agent):void=
        # Grant a random item to the player
        LuckyGranter.CycleToRandomItem(Agent)
    
    Cooldown()<suspends>:void=
        # Wait for the cooldown time
        Sleep(CoolDownTime)```
1 Like