What is the best way to implement a one-time pick up item (like a life upgrade)?

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

Hi, what I want to do is this:

  1. Player is in Stage1 and picks up a life upgrade (an Area2d that is queue_freed on body entered);
  2. Player goes to Stage2 (using change_scene());
  3. Player goes back to Stage1 and the item does not spawn anymore because it was taken already.
  4. If the Player goes to the Title Screen (Main Menu) and than goes back to Stage1 the item should be there.

After thinking for a while I decided to use a Singleton (globals.gd) for the spawnable items like this:

extends Node

var spawnable_items = {
	"DarkMaze": {
		"LifeUpgrade" : {
		"pos" : Vector2(-1161,1195),
		"id" : 0,
		"taken" : false
	},
	"NormalBullet" : {
		"pos" : Vector2(2850,752),
		"id" : 1,
		"taken" : false
	}
	}
}

func set_item_as_taken(stage_name, id):
	for key in spawnable_items[stage_name]:
		if spawnable_items[stage_name][key]["id"] == id:
			spawnable_items[stage_name][key]["taken"] = true

I use the “spawnable_items” dictionary to spawn the items on the stages. Then on the pickup script on_body_entered method I use:

globals.set_item_as_taken(get_tree().get_current_scene().get_name(), id)

And it works. But if I go to the main menu (another call to change_scene()) and go back to the stage, the item is not there! So each time I go back to the title screen I have to reset the items that were taken. So on the the autoload script I added:

func reset_taken_items(stage_name):
    for key in spawnable_items[stage_name]:
        spawnable_items[stage_name][key]["taken"] = false

Then each time I go to the Title Screen I use:

globals.reset_taken_items(get_tree().get_current_scene().get_name())

Again it works, but it does not feel like a good solution:

  1. I want to implement this “persistent” behaviour through stages on other things (like destructible blocks, dialogs, etc.) and this autoload script can became huge.
  2. I did not implement a save system yet, but when I do I’ll have to keep track of that too. (I’ll probably won’t be able to just set every “taken” value to false when I go to the title screen),
  3. Spawning the items through code is not as convenient as just draging than on the GUI.

Any ideas would be apreciated.
Thanks in advance.

:bust_in_silhouette: Reply From: gmaps

You’re on the right path, but there may be a few ways to improve your flow. You don’t have to store the objects and their locations in singleton. Singleton could just store the cross stage info - is the object picked up or no, unless you want them to be on the same location on every level.

So to answer the first question, you can have many autoload functions (singletons). You can have one named e.g. “ItemManager” where you store the info about picked items, you can have “Dialogs” and “DestructableBlocks”, each one of them doing their thing and storing their task specific info. You can have many smaller scripts. “Globals” is just an example, you don’t need it actually.

When you’ll be saving levels, if you want to have persistant info about picked items, you can just serialize the array besides all other info. And then load it back. You want your singletons to contain only the info that glues your scenes.

So to answer the 3.) question - you can drag&drop an area (item) to scene, if you don’t like script spawning, and wan to do it in a gui - Add the object via gui, and dequeue it if it was already taken. You do that by storing an only an array of picked items in singleton (var picked_items = [] which contains the id’s of your objects. In case the id’s are consistent across the stages e.g. id 1 is same item in stage 1 and 2, if not, you may want to save item types, not id’s e.g. “power_up_100hp” or something. And in body_entered function just add the id of the object to picked_items array. And in ready function you can just put a check if the array contains the item, and dequeue (delete) the area in that case. It does add a negligible amount of performance overhead and it gives you prototyping freedom. And when you want to release the game, you can always move it to script.