feat: Add new visual effects, player and lobby scenes, and a special tiles manager.

This commit is contained in:
2026-03-17 18:04:57 +08:00
parent efdd8f4969
commit 05d9617f84
22 changed files with 5385 additions and 7 deletions
+7 -1
View File
@@ -247,11 +247,17 @@ func _on_server_option_selected(index: int) -> void:
if server_ip_input: NakamaManager.set_server(server_ip_input.text)
LobbyManager.is_lan_mode = false
connection_status.text = "Mode: Online (Nakama Remote)"
else:
elif index == 2:
# LAN Direct
if server_ip_input: server_ip_input.visible = false
LobbyManager.is_lan_mode = true
connection_status.text = "Mode: LAN Direct (No Server)"
elif index == 3:
# Tekton Dash EU
if server_ip_input: server_ip_input.visible = false
NakamaManager.set_server("tektondash.vps.webdock.cloud")
LobbyManager.is_lan_mode = false
connection_status.text = "Mode: Online (Tekton Dash EU)"
func _on_server_ip_submitted(new_text: String) -> void:
if server_option and server_option.selected == 1:
+3 -1
View File
@@ -90,13 +90,15 @@ text = "CONNECTION MODE"
custom_minimum_size = Vector2(0, 44)
layout_mode = 2
selected = 0
item_count = 3
item_count = 4
popup/item_0/text = "Nakama - Localhost (Testing)"
popup/item_0/id = 0
popup/item_1/text = "Nakama - Remote Server (Host IP)"
popup/item_1/id = 1
popup/item_2/text = "LAN Direct (No Server)"
popup/item_2/id = 2
popup/item_3/text = "Tekton Dash EU"
popup/item_3/id = 3
[node name="ServerIPInput" type="LineEdit" parent="MainMenuPanel/VBoxContainer/ServerSelectionSection" unique_id=748392104]
visible = false
+52
View File
@@ -153,6 +153,17 @@ var _is_highlighting: bool = false
@onready var character_gatot: Node3D = $Gatot
@onready var character_oldpop: Node3D = $Oldpop
# Skill initiator VFX
@onready var vfx_skill_freeze: AnimatedSprite3D = $skill_freeze
@onready var vfx_skill_ghost: AnimatedSprite3D = $skill_ghost
@onready var vfx_skill_speed: AnimatedSprite3D = $skill_speed
@onready var vfx_skill_wall: AnimatedSprite3D = $skill_wall
# Knock / receiver VFX
@onready var vfx_attack_top: AnimatedSprite3D = $attack_mode_top
@onready var vfx_attack_bot: AnimatedSprite3D = $attack_mode_bot
@onready var vfx_stunned: AnimatedSprite3D = $receiver_skill_stunned
var _selected_character: String = "Masbro"
var selected_character: String:
get: return _selected_character
@@ -500,6 +511,22 @@ func sync_special_animation() -> void:
"""Sync special ability animation across network."""
play_special_animation()
# =============================================================================
# Skill VFX
# =============================================================================
@rpc("any_peer", "call_local", "reliable")
func play_skill_vfx(node_name: String) -> void:
"""Show, play, and auto-hide a skill AnimatedSprite3D by node name.
Called on all peers so everyone sees the initiator's VFX."""
var vfx: AnimatedSprite3D = get_node_or_null(node_name)
if not vfx:
return
vfx.visible = true
vfx.play()
await vfx.animation_finished
vfx.visible = false
# =============================================================================
# Screen Shake
# =============================================================================
@@ -738,6 +765,9 @@ func apply_stagger(duration: float = 1.5):
is_frozen = true
_apply_tint_recursive(self , Color.BLUE) # Visual feedback
# Play knock VFX sequence on the receiver
_play_knock_vfx()
# Set immunity (3 seconds as requested)
immunity_timer = 3.0
@@ -762,6 +792,28 @@ func apply_stagger(duration: float = 1.5):
else:
_apply_tint_recursive(self , Color.WHITE) # Remove tint
func _play_knock_vfx() -> void:
"""Plays the three knock receiver VFX in sequence: top -> bot -> stunned.
Each hides itself once its animation finishes."""
# 1. attack_mode_top
if vfx_attack_top:
vfx_attack_top.visible = true
vfx_attack_top.play()
await vfx_attack_top.animation_finished
vfx_attack_top.visible = false
# 2. attack_mode_bot
if vfx_attack_bot:
vfx_attack_bot.visible = true
vfx_attack_bot.play()
await vfx_attack_bot.animation_finished
vfx_attack_bot.visible = false
# 3. receiver_skill_stunned
if vfx_stunned:
vfx_stunned.visible = true
vfx_stunned.play()
await vfx_stunned.animation_finished
vfx_stunned.visible = false
@rpc("any_peer", "call_local", "reliable")
func sync_stop_freeze(enabled: bool):
# Security: Only allow server (peer 1) or local calls (peer 0)
+70
View File
@@ -9,6 +9,7 @@
[ext_resource type="AnimationLibrary" uid="uid://c3pyopnwibckj" path="res://assets/characters/animations/animation-pack.res" id="6_5oq5w"]
[ext_resource type="Script" uid="uid://cwwwixc07jc86" path="res://scripts/bot_controller.gd" id="7_botctrl"]
[ext_resource type="FontFile" uid="uid://xnjx058n4tsw" path="res://assets/fonts/Nougat-ExtraBlack.ttf" id="8_y4r1p"]
[ext_resource type="SpriteFrames" uid="uid://7r0qbbm88vfy" path="res://assets/graphics/vfx/effects/animation-head.tres" id="10_y4r1p"]
[sub_resource type="TorusMesh" id="TorusMesh_ur7pv"]
inner_radius = 1.0
@@ -30,8 +31,17 @@ spacing_glyph = 5
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.5, 0)
collision_layer = 2
script = ExtResource("1_qecr4")
is_bot = null
enhanced_gridmap_path = null
current_position = null
cell_size = Vector3(1, 1, 1)
cell_offset = null
goals = null
playerboard = null
movement_range = null
use_diagonal_movement = true
is_my_turn = null
has_moved_this_turn = null
[node name="Masbro" parent="." unique_id=1585899496 instance=ExtResource("2_mjsl8")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -0.485, 0)
@@ -108,3 +118,63 @@ width = 700.0
[node name="BotController" type="Node" parent="." unique_id=723259755]
script = ExtResource("7_botctrl")
[node name="skill_freeze" type="AnimatedSprite3D" parent="." unique_id=674916570]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 1.5653763, 0)
visible = false
billboard = 1
sprite_frames = ExtResource("10_y4r1p")
animation = &"freeze-initiator"
frame = 149
frame_progress = 1.0
[node name="skill_ghost" type="AnimatedSprite3D" parent="." unique_id=436351243]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 1.5653763, 0)
visible = false
billboard = 1
sprite_frames = ExtResource("10_y4r1p")
animation = &"ghost-initiator"
frame = 149
frame_progress = 1.0
[node name="skill_speed" type="AnimatedSprite3D" parent="." unique_id=374194236]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 1.5653763, 0)
visible = false
billboard = 1
sprite_frames = ExtResource("10_y4r1p")
animation = &"speed-initator"
frame = 149
frame_progress = 1.0
[node name="skill_wall" type="AnimatedSprite3D" parent="." unique_id=592755982]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, 1.5653763, 0)
visible = false
billboard = 1
sprite_frames = ExtResource("10_y4r1p")
animation = &"wall-initiator"
frame = 149
frame_progress = 1.0
[node name="receiver_skill_stunned" type="AnimatedSprite3D" parent="." unique_id=56953581]
transform = Transform3D(0.5, 0, 0, 0, -2.1855694e-08, -0.5, 0, 0.5, -2.1855694e-08, 0, 1.5653763, 0)
visible = false
billboard = 1
sprite_frames = ExtResource("10_y4r1p")
animation = &"stunned-receiver"
frame_progress = 0.016138485
[node name="attack_mode_top" type="AnimatedSprite3D" parent="." unique_id=2002706555]
transform = Transform3D(0.5, 0, 0, 0, -2.1855694e-08, -0.5, 0, 0.5, -2.1855694e-08, 0, 1.5653763, 0)
visible = false
sprite_frames = ExtResource("10_y4r1p")
animation = &"attack-mode-receiver-top"
frame = 7
frame_progress = 1.0
[node name="attack_mode_bot" type="AnimatedSprite3D" parent="." unique_id=1320274503]
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, -1, 0, 1, -4.371139e-08, 0, -0.6701627, 0)
visible = false
sprite_frames = ExtResource("10_y4r1p")
animation = &"attack-mode-receiver-bot"
frame = 31
frame_progress = 1.0