godot-shooter / commit
Moved a lot of UI stuff (like the intro dialog, the pause dialog, etc.) out of the World scene and into the Player scene.
HUD.gd | 2 + Player.gd | 131 ++++++++++++++---- Player.tscn | 448 +++++++++++++++++++++++++++++++++++------------------------- Rocket.gd | 1 - Settings.gd | 30 +++- Utils.gd | 60 +++++++- World.gd | 82 ++--------- World.tscn | 217 +---------------------------- 8 files changed, 459 insertions(+), 512 deletions(-) diff --git a/HUD.gd b/HUD.gd index 5888efd..98deb5d 100644 --- a/HUD.gd +++ b/HUD.gd @@ -47,6 +47,8 @@ func _input(event: InputEvent) -> void: func _process(_delta: float) -> void: score_label.text = "%s" % score objective_label.text = "%s" % objective + if Input.is_action_just_pressed("pause"): + emit_signal("paused") if Input.is_action_just_pressed("exit"): if OS.get_name() == "OSX": get_tree().quit() diff --git a/Player.gd b/Player.gd index 33c3903..c7f4a99 100644 --- a/Player.gd +++ b/Player.gd @@ -3,17 +3,33 @@ class_name Player const Weapons = preload("res://Weapons.gd") +export var score = 0 export var move_speed = 60 export var health = 50 export var max_health = 100 export var is_invincible = false export var turn_rate = 0.03 -export var shield_active = true export var radar_min_radius = 350 export var radar_max_radius = 2000 +signal player_died +signal destination_set(position) +signal debug +onready var hud = $UI/HUD +onready var static_controls = $Graphics/Alive/StaticControls onready var radar_sprite = $Graphics/Alive/StaticControls/RadarSprite onready var radar_container = $Graphics/Alive/StaticControls/Radar +onready var scores_container = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores +onready var scores_list = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores/ScoresList +onready var intro_menu = $UI/IntroMenu +onready var intro_always_show_intro_check = $UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/AlwaysShowIntroCheckBox +onready var pause_always_show_intro_check = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Pref/AlwaysShowIntroCheckBox +onready var health_label = $Graphics/Alive/StaticControls/HealthLabel +onready var trails = $Graphics/Alive/Trails +onready var shot_timer = $ShotTimer +onready var death_screen = $UI/DeathScreen +onready var final_score_label = $UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer/FinalScoreLabel + const Utils = preload("res://Utils.gd") const PopupControl = preload("res://Popup.tscn") @@ -22,26 +38,41 @@ var default_weapon: Weapon = Weapons.create_basic_gun() var dead = false var velocity: Vector2 var can_fire = true -var destination: Vector2 -var is_destination_set = false +var settings: Settings func _ready() -> void: velocity= Vector2.UP * move_speed weapon = Weapons.create_basic_gun() -# weapon = Weapons.create_gattling_gun() - if shield_active: - enable_shield() - else: - $Graphics/Alive/Shield/Sprite/AnimationPlayer.play("fade out instant") + + var now = Time.get_datetime_dict_from_system() +# Utils.remove_user_settings() + settings = Utils.load_user_settings() + settings.save_user_settings() + pause_always_show_intro_check.pressed = settings.show_intro + if settings.show_intro: + get_tree().paused = true + intro_menu.visible = true + if settings.high_scores.size() > 0: + Utils.remove_all_children(scores_list) + settings.high_scores.sort_custom(self, "sort_high_scores") + var index = 0 + for hs in settings.high_scores: + index += 1 + var label = Label.new() + var date_dict = hs.date + var date_diff_secs = Utils.time_diff_secs(date_dict, now) + var date_diff = Utils.timespan_to_english(date_diff_secs) + label.text = "%s - %s points, %s" % [index, hs.score, date_diff] + scores_list.add_child(label) func _process(_delta: float) -> void: if dead: return - $Graphics/Alive/StaticControls/HealthLabel.text = "%s / %s" % [health, max_health] - $Graphics/Alive/StaticControls.global_rotation = 0 - $Graphics/Alive/Trails.add_position(global_position) - $ShieldCollisionShape.disabled = !shield_active - $ShipCollisionShape.disabled = shield_active + health_label.text = "%s / %s" % [health, max_health] + static_controls.global_rotation = 0 + trails.add_position(global_position) + hud.set_current_weapon(weapon) + hud.score = score func _physics_process(_delta: float) -> void: if dead: @@ -76,9 +107,12 @@ func kill(): if dead or is_invincible: return dead = true + emit_signal("player_died") + add_high_score(score) $Graphics/Dead.show() $Graphics/Alive.hide() - $CanvasLayer/CenterContainer/DeathScreen.show() + final_score_label.text = "Final score: %s" % score + death_screen.show() z_index = -1 get_tree().paused = true @@ -94,19 +128,9 @@ func speed_up(delta: int = 10): return move_speed += delta -func enable_shield() -> void: - shield_active = true - $Graphics/Alive/Shield/Sprite/AnimationPlayer.play("fade in") - -func disable_shield() -> void: - shield_active = false - $Graphics/Alive/Shield/Sprite/AnimationPlayer.play("fade out") - func hurt(damage_amount: int = 1): if dead or is_invincible: return - if shield_active: - disable_shield() else: health -= damage_amount if health <= 0: @@ -123,8 +147,8 @@ func shoot(): if !can_fire: return can_fire = false - $ShotTimer.wait_time = weapon.shot_period_seconds - $ShotTimer.start() + shot_timer.wait_time = weapon.shot_period_seconds + shot_timer.start() var particle = weapon.particle.instance() owner.add_child(particle) particle.transform = $Barrel.global_transform @@ -137,8 +161,8 @@ func shoot_at(pos: Vector2): if weapon.ammo <= 0: weapon = default_weapon weapon.ammo -= 1 - $ShotTimer.wait_time = weapon.cooldown_seconds - $ShotTimer.start() + shot_timer.wait_time = weapon.cooldown_seconds + shot_timer.start() var particle = weapon.particle.instance() particle.damage_amount = weapon.damage_amount owner.add_child(particle) @@ -151,3 +175,54 @@ func allow_fire(): func _on_Button_pressed() -> void: get_tree().paused = false restart() + +func set_objective(objective: String) -> void: + hud.objective = objective + +func _on_StartButton_pressed() -> void: + get_tree().paused = false + intro_menu.visible = false + settings.show_intro = intro_always_show_intro_check.pressed + settings.save_user_settings() + +func set_intro_dialog(enabled: bool) -> void: + settings.show_intro = enabled + settings.save_user_settings() + +func _on_PauseAlwaysShowIntroCheckBox_pressed() -> void: + var show_intro = pause_always_show_intro_check.pressed + set_intro_dialog(show_intro) + +func add_high_score(new_score: int) -> void: + var hs = Settings.HighScore.new(new_score, Time.get_datetime_dict_from_system()) + var hss = settings.high_scores + hss.push_back(hs) + settings.high_scores.sort_custom(self, "sort_high_scores") + while settings.high_scores.size() > 10: + settings.high_scores.pop_back() + settings.save_user_settings() + +func _on_LinkButton_pressed() -> void: + get_tree().paused = true + var _err = OS.shell_open("https://gitlab.com/PavelSafronov/godot-shooter") + get_tree().paused = true + +func _on_HUD_position_set(position) -> void: + emit_signal("destination_set", position) + +func show_message(message: String, duration_seconds: int = 2) -> void: + hud.show_message(message, duration_seconds) + +func _on_HUD_paused() -> void: + get_tree().paused = true + $UI/PauseMenu.show() + +func _on_ResumeButton_pressed() -> void: + get_tree().paused = false + $UI/PauseMenu.hide() + +func _on_HUD_debug_signal() -> void: + emit_signal("debug") + +func sort_high_scores(a, b): + return (a as Settings.HighScore).score > (b as Settings.HighScore).score diff --git a/Player.tscn b/Player.tscn index 7629a80..7954406 100644 --- a/Player.tscn +++ b/Player.tscn @@ -6,10 +6,9 @@ [ext_resource path="res://assets/kenney_simple-space/PNG/Retina/ship_B.png" type="Texture" id=4] [ext_resource path="res://CharacterTheme.tres" type="Theme" id=5] [ext_resource path="res://Trails.tscn" type="PackedScene" id=6] -[ext_resource path="res://assets/kenney_crosshair-pack/PNG/White Retina/crosshair120.png" type="Texture" id=7] - -[sub_resource type="CircleShape2D" id=7] -radius = 36.0 +[ext_resource path="res://assets/neuropolitical rg.otf" type="DynamicFontData" id=8] +[ext_resource path="res://Theme.tres" type="Theme" id=9] +[ext_resource path="res://HUD.tscn" type="PackedScene" id=10] [sub_resource type="Gradient" id=2] colors = PoolColorArray( 1, 0, 0, 1, 1, 0, 0, 0 ) @@ -17,159 +16,34 @@ colors = PoolColorArray( 1, 0, 0, 1, 1, 0, 0, 0 ) [sub_resource type="Gradient" id=1] colors = PoolColorArray( 0, 0, 0, 0, 0, 1, 0, 1 ) -[sub_resource type="Animation" id=3] -length = 0.001 -tracks/0/type = "value" -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Color( 0, 0, 1, 1 ) ] -} -tracks/1/type = "value" -tracks/1/path = NodePath(".:rotation_degrees") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ 0.0 ] -} -tracks/2/type = "value" -tracks/2/path = NodePath(".:scale") -tracks/2/interp = 1 -tracks/2/loop_wrap = true -tracks/2/imported = false -tracks/2/enabled = true -tracks/2/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Vector2( 1, 1 ) ] -} - -[sub_resource type="Animation" id=4] -resource_name = "blinking" -loop = true -tracks/0/type = "value" -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0, 0.7, 1 ), -"transitions": PoolRealArray( 1, 1, 1 ), -"update": 0, -"values": [ Color( 0, 0, 1, 1 ), Color( 0, 0, 1, 0.509804 ), Color( 0, 0, 1, 1 ) ] -} -tracks/1/type = "value" -tracks/1/path = NodePath(".:rotation_degrees") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/keys = { -"times": PoolRealArray( 0, 1 ), -"transitions": PoolRealArray( 1, 1 ), -"update": 0, -"values": [ 0.0, 90.0 ] -} -tracks/2/type = "value" -tracks/2/path = NodePath(".:scale") -tracks/2/interp = 1 -tracks/2/loop_wrap = true -tracks/2/imported = false -tracks/2/enabled = true -tracks/2/keys = { -"times": PoolRealArray( 0, 0.7, 1 ), -"transitions": PoolRealArray( 1, 1, 1 ), -"update": 0, -"values": [ Vector2( 0.7, 0.7 ), Vector2( 0.6, 0.6 ), Vector2( 0.7, 0.7 ) ] -} +[sub_resource type="DynamicFont" id=9] +size = 24 +font_data = ExtResource( 8 ) -[sub_resource type="Animation" id=5] -resource_name = "fade in" -tracks/0/type = "value" -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0, 1 ), -"transitions": PoolRealArray( 1, 1 ), -"update": 0, -"values": [ Color( 0, 0, 1, 0 ), Color( 0, 0, 1, 1 ) ] -} -tracks/1/type = "method" -tracks/1/path = NodePath("AnimationPlayer") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/keys = { -"times": PoolRealArray( 1 ), -"transitions": PoolRealArray( 1 ), -"values": [ { -"args": [ "blinking", -1, 1.0, false ], -"method": "play" -} ] -} +[sub_resource type="DynamicFont" id=10] +outline_color = Color( 0, 0.490196, 0.831373, 1 ) +use_mipmaps = true +use_filter = true +font_data = ExtResource( 8 ) -[sub_resource type="Animation" id=6] -resource_name = "fade out" -tracks/0/type = "value" -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0, 1 ), -"transitions": PoolRealArray( 1, 1 ), -"update": 0, -"values": [ Color( 0, 0, 1, 1 ), Color( 0, 0, 1, 0 ) ] -} +[sub_resource type="Theme" id=11] +default_font = SubResource( 10 ) -[sub_resource type="Animation" id=8] -resource_name = "fade out instant" -length = 0.001 -tracks/0/type = "value" -tracks/0/path = NodePath(".:modulate") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/keys = { -"times": PoolRealArray( 0 ), -"transitions": PoolRealArray( 1 ), -"update": 0, -"values": [ Color( 0, 0, 1, 0 ) ] -} +[sub_resource type="DynamicFont" id=12] +use_mipmaps = true +use_filter = true +font_data = ExtResource( 8 ) [node name="Player" type="KinematicBody2D" groups=["player"]] rotation = -0.000165944 z_index = 10 collision_mask = 6 script = ExtResource( 3 ) -shield_active = false radar_max_radius = 3000 [node name="ShipCollisionShape" type="CollisionPolygon2D" parent="."] polygon = PoolVector2Array( -0.996349, -22.0002, -8.00133, 7.99867, -7.00166, 9.99884, -11.002, 11.9982, -14.0007, 3.99768, -20.002, 11.9967, -17.0038, 22.9972, -9.00465, 27.9985, -8.00448, 26.9987, -10.0027, 15.9983, -6.00232, 13.999, -0.00398266, 24, 4.99784, 13.0008, 9.99734, 16.0017, 6.99552, 27.0012, 7.99519, 29.0013, 10.9952, 29.0018, 17.996, 24.003, 20.998, 12.0035, 15.9993, 4.00266, 12.9993, 4.00216, 10.9982, 11.0018, 6.99834, 10.0012, 0.00381671, -23 ) -[node name="ShieldCollisionShape" type="CollisionShape2D" parent="."] -shape = SubResource( 7 ) - [node name="Graphics" type="Node2D" parent="."] [node name="Alive" type="Node2D" parent="Graphics"] @@ -208,81 +82,270 @@ offset = Vector2( 0, -300 ) modulate = Color( 1, 0, 0, 1 ) gradient = SubResource( 1 ) -[node name="Shield" type="Node2D" parent="Graphics/Alive"] - -[node name="Sprite" type="Sprite" parent="Graphics/Alive/Shield"] -modulate = Color( 0, 0, 1, 1 ) -texture = ExtResource( 7 ) - -[node name="AnimationPlayer" type="AnimationPlayer" parent="Graphics/Alive/Shield/Sprite"] -autoplay = "blinking" -anims/RESET = SubResource( 3 ) -anims/blinking = SubResource( 4 ) -"anims/fade in" = SubResource( 5 ) -"anims/fade out" = SubResource( 6 ) -"anims/fade out instant" = SubResource( 8 ) - [node name="Dead" type="Node2D" parent="Graphics"] visible = false [node name="Explosion" parent="Graphics/Dead" instance=ExtResource( 2 )] -[node name="CanvasLayer" type="CanvasLayer" parent="."] +[node name="UI" type="CanvasLayer" parent="."] +pause_mode = 2 layer = 100 -[node name="CenterContainer" type="CenterContainer" parent="CanvasLayer"] +[node name="HUD" parent="UI" instance=ExtResource( 10 )] +pause_mode = 2 + +[node name="DeathScreen" type="Control" parent="UI"] +pause_mode = 2 +visible = false margin_left = 109.0 margin_top = 84.0 margin_right = 109.0 margin_bottom = 84.0 mouse_filter = 2 - -[node name="DeathScreen" type="Control" parent="CanvasLayer/CenterContainer"] -pause_mode = 2 -visible = false -mouse_filter = 2 size_flags_horizontal = 5 size_flags_vertical = 5 -[node name="ColorRect" type="ColorRect" parent="CanvasLayer/CenterContainer/DeathScreen"] +[node name="CenterContainer" type="CenterContainer" parent="UI/DeathScreen"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="PanelContainer" type="PanelContainer" parent="UI/DeathScreen/CenterContainer"] +margin_right = 300.0 +margin_bottom = 200.0 +rect_min_size = Vector2( 300, 200 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="UI/DeathScreen/CenterContainer/PanelContainer"] +margin_left = 14.0 +margin_top = 14.0 +margin_right = 286.0 +margin_bottom = 186.0 + +[node name="Label" type="Label" parent="UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer"] +margin_right = 272.0 +margin_bottom = 25.0 +size_flags_horizontal = 3 +theme = ExtResource( 5 ) +text = "Game Over" +align = 1 +valign = 1 + +[node name="FinalScoreLabel" type="Label" parent="UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer"] margin_right = 40.0 -margin_bottom = 40.0 -color = Color( 0, 0, 0, 1 ) +margin_bottom = 25.0 -[node name="Panel" type="Panel" parent="CanvasLayer/CenterContainer/DeathScreen"] -margin_right = 250.0 -margin_bottom = 150.0 +[node name="Button" type="Button" parent="UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 135.0 +margin_right = 272.0 +margin_bottom = 172.0 +size_flags_vertical = 14 +theme = ExtResource( 5 ) +text = "Restart" + +[node name="IntroMenu" type="Control" parent="UI" groups=["pause_menu"]] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="CenterContainer" type="CenterContainer" parent="UI/IntroMenu"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="PanelContainer" type="PanelContainer" parent="UI/IntroMenu/CenterContainer"] +margin_left = 236.0 +margin_top = 386.0 +margin_right = 264.0 +margin_bottom = 414.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="UI/IntroMenu/CenterContainer/PanelContainer"] +margin_left = 14.0 +margin_top = 14.0 +margin_right = 447.0 +margin_bottom = 451.0 +theme = ExtResource( 9 ) +alignment = 1 + +[node name="Label" type="Label" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_right = 433.0 +margin_bottom = 30.0 +custom_fonts/font = SubResource( 9 ) +text = "Welcome to Tactical Shooter!" + +[node name="RichTextLabel" type="RichTextLabel" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 38.0 +margin_right = 433.0 +margin_bottom = 324.0 +theme = SubResource( 11 ) +text = "This is a 2D space shooter game. + +You choose where to fly in this game. + +Your ship will attack for you. + +Touch/click + hold anywhere to pause the game and plan your course. +Let go to set location as the destination. + +Your objective count is in the lower-right corner. +The radar shows enemies and objectives." +fit_content_height = true + +[node name="AlwaysShowIntroCheckBox" type="CheckBox" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 332.0 +margin_right = 433.0 +margin_bottom = 380.0 +custom_fonts/font = SubResource( 12 ) +pressed = true +text = "Always show intro" + +[node name="StartButton" type="Button" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 388.0 +margin_right = 433.0 +margin_bottom = 437.0 +mouse_default_cursor_shape = 2 +text = "Start Game" + +[node name="PauseMenu" type="Control" parent="UI" groups=["pause_menu"]] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 mouse_filter = 2 -[node name="Label" type="Label" parent="CanvasLayer/CenterContainer/DeathScreen/Panel"] +[node name="CenterContainer" type="CenterContainer" parent="UI/PauseMenu"] anchor_right = 1.0 -margin_top = 50.0 -margin_bottom = 14.0 -theme = ExtResource( 5 ) -text = "Game Over" +anchor_bottom = 1.0 + +[node name="PanelContainer" type="PanelContainer" parent="UI/PauseMenu/CenterContainer"] +margin_left = 25.0 +margin_top = 282.0 +margin_right = 475.0 +margin_bottom = 517.0 +rect_min_size = Vector2( 450, 235 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer"] +margin_left = 14.0 +margin_top = 14.0 +margin_right = 436.0 +margin_bottom = 233.0 + +[node name="Label" type="Label" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_right = 422.0 +margin_bottom = 37.0 +theme = ExtResource( 9 ) +text = "Paused" align = 1 -valign = 1 -[node name="Button" type="Button" parent="CanvasLayer/CenterContainer/DeathScreen/Panel"] -anchor_top = 1.0 +[node name="TabContainer" type="TabContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 45.0 +margin_right = 422.0 +margin_bottom = 174.0 + +[node name="Scores" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer"] anchor_right = 1.0 anchor_bottom = 1.0 -margin_top = -20.0 -theme = ExtResource( 5 ) -text = "Restart" +margin_left = 8.0 +margin_top = 61.0 +margin_right = -8.0 +margin_bottom = -8.0 + +[node name="Label" type="Label" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores"] +margin_right = 406.0 +margin_bottom = 25.0 +text = "High scores:" + +[node name="ScoresList" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores"] +margin_top = 33.0 +margin_right = 406.0 +margin_bottom = 33.0 + +[node name="Label" type="Label" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Scores/ScoresList"] +margin_right = 40.0 +margin_bottom = 25.0 +text = "No high scores yet!" -[node name="Camera2D" type="Camera2D" parent="."] -current = true +[node name="Pref" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer"] +visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 61.0 +margin_right = -8.0 +margin_bottom = -8.0 + +[node name="AlwaysShowIntroCheckBox" type="CheckBox" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Pref"] +margin_right = 406.0 +margin_bottom = 48.0 +text = "Show intro dialog" -[node name="CanvasLayer2" type="CanvasLayer" parent="."] +[node name="About" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer"] visible = false +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 61.0 +margin_right = -8.0 +margin_bottom = -8.0 + +[node name="RichTextLabel" type="RichTextLabel" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About"] +margin_right = 406.0 +margin_bottom = 27.0 +rect_min_size = Vector2( 200, 0 ) +text = "Welcome to my Godot experiment." +fit_content_height = true + +[node name="LinkButton" type="LinkButton" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About"] +margin_top = 35.0 +margin_right = 406.0 +margin_bottom = 60.0 +focus_mode = 2 +text = "Source code" + +[node name="ResumeButton" type="Button" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 182.0 +margin_right = 422.0 +margin_bottom = 219.0 +theme = ExtResource( 5 ) +text = "Resume" -[node name="Background" type="ColorRect" parent="CanvasLayer2"] +[node name="GameFinishedMenu" type="Control" parent="UI"] +pause_mode = 2 +visible = false anchor_right = 1.0 anchor_bottom = 1.0 -margin_top = -2.0 -margin_bottom = -2.0 -color = Color( 0.811765, 0, 0, 1 ) +mouse_filter = 2 +theme = ExtResource( 9 ) + +[node name="CenterContainer" type="CenterContainer" parent="UI/GameFinishedMenu"] +anchor_right = 1.0 +anchor_bottom = 1.0 + +[node name="PanelContainer" type="PanelContainer" parent="UI/GameFinishedMenu/CenterContainer"] +margin_left = 236.0 +margin_top = 386.0 +margin_right = 264.0 +margin_bottom = 414.0 + +[node name="VBoxContainer" type="VBoxContainer" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer"] +margin_left = 14.0 +margin_top = 14.0 +margin_right = 182.0 +margin_bottom = 208.0 + +[node name="Label" type="Label" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_right = 168.0 +margin_bottom = 118.0 +theme = ExtResource( 9 ) +text = "You've won!" +align = 1 +valign = 1 +autowrap = true + +[node name="Button" type="Button" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer/VBoxContainer"] +margin_top = 126.0 +margin_right = 168.0 +margin_bottom = 194.0 +text = "Replay!" + +[node name="Camera2D" type="Camera2D" parent="."] +current = true [node name="ShotTimer" type="Timer" parent="."] wait_time = 0.5 @@ -295,5 +358,12 @@ __meta__ = { "_gizmo_extents_": 14.2 } -[connection signal="pressed" from="CanvasLayer/CenterContainer/DeathScreen/Panel/Button" to="." method="_on_Button_pressed"] +[connection signal="debug_signal" from="UI/HUD" to="." method="_on_HUD_debug_signal"] +[connection signal="paused" from="UI/HUD" to="." method="_on_HUD_paused"] +[connection signal="position_set" from="UI/HUD" to="." method="_on_HUD_position_set"] +[connection signal="pressed" from="UI/DeathScreen/CenterContainer/PanelContainer/VBoxContainer/Button" to="." method="_on_Button_pressed"] +[connection signal="pressed" from="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/StartButton" to="." method="_on_StartButton_pressed"] +[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Pref/AlwaysShowIntroCheckBox" to="." method="_on_PauseAlwaysShowIntroCheckBox_pressed"] +[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About/LinkButton" to="." method="_on_LinkButton_pressed"] +[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/ResumeButton" to="." method="_on_ResumeButton_pressed"] [connection signal="timeout" from="ShotTimer" to="." method="allow_fire"] diff --git a/Rocket.gd b/Rocket.gd index 3e07107..afc663f 100644 --- a/Rocket.gd +++ b/Rocket.gd @@ -9,7 +9,6 @@ export var arm_secs = 1 export var damage_amount = 2 func _ready() -> void: -# $AnimationPlayer.play("arming") pass func _physics_process(delta: float) -> void: diff --git a/Settings.gd b/Settings.gd index 71e0da0..3fc05a2 100644 --- a/Settings.gd +++ b/Settings.gd @@ -1,17 +1,39 @@ class_name Settings var show_intro = true +var high_scores: Array = [ +] + +class HighScore: + var score: int + var date: Dictionary + func _init(s: int, d: Dictionary): + score = s + date = d + func to_json() -> Dictionary: + return { + "score": score, + "date": date + } const settings_path = "user://settings.json" func to_json() -> Dictionary: - return { - "show_intro": show_intro + var scores = [] + for hs in high_scores: + var high_score: HighScore = hs + var json = high_score.to_json() + scores.push_back(json) + var final_obj = { + "show_intro": show_intro, + "high_scores": scores } + return final_obj func save_user_settings(path = settings_path) -> void: var save_game: File = File.new() - save_game.open(path, File.WRITE) + var _err = save_game.open(path, File.WRITE) var json_data = JSON.print(self.to_json()) - save_game.store_line(json_data) + print("saving settings:", json_data) + save_game.store_string(json_data) save_game.close() diff --git a/Utils.gd b/Utils.gd index 8458c2a..e9d6e0c 100644 --- a/Utils.gd +++ b/Utils.gd @@ -11,13 +11,22 @@ static func load_user_settings(path = SettingsClass.settings_path) -> Settings: var save_game: File = File.new() var settings_exists = save_game.file_exists(path) if settings_exists: - save_game.open(path, File.READ) - var current_line = save_game.get_line() - save_game.close() - var json = parse_json(current_line) - var data: Settings = Settings.new() - data.show_intro = json["show_intro"] - return data + var _err = save_game.open(path, File.READ) + var text = save_game.get_as_text() + if text.length() > 0: + var json = parse_json(text) + var data: Settings = Settings.new() + data.show_intro = json["show_intro"] + if json.has("high_scores"): + data.high_scores = [] +# print("high scores", high_scores) + for hs in json["high_scores"]: + var score = hs["score"] + var date = hs["date"] + var high_score: Settings.HighScore = Settings.HighScore.new(score, date) + data.high_scores.push_back(high_score) + # Utils.log("high scores", high_scores) + return data var data: Settings = Settings.new() return data @@ -64,3 +73,40 @@ static func dist_to_ratio(dist: float, min_dist: float, max_dist: float) -> floa # var exponential_decay_curve = 1.0 - exp(-t * 5.0) # var inverse_exponential_curve = 1.0 - (1.0 - exp(-t * 5.0)) return linear_curve + +static func time_diff_secs(a: Dictionary, b: Dictionary) -> float: + var at = Time.get_unix_time_from_datetime_dict(a) + var bt = Time.get_unix_time_from_datetime_dict(b) + var diff = at - bt + return diff + +static func timespan_to_english(seconds: float) -> String: + var is_past = seconds < 0 + seconds = abs(seconds) + var minutes = seconds / 60 + var hours = minutes / 60 + var days = hours / 24 + var weeks = days / 7 + var result = "" + if weeks > 1 and result == "": + result += "%1.f weeks" % weeks + if days > 1 and result == "": + result += "%1.f days" % days + if hours > 1 and result == "": + result += "%1.f hours" % hours + if minutes > 1 and result == "": + result += "%1.f minutes" % minutes + if seconds > 1 and result == "": + result += "%1.f seconds" % seconds + + if result == "": + result = "just now" + elif is_past: + result += " ago" + return result + +static func remove_all_children(node: Node) -> void: + var count = node.get_child_count() + for i in count: + var child = node.get_child(0) + node.remove_child(child) diff --git a/World.gd b/World.gd index 80275f8..a433217 100644 --- a/World.gd +++ b/World.gd @@ -7,8 +7,7 @@ const PowerUp = preload("res://PowerUp.tscn") const Settings = preload("res://Settings.gd") var width = ProjectSettings.get("display/window/size/width") var height = ProjectSettings.get("display/window/size/height") - -onready var player = $Player +onready var player: Player = $Player var destination: Vector2 var is_destination_set = false @@ -17,25 +16,20 @@ var generators = [ ConstantItemGenerator.new("create_health", self, "health", 10), ConstantItemGenerator.new("create_weapon", self, "weapon", 10), LevelItemGenerator.new(5, "create_gold", self, "gold", 5), -# LevelItemGenerator.new(3, "create_shield", self, "shields", 3), ] var completed_objectives = 0 var max_objectives = 5 var item_replacement_distance = 2000 var is_dragging = false -var settings: Settings func enemy_killed(_position: Vector2): - $UI/HUD.score += 5 - $UI/HUD.show_message("Enemy killed!", 1) + player.score += 5 + player.show_message("Enemy killed!", 1) func health_consumed(_data): $Player.heal(10) -func shield_consumed(_data): - Utils.log("consuming a shield and sending a message") - $Player.enable_shield() func gold_consumed(_data): completed_objectives += 1 - $UI/HUD.score += 1 + player.score += 1 func weapon_consumed(data): var weapon: Weapon = data var is_same_weapon = $Player.weapon.name == weapon.name @@ -81,21 +75,6 @@ func create_weapon() -> PowerUp: weapon.label = "LZR" weapon.color = Color.pink return weapon -func create_shield() -> PowerUp: - var shield = PowerUp.instance() - shield.position = get_new_item_pos(randf() + .5) - shield.connect("consumed", self, "shield_consumed") - shield.label = "Shield" - shield.color = Color.blue - return shield - -func _ready() -> void: - settings = Utils.load_user_settings() - var pause_menu_show_intro_check = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Settings/CheckBox - pause_menu_show_intro_check.pressed = settings.show_intro - if settings.show_intro: - get_tree().paused = true - $UI/IntroMenu.visible = true func get_new_item_pos(dist_ratio: float = 1.0) -> Vector2: var pos_angle = randi() % 360 @@ -131,16 +110,12 @@ func _input(_event: InputEvent) -> void: return func _process(_delta: float) -> void: - if Input.is_action_just_pressed("pause"): - pause() - return - - $UI/HUD.objective = "%s / %s" % [completed_objectives, max_objectives] - $UI/HUD.set_current_weapon(player.weapon) + player.set_objective("%s / %s" % [completed_objectives, max_objectives]) var all_done = completed_objectives == max_objectives if all_done: get_tree().paused = true var score = $UI/HUD.score + player.add_high_score(score) var final_message = "You won with %s points!" % score var _err = ($UI/GameFinishedMenu.find_node("Button") as Button).connect("pressed", self, "restart") $UI/GameFinishedMenu.find_node("Label").text = final_message @@ -174,10 +149,6 @@ func _process(_delta: float) -> void: player.update_radar(get_tree().get_nodes_in_group("gold"), Color.yellow) player.update_radar(filteredEnemies, Color.red) -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 @@ -228,43 +199,16 @@ func _on_Game_over_button_pressed() -> void: $UI/GameFinishedMenu.hide() restart() -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() - func restart() -> void: get_tree().paused = false var _err = get_tree().reload_current_scene() -func _on_HUD_debug_signal() -> void: - var p = $Player.global_position - - set_destination(Vector2(p.x, p.y - 1000)) - pass - -func _on_LinkButton_pressed() -> void: - get_tree().paused = true - var _err = OS.shell_open("https://gitlab.com/PavelSafronov/godot-shooter") - get_tree().paused = true - - -func _on_StartButton_pressed() -> void: - get_tree().paused = false - $UI/IntroMenu.visible = false - settings.show_intro = $UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/CheckBox.pressed - settings.save_user_settings() - var pause_menu_show_intro_check = $UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Settings/CheckBox - pause_menu_show_intro_check.pressed = settings.show_intro - get_tree().set_input_as_handled() +func _on_Player_destination_set(position) -> void: + set_destination(position) + pass # Replace with function body. -func _on_CheckBox_pressed() -> void: - var show_intro_check = $UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/CheckBox - var show_intro = show_intro_check.pressed - set_intro_dialog(show_intro) -func set_intro_dialog(enabled: bool) -> void: - settings.show_intro = enabled - settings.save_user_settings() +func _on_Player_debug() -> void: + player.score += 1 + player.kill() + pass # Replace with function body. diff --git a/World.tscn b/World.tscn index bc211d9..123aab8 100644 --- a/World.tscn +++ b/World.tscn @@ -1,35 +1,13 @@ -[gd_scene load_steps=14 format=2] +[gd_scene load_steps=6 format=2] [ext_resource path="res://assets/kenney_simple-space/PNG/Default/icon_crossLarge.png" type="Texture" id=1] [ext_resource path="res://Player.tscn" type="PackedScene" id=2] -[ext_resource path="res://HUD.tscn" type="PackedScene" id=3] [ext_resource path="res://World.gd" type="Script" id=4] [ext_resource path="res://StarField.tscn" type="PackedScene" id=5] -[ext_resource path="res://CharacterTheme.tres" type="Theme" id=6] -[ext_resource path="res://Theme.tres" type="Theme" id=7] -[ext_resource path="res://assets/neuropolitical rg.otf" type="DynamicFontData" id=8] [sub_resource type="CircleShape2D" id=1] radius = 29.0172 -[sub_resource type="DynamicFont" id=2] -size = 24 -font_data = ExtResource( 8 ) - -[sub_resource type="DynamicFont" id=3] -outline_color = Color( 0, 0.490196, 0.831373, 1 ) -use_mipmaps = true -use_filter = true -font_data = ExtResource( 8 ) - -[sub_resource type="Theme" id=4] -default_font = SubResource( 3 ) - -[sub_resource type="DynamicFont" id=5] -use_mipmaps = true -use_filter = true -font_data = ExtResource( 8 ) - [node name="World" type="Node2D"] script = ExtResource( 4 ) @@ -77,190 +55,6 @@ __meta__ = { [node name="Environment" type="Node2D" parent="."] -[node name="UI" type="CanvasLayer" parent="."] -pause_mode = 2 - -[node name="HUD" parent="UI" instance=ExtResource( 3 )] -pause_mode = 2 - -[node name="PauseMenu" type="Control" parent="UI" groups=["pause_menu"]] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -mouse_filter = 2 - -[node name="CenterContainer" type="CenterContainer" parent="UI/PauseMenu"] -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="PanelContainer" type="PanelContainer" parent="UI/PauseMenu/CenterContainer"] -margin_left = 50.0 -margin_top = 386.0 -margin_right = 450.0 -margin_bottom = 414.0 -rect_min_size = Vector2( 400, 0 ) - -[node name="VBoxContainer" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer"] -margin_left = 14.0 -margin_top = 14.0 -margin_right = 386.0 -margin_bottom = 227.0 - -[node name="Label" type="Label" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_right = 372.0 -margin_bottom = 37.0 -theme = ExtResource( 7 ) -text = "Paused" -align = 1 - -[node name="TabContainer" type="TabContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 45.0 -margin_right = 372.0 -margin_bottom = 168.0 - -[node name="Settings" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer"] -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = 8.0 -margin_top = 61.0 -margin_right = -8.0 -margin_bottom = -8.0 - -[node name="CheckBox" type="CheckBox" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Settings"] -margin_right = 48.0 -margin_bottom = 48.0 -text = "Show intro dialog" - -[node name="About" type="VBoxContainer" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer"] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 -margin_left = 8.0 -margin_top = 61.0 -margin_right = -8.0 -margin_bottom = -8.0 - -[node name="RichTextLabel" type="RichTextLabel" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About"] -margin_right = 356.0 -margin_bottom = 54.0 -rect_min_size = Vector2( 200, 0 ) -text = "Welcome to my Godot experiment." -fit_content_height = true - -[node name="LinkButton" type="LinkButton" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About"] -margin_top = 89.0 -margin_right = 200.0 -margin_bottom = 114.0 -text = "Source code" - -[node name="Button" type="Button" parent="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 176.0 -margin_right = 372.0 -margin_bottom = 213.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 -mouse_filter = 2 -theme = ExtResource( 7 ) - -[node name="CenterContainer" type="CenterContainer" parent="UI/GameFinishedMenu"] -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="PanelContainer" type="PanelContainer" parent="UI/GameFinishedMenu/CenterContainer"] -margin_left = 236.0 -margin_top = 386.0 -margin_right = 264.0 -margin_bottom = 414.0 - -[node name="VBoxContainer" type="VBoxContainer" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer"] -margin_left = 14.0 -margin_top = 14.0 -margin_right = 182.0 -margin_bottom = 208.0 - -[node name="Label" type="Label" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_right = 168.0 -margin_bottom = 118.0 -theme = ExtResource( 7 ) -text = "You've won!" -align = 1 -valign = 1 -autowrap = true - -[node name="Button" type="Button" parent="UI/GameFinishedMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 126.0 -margin_right = 168.0 -margin_bottom = 194.0 -text = "Replay!" - -[node name="IntroMenu" type="Control" parent="UI" groups=["pause_menu"]] -visible = false -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="CenterContainer" type="CenterContainer" parent="UI/IntroMenu"] -anchor_right = 1.0 -anchor_bottom = 1.0 - -[node name="PanelContainer" type="PanelContainer" parent="UI/IntroMenu/CenterContainer"] -margin_left = 236.0 -margin_top = 386.0 -margin_right = 264.0 -margin_bottom = 414.0 - -[node name="VBoxContainer" type="VBoxContainer" parent="UI/IntroMenu/CenterContainer/PanelContainer"] -margin_left = 14.0 -margin_top = 14.0 -margin_right = 447.0 -margin_bottom = 451.0 -theme = ExtResource( 7 ) -alignment = 1 - -[node name="Label" type="Label" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_right = 433.0 -margin_bottom = 30.0 -custom_fonts/font = SubResource( 2 ) -text = "Welcome to Tactical Shooter!" - -[node name="RichTextLabel" type="RichTextLabel" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 38.0 -margin_right = 433.0 -margin_bottom = 324.0 -theme = SubResource( 4 ) -text = "This is a 2D space shooter game. - -You choose where to fly in this game. - -Your ship will attack for you. - -Touch/click + hold anywhere to pause the game and plan your course. -Let go to set location as the destination. - -Your objective count is in the lower-right corner. -The radar shows enemies and objectives." -fit_content_height = true - -[node name="CheckBox" type="CheckBox" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 332.0 -margin_right = 433.0 -margin_bottom = 380.0 -custom_fonts/font = SubResource( 5 ) -pressed = true -text = "Always show intro" - -[node name="StartButton" type="Button" parent="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer"] -margin_top = 388.0 -margin_right = 433.0 -margin_bottom = 437.0 -mouse_default_cursor_shape = 2 -text = "Start Game" - [node name="ShootTimer" type="Timer" parent="."] process_mode = 0 wait_time = 0.05 @@ -279,11 +73,6 @@ round_precision = 13 antialiased = true [connection signal="body_entered" from="Destination" to="." method="_on_Destination_body_entered"] -[connection signal="debug_signal" from="UI/HUD" to="." method="_on_HUD_debug_signal"] -[connection signal="paused" from="UI/HUD" to="." method="_on_HUD_paused"] -[connection signal="position_set" from="UI/HUD" to="." method="set_destination"] -[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/Settings/CheckBox" to="." method="_on_CheckBox_pressed"] -[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/TabContainer/About/LinkButton" to="." method="_on_LinkButton_pressed"] -[connection signal="pressed" from="UI/PauseMenu/CenterContainer/PanelContainer/VBoxContainer/Button" to="." method="_on_Button_pressed"] -[connection signal="pressed" from="UI/IntroMenu/CenterContainer/PanelContainer/VBoxContainer/StartButton" to="." method="_on_StartButton_pressed"] +[connection signal="debug" from="Player" to="." method="_on_Player_debug"] +[connection signal="destination_set" from="Player" to="." method="_on_Player_destination_set"] [connection signal="timeout" from="ShootTimer" to="." method="_on_ShootTimer_timeout"]