first commit 🎉

This commit is contained in:
anyreso 2026-02-17 23:36:57 -05:00
commit 6e724f67fe
805 changed files with 62098 additions and 0 deletions

View file

@ -0,0 +1,58 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class_name HostPanel extends MarginContainer
@export var host_button:Button
@export var menu_button:Button
@export var quit_button:Button
@export var server_host:LineEdit
@export var server_port:LineEdit
@export var map_selector:OptionButton
@export var mode_selector:OptionButton
var _host:RegEx = RegEx.new()
var _port:RegEx = RegEx.new()
func _ready() -> void:
# see https://datatracker.ietf.org/doc/html/rfc1700
_port.compile(r'^(?<port>102[4-9]|10[3-9]\d|1[1-9]\d{2}|[2-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5])')
# "localhost", wildcard or ipv4|6
# @TODO: resolve hostname to ip to accept domain names as valid hosts, because host should be ipv4 or ipv6 (see "set_bind_ip")
_host.compile(r'^(?<host>localhost|\*|((([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$|^(([a-fA-F]|[a-fA-F][a-fA-F0-9\-]*[a-fA-F0-9])\.)*([A-Fa-f]|[A-Fa-f][A-Fa-f0-9\-]*[A-Fa-f0-9])$|^(?:(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){6})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:::(?:(?:(?:[0-9a-fA-F]{1,4})):){5})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){4})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,1}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){3})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,2}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:(?:[0-9a-fA-F]{1,4})):){2})(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,3}(?:(?:[0-9a-fA-F]{1,4})))?::(?:(?:[0-9a-fA-F]{1,4})):)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,4}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9]))\.){3}(?:(?:25[0-5]|(?:[1-9]|1[0-9]|2[0-4])?[0-9])))))))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,5}(?:(?:[0-9a-fA-F]{1,4})))?::)(?:(?:[0-9a-fA-F]{1,4})))|(?:(?:(?:(?:(?:(?:[0-9a-fA-F]{1,4})):){0,6}(?:(?:[0-9a-fA-F]{1,4})))?::))))))$')
## This method gets a validated host as specified in the host panel or returns the default one
## from configuration if invalid
func get_host() -> String:
if server_host.text:
var result:RegExMatch = _host.search(server_host.text)
if result:
return result.get_string("host")
return Settings.get_value("server", "host", "localhost")
## This method gets a validated port as specified in the host panel or returns the default one
## from configuration if invalid
func get_port() -> int:
if server_port.text:
var result:RegExMatch = _port.search(server_port.text)
if result:
var port:int = result.get_string("port").to_int()
if port != 0:
return port
return Settings.get_value("server", "port", 9000)
## This method gets an array of validated host and port as specified in the host panel or
## returns an array of default values from configuration if invalid
func get_addr() -> Array:
return [get_host(), get_port()]

View file

@ -0,0 +1 @@
uid://bcnltcd1jp8l

View file

