diff options
author | metamuffin <metamuffin@disroot.org> | 2025-09-04 23:47:24 +0200 |
---|---|---|
committer | metamuffin <metamuffin@disroot.org> | 2025-09-05 23:07:07 +0200 |
commit | 81deaf81c800900e30046cb927be1c9d91ae61b8 (patch) | |
tree | 20ce9898465e8d4c49eeff12a9ea55572517ea7b /client/gui/menus | |
parent | fd80142282fcef628466a18e3ea62f0d1372d807 (diff) | |
download | hurrycurry-81deaf81c800900e30046cb927be1c9d91ae61b8.tar hurrycurry-81deaf81c800900e30046cb927be1c9d91ae61b8.tar.bz2 hurrycurry-81deaf81c800900e30046cb927be1c9d91ae61b8.tar.zst |
reorganize client gui files
Diffstat (limited to 'client/gui/menus')
97 files changed, 4373 insertions, 0 deletions
diff --git a/client/gui/menus/character.gd b/client/gui/menus/character.gd new file mode 100644 index 00000000..3c1230ae --- /dev/null +++ b/client/gui/menus/character.gd @@ -0,0 +1,96 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +@onready var character: Character = $Node3D/Character +@onready var back_button := $VBoxContainer/bottom_panel/back +@onready var map: Map = $Node3D/Map +@onready var username_edit = $VBoxContainer/top_panel/a/username + +func _ready(): + super() + $VBoxContainer/top_panel/a/username.text = Global.get_profile("username") + character.set_style(Global.get_profile("character_style"), "chef") + init_map() + +func init_map(): + var map_tile = func (t): match t: + ".": return "floor" + "=": return "counter" + "s": return "stove" + "c": return "chair" + "t": return "table" + "o": return "oven" + "#": return "wall" + _: push_error("unknown tile: ", t) + var tiles = [ + "...............", + "###############", + "=oo==ss===.ctc#", + "..............#", + ".............=#", + ".............=#", + ".............=#" + ].map(func (l): return Array(l.split("")).map(map_tile)) + var gt = func (e): return null if e[1] >= tiles.size() else null if e[0] >= tiles[e[1]].size() else tiles[e[1]][e[0]] + var co = Vector2i(floor(tiles[0].size() / 2), floor(tiles.size() - 2)) + for y in tiles.size(): + for x in tiles[y].size(): + map.set_tile(Vector2i(x,y) - co, gt.call([x,y]), [[x,y-1],[x-1,y],[x,y+1],[x+1,y]].map(gt)) + map.flush() + +func exit(): + if username_edit.text == "": + var popup_data := MenuPopup.Data.new() + popup_data.text = tr("c.error.empty_username") + var accept_button := Button.new() + accept_button.text = tr("c.menu.accept") + popup_data.buttons = [accept_button] + await submenu("res://gui/menus/popup.tscn", popup_data) + return + Global.set_profile("username", username_edit.text) + Global.save_profile() + super() + +func _on_character_back_pressed(): + modify_style(func m(current_style: Dictionary): + current_style.color = G.rem_euclid(current_style.color - 1, character.COLORS.size())) + +func _on_character_forward_pressed(): + modify_style(func m(current_style: Dictionary): + current_style.color = G.rem_euclid(current_style.color + 1, character.COLORS.size())) + +func _on_headwear_back_pressed() -> void: + modify_style(func m(current_style: Dictionary): + current_style.headwear = G.rem_euclid(current_style.headwear - 1, character.headwears.size())) + +func _on_headwear_forward_pressed() -> void: + modify_style(func m(current_style: Dictionary): + current_style.headwear = G.rem_euclid(current_style.headwear + 1, character.headwears.size())) + +func _on_hairstyle_back_pressed() -> void: + modify_style(func m(current_style: Dictionary): + current_style.hairstyle = G.rem_euclid(current_style.hairstyle - 1, character.hairstyles.size())) + +func _on_hairstyle_forward_pressed() -> void: + modify_style(func m(current_style: Dictionary): + current_style.hairstyle = G.rem_euclid(current_style.hairstyle + 1, character.hairstyles.size())) + +func modify_style(modifier: Callable): + var current_style: Dictionary = Global.get_profile("character_style") + modifier.call(current_style) + Global.set_profile("character_style", current_style) + character.set_style(Global.get_profile("character_style"), "chef") diff --git a/client/gui/menus/character.gd.uid b/client/gui/menus/character.gd.uid new file mode 100644 index 00000000..d0df5488 --- /dev/null +++ b/client/gui/menus/character.gd.uid @@ -0,0 +1 @@ +uid://bglusga8l5c27 diff --git a/client/gui/menus/character.tscn b/client/gui/menus/character.tscn new file mode 100644 index 00000000..7bdf03e6 --- /dev/null +++ b/client/gui/menus/character.tscn @@ -0,0 +1,230 @@ +[gd_scene load_steps=11 format=3 uid="uid://1f7xpirm5d28"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_ak2pw"] +[ext_resource type="Script" uid="uid://bglusga8l5c27" path="res://gui/menus/character.gd" id="1_brhd1"] +[ext_resource type="PackedScene" uid="uid://b4gone8fu53r7" path="res://map/map.tscn" id="3_6mc88"] +[ext_resource type="PackedScene" uid="uid://b3hhir2fvnunu" path="res://player/character/character.tscn" id="3_odq7n"] +[ext_resource type="PackedScene" uid="uid://bg2d78ycorcqk" path="res://gui/menus/transition/scene_transition.tscn" id="4_c0ocf"] +[ext_resource type="Texture2D" uid="uid://35rd5gamtyqm" path="res://gui/resources/icons/arrow.svg" id="5_kvd7k"] +[ext_resource type="Texture2D" uid="uid://j75dbytlbju" path="res://gui/resources/icons/arrow_pressed.svg" id="5_xpff8"] +[ext_resource type="Texture2D" uid="uid://b33qmctbpf48g" path="res://gui/resources/icons/arrow_hover.svg" id="6_soj8g"] +[ext_resource type="Texture2D" uid="uid://by3qsrpxnfq4w" path="res://gui/resources/icons/arrow_focus.svg" id="6_u31hl"] + +[sub_resource type="Environment" id="Environment_ex25y"] +background_mode = 1 +background_color = Color(0.145548, 0.151043, 0.207031, 1) + +[node name="CharacterMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_ak2pw") +script = ExtResource("1_brhd1") + +[node name="Node3D" type="Node3D" parent="."] + +[node name="WorldEnvironment" type="WorldEnvironment" parent="Node3D"] +environment = SubResource("Environment_ex25y") + +[node name="Camera3D" type="Camera3D" parent="Node3D"] +transform = Transform3D(1, 0, 0, 0, 0.977046, 0.21303, 0, -0.21303, 0.977046, 0, 1.137, 2.703) +current = true +fov = 41.8 + +[node name="Map" parent="Node3D" instance=ExtResource("3_6mc88")] +transform = Transform3D(0.866025, 0, 0.5, 0, 1, 0, -0.5, 0, 0.866025, 0, 0, 0) + +[node name="Character" parent="Node3D" instance=ExtResource("3_odq7n")] + +[node name="SpotLight3D" type="SpotLight3D" parent="Node3D"] +transform = Transform3D(0.631535, -0.571246, 0.524254, 0.0428654, 0.700843, 0.712026, -0.774162, -0.427197, 0.467093, 1.79161, 3.07541, 1.58055) +light_energy = 2.689 +spot_range = 20.159 +spot_angle = 17.9256 + +[node name="SpotLight3D2" type="SpotLight3D" parent="Node3D"] +transform = Transform3D(0.32457, 0.109091, -0.93955, 0.0604837, 0.9889, 0.135716, 0.943926, -0.100877, 0.314369, -5.22608, 2.10824, 2.35824) +light_energy = 2.689 +spot_range = 20.159 +spot_angle = 17.9256 + +[node name="SpotLight3D3" type="SpotLight3D" parent="Node3D"] +transform = Transform3D(0.114088, -0.0173997, 0.993318, 0.0610452, 0.99808, 0.0104718, -0.991594, 0.0594426, 0.114931, 8.10732, 0.437069, 2.35824) +light_energy = 2.689 +spot_range = 20.159 +spot_angle = 17.9256 + +[node name="VBoxContainer" type="VBoxContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="top_panel" type="Panel" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 100) +layout_mode = 2 + +[node name="a" type="VBoxContainer" parent="VBoxContainer/top_panel"] +layout_mode = 1 +anchors_preset = 5 +anchor_left = 0.5 +anchor_right = 0.5 +offset_left = -213.0 +offset_top = 13.0 +offset_right = 216.0 +offset_bottom = 110.0 +grow_horizontal = 2 + +[node name="Label" type="Label" parent="VBoxContainer/top_panel/a"] +layout_mode = 2 +text = "c.settings.username" +horizontal_alignment = 1 + +[node name="username" type="LineEdit" parent="VBoxContainer/top_panel/a"] +layout_mode = 2 +max_length = 32 + +[node name="Spacer" type="MarginContainer" parent="VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 50 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 50 + +[node name="VBoxContainer" type="VBoxContainer" parent="VBoxContainer/Spacer"] +layout_mode = 2 +alignment = 1 + +[node name="Headware" type="HBoxContainer" parent="VBoxContainer/Spacer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 1 + +[node name="Back" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Headware"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_right = NodePath("../Forward") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 +flip_h = true + +[node name="Spacer" type="Control" parent="VBoxContainer/Spacer/VBoxContainer/Headware"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Forward" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Headware"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_left = NodePath("../Back") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Hairstyle" type="HBoxContainer" parent="VBoxContainer/Spacer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 1 + +[node name="Back" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Hairstyle"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_right = NodePath("../Forward") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 +flip_h = true + +[node name="Spacer" type="Control" parent="VBoxContainer/Spacer/VBoxContainer/Hairstyle"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Forward" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Hairstyle"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_left = NodePath("../Back") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 + +[node name="Character" type="HBoxContainer" parent="VBoxContainer/Spacer/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 1 + +[node name="Back" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Character"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_right = NodePath("../Forward") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 +flip_h = true + +[node name="Spacer" type="Control" parent="VBoxContainer/Spacer/VBoxContainer/Character"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="Forward" type="TextureButton" parent="VBoxContainer/Spacer/VBoxContainer/Character"] +layout_mode = 2 +size_flags_horizontal = 3 +focus_neighbor_left = NodePath("../Back") +texture_normal = ExtResource("5_kvd7k") +texture_pressed = ExtResource("5_xpff8") +texture_hover = ExtResource("6_soj8g") +texture_focused = ExtResource("6_u31hl") +ignore_texture_size = true +stretch_mode = 5 + +[node name="bottom_panel" type="Panel" parent="VBoxContainer"] +custom_minimum_size = Vector2(0, 75) +layout_mode = 2 + +[node name="back" type="Button" parent="VBoxContainer/bottom_panel"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -39.5 +offset_top = -22.0 +offset_right = 39.5 +offset_bottom = 22.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_vertical = 8 +text = "c.menu.back" + +[node name="SceneTransition" parent="." instance=ExtResource("4_c0ocf")] +visible = false +layout_mode = 1 + +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Headware/Back" to="." method="_on_headwear_back_pressed"] +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Headware/Forward" to="." method="_on_headwear_forward_pressed"] +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Hairstyle/Back" to="." method="_on_hairstyle_back_pressed"] +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Hairstyle/Forward" to="." method="_on_hairstyle_forward_pressed"] +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Character/Back" to="." method="_on_character_back_pressed"] +[connection signal="pressed" from="VBoxContainer/Spacer/VBoxContainer/Character/Forward" to="." method="_on_character_forward_pressed"] +[connection signal="pressed" from="VBoxContainer/bottom_panel/back" to="." method="exit"] diff --git a/client/gui/menus/chat.gd b/client/gui/menus/chat.gd new file mode 100644 index 00000000..150b0e7e --- /dev/null +++ b/client/gui/menus/chat.gd @@ -0,0 +1,48 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu +class_name ChatOpen + +const CHAT_MESSAGE_SCENE = preload("res://menu/communicate/chat/chat_message.tscn") + +@onready var messages_container: VBoxContainer = $PanelContainer/MarginContainer/VBoxContainer/ScrollContainerCustom/Messages +@onready var scroll_container: ScrollContainerCustom = $PanelContainer/MarginContainer/VBoxContainer/ScrollContainerCustom +@onready var line: LineEdit = $PanelContainer/MarginContainer/VBoxContainer/LineEdit +@onready var game_menu: GameMenu = get_parent() +@onready var game: Game = game_menu.game + +func _ready() -> void: + super() + for i in game.text_message_history: + add_message(i) + + game.text_message.connect( + func message(m: Game.TextMessage): + add_message(m) + ) + +func _input(event: InputEvent) -> void: + if Input.is_action_just_pressed("chat"): + if line.text != "": + game.mp.send_chat(game.my_player_id, line.text) + exit() + super(event) + +func add_message(message: Game.TextMessage): + var chat_message: ChatMessage = CHAT_MESSAGE_SCENE.instantiate() + messages_container.add_child(chat_message) + chat_message.set_message(message.username, message.text, message.color) + scroll_container.call_deferred("scroll_to_bottom") diff --git a/client/gui/menus/chat.gd.uid b/client/gui/menus/chat.gd.uid new file mode 100644 index 00000000..672792c0 --- /dev/null +++ b/client/gui/menus/chat.gd.uid @@ -0,0 +1 @@ +uid://cfweimyoq5vv0 diff --git a/client/gui/menus/chat.tscn b/client/gui/menus/chat.tscn new file mode 100644 index 00000000..626038f4 --- /dev/null +++ b/client/gui/menus/chat.tscn @@ -0,0 +1,56 @@ +[gd_scene load_steps=7 format=3 uid="uid://dbd6k56l4p0ls"] + +[ext_resource type="Script" uid="uid://cfweimyoq5vv0" path="res://gui/menus/chat.gd" id="1_gntkb"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="2_1au48"] +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="3_lrbjr"] +[ext_resource type="StyleBox" uid="uid://bw4jamyna1top" path="res://gui/resources/style/panel_style_sidebar.tres" id="4_d4nta"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="5_l1coj"] +[ext_resource type="Script" uid="uid://bd7bylb2t2m0" path="res://gui/components/touch_scroll_container.gd" id="6_ff15x"] + +[node name="ChatOpen" type="Control"] +layout_mode = 3 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 296.0 +grow_vertical = 2 +script = ExtResource("1_gntkb") +support_anim = false +auto_anim = null + +[node name="PanelContainer" type="PanelContainer" parent="."] +material = ExtResource("2_1au48") +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 296.0 +grow_vertical = 2 +theme = ExtResource("3_lrbjr") +theme_override_styles/panel = ExtResource("4_d4nta") +script = ExtResource("5_l1coj") + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/separation = 0 + +[node name="ScrollContainerCustom" type="ScrollContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] +material = ExtResource("2_1au48") +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +script = ExtResource("6_ff15x") +auto_scroll_to_bottom = true + +[node name="Messages" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer/ScrollContainerCustom"] +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="LineEdit" type="LineEdit" parent="PanelContainer/MarginContainer/VBoxContainer" groups=["autoselect"]] +layout_mode = 2 +placeholder_text = "c.chat.write_message" +keep_editing_on_text_submit = true diff --git a/client/gui/menus/document/document.gd b/client/gui/menus/document/document.gd new file mode 100644 index 00000000..c7042852 --- /dev/null +++ b/client/gui/menus/document/document.gd @@ -0,0 +1,152 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +const MARGIN: int = 75 + +var labels := {} + +func _ready(): + super() + $ScrollContainer/VBoxContainer.add_child(build_document(data)) + +func build_document(element: Dictionary, bullet: bool = false) -> Control: + var node: Control + match element["t"]: + "document": + node = VBoxContainer.new() + node.name = "Document" + for e in element["es"]: + node.add_child(build_document(e)) + "page": + node = PanelContainer.new() + node.name = "Page" + node.add_theme_stylebox_override("panel", preload("res://menu/theme/style/paper_panel_style.tres")) + node.set_custom_minimum_size(Vector2(800, 1131.371)) + var margin := MarginContainer.new() + margin.add_theme_constant_override("margin_bottom", MARGIN) + margin.add_theme_constant_override("margin_top", MARGIN) + margin.add_theme_constant_override("margin_left", MARGIN) + margin.add_theme_constant_override("margin_right", MARGIN) + var vbox := VBoxContainer.new() + if element["background"]: + margin.add_child(background(element["background"])) + margin.add_child(vbox) + for e in element["es"]: + vbox.add_child(build_document(e, bullet)) + node.add_child(margin) + "label": + var label_id = element["id"] + node = build_document(element["e"], bullet) + labels[label_id] = node + "list": + node = VBoxContainer.new() + node.name = "List" + for e in element["es"]: + node.add_child(build_document(e, true)) + "table": + node = VBoxContainer.new() + node.name = "Rows" + node.size_flags_horizontal = Control.SIZE_EXPAND_FILL + for r in range(element["es"].size()): + var row = HBoxContainer.new() + node.add_child(row) + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.name = "Row%d" % r + for c in element["es"][r]: + var e = build_document(c, bullet) + e.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.add_child(e) + "par": + node = VBoxContainer.new() + node.name = "Paragraph" + for e in element["es"]: + node.add_child(build_document(e, bullet)) + "ref": + # TODO: Support clicking + node = build_document(element["e"], bullet) + "conditional": + # Ignore all conditionals for now, since they are only revelant for typst version + node = Control.new() + "text": + node = text_node(element, bullet) + _: + node = Control.new() + push_error("Error building document: Unknown type \"%s\"" % element["t"]) + return node + +func text_node(element: Dictionary, bullet: bool) -> Control: + var node: Control + var label := Label.new() + # we need a hbox container for rtl + if bullet: + node = HBoxContainer.new() + var bullet_label := Label.new() + bullet_label.text = "•" + if element.get("size"): + bullet_label.add_theme_font_size_override("font_size", element["size"]) + # TODO: Ignore font color for now. Will be removed in the future. + # if element.get("color"): + # bullet_label.add_theme_color_override("font_color", Color(element["color"])) + bullet_label.add_theme_color_override("font_color", Color.BLACK) + label.add_theme_color_override("font_color", Color.BLACK) + node.add_child(bullet_label) + label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + node.add_child(label) + else: + node = label + label.name = "Text" + label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART + label.text = Global.get_message_str(element["s"]) + if element.get("font"): + match element["font"]: + "Great Vibes": + node.add_theme_font_override("font", preload("res://menu/theme/fonts/font-sansita-swashed.woff2")) + if element.get("size"): + node.add_theme_font_size_override("font_size", element["size"]) + # TODO: Ignore font color for now. Will be removed in the future. + # if element.get("color"): + # label.add_theme_color_override("font_color", Color(element["color"])) + label.add_theme_color_override("font_color", Color.BLACK) + return node + +func background(background_name: String) -> SubViewportContainer: + var item_name: String + match background_name: + "cover": item_name = "plate:plate,plate,plate,dirt" + "toc": item_name = "tomato" + "tomato_soup": item_name = "plate:tomato-soup" + "burger": item_name = "plate:sliced-bun,sliced-tomato,sliced-lettuce" + "mochi": item_name = "plate:strawberry-mochi" + "curry": item_name = "plate:curry,cooked-rice" + "icecream": item_name = "plate:strawberry-icecream" + "drinks": item_name = "glass:strawberry-shake" + var n: item_name = n + var scene: ItemRender = preload("res://menu/communicate/item/item_render.tscn").instantiate() + scene.set_item(item_name, false) + var vc := SubViewportContainer.new() + var viewport := SubViewport.new() + viewport.add_child(scene) + viewport.own_world_3d = true + viewport.transparent_bg = true + vc.size_flags_horizontal = Control.SIZE_SHRINK_CENTER + vc.size_flags_vertical = Control.SIZE_SHRINK_END + #vc.material = preload("res://menu/theme/materials/printed_material.tres") + vc.add_child(viewport) + return vc + +func _menu_open(): pass +func _menu_exit(): pass diff --git a/client/gui/menus/document/document.gd.uid b/client/gui/menus/document/document.gd.uid new file mode 100644 index 00000000..c84b53b1 --- /dev/null +++ b/client/gui/menus/document/document.gd.uid @@ -0,0 +1 @@ +uid://c83p4k0nredmd diff --git a/client/gui/menus/document/document.tscn b/client/gui/menus/document/document.tscn new file mode 100644 index 00000000..537ac8b8 --- /dev/null +++ b/client/gui/menus/document/document.tscn @@ -0,0 +1,29 @@ +[gd_scene load_steps=3 format=3 uid="uid://bdggwo8un3mys"] + +[ext_resource type="Script" uid="uid://c83p4k0nredmd" path="res://gui/menus/document/document.gd" id="1_gyisx"] +[ext_resource type="Script" uid="uid://bd7bylb2t2m0" path="res://gui/components/touch_scroll_container.gd" id="2_0d0p0"] + +[node name="Document" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_gyisx") +support_anim = false +auto_anim = null + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("2_0d0p0") + +[node name="VBoxContainer" type="VBoxContainer" parent="ScrollContainer"] +layout_mode = 2 +size_flags_horizontal = 6 +size_flags_vertical = 4 diff --git a/client/gui/menus/entry.gd b/client/gui/menus/entry.gd new file mode 100644 index 00000000..91bec795 --- /dev/null +++ b/client/gui/menus/entry.gd @@ -0,0 +1,35 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Entry +extends Menu + +func _ready(): + super() + get_window().title = "Hurry Curry!" + + var args = OS.get_cmdline_user_args() + if args.size() == 1: + await submenu("res://gui/menus/game.tscn", args[0]) + elif not Global.get_setting("gameplay.setup_completed"): + await submenu("res://gui/menus/setup/setup.tscn") + else: + await submenu("res://gui/menus/main/main.tscn") + + print("Menu stack empty, quitting game.") + get_tree().quit() + +func quit(): + pass diff --git a/client/gui/menus/entry.gd.uid b/client/gui/menus/entry.gd.uid new file mode 100644 index 00000000..4bd126a6 --- /dev/null +++ b/client/gui/menus/entry.gd.uid @@ -0,0 +1 @@ +uid://yxaynnimyxgr diff --git a/client/gui/menus/entry.tscn b/client/gui/menus/entry.tscn new file mode 100644 index 00000000..f4eced3e --- /dev/null +++ b/client/gui/menus/entry.tscn @@ -0,0 +1,12 @@ +[gd_scene load_steps=2 format=3 uid="uid://cd52sr1cmo8oj"] + +[ext_resource type="Script" uid="uid://yxaynnimyxgr" path="res://gui/menus/entry.gd" id="1_kibw2"] + +[node name="Entry" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_kibw2") diff --git a/client/gui/menus/error.gd b/client/gui/menus/error.gd new file mode 100644 index 00000000..e11812d5 --- /dev/null +++ b/client/gui/menus/error.gd @@ -0,0 +1,23 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +func _ready(): + super() + $Panel/SmartMarginContainer/contents/mesage.text = Global.error_message + +func _on_return_pressed(): + replace_menu("res://gui/menus/main/main.tscn") diff --git a/client/gui/menus/error.gd.uid b/client/gui/menus/error.gd.uid new file mode 100644 index 00000000..20a67804 --- /dev/null +++ b/client/gui/menus/error.gd.uid @@ -0,0 +1 @@ +uid://bl0n4atrdcogm diff --git a/client/gui/menus/error.tscn b/client/gui/menus/error.tscn new file mode 100644 index 00000000..dcebf322 --- /dev/null +++ b/client/gui/menus/error.tscn @@ -0,0 +1,70 @@ +[gd_scene load_steps=7 format=3 uid="uid://cimgn07lbcs4v"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_cabdu"] +[ext_resource type="PackedScene" uid="uid://l4vm07dtda4j" path="res://gui/menus/main/background.tscn" id="2_5fxol"] +[ext_resource type="Script" uid="uid://bl0n4atrdcogm" path="res://gui/menus/error.gd" id="2_dbe41"] +[ext_resource type="PackedScene" uid="uid://bg2d78ycorcqk" path="res://gui/menus/transition/scene_transition.tscn" id="4_1nbt3"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="4_hxkkd"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="5_rfcg2"] + +[node name="ErrorMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_cabdu") +script = ExtResource("2_dbe41") + +[node name="MenuBackground" parent="." instance=ExtResource("2_5fxol")] + +[node name="Panel" type="Panel" parent="."] +material = ExtResource("4_hxkkd") +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="SmartMarginContainer" type="MarginContainer" parent="Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("5_rfcg2") +metadata/_custom_type_script = "uid://byshs20og68tn" + +[node name="contents" type="VBoxContainer" parent="Panel/SmartMarginContainer"] +layout_mode = 2 +alignment = 1 + +[node name="title" type="Label" parent="Panel/SmartMarginContainer/contents"] +layout_mode = 2 +theme_override_font_sizes/font_size = 61 +text = "Error" +horizontal_alignment = 1 + +[node name="mesage" type="Label" parent="Panel/SmartMarginContainer/contents"] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "This should be the error message." +horizontal_alignment = 1 + +[node name="Control" type="Control" parent="Panel/SmartMarginContainer/contents"] +custom_minimum_size = Vector2(0, 15.805) +layout_mode = 2 + +[node name="return" type="Button" parent="Panel/SmartMarginContainer/contents"] +layout_mode = 2 +size_flags_horizontal = 4 +text = "Return to Main Menu" + +[node name="SceneTransition" parent="." instance=ExtResource("4_1nbt3")] +visible = false +layout_mode = 1 + +[connection signal="pressed" from="Panel/SmartMarginContainer/contents/return" to="." method="_on_return_pressed"] diff --git a/client/gui/menus/game.gd b/client/gui/menus/game.gd new file mode 100644 index 00000000..a4916b92 --- /dev/null +++ b/client/gui/menus/game.gd @@ -0,0 +1,64 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu +class_name GameMenu + +@onready var game: Game = $Game +@onready var debug_label: RichTextLabel = $Debug +@onready var overlay: Overlay = $Overlay +@onready var popup_message: PopupMessage = $PopupMessage +@onready var chat_preview: ChatPreview = $ChatPreview +@onready var pinned_items: PinnedItemMessages = $PinnedItemMessages + +func _ready(): + get_tree().get_root().go_back_requested.connect(open_ingame_menu) + super() + transition.set_loading_text(tr("c.menu.game.connecting")) + Settings.hook_changed_init("ui.hide_overlays", false, apply_hide_overlays) + +func _input(_event): + if Input.is_action_just_pressed("ui_menu"): + open_ingame_menu() + + if Input.is_action_just_pressed("chat"): + Sound.play_click() + chat_preview.visible = false + await submenu("res://gui/menus/chat.tscn") + chat_preview.visible = true + + if Input.is_action_just_pressed("toggle_overlay"): + Global.set_setting("ui.hide_overlays", not Global.get_setting("ui.hide_overlays")) + +func _menu_cover(state): + game.follow_camera.disable_input_menu = state + game.follow_camera.update_disable_input() + +func _process(_delta): + if Global.get_setting("graphics.debug_info"): + debug_label.show() + debug_label.text = "%d FPS\nDriver: %s" % [Engine.get_frames_per_second(), ProjectSettings.get_setting("rendering/rendering_device/driver")] + else: debug_label.hide() + +func open_ingame_menu(): + if popup != null: return + Sound.play_click() + submenu("res://gui/menus/ingame.tscn") + +func apply_hide_overlays(v: bool): + overlay.visible = v + pinned_items.visible = v + chat_preview.visible = v + popup_message.visible = false diff --git a/client/gui/menus/game.gd.uid b/client/gui/menus/game.gd.uid new file mode 100644 index 00000000..992bc7f3 --- /dev/null +++ b/client/gui/menus/game.gd.uid @@ -0,0 +1 @@ +uid://bmno0s2du3ie6 diff --git a/client/gui/menus/game.tscn b/client/gui/menus/game.tscn new file mode 100644 index 00000000..56df81d6 --- /dev/null +++ b/client/gui/menus/game.tscn @@ -0,0 +1,46 @@ +[gd_scene load_steps=8 format=3 uid="uid://bbjwoxs71fnsk"] + +[ext_resource type="Script" uid="uid://bmno0s2du3ie6" path="res://gui/menus/game.gd" id="1_cdpsh"] +[ext_resource type="PackedScene" uid="uid://c6krh36hoqfg8" path="res://game.tscn" id="2_uojcy"] +[ext_resource type="PackedScene" uid="uid://bpikve6wlsjfl" path="res://gui/overlays/ingame/score.tscn" id="3_i0ytb"] +[ext_resource type="PackedScene" uid="uid://bc50la65ntifb" path="res://gui/overlays/lobby/lobby.tscn" id="3_udxby"] +[ext_resource type="PackedScene" uid="uid://b21nrnkygiyjt" path="res://gui/components/message/popup_message/popup_message.tscn" id="5_n1wy0"] +[ext_resource type="PackedScene" uid="uid://xcxbmynn8mhi" path="res://gui/overlays/ingame/chat.tscn" id="6_dh5lr"] +[ext_resource type="PackedScene" uid="uid://dcrr1rwdwbkq8" path="res://gui/components/message/popup_message/pinned_item_messages.tscn" id="7_lf2li"] + +[node name="GameMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_cdpsh") +auto_anim = false + +[node name="Game" parent="." instance=ExtResource("2_uojcy")] + +[node name="Overlay" parent="." instance=ExtResource("3_i0ytb")] +layout_mode = 1 + +[node name="Lobby" parent="." instance=ExtResource("3_udxby")] +layout_mode = 1 + +[node name="PinnedItemMessages" parent="." instance=ExtResource("7_lf2li")] +layout_mode = 1 + +[node name="ChatPreview" parent="." instance=ExtResource("6_dh5lr")] +layout_mode = 1 + +[node name="PopupMessage" parent="." instance=ExtResource("5_n1wy0")] +layout_mode = 1 + +[node name="Debug" type="RichTextLabel" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 diff --git a/client/gui/menus/ingame.gd b/client/gui/menus/ingame.gd new file mode 100644 index 00000000..4809b2ee --- /dev/null +++ b/client/gui/menus/ingame.gd @@ -0,0 +1,82 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +@onready var anim = $AnimationPlayer +@onready var options = $Side/Margin/Options +@onready var game: Game = $"../Game" +@onready var lobby_button: Button = $Side/Margin/Options/Lobby +@onready var leave_button: Button = $Side/Margin/Options/Leave + +var opened +func _ready(): + opened = Time.get_ticks_msec() + game.join_state_updated.connect(_on_game_join_state_changed) + _on_game_join_state_changed(game.join_state) + update_lobby_button() + super() + +func update_lobby_button(): + lobby_button.disabled = game.in_lobby or game.join_state == Game.JoinState.SPECTATING + if game.in_lobby: + lobby_button.tooltip_text = "Cannot cancel game since no game is running." + elif not game.join_state == Game.JoinState.JOINED: + lobby_button.tooltip_text = "You must join in order to be able to cancel the current game." + else: + lobby_button.tooltip_text = "" + +func anim_setup(): pass +func _menu_open(): + anim.play("activate") + await anim.animation_finished + game.mp.send_idle(true) +func _menu_exit(): + game.mp.send_idle(false) + anim.play_backwards("activate") + await anim.animation_finished + +func _on_resume_pressed(): + exit() + +func _on_main_menu_pressed(): + parent_menu.replace_menu("res://gui/menus/main/main.tscn") + +func _on_settings_pressed(): + submenu("res://gui/menus/settings/settings.tscn") + +func _on_reconnect_pressed(): + parent_menu.replace_menu("res://gui/menus/game.tscn", parent_menu.data) + +func _on_quit_pressed(): + quit() + +func _on_lobby_pressed(): + game.mp.send_chat(game.my_player_id, "/end") + exit() + +func _on_leave_pressed(): + game.toggle_join() + +func _on_game_join_state_changed(state: Game.JoinState): + match state: + Game.JoinState.JOINED: + leave_button.disabled = false + leave_button.text = tr("c.menu.ingame.leave") + Game.JoinState.SPECTATING: + leave_button.disabled = false + leave_button.text = tr("c.menu.ingame.join") + Game.JoinState.WAITING: + leave_button.disabled = true diff --git a/client/gui/menus/ingame.gd.uid b/client/gui/menus/ingame.gd.uid new file mode 100644 index 00000000..496c313c --- /dev/null +++ b/client/gui/menus/ingame.gd.uid @@ -0,0 +1 @@ +uid://dyi2xohgxeybb diff --git a/client/gui/menus/ingame.tscn b/client/gui/menus/ingame.tscn new file mode 100644 index 00000000..0a77af14 --- /dev/null +++ b/client/gui/menus/ingame.tscn @@ -0,0 +1,142 @@ +[gd_scene load_steps=12 format=3 uid="uid://lxlgtjm8hw7v"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_2vmyh"] +[ext_resource type="Script" uid="uid://dyi2xohgxeybb" path="res://gui/menus/ingame.gd" id="2_0h3no"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="3_vvvlt"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="4_b6bm7"] +[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://gui/resources/fonts/font-sansita-swashed.woff2" id="4_scupw"] +[ext_resource type="StyleBox" uid="uid://bw4jamyna1top" path="res://gui/resources/style/panel_style_sidebar.tres" id="4_vr8y1"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="6_poj4k"] + +[sub_resource type="Animation" id="Animation_8sedy"] +length = 0.001 +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Side:position:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0), +"points": PackedFloat32Array(0, -0.0005, 0, 0.0005, 0), +"times": PackedFloat32Array(0) +} + +[sub_resource type="Animation" id="Animation_660jl"] +resource_name = "activate" +tracks/0/type = "bezier" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Side:position:x") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"handle_modes": PackedInt32Array(0, 0), +"points": PackedFloat32Array(-400, -0.25, 0, 0.25, 0, 0, -0.25, 0, 0.25, 0), +"times": PackedFloat32Array(0, 1) +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_u0kyp"] +_data = { +&"RESET": SubResource("Animation_8sedy"), +&"activate": SubResource("Animation_660jl") +} + +[sub_resource type="FontVariation" id="FontVariation_ud3l8"] +base_font = ExtResource("4_scupw") +variation_embolden = 0.5 + +[node name="IngameMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_2vmyh") +script = ExtResource("2_0h3no") + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_u0kyp") +} +speed_scale = 8.0 + +[node name="Side" type="PanelContainer" parent="."] +material = ExtResource("3_vvvlt") +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 296.0 +grow_vertical = 2 +theme_override_styles/panel = ExtResource("4_vr8y1") +script = ExtResource("4_b6bm7") + +[node name="Margin" type="MarginContainer" parent="Side"] +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 +script = ExtResource("6_poj4k") + +[node name="Options" type="VBoxContainer" parent="Side/Margin"] +layout_mode = 2 + +[node name="Title" type="Label" parent="Side/Margin/Options"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0.566408, 0.208917, 0.266045, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = SubResource("FontVariation_ud3l8") +theme_override_font_sizes/font_size = 48 +text = "Hurry Curry!" + +[node name="Spacer" type="Control" parent="Side/Margin/Options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="Resume" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.ingame.resume" +alignment = 0 + +[node name="Leave" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.ingame.join" +alignment = 0 + +[node name="Lobby" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.ingame.cancel" +alignment = 0 + +[node name="Reconnect" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.ingame.reconnect" +alignment = 0 + +[node name="Spacer2" type="Control" parent="Side/Margin/Options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="Settings" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.settings" +alignment = 0 + +[node name="Spacer3" type="Control" parent="Side/Margin/Options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="MainMenu" type="Button" parent="Side/Margin/Options"] +layout_mode = 2 +text = "c.menu.ingame.main_menu" +alignment = 0 + +[connection signal="pressed" from="Side/Margin/Options/Resume" to="." method="_on_resume_pressed"] +[connection signal="pressed" from="Side/Margin/Options/Leave" to="." method="_on_leave_pressed"] +[connection signal="pressed" from="Side/Margin/Options/Lobby" to="." method="_on_lobby_pressed"] +[connection signal="pressed" from="Side/Margin/Options/Reconnect" to="." method="_on_reconnect_pressed"] +[connection signal="pressed" from="Side/Margin/Options/Settings" to="." method="_on_settings_pressed"] +[connection signal="pressed" from="Side/Margin/Options/MainMenu" to="." method="_on_main_menu_pressed"] diff --git a/client/gui/menus/main/about.gd b/client/gui/menus/main/about.gd new file mode 100644 index 00000000..b56d3941 --- /dev/null +++ b/client/gui/menus/main/about.gd @@ -0,0 +1,169 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +var authors := ["metamuffin", "nokoe", "tpart"] +var contributors := ["sofviic", "BigBrotherNii", "Miner34"] +const cc_by_4 := "CC-BY 4.0" +const cc_by_3 := "CC-BY 3.0" +const cc0 := "CC0" + +const AGPL_NOTICE := """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, version 3 of the License only. + +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/>.""" + +const SOURCE_CODE := "https://codeberg.org/hurrycurry/hurrycurry" + +func _ready() -> void: + super() + $side/margin/options/first/source.tooltip_text = SOURCE_CODE + +var credits := [ + [tr("c.credits.models"), [ + ["kenney.nl", "Various Models", cc0], + ["Kay Lousberg", "Kitchen tiles", cc0], + ["Poly by Google", "Strawberry", cc_by_3], + ["Poly by Google", "Fish", cc_by_3], + ["jeremy", "Propeller hat", cc_by_3] + ]], + [tr("c.credits.sounds"), [ + ["Dryoma", "Footstep sounds", cc_by_4], + ["Koops", "Page_Turn_24.wav", cc_by_4], + ["InspectorJ", "Pencil, Writing, Close, A.wav", cc_by_4], + ["Dillon Becker", "Super Dialogue Audio Pack", cc_by_4], + ["Ellr", "Universal UI/Menu Soundpack", cc_by_4], + ["toyoto", "Woosh Fleuret Escrime B.wav", cc_by_4], + ["deleted_user_877451", "Game_over.wav", cc_by_3], + ["Quaternius", "Someting, dont remember...", cc0], + ["Dillon Becker", "Super Dialogue Audio Pack V1", cc_by_4] + ]], + [tr("c.credits.translations"), { + tr("c.settings.ui.language.zh_Hans"): ["Outbreak2096"], + tr("c.settings.ui.language.zh_Hant"): ["hugoalh"], + tr("c.settings.ui.language.nl"): ["Vistaus"], + tr("c.settings.ui.language.it"): ["Miner34", "solemden"], + tr("c.settings.ui.language.eu"): ["josuigoa"], + tr("c.settings.ui.language.fr"): ["fnetX", "lejun"], + tr("c.settings.ui.language.pl"): ["tranzystorekk"], + tr("c.settings.ui.language.he"): ["RustyStriker"], + tr("c.settings.ui.language.el"): ["n0toose"], + tr("c.settings.ui.language.ja"): ["BigBrotherNii"], + tr("c.settings.ui.language.ar"): ["sofviic"], + tr("c.settings.ui.language.tr"): ["furkanunsalan", "tekrei"], + tr("c.settings.ui.language.ru"): ["0ko"], + }] +] + +func _menu_cover(state): + $side.visible = not state + +func credits_text() -> String: + var text = "[center]" + authors.shuffle() + contributors.shuffle() + + text += "\n\n\n[b]%s[/b]\n\n%s\n\n[b]%s[/b]\n\n%s\n\n[b]%s[/b]\n\n\n" % [ + tr("c.credits.title"), + tr("c.credits.developed_by"), + "\n".join(authors), + tr("c.credits.contributors"), + ", ".join(contributors), + ] + + for section in credits: + text += "[b]%s[/b]\n\n" % section[0] + if typeof(section[1]) == TYPE_DICTIONARY: + text += "[table=2]" + for key in section[1]: + text += "[cell][right]%s[/right][/cell]" % key + text += "[cell][left]%s[/left][/cell]" % ", ".join(section[1][key]) + text += "[/table]" + else: + text += "[table=3]" + for entry in section[1]: + text += "[cell][right]%s[/right][/cell]" % entry[0] + text += "[cell][left]%s[/left][/cell]" % entry[1] + text += "[cell][left]%s[/left][/cell]" % entry[2] + text += "[/table]" + text += "\n\n\n" + + text += "\n[b]%s[/b]\n\n\n[/center]" % tr("c.credits.thanks") + return text + +func legal_text() -> String: + var all: Array[String] = [] + var translators: Array[String] = [] + for c in credits[2][1].values(): + translators.append_array(c) + translators.shuffle() + authors.shuffle() + contributors.shuffle() + all.append_array(authors) + all.append_array(contributors) + all.append_array(translators) + + var text := "Hurry Curry! - a game about cooking\n" + text += "[code]Copyright 2024, 2025 %s\n\n" % ", ".join(dedup_array(all)) + text += "%s[/code]\n\n" % AGPL_NOTICE + text += tr("c.legal.using_godot") + text += "\n\n[code]%s[/code]" % Engine.get_license_text() + return text + +func version_text() -> String: + var text := "[center][b]Hurry Curry![/b]\n\n" + OS.get_version() + text += "[table=2]" + var row = "[cell][right]%s[/right][/cell][cell][left]%s[/left][/cell]" + text += row % [tr("c.version.game"), Global.VERSION] + text += row % [tr("c.version.protocol"), "%s.%s" % [Multiplayer.VERSION_MAJOR, Multiplayer.VERSION_MINOR]] + text += row % [tr("c.version.godot"), Engine.get_version_info().string] + text += row % [tr("c.version.os"), OS.get_name()] + text += row % [tr("c.version.arch"), Engine.get_architecture_name()] + text += row % [tr("c.version.distribution"), Global.DISTRIBUTION] + + text += "[/table]" + text += "[/center]" + return text + +func _on_credits_pressed() -> void: + submenu("res://gui/menus/popup_large.tscn", credits_text()) + +func _on_legal_pressed() -> void: + submenu("res://gui/menus/popup_large.tscn", legal_text()) + +func _on_version_pressed() -> void: + submenu("res://gui/menus/popup_large.tscn", version_text()) + +func _on_back_pressed() -> void: + exit() + + +func _on_source_pressed() -> void: + OS.shell_open(SOURCE_CODE) + +func dedup_array(a: Array) -> Array: + var b = [] + for x in a: if not b.has(x): b.append(x) + return b diff --git a/client/gui/menus/main/about.gd.uid b/client/gui/menus/main/about.gd.uid new file mode 100644 index 00000000..3929f2c3 --- /dev/null +++ b/client/gui/menus/main/about.gd.uid @@ -0,0 +1 @@ +uid://pcu87stpkgd8 diff --git a/client/gui/menus/main/about.tscn b/client/gui/menus/main/about.tscn new file mode 100644 index 00000000..006b61fe --- /dev/null +++ b/client/gui/menus/main/about.tscn @@ -0,0 +1,93 @@ +[gd_scene load_steps=7 format=3 uid="uid://bpaenm8c6nmo8"] + +[ext_resource type="Script" uid="uid://pcu87stpkgd8" path="res://gui/menus/main/about.gd" id="1_0acu0"] +[ext_resource type="Material" uid="uid://2j8a0c0a2ta5" path="res://gui/resources/materials/blur_material.tres" id="1_ai5pk"] +[ext_resource type="StyleBox" uid="uid://bw4jamyna1top" path="res://gui/resources/style/panel_style_sidebar.tres" id="2_pya1x"] +[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://gui/resources/fonts/font-sansita-swashed.woff2" id="4_kx3j7"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="4_t51wf"] + +[sub_resource type="FontVariation" id="FontVariation_o2r3e"] +base_font = ExtResource("4_kx3j7") +variation_embolden = 0.5 + +[node name="AboutMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_0acu0") +support_anim = false + +[node name="side" type="PanelContainer" parent="."] +material = ExtResource("1_ai5pk") +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 294.0 +grow_vertical = 2 +theme_override_styles/panel = ExtResource("2_pya1x") + +[node name="margin" type="MarginContainer" parent="side"] +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 +script = ExtResource("4_t51wf") + +[node name="options" type="VBoxContainer" parent="side/margin"] +layout_mode = 2 + +[node name="title" type="Label" parent="side/margin/options"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0.566408, 0.208917, 0.266045, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = SubResource("FontVariation_o2r3e") +theme_override_font_sizes/font_size = 48 +text = "Hurry Curry!" + +[node name="spacer" type="Control" parent="side/margin/options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="first" type="VBoxContainer" parent="side/margin/options"] +layout_mode = 2 + +[node name="credits" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.about.credits" +alignment = 0 + +[node name="version" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.about.version" +alignment = 0 + +[node name="legal" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.about.legal" +alignment = 0 + +[node name="source" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.about.source" +alignment = 0 + +[node name="first2" type="VBoxContainer" parent="side/margin/options"] +layout_mode = 2 +size_flags_vertical = 3 +alignment = 2 + +[node name="back" type="Button" parent="side/margin/options/first2"] +layout_mode = 2 +text = "c.menu.back" +alignment = 0 + +[connection signal="pressed" from="side/margin/options/first/credits" to="." method="_on_credits_pressed"] +[connection signal="pressed" from="side/margin/options/first/version" to="." method="_on_version_pressed"] +[connection signal="pressed" from="side/margin/options/first/legal" to="." method="_on_legal_pressed"] +[connection signal="pressed" from="side/margin/options/first/source" to="." method="_on_source_pressed"] +[connection signal="pressed" from="side/margin/options/first2/back" to="." method="_on_back_pressed"] diff --git a/client/gui/menus/main/background.gd b/client/gui/menus/main/background.gd new file mode 100644 index 00000000..4abb84b4 --- /dev/null +++ b/client/gui/menus/main/background.gd @@ -0,0 +1,50 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Node3D + +const CRATES = ["tomato-crate", "steak-crate", "cheese-crate", "lettuce-crate", "flour-crate", "coconut-crate"] +const TOOLS = ["stove", "stove", "stove", "sink", "cuttingboard", "sink", "cuttingboard", "oven", "freezer"] + +@onready var environment: WorldEnvironment = $Environment +@onready var map: Map = $Map + +func _ready(): + if !Global.on_vulkan(): + environment.environment.tonemap_exposure = 0.25 + + var tiles = {} + for x in range(-10, 11): + for y in range(-10, 11): + var w = exp(-sqrt(x * x + y * y) * 0.15) + var k = randf() * w + var tn = null + if k > 0.25: tn = "floor" + if k > 0.4: tn = choose(CRATES) if randf() > 0.3 else "counter" + if k > 0.6: tn = choose(TOOLS) + if tn != null: tiles[str(Vector2i(x,y))] = [tn,[x,y]] + + var gt = func (cs): + var t = tiles.get(str(Vector2i(cs[0],cs[1]))) + return null if t == null else t[0] + for pk in tiles.keys(): + var x = tiles[pk][1][0] + var y = tiles[pk][1][1] + var t = gt.call([x,y]) + if t != null: map.set_tile(Vector2i(x,y), t, [[x,y-1],[x-1,y],[x,y+1],[x+1,y]].map(gt)) + + map.flush() + +func choose(a): return a[floor(a.size() * randf())] diff --git a/client/gui/menus/main/background.gd.uid b/client/gui/menus/main/background.gd.uid new file mode 100644 index 00000000..7d61d488 --- /dev/null +++ b/client/gui/menus/main/background.gd.uid @@ -0,0 +1 @@ +uid://b2tq5rcjjcxdg diff --git a/client/gui/menus/main/background.tscn b/client/gui/menus/main/background.tscn new file mode 100644 index 00000000..2bd4ee04 --- /dev/null +++ b/client/gui/menus/main/background.tscn @@ -0,0 +1,71 @@ +[gd_scene load_steps=12 format=3 uid="uid://l4vm07dtda4j"] + +[ext_resource type="Script" uid="uid://b2tq5rcjjcxdg" path="res://gui/menus/main/background.gd" id="1_pgu7b"] +[ext_resource type="Script" uid="uid://cwg7wympevxs4" path="res://map/auto_setup/environment_setup.gd" id="2_7dwbj"] +[ext_resource type="Shader" uid="uid://b1k6ipo0sagli" path="res://gui/menus/main/clouds.gdshader" id="3_lapmn"] +[ext_resource type="PackedScene" uid="uid://b4gone8fu53r7" path="res://map/map.tscn" id="4_nslxb"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_uw50b"] +sky_top_color = Color(0.55, 0.55, 0.55, 1) + +[sub_resource type="Sky" id="Sky_utrtx"] +sky_material = SubResource("ProceduralSkyMaterial_uw50b") + +[sub_resource type="Environment" id="Environment_slkjl"] +background_mode = 1 +background_color = Color(0.517035, 0.49506, 0.878906, 1) +sky = SubResource("Sky_utrtx") +tonemap_mode = 2 +tonemap_exposure = 0.6 +tonemap_white = 0.9 +ssao_enabled = true + +[sub_resource type="QuadMesh" id="QuadMesh_fvp2p"] +size = Vector2(100, 100) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_e3eby"] +noise_type = 2 +frequency = 0.008 +fractal_octaves = 7 +fractal_gain = 0.72 + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_s4fnp"] +width = 1024 +height = 1024 +generate_mipmaps = false +seamless = true +noise = SubResource("FastNoiseLite_e3eby") + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_gd87g"] +render_priority = 0 +shader = ExtResource("3_lapmn") +shader_parameter/noise = SubResource("NoiseTexture2D_s4fnp") +shader_parameter/ccloud = Color(0.835938, 0.835938, 0.835938, 1) +shader_parameter/csky = Color(0.329412, 0.333333, 0.8, 1) + +[node name="MenuBackground" type="Node3D"] +script = ExtResource("1_pgu7b") + +[node name="Camera" type="Camera3D" parent="."] +transform = Transform3D(0.614606, 0.499662, -0.610408, -0.00282255, 0.775198, 0.631712, 0.78883, -0.386531, 0.477852, -9.13611, 4.90356, 1.22532) +projection = 1 +current = true +size = 8.0 +far = 100.0 + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(0.290334, 0.904946, -0.311092, 0.336606, 0.207739, 0.918445, 0.895769, -0.371371, -0.244296, 0, 7, 0) +light_energy = 1.25 +shadow_enabled = true + +[node name="Environment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_slkjl") +script = ExtResource("2_7dwbj") +allow_sdfgi = false + +[node name="the-sky-tm" type="MeshInstance3D" parent="."] +transform = Transform3D(0.614606, 0.499662, -0.610408, -0.00282255, 0.775198, 0.631712, 0.78883, -0.386531, 0.477851, 6, -13, -11) +mesh = SubResource("QuadMesh_fvp2p") +surface_material_override/0 = SubResource("ShaderMaterial_gd87g") + +[node name="Map" parent="." instance=ExtResource("4_nslxb")] diff --git a/client/gui/menus/main/clouds.gdshader b/client/gui/menus/main/clouds.gdshader new file mode 100644 index 00000000..8103f691 --- /dev/null +++ b/client/gui/menus/main/clouds.gdshader @@ -0,0 +1,36 @@ +/* + Hurry Curry! - a game about cooking + Copyright (C) 2025 Hurry Curry! Contributors + + 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, version 3 of the License only. + + 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/>. + +*/ +shader_type spatial; +render_mode unshaded; + +uniform sampler2D noise : source_color; +uniform vec3 ccloud : source_color; +uniform vec3 csky : source_color; + +void fragment() { + vec2 uv = UV * 0.9; + uv += TIME * vec2(0.001,0.002); + + float f = texture(noise, uv).x; + f = 1. - f; + f = pow(f, 1.5); + f = floor(f*5.)/5.; + f = pow(f, 2.); + + ALBEDO = mix(csky, ccloud, f); +} diff --git a/client/gui/menus/main/clouds.gdshader.uid b/client/gui/menus/main/clouds.gdshader.uid new file mode 100644 index 00000000..00c2d21a --- /dev/null +++ b/client/gui/menus/main/clouds.gdshader.uid @@ -0,0 +1 @@ +uid://b1k6ipo0sagli diff --git a/client/gui/menus/main/main.gd b/client/gui/menus/main/main.gd new file mode 100644 index 00000000..423f756e --- /dev/null +++ b/client/gui/menus/main/main.gd @@ -0,0 +1,44 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +@onready var quit_button = $side/margin/options/first/quit +@onready var margin_container: MarginContainer = $side/margin + +func _ready(): + super() + if OS.has_feature("web"): + quit_button.hide() + Sound.play_music("MainMenu") + ServerList.one_shot() + +func _menu_cover(state): + $side.visible = not state + +func _on_quit_pressed(): + quit() + +func _on_about_pressed(): + submenu("res://gui/menus/main/about.tscn") + +func _on_change_character_pressed(): + replace_menu("res://gui/menus/character.tscn", null, "res://gui/menus/main.tscn") + +func _on_settings_pressed(): + submenu("res://gui/menus/settings/settings.tscn") + +func _on_play_pressed(): + submenu("res://gui/menus/main/play.tscn") diff --git a/client/gui/menus/main/main.gd.uid b/client/gui/menus/main/main.gd.uid new file mode 100644 index 00000000..dc2cecd3 --- /dev/null +++ b/client/gui/menus/main/main.gd.uid @@ -0,0 +1 @@ +uid://bpiynadrmdd37 diff --git a/client/gui/menus/main/main.tscn b/client/gui/menus/main/main.tscn new file mode 100644 index 00000000..81595966 --- /dev/null +++ b/client/gui/menus/main/main.tscn @@ -0,0 +1,94 @@ +[gd_scene load_steps=10 format=3 uid="uid://dbj8508whxgwv"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_3qfu3"] +[ext_resource type="Script" uid="uid://bpiynadrmdd37" path="res://gui/menus/main/main.gd" id="2_xjnc3"] +[ext_resource type="PackedScene" uid="uid://l4vm07dtda4j" path="res://gui/menus/main/background.tscn" id="3_4evao"] +[ext_resource type="Material" uid="uid://2j8a0c0a2ta5" path="res://gui/resources/materials/blur_material.tres" id="4_nx4vf"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="5_0mn56"] +[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://gui/resources/fonts/font-sansita-swashed.woff2" id="5_k7bqq"] +[ext_resource type="StyleBox" uid="uid://bw4jamyna1top" path="res://gui/resources/style/panel_style_sidebar.tres" id="5_qlyeo"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="7_btdj1"] + +[sub_resource type="FontVariation" id="FontVariation_htgmg"] +base_font = ExtResource("5_k7bqq") +variation_embolden = 0.5 + +[node name="MainMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_3qfu3") +script = ExtResource("2_xjnc3") + +[node name="MenuBackground" parent="." instance=ExtResource("3_4evao")] + +[node name="side" type="PanelContainer" parent="."] +material = ExtResource("4_nx4vf") +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 294.0 +grow_vertical = 2 +theme_override_styles/panel = ExtResource("5_qlyeo") +script = ExtResource("5_0mn56") + +[node name="margin" type="MarginContainer" parent="side"] +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 +script = ExtResource("7_btdj1") + +[node name="options" type="VBoxContainer" parent="side/margin"] +layout_mode = 2 + +[node name="title" type="Label" parent="side/margin/options"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0.566408, 0.208917, 0.266045, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = SubResource("FontVariation_htgmg") +theme_override_font_sizes/font_size = 48 +text = "Hurry Curry!" + +[node name="spacer" type="Control" parent="side/margin/options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="first" type="VBoxContainer" parent="side/margin/options"] +layout_mode = 2 + +[node name="play" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.play" +alignment = 0 + +[node name="change_character" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.my_chef" +alignment = 0 + +[node name="settings" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.settings" +alignment = 0 + +[node name="about" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.about" +alignment = 0 + +[node name="quit" type="Button" parent="side/margin/options/first"] +layout_mode = 2 +text = "c.menu.quit" +alignment = 0 + +[connection signal="pressed" from="side/margin/options/first/play" to="." method="_on_play_pressed"] +[connection signal="pressed" from="side/margin/options/first/change_character" to="." method="_on_change_character_pressed"] +[connection signal="pressed" from="side/margin/options/first/settings" to="." method="_on_settings_pressed"] +[connection signal="pressed" from="side/margin/options/first/about" to="." method="_on_about_pressed"] +[connection signal="pressed" from="side/margin/options/first/quit" to="." method="_on_quit_pressed"] diff --git a/client/gui/menus/main/play.gd b/client/gui/menus/main/play.gd new file mode 100644 index 00000000..c9cbee2f --- /dev/null +++ b/client/gui/menus/main/play.gd @@ -0,0 +1,208 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +var server_list_item: PackedScene = preload("res://gui/menus/main/server_list_item.tscn") +var url_regex: RegEx = RegEx.new() + +@onready var server_list: VBoxContainer = $side/margin/options/second/ScrollContainerCustom/ServerList +@onready var server_list_loading: Label = $side/margin/options/second/Loading +@onready var server_list_empty: Label = $side/margin/options/second/NoServers +@onready var connect_uri = $side/margin/options/second/connect/uri +@onready var server = $side/margin/options/second/server +@onready var server_control = $side/margin/options/second/server/control +@onready var server_connect = $side/margin/options/second/server/connect +@onready var editor_control = $side/margin/options/second/editor/control +@onready var editor_connect = $side/margin/options/second/editor/connect +@onready var editor_container = $side/margin/options/second/editor + + +func _ready(): + url_regex.compile("^(?:(ws|wss)://)?([^:]+)(?::([0-9]+))?$") + if OS.has_feature("web"): + server.hide() + connect_uri.text = Global.get_profile("last_server_url") + Sound.play_music("MainMenu") + + ServerList.update_server_list.connect(update_server_list) + ServerList.update_loading.connect(update_server_list_loading) + update_server_list(ServerList.current_list) + update_server_list_loading(ServerList.loading) + + super() + if not Global.get_profile("registry_asked"): + var popup_data := MenuPopup.Data.new() + popup_data.text = tr("c.menu.play.allow_query_registry").format([Global.get_setting("online.registry_url")]) + var allow_button := Button.new() + allow_button.text = tr("c.menu.accept") + var deny_button := Button.new() + deny_button.text = tr("c.menu.deny") + allow_button.pressed.connect(func(): Global.set_setting("online.use_registry", true)) + deny_button.pressed.connect(func(): Global.set_setting("online.use_registry", false)) + popup_data.buttons = [allow_button, deny_button] + await submenu("res://gui/menus/popup.tscn", popup_data) + Global.set_profile("registry_asked", true) + Global.save_settings() + Global.save_profile() + + ServerList.start() + +func update_server_list(lists: Array[Array]): + # Find out the index of the currently focused server in the list + var prev_selected_idx := -1 + for i in range(server_list.get_children().size()): + if server_list.get_child(i).button.has_focus(): + prev_selected_idx = i + break + + for c in server_list.get_children(): + c.queue_free() + + var idx := 0 + for l in lists: + for i in l: + var server_item: ServerListItem = server_list_item.instantiate() + server_list.add_child(server_item) + # TODO: Implement fallback address correctly + server_item.setup(i.name, roundi(i.players_online), i.version) + server_item.button.pressed.connect(connect_to.bind(i.address[0])) + # Focus the same server with the same index as the previously focused one + if idx == prev_selected_idx: + server_item.button.grab_focus() + idx += 1 + + if prev_selected_idx > idx: + # Same index cannot be focused, since number of servers decreased + if idx - 1 < 0: + connect_uri.grab_focus() + else: + server_list.get_child(idx - 1).button.grab_focus() + + # Show message if no servers available + server_list_empty.visible = idx == 0 + +func update_server_list_loading(status: bool): + server_list_loading.visible = status + +func _menu_cover(state): + $side.visible = not state + +func _on_connect_pressed(): + var url = connect_uri.text + var result := url_regex.search(url) + if result != null: + if result.get_string(1) == "": + url = "ws://" + url + # only set default port for non-tls websocket connections + if result.get_string(3) == "" and result.get_string(1) != "wss": + url = url + ":27032" + connect_uri.text = url + Global.set_profile("last_server_url", url) + Global.save_profile() + connect_to(url) + +func _on_quick_connect_pressed(): + if OS.has_feature("web"): + connect_to(JavaScriptBridge.eval(""" + window.location.protocol.endsWith("s:") + ? `wss://${window.location.host}/` + : `ws://${window.location.hostname}:27032/` + """)) + else: + connect_to("wss://hurrycurry.metamuffin.org/") + +func connect_to(url: String): + print("Connecting to %s" % url) + get_parent().replace_menu("res://gui/menus/game.tscn", url) + +func _on_server_control_pressed(): + match Server.state: + Service.State.RUNNING: Server.stop() + Service.State.STOPPED: Server.start() + Service.State.FAILED: Server.start() + +func _on_editor_control_pressed(): + match Editor.state: + Service.State.RUNNING: Editor.stop() + Service.State.STOPPED: Editor.start(); Server.start() + Service.State.FAILED: Editor.start() + +func _on_server_connect_pressed(): + connect_to("ws://%s:%d" % [Server.connect_address(), Global.get_setting("server.bind_port")]) + +func _on_editor_connect_pressed(): + connect_to("ws://[::1]:27032/") + +func _process(_delta): + server_control.disabled = false + server_connect.visible = Server.state == Service.State.RUNNING + server_control.modulate = Color.WHITE + match Server.state: + Service.State.RUNNING: + server_control.text = tr("c.menu.play.server_stop") + server_control.modulate = Color.AQUAMARINE + Service.State.TESTING: + server_control.text = tr("c.menu.play.server_testing") + server_control.disabled = true + Service.State.STARTING: + server_control.text = tr("c.menu.play.server_starting") + server_control.disabled = true + Service.State.STOPPED: + server_control.text = tr("c.menu.play.server_start") + Service.State.FAILED: + server_control.text = tr("c.menu.play.server_failed") + server_control.modulate = Color(1, 0.4, 0.5) + server_control.tooltip_text = tr("c.menu.play.server_failed_tooltip") + Service.State.UNAVAILABLE: + server_control.text = tr("c.menu.play.server_unavailable") + server_control.disabled = true + server_control.tooltip_text = tr("c.menu.play.server_binary_not_found") + + editor_control.disabled = false + editor_connect.visible = Editor.state == Service.State.RUNNING + editor_control.modulate = Color.WHITE + editor_container.visible = Editor.state != Service.State.UNAVAILABLE + match Editor.state: + Service.State.RUNNING: + editor_control.text = tr("c.menu.play.editor_stop") + editor_control.modulate = Color.AQUAMARINE + Service.State.TESTING: + editor_control.text = tr("c.menu.play.editor_testing") + editor_control.disabled = true + Service.State.STARTING: + editor_control.text = tr("c.menu.play.editor_starting") + editor_control.disabled = true + Service.State.STOPPED: + editor_control.text = tr("c.menu.play.editor_start") + Service.State.FAILED: + editor_control.text = tr("c.menu.play.editor_failed") + editor_control.modulate = Color(1, 0.4, 0.5) + editor_control.tooltip_text = tr("c.menu.play.server_failed_tooltip") + Service.State.UNAVAILABLE: + editor_control.text = tr("c.menu.play.editor_unavailable") + editor_control.disabled = true + editor_control.tooltip_text = tr("c.menu.play.server_binary_not_found") + + +func _on_uri_text_changed(new_text): + connect_uri.modulate = Color.WHITE if url_regex.search(new_text) else Color.RED + +func _on_back_pressed(): + exit() + +func _menu_exit(): + ServerList.stop() + super() diff --git a/client/gui/menus/main/play.gd.uid b/client/gui/menus/main/play.gd.uid new file mode 100644 index 00000000..d8ca168f --- /dev/null +++ b/client/gui/menus/main/play.gd.uid @@ -0,0 +1 @@ +uid://b126k2228nj4s diff --git a/client/gui/menus/main/play.tscn b/client/gui/menus/main/play.tscn new file mode 100644 index 00000000..441024a2 --- /dev/null +++ b/client/gui/menus/main/play.tscn @@ -0,0 +1,149 @@ +[gd_scene load_steps=9 format=3 uid="uid://c8url5fpttbem"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_cckds"] +[ext_resource type="Script" uid="uid://b126k2228nj4s" path="res://gui/menus/main/play.gd" id="2_phxx0"] +[ext_resource type="Material" uid="uid://2j8a0c0a2ta5" path="res://gui/resources/materials/blur_material.tres" id="3_fsbt7"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="4_gst6r"] +[ext_resource type="Script" uid="uid://bd7bylb2t2m0" path="res://gui/components/touch_scroll_container.gd" id="5_cm120"] +[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://gui/resources/fonts/font-sansita-swashed.woff2" id="5_ojpbf"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_ukani"] + +[sub_resource type="FontVariation" id="FontVariation_htgmg"] +base_font = ExtResource("5_ojpbf") +variation_embolden = 0.5 + +[node name="PlayMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_cckds") +script = ExtResource("2_phxx0") +support_anim = false + +[node name="side" type="PanelContainer" parent="."] +material = ExtResource("3_fsbt7") +layout_mode = 1 +anchors_preset = 9 +anchor_bottom = 1.0 +offset_right = 294.0 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_ukani") + +[node name="margin" type="MarginContainer" parent="side"] +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 +script = ExtResource("4_gst6r") + +[node name="options" type="VBoxContainer" parent="side/margin"] +layout_mode = 2 + +[node name="title" type="Label" parent="side/margin/options"] +auto_translate_mode = 2 +layout_mode = 2 +theme_override_colors/font_outline_color = Color(0.566408, 0.208917, 0.266045, 1) +theme_override_constants/outline_size = 10 +theme_override_fonts/font = SubResource("FontVariation_htgmg") +theme_override_font_sizes/font_size = 48 +text = "Hurry Curry!" + +[node name="spacer" type="Control" parent="side/margin/options"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="second" type="VBoxContainer" parent="side/margin/options"] +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Loading" type="Label" parent="side/margin/options/second"] +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +text = "c.menu.play.fetching_list" +horizontal_alignment = 1 + +[node name="NoServers" type="Label" parent="side/margin/options/second"] +visible = false +layout_mode = 2 +size_flags_horizontal = 3 +text = "c.menu.play.no_servers" +horizontal_alignment = 1 + +[node name="ScrollContainerCustom" type="ScrollContainer" parent="side/margin/options/second"] +layout_mode = 2 +size_flags_vertical = 3 +horizontal_scroll_mode = 0 +script = ExtResource("5_cm120") + +[node name="ServerList" type="VBoxContainer" parent="side/margin/options/second/ScrollContainerCustom"] +layout_mode = 2 +size_flags_horizontal = 3 + +[node name="spacer" type="Control" parent="side/margin/options/second"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="connect" type="HBoxContainer" parent="side/margin/options/second"] +layout_mode = 2 + +[node name="uri" type="LineEdit" parent="side/margin/options/second/connect"] +auto_translate_mode = 2 +layout_mode = 2 +size_flags_horizontal = 3 +placeholder_text = "wss://example.org" + +[node name="connect" type="Button" parent="side/margin/options/second/connect"] +layout_mode = 2 +text = "c.menu.play.connect" + +[node name="server" type="HBoxContainer" parent="side/margin/options/second"] +layout_mode = 2 + +[node name="control" type="Button" parent="side/margin/options/second/server"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "c.menu.play.server" +alignment = 0 + +[node name="connect" type="Button" parent="side/margin/options/second/server"] +layout_mode = 2 +text = "c.menu.play.connect" + +[node name="editor" type="HBoxContainer" parent="side/margin/options/second"] +layout_mode = 2 + +[node name="control" type="Button" parent="side/margin/options/second/editor"] +layout_mode = 2 +size_flags_horizontal = 3 +text = "c.menu.play.editor" +alignment = 0 + +[node name="connect" type="Button" parent="side/margin/options/second/editor"] +layout_mode = 2 +text = "c.menu.play.connect" + +[node name="spacer2" type="Control" parent="side/margin/options/second"] +custom_minimum_size = Vector2(0, 10) +layout_mode = 2 + +[node name="back" type="Button" parent="side/margin/options/second"] +layout_mode = 2 +text = "c.menu.back" +alignment = 0 + +[node name="VBoxContainer" type="VBoxContainer" parent="side/margin/options/second"] +layout_mode = 2 + +[connection signal="text_changed" from="side/margin/options/second/connect/uri" to="." method="_on_uri_text_changed"] +[connection signal="pressed" from="side/margin/options/second/connect/connect" to="." method="_on_connect_pressed"] +[connection signal="pressed" from="side/margin/options/second/server/control" to="." method="_on_server_control_pressed"] +[connection signal="pressed" from="side/margin/options/second/server/connect" to="." method="_on_server_connect_pressed"] +[connection signal="pressed" from="side/margin/options/second/editor/control" to="." method="_on_editor_control_pressed"] +[connection signal="pressed" from="side/margin/options/second/editor/connect" to="." method="_on_editor_connect_pressed"] +[connection signal="pressed" from="side/margin/options/second/back" to="." method="_on_back_pressed"] diff --git a/client/gui/menus/main/server_list_item.gd b/client/gui/menus/main/server_list_item.gd new file mode 100644 index 00000000..0cffdd72 --- /dev/null +++ b/client/gui/menus/main/server_list_item.gd @@ -0,0 +1,38 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 ServerListItem +extends PanelContainer + +var error_style_focus: StyleBoxFlat = preload("res://gui/resources/style/error_focus_style.tres") +@onready var title: Label = $MarginContainer/VBoxContainer/Title +@onready var info: Label = $MarginContainer/VBoxContainer/Info +@onready var button: Button = $Button + +func setup(name_: String, online_players: int, version: Array): + title.text = name_ + if version[0] != Multiplayer.VERSION_MAJOR or version[1] > Multiplayer.VERSION_MINOR: + button.disabled = true + button.add_theme_stylebox_override("focus", error_style_focus) + info.text = tr("c.menu.play.server_version_mismatch") + info.add_theme_color_override("font_color", Color("ff2222")) + return + info.text = tr("c.menu.play.server_players").format([online_players]) + + # This node is in group not no_click_sound, so sounds won't be automatically connected + # by menu system. Reason: These nodes are deleted and re-created every few seconds + # in server list, and signals are only connected on ready. + button.pressed.connect(Sound.play_click) + button.mouse_entered.connect(Sound.play_hover) diff --git a/client/gui/menus/main/server_list_item.gd.uid b/client/gui/menus/main/server_list_item.gd.uid new file mode 100644 index 00000000..276bb06f --- /dev/null +++ b/client/gui/menus/main/server_list_item.gd.uid @@ -0,0 +1 @@ +uid://xr5oigbgd0aw diff --git a/client/gui/menus/main/server_list_item.tscn b/client/gui/menus/main/server_list_item.tscn new file mode 100644 index 00000000..40e9eedb --- /dev/null +++ b/client/gui/menus/main/server_list_item.tscn @@ -0,0 +1,39 @@ +[gd_scene load_steps=3 format=3 uid="uid://t2h60dhuvfsk"] + +[ext_resource type="Script" uid="uid://xr5oigbgd0aw" path="res://gui/menus/main/server_list_item.gd" id="1_1n1yg"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_1n1yg"] + +[node name="ServerListItem" type="PanelContainer"] +offset_right = 400.0 +offset_bottom = 40.0 +size_flags_horizontal = 3 +theme_override_styles/panel = SubResource("StyleBoxEmpty_1n1yg") +script = ExtResource("1_1n1yg") + +[node name="Button" type="Button" parent="." groups=["no_click_sound"]] +layout_mode = 2 + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 2 +mouse_filter = 2 +theme_override_constants/margin_left = 10 +theme_override_constants/margin_top = 10 +theme_override_constants/margin_right = 10 +theme_override_constants/margin_bottom = 10 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"] +layout_mode = 2 +mouse_filter = 2 + +[node name="Title" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.87451, 0.87451, 0.87451, 1) +theme_override_font_sizes/font_size = 18 +text = "Example Server" + +[node name="Info" type="Label" parent="MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0.749781, 0.74978, 0.74978, 1) +theme_override_font_sizes/font_size = 14 +text = "5 players online" diff --git a/client/gui/menus/menu.gd b/client/gui/menus/menu.gd new file mode 100644 index 00000000..0f6e0624 --- /dev/null +++ b/client/gui/menus/menu.gd @@ -0,0 +1,151 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu +extends Control + + +#enum Anim { NONE, FADE } +#@export var animation: Anim = Anim.NONE +@export var support_anim := true +@export var auto_anim := true + +var data + +signal submenu_close() + +const transition_scene = preload("res://gui/menus/transition/scene_transition.tscn") +var transition: SceneTransition +var parent_menu: Menu = null +var previous_path = null # : String +var open_since = 0 + +func _ready(): + open_since = Time.get_ticks_msec() + Global.focused_menu = self + focus_first(self) + connect_button_sounds(self) + disable_context_menus(self) + update_parent_menu(self.get_parent()) + if support_anim: anim_setup() + if auto_anim: _menu_open() + get_tree().get_root().go_back_requested.connect(exit_maybe) + +func anim_setup(): + transition = transition_scene.instantiate() + add_child(transition) +func _menu_open(): + if transition != null: await transition.fade_in() +func _menu_exit(): + if transition != null: await transition.fade_out() +func _menu_cover(_state: bool): + pass + +var popup: Menu = null +var covered := false +func submenu(path: String, data_ = null): + var prev_focus = Global.focused_node + if popup != null: return + _disable_recursive(self, true) + covered = true + await _menu_cover(true) + popup = load(path).instantiate() + popup.data = data_ + add_child(popup) + print("Submenu opened ", path) + await submenu_close + print("Submenu closed ", path) + await _menu_cover(false) + covered = false + Global.focused_menu = self + _disable_recursive(self, false) + if prev_focus != null: prev_focus.grab_focus() + +func _disable_recursive(node: Node, state: bool): + if node is BaseButton: + if state and node.disabled: node.add_to_group("was_disabled") + else: node.remove_from_group("was_disabled") + node.disabled = state or node.is_in_group("was_disabled") + for c in node.get_children(): _disable_recursive(c, state) + +func exit(): + await self._menu_exit() + if previous_path != null: + replace_menu(previous_path) + else: + get_parent().submenu_close.emit() + queue_free() + +func quit(): + await exit() + get_parent().quit() + +func replace_menu(path: String, data_ = null, prev_path = null): # prev_path: String? + print("Replace menu: ", path) + if popup != null: await popup.exit() + _disable_recursive(self, true) + await _menu_exit() + var new_popup: Menu = load(path).instantiate() + new_popup.data = data_ + if prev_path != null: new_popup.previous_path = prev_path + get_parent().add_child(new_popup) + if parent_menu != null: parent_menu.popup = new_popup + queue_free() + +var focus_auto_changed := false +func focus_first(node: Node) -> bool: + focus_auto_changed = true + if node.is_in_group("no_auto_focus"): + return false + if node is Button or node.is_in_group("autoselect"): + node.grab_focus() + print("Node %s (%s) was selected for focus" % [node.name, node]) + return true + for c in node.get_children(): + if focus_first(c): + return true + return false + +func connect_button_sounds(node: Node): + if node is Button or node is TextureButton: + if not node.is_in_group("no_click_sound"): + node.pressed.connect(Sound.play_click) + if (node is Button and not node.disabled) or (node is LineEdit and node.editable) or node is Slider: + if not node.is_in_group("no_click_sound"): + node.mouse_entered.connect(Sound.play_hover) + for c in node.get_children(): + connect_button_sounds(c) + +func disable_context_menus(node: Node): + if node is LineEdit: + node.context_menu_enabled = false + for c in node.get_children(): + disable_context_menus(c) + +func update_parent_menu(node: Node): + if node is Menu: parent_menu = node + elif node.get_parent() != null: update_parent_menu(node.get_parent()) + +func _input(_event): + if Input.is_action_just_pressed("menu"): + exit_maybe() + +func exit_maybe() -> void: + # Exit menu if all conditions are met + if popup != null: return + var time := Time.get_ticks_msec() + if time - open_since < 100: return + Sound.play_click() + exit() diff --git a/client/gui/menus/menu.gd.uid b/client/gui/menus/menu.gd.uid new file mode 100644 index 00000000..5711a6e0 --- /dev/null +++ b/client/gui/menus/menu.gd.uid @@ -0,0 +1 @@ +uid://d2h2q16vykpl4 diff --git a/client/gui/menus/popup.gd b/client/gui/menus/popup.gd new file mode 100644 index 00000000..d4849e92 --- /dev/null +++ b/client/gui/menus/popup.gd @@ -0,0 +1,32 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu +class_name MenuPopup + +@onready var message := $MarginContainer/CenterContainer/Panel/MarginContainer/VBoxContainer/Message +@onready var button_container := $MarginContainer/CenterContainer/Panel/MarginContainer/VBoxContainer/HBoxContainer + +class Data: + var buttons: Array[Button] + var text: String + +func _ready(): + var setup: Data = self.data + for i in setup.buttons: + button_container.add_child(i) + i.pressed.connect(exit) + message.text = setup.text + super() diff --git a/client/gui/menus/popup.gd.uid b/client/gui/menus/popup.gd.uid new file mode 100644 index 00000000..4800ca4d --- /dev/null +++ b/client/gui/menus/popup.gd.uid @@ -0,0 +1 @@ +uid://bevyiytj5tawr diff --git a/client/gui/menus/popup.tscn b/client/gui/menus/popup.tscn new file mode 100644 index 00000000..13137ccb --- /dev/null +++ b/client/gui/menus/popup.tscn @@ -0,0 +1,52 @@ +[gd_scene load_steps=6 format=3 uid="uid://lwtym0pbc17g"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_m0d0r"] +[ext_resource type="Script" uid="uid://bevyiytj5tawr" path="res://gui/menus/popup.gd" id="2_1h10j"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="3_iouvy"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="3_j0ajn"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="4_e4iqk"] + +[node name="Popup" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_m0d0r") +script = ExtResource("2_1h10j") +support_anim = false + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("3_j0ajn") + +[node name="CenterContainer" type="CenterContainer" parent="MarginContainer"] +layout_mode = 2 + +[node name="Panel" type="PanelContainer" parent="MarginContainer/CenterContainer"] +material = ExtResource("3_iouvy") +layout_mode = 2 +script = ExtResource("4_e4iqk") + +[node name="MarginContainer" type="MarginContainer" parent="MarginContainer/CenterContainer/Panel"] +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/CenterContainer/Panel/MarginContainer"] +layout_mode = 2 +theme_override_constants/separation = 16 + +[node name="Message" type="Label" parent="MarginContainer/CenterContainer/Panel/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(400, 0) +layout_mode = 2 +horizontal_alignment = 1 +autowrap_mode = 3 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/CenterContainer/Panel/MarginContainer/VBoxContainer"] +layout_mode = 2 +alignment = 1 diff --git a/client/gui/menus/popup_large.gd b/client/gui/menus/popup_large.gd new file mode 100644 index 00000000..909ee4c0 --- /dev/null +++ b/client/gui/menus/popup_large.gd @@ -0,0 +1,25 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +@onready var label = $OuterMargin/Panel/InnerMargin/Vert/ScrollContainer/CreditsText + +func _ready(): + super() + label.text = data + +func _on_back_pressed(): + exit() diff --git a/client/gui/menus/popup_large.gd.uid b/client/gui/menus/popup_large.gd.uid new file mode 100644 index 00000000..aef2a852 --- /dev/null +++ b/client/gui/menus/popup_large.gd.uid @@ -0,0 +1 @@ +uid://c3eimx76ucpsp diff --git a/client/gui/menus/popup_large.tscn b/client/gui/menus/popup_large.tscn new file mode 100644 index 00000000..993dde9d --- /dev/null +++ b/client/gui/menus/popup_large.tscn @@ -0,0 +1,74 @@ +[gd_scene load_steps=7 format=3 uid="uid://7mqbxa054bjv"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_kabr3"] +[ext_resource type="Script" uid="uid://c3eimx76ucpsp" path="res://gui/menus/popup_large.gd" id="2_m0b5d"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="3_36vhf"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="4_8ybj3"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="5_63jf0"] +[ext_resource type="Script" uid="uid://bd7bylb2t2m0" path="res://gui/components/touch_scroll_container.gd" id="6_smk7v"] + +[node name="CreditsMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_kabr3") +script = ExtResource("2_m0b5d") +support_anim = false + +[node name="OuterMargin" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("3_36vhf") + +[node name="Panel" type="Panel" parent="OuterMargin"] +material = ExtResource("4_8ybj3") +layout_mode = 2 +script = ExtResource("5_63jf0") + +[node name="InnerMargin" type="MarginContainer" parent="OuterMargin/Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 50 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 50 + +[node name="Vert" type="VBoxContainer" parent="OuterMargin/Panel/InnerMargin"] +layout_mode = 2 + +[node name="ScrollContainer" type="ScrollContainer" parent="OuterMargin/Panel/InnerMargin/Vert"] +layout_mode = 2 +size_flags_vertical = 3 +script = ExtResource("6_smk7v") + +[node name="CreditsText" type="RichTextLabel" parent="OuterMargin/Panel/InnerMargin/Vert/ScrollContainer"] +auto_translate_mode = 2 +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 +theme_override_constants/table_h_separation = 25 +theme_override_font_sizes/bold_italics_font_size = 22 +theme_override_font_sizes/italics_font_size = 22 +theme_override_font_sizes/mono_font_size = 22 +theme_override_font_sizes/normal_font_size = 22 +theme_override_font_sizes/bold_font_size = 22 +bbcode_enabled = true +fit_content = true +scroll_active = false + +[node name="back" type="Button" parent="OuterMargin/Panel/InnerMargin/Vert"] +layout_mode = 2 +text = "c.menu.back" + +[connection signal="pressed" from="OuterMargin/Panel/InnerMargin/Vert/back" to="." method="_on_back_pressed"] diff --git a/client/gui/menus/rating/desaturate.gdshader b/client/gui/menus/rating/desaturate.gdshader new file mode 100644 index 00000000..e6861560 --- /dev/null +++ b/client/gui/menus/rating/desaturate.gdshader @@ -0,0 +1,7 @@ +shader_type canvas_item; + +uniform float t : hint_range(0.0, 1.0); + +void fragment() { + COLOR.rgb = mix(vec3(pow((COLOR.r+COLOR.g+COLOR.b)/3.,3.)),COLOR.rgb,t); +} diff --git a/client/gui/menus/rating/desaturate.gdshader.uid b/client/gui/menus/rating/desaturate.gdshader.uid new file mode 100644 index 00000000..621837a6 --- /dev/null +++ b/client/gui/menus/rating/desaturate.gdshader.uid @@ -0,0 +1 @@ +uid://cekkkqsvd7rvw diff --git a/client/gui/menus/rating/rating.gd b/client/gui/menus/rating/rating.gd new file mode 100644 index 00000000..023c1333 --- /dev/null +++ b/client/gui/menus/rating/rating.gd @@ -0,0 +1,65 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +const PARTICLE_AMOUNTS = [1, 6, 32, 128] + +@onready var game: Game = $"../Game" +@onready var title: Label = $MarginContainer/PanelContainer/VBoxContainer/Text/Title +@onready var subtitle: Label = $MarginContainer/PanelContainer/VBoxContainer/Text/Subtitle +@onready var stars = $MarginContainer/PanelContainer/VBoxContainer/Stars.get_children() +@onready var star_timer = $StarTimer +@onready var particles = $Control/Particles +@onready var close_button: Button = $MarginContainer/PanelContainer/VBoxContainer/HBoxContainer/Close + +func _ready(): + super() + show_rating(data[0], data[1]) + close_button.disabled = true # Disable for short time period to prevent accidental button press + +func _process(_delta): + particles.emission_rect_extents = get_viewport_rect().size * Vector2(0.5, 0.5) + +func show_rating(stars_: int, points: int): + match stars_: + 0: title.text = tr("c.score.poor") + 1: title.text = tr("c.score.acceptable") + 2: title.text = tr("c.score.good") + 3: title.text = tr("c.score.excellent") + + subtitle.text = tr("c.score.points_par").format([points]) + + for i in range(0, stars_): + var star: TextureRect = stars[i] + star_timer.start() + await star_timer.timeout + star.material.set_shader_parameter("t", 1) + star.get_node("Sound").play() + + particles.amount = PARTICLE_AMOUNTS[stars_] + + if stars_ > 1: + particles.emitting = true + +func _on_close_pressed(): + exit() + +func _on_button_timer_timeout() -> void: + close_button.disabled = false + +func _on_scoreboard_pressed() -> void: + exit() + game.mp.send_chat(game.my_player_id, "/scoreboard %s" % Global.last_map_name) diff --git a/client/gui/menus/rating/rating.gd.uid b/client/gui/menus/rating/rating.gd.uid new file mode 100644 index 00000000..fd729d8f --- /dev/null +++ b/client/gui/menus/rating/rating.gd.uid @@ -0,0 +1 @@ +uid://5tmklxkaa6e0 diff --git a/client/gui/menus/rating/rating.tscn b/client/gui/menus/rating/rating.tscn new file mode 100644 index 00000000..062dcca2 --- /dev/null +++ b/client/gui/menus/rating/rating.tscn @@ -0,0 +1,168 @@ +[gd_scene load_steps=12 format=3 uid="uid://buu3cdpigs8qq"] + +[ext_resource type="Texture2D" uid="uid://b10goh4dsa3b0" path="res://player/particles/satisfied/star.webp" id="1_7qv7r"] +[ext_resource type="Shader" uid="uid://cekkkqsvd7rvw" path="res://gui/menus/rating/desaturate.gdshader" id="1_pddsm"] +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_uwajf"] +[ext_resource type="Script" uid="uid://5tmklxkaa6e0" path="res://gui/menus/rating/rating.gd" id="2_cq0se"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="4_hdurb"] +[ext_resource type="AudioStream" uid="uid://camy77x26mmpv" path="res://gui/resources/sounds/success.ogg" id="5_tutpj"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_oi7xd"] +shader = ExtResource("1_pddsm") +shader_parameter/t = 0.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_ney6s"] +shader = ExtResource("1_pddsm") +shader_parameter/t = 0.0 + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_27tx1"] +shader = ExtResource("1_pddsm") +shader_parameter/t = 0.0 + +[sub_resource type="Curve" id="Curve_dqga7"] +_data = [Vector2(0, 0), 0.0, 0.0, 0, 0, Vector2(0.0954774, 1), 0.262418, 0.0, 0, 0] +point_count = 2 + +[sub_resource type="Gradient" id="Gradient_majwe"] +offsets = PackedFloat32Array(0, 0.0584795, 1) +colors = PackedColorArray(1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0) + +[node name="Rating" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_uwajf") +script = ExtResource("2_cq0se") +support_anim = false + +[node name="MarginContainer" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 128 +theme_override_constants/margin_top = 64 +theme_override_constants/margin_right = 128 +theme_override_constants/margin_bottom = 64 + +[node name="PanelContainer" type="PanelContainer" parent="MarginContainer"] +material = ExtResource("4_hdurb") +layout_mode = 2 + +[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/PanelContainer"] +layout_mode = 2 +theme_override_constants/separation = 64 +alignment = 1 + +[node name="Text" type="VBoxContainer" parent="MarginContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 + +[node name="Title" type="Label" parent="MarginContainer/PanelContainer/VBoxContainer/Text"] +layout_mode = 2 +theme_override_font_sizes/font_size = 48 +text = "Title here" +horizontal_alignment = 1 + +[node name="Subtitle" type="Label" parent="MarginContainer/PanelContainer/VBoxContainer/Text"] +layout_mode = 2 +theme_override_font_sizes/font_size = 24 +text = "Subtitle here" +horizontal_alignment = 1 + +[node name="Stars" type="HBoxContainer" parent="MarginContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Star1" type="TextureRect" parent="MarginContainer/PanelContainer/VBoxContainer/Stars"] +material = SubResource("ShaderMaterial_oi7xd") +custom_minimum_size = Vector2(128, 128) +layout_mode = 2 +texture = ExtResource("1_7qv7r") +expand_mode = 1 +stretch_mode = 5 + +[node name="Sound" type="AudioStreamPlayer" parent="MarginContainer/PanelContainer/VBoxContainer/Stars/Star1"] +stream = ExtResource("5_tutpj") +pitch_scale = 1.5 + +[node name="Star2" type="TextureRect" parent="MarginContainer/PanelContainer/VBoxContainer/Stars"] +material = SubResource("ShaderMaterial_ney6s") +custom_minimum_size = Vector2(128, 128) +layout_mode = 2 +texture = ExtResource("1_7qv7r") +expand_mode = 1 +stretch_mode = 5 + +[node name="Sound" type="AudioStreamPlayer" parent="MarginContainer/PanelContainer/VBoxContainer/Stars/Star2"] +stream = ExtResource("5_tutpj") +pitch_scale = 1.65 + +[node name="Star3" type="TextureRect" parent="MarginContainer/PanelContainer/VBoxContainer/Stars"] +material = SubResource("ShaderMaterial_27tx1") +custom_minimum_size = Vector2(128, 128) +layout_mode = 2 +texture = ExtResource("1_7qv7r") +expand_mode = 1 +stretch_mode = 5 + +[node name="Sound" type="AudioStreamPlayer" parent="MarginContainer/PanelContainer/VBoxContainer/Stars/Star3"] +stream = ExtResource("5_tutpj") +pitch_scale = 1.9 + +[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer/PanelContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 64 +alignment = 1 + +[node name="Scoreboard" type="Button" parent="MarginContainer/PanelContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "c.menu.scoreboard.button" + +[node name="Close" type="Button" parent="MarginContainer/PanelContainer/VBoxContainer/HBoxContainer"] +layout_mode = 2 +text = "c.menu.accept" + +[node name="StarTimer" type="Timer" parent="."] +wait_time = 0.5 +one_shot = true + +[node name="ButtonTimer" type="Timer" parent="."] +one_shot = true +autostart = true + +[node name="Control" type="Control" parent="."] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Particles" type="CPUParticles2D" parent="Control"] +emitting = false +amount = 32 +texture = ExtResource("1_7qv7r") +emission_shape = 3 +emission_rect_extents = Vector2(512, 256) +direction = Vector2(0, -1) +initial_velocity_min = 256.0 +initial_velocity_max = 256.0 +angular_velocity_min = -30.0 +angular_velocity_max = 30.0 +angle_min = -20.0 +angle_max = 20.0 +scale_amount_min = 0.1 +scale_amount_max = 0.2 +scale_amount_curve = SubResource("Curve_dqga7") +color_ramp = SubResource("Gradient_majwe") + +[connection signal="pressed" from="MarginContainer/PanelContainer/VBoxContainer/HBoxContainer/Scoreboard" to="." method="_on_scoreboard_pressed"] +[connection signal="pressed" from="MarginContainer/PanelContainer/VBoxContainer/HBoxContainer/Close" to="." method="_on_close_pressed"] +[connection signal="timeout" from="ButtonTimer" to="." method="_on_button_timer_timeout"] diff --git a/client/gui/menus/settings/button_setting.gd b/client/gui/menus/settings/button_setting.gd new file mode 100644 index 00000000..fff8c184 --- /dev/null +++ b/client/gui/menus/settings/button_setting.gd @@ -0,0 +1,30 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 ButtonSetting +extends GameSetting + +var callback + +func _init(new_id: String, new_default, callback_): + callback = callback_ + super (new_id, new_default) + +func create_row(): + var row = super () + row.value_node = Button.new() + row.value_node.text = tr(nskey + ".button_label") + row.value_node.pressed.connect(callback) + return row diff --git a/client/gui/menus/settings/button_setting.gd.uid b/client/gui/menus/settings/button_setting.gd.uid new file mode 100644 index 00000000..cf0a8d95 --- /dev/null +++ b/client/gui/menus/settings/button_setting.gd.uid @@ -0,0 +1 @@ +uid://dku75bw31ux1k diff --git a/client/gui/menus/settings/dropdown_setting.gd b/client/gui/menus/settings/dropdown_setting.gd new file mode 100644 index 00000000..514df666 --- /dev/null +++ b/client/gui/menus/settings/dropdown_setting.gd @@ -0,0 +1,36 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 DropdownSetting +extends GameSetting + +var options: Array + +func _init(new_id: String, new_default, new_options: Array): + super(new_id, new_default) + options = new_options + +func create_row(): + var row = super() + row.value_node = OptionButton.new() + row.value_node.clip_text = true + for i in options: row.value_node.add_item(tr(nskey + "." + i)) + Settings.hook_changed_init(key, true, + func(value): + if is_instance_valid(row): + row.value_node.select(options.find(value)) + ) + row.value_node.item_selected.connect(func(item): Global.set_setting(key, options[item])) + return row diff --git a/client/gui/menus/settings/dropdown_setting.gd.uid b/client/gui/menus/settings/dropdown_setting.gd.uid new file mode 100644 index 00000000..409bf3ab --- /dev/null +++ b/client/gui/menus/settings/dropdown_setting.gd.uid @@ -0,0 +1 @@ +uid://cjqswo4mwbvon diff --git a/client/gui/menus/settings/game_setting.gd b/client/gui/menus/settings/game_setting.gd new file mode 100644 index 00000000..1c04ad3b --- /dev/null +++ b/client/gui/menus/settings/game_setting.gd @@ -0,0 +1,46 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 GameSetting +extends Object + +var default +var key: String +var nskey: String + +func _init(new_id: String, new_default = null): + default = new_default + key = new_id + +func set_parent(parent: GameSetting): + if parent != null: key = parent.key + "." + key + nskey = "c.settings." + key + +func create_row(): + var row = preload("res://gui/menus/settings/settings_row.tscn").instantiate() + row.description = tr(nskey) + row.reset.connect(func(): Global.set_setting(key, default)) + return row + +func check(): + if default != null: + if not key in Global.settings: + Global.set_setting_unchecked(key, default) + if typeof(default) != typeof(Global.settings[key]): + Global.set_setting_unchecked(key, default) + +func changed_keys(): + if Global.get_setting(key) != default: return [key] + else: return [] diff --git a/client/gui/menus/settings/game_setting.gd.uid b/client/gui/menus/settings/game_setting.gd.uid new file mode 100644 index 00000000..99d79bee --- /dev/null +++ b/client/gui/menus/settings/game_setting.gd.uid @@ -0,0 +1 @@ +uid://cyy8l32i44l63 diff --git a/client/gui/menus/settings/input/input_manager.gd b/client/gui/menus/settings/input/input_manager.gd new file mode 100644 index 00000000..e3158a03 --- /dev/null +++ b/client/gui/menus/settings/input/input_manager.gd @@ -0,0 +1,101 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Node + +enum EventType { + KEYBOARD, + JOYPAD, + TOUCH, + OTHER +} + +var default_input_map = {} +var input_map: Dictionary + +func _init(): + default_input_map = get_input_map() + input_map = default_input_map.duplicate(true) + +func get_input_map() -> Dictionary: + var actions = InputMap.get_actions().filter(func isBuiltIn(k: String): return !k.begins_with("ui_")) + var kb = {} + for a in actions: + var input_events: Array[InputEvent] = InputMap.action_get_events(a).duplicate(true) + kb[a] = input_events + return kb + +func get_events(action_name: String) -> Array: + if not input_map.has(action_name): + push_error("Tried to get action %s in input map which does not exist" % action_name) + return [] + return input_map[action_name] + +func settings() -> Array: + var entries := [] + for k in input_map.keys(): entries.append(InputSetting.new(k)) + return entries + +func change_input_map_action(action_name: String, events: Array, save: bool = true): + if !InputMap.has_action(action_name): + push_error("Action %s does not exist" % action_name, false) + return + # Erase previous keybindings + InputMap.action_erase_events(action_name) + # Add new keybindings + for e in events: + InputMap.action_add_event(action_name, e) + + if save: + # Update input map dictionary + input_map = get_input_map() + # Save settings + Global.set_setting("input_map", input_map.duplicate(true)) + +func apply_input_map(new_input_map: Dictionary): + # Load into input map dictionary + for k in new_input_map.keys(): + input_map[k] = [] + for a in new_input_map[k]: + input_map[k].append(a) + + # Apply keybindings + for k in input_map.keys(): + change_input_map_action(k, input_map[k], false) + +func reset_input_map(): + Global.set_setting("input_map", default_input_map.duplicate()) + apply_input_map(Global.get_setting("input_map")) + +func get_event_type(input_event: InputEvent) -> EventType: + if input_event is InputEventKey or input_event is InputEventMouseButton: + return EventType.KEYBOARD + elif input_event is InputEventJoypadButton or input_event is InputEventJoypadMotion: + return EventType.JOYPAD + elif input_event is InputEventScreenTouch or input_event is InputEventScreenDrag: + return EventType.TOUCH + return EventType.OTHER + +func display_input_event(input_event: InputEvent) -> String: + if input_event is InputEventKey: + return tr("c.settings.input.keyboard").format([OS.get_keycode_string(input_event.physical_keycode)]) + elif input_event is InputEventMouseButton: + return tr("c.settings.input.mouse_button").format([input_event.button_index]) + elif input_event is InputEventJoypadButton: + return tr("c.settings.input.joypad").format([input_event.button_index]) + elif input_event is InputEventJoypadMotion: + return tr("c.settings.input.joypad_axis").format([input_event.axis]) + else: + return tr("c.settings.input.other_event") diff --git a/client/gui/menus/settings/input/input_manager.gd.uid b/client/gui/menus/settings/input/input_manager.gd.uid new file mode 100644 index 00000000..678c2192 --- /dev/null +++ b/client/gui/menus/settings/input/input_manager.gd.uid @@ -0,0 +1 @@ +uid://bfu78iwybbu2s diff --git a/client/gui/menus/settings/input/input_setting.gd b/client/gui/menus/settings/input/input_setting.gd new file mode 100644 index 00000000..fa903771 --- /dev/null +++ b/client/gui/menus/settings/input/input_setting.gd @@ -0,0 +1,39 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 InputSetting +extends GameSetting + +const INPUT_VALUE_NODE_SCENE = preload("res://gui/menus/settings/input/input_value_node.tscn") + +func _init(new_id: String): + super(new_id) + default = InputManager.default_input_map[new_id] + +func create_row(): + var row = super() + row.value_node = INPUT_VALUE_NODE_SCENE.instantiate() + Settings.hook_changed_init(key, true, + func(value): + if is_instance_valid(row): + row.value_node.value = value + ) + row.value_node.changed.connect(func(): Global.set_setting(key, row.value_node.value)) + return row + +func changed_keys(): + return [key] + # if Global.array_eq(Global.get_setting(key), default): return [key] + # else: return [] diff --git a/client/gui/menus/settings/input/input_setting.gd.uid b/client/gui/menus/settings/input/input_setting.gd.uid new file mode 100644 index 00000000..7866fc2f --- /dev/null +++ b/client/gui/menus/settings/input/input_setting.gd.uid @@ -0,0 +1 @@ +uid://d2xwn2u4ycpe8 diff --git a/client/gui/menus/settings/input/input_value_node.gd b/client/gui/menus/settings/input/input_value_node.gd new file mode 100644 index 00000000..7c718e25 --- /dev/null +++ b/client/gui/menus/settings/input/input_value_node.gd @@ -0,0 +1,74 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 VBoxContainer +class_name InputValueNode + +var value: Array[InputEvent] = [] +var listening := false + +signal changed() + +@onready var actions_container: VBoxContainer = $ActionsContainer +@onready var add_button: Button = $Add +@onready var add_text = add_button.text + +func _ready(): + update() + +func update(fix_focus: bool = false): + for c in actions_container.get_children(): + c.queue_free() + + for e: InputEvent in value: + var description: String = InputManager.display_input_event(e) + var button := Button.new() + + button.text = description + button.pressed.connect(erase_event.bind(e)) + actions_container.add_child(button) + + if fix_focus: + add_button.grab_focus() + +func erase_event(e: InputEvent): + value.erase(e) + update(true) + changed.emit() + +func _input(e: InputEvent): + if listening: + if e is InputEventKey or e is InputEventMouseButton or e is InputEventJoypadButton or e is InputEventJoypadMotion: + # Check if key was already added + for e2 in value: + if events_equal(e, e2): return + + value.append(e) + _on_add_pressed() + update() + changed.emit() + +func events_equal(e1: InputEvent, e2: InputEvent) -> bool: + if e1 is InputEventKey and e2 is InputEventKey: + return e1.physical_keycode == e2.physical_keycode + if (e1 is InputEventMouseButton and e2 is InputEventMouseButton) or (e1 is InputEventJoypadButton and e2 is InputEventJoypadButton): + return e1.button_index == e2.button_index + if e1 is InputEventJoypadMotion and e2 is InputEventJoypadMotion: + return e1.axis == e2.axis + return false + +func _on_add_pressed() -> void: + listening = not listening + add_button.text = tr("c.settings.input.press_any_key") if listening else add_text diff --git a/client/gui/menus/settings/input/input_value_node.gd.uid b/client/gui/menus/settings/input/input_value_node.gd.uid new file mode 100644 index 00000000..3669b991 --- /dev/null +++ b/client/gui/menus/settings/input/input_value_node.gd.uid @@ -0,0 +1 @@ +uid://ckb78voiq05e3 diff --git a/client/gui/menus/settings/input/input_value_node.tscn b/client/gui/menus/settings/input/input_value_node.tscn new file mode 100644 index 00000000..1b2e89c4 --- /dev/null +++ b/client/gui/menus/settings/input/input_value_node.tscn @@ -0,0 +1,24 @@ +[gd_scene load_steps=3 format=3 uid="uid://c6r0nv5daq7wc"] + +[ext_resource type="Script" uid="uid://ckb78voiq05e3" path="res://gui/menus/settings/input/input_value_node.gd" id="1_snxax"] +[ext_resource type="Texture2D" uid="uid://cnfjbowd2i02r" path="res://gui/resources/icons/plus.svg" id="2_3vlvc"] + +[node name="InputValueNode" type="VBoxContainer"] +offset_right = 128.0 +offset_bottom = 31.0 +theme_override_constants/separation = 0 +script = ExtResource("1_snxax") + +[node name="ActionsContainer" type="VBoxContainer" parent="."] +layout_mode = 2 +theme_override_constants/separation = 0 + +[node name="Add" type="Button" parent="."] +custom_minimum_size = Vector2(128, 0) +layout_mode = 2 +size_flags_vertical = 3 +text = "c.settings.input.add" +icon = ExtResource("2_3vlvc") +expand_icon = true + +[connection signal="pressed" from="Add" to="." method="_on_add_pressed"] diff --git a/client/gui/menus/settings/number_setting.gd b/client/gui/menus/settings/number_setting.gd new file mode 100644 index 00000000..5fa5a115 --- /dev/null +++ b/client/gui/menus/settings/number_setting.gd @@ -0,0 +1,41 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 NumberSetting +extends GameSetting + +var placeholder: String +var min_value: int +var max_value: int + +func _init(new_id: String, new_default: int, new_min_value: int, new_max_value: int): + super(new_id, new_default) + min_value = new_min_value + max_value = new_max_value + +func create_row(): + var row = super() + var input := SpinBox.new() + input.min_value = min_value + input.max_value = max_value + + input.value_changed.connect(func(value): Global.set_setting(key, value as int)) + Settings.hook_changed_init(key, true, + func(v): + if is_instance_valid(input): + input.value = v + ) + row.value_node = input + return row diff --git a/client/gui/menus/settings/number_setting.gd.uid b/client/gui/menus/settings/number_setting.gd.uid new file mode 100644 index 00000000..4301c642 --- /dev/null +++ b/client/gui/menus/settings/number_setting.gd.uid @@ -0,0 +1 @@ +uid://babmw2ohuhmuk diff --git a/client/gui/menus/settings/path_setting.gd b/client/gui/menus/settings/path_setting.gd new file mode 100644 index 00000000..37492ed7 --- /dev/null +++ b/client/gui/menus/settings/path_setting.gd @@ -0,0 +1,64 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 PathSetting +extends TextSetting + +var select_file_icon: Texture2D = preload("res://gui/resources/icons/select_file.svg") +var select_dir_icon: Texture2D = preload("res://gui/resources/icons/select_directory.svg") + +var access: FileDialog.Access +var file_mode: FileDialog.FileMode + +func _init(new_id: String, + new_default: String, + new_file_mode: FileDialog.FileMode, + new_placeholder: String = "", + new_access: FileDialog.Access = FileDialog.Access.ACCESS_FILESYSTEM +): + super(new_id, new_default) + placeholder = new_placeholder + access = new_access + file_mode = new_file_mode + +func create_row(): + var row = super () + var input: LineEdit = row.value_node; + input.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.value_node = HBoxContainer.new() + row.value_node.add_child(input) + var button := Button.new() + button.icon = select_file_icon if file_mode == FileDialog.FileMode.FILE_MODE_OPEN_FILE else select_dir_icon + row.value_node.add_child(button) + button.pressed.connect(func(): + var d := FileDialog.new() + Global.focused_menu.add_child(d) + d.move_to_center() + d.use_native_dialog = true + d.borderless = true + d.dir_selected.connect(_selected.bind(input)) + d.file_selected.connect(_selected.bind(input)) + d.file_mode = file_mode + d.access = access + d.show() + # this feels wrong + d.canceled.connect(d.queue_free) + d.confirmed.connect(d.queue_free) + ) + return row + +func _selected(path: String, input: LineEdit): + input.text = path + input.text_changed.emit(path) diff --git a/client/gui/menus/settings/path_setting.gd.uid b/client/gui/menus/settings/path_setting.gd.uid new file mode 100644 index 00000000..a524b17c --- /dev/null +++ b/client/gui/menus/settings/path_setting.gd.uid @@ -0,0 +1 @@ +uid://cj5jf7t771j5b diff --git a/client/gui/menus/settings/preset_row.gd b/client/gui/menus/settings/preset_row.gd new file mode 100644 index 00000000..f3c46a26 --- /dev/null +++ b/client/gui/menus/settings/preset_row.gd @@ -0,0 +1,46 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 PresetRow +extends GameSetting + +var options: Dictionary +var arr: Array[Button] + +func _init(id: String, options_: Dictionary): + super(id) + options = options_ + +var prefix = "" +func set_parent(parent): + super(parent) + if parent != null: prefix = parent.key + +func apply(preset_name: String): + var preset = options[preset_name] + for i in preset.keys(): + Global.set_setting(prefix + "." + i, preset[i]) + +func create_row(): + var row = super() + row.value_node = HBoxContainer.new() + for i in options.keys(): + var button := Button.new() + button.pressed.connect(apply.bind(i)) + button.text = tr(nskey + "." + i) + row.value_node.add_child(button) + return row + +func changed_keys(): return [] diff --git a/client/gui/menus/settings/preset_row.gd.uid b/client/gui/menus/settings/preset_row.gd.uid new file mode 100644 index 00000000..51605058 --- /dev/null +++ b/client/gui/menus/settings/preset_row.gd.uid @@ -0,0 +1 @@ +uid://dawqyyllgis0b diff --git a/client/gui/menus/settings/range_setting.gd b/client/gui/menus/settings/range_setting.gd new file mode 100644 index 00000000..b8d392a4 --- /dev/null +++ b/client/gui/menus/settings/range_setting.gd @@ -0,0 +1,44 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 RangeSetting +extends GameSetting + +var min_value: float +var max_value: float +var tick_count +var smooth: bool + +func _init(new_id: String, new_default: float, new_min_value: float, new_max_value: float, new_smooth: bool = true, new_tick_count = null): + super(new_id, new_default) + min_value = new_min_value + max_value = new_max_value + tick_count = new_tick_count + smooth = new_smooth + +func create_row(): + var row = super() + row.value_node = HSlider.new() + row.value_node.min_value = min_value + row.value_node.max_value = max_value + row.value_node.tick_count = abs(max_value - min_value) if tick_count == null else tick_count + row.value_node.step = 0 if smooth else (1 if tick_count == null else abs(max_value - min_value) / (tick_count - 1)) + Settings.hook_changed_init(key, true, + func(value): + if is_instance_valid(row): + row.value_node.value = value + ) + row.value_node.value_changed.connect(func(value): Global.set_setting(key, value)) + return row diff --git a/client/gui/menus/settings/range_setting.gd.uid b/client/gui/menus/settings/range_setting.gd.uid new file mode 100644 index 00000000..a4ca49a2 --- /dev/null +++ b/client/gui/menus/settings/range_setting.gd.uid @@ -0,0 +1 @@ +uid://civr7cckqfndj diff --git a/client/gui/menus/settings/settings.gd b/client/gui/menus/settings/settings.gd new file mode 100644 index 00000000..32da54cc --- /dev/null +++ b/client/gui/menus/settings/settings.gd @@ -0,0 +1,40 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +@onready var container = $OuterGap/Panel/InnerGap/VBoxContainer +@onready var outer_gap = $OuterGap + +func _ready(): + super() + var row = Global.settings_tree.create_row() + container.add_child(row) + container.move_child(row, 1) + +func _process(_dt): + var os := OS.get_name() + if os == "iOS" or os == "Android": return + # TODO probably bad performance, only update on change + var margin = max((self.size.x - 1200) / 2, 20) + outer_gap.add_theme_constant_override("margin_left", margin) + outer_gap.add_theme_constant_override("margin_right", margin) + +func _on_back_pressed(): + exit() + +func exit(): + Global.save_settings() + super() diff --git a/client/gui/menus/settings/settings.gd.uid b/client/gui/menus/settings/settings.gd.uid new file mode 100644 index 00000000..79b89b85 --- /dev/null +++ b/client/gui/menus/settings/settings.gd.uid @@ -0,0 +1 @@ +uid://bbqmsf8u5rhtn diff --git a/client/gui/menus/settings/settings.tscn b/client/gui/menus/settings/settings.tscn new file mode 100644 index 00000000..71549464 --- /dev/null +++ b/client/gui/menus/settings/settings.tscn @@ -0,0 +1,61 @@ +[gd_scene load_steps=6 format=3 uid="uid://8ic77jmadadj"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_1vjiw"] +[ext_resource type="Script" uid="uid://bbqmsf8u5rhtn" path="res://gui/menus/settings/settings.gd" id="2_5xn7x"] +[ext_resource type="Script" uid="uid://byshs20og68tn" path="res://gui/components/smart_margin_container.gd" id="3_h533i"] +[ext_resource type="Material" uid="uid://beea1pc5nt67r" path="res://gui/resources/materials/dark_blur_material.tres" id="4_b0x33"] +[ext_resource type="Script" uid="uid://cmncjc06kadpe" path="res://gui/components/blur_setup.gd" id="5_dvivs"] + +[node name="SettingsMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme = ExtResource("1_1vjiw") +script = ExtResource("2_5xn7x") +support_anim = false + +[node name="OuterGap" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 80 +script = ExtResource("3_h533i") + +[node name="Panel" type="Panel" parent="OuterGap"] +material = ExtResource("4_b0x33") +layout_mode = 2 +script = ExtResource("5_dvivs") + +[node name="InnerGap" type="MarginContainer" parent="OuterGap/Panel"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_constants/margin_left = 40 +theme_override_constants/margin_top = 40 +theme_override_constants/margin_right = 40 +theme_override_constants/margin_bottom = 40 + +[node name="VBoxContainer" type="VBoxContainer" parent="OuterGap/Panel/InnerGap"] +layout_mode = 2 + +[node name="Title" type="Label" parent="OuterGap/Panel/InnerGap/VBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 0 +theme_override_font_sizes/font_size = 36 +text = "c.menu.settings" + +[node name="Back" type="Button" parent="OuterGap/Panel/InnerGap/VBoxContainer"] +layout_mode = 2 +size_flags_vertical = 8 +text = "c.settings.apply" + +[connection signal="pressed" from="OuterGap/Panel/InnerGap/VBoxContainer/Back" to="." method="_on_back_pressed"] diff --git a/client/gui/menus/settings/settings_category.gd b/client/gui/menus/settings/settings_category.gd new file mode 100644 index 00000000..bf85abd9 --- /dev/null +++ b/client/gui/menus/settings/settings_category.gd @@ -0,0 +1,49 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 SettingsCategory +extends GameSetting + +var settings: Array # Dictionary[String, GameSetting] + +func _init(new_id: String, new_settings: Array): + super(new_id) + settings = new_settings + +func set_parent(parent: GameSetting): + super(parent) + for c in settings: + c.set_parent(self) + +func create_row(): + var row = ScrollContainerCustom.new() + var options = VBoxContainer.new() + row.name = tr(nskey) + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + options.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.add_child(options) + + for r in settings: options.add_child(r.create_row()) + return row + +func check(): + for c in settings: + c.check() + +func changed_keys(): + var changed = [] + for c in settings: + changed.append_array(c.changed_keys()) + return changed diff --git a/client/gui/menus/settings/settings_category.gd.uid b/client/gui/menus/settings/settings_category.gd.uid new file mode 100644 index 00000000..421ce213 --- /dev/null +++ b/client/gui/menus/settings/settings_category.gd.uid @@ -0,0 +1 @@ +uid://b8s3cqb01w3wh diff --git a/client/gui/menus/settings/settings_root.gd b/client/gui/menus/settings/settings_root.gd new file mode 100644 index 00000000..a9a024d8 --- /dev/null +++ b/client/gui/menus/settings/settings_root.gd @@ -0,0 +1,40 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 GameSetting +class_name SettingsRoot + +var children: Array +func _init(new_children: Array): + super("root") + children = new_children + for c in children: + c.set_parent(null) + +func create_row(): + var row = TabContainer.new() + row.size_flags_vertical = Control.SIZE_EXPAND_FILL + for r in children: row.add_child(r.create_row()) + return row + +func check(): + for c in children: + c.check() + +func changed_keys(): + var changed = [] + for c in children: + changed.append_array(c.changed_keys()) + return changed diff --git a/client/gui/menus/settings/settings_root.gd.uid b/client/gui/menus/settings/settings_root.gd.uid new file mode 100644 index 00000000..95a46d5e --- /dev/null +++ b/client/gui/menus/settings/settings_root.gd.uid @@ -0,0 +1 @@ +uid://jonib2ixqsp7 diff --git a/client/gui/menus/settings/settings_row.gd b/client/gui/menus/settings/settings_row.gd new file mode 100644 index 00000000..d88d49c1 --- /dev/null +++ b/client/gui/menus/settings/settings_row.gd @@ -0,0 +1,37 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 SettingsRow +extends PanelContainer + +signal reset() + +@onready var value_parent = $HBoxContainer/BoxContainer +@onready var label = $HBoxContainer/Label +@onready var reset_button = $HBoxContainer/Reset + +var value_node: Node +var description = "No value was given to the row" + +func _ready(): + if value_node != null: + var c: Control = value_node + c.size_flags_vertical = Control.SIZE_EXPAND_FILL + c.size_flags_horizontal = Control.SIZE_EXPAND_FILL + label.text = description + value_parent.add_child(c) + +func _on_reset_pressed(): + reset.emit() diff --git a/client/gui/menus/settings/settings_row.gd.uid b/client/gui/menus/settings/settings_row.gd.uid new file mode 100644 index 00000000..a6dea492 --- /dev/null +++ b/client/gui/menus/settings/settings_row.gd.uid @@ -0,0 +1 @@ +uid://b3m1f76o5qo68 diff --git a/client/gui/menus/settings/settings_row.tscn b/client/gui/menus/settings/settings_row.tscn new file mode 100644 index 00000000..09378ab6 --- /dev/null +++ b/client/gui/menus/settings/settings_row.tscn @@ -0,0 +1,40 @@ +[gd_scene load_steps=7 format=3 uid="uid://o5e5vpem8w0k"] + +[ext_resource type="Theme" uid="uid://b0qmvo504e457" path="res://gui/resources/theme/theme.tres" id="1_iij3k"] +[ext_resource type="Script" uid="uid://b3m1f76o5qo68" path="res://gui/menus/settings/settings_row.gd" id="2_l8i7p"] +[ext_resource type="FontFile" uid="uid://5ixo6b3bd3km" path="res://gui/resources/fonts/font-josefin-sans.woff2" id="3_7k5da"] +[ext_resource type="Texture2D" uid="uid://cucnmy0j5n8l8" path="res://gui/resources/icons/reset.svg" id="4_bj3dr"] + +[sub_resource type="StyleBoxEmpty" id="StyleBoxEmpty_pk3rs"] +content_margin_left = 16.0 + +[sub_resource type="FontVariation" id="FontVariation_o6i7s"] +base_font = ExtResource("3_7k5da") + +[node name="SettingsRow" type="PanelContainer"] +offset_right = 105.0 +offset_bottom = 23.0 +size_flags_horizontal = 3 +theme = ExtResource("1_iij3k") +script = ExtResource("2_l8i7p") + +[node name="HBoxContainer" type="HBoxContainer" parent="."] +layout_mode = 2 + +[node name="Label" type="Label" parent="HBoxContainer"] +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_styles/normal = SubResource("StyleBoxEmpty_pk3rs") + +[node name="BoxContainer" type="BoxContainer" parent="HBoxContainer"] +custom_minimum_size = Vector2(300, 50) +layout_mode = 2 +alignment = 2 + +[node name="Reset" type="Button" parent="HBoxContainer"] +layout_mode = 2 +theme_override_fonts/font = SubResource("FontVariation_o6i7s") +theme_override_font_sizes/font_size = 24 +icon = ExtResource("4_bj3dr") + +[connection signal="pressed" from="HBoxContainer/Reset" to="." method="_on_reset_pressed"] diff --git a/client/gui/menus/settings/text_setting.gd b/client/gui/menus/settings/text_setting.gd new file mode 100644 index 00000000..8e2b6bec --- /dev/null +++ b/client/gui/menus/settings/text_setting.gd @@ -0,0 +1,38 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 TextSetting +extends GameSetting + +var placeholder: String + +func _init(new_id: String, new_default: String, new_placeholder: String = ""): + super(new_id, new_default) + placeholder = new_placeholder + +func create_row(): + var row = super() + var input := LineEdit.new() + input.placeholder_text = placeholder + input.text_changed.connect(func(text): Global.set_setting(key, text)) + Settings.hook_changed_init(key, true, + func(text): + if is_instance_valid(input): + var pos = input.caret_column + input.text = text + input.caret_column = pos + ) + row.value_node = input + return row diff --git a/client/gui/menus/settings/text_setting.gd.uid b/client/gui/menus/settings/text_setting.gd.uid new file mode 100644 index 00000000..58ac5abe --- /dev/null +++ b/client/gui/menus/settings/text_setting.gd.uid @@ -0,0 +1 @@ +uid://3rgucgbbt135 diff --git a/client/gui/menus/settings/toggle_setting.gd b/client/gui/menus/settings/toggle_setting.gd new file mode 100644 index 00000000..abcb7f4a --- /dev/null +++ b/client/gui/menus/settings/toggle_setting.gd @@ -0,0 +1,31 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 ToggleSetting +extends GameSetting + +func _init(new_id: String, new_default: bool): + super(new_id, new_default) + +func create_row(): + var row = super() + row.value_node = CheckButton.new() + row.value_node.pressed.connect(func(): Global.set_setting(key, row.value_node.button_pressed)) + Settings.hook_changed_init(key, true, + func(value): + if is_instance_valid(row): + row.value_node.button_pressed = value + ) + return row diff --git a/client/gui/menus/settings/toggle_setting.gd.uid b/client/gui/menus/settings/toggle_setting.gd.uid new file mode 100644 index 00000000..1d2ca55b --- /dev/null +++ b/client/gui/menus/settings/toggle_setting.gd.uid @@ -0,0 +1 @@ +uid://cojnv8bmv6aw5 diff --git a/client/gui/menus/setup/hairstyle_preview.gd b/client/gui/menus/setup/hairstyle_preview.gd new file mode 100644 index 00000000..78576491 --- /dev/null +++ b/client/gui/menus/setup/hairstyle_preview.gd @@ -0,0 +1,27 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 HairstylePreview +extends VBoxContainer + +signal selected(character_style: Dictionary) + +func setup(hairstyle: int, group: ButtonGroup): + var character_style = Global.default_profile.character_style + character_style.hairstyle = hairstyle + $HairViewport/Node3D/Character.set_style(character_style, "chef") + $Select.button_group = group + $Select.text = tr("c.setup.uniform.value").format([hairstyle + 1]) + $Select.pressed.connect(func(): selected.emit(character_style)) diff --git a/client/gui/menus/setup/hairstyle_preview.gd.uid b/client/gui/menus/setup/hairstyle_preview.gd.uid new file mode 100644 index 00000000..8f5b3cd4 --- /dev/null +++ b/client/gui/menus/setup/hairstyle_preview.gd.uid @@ -0,0 +1 @@ +uid://dvveoqur81l0s diff --git a/client/gui/menus/setup/hairstyle_preview.tscn b/client/gui/menus/setup/hairstyle_preview.tscn new file mode 100644 index 00000000..ee4a65e9 --- /dev/null +++ b/client/gui/menus/setup/hairstyle_preview.tscn @@ -0,0 +1,55 @@ +[gd_scene load_steps=7 format=3 uid="uid://dfon56nwd2tgn"] + +[ext_resource type="Script" uid="uid://dvveoqur81l0s" path="res://gui/menus/setup/hairstyle_preview.gd" id="1_0qdmv"] +[ext_resource type="Shader" uid="uid://qjrh2imc53u1" path="res://gui/resources/shaders/grayscale.gdshader" id="1_sf0gc"] +[ext_resource type="PackedScene" uid="uid://b3hhir2fvnunu" path="res://player/character/character.tscn" id="2_jtitc"] + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_entrs"] +shader = ExtResource("1_sf0gc") + +[sub_resource type="ViewportTexture" id="ViewportTexture_giuq2"] +viewport_path = NodePath("HairViewport") + +[sub_resource type="ButtonGroup" id="ButtonGroup_c5p7t"] + +[node name="HairstylePreview" type="VBoxContainer"] +offset_right = 40.0 +offset_bottom = 40.0 +script = ExtResource("1_0qdmv") + +[node name="Preview" type="TextureRect" parent="."] +material = SubResource("ShaderMaterial_entrs") +layout_mode = 2 +texture = SubResource("ViewportTexture_giuq2") + +[node name="Select" type="CheckBox" parent="."] +layout_mode = 2 +button_group = SubResource("ButtonGroup_c5p7t") +text = "Hairstyle 1" + +[node name="HairViewport" type="SubViewport" parent="."] +own_world_3d = true +transparent_bg = true +msaa_3d = 1 +size = Vector2i(128, 128) + +[node name="Node3D" type="Node3D" parent="HairViewport"] + +[node name="Camera3D" type="Camera3D" parent="HairViewport/Node3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.75, 1.5) +fov = 25.5 + +[node name="Character" parent="HairViewport/Node3D" instance=ExtResource("2_jtitc")] + +[node name="SpotLight3D" type="SpotLight3D" parent="HairViewport/Node3D"] +transform = Transform3D(0.866025, 0, -0.5, 0, 1, 0, 0.5, 0, 0.866025, -2, 0, 2) + +[node name="SpotLight3D2" type="SpotLight3D" parent="HairViewport/Node3D"] +transform = Transform3D(0.876399, 0, 0.481585, 0, 1, 0, -0.481585, 0, 0.876399, 2, 0.499189, 2) +light_color = Color(0.857819, 0.80038, 0.775519, 1) +light_energy = 4.11 + +[node name="SpotLight3D3" type="SpotLight3D" parent="HairViewport/Node3D"] +transform = Transform3D(-0.965926, 0, -0.258819, -0.129409, 0.866025, 0.482963, 0.224144, 0.5, -0.836516, -1, 2, -2) +light_color = Color(0.540595, 0.865144, 1, 1) +light_energy = 8.2 diff --git a/client/gui/menus/setup/setup.gd b/client/gui/menus/setup/setup.gd new file mode 100644 index 00000000..6170786c --- /dev/null +++ b/client/gui/menus/setup/setup.gd @@ -0,0 +1,110 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 Menu + +const SCROLL_SPEED := 500. + +var character_style = null # : Dictionary? + +@onready var anim: AnimationPlayer = $AnimationPlayer +@onready var username: LineEdit = $ScrollContainer/Control/TextureRect/PaperMargin/Contents/NameEntry/LineEdit +@onready var sign_sound: AudioStreamPlayer = $Sign +@onready var sign_button: Button = $ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin/Sign/Signature +@onready var scroll: ScrollContainer = $ScrollContainer +@onready var select_uniform: HBoxContainer = $ScrollContainer/Control/TextureRect/PaperMargin/Contents/SelectUniform +@onready var skip_tutorial: CheckBox = $ScrollContainer/Control/TextureRect/PaperMargin/Contents/SelectExperience/CheckBox + +func _ready(): + anim.play("paper_slide") + var button_group := ButtonGroup.new() + for i in range(3): + var preview: HairstylePreview = preload("res://gui/menus/setup/hairstyle_preview.tscn").instantiate() + select_uniform.add_child(preview) + preview.setup(i, button_group) + preview.selected.connect(_character_selected) + if i < 2: + var spacer = Control.new() + spacer.size_flags_vertical = Control.SIZE_EXPAND + spacer.custom_minimum_size.x = 50 + select_uniform.add_child(spacer) + + # Enable msaa 2D for this scene + Global.get_viewport().msaa_2d = Viewport.MSAA_4X + + if Global.profile.username != "": username.text = Global.profile.username + + if TranslationServer.get_locale().begins_with("zh"): + $ScrollContainer/Control/TextureRect.rotation = 0 + increase_font_size(self) + + super() + $Back.visible = not is_instance_of(parent_menu, Entry) + +func increase_font_size(node: Node): + if node is RichTextLabel: + for oname in ["bold_italics_font_size", "italics_font_size", "normal_font_size", "mono_font_size", "bold_font_size"]: + node.add_theme_font_size_override(oname, node.get_theme_default_font_size() * 1.2) + for c in node.get_children(): increase_font_size(c) + +func _on_back_pressed() -> void: + exit() + +func _character_selected(style: Dictionary): + character_style = style + +func _process(delta): + var s = Input.get_axis("rotate_up", "rotate_down") + scroll.set_deferred("scroll_vertical", scroll.scroll_vertical + s * delta * SCROLL_SPEED) + + +func check(): + if username.text == "": return tr("c.error.empty_username") + if character_style == null: return tr("c.error.select_hairstyle") + return null + +func _on_sign_pressed(): + if check() != null: + var popup_data := MenuPopup.Data.new() + popup_data.text = check() + var accept_button := Button.new() + accept_button.text = tr("c.menu.accept") + popup_data.buttons = [accept_button] + await submenu("res://gui/menus/popup.tscn", popup_data) + return + + sign_button.disabled = true + + sign_sound.play() + await sign_sound.finished + anim.play_backwards("paper_slide") + await anim.animation_finished + + Global.set_profile("username", username.text) + Global.set_profile("character_style", character_style) + if skip_tutorial.button_pressed: + for k in Global.profile["hints"].keys(): + Global.set_hint(k, true) + Global.save_profile() + + Global.set_setting("gameplay.hints_started", skip_tutorial.button_pressed) + Global.set_setting("gameplay.tutorial_disabled", skip_tutorial.button_pressed) + Global.set_setting("gameplay.setup_completed", true) + Global.save_settings() + + Global.get_viewport().msaa_2d = Viewport.MSAA_DISABLED + + if not is_instance_of(parent_menu, Entry): exit() + else: replace_menu("res://gui/menu/main/main.tscn") diff --git a/client/gui/menus/setup/setup.gd.uid b/client/gui/menus/setup/setup.gd.uid new file mode 100644 index 00000000..c9c2be3c --- /dev/null +++ b/client/gui/menus/setup/setup.gd.uid @@ -0,0 +1 @@ +uid://dxn6ow6hiwhbf diff --git a/client/gui/menus/setup/setup.tscn b/client/gui/menus/setup/setup.tscn new file mode 100644 index 00000000..d0cce350 --- /dev/null +++ b/client/gui/menus/setup/setup.tscn @@ -0,0 +1,398 @@ +[gd_scene load_steps=15 format=3 uid="uid://ddl3efikvqp66"] + +[ext_resource type="Script" uid="uid://dxn6ow6hiwhbf" path="res://gui/menus/setup/setup.gd" id="1_mo46n"] +[ext_resource type="Theme" uid="uid://ci2qajdoa1an1" path="res://gui/resources/theme/paper.tres" id="1_yq0aa"] +[ext_resource type="Script" uid="uid://bd7bylb2t2m0" path="res://gui/components/touch_scroll_container.gd" id="2_4caf2"] +[ext_resource type="FontFile" uid="uid://bo4vh5xkpvrh1" path="res://gui/resources/fonts/font-sansita-swashed.woff2" id="3_2vg4d"] +[ext_resource type="AudioStream" uid="uid://do7ii5hx71p0m" path="res://gui/resources/sounds/page.ogg" id="5_xac6d"] +[ext_resource type="AudioStream" uid="uid://5b3noxjmasmu" path="res://gui/resources/sounds/sign.ogg" id="6_wf0gh"] + +[sub_resource type="Animation" id="Animation_m4a1a"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("ScrollContainer:position") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Vector2(0, 0)] +} + +[sub_resource type="Animation" id="Animation_s1to2"] +resource_name = "paper_slide" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("ScrollContainer:position") +tracks/0/interp = 2 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Vector2(0, -1800), Vector2(0, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_wjgak"] +_data = { +&"RESET": SubResource("Animation_m4a1a"), +&"paper_slide": SubResource("Animation_s1to2") +} + +[sub_resource type="Gradient" id="Gradient_nsc3h"] +colors = PackedColorArray(0.941084, 0.949219, 0.918643, 1, 1, 1, 1, 1) + +[sub_resource type="FastNoiseLite" id="FastNoiseLite_amioi"] + +[sub_resource type="NoiseTexture2D" id="NoiseTexture2D_bvvl7"] +color_ramp = SubResource("Gradient_nsc3h") +noise = SubResource("FastNoiseLite_amioi") + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_e7xn5"] +bg_color = Color(0.196078, 0.196078, 0.235294, 1) +corner_radius_top_left = 10 +corner_radius_top_right = 10 +corner_radius_bottom_right = 10 +corner_radius_bottom_left = 10 + +[sub_resource type="FontVariation" id="FontVariation_2cc7p"] +base_font = ExtResource("3_2vg4d") + +[node name="SetupMenu" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_mo46n") +support_anim = null +auto_anim = null + +[node name="ColorRect" type="ColorRect" parent="."] +layout_mode = 2 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0.196078, 0.196078, 0.235294, 1) + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_wjgak") +} +speed_scale = 2.0 + +[node name="ScrollContainer" type="ScrollContainer" parent="."] +clip_contents = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +follow_focus = true +horizontal_scroll_mode = 0 +script = ExtResource("2_4caf2") + +[node name="Control" type="Control" parent="ScrollContainer"] +custom_minimum_size = Vector2(0, 1500) +layout_mode = 2 +size_flags_horizontal = 3 +size_flags_vertical = 3 + +[node name="TextureRect" type="TextureRect" parent="ScrollContainer/Control"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -400.0 +offset_top = -559.57 +offset_right = 400.0 +offset_bottom = 571.801 +grow_horizontal = 2 +grow_vertical = 2 +rotation = 0.0174533 +theme = ExtResource("1_yq0aa") +texture = SubResource("NoiseTexture2D_bvvl7") + +[node name="Hole1" type="Panel" parent="ScrollContainer/Control/TextureRect"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -365.0 +offset_top = -189.686 +offset_right = -345.0 +offset_bottom = -169.686 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_e7xn5") + +[node name="Hole2" type="Panel" parent="ScrollContainer/Control/TextureRect"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -365.0 +offset_top = 130.314 +offset_right = -345.0 +offset_bottom = 150.314 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_e7xn5") + +[node name="PaperMargin" type="MarginContainer" parent="ScrollContainer/Control/TextureRect"] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="Contents" type="VBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin"] +layout_mode = 2 + +[node name="Title" type="Label" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +size_flags_horizontal = 4 +theme_override_font_sizes/font_size = 30 +text = "c.setup.contract_title" + +[node name="Sep" type="HSeparator" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 + +[node name="Intro" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.contract_desc" +fit_content = true +scroll_active = false + +[node name="Name" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.name" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="NameEntry" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +tooltip_text = "c.setup.name.desc" + +[node name="LineEdit" type="LineEdit" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/NameEntry"] +custom_minimum_size = Vector2(300, 30) +layout_mode = 2 +max_length = 32 + +[node name="Control" type="Control" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/NameEntry"] +layout_mode = 2 + +[node name="Position" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.position" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="PositionEntry" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 + +[node name="LineEdit" type="LineEdit" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/PositionEntry"] +custom_minimum_size = Vector2(300, 30) +layout_mode = 2 +theme_override_colors/font_uneditable_color = Color(0.458824, 0, 0, 1) +theme_override_colors/font_color = Color(0.458824, 0, 0, 1) +editable = false + +[node name="LineEdit2" type="Label" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/PositionEntry/LineEdit"] +custom_minimum_size = Vector2(300, 30) +layout_mode = 1 +offset_left = 9.97753 +offset_top = 3.2088 +offset_right = 309.978 +offset_bottom = 33.2088 +theme_override_colors/font_color = Color(0.458824, 0, 0, 1) +theme_override_fonts/font = ExtResource("3_2vg4d") +text = "c.setup.position.value" + +[node name="Uniform" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.uniform" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="SelectUniform" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +alignment = 1 + +[node name="Experience" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.experience" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="SelectExperience" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +alignment = 1 + +[node name="CheckBox" type="CheckBox" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/SelectExperience"] +layout_mode = 2 +text = "c.setup.experience.skip" +text_direction = 3 + +[node name="Duties" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.duties" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="Terms" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.additional_terms" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="Compensation" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.compensation" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="CompensationEntry" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 + +[node name="Spacer" type="Control" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/CompensationEntry"] +custom_minimum_size = Vector2(15.045, 0) +layout_mode = 2 + +[node name="Text1" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/CompensationEntry"] +custom_minimum_size = Vector2(100.08, 0) +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.compensation.salary.prefix" +fit_content = true +scroll_active = false +autowrap_mode = 0 + +[node name="LineEdit" type="LineEdit" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/CompensationEntry"] +custom_minimum_size = Vector2(50, 30) +layout_mode = 2 +theme_override_colors/font_uneditable_color = Color(0.478431, 0, 0, 1) +editable = false + +[node name="LineEdit2" type="Label" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/CompensationEntry/LineEdit"] +custom_minimum_size = Vector2(50, 30) +layout_mode = 1 +offset_left = 9.55965 +offset_top = 4.09178 +offset_right = 79.5597 +offset_bottom = 34.0918 +theme_override_colors/font_color = Color(0.478431, 0, 0, 1) +theme_override_fonts/font = ExtResource("3_2vg4d") +text = "c.setup.compensation.salary" + +[node name="Text2" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/CompensationEntry"] +custom_minimum_size = Vector2(100.08, 0) +layout_mode = 2 +bbcode_enabled = true +text = "c.setup.compensation.salary.suffix" +scroll_active = false + +[node name="Spacer" type="Control" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +custom_minimum_size = Vector2(0, 200) +layout_mode = 2 + +[node name="Signatures" type="HBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents"] +layout_mode = 2 + +[node name="EmployerMargin" type="MarginContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures"] +layout_mode = 2 + +[node name="Sign" type="VBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployerMargin"] +layout_mode = 2 + +[node name="Desc" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployerMargin/Sign"] +layout_mode = 2 +theme_override_font_sizes/normal_font_size = 15 +bbcode_enabled = true +text = "c.setup.frank_signature.desc" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="Signature" type="Label" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployerMargin/Sign"] +custom_minimum_size = Vector2(200, 80) +layout_mode = 2 +theme_override_colors/font_color = Color(0.415686, 0.0253044, 0.135441, 1) +theme_override_fonts/font = SubResource("FontVariation_2cc7p") +theme_override_font_sizes/font_size = 31 +text = "c.setup.frank_signature" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="Underline" type="HSeparator" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployerMargin/Sign"] +layout_mode = 2 + +[node name="EmployeeMargin" type="MarginContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures"] +layout_mode = 2 + +[node name="Sign" type="VBoxContainer" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin"] +layout_mode = 2 + +[node name="Desc" type="RichTextLabel" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin/Sign"] +layout_mode = 2 +theme_override_font_sizes/normal_font_size = 15 +bbcode_enabled = true +text = "c.setup.user_signature.desc" +fit_content = true +scroll_active = false +text_direction = 3 + +[node name="Signature" type="Button" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin/Sign" groups=["no_click_sound"]] +custom_minimum_size = Vector2(200, 80) +layout_mode = 2 +text = "c.setup.user_signature" + +[node name="Underline" type="HSeparator" parent="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin/Sign"] +layout_mode = 2 + +[node name="Back" type="Button" parent="."] +layout_mode = 1 +offset_right = 106.0 +offset_bottom = 31.0 +text = "c.menu.back" + +[node name="Page" type="AudioStreamPlayer" parent="."] +stream = ExtResource("5_xac6d") +volume_db = -16.0 +autoplay = true + +[node name="Sign" type="AudioStreamPlayer" parent="."] +stream = ExtResource("6_wf0gh") +volume_db = -16.0 + +[connection signal="pressed" from="ScrollContainer/Control/TextureRect/PaperMargin/Contents/Signatures/EmployeeMargin/Sign/Signature" to="." method="_on_sign_pressed"] +[connection signal="pressed" from="Back" to="." method="_on_back_pressed"] diff --git a/client/gui/menus/transition/scene_transition.gd b/client/gui/menus/transition/scene_transition.gd new file mode 100644 index 00000000..330d67d6 --- /dev/null +++ b/client/gui/menus/transition/scene_transition.gd @@ -0,0 +1,66 @@ +# Hurry Curry! - a game about cooking +# Copyright (C) 2025 Hurry Curry! contributors +# +# 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, version 3 of the License only. +# +# 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 SceneTransition +extends Control + +@onready var black_anim: AnimationPlayer = $black_fader +@onready var text_anim: AnimationPlayer = $text_fader +@onready var text: Label = $text_margin/text + +var s_current = false +var s_target = false +var fading = false + +func _ready(): + $black.visible = true + text.visible = true + text.text = "" + +func set_loading_text(s: String): + text.text = s + text_anim.play("fade") + +func next(): + while fading: await black_anim.animation_finished + if s_target == s_current: return + fading = true + if s_target: + if text.text != "": + text_anim.play_backwards("fade") + await text_anim.animation_finished + black_anim.play_backwards("fade") + await black_anim.animation_finished + self.mouse_filter = Control.MOUSE_FILTER_IGNORE + set_loading_text("") + s_current = true + else: + self.mouse_filter = Control.MOUSE_FILTER_STOP + black_anim.play("fade") + await black_anim.animation_finished + await get_tree().process_frame # animation finishes one frame early + s_current = false + fading = false + await next() + +func fade_in(): + s_target = true + await next() +func fade_out(): + s_target = false + await next() + +func _exit_tree(): + if fading: push_error("SceneTransition destroyed while fading") diff --git a/client/gui/menus/transition/scene_transition.gd.uid b/client/gui/menus/transition/scene_transition.gd.uid new file mode 100644 index 00000000..60f764ae --- /dev/null +++ b/client/gui/menus/transition/scene_transition.gd.uid @@ -0,0 +1 @@ +uid://ciml1u2x4f1ci diff --git a/client/gui/menus/transition/scene_transition.tscn b/client/gui/menus/transition/scene_transition.tscn new file mode 100644 index 00000000..dab16084 --- /dev/null +++ b/client/gui/menus/transition/scene_transition.tscn @@ -0,0 +1,135 @@ +[gd_scene load_steps=11 format=3 uid="uid://bg2d78ycorcqk"] + +[ext_resource type="Script" uid="uid://ciml1u2x4f1ci" path="res://gui/menus/transition/scene_transition.gd" id="1_fpbwj"] +[ext_resource type="Shader" uid="uid://bmxrbbw18xq7u" path="res://gui/menus/transition/text_loading_anim.gdshader" id="2_g21ck"] + +[sub_resource type="Animation" id="Animation_g21ck"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("black:color") +tracks/0/interp = 1 +tracks/0/loop_wrap = false +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(0, 0, 0, 1)] +} + +[sub_resource type="Animation" id="Animation_e6dcd"] +resource_name = "fade" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("black:color") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(0, 0, 0, 0), Color(0, 0, 0, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_00tv0"] +_data = { +&"RESET": SubResource("Animation_g21ck"), +&"fade": SubResource("Animation_e6dcd") +} + +[sub_resource type="Animation" id="Animation_xgn2a"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("text_margin/text: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)] +} + +[sub_resource type="Animation" id="Animation_cq5i2"] +resource_name = "fade" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("text_margin/text:modulate") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_pea72"] +_data = { +&"RESET": SubResource("Animation_xgn2a"), +&"fade": SubResource("Animation_cq5i2") +} + +[sub_resource type="ShaderMaterial" id="ShaderMaterial_00tv0"] +shader = ExtResource("2_g21ck") + +[sub_resource type="LabelSettings" id="LabelSettings_e6dcd"] +font_size = 34 + +[node name="SceneTransition" type="Control"] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +script = ExtResource("1_fpbwj") + +[node name="black_fader" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_00tv0") +} +speed_scale = 4.0 + +[node name="black" type="ColorRect" parent="."] +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +mouse_filter = 2 +color = Color(0, 0, 0, 1) + +[node name="text_fader" type="AnimationPlayer" parent="."] +libraries = { +&"": SubResource("AnimationLibrary_pea72") +} +speed_scale = 4.0 + +[node name="text_margin" type="MarginContainer" parent="."] +layout_mode = 1 +anchors_preset = 2 +anchor_top = 1.0 +anchor_bottom = 1.0 +offset_top = -107.0 +offset_right = 401.0 +grow_vertical = 0 +mouse_filter = 2 +theme_override_constants/margin_left = 50 +theme_override_constants/margin_top = 50 +theme_override_constants/margin_right = 50 +theme_override_constants/margin_bottom = 50 + +[node name="text" type="Label" parent="text_margin"] +modulate = Color(1, 1, 1, 0) +material = SubResource("ShaderMaterial_00tv0") +layout_mode = 2 +text = "Loading something..." +label_settings = SubResource("LabelSettings_e6dcd") diff --git a/client/gui/menus/transition/text_loading_anim.gdshader b/client/gui/menus/transition/text_loading_anim.gdshader new file mode 100644 index 00000000..145dab78 --- /dev/null +++ b/client/gui/menus/transition/text_loading_anim.gdshader @@ -0,0 +1,13 @@ +shader_type canvas_item; + +varying vec4 vertex_color; +void vertex() { + vertex_color = COLOR; +} + +void fragment() { + vec4 tex = texture(TEXTURE, UV) * COLOR; + float wave = sin(VERTEX.x*0.01-TIME*10.) * 0.5 + 0.5; + wave = pow(wave, 3.); + COLOR = tex * (1. - wave * 0.2); +} diff --git a/client/gui/menus/transition/text_loading_anim.gdshader.uid b/client/gui/menus/transition/text_loading_anim.gdshader.uid new file mode 100644 index 00000000..26730c73 --- /dev/null +++ b/client/gui/menus/transition/text_loading_anim.gdshader.uid @@ -0,0 +1 @@ +uid://bmxrbbw18xq7u |