fuzzyworld

godot-shooter / commit

Merge branch 'level' into 'main'

04e45e2e7e56b7d3a4287c0877f50eb2fac246ff · Pavel Safronov · 2023-11-08 18:49:41 +0000 · parents 589bdfbf4bf4 d3f3b85f0a6e

Merge branch 'level' into 'main'

Add level and objective support

See merge request PavelSafronov/godot-shooter!1
 ItemGenerator.gd => ConstantItemGenerator.gd |  2 +-
 HUD.gd                                       | 19 ++++++--
 HUD.tscn                                     | 68 +++++++++++++++++++++-------
 LevelItemGenerator.gd                        | 34 ++++++++++++++
 Player.gd                                    | 11 +++--
 Player.tscn                                  |  4 +-
 Theme.tres                                   |  6 +--
 World.gd                                     | 35 +++++++++++---
 World.tscn                                   | 45 ++++++++++++++++--
 project.godot                                | 12 +++--
 10 files changed, 191 insertions(+), 45 deletions(-)

diff --git a/ItemGenerator.gd b/ConstantItemGenerator.gd
similarity index 94%
rename from ItemGenerator.gd
rename to ConstantItemGenerator.gd
index 2c369f7..360f741 100644
--- a/ItemGenerator.gd
+++ b/ConstantItemGenerator.gd
@@ -1,4 +1,4 @@
-class_name ItemGenerator
+class_name ConstantItemGenerator
 
 const Utils = preload("res://Utils.gd")
 var item_count: int
diff --git a/HUD.gd b/HUD.gd
index 46fcafd..10275ab 100644
--- a/HUD.gd
+++ b/HUD.gd
@@ -2,17 +2,28 @@ extends Control
 
 const Utils = preload("res://Utils.gd")
 export var score = 0
-onready var label = $CanvasLayer/Label
+export var level = 1
+export var objective = "1/17"
+signal paused
+
+onready var label = $CanvasLayer/MessageLabel
 onready var animation_player = $AnimationPlayer
+onready var score_label = $HBoxContainer/Score
+onready var level_label = $MarginContainer/HBoxContainer2/Level
+onready var objective_label = $MarginContainer/HBoxContainer2/Objective
+
 var message_count = 0
 
 func _process(_delta: float) -> void:
-	$VBoxContainer/HBoxContainer/Score.text = "%s" % score
+	score_label.text = "%s" % score
+	level_label.text = "%s" % level
+	objective_label.text = "%s" % objective
 	if Input.is_action_just_pressed("exit"):
 		if OS.get_name() == "OSX":
 			get_tree().quit()
 		else:
-			restart()
+			emit_signal("paused")
+#			restart()
 	if Input.is_action_just_pressed("restart"):
 		restart()
 
@@ -40,4 +51,4 @@ func show_message(message: String, duration_seconds: int = 2) -> void:
 	animation_player.queue(new_fade_out)
 	
 func set_label_text(new_text: String) -> void:
-	$CanvasLayer/Label.text = new_text
+	label.text = new_text
diff --git a/HUD.tscn b/HUD.tscn
index b98556e..6e38069 100644
--- a/HUD.tscn
+++ b/HUD.tscn
@@ -6,7 +6,7 @@
 [sub_resource type="Animation" id=2]
 resource_name = "message fade in"
 tracks/0/type = "value"
-tracks/0/path = NodePath("CanvasLayer/Label:modulate")
+tracks/0/path = NodePath("CanvasLayer/MessageLabel:modulate")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
 tracks/0/imported = false
@@ -18,7 +18,7 @@ tracks/0/keys = {
 "values": [ Color( 1, 1, 1, 0 ), Color( 1, 1, 1, 1 ) ]
 }
 tracks/1/type = "value"
-tracks/1/path = NodePath("CanvasLayer/Label:text")
+tracks/1/path = NodePath("CanvasLayer/MessageLabel:text")
 tracks/1/interp = 1
 tracks/1/loop_wrap = true
 tracks/1/imported = false
