This week we do:

* Level Loading, spawning scenes
* Collectible items
* Enemies
* Player death

# Multiple Levels

Having multiple levels means changing things so that the game controller remains persistent in memory across all other things in the display list getting reloaded, or even destroyed.

### Moving the GameController

The game controller needs to be persistent across the entire life of the game. Godot provides a way to move a class outside of the display tree to live on its own in a persistent manner. It is called an 'autoload' and it can be found in project settings > globals.

When we have reassigned the game controller to an autoload, it can be accessed by the scene manager simply by class name - it, as well as any other code, will know how to find it.

Likewise the game controller should not directly reference any other class in the display list, since it is not longer a member of the display list. Instead, it should send out a signal that members of the display list can subscribe to.

## Autoload Singletons

The scene system in Godot is great for building multiple levels of a game and being able to run each one of them individually during development. However it does have a limitation.

When a scene is reloaded, or another scene is loaded in its place, all information is lost. This can be problematic for gameplay, because it means that variables storing information such as player health, number of enemies, etc. are lost when the scene is reloaded.

One answer to this is the use of an Autoload resource.

An autoload resource is one that can be specified in the project settings. It will be loaded ahead of all other resources, and can be given a name that can be referenced throughout the game by any other code. Data in this autoloaded resource will persist across scene changes.

An autoload script is a good place to put any persistent game logic and data, as well as any helper methods that may be useful to a wide variety of nodes in your game.

It is possible that an autoload script can get quite cluttered with information, so a good combination is to use an autoload script and data objects together.

### Example - Updating timer

Now that the game controller is persistent, it does not reload when the level timer completes. As a result, the level loads over and over again because it thinks the time has run out. 

### Timer Example

```
var timer = Timer.new()
var timeLimit = 10
var level="res://scenes/game.tscn"

func _ready() -> void:
	add_child(timer)
	timer.wait_time = 1
	timer.one_shot = false
	timer.connect("timeout", secondCounter)
	timer.start()

func secondCounter() -> void:
	timeLimit -=1
	if timeLimit <=0:
		print("You lose")
		get_tree().call_deferred("change_scene_to_file", level)
```

We can use this example to show the relationship of the scene manager to the game controller - because the scene manager DOES reload each time the level is updated. As it loads, it will tell the game controller to reset the timer.

In `Gamecontroller`:

```
func reset():
	timeLimit = 10
```

And in the scene manager, we have a `buildLevel` function that gets called whenever the scene manager loads. This is a good place to tell the game controller to reset the timer.

```
func buildLevel()->void:
	Gamecontroller.reset()
```

### Example - Destroy Signal (for crates)

Right now the scene manager counts up crates and tells the game controller how many there are.

Let's create a routine that allows the scene manager to connect signals from the triggers in our level to a function that will handle the logic.

Put all of your triggers in a blank Node2D, like we have done with our crates.

In `Scenemanager`>buildLevel:

```
for obj in triggers.get_children():
		if obj is Trigger:
			# wire up to GC
			obj.triggerFired.connect(Gamecontroller._on_trigger_fired)
```

We can now update the game controller with a custom signal it will emit to indicate that the crate should be destroyed when it hits the "destroy" trigger.

In `Gamecontroller`:

```
func _on_trigger_fired(effect: Variant, body: Variant) -> void:
	if effect=="destroy":
		if body is Crate:
			totalCrates -= 1
			print("Crates Remaining " + str(totalCrates) )
			destroySignal.emit(body)
		if totalCrates == 0:
			print("YOU WIN!")
			get_tree().reload_current_scene()
```

Now if something hits the "destroy" trigger, the game controller checks to see if it is a crate, and if so signals to destroy it. This is heard by the scene manager, who deletes the crate.

In `Scenemanager`:

```
func destroy(body):
	if body is Crate:
		body.queue_free()
```

Optionally, the `Scenemanager`could now count up the number of remaining crates, and tell the `Gamecontroller` how many there are.

### Example: Loading Scenes

Lets move the option to load scenes to the scene manager as well, and trigger it using a custom signal from the game controller.

In `Scenemanager`:

```
func loadLevel():
	get_tree().reload_current_scene()
```

In `Gamecontroller`, change any references to loading scenes to simple signal emits:

```
func _on_trigger_fired(effect: Variant, body: Variant) -> void:
	if effect=="destroy":
		if body is Crate:
			totalCrates -= 1
			print("Crates Remaining " + str(totalCrates) )
			destroySignal.emit(body)
		if totalCrates == 0:
			print("YOU WIN!")
			levelCompleteSignal.emit()
```

```
func secondCounter():
	timeLimit -=1
	if timeLimit <= 0:
		print("YOU LOSE")
		levelCompleteSignal.emit()
```

We have now freed the game controller from the roll of loading scenes. And we have seen how to make additions to the functionality of our game.

