diff --git a/.gdlintrc b/.gdlintrc index eb8df5b..0209c23 100644 --- a/.gdlintrc +++ b/.gdlintrc @@ -36,7 +36,7 @@ loop-variable-name: _?[a-z][a-z0-9]*(_[a-z0-9]+)* max-file-lines: 1000 max-line-length: 100 max-public-methods: 20 -max-returns: 6 +max-returns: 12 mixed-tabs-and-spaces: null no-elif-return: null no-else-return: null diff --git a/src/game/game.gd b/src/game/game.gd index 114d071..2f8dd1d 100644 --- a/src/game/game.gd +++ b/src/game/game.gd @@ -1,18 +1,65 @@ class_name Game extends Control +enum Move { + NONE, + ROCK, + PAPER, + SCISSORS, +} + +enum Outcome { + DEFEAT = -1, + TIE = 0, + VICTORY = 1, +} + @export var player_name: String +var player_wins := 0: + set(value): + player_wins = value + player_win_counter.text = str(player_wins) + +var opponent_wins := 0: + set(value): + opponent_wins = value + opponent_win_counter.text = str(opponent_wins) + +var player_move: Move +var opponent_move: Move + @onready var player_name_label: Label = %PlayerName @onready var opponent_name: Label = %OpponentName +@onready var player_win_counter: Label = %PlayerWinCounter +@onready var opponent_win_counter: Label = %OpponentWinCounter + +@onready var move_select: VBoxContainer = %MoveSelect + +@onready var opponent_choosing_move: Label = %OpponentChoosingMove +@onready var opponent_ready: Label = %OpponentReady + +@onready var player_move_label: Label = %PlayerMoveLabel +@onready var opponent_move_label: Label = %OpponentMoveLabel +@onready var victory: Label = %Victory +@onready var defeat: Label = %Defeat +@onready var tie: Label = %Tie + +@onready var result_animation: AnimationPlayer = %ResultAnimation + @onready var connecting_screen: ColorRect = %ConnectingScreen +@onready var fail_screen: ColorRect = %FailScreen + +@onready var default_opp_name := opponent_name.text func _ready() -> void: multiplayer.peer_connected.connect(_on_peer_connected) multiplayer.peer_disconnected.connect(_on_peer_disconnected) + multiplayer.connection_failed.connect(_on_connection_failed) player_name_label.text = player_name + print("Starting client for player ", player_name) @rpc("any_peer", "call_remote", "reliable", 0) @@ -20,12 +67,115 @@ func set_opponent_name(opp_name: String) -> void: opponent_name.text = opp_name +@rpc("any_peer", "call_remote", "reliable", 0) +func set_opponent_move(move: Move) -> void: + print("Opponent set move: ", move) + opponent_move = move + opponent_move_label.text = Move.keys()[opponent_move] + if move == Move.NONE: + opponent_ready.hide() + opponent_choosing_move.show() + else: + opponent_choosing_move.hide() + opponent_ready.show() + _check_play_condition() + + +func start_round() -> void: + move_select.show() + opponent_ready.hide() + opponent_choosing_move.show() + + +func finish_round() -> void: + match get_outcome(): + Outcome.VICTORY: + player_wins += 1 + Outcome.DEFEAT: + opponent_wins += 1 + player_move = Move.NONE + opponent_move = Move.NONE + for button: Button in get_tree().get_nodes_in_group("MoveButton"): + button.set_pressed_no_signal(false) + start_round() + + func _on_peer_connected(id: int) -> void: print("PEER CONNECTED ", id) set_opponent_name.rpc(player_name) + opponent_wins = 0 connecting_screen.hide() + fail_screen.hide() + start_round() func _on_peer_disconnected(id: int) -> void: print("PEER DISCONNECTED: ", id) + set_opponent_name(default_opp_name) + opponent_wins = 0 + opponent_choosing_move.hide() + opponent_ready.hide() connecting_screen.show() + + +func _on_connection_failed() -> void: + print("CONNECTION FAILED!") + connecting_screen.hide() + fail_screen.show() + + +func _set_player_move(value: Move) -> void: + player_move = value + player_move_label.text = Move.keys()[player_move] + set_opponent_move.rpc(player_move) + _check_play_condition() + + +func _check_play_condition() -> void: + if player_move and opponent_move: + _set_outcome_label(get_outcome()) + move_select.hide() + opponent_ready.hide() + opponent_choosing_move.hide() + result_animation.play("show") + + +func get_outcome() -> Outcome: + match player_move: + Move.ROCK: + match opponent_move: + Move.PAPER: + return Outcome.DEFEAT + Move.SCISSORS: + return Outcome.VICTORY + Move.PAPER: + match opponent_move: + Move.SCISSORS: + return Outcome.DEFEAT + Move.ROCK: + return Outcome.VICTORY + Move.SCISSORS: + match opponent_move: + Move.ROCK: + return Outcome.DEFEAT + Move.PAPER: + return Outcome.VICTORY + return Outcome.TIE + + +func _set_outcome_label(outcome: Outcome) -> void: + victory.visible = outcome == Outcome.VICTORY + defeat.visible = outcome == Outcome.DEFEAT + tie.visible = outcome == Outcome.TIE + + +func _on_rock_toggled(toggled_on: bool) -> void: + _set_player_move(Move.ROCK if toggled_on else Move.NONE) + + +func _on_paper_toggled(toggled_on: bool) -> void: + _set_player_move(Move.PAPER if toggled_on else Move.NONE) + + +func _on_scissors_toggled(toggled_on: bool) -> void: + _set_player_move(Move.SCISSORS if toggled_on else Move.NONE) diff --git a/src/game/game.tscn b/src/game/game.tscn index 4c02e28..21fb47b 100644 --- a/src/game/game.tscn +++ b/src/game/game.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://d0d6xpa4ma7wd"] +[gd_scene load_steps=9 format=3 uid="uid://d0d6xpa4ma7wd"] [ext_resource type="Shader" path="res://src/shaders/balatro_bg.gdshader" id="1_defdw"] [ext_resource type="Script" path="res://src/game/game.gd" id="1_kepju"] @@ -20,6 +20,180 @@ shader_parameter/contrast = 2.0 shader_parameter/spin_amount = 0.36 shader_parameter/pixel_filter = 300.0 +[sub_resource type="ButtonGroup" id="ButtonGroup_yg2pd"] +allow_unpress = true + +[sub_resource type="Animation" id="Animation_dtpn3"] +resource_name = "show" +length = 4.0 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 4), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [true, false] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 0.3, 3.7, 4), +"transitions": PackedFloat32Array(0.618, 1, 0.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 1), Color(1, 1, 1, 1), Color(1, 1, 1, 0)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("VBoxContainer/OutcomeContainer:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 2.5, 2.8), +"transitions": PackedFloat32Array(1, 1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("VBoxContainer/HBoxContainer/PlayerMoveLabel:modulate") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0, 0.5, 0.7), +"transitions": PackedFloat32Array(1, 1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("VBoxContainer/HBoxContainer/VsLabel:modulate") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0, 0.9, 1.1), +"transitions": PackedFloat32Array(1, 1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("VBoxContainer/HBoxContainer/OpponentMoveLabel:modulate") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0, 1.3, 1.5), +"transitions": PackedFloat32Array(1, 1.618, 1), +"update": 0, +"values": [Color(1, 1, 1, 0), Color(1, 1, 1, 0), Color(1, 1, 1, 1)] +} +tracks/6/type = "method" +tracks/6/imported = false +tracks/6/enabled = true +tracks/6/path = NodePath("%ResultScreen/..") +tracks/6/interp = 1 +tracks/6/loop_wrap = true +tracks/6/keys = { +"times": PackedFloat32Array(4), +"transitions": PackedFloat32Array(1), +"values": [{ +"args": [], +"method": &"finish_round" +}] +} + +[sub_resource type="Animation" id="Animation_q607b"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath(".:visible") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [false] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath(".:modulate") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 1)] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("VBoxContainer/OutcomeContainer:modulate") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("VBoxContainer/HBoxContainer/PlayerMoveLabel:modulate") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/4/type = "value" +tracks/4/imported = false +tracks/4/enabled = true +tracks/4/path = NodePath("VBoxContainer/HBoxContainer/VsLabel:modulate") +tracks/4/interp = 1 +tracks/4/loop_wrap = true +tracks/4/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} +tracks/5/type = "value" +tracks/5/imported = false +tracks/5/enabled = true +tracks/5/path = NodePath("VBoxContainer/HBoxContainer/OpponentMoveLabel:modulate") +tracks/5/interp = 1 +tracks/5/loop_wrap = true +tracks/5/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [Color(1, 1, 1, 0)] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_1nmkl"] +_data = { +"RESET": SubResource("Animation_q607b"), +"show": SubResource("Animation_dtpn3") +} + [node name="Game" type="Control"] layout_mode = 3 anchors_preset = 15 @@ -56,6 +230,7 @@ theme_override_constants/margin_bottom = 32 [node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/LeftColumn"] layout_mode = 2 +theme_override_constants/separation = 16 [node name="Label" type="Label" parent="HBoxContainer/LeftColumn/VBoxContainer"] layout_mode = 2 @@ -71,6 +246,48 @@ theme_override_font_sizes/font_size = 32 text = "Lieutenant Nudisco" horizontal_alignment = 1 +[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/LeftColumn/VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="HBoxContainer/LeftColumn/VBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "Wins:" + +[node name="PlayerWinCounter" type="Label" parent="HBoxContainer/LeftColumn/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "0 +" + +[node name="MoveSelect" type="VBoxContainer" parent="HBoxContainer/LeftColumn/VBoxContainer"] +unique_name_in_owner = true +visible = false +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 +theme_override_constants/separation = 16 + +[node name="Rock" type="Button" parent="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect" groups=["MoveButton"]] +layout_mode = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_yg2pd") +text = "ROCK" + +[node name="Paper" type="Button" parent="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect" groups=["MoveButton"]] +layout_mode = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_yg2pd") +text = "PAPER" + +[node name="Scissors" type="Button" parent="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect" groups=["MoveButton"]] +layout_mode = 2 +toggle_mode = true +button_group = SubResource("ButtonGroup_yg2pd") +text = "SCISSORS" + [node name="RightColumn" type="MarginContainer" parent="HBoxContainer"] layout_mode = 2 size_flags_horizontal = 3 @@ -81,6 +298,7 @@ theme_override_constants/margin_bottom = 32 [node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer/RightColumn"] layout_mode = 2 +theme_override_constants/separation = 16 [node name="Label" type="Label" parent="HBoxContainer/RightColumn/VBoxContainer"] layout_mode = 2 @@ -93,9 +311,172 @@ unique_name_in_owner = true layout_mode = 2 theme_override_colors/font_color = Color(0, 0, 0, 1) theme_override_font_sizes/font_size = 32 -text = "Waiting for Connection..." +text = "???" horizontal_alignment = 1 +[node name="HBoxContainer" type="HBoxContainer" parent="HBoxContainer/RightColumn/VBoxContainer"] +layout_mode = 2 +alignment = 1 + +[node name="Label" type="Label" parent="HBoxContainer/RightColumn/VBoxContainer/HBoxContainer"] +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "Wins:" + +[node name="OpponentWinCounter" type="Label" parent="HBoxContainer/RightColumn/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +text = "0 +" + +[node name="OpponentChoosingMove" type="Label" parent="HBoxContainer/RightColumn/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(0, 200) +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_fonts/font = ExtResource("3_jqmr8") +theme_override_font_sizes/font_size = 28 +text = "Choosing a move..." +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="OpponentReady" type="Label" parent="HBoxContainer/RightColumn/VBoxContainer"] +unique_name_in_owner = true +visible = false +custom_minimum_size = Vector2(0, 200) +layout_mode = 2 +theme_override_colors/font_color = Color(0, 0, 0, 1) +theme_override_fonts/font = ExtResource("3_jqmr8") +theme_override_font_sizes/font_size = 28 +text = "Ready!" +horizontal_alignment = 1 +vertical_alignment = 1 + +[node name="ResultScreen" type="ColorRect" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.52549) + +[node name="VBoxContainer" type="VBoxContainer" parent="ResultScreen"] +layout_mode = 1 +anchors_preset = 14 +anchor_top = 0.5 +anchor_right = 1.0 +anchor_bottom = 0.5 +grow_horizontal = 2 +grow_vertical = 2 + +[node name="HBoxContainer" type="HBoxContainer" parent="ResultScreen/VBoxContainer"] +layout_mode = 2 + +[node name="PlayerMoveLabel" type="Label" parent="ResultScreen/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/outline_size = 12 +theme_override_font_sizes/font_size = 64 +text = "SCISSORS" +horizontal_alignment = 2 + +[node name="VsLabel" type="Label" parent="ResultScreen/VBoxContainer/HBoxContainer"] +modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(60, 0) +layout_mode = 2 +theme_override_constants/outline_size = 12 +theme_override_font_sizes/font_size = 37 +text = "vs" +horizontal_alignment = 1 + +[node name="OpponentMoveLabel" type="Label" parent="ResultScreen/VBoxContainer/HBoxContainer"] +unique_name_in_owner = true +modulate = Color(1, 1, 1, 0) +layout_mode = 2 +size_flags_horizontal = 3 +theme_override_constants/outline_size = 12 +theme_override_font_sizes/font_size = 64 +text = "SCISSORS" + +[node name="OutcomeContainer" type="Control" parent="ResultScreen/VBoxContainer"] +modulate = Color(1, 1, 1, 0) +custom_minimum_size = Vector2(0, 158) +layout_mode = 2 +size_flags_horizontal = 4 + +[node name="Victory" type="Label" parent="ResultScreen/VBoxContainer/OutcomeContainer"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -576.0 +offset_top = -12.0 +offset_right = 576.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_color = Color(1, 0.3, 0.3, 1) +theme_override_constants/outline_size = 20 +theme_override_font_sizes/font_size = 108 +text = "VICTORY" +horizontal_alignment = 1 + +[node name="Defeat" type="Label" parent="ResultScreen/VBoxContainer/OutcomeContainer"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -576.0 +offset_top = -12.0 +offset_right = 576.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_color = Color(0.3, 0.556667, 1, 1) +theme_override_constants/outline_size = 20 +theme_override_font_sizes/font_size = 108 +text = "DEFEAT" +horizontal_alignment = 1 + +[node name="Tie" type="Label" parent="ResultScreen/VBoxContainer/OutcomeContainer"] +unique_name_in_owner = true +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -576.0 +offset_top = -12.0 +offset_right = 576.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_color = Color(1, 0.3, 0.988333, 1) +theme_override_constants/outline_size = 20 +theme_override_font_sizes/font_size = 108 +text = "TIE" +horizontal_alignment = 1 + +[node name="ResultAnimation" type="AnimationPlayer" parent="ResultScreen"] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_1nmkl") +} + [node name="ConnectingScreen" type="ColorRect" parent="."] unique_name_in_owner = true layout_mode = 1 @@ -119,6 +500,42 @@ offset_right = 20.0 offset_bottom = 12.0 grow_horizontal = 2 grow_vertical = 2 +theme_override_constants/outline_size = 12 theme_override_fonts/font = ExtResource("3_jqmr8") theme_override_font_sizes/font_size = 64 text = "Waiting for Connection..." + +[node name="FailScreen" type="ColorRect" parent="."] +unique_name_in_owner = true +visible = false +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +color = Color(0, 0, 0, 0.52549) + +[node name="Label" type="Label" parent="FailScreen"] +layout_mode = 1 +anchors_preset = 8 +anchor_left = 0.5 +anchor_top = 0.5 +anchor_right = 0.5 +anchor_bottom = 0.5 +offset_left = -20.0 +offset_top = -12.0 +offset_right = 20.0 +offset_bottom = 12.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_colors/font_color = Color(1, 0.41, 0.41, 1) +theme_override_colors/font_outline_color = Color(0, 0, 0, 1) +theme_override_constants/outline_size = 12 +theme_override_fonts/font = ExtResource("3_jqmr8") +theme_override_font_sizes/font_size = 64 +text = "Connection Failed!" + +[connection signal="toggled" from="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect/Rock" to="." method="_on_rock_toggled"] +[connection signal="toggled" from="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect/Paper" to="." method="_on_paper_toggled"] +[connection signal="toggled" from="HBoxContainer/LeftColumn/VBoxContainer/MoveSelect/Scissors" to="." method="_on_scissors_toggled"] diff --git a/src/title_screen/title_screen.gd b/src/title_screen/title_screen.gd index 83edfae..142613c 100644 --- a/src/title_screen/title_screen.gd +++ b/src/title_screen/title_screen.gd @@ -1,6 +1,6 @@ extends Control -const MAX_CLIENTS = 2 +const MAX_CLIENTS = 1 const DEFAULT_PORT = 8383 const RANDOM_NAMES = [ diff --git a/src/title_screen/title_screen.tscn b/src/title_screen/title_screen.tscn index 050c26b..c62a9ff 100644 --- a/src/title_screen/title_screen.tscn +++ b/src/title_screen/title_screen.tscn @@ -69,6 +69,7 @@ grow_vertical = 2 [node name="NameInput" type="LineEdit" parent="Menu"] unique_name_in_owner = true +custom_minimum_size = Vector2(160, 0) layout_mode = 2 theme_override_fonts/font = ExtResource("4_c3wh6") placeholder_text = "Enter a name..." diff --git a/ui.theme b/ui.theme index e94403a..893a361 100644 Binary files a/ui.theme and b/ui.theme differ