@ -0,0 +1,109 @@
[gd_scene load_steps=4 format=3 uid="uid://dv0lqek8bqyv"]
[ext_resource type="Script" uid="uid://bcnltcd1jp8l" path="res://scenes/interfaces/menus/boot/multiplayer/host_panel/host_panel.gd" id="1_wnvng"]
[ext_resource type="Script" uid="uid://f1xvkf1355ie" path="res://scenes/interfaces/menus/boot/multiplayer/host_panel/map_selector.gd" id="2_kwlr3"]
[ext_resource type="Script" uid="uid://f0y6q6ml3lk5" path="res://scenes/interfaces/menus/boot/multiplayer/host_panel/mode_selector.gd" id="3_o6ecq"]
[node name="HostPanel" type="MarginContainer" node_paths=PackedStringArray("host_button", "menu_button", "quit_button", "server_host", "server_port", "map_selector", "mode_selector")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
script = ExtResource("1_wnvng")
host_button = NodePath("HBoxContainer/LeftBox/Top/Host")
menu_button = NodePath("HBoxContainer/LeftBox/Bottom/Menu")
quit_button = NodePath("HBoxContainer/LeftBox/Bottom/Quit")
server_host = NodePath("HBoxContainer/RightBox/GridContainer/ServerHost")
server_port = NodePath("HBoxContainer/RightBox/GridContainer/ServerPort")
map_selector = NodePath("HBoxContainer/RightBox/GridContainer/MapSelector")
mode_selector = NodePath("HBoxContainer/RightBox/GridContainer/ModeSelector")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 20
[node name="LeftBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Top" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Host" type="Button" parent="HBoxContainer/LeftBox/Top"]
layout_mode = 2
text = "Host"
[node name="Bottom" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_vertical = 10
[node name="Menu" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="MapPreview" type="ColorRect" parent="HBoxContainer/RightBox"]
layout_mode = 2
[node name="GridContainer" type="GridContainer" parent="HBoxContainer/RightBox"]
layout_mode = 2
size_flags_vertical = 3
columns = 2
[node name="HostLabel" type="Label" parent="HBoxContainer/RightBox/GridContainer"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
text = "Host"
[node name="ServerHost" type="LineEdit" parent="HBoxContainer/RightBox/GridContainer"]
custom_minimum_size = Vector2(120, 0)
layout_mode = 2
placeholder_text = "localhost"
max_length = 128
[node name="PortLabel" type="Label" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
text = "Port"
[node name="ServerPort" type="LineEdit" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
placeholder_text = "9000"
alignment = 2
[node name="MapLabel" type="Label" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
text = "Map"
[node name="MapSelector" type="OptionButton" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
clip_text = true
allow_reselect = true
script = ExtResource("2_kwlr3")
[node name="ModeLabel" type="Label" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
text = "Mode"
[node name="ModeSelector" type="OptionButton" parent="HBoxContainer/RightBox/GridContainer"]
layout_mode = 2
script = ExtResource("3_o6ecq")

View file

@ -0,0 +1,20 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
extends OptionButton
# Called when the node enters the scene tree for the first time.
func _ready() -> void:
for map:PackedScene in Game.maps:
add_item(map._bundled.names[0])

View file

@ -0,0 +1 @@
uid://f1xvkf1355ie

View file

@ -0,0 +1,28 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
extends OptionButton
func format(enum_key: String) -> String:
# split enum value by underscores
var words:PackedStringArray = enum_key.split("_")
# capitalize first letter of each word
for i in range(words.size()):
words[i] = words[i].capitalize()
# join words with spaces
return " ".join(words)
func _ready() -> void:
for mode:Mode in Match.modes:
add_item(format(mode.name.to_upper()))

View file

@ -0,0 +1 @@
uid://f0y6q6ml3lk5

View file

@ -0,0 +1,42 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class_name JoinPanel extends MarginContainer
@export var join_button : Button
@export var menu_button : Button
@export var quit_button : Button
@export var join_address : LineEdit
var _addr:RegEx = RegEx.new()
func _ready() -> void:
# see https://datatracker.ietf.org/doc/html/rfc1700
_addr.compile(r'^(?<host>(?![0-9]+$)[a-zA-Z0-9.-]+)(?::(?<port>:102[4-9]|10[3-9]\d|1[1-9]\d{2}|[2-9]\d{3}|[1-5]\d{4}|6[0-4]\d{3}|65[0-4]\d{2}|655[0-2]\d|6553[0-5]))?$')
## This method gets an array of validated host and port as specified in the
## join panel or returns an array of default values from configuration if invalid
func get_addr() -> Array:
if join_address.text:
var result:RegExMatch = _addr.search(join_address.text)
if result:
var host:String = result.get_string("host")
var port:int = result.get_string("port").to_int()
prints("join", host, port)
if port == 0:
port = Settings.get_value("server", "port", 9000)
return [host, port]
else:
push_warning("join address is invalid")
return [ Settings.get_value("server", "host", "localhost"), Settings.get_value("server", "port", 9000) ]

View file

@ -0,0 +1 @@
uid://ffilb2d365hu

View file

@ -0,0 +1,62 @@
[gd_scene load_steps=2 format=3 uid="uid://cfegls11p6wav"]
[ext_resource type="Script" uid="uid://ffilb2d365hu" path="res://scenes/interfaces/menus/boot/multiplayer/join_panel/join_panel.gd" id="1_svm4h"]
[node name="JoinPanel" type="MarginContainer" node_paths=PackedStringArray("join_button", "menu_button", "quit_button", "join_address")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
script = ExtResource("1_svm4h")
join_button = NodePath("HBoxContainer/LeftBox/Top/Join")
menu_button = NodePath("HBoxContainer/LeftBox/Bottom/Menu")
quit_button = NodePath("HBoxContainer/LeftBox/Bottom/Quit")
join_address = NodePath("HBoxContainer/RightBox/JoinAddress")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 20
[node name="LeftBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Top" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Join" type="Button" parent="HBoxContainer/LeftBox/Top"]
layout_mode = 2
text = "Join"
[node name="Bottom" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_vertical = 10
[node name="Menu" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="JoinAddress" type="LineEdit" parent="HBoxContainer/RightBox"]
layout_mode = 2
placeholder_text = "localhost"

View file

@ -0,0 +1,75 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class_name MultiplayerPanelContainer extends PanelContainer
# @TODO: move profiles in a "profiles" folder since there will be many profiles
const CONFIG_FILE_PATH:String = "user://profile.cfg"
var _config_file:ConfigFile = ConfigFile.new()
signal menu_pressed
signal join_pressed
signal host_pressed
@export var tab_container:TabContainer
@export var profile_panel:ProfilePanel
@export var join_panel:JoinPanel
@export var host_panel:HostPanel
@onready var modal:Control = $Modal
func _ready() -> void:
# connect signals
join_panel.join_button.pressed.connect(_on_join_pressed)
host_panel.host_button.pressed.connect(_on_host_pressed)
for panel:Control in [profile_panel, host_panel, join_panel]:
panel.menu_button.pressed.connect(_on_menu_pressed)
panel.quit_button.pressed.connect(Game.quit)
# load configuration file
_load_config()
func _unhandled_input(event: InputEvent) -> void:
if event.is_action_pressed("exit"):
modal.hide()
func _load_config() -> void:
var error:Error = _config_file.load(CONFIG_FILE_PATH)
if error != OK:
return
profile_panel.profile_name.text = _config_file.get_value("profile", "name", "Newblood")
func _on_save_pressed() -> void:
_config_file.set_value("profile", "name", profile_panel.profile_name.text)
_config_file.save(CONFIG_FILE_PATH)
func _on_menu_pressed() -> void:
menu_pressed.emit()
func _on_host_pressed() -> void:
host_pressed.emit(
host_panel.get_addr(),
host_panel.map_selector.selected,
host_panel.mode_selector.selected,
profile_panel.profile_name.text)
func _on_join_pressed() -> void:
var addr := join_panel.get_addr()
if addr:
modal.show()
multiplayer.connection_failed.connect(modal.hide, CONNECT_ONE_SHOT)
multiplayer.connected_to_server.connect(func() -> void: modal.hide(); owner.hide(), CONNECT_ONE_SHOT)
join_pressed.emit(join_panel.get_addr(), profile_panel.profile_name.text)

View file

@ -0,0 +1 @@
uid://bnyd7frdcpwpk

View file

@ -0,0 +1,94 @@
[gd_scene load_steps=7 format=3 uid="uid://7chfb47enqwm"]
[ext_resource type="Script" uid="uid://bnyd7frdcpwpk" path="res://scenes/interfaces/menus/boot/multiplayer/multiplayer_panel_container.gd" id="1_yyvwh"]
[ext_resource type="PackedScene" uid="uid://cfegls11p6wav" path="res://scenes/interfaces/menus/boot/multiplayer/join_panel/join_panel.tscn" id="2_4vm27"]
[ext_resource type="PackedScene" uid="uid://moey0w3677td" path="res://scenes/interfaces/menus/boot/multiplayer/profile_panel/profile_panel.tscn" id="2_aidbu"]
[ext_resource type="PackedScene" uid="uid://dv0lqek8bqyv" path="res://scenes/interfaces/menus/boot/multiplayer/host_panel/host_panel.tscn" id="2_ameih"]
[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_krqeq"]
bg_color = Color(0.501961, 0.501961, 0.501961, 0.25098)
[sub_resource type="GDScript" id="GDScript_tcq1f"]
script/source = "extends Panel
@onready var label: Label = $Label
func _hide() -> void:
label.text = \"CONNECTING ...\"
"
[node name="MultiplayerPanelContainer" type="PanelContainer" node_paths=PackedStringArray("tab_container", "profile_panel", "join_panel", "host_panel")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
theme_override_styles/panel = SubResource("StyleBoxFlat_krqeq")
script = ExtResource("1_yyvwh")
tab_container = NodePath("MarginContainer/VBoxContainer/TabContainer")
profile_panel = NodePath("MarginContainer/VBoxContainer/TabContainer/Profile/ProfilePanel")
join_panel = NodePath("MarginContainer/VBoxContainer/TabContainer/Join/JoinPanel")
host_panel = NodePath("MarginContainer/VBoxContainer/TabContainer/Host/HostPanel")
[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
layout_mode = 2
[node name="TabContainer" type="TabContainer" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
size_flags_vertical = 3
theme_override_constants/side_margin = 0
current_tab = 2
[node name="Profile" type="TabBar" parent="MarginContainer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
theme_override_constants/h_separation = 0
metadata/_tab_index = 0
[node name="ProfilePanel" parent="MarginContainer/VBoxContainer/TabContainer/Profile" instance=ExtResource("2_aidbu")]
layout_mode = 1
[node name="Join" type="TabBar" parent="MarginContainer/VBoxContainer/TabContainer"]
visible = false
layout_mode = 2
select_with_rmb = true
metadata/_tab_index = 1
[node name="JoinPanel" parent="MarginContainer/VBoxContainer/TabContainer/Join" instance=ExtResource("2_4vm27")]
layout_mode = 1
[node name="Host" type="TabBar" parent="MarginContainer/VBoxContainer/TabContainer"]
layout_mode = 2
select_with_rmb = true
metadata/_tab_index = 2
[node name="HostPanel" parent="MarginContainer/VBoxContainer/TabContainer/Host" instance=ExtResource("2_ameih")]
layout_mode = 1
[node name="Modal" type="Panel" parent="."]
visible = false
layout_mode = 2
script = SubResource("GDScript_tcq1f")
[node name="Label" type="Label" parent="Modal"]
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -59.5
offset_top = -11.5
offset_right = 59.5
offset_bottom = 11.5
grow_horizontal = 2
grow_vertical = 2
text = "CONNECTING ..."
horizontal_alignment = 1

View file

@ -0,0 +1,22 @@
# This file is part of sunder.
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
class_name ProfilePanel extends MarginContainer
@export var create_button : Button
@export var delete_button : Button
@export var save_button : Button
@export var menu_button : Button
@export var quit_button : Button
@export var profile_name : LineEdit

View file

@ -0,0 +1 @@
uid://7sc85512bgrr

View file

@ -0,0 +1,79 @@
[gd_scene load_steps=2 format=3 uid="uid://moey0w3677td"]
[ext_resource type="Script" uid="uid://7sc85512bgrr" path="res://scenes/interfaces/menus/boot/multiplayer/profile_panel/profile_panel.gd" id="1_e3jmb"]
[node name="ProfilePanel" type="MarginContainer" node_paths=PackedStringArray("create_button", "delete_button", "save_button", "menu_button", "quit_button", "profile_name")]
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
size_flags_horizontal = 3
size_flags_vertical = 3
theme_override_constants/margin_left = 20
theme_override_constants/margin_top = 20
theme_override_constants/margin_right = 20
theme_override_constants/margin_bottom = 20
script = ExtResource("1_e3jmb")
create_button = NodePath("HBoxContainer/LeftBox/Top/Create")
delete_button = NodePath("HBoxContainer/LeftBox/Top/Delete")
save_button = NodePath("HBoxContainer/LeftBox/Top/Save")
menu_button = NodePath("HBoxContainer/LeftBox/Bottom/Menu")
quit_button = NodePath("HBoxContainer/LeftBox/Bottom/Quit")
profile_name = NodePath("HBoxContainer/RightBox/ProfileName")
[node name="HBoxContainer" type="HBoxContainer" parent="."]
layout_mode = 2
theme_override_constants/separation = 20
[node name="LeftBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Top" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_horizontal = 3
size_flags_stretch_ratio = 0.25
[node name="Create" type="Button" parent="HBoxContainer/LeftBox/Top"]
layout_mode = 2
disabled = true
text = "Create"
[node name="Delete" type="Button" parent="HBoxContainer/LeftBox/Top"]
layout_mode = 2
disabled = true
text = "Delete"
[node name="Save" type="Button" parent="HBoxContainer/LeftBox/Top"]
layout_mode = 2
text = "Save"
[node name="Bottom" type="VBoxContainer" parent="HBoxContainer/LeftBox"]
layout_mode = 2
size_flags_vertical = 10
[node name="Menu" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Main Menu
"
[node name="Quit" type="Button" parent="HBoxContainer/LeftBox/Bottom"]
layout_mode = 2
size_flags_horizontal = 3
text = "Quit"
[node name="RightBox" type="VBoxContainer" parent="HBoxContainer"]
layout_mode = 2
size_flags_horizontal = 3
[node name="Label" type="Label" parent="HBoxContainer/RightBox"]
layout_mode = 2
text = "Current Profile:"
[node name="ProfileName" type="LineEdit" parent="HBoxContainer/RightBox"]
layout_mode = 2
size_flags_horizontal = 3
text = "Newblood"