Added local multiplayer deathmatch setup

This commit is contained in:
Rob Kelly 2024-12-17 12:53:07 -07:00
parent ec1b73d7b3
commit a0ee9849eb
16 changed files with 1245 additions and 391 deletions

View File

@ -34,6 +34,8 @@ DM_NAME_PLACEHOLDER,"Enter a name"
PAUSE_HEADING,Paused
PAUSE_RESUME,Resume
PAUSE_SETTINGS,Settings
PAUSE_END,"End Game"
PAUSE_END_MSG,"End the current game?"
PAUSE_QUIT,Quit
PAUSE_QUIT_MSG,"Quit to desktop?"
,

1 keys en
34 PAUSE_HEADING Paused
35 PAUSE_RESUME Resume
36 PAUSE_SETTINGS Settings
37 PAUSE_END End Game
38 PAUSE_END_MSG End the current game?
39 PAUSE_QUIT Quit
40 PAUSE_QUIT_MSG Quit to desktop?
41

View File

@ -2,7 +2,7 @@
[ext_resource type="Texture2D" uid="uid://dlqet1c5a7j7k" path="res://levels/debug_level/preview.jpg" id="1_1ewcj"]
[ext_resource type="PackedScene" uid="uid://bm2o3mex10v11" path="res://levels/debug_level/debug_level.tscn" id="2_eu01o"]
[ext_resource type="Script" path="res://src/world/level_catalog/level.gd" id="3_83327"]
[ext_resource type="Script" path="res://src/world/level/level.gd" id="3_83327"]
[resource]
script = ExtResource("3_83327")

View File