@@ -34,7 +34,7 @@ tracks/1/keys = {
 resource_name = "message fade out"
 length = 2.0
 tracks/0/type = "value"
-tracks/0/path = NodePath("CanvasLayer/Label:modulate")
+tracks/0/path = NodePath("CanvasLayer/MessageLabel:modulate")
 tracks/0/interp = 1
 tracks/0/loop_wrap = true
 tracks/0/imported = false
@@ -49,32 +49,68 @@ tracks/0/keys = {
 [node name="HUD" type="Control"]
 anchor_right = 1.0
 anchor_bottom = 1.0
+margin_left = 10.0
+margin_top = 10.0
+margin_right = -10.0
+margin_bottom = -10.0
 theme = ExtResource( 1 )
 script = ExtResource( 2 )
 
-[node name="VBoxContainer" type="VBoxContainer" parent="."]
-margin_right = 40.0
-margin_bottom = 40.0
-
-[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
+[node name="HBoxContainer" type="HBoxContainer" parent="."]
 margin_right = 282.0
 margin_bottom = 74.0
 
-[node name="ScoreText" type="Label" parent="VBoxContainer/HBoxContainer"]
-margin_right = 192.0
-margin_bottom = 74.0
+[node name="ScoreText" type="Label" parent="HBoxContainer"]
+margin_top = 9.0
+margin_right = 143.0
+margin_bottom = 65.0
 text = "Score: "
 
-[node name="Score" type="Label" parent="VBoxContainer/HBoxContainer"]
-margin_left = 200.0
-margin_right = 282.0
-margin_bottom = 74.0
+[node name="Score" type="Label" parent="HBoxContainer"]
+margin_left = 151.0
+margin_top = 9.0
+margin_right = 212.0
+margin_bottom = 65.0
 text = "100"
 uppercase = true
 
+[node name="MarginContainer" type="MarginContainer" parent="."]
+anchor_top = 1.0
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_top = -74.0
+
+[node name="HBoxContainer2" type="HBoxContainer" parent="MarginContainer"]
+margin_right = 480.0
+margin_bottom = 74.0
+
+[node name="LevelText" type="Label" parent="MarginContainer/HBoxContainer2"]
+visible = false
+margin_top = 9.0
+margin_right = 118.0
+margin_bottom = 65.0
+text = "Level:"
+
+[node name="Level" type="Label" parent="MarginContainer/HBoxContainer2"]
+visible = false
+margin_top = 9.0
+margin_right = 13.0
+margin_bottom = 65.0
+text = "1"
+
+[node name="Objective" type="Label" parent="MarginContainer/HBoxContainer2"]
+margin_left = 407.0
+margin_right = 480.0
+margin_bottom = 74.0
+size_flags_horizontal = 10
+size_flags_vertical = 1
+text = "1/17"
+align = 2
+valign = 2
+
 [node name="CanvasLayer" type="CanvasLayer" parent="."]
 
-[node name="Label" type="Label" parent="CanvasLayer"]
+[node name="MessageLabel" type="Label" parent="CanvasLayer"]
 modulate = Color( 1, 1, 1, 0 )
 anchor_left = 0.1
 anchor_top = 0.6
diff --git a/LevelItemGenerator.gd b/LevelItemGenerator.gd
new file mode 100644
index 0000000..d3be1c6
--- /dev/null
+++ b/LevelItemGenerator.gd
@@ -0,0 +1,34 @@
+class_name LevelItemGenerator
+
+const Utils = preload("res://Utils.gd")
+var generated_count: int = 0
+var simultaneous_count: int = 1
+
+var item_count: int
+var creator_name: String
+var instance_object: Object
+var group_name: String
+
+func _init(count, creator, instance, group, sim_count = 1) -> void:
+	item_count = count
+	creator_name = creator
+	group_name = group
+	instance_object = instance
+	simultaneous_count = sim_count
+
+func run(node: Node) -> bool:
+	var items = node.get_tree().get_nodes_in_group(group_name)
+	var generated_enough = generated_count == item_count
+	var finished = generated_enough && items.size() == 0
+	if finished:
+		return true
+	if generated_enough:
+		return false
+	var should_add = items.size() < simultaneous_count
+	if should_add:
+		var item: Node = funcref(instance_object, creator_name).call_func()
+		item.add_to_group(group_name)
+		node.add_child(item)
+		items = node.get_tree().get_nodes_in_group(group_name)
+		generated_count += 1
+	return false
diff --git a/Player.gd b/Player.gd
index 95bce87..f30202b 100644
--- a/Player.gd
+++ b/Player.gd
@@ -73,16 +73,17 @@ func hurt(damage_amount: int = 1):
 		shield_active = false
 		$Graphics/Alive/Shield/Sprite/AnimationPlayer.play("fade out")
 		emit_signal("shield_changed", shield_active)
+		
 		var timer = Timer.new()
-		add_child(timer)
 		timer.wait_time = shield_restart_period_seconds
 		timer.one_shot = true
 		timer.connect("timeout", self, "enable_shield")
+		add_child(timer)
 		timer.start()
-		return
-	health -= damage_amount
-	if health <= 0:
-		kill()
+	else:
+		health -= damage_amount
+		if health <= 0:
+			kill()
 
 func turn_towards(pos: Vector2):
 	var to_dir = (pos - global_position).normalized()
diff --git a/Player.tscn b/Player.tscn
index 25fd651..e1ca400 100644
--- a/Player.tscn
+++ b/Player.tscn
@@ -9,7 +9,7 @@
 [ext_resource path="res://assets/kenney_crosshair-pack/PNG/White Retina/crosshair120.png" type="Texture" id=7]
 
 [sub_resource type="CircleShape2D" id=7]
-radius = 61.0
+radius = 36.0
 
 [sub_resource type="Gradient" id=2]
 colors = PoolColorArray( 1, 0, 0, 1, 1, 0, 0, 0 )
@@ -93,7 +93,7 @@ tracks/2/keys = {
 "times": PoolRealArray( 0, 0.7, 1 ),
 "transitions": PoolRealArray( 1, 1, 1 ),
 "update": 0,
-"values": [ Vector2( 1, 1 ), Vector2( 0.8, 0.8 ), Vector2( 1, 1 ) ]
+"values": [ Vector2( 0.7, 0.7 ), Vector2( 0.6, 0.6 ), Vector2( 0.7, 0.7 ) ]
 }
 
 [sub_resource type="Animation" id=5]
diff --git a/Theme.tres b/Theme.tres
index 6fbeb7c..fdf2be2 100644
--- a/Theme.tres
+++ b/Theme.tres
@@ -3,9 +3,9 @@
 [ext_resource path="res://assets/Unique.ttf" type="DynamicFontData" id=1]
 
 [sub_resource type="DynamicFont" id=1]
-size = 64
-outline_size = 3
-outline_color = Color( 1, 0, 0, 1 )
+size = 48
+outline_size = 2
+outline_color = Color( 0, 0, 0, 1 )
 use_mipmaps = true
 use_filter = true
 font_data = ExtResource( 1 )
diff --git a/World.gd b/World.gd
index da851b3..9e2eaf0 100644
--- a/World.gd
+++ b/World.gd
@@ -9,15 +9,18 @@ var height = ProjectSettings.get("display/window/size/height")
 var destination: Vector2
 var is_destination_set = false
 var generators = [
-	ItemGenerator.new(10, "create_enemy", self, "enemies"),
-	ItemGenerator.new(5, "create_health", self, "health"),
-	ItemGenerator.new(10, "create_shield", self, "shields"),
-	ItemGenerator.new(5, "create_gold", self, "gold"),
+	LevelItemGenerator.new(4, "create_enemy", self, "enemies", 2),
+	LevelItemGenerator.new(10, "create_health", self, "health", 10),
+	LevelItemGenerator.new(3, "create_shield", self, "shields", 3),
+	LevelItemGenerator.new(5, "create_gold", self, "gold", 5),
 ]
+var completed_objectives = 0
+var max_objectives = 4
 
 func enemy_killed(_position: Vector2):
 	$UI/HUD.score += 1
 	$UI/HUD.show_message("Enemy killed!", 1)
+	completed_objectives += 1
 func health_consumed():
 	$Player.heal()
 	$UI/HUD.show_message("+1 health", 1)
@@ -83,13 +86,19 @@ func _input(event: InputEvent) -> void:
 
 func _process(_delta: float) -> void:
 	if Input.is_action_just_pressed("pause"):
+		pause()
+		return
+
+	$UI/HUD.objective = "%s / %s" % [completed_objectives, max_objectives]
+	var all_done = completed_objectives == max_objectives
+	if all_done:
 		get_tree().paused = true
-		$UI/PauseMenu.show()
+		$UI/GameFinishedMenu.show()
 		return
 
 	for g in generators:
-		var generator: ItemGenerator = g
-		generator.ensure_items_exist(self)
+		var generator: LevelItemGenerator = g
+		generator.run(self)
 
 	if is_destination_set:
 		$Player.turn_towards(destination)
@@ -115,6 +124,10 @@ func _process(_delta: float) -> void:
 		if is_too_far:
 			item.queue_free()
 
+func pause():
+	get_tree().paused = true
+	$UI/PauseMenu.show()
+
 func set_destination(event_pos: Vector2) -> void:
 	var mid = Vector2(width / 2.0, height / 2.0)
 	var pos = event_pos + $Player.position - mid
@@ -162,7 +175,15 @@ func _on_Button_pressed() -> void:
 	get_tree().paused = false
 	$UI/PauseMenu.hide()
 
+func _on_Game_over_button_pressed() -> void:
+	get_tree().paused = false
+	$UI/GameFinishedMenu.hide()
+	get_tree().reload_current_scene()
 
 func _on_Player_shield_changed(shield_active: bool) -> void:
 	var message = "Shield Activated" if shield_active else "Shield Lost!"
 	$UI/HUD.show_message(message)
+
+
+func _on_HUD_paused() -> void:
+	pause()
diff --git a/World.tscn b/World.tscn
index a85e592..6d6aa5c 100644
--- a/World.tscn
+++ b/World.tscn
@@ -80,19 +80,54 @@ margin_right = 132.0
 margin_bottom = 110.0
 
 [node name="Label" type="Label" parent="UI/PauseMenu/Panel/VBoxContainer"]
-margin_right = 114.0
+margin_right = 126.0
 margin_bottom = 35.0
 theme = ExtResource( 6 )
 text = "Paused"
 align = 1
 
 [node name="Button" type="Button" parent="UI/PauseMenu/Panel/VBoxContainer"]
-margin_top = 39.0
-margin_right = 114.0
-margin_bottom = 80.0
+margin_top = 43.0
+margin_right = 126.0
+margin_bottom = 90.0
 theme = ExtResource( 6 )
 text = "Resume"
 
+[node name="GameFinishedMenu" type="Control" parent="UI"]
+pause_mode = 2
+visible = false
+anchor_right = 1.0
+anchor_bottom = 1.0
+margin_right = 40.0
+margin_bottom = 40.0
+mouse_filter = 2
+
+[node name="Panel" type="Panel" parent="UI/GameFinishedMenu"]
+margin_left = 120.0
+margin_top = 233.0
+margin_right = 400.0
+margin_bottom = 483.0
+
+[node name="VBoxContainer" type="VBoxContainer" parent="UI/GameFinishedMenu/Panel"]
+margin_left = 68.0
+margin_top = 72.0
+margin_right = 132.0
+margin_bottom = 110.0
+
+[node name="Label" type="Label" parent="UI/GameFinishedMenu/Panel/VBoxContainer"]
+margin_right = 148.0
+margin_bottom = 35.0
+theme = ExtResource( 6 )
+text = "You've won!"
+align = 1
+
+[node name="Button" type="Button" parent="UI/GameFinishedMenu/Panel/VBoxContainer"]
+margin_top = 43.0
+margin_right = 148.0
+margin_bottom = 90.0
+theme = ExtResource( 6 )
+text = "Restart"
+
 [node name="ShootTimer" type="Timer" parent="."]
 process_mode = 0
 wait_time = 0.1
@@ -112,5 +147,7 @@ antialiased = true
 
 [connection signal="body_entered" from="Destination" to="." method="_on_Destination_body_entered"]
 [connection signal="shield_changed" from="Player" to="." method="_on_Player_shield_changed"]
+[connection signal="paused" from="UI/HUD" to="." method="_on_HUD_paused"]
 [connection signal="pressed" from="UI/PauseMenu/Panel/VBoxContainer/Button" to="." method="_on_Button_pressed"]
+[connection signal="pressed" from="UI/GameFinishedMenu/Panel/VBoxContainer/Button" to="." method="_on_Game_over_button_pressed"]
 [connection signal="timeout" from="ShootTimer" to="." method="_on_ShootTimer_timeout"]
diff --git a/project.godot b/project.godot
index fac0ddf..1dfe127 100644
--- a/project.godot
+++ b/project.godot
@@ -14,15 +14,20 @@ _global_script_classes=[ {
 "language": "GDScript",
 "path": "res://Bullet.gd"
 }, {
+"base": "Reference",
+"class": "ConstantItemGenerator",
+"language": "GDScript",
+"path": "res://ConstantItemGenerator.gd"
+}, {
 "base": "KinematicBody2D",
 "class": "Enemy",
 "language": "GDScript",
 "path": "res://Enemy.gd"
 }, {
 "base": "Reference",
-"class": "ItemGenerator",
+"class": "LevelItemGenerator",
 "language": "GDScript",
-"path": "res://ItemGenerator.gd"
+"path": "res://LevelItemGenerator.gd"
 }, {
 "base": "KinematicBody2D",
 "class": "Player",
@@ -41,8 +46,9 @@ _global_script_classes=[ {
 } ]
 _global_script_class_icons={
 "Bullet": "",
+"ConstantItemGenerator": "",
 "Enemy": "",
-"ItemGenerator": "",
+"LevelItemGenerator": "",
 "Player": "",
 "PowerUp": "",
 "Trails": ""