How do I make Godot randomize upgrades on 3 buttons and make it not put 2 upgrades on 1 button?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By Stremper

I’m trying to make 3 buttons have random upgrades. Right now I only have 3 upgrades, but I’m planning on adding more. The upgrades are node2D’s consisting of a text and a picture, they are children of the buttons. Is there a way I could somehow optimize the chosen upgrades and make it that 1 button doesn’t get 2 upgrades. Thanks for any help! I am very new to coding and game development, so have mercy! :smiley:

extends HBoxContainer

onready var SPDup0 = $TextureButton0/SPDup
onready var DMGup0 = $TextureButton0/DMGup
onready var SPDup1 = $TextureButton1/SPDup
onready var DMGup1 = $TextureButton1/DMGup
onready var SPDup2 = $TextureButton2/SPDup
onready var DMGup2 = $TextureButton2/DMGup

func _ready():
	if $".".visible == true:
		rng_upgrade()

func turn_off_button():
	$".".visible = false

func used_button():
	Persist.points -= 2 + Persist.needed_points4lvl
	Persist.needed_points4lvl += 2
	
func rng_upgrade():
	var amountOfButtons = 3
	for i in range(amountOfButtons):
		randomize()
		var RNGUpgrade = randi() % 2 + 1
		var Number4Button = [1, 2, 3]
		var rand_index:int = randi() % Number4Button.size()
		RNGNumber4Button.erase(rand_index)
		print(rand_index)
		if RNGUpgrade == 1:
			if rand_index == 0:
				DMGup0.visible = true
			if rand_index == 1:
				DMGup1.visible = true
			if rand_index == 2:
				DMGup2.visible = true
		if RNGUpgrade == 2:
			if rand_index == 0:
				SPDup0.visible = true
			if rand_index == 1:
				SPDup1.visible = true
			if rand_index == 2:
				SPDup2.visible = true
	
	
	

func _on_TextureButton_pressed():
	turn_off_button()
	used_button()
	if DMGup0.visible == true:
		Persist.B1dmg += 1
	if SPDup0.visible == true:
		Persist.PlayerSpeed + Persist.PlayerSpeed * 0.05


func _on_TextureButton2_pressed():
	turn_off_button()
	used_button()
	if DMGup1.visible == true:
		Persist.B1dmg += 1
	if SPDup1.visible == true:
		Persist.PlayerSpeed + Persist.PlayerSpeed * 0.05

func _on_TextureButton3_pressed():
	turn_off_button()
	used_button()
	if DMGup2.visible == true:
		Persist.B1dmg += 1
	if SPDup2.visible == true:
		Persist.PlayerSpeed + Persist.PlayerSpeed * 0.05
:bust_in_silhouette: Reply From: zhyrin

Here is a process that might help:

  • When beginning the randomization, store all possible upgrades in an array (e.g.: unused upgrades), probably randomize the order
  • Store all your buttons in an array
  • Iterate over the array storing the buttons - your code will run once for each button
  • pick a random upgrade from your unused upgrades
  • assign the selected upgrade’s properties to the current button as needed
  • remove the selected upgrade from the unused upgrades array

Each time the code steps to the next button, the previously selected upgrade should no longer be in the unused upgrades array, that way an upgrade doesn’t get picked more than one, and since you iterate over each button once, each of them only gets assigned one upgrade.

Watch out for the case where you have more buttons than upgrades.

Hey!
I got it to work pretty much, but it just looks unoptimized, too much code repeating itself. I’ll try to clean it up tomorrow if I can. But the changed parts code now looks like this:

    onready var Menu = $"."

var UnunsedUpgrades0 = ["SPDup0", "DMGup0"]
var UnunsedUpgrades1 = ["SPDup1", "DMGup1"]
var UnunsedUpgrades2 = ["SPDup2", "DMGup2"]
var Buttons = ["Button0", "Button1", "Button2"]

var Menu_called = false

func _physics_process(delta):
	if Menu.visible == true and !Menu_called:
		rng_upgrade()
		Menu_called = true
		
func _ready():
	pass

func turn_off_button():
	Menu.visible = false

