From ddef4c8736fedd8f5189b55481597d671bf5fc2d Mon Sep 17 00:00:00 2001 From: Rob Kelly Date: Thu, 19 Dec 2024 19:48:58 -0700 Subject: [PATCH] Added practice mode --- assets/text/text.csv | 3 + levels/oneill/oneill.tres | 4 +- levels/oneill/preview.jpg | 3 + levels/oneill/preview.jpg.import | 34 +++ src/game/game.tscn | 1 - .../deathmatch_setup/deathmatch_setup.gd | 4 +- .../practice_setup/practice_setup.gd | 55 +++++ .../practice_setup/practice_setup.tscn | 197 ++++++++++++++++++ src/ui/menus/title_screen/title_screen.gd | 8 + src/ui/menus/title_screen/title_screen.tscn | 8 +- src/world/play_manager/practice_manager.gd | 23 ++ src/world/world.gd | 2 + 12 files changed, 334 insertions(+), 8 deletions(-) create mode 100644 levels/oneill/preview.jpg create mode 100644 levels/oneill/preview.jpg.import create mode 100644 src/ui/menus/title_screen/practice_setup/practice_setup.gd create mode 100644 src/ui/menus/title_screen/practice_setup/practice_setup.tscn create mode 100644 src/world/play_manager/practice_manager.gd diff --git a/assets/text/text.csv b/assets/text/text.csv index 3bcee61..cebdbd8 100644 --- a/assets/text/text.csv +++ b/assets/text/text.csv @@ -31,6 +31,9 @@ DM_PLAYERS,Gfolfers DM_ADD_PLAYER,"Add a Gfolfer" DM_NAME_PLACEHOLDER,"Enter a name" , +PRACTICE_HEADING,"Practice your GFOLFing" +PRACTICE_MAP,"Select a Map" +, PAUSE_HEADING,Paused PAUSE_RESUME,Resume PAUSE_SETTINGS,Settings diff --git a/levels/oneill/oneill.tres b/levels/oneill/oneill.tres index 45d70e1..7cb0c88 100644 --- a/levels/oneill/oneill.tres +++ b/levels/oneill/oneill.tres @@ -1,9 +1,11 @@ -[gd_resource type="Resource" script_class="Level" load_steps=3 format=3 uid="uid://cfsy1nlfo4inx"] +[gd_resource type="Resource" script_class="Level" load_steps=4 format=3 uid="uid://cfsy1nlfo4inx"] [ext_resource type="PackedScene" uid="uid://x2bqqlrnno28" path="res://levels/oneill/oneill.tscn" id="1_431yv"] +[ext_resource type="Texture2D" uid="uid://dlq6b8frq21o2" path="res://levels/oneill/preview.jpg" id="1_n552e"] [ext_resource type="Script" path="res://src/world/level/level.gd" id="2_fnj1e"] [resource] script = ExtResource("2_fnj1e") scene = ExtResource("1_431yv") level_name = "MAP_ONEILL" +preview = ExtResource("1_n552e") diff --git a/levels/oneill/preview.jpg b/levels/oneill/preview.jpg new file mode 100644 index 0000000..44bcf0b --- /dev/null +++ b/levels/oneill/preview.jpg @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:125325375bfbffbd5f1dffbbef5b4603486d7ddfcdef3691f7908e1113f522e7 +size 139756 diff --git a/levels/oneill/preview.jpg.import b/levels/oneill/preview.jpg.import new file mode 100644 index 0000000..1e2e422 --- /dev/null +++ b/levels/oneill/preview.jpg.import @@ -0,0 +1,34 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://dlq6b8frq21o2" +path="res://.godot/imported/preview.jpg-15d3a679d46e9934ca032ccdfbdfe6af.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://levels/oneill/preview.jpg" +dest_files=["res://.godot/imported/preview.jpg-15d3a679d46e9934ca032ccdfbdfe6af.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/src/game/game.tscn b/src/game/game.tscn index 8bb2df8..6e09999 100644 --- a/src/game/game.tscn +++ b/src/game/game.tscn @@ -119,7 +119,6 @@ _data = { [node name="Game" type="Node" groups=["GameGroup"]] process_mode = 3 script = ExtResource("1_4qa87") -start_scene = "res://src/world/world.tscn" [node name="RootControl" type="Control" parent="."] unique_name_in_owner = true diff --git a/src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.gd b/src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.gd index 9916e2e..58ce95a 100644 --- a/src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.gd +++ b/src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.gd @@ -1,7 +1,5 @@ extends PanelContainer -const WORLD_SCENE_PATH := "res://src/world/world.tscn" - const RANDOM_PLAYER_COLORS := [ Color.LIGHT_CORAL, Color.GOLDENROD, @@ -54,7 +52,7 @@ func start() -> void: world.manager = RoundRobinManager.new() world.manager.players = player_list world.initial_level = level.scene - game.queue_scene(WORLD_SCENE_PATH).then(init_world) + game.queue_scene(World.SCENE).then(init_world) ## Returns the `Level` selected by the user if there is one, or `null` if no level is selected. diff --git a/src/ui/menus/title_screen/practice_setup/practice_setup.gd b/src/ui/menus/title_screen/practice_setup/practice_setup.gd new file mode 100644 index 0000000..360a3d4 --- /dev/null +++ b/src/ui/menus/title_screen/practice_setup/practice_setup.gd @@ -0,0 +1,55 @@ +extends PanelContainer + +@export var practice_player: WorldPlayer + +@onready var map_select: ItemList = %MapSelect + +@onready var start_button: Button = %Start + +@onready var game: Game = get_tree().get_first_node_in_group(Game.group) + + +func play_chime() -> void: + game.sfx.chime.play() + + +func _ready() -> void: + # Populate map selection + for level: Level in Level.catalog.levels: + map_select.add_item(level.level_name, level.preview) + + check_start_conditions() + + +## Close menu without starting game. +func cancel() -> void: + queue_free() + + +## Start with the current game configuration. +func start() -> void: + var manager := PracticeManager.new() + manager.players = [practice_player] + var level := selected_level() + var init_world := func(world: World) -> void: + world.manager = manager + world.initial_level = level.scene + game.queue_scene(World.SCENE).then(init_world) + + +## Returns the `Level` selected by the user if there is one, or `null` if no level is selected. +func selected_level() -> Level: + var selections := map_select.get_selected_items() + if selections: + return Level.catalog.levels[selections[0]] + return null + + +func _can_start() -> bool: + return selected_level() != null + + +## Check if the game can be started with the current selection, +## and set the start button status appropriately. +func check_start_conditions() -> void: + start_button.disabled = not _can_start() diff --git a/src/ui/menus/title_screen/practice_setup/practice_setup.tscn b/src/ui/menus/title_screen/practice_setup/practice_setup.tscn new file mode 100644 index 0000000..dbe9edb --- /dev/null +++ b/src/ui/menus/title_screen/practice_setup/practice_setup.tscn @@ -0,0 +1,197 @@ +[gd_scene load_steps=11 format=3 uid="uid://5soelwtn4v34"] + +[ext_resource type="Script" path="res://src/ui/menus/title_screen/practice_setup/practice_setup.gd" id="1_jgggo"] +[ext_resource type="Resource" uid="uid://c1pnqsddvey3m" path="res://src/equipment/clubs/drivers/debug_driver.tres" id="2_47376"] +[ext_resource type="Resource" uid="uid://ck17u5yn6k0bi" path="res://src/equipment/clubs/irons/debug_iron.tres" id="3_veaoo"] +[ext_resource type="Resource" uid="uid://dagld0q5krapu" path="res://src/equipment/clubs/putters/debug_putter.tres" id="4_mixgr"] +[ext_resource type="Script" path="res://src/player/world_player.gd" id="5_btf7p"] +[ext_resource type="Resource" uid="uid://dthtc1no2c4wy" path="res://src/equipment/clubs/wedges/debug_wedge.tres" id="6_8yfm7"] + +[sub_resource type="Resource" id="Resource_4ls7o"] +script = ExtResource("5_btf7p") +life = 100.0 +name = "Practice Gfolfer" +color = Color(0.955, 0.7, 1, 1) +driver = ExtResource("2_47376") +iron = ExtResource("3_veaoo") +wedge = ExtResource("6_8yfm7") +putter = ExtResource("4_mixgr") +_balls = { +1: -1, +2: -1, +3: -1, +4: -1, +5: -1 +} + +[sub_resource type="Animation" id="Animation_x4wqc"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:disabled") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 2 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} + +[sub_resource type="Animation" id="Animation_560ro"] +resource_name = "fade_out" +length = 0.6 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:modulate") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.0666667, 0.133333, 0.2, 0.266667, 0.333333, 0.4, 0.466667, 0.533333, 0.6), +"transitions": PackedFloat32Array(1, 1, 1, 1, 1, 1, 1, 1, 1, 1), +"update": 0, +"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 0.5), Color(1, 1, 1, 0), Color(1, 1, 1, 0.25), Color(1, 1, 1, 0), Color(1, 1, 1, 0.125), Color(1, 1, 1, 0)] +} +tracks/1/type = "method" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("../../..") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.6), +"transitions": PackedFloat32Array(1, 1), +"values": [{ +"args": [], +"method": &"play_chime" +}, { +"args": [], +"method": &"start" +}] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath(".:disabled") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_s1eyj"] +_data = { +"RESET": SubResource("Animation_x4wqc"), +"fade_out": SubResource("Animation_560ro") +} + +[node name="PracticeSetup" type="PanelContainer"] +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_jgggo") +practice_player = SubResource("Resource_4ls7o") + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +theme_override_constants/margin_left = 32 +theme_override_constants/margin_top = 32 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 80 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_type_variation = &"HeaderXLarge" +text = "PRACTICE_HEADING" + +[node name="MapSelectContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/MapSelectContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 16 +theme_override_constants/margin_bottom = 16 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer"] +layout_mode = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_type_variation = &"HeaderLarge" +text = "PRACTICE_MAP" + +[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="MapSelect" type="ItemList" parent="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer"] +unique_name_in_owner = true +custom_minimum_size = Vector2(0, 130) +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/v_separation = 32 +theme_override_constants/h_separation = 32 +max_columns = 8 +icon_mode = 0 +fixed_icon_size = Vector2i(64, 64) + +[node name="SouthEast" type="MarginContainer" parent="."] +layout_mode = 2 +size_flags_horizontal = 8 +size_flags_vertical = 8 +theme_override_constants/margin_left = 16 +theme_override_constants/margin_top = 16 +theme_override_constants/margin_right = 32 +theme_override_constants/margin_bottom = 16 + +[node name="HBoxContainer" type="HBoxContainer" parent="SouthEast"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Cancel" type="Button" parent="SouthEast/HBoxContainer"] +layout_mode = 2 +theme_type_variation = &"CancelButton" +text = "UI_CANCEL" + +[node name="Start" type="Button" parent="SouthEast/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_type_variation = &"AlertButton" +text = "UI_START" + +[node name="AnimationPlayer" type="AnimationPlayer" parent="SouthEast/HBoxContainer/Start"] +libraries = { +"": SubResource("AnimationLibrary_s1eyj") +} + +[connection signal="item_selected" from="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer/MapSelect" to="." method="check_start_conditions" unbinds=1] +[connection signal="pressed" from="SouthEast/HBoxContainer/Cancel" to="." method="cancel"] +[connection signal="pressed" from="SouthEast/HBoxContainer/Start" to="SouthEast/HBoxContainer/Start/AnimationPlayer" method="play" binds= ["fade_out"]] diff --git a/src/ui/menus/title_screen/title_screen.gd b/src/ui/menus/title_screen/title_screen.gd index ceb8e4d..e66f2b5 100644 --- a/src/ui/menus/title_screen/title_screen.gd +++ b/src/ui/menus/title_screen/title_screen.gd @@ -5,6 +5,7 @@ const SCENE := "res://src/ui/menus/title_screen/title_screen.tscn" @export var title_crawl_scene: PackedScene @export var settings_scene: PackedScene @export var local_deathmatch_setup_scene: PackedScene +@export var practice_setup_scene: PackedScene @onready var menu: Control = %Menu @onready var settings_container: MarginContainer = %SettingsContainer @@ -84,3 +85,10 @@ func _open_local_deathmatch_setup() -> void: deathmatch_setup_container.add_child(instance) instance.tree_exited.connect(_unhide) _hide() + + +func _open_practice_setup() -> void: + var instance: Control = practice_setup_scene.instantiate() + deathmatch_setup_container.add_child(instance) + instance.tree_exited.connect(_unhide) + _hide() diff --git a/src/ui/menus/title_screen/title_screen.tscn b/src/ui/menus/title_screen/title_screen.tscn index 1e75ea7..b3d496d 100644 --- a/src/ui/menus/title_screen/title_screen.tscn +++ b/src/ui/menus/title_screen/title_screen.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=86 format=3 uid="uid://7fsgocmdas7i"] +[gd_scene load_steps=87 format=3 uid="uid://7fsgocmdas7i"] [ext_resource type="Script" path="res://src/ui/menus/title_screen/title_screen.gd" id="1_2qtlb"] [ext_resource type="Texture2D" uid="uid://880x5n8j3b5l" path="res://assets/logo/title.png" id="1_pm82i"] @@ -6,6 +6,7 @@ [ext_resource type="Texture2D" uid="uid://clesl8lljs6of" path="res://assets/logo/subtitle.png" id="2_y0ulk"] [ext_resource type="PackedScene" uid="uid://ccx2u5oli6men" path="res://src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.tscn" id="3_bo4ty"] [ext_resource type="Material" uid="uid://dpsmjlhjpc7vs" path="res://assets/materials/basic_ball_material.tres" id="5_5q83g"] +[ext_resource type="PackedScene" uid="uid://5soelwtn4v34" path="res://src/ui/menus/title_screen/practice_setup/practice_setup.tscn" id="5_hws1o"] [ext_resource type="PackedScene" uid="uid://cqu315hviu72n" path="res://src/ui/menus/title_screen/title_crawl.tscn" id="7_r26eu"] [sub_resource type="Environment" id="Environment_ardux"] @@ -1362,6 +1363,7 @@ script = ExtResource("1_2qtlb") title_crawl_scene = ExtResource("7_r26eu") settings_scene = ExtResource("2_g5q2v") local_deathmatch_setup_scene = ExtResource("3_bo4ty") +practice_setup_scene = ExtResource("5_hws1o") [node name="SceneRoot" type="Node3D" parent="."] @@ -1480,7 +1482,7 @@ grow_horizontal = 2 grow_vertical = 2 [node name="PressStart" type="Label" parent="Menu"] -modulate = Color(1, 1, 1, 0.850113) +modulate = Color(1, 1, 1, 0.936746) layout_mode = 1 anchors_preset = 8 anchor_left = 0.5 @@ -1576,7 +1578,6 @@ text = "TITLE_ROGUELIKE" [node name="Practice" type="Button" parent="Menu/SingleplayerMenu"] layout_mode = 2 theme_type_variation = &"PauseMenuButton" -disabled = true text = "TITLE_PRACTICE" [node name="Back" type="Button" parent="Menu/SingleplayerMenu"] @@ -1743,6 +1744,7 @@ autostart = true [connection signal="pressed" from="Menu/MainMenu/MultiPlayer" to="." method="_to_multi_player"] [connection signal="pressed" from="Menu/MainMenu/Settings" to="." method="_open_settings"] [connection signal="pressed" from="Menu/MainMenu/Quit" to="Menu/MainMenu/Quit/AnimationPlayer" method="play" binds= ["fade_out"]] +[connection signal="pressed" from="Menu/SingleplayerMenu/Practice" to="." method="_open_practice_setup"] [connection signal="pressed" from="Menu/SingleplayerMenu/Back" to="." method="_to_main_menu"] [connection signal="pressed" from="Menu/MultiplayerMenu/LocalMultiplayer" to="." method="_to_local_multi"] [connection signal="pressed" from="Menu/MultiplayerMenu/NetMultiplayer" to="." method="_to_network_multi"] diff --git a/src/world/play_manager/practice_manager.gd b/src/world/play_manager/practice_manager.gd new file mode 100644 index 0000000..4d0e0a6 --- /dev/null +++ b/src/world/play_manager/practice_manager.gd @@ -0,0 +1,23 @@ +class_name PracticeManager extends PlayManager +## A single player can just kind of chill out and vibe... + +var player: WorldPlayer: + get: + return players[0] + set(value): + players[0] = value + + +func on_initialization() -> void: + # Set first player as active + player.shot_setup.phase = ShotSetup.Phase.AIM + + +func on_turn_finished(source: ShotSetup) -> void: + player.shot_setup.queue_restart() + turn_started.emit(player) + + +func on_player_death(player: WorldPlayer) -> void: + # TODO game over screen + winner.emit(player) diff --git a/src/world/world.gd b/src/world/world.gd index 81011ee..76cdf9a 100644 --- a/src/world/world.gd +++ b/src/world/world.gd @@ -4,6 +4,8 @@ class_name World extends Node ## A world contains player(s) and the active level, manages player states, ## and transitions between active levels. +const SCENE := "res://src/world/world.tscn" + @export var initial_level: PackedScene @export var manager: PlayManager