@ -21,7 +21,7 @@ run/max_fps=60
GameRuntime="*res://src/game/game_runtime.gd"
ClubCatalog="*res://src/equipment/clubs/club_catalog.tscn"
LevelCatalog="*res://src/world/level/level_catalog.gd"
LevelCatalog="*res://src/world/level/level_catalog.tscn"
GameSettings="*res://src/game/game_settings.gd"
[debug]
@ -80,6 +80,13 @@ theme/custom="res://src/ui/main_theme.tres"
[input]
ui_start={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":-1,"button_index":6,"pressure":0.0,"pressed":true,"script":null)
, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194310,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null)
]
}
camera_forward={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":87,"key_label":0,"unicode":119,"location":0,"echo":false,"script":null)

View File

@ -1,2 +1,2 @@
gdtoolkit>=4.2.2,<4.3
gdtoolkit>=4.3.3,<4.4
setuptools>=69.5.1,<69.6

View File

@ -1,7 +1,7 @@
class_name Game extends Node
## Wrapper for the game application
@export var start_scene: String = "res://src/world/world.tscn"
@export var start_scene: String = "res://src/ui/menus/title_screen/title_screen.tscn"
var _loading_resources := {}
@ -14,7 +14,9 @@ var _loading_resources := {}
static var group := "GameGroup" # hey i'm group
## Typesafe accessor for GameSettings singleton
static var settings := GameSettings as GameSettingsType
static var settings: GameSettingsType:
get:
return GameSettings
class Promise:

View File

@ -1,4 +1,4 @@
[gd_resource type="Theme" load_steps=9 format=3 uid="uid://diodjft5u2cck"]
[gd_resource type="Theme" load_steps=10 format=3 uid="uid://diodjft5u2cck"]
[ext_resource type="FontFile" uid="uid://dsa0oh7c0h4pu" path="res://assets/fonts/Racing_Sans_One/RacingSansOne-Regular.ttf" id="1_3rv2b"]
[ext_resource type="FontFile" uid="uid://comihs66wounx" path="res://assets/fonts/Dokdo/Dokdo-Regular.ttf" id="1_eha6a"]
@ -6,6 +6,17 @@
[ext_resource type="FontFile" uid="uid://dyog4ex5nqfat" path="res://assets/fonts/promptfont/promptfont.otf" id="2_8kux8"]
[ext_resource type="FontFile" uid="uid://s4c1kf0rk2mb" path="res://assets/fonts/Geo/Geo-Regular.ttf" id="3_cee6l"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_5pkn5"]
content_margin_left = 16.0
content_margin_top = 8.0
content_margin_right = 16.0
content_margin_bottom = 8.0
bg_color = Color(0.1, 0.1, 0.1, 0.6)
corner_radius_top_left = 3
corner_radius_top_right = 3
corner_radius_bottom_right = 3
corner_radius_bottom_left = 3
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_rq1no"]
content_margin_left = 0.0
content_margin_top = 0.0
@ -62,6 +73,13 @@ ChannelMixerIcon/colors/font_outline_color = Color(0, 0, 0, 1)
ChannelMixerIcon/constants/outline_size = 5
ChannelMixerIcon/font_sizes/font_size = 24
ChannelMixerIcon/fonts/font = ExtResource("2_8kux8")
CharSelectButton/base_type = &"Button"
CharSelectButton/colors/font_color = Color(1, 1, 0.870588, 1)
CharSelectButton/colors/font_outline_color = Color(0, 0, 0, 1)
CharSelectButton/constants/outline_size = 8
CharSelectButton/font_sizes/font_size = 32
CharSelectButton/fonts/font = ExtResource("2_8kux8")
CharSelectButton/styles/normal = SubResource("StyleBoxFlat_5pkn5")
CheckerContainerEven/base_type = &"PanelContainer"
CheckerContainerEven/styles/panel = SubResource("StyleBoxFlat_rq1no")
CheckerContainerOdd/base_type = &"PanelContainer"
@ -93,6 +111,17 @@ HeaderSmall/font_sizes/font_size = 20
HeaderXLarge/base_type = &"Label"
HeaderXLarge/constants/outline_size = 10
HeaderXLarge/font_sizes/font_size = 36
IconButton/base_type = &"Button"
IconButton/colors/font_outline_color = Color(0, 0, 0, 1)
IconButton/constants/outline_size = 6
IconButton/font_sizes/font_size = 36
IconButton/fonts/font = ExtResource("2_8kux8")
IconXL/base_type = &"Label"
IconXL/colors/font_color = Color(1, 1, 0.870588, 1)
IconXL/colors/font_outline_color = Color(0, 0, 0, 1)
IconXL/constants/outline_size = 8
IconXL/font_sizes/font_size = 42
IconXL/fonts/font = ExtResource("2_8kux8")
InputBindButton/base_type = &"UIButton"
InputBindButton/colors/font_color = Color(1, 1, 0.870588, 1)
InputBindButton/constants/outline_size = 8
@ -108,6 +137,9 @@ PauseMenuButton/colors/font_outline_color = Color(0, 0, 0, 1)
PauseMenuButton/constants/outline_size = 6
PauseMenuButton/font_sizes/font_size = 32
PauseMenuButton/fonts/font = ExtResource("2_5ty6u")
PlayerNameInput/base_type = &"LineEdit"
PlayerNameInput/font_sizes/font_size = 32
PlayerNameInput/fonts/font = ExtResource("1_3rv2b")
QuantityLabel/base_type = &"Label"
QuantityLabel/colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
QuantityLabel/colors/font_outline_color = Color(1, 0.901961, 0.509804, 1)

View File

@ -1,23 +1,26 @@
extends Control
## Menu shown in-game when the user presses pause.
const SETTINGS_SCENE := preload("res://src/ui/menus/settings_menu/settings_menu.tscn")
@export var settings_scene: PackedScene
var _freeze_input := false
@onready var menu_list: VBoxContainer = %MenuList
@onready var quit_confirm: CenterContainer = %QuitConfirm
@onready var end_game_confirm: CenterContainer = %EndGameConfirm
@onready var settings_container: MarginContainer = %SettingsContainer
@onready var transition_animation: AnimationPlayer = %TransitionAnimation
@onready var game: Game = get_tree().get_first_node_in_group(Game.group)
func _ready() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_VISIBLE)
func _exit_tree() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
func _unhandled_key_input(event: InputEvent) -> void:
if event.is_action_pressed("pause"):
if not _freeze_input and event.is_action_pressed("pause"):
get_viewport().set_input_as_handled()
resume()
@ -31,11 +34,12 @@ func _hide() -> void:
func resume() -> void:
Input.set_mouse_mode(Input.MOUSE_MODE_CAPTURED)
queue_free()
func settings() -> void:
var instance: Control = SETTINGS_SCENE.instantiate()
var instance: Control = settings_scene.instantiate()
settings_container.add_child(instance)
instance.tree_exited.connect(_unhide)
_hide()
@ -51,3 +55,19 @@ func cancel_quit() -> void:
func confirm_quit() -> void:
get_tree().root.propagate_notification(NOTIFICATION_WM_CLOSE_REQUEST)
func end_game() -> void:
end_game_confirm.show()
func cancel_end_game() -> void:
end_game_confirm.hide()
func _return_to_title() -> void:
game.queue_scene(TitleScreen.SCENE)
func confirm_end_game() -> void:
transition_animation.play("fade_to_black")

View File

@ -1,7 +1,83 @@
[gd_scene load_steps=3 format=3 uid="uid://byvjsvavbg5xe"]
[gd_scene load_steps=7 format=3 uid="uid://byvjsvavbg5xe"]
[ext_resource type="FontFile" uid="uid://b6gxwgomstkgu" path="res://assets/fonts/Geo/Geo-Italic.ttf" id="1_4nw1f"]
[ext_resource type="Script" path="res://src/ui/menus/pause_menu/pause_menu.gd" id="1_rd0j2"]
[ext_resource type="PackedScene" uid="uid://d3eaqw2rdurct" path="res://src/ui/menus/settings_menu/settings_menu.tscn" id="2_kjr35"]
[sub_resource type="Animation" id="Animation_rq4bw"]
resource_name = "fade_to_black"
length = 0.6
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("TransitionAnimation/TransitionShade:color")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.6),
"transitions": PackedFloat32Array(1.618, 1),
"update": 0,
"values": [Color(0, 0, 0, 0), Color(0, 0, 0, 1)]
}
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.6),
"transitions": PackedFloat32Array(1),
"values": [{
"args": [],
"method": &"_return_to_title"
}]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("TransitionAnimation/TransitionShade:visible")
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="Animation" id="Animation_qmmn2"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("TransitionAnimation/TransitionShade:color")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(0, 0, 0, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("TransitionAnimation/TransitionShade:visible")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [false]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_7b7fo"]
_data = {
"RESET": SubResource("Animation_qmmn2"),
"fade_to_black": SubResource("Animation_rq4bw")
}
[node name="PauseMenu" type="Control"]
process_mode = 3
@ -12,6 +88,7 @@ anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_rd0j2")
settings_scene = ExtResource("2_kjr35")
[node name="Shade" type="ColorRect" parent="."]
layout_mode = 1
@ -57,6 +134,12 @@ layout_mode = 2
theme_type_variation = &"PauseMenuButton"
text = "PAUSE_SETTINGS"
[node name="EndGameButton" type="Button" parent="MarginContainer/MenuList"]
layout_mode = 2
theme_type_variation = &"PauseMenuButton"
theme_override_colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
text = "PAUSE_END"
[node name="QuitButton" type="Button" parent="MarginContainer/MenuList"]
layout_mode = 2
theme_type_variation = &"PauseMenuButton"
@ -124,8 +207,71 @@ size_flags_horizontal = 3
theme_type_variation = &"AlertButton"
text = "PAUSE_QUIT"
[node name="EndGameConfirm" type="CenterContainer" parent="."]
unique_name_in_owner = true
visible = false
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="PanelContainer" type="PanelContainer" parent="EndGameConfirm"]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="EndGameConfirm/PanelContainer"]
layout_mode = 2
theme_override_constants/margin_left = 8
theme_override_constants/margin_top = 8
theme_override_constants/margin_right = 8
theme_override_constants/margin_bottom = 8
[node name="VBoxContainer" type="VBoxContainer" parent="EndGameConfirm/PanelContainer/MarginContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_font_sizes/font_size = 24
text = "PAUSE_END_MSG"
horizontal_alignment = 1
[node name="HBoxContainer" type="HBoxContainer" parent="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
theme_override_constants/separation = 16
[node name="CancelButton" type="Button" parent="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"CancelButton"
text = "UI_CANCEL"
[node name="ConfirmEndGameButton" type="Button" parent="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"AlertButton"
text = "PAUSE_END"
[node name="TransitionAnimation" type="AnimationPlayer" parent="."]
unique_name_in_owner = true
libraries = {
"": SubResource("AnimationLibrary_7b7fo")
}
[node name="TransitionShade" type="ColorRect" parent="TransitionAnimation"]
visible = false
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
color = Color(0, 0, 0, 0)
[connection signal="pressed" from="MarginContainer/MenuList/ResumeButton" to="." method="resume"]
[connection signal="pressed" from="MarginContainer/MenuList/SettingsButton" to="." method="settings"]
[connection signal="pressed" from="MarginContainer/MenuList/EndGameButton" to="." method="end_game"]
[connection signal="pressed" from="MarginContainer/MenuList/QuitButton" to="." method="quit"]
[connection signal="pressed" from="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="cancel_quit"]
[connection signal="pressed" from="QuitConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ConfirmQuitButton" to="." method="confirm_quit"]
[connection signal="pressed" from="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/CancelButton" to="." method="cancel_end_game"]
[connection signal="pressed" from="EndGameConfirm/PanelContainer/MarginContainer/VBoxContainer/HBoxContainer/ConfirmEndGameButton" to="." method="confirm_end_game"]

View File

@ -0,0 +1,94 @@
extends PanelContainer
const WORLD_SCENE_PATH := "res://src/world/world.tscn"
const RANDOM_PLAYER_COLORS := [
Color.LIGHT_CORAL,
Color.GOLDENROD,
Color.KHAKI,
Color.AQUAMARINE,
Color.SKY_BLUE,
Color.VIOLET,
]
const MIN_PLAYERS := 2
@export var player_setup_scene: PackedScene
@onready var map_select: ItemList = %MapSelect
@onready var start_button: Button = %Start
@onready var player_setup_list: HBoxContainer = %PlayerSetupList
@onready var game: Game = get_tree().get_first_node_in_group(Game.group)
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()
func _input(event: InputEvent) -> void:
if event.is_action_pressed("ui_start"):
add_player(InputMethod.get_method(event))
## Close menu without starting game.
func cancel() -> void:
queue_free()
## Start with the current game configuration.
func start() -> void:
var player_list := players()
var init_world := func(world: World) -> void:
world.manager = RoundRobinManager.new()
world.manager.players = player_list
game.queue_scene(WORLD_SCENE_PATH).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
## Build the array of players represented by the selections made here.
func players() -> Array[WorldPlayer]:
var arr: Array[WorldPlayer] = []
arr.assign(
player_setup_list.get_children().map(
func(s: PlayerSetup) -> WorldPlayer: return s.build_player()
)
)
return arr
## Add a new PlayerSetup node for the given input method.
func add_player(input_method: InputMethod.Type) -> void:
var instance: PlayerSetup = player_setup_scene.instantiate()
instance.input_method = input_method
instance.player_color = RANDOM_PLAYER_COLORS.pick_random()
player_setup_list.add_child(instance)
instance.tree_exited.connect(check_start_conditions)
check_start_conditions()
func _can_start() -> bool:
return selected_level() and len(players()) >= MIN_PLAYERS
## 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()
func _on_add_player_button_pressed() -> void:
add_player(InputMethod.Type.MOUSE_KEYBOARD)

View File

@ -0,0 +1,264 @@
[gd_scene load_steps=7 format=3 uid="uid://ccx2u5oli6men"]
[ext_resource type="Script" path="res://src/ui/menus/title_screen/deathmatch_setup/deathmatch_setup.gd" id="1_bchix"]
[ext_resource type="PackedScene" uid="uid://cr13d4p235b5y" path="res://src/ui/menus/title_screen/deathmatch_setup/player_setup/player_setup.tscn" id="2_on4ab"]
[ext_resource type="PackedScene" uid="uid://b47goj32i6sdh" path="res://src/ui/elements/input_prompt/input_prompt.tscn" id="4_2vecx"]
[sub_resource type="Animation" id="Animation_r61vj"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("GamepadStartSymbol:modulate")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("KeyboardStartSymbol:modulate")
tracks/1/interp = 1
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_c3p73"]
resource_name = "rotate"
length = 4.8
loop_mode = 1
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("GamepadStartSymbol:modulate")
tracks/0/interp = 2
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 1.6, 2.4, 3.2, 4),
"transitions": PackedFloat32Array(1, 0.618, 1, 1.618, 1),
"update": 0,
"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("KeyboardStartSymbol:modulate")
tracks/1/interp = 2
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.8, 1.6, 4, 4.8),
"transitions": PackedFloat32Array(1, 1.618, 1, 0.618, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_5sl0m"]
_data = {
"RESET": SubResource("Animation_r61vj"),
"rotate": SubResource("Animation_c3p73")
}
[node name="DeathmatchSetup" type="PanelContainer"]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_bchix")
player_setup_scene = ExtResource("2_on4ab")
[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 = "DM_HEADING"
[node name="MapSelectContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
[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 = "DM_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
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="PlayersContainer" type="PanelContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/PlayersContainer"]
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/PlayersContainer/MarginContainer"]
layout_mode = 2
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="Label" type="Label" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"HeaderLarge"
text = "DM_PLAYERS"
[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_type_variation = &"SettingsListMargin"
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="PlayerSetupList" type="HBoxContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
[node name="AddPlayerButton" type="Button" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer"]
custom_minimum_size = Vector2(400, 0)
layout_mode = 2
[node name="CenterContainer" type="CenterContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton"]
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer"]
layout_mode = 2
alignment = 1
[node name="SymbolBlinker" type="Control" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer/VBoxContainer"]
custom_minimum_size = Vector2(64, 64)
layout_mode = 2
mouse_filter = 1
[node name="GamepadStartSymbol" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer/VBoxContainer/SymbolBlinker" instance=ExtResource("4_2vecx")]
modulate = Color(1, 1, 1, 0)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -56.5
offset_top = -19.5
offset_right = 56.5
offset_bottom = 19.5
text = "❓"
horizontal_alignment = 1
action = &"ui_start"
show_name = false
event_index = 1
[node name="KeyboardStartSymbol" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer/VBoxContainer/SymbolBlinker" instance=ExtResource("4_2vecx")]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -56.5
offset_top = -19.5
offset_right = 56.5
offset_bottom = 19.5
text = "❓"
horizontal_alignment = 1
action = &"ui_start"
show_name = false
[node name="AnimationPlayer" type="AnimationPlayer" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer/VBoxContainer/SymbolBlinker"]
libraries = {
"": SubResource("AnimationLibrary_5sl0m")
}
autoplay = "rotate"
[node name="Label2" type="Label" parent="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton/CenterContainer/VBoxContainer"]
layout_mode = 2
text = "DM_ADD_PLAYER"
horizontal_alignment = 1
[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"
[connection signal="item_selected" from="MarginContainer/VBoxContainer/MapSelectContainer/MarginContainer/VBoxContainer/MapSelect" to="." method="check_start_conditions" unbinds=1]
[connection signal="pressed" from="MarginContainer/VBoxContainer/PlayersContainer/MarginContainer/VBoxContainer/ScrollContainer/MarginContainer/HBoxContainer/AddPlayerButton" to="." method="_on_add_player_button_pressed"]
[connection signal="pressed" from="SouthEast/HBoxContainer/Cancel" to="." method="cancel"]
[connection signal="pressed" from="SouthEast/HBoxContainer/Start" to="." method="start"]

View File

@ -0,0 +1,80 @@
class_name PlayerSetup extends PanelContainer
const DEFAULT_PLAYER_NAME := "Mysterious Gfolfer X"
const DEFAULT_PLAYER_LIFE := 100.0
const DEFAULT_PLAYER_BALLS := {
GameBall.Type.BASIC: -1,
}
@export var input_method: InputMethod.Type:
set = _set_input_method
@export var player_name: String:
set(value):
await ready
name_input.text = value
get:
return name_input.text
@export var player_color: Color:
set(value):
await ready
color_input.color = value
get:
return color_input.color
@onready var keyboard_symbol: Label = %KeyboardSymbol
@onready var gamepad_symbol: Label = %GamepadSymbol
@onready var sony_symbol: Label = %SonySymbol
@onready var xbox_symbol: Label = %XboxSymbol
@onready var name_input: LineEdit = %Name
@onready var color_input: ColorPickerButton = %Color
@onready var char_name: Label = %CharName
func _set_input_method(value: InputMethod.Type) -> void:
await ready
keyboard_symbol.hide()
gamepad_symbol.hide()
sony_symbol.hide()
xbox_symbol.hide()
match value:
InputMethod.Type.GAMEPAD, InputMethod.Type.NINTENDO:
gamepad_symbol.show()
InputMethod.Type.SONY:
sony_symbol.show()
InputMethod.Type.XBOX:
xbox_symbol.show()
_: # Assume everything else is mouse+keyboard
keyboard_symbol.show()
input_method = value
## Build a `WorldPlayer` representing the selections made here.
func build_player() -> WorldPlayer:
var player := WorldPlayer.new()
player.life = DEFAULT_PLAYER_LIFE
player.name = player_name if player_name else DEFAULT_PLAYER_NAME
player.color = player_color
# TODO club selection
player.driver = Club.catalog.debug_driver
player.iron = Club.catalog.debug_iron
player.wedge = Club.catalog.debug_wedge
player.putter = Club.catalog.debug_putter
player.special = null
# TODO ball selection maybe?
player._balls = DEFAULT_PLAYER_BALLS.duplicate(true)
# TODO set input method
return player
func _on_remove_button_pressed() -> void:
queue_free()

View File

@ -0,0 +1,129 @@
[gd_scene load_steps=2 format=3 uid="uid://cr13d4p235b5y"]
[ext_resource type="Script" path="res://src/ui/menus/title_screen/deathmatch_setup/player_setup/player_setup.gd" id="1_l1wxd"]
[node name="PlayerSetup" type="PanelContainer"]
custom_minimum_size = Vector2(400, 0)
anchors_preset = 13
anchor_left = 0.5
anchor_right = 0.5
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
script = ExtResource("1_l1wxd")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 4
theme_override_constants/margin_top = 4
theme_override_constants/margin_right = 4
theme_override_constants/margin_bottom = 4
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer"]
layout_mode = 2
[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/ScrollContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 12
theme_override_constants/margin_top = 12
theme_override_constants/margin_right = 12
theme_override_constants/margin_bottom = 12
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
[node name="KeyboardSymbol" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"IconXL"
text = "␿"
horizontal_alignment = 1
[node name="GamepadSymbol" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"IconXL"
text = "␼"
horizontal_alignment = 1
[node name="SonySymbol" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"IconXL"
text = "⑀"
horizontal_alignment = 1
[node name="XboxSymbol" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
visible = false
layout_mode = 2
theme_type_variation = &"IconXL"
text = "⑂"
horizontal_alignment = 1
[node name="Name" type="LineEdit" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
layout_mode = 2
theme_type_variation = &"PlayerNameInput"
text = "Gfolfer"
placeholder_text = "DM_NAME_PLACEHOLDER"
alignment = 1
[node name="Color" type="ColorPickerButton" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(0, 64)
layout_mode = 2
edit_alpha = false
[node name="CharacterSelectContainer" type="VBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer"]
layout_mode = 2
[node name="SubViewportContainer" type="SubViewportContainer" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer"]
layout_mode = 2
[node name="SubViewport" type="SubViewport" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer/SubViewportContainer"]
handle_input_locally = false
msaa_3d = 3
screen_space_aa = 1
use_taa = true
use_debanding = true
size = Vector2i(368, 512)
render_target_update_mode = 4
[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer"]
layout_mode = 2
[node name="PrevCharButton" type="Button" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"CharSelectButton"
text = "<"
[node name="CharName" type="Label" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer/HBoxContainer"]
unique_name_in_owner = true
custom_minimum_size = Vector2(240, 0)
layout_mode = 2
size_flags_horizontal = 3
theme_type_variation = &"InputPrompt"
text = "Character"
horizontal_alignment = 1
autowrap_mode = 2
[node name="NextCharButton" type="Button" parent="MarginContainer/ScrollContainer/MarginContainer/VBoxContainer/CharacterSelectContainer/HBoxContainer"]
layout_mode = 2
theme_type_variation = &"CharSelectButton"
text = ">"
[node name="RemoveButton" type="Button" parent="MarginContainer/ScrollContainer/MarginContainer"]
layout_mode = 2
size_flags_horizontal = 8
size_flags_vertical = 0
theme_type_variation = &"IconButton"
theme_override_colors/font_color = Color(0.819608, 0.196078, 0.196078, 1)
text = "✗"
flat = true
[connection signal="pressed" from="MarginContainer/ScrollContainer/MarginContainer/RemoveButton" to="." method="_on_remove_button_pressed"]

View File

@ -1,13 +1,23 @@
extends Control
class_name TitleScreen extends Control
const SCENE := "res://src/ui/menus/title_screen/title_screen.tscn"
@export var settings_scene: PackedScene
@export var local_deathmatch_setup_scene: PackedScene
@onready var menu: Control = %Menu
@onready var settings_container: MarginContainer = %SettingsContainer
@onready var deathmatch_setup_container: MarginContainer = %DeathmatchSetupContainer
@onready var state_machine: AnimationTree = $Menu/MenuStateMachine
@onready var menu_state: AnimationNodeStateMachinePlayback = state_machine["parameters/playback"]
@onready var confirm_sfx: AudioStreamPlayer = %ConfirmSFX
func play_chime() -> void:
confirm_sfx.play()
func _input(event: InputEvent) -> void:
if event is InputEventKey or event is InputEventJoypadButton or event is InputEventMouseButton:
@ -52,3 +62,10 @@ func _open_settings() -> void:
settings_container.add_child(instance)
instance.tree_exited.connect(_unhide)
_hide()
func _open_local_deathmatch_setup() -> void:
var instance: Control = local_deathmatch_setup_scene.instantiate()
deathmatch_setup_container.add_child(instance)
instance.tree_exited.connect(_unhide)
_hide()

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
class_name LevelCatalogType extends Node
## Provides access to levels.
@export var levels: Array[Level] = []
@export var levels: Array[Level]

View File

@ -4,7 +4,7 @@ class_name World extends Node
## A world contains player(s) and the active level, manages player states,
## and transitions between active levels.
@export var initial_level: PackedScene = load("res://levels/debug_level/debug_level.tscn")
@export var initial_level: PackedScene
@export var manager: PlayManager