func used_button():
	Persist.points -= 2 + Persist.needed_points4lvl
	Persist.needed_points4lvl += 2
	
func rng_upgrade():
	var AmountOfButtons = Buttons.size()
	randomize()
	for upgrade in UnunsedUpgrades0:
		var RNGUpgrade0 = randi() % UnunsedUpgrades0.size()
		UnunsedUpgrades0.remove(RNGUpgrade0)
	for upgrade in UnunsedUpgrades1:
		var RNGUpgrade1 = randi() % UnunsedUpgrades1.size()
		UnunsedUpgrades1.remove(RNGUpgrade1)
	for upgrade in UnunsedUpgrades2:
		var RNGUpgrade2 = randi() % UnunsedUpgrades2.size()
		UnunsedUpgrades2.remove(RNGUpgrade2)
	for button in AmountOfButtons:
		randomize()
		if "Button0"in Buttons:
			if "SPDup0" in UnunsedUpgrades0:
				SPDup0.show()
			elif "DMGup0" in UnunsedUpgrades0:
				DMGup0.show()
		if "Button1" in Buttons:
			if "SPDup1" in UnunsedUpgrades1:
				SPDup1.show()
			elif "DMGup1" in UnunsedUpgrades1:
				DMGup1.show()
		if "Button2" in Buttons:
			if "SPDup2" in UnunsedUpgrades2:
				SPDup2.show()
			elif "DMGup2" in UnunsedUpgrades2:
				DMGup2.show()

The problem seemed like the way I checked the rand_index and somehow erase() wasn’t right but remove() worked perfectly. I think I got different buttons in the rand_index, but it only took the position number. So let’s say I removed Button0 from the array it was before [0] but now [0] is Button1, so the code ran 3 times and it could give me two 0’s, because at the position 0 there was still a value. So I just made a system that just randomizes what upgrade the button gets, not by getting a RNGUpgrade number and then using rand_index to get what upgrade where to place.
Thank you for your help!

Stremper | 2023-02-15 16:17

Oh!
And I used

for upgrade in UnusedUpgrades:

to make it that only 1 upgrade comes out of the array.

Stremper | 2023-02-15 16:19

You certainly refactored, but it doesn’t look like you utilise the potential of the structures.
Btw, you are calling randomize() in every iteration of the loop, it’s enought to only call it once the game starts.
If you want to refactor on your own, don’t peep the below code :slight_smile:

func rng_upgrade():
    var Buttons = ["TextureButton0", "TextureButton1", "TextureButton2"] # these are the names of the nodes in the scene
    for ButtonName in Buttons:
        var Button = get_node(ButtonName)
        var PossibleUpgrades = ["SPDup", "DMGup"]
        var UpgradeName = PossibleUpgrades[randi() % PossibleUpgrades.size()]
        # this assumes the structure of: TextureButtonX/SPDup and TextureButtonX/DMGup
        var UpgradeNode = Button.get_node(UpgradeName)
        UpgradeNode.show()

zhyrin | 2023-02-15 20:16

Amazing!
I was just looking for some way to make it that I can take the value from the array and use it to get the node. This was super helpful, thanks! I am slowly getting addicted to solving coding problems. :smiley:
I did just make a function for the upgrade to make the code not repeat too much:

func test1(myArray: Array)
randomize()
for upgrade in myArray:
   var EraseUpgrade = randi() % myArray.size()
   myArray.remove_at(EraseUpgrade)
print(myArray)

I wrote the code in a phone, so the indentation is probably wrong. When I use get_node(UpgradeName), does this make it that, I don’t have to use anymore?

onready var = $.../DMGup1

(too lazy to write the beginning half)

Stremper | 2023-02-16 10:28

Ok so I did need the onready var’s, because of these functions for buttons:

func _on_TextureButton3_pressed():
turn_off_button()
used_button()
if DMGup2.visible == true:
	Persist.B1dmg += 1
if SPDup2.visible == true:
	Persist.PlayerSpeed + Persist.PlayerSpeed * 0.05
if HPup2.visible == true:
	Persist.PlayerMaxHealth += 1

Stremper | 2023-02-16 16:25