# Multiple Levels

Copy your initial "game" level, and name the copies "level2" and "level3"

We can now create an array to keep track of each level.

```
var levels = ["res://scenes/game.tscn","res://scenes/level2.tscn","res://scenes/level3.tscn"]
var timers = [20,15,25]
var currentLevel = 0
```

Update the ready function of Gamecontroller to now use the timers array.

```
timeLimit = timers[currentLevel]
```

Likewise update the reset function:

```
func reset():
	timeLimit = timers[currentLevel]
```

Now, when the player loses we want to reload the current level. But if they win we want to load the next level.

Reload the current level by simply sending out a signal to loadLevel without updating the currentLevel.

```
func secondCounter():
	timeLimit -=1
	if timeLimit <= 0:
		print("YOU LOSE")
		levelCompleteSignal.emit(levels[currentLevel])
```

In SceneManager, update the loadLevel function to receive a string indicating what level to load. Note that we will do this using `call_deferred` which allows us to safely load a scene without interrupting currently running operations.

```
func loadLevel(level):
	get_tree().call_deferred("change_scene_to_file",level)
```

When the player wins, we follow a similar pattern, but we increase the current level before emitting the signal. We also check to see if you just finished the final level, in which case you return to the beginning.

Update the `on_trigger_fired` function:

```
if totalCrates == 0:
			print("YOU WIN!")
			currentLevel +=1
			#if on final level, go back to beginning
			if currentLevel >= levels.size():
				currentLevel=0
			levelCompleteSignal.emit(levels[currentLevel])
```

Congratulations - you can now have as many levels as you wish! Try editing your additional levels to make them interesting.

-------

# Collectible Items

Now that we know how to add things to the game loop, let's test our skills by having collectible coins.

The process will be:

* Create a coin scene, and give it a custom signal when it is hit
* Place some coins in the level in a `coins` holder
* Have the scene manager loop through the coins wiring up their custom signal to the game controller
* Have the game controller track how many coins are collected, and emit a signal to destroy the coin when collected

The coin scene will have code that looks like this:

```
class_name Coin extends Area2d

signal coinCollectedSignal(body, coin)

func _on_body_entered(body: Node2D) -> void:
	coinCollectedSignal.emit(body, self)
```

Now in the Gamecontroller, receive that signal.

```
func _on_coin_collected(body, coin):
	if body is Player:
		destroySignal.emit(coin)
```

And in the SceneManager, you can update the destroy function if you like:

```
func destroy(body):
	if body is Crate:
		body.queue_free()
	if body is Coin:
		body.queue_free()
```

Don't forget to add a `coins` node to your additional levels, or they will trigger an error when the Scenemanager tries to find it.

---------------------

# Bad Guys and Enemies

Adding a fun enemy to the level is remarkably similar to adding a collectible like a coin. The difference is that the enemy will be able to move around, and when we make contact with them the game controller will make a different decision - namely to hurt the player.

Just as with the coin, we will first create the bad guy. At a minimum, it will need a custom signal and a handler function for collisions.

Badguy script:

```
class_name Badguy extends Area2D

signal playerDamageSignal

func _on_body_entered(body: Node2D) -> void:
	playerDamageSignal.emit(body, self)
```

As you did with the coins, you can make a node2D called "badguys" and add it to the game. You can then drag a Badguy scene into it, and place the badguy where you wish.

Now we can update the Scenemanager to have it wire up the badguys signals to the Gamecontroller.

In Scenemanager:

```
#wire up badguys
	if badguys:
		for obj in badguys.get_children():
			if obj is Badguy:
				obj.playerDamageSignal.connect(Gamecontroller.playerDamage)
```

The playerDamage function in the Gamecontroller can be very simple for now. We will revisit the player damage in the future. For the time being, it can look like this:

```
func playerDamage(body, badguy):
	if body is Player:
		print("player got damaged")
```

### Enemy Movement

To get the enemy moving around, we will use Raycast2D to have them detect obstacles, or if they are reaching the edge of a ledge.

We can use the `_process` function to update the position of the enemy every frame, and to update what direction they should be facing.

```
class_name Badguy extends Area2D
@onready var right_side_cast: RayCast2D = $RightSideCast
@onready var right_down_cast: RayCast2D = $RightDownCast
@onready var left_down_cast: RayCast2D = $LeftDownCast
@onready var left_side_cast: RayCast2D = $LeftSideCast
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D

var direction = 1
var speed = 100

signal playerDamageSignal

func _process(delta: float) -> void:
	if not right_down_cast.is_colliding():
		#about to fall on the right....
		direction = -1
		sprite.flip_h = true
	if not left_down_cast.is_colliding():
		direction = 1
		sprite.flip_h = false
		
	position.x += direction * speed * delta
	
func _on_body_entered(body: Node2D) -> void:
	playerDamageSignal.emit(body, self)

```



