feat: update
This commit is contained in:
+88
-67
@@ -70,25 +70,23 @@ var is_carrying_tekton: bool = false:
|
||||
emit_signal("tekton_carried_changed", value)
|
||||
# Visual/Logic side effects if any
|
||||
|
||||
var is_attack_mode: bool = false:
|
||||
var is_charged_strike: bool = false:
|
||||
set(value):
|
||||
if is_attack_mode == value:
|
||||
return # Prevent infinite recursion / redundant updates
|
||||
if is_charged_strike == value:
|
||||
return
|
||||
|
||||
is_attack_mode = value
|
||||
if is_attack_mode:
|
||||
attack_mode_timer = MAX_ATTACK_MODE_TIME
|
||||
is_charged_strike = value
|
||||
if is_charged_strike:
|
||||
charged_strike_timer = MAX_CHARGED_STRIKE_TIME
|
||||
_refresh_player_visuals()
|
||||
|
||||
# Sync to others if we are the authority
|
||||
if is_multiplayer_authority() and can_rpc():
|
||||
rpc("sync_attack_mode", is_attack_mode)
|
||||
rpc("sync_charged_strike", is_charged_strike)
|
||||
|
||||
@rpc("any_peer", "call_local", "reliable")
|
||||
func sync_attack_mode(state: bool):
|
||||
# We WANT to trigger the setter to apply visuals on clients
|
||||
# Using self.var triggers setter in GDScript
|
||||
is_attack_mode = state
|
||||
func sync_charged_strike(state: bool):
|
||||
is_charged_strike = state
|
||||
|
||||
@export var is_bot: bool = false
|
||||
|
||||
@@ -248,6 +246,9 @@ func _ready():
|
||||
# Character Pointer Visibility
|
||||
# Visible to all human players. Green for local player, Red for others.
|
||||
var pointer = get_node_or_null("CharacterPointer")
|
||||
|
||||
# === Dynamically load new Dasher animations ===
|
||||
_load_dasher_animations()
|
||||
if pointer:
|
||||
pointer.visible = true
|
||||
|
||||
@@ -372,6 +373,47 @@ func _init_floor_spawn_anchor():
|
||||
if floor_spawn_top:
|
||||
floor_spawn_top.reparent(floor_spawn_anchor, false)
|
||||
|
||||
func _load_dasher_animations():
|
||||
"""Dynamically loads dasher animations from GLB files and adds them to the AnimationPlayer."""
|
||||
if not anim_player: return
|
||||
|
||||
var anim_library = anim_player.get_animation_library("animation-pack")
|
||||
if not anim_library:
|
||||
anim_library = AnimationLibrary.new()
|
||||
anim_player.add_animation_library("animation-pack", anim_library)
|
||||
|
||||
var dasher_files = [
|
||||
{"path": "res://assets/characters/dashers/dasher_getting_hit.glb", "name": "dasher_getting_hit"},
|
||||
{"path": "res://assets/characters/dashers/dasher_hit.glb", "name": "dasher_hit"},
|
||||
{"path": "res://assets/characters/dashers/dasher_hold.glb", "name": "dasher_hold"},
|
||||
{"path": "res://assets/characters/dashers/dasher_put.glb", "name": "dasher_put"},
|
||||
{"path": "res://assets/characters/dashers/dasher_stun.glb", "name": "dasher_stun"},
|
||||
{"path": "res://assets/characters/dashers/dasher_take.glb", "name": "dasher_take"}
|
||||
]
|
||||
|
||||
for file_data in dasher_files:
|
||||
var gltf_doc = GLTFDocument.new()
|
||||
var gltf_state = GLTFState.new()
|
||||
var error = gltf_doc.append_from_file(file_data.path, gltf_state)
|
||||
|
||||
if error == OK:
|
||||
var anim_player_node = gltf_state.get_animation_player(0)
|
||||
# Godot's GLTF importer creates an AnimationPlayer inside the scene
|
||||
var scene = gltf_doc.generate_scene(gltf_state)
|
||||
if scene:
|
||||
var scene_anim_player = scene.find_child("AnimationPlayer", true, false)
|
||||
if scene_anim_player:
|
||||
var libs = scene_anim_player.get_animation_library_list()
|
||||
for lib_name in libs:
|
||||
var temp_lib = scene_anim_player.get_animation_library(lib_name)
|
||||
for anim_name in temp_lib.get_animation_list():
|
||||
var anim = temp_lib.get_animation(anim_name)
|
||||
if not anim_library.has_animation(file_data.name):
|
||||
anim_library.add_animation(file_data.name, anim)
|
||||
scene.queue_free()
|
||||
|
||||
print("[Player] Dasher animations loaded into 'animation-pack'.")
|
||||
|
||||
@onready var floor_spawn_bot: AnimatedSprite3D = $floor_spawn_bot
|
||||
@onready var floor_spawn_top: AnimatedSprite3D = $floor_spawn_top
|
||||
@onready var vfx_scatter_knock: AnimatedSprite3D = $scatter_knock
|
||||
@@ -874,10 +916,10 @@ func _refresh_player_visuals():
|
||||
color_to_apply = Color.CYAN # Stop n Go Freeze
|
||||
elif is_slowed:
|
||||
color_to_apply = Color(0.6, 0.8, 1.0) # Slowed / Icy Blue
|
||||
elif is_attack_mode:
|
||||
color_to_apply = Color(1.0, 0.5, 0.5) # Attack Mode (Red Tint)
|
||||
elif is_carrying_tekton or is_knock_mode:
|
||||
color_to_apply = Color(1.0, 1.0, 0.0) # Carrying or Knocking (Yellow)
|
||||
elif is_charged_strike:
|
||||
color_to_apply = Color(1.0, 0.5, 0.5) # Charged Strike (Red Tint)
|
||||
elif is_carrying_tekton:
|
||||
color_to_apply = Color(1.0, 1.0, 0.0) # Carrying (Yellow)
|
||||
alpha_to_apply = 0.5 # 50% opacity when carrying Tekton
|
||||
elif immunity_timer > 0:
|
||||
color_to_apply = Color(0.5, 1.0, 0.5) # Immunity (Light Green)
|
||||
@@ -905,7 +947,7 @@ func update_rank_visuals(rank: int):
|
||||
if not pos_label:
|
||||
return
|
||||
|
||||
if rank <= 3:
|
||||
if rank <= 4:
|
||||
pos_label.visible = true
|
||||
if race_manager:
|
||||
pos_label.text = race_manager.get_ordinal_string(rank)
|
||||
@@ -913,9 +955,10 @@ func update_rank_visuals(rank: int):
|
||||
pos_label.text = str(rank)
|
||||
|
||||
match rank:
|
||||
1: pos_label.modulate = Color(0.85, 0.0, 0.0) # Red
|
||||
2: pos_label.modulate = Color(0.0, 0.0, 1.0) # Blue
|
||||
3: pos_label.modulate = Color(1.0, 0.9, 0.0) # Yellow
|
||||
1: pos_label.modulate = Color(1.0, 0.84, 0.0) # Gold
|
||||
2: pos_label.modulate = Color(0.75, 0.75, 0.75) # Silver
|
||||
3: pos_label.modulate = Color(0.8, 0.5, 0.2) # Bronze
|
||||
4: pos_label.modulate = Color(0.5, 0.5, 0.5) # Grey
|
||||
else:
|
||||
pos_label.visible = false
|
||||
|
||||
@@ -941,8 +984,8 @@ var slow_timer: float = 0.0
|
||||
var tekton_carry_timer: float = 0.0
|
||||
const MAX_TEKTON_CARRY_TIME: float = 3.0
|
||||
|
||||
var attack_mode_timer: float = 0.0
|
||||
const MAX_ATTACK_MODE_TIME: float = 5.0
|
||||
var charged_strike_timer: float = 0.0
|
||||
const MAX_CHARGED_STRIKE_TIME: float = 5.0
|
||||
|
||||
@rpc("any_peer", "call_local")
|
||||
func apply_stagger(duration: float = 1.5):
|
||||
@@ -1220,7 +1263,7 @@ func attempt_target_action(target_index: int):
|
||||
inventory_ui.deselect()
|
||||
|
||||
func activate_powerup(effect_id: int):
|
||||
if is_carrying_tekton or is_knock_mode or is_attack_mode:
|
||||
if is_carrying_tekton or is_charged_strike:
|
||||
NotificationManager.send_message(self, "Cannot use Power-Up right now!", NotificationManager.MessageType.WARNING)
|
||||
return
|
||||
|
||||
@@ -1253,7 +1296,7 @@ func activate_powerup(effect_id: int):
|
||||
|
||||
func activate_held_powerup():
|
||||
"""Finds whichever powerup is currently held and activates it."""
|
||||
if is_carrying_tekton or is_knock_mode or is_attack_mode:
|
||||
if is_carrying_tekton or is_charged_strike:
|
||||
NotificationManager.send_message(self, "Cannot use Power-Up right now!", NotificationManager.MessageType.WARNING)
|
||||
return
|
||||
|
||||
@@ -1299,15 +1342,14 @@ func _process(delta):
|
||||
if movement_manager:
|
||||
movement_manager._process(delta)
|
||||
|
||||
# Attack/Knock Mode Expiration Timer
|
||||
if is_multiplayer_authority() and (is_attack_mode or is_knock_mode):
|
||||
if attack_mode_timer > 0:
|
||||
attack_mode_timer -= delta
|
||||
if attack_mode_timer <= 0:
|
||||
attack_mode_timer = 0.0
|
||||
is_attack_mode = false
|
||||
is_knock_mode = false
|
||||
NotificationManager.send_message(self, "Knock Mode Expired!", NotificationManager.MessageType.WARNING)
|
||||
# Charged Strike Expiration Timer
|
||||
if is_multiplayer_authority() and is_charged_strike:
|
||||
if charged_strike_timer > 0:
|
||||
charged_strike_timer -= delta
|
||||
if charged_strike_timer <= 0:
|
||||
charged_strike_timer = 0.0
|
||||
is_charged_strike = false
|
||||
NotificationManager.send_message(self, "Charged Strike Expired!", NotificationManager.MessageType.WARNING)
|
||||
if powerup_manager:
|
||||
powerup_manager.reset_boost()
|
||||
|
||||
@@ -2393,8 +2435,8 @@ func sync_snatch_tekton(carrier_path: NodePath, tekton_path: NodePath):
|
||||
tekton_carry_timer = 0.0
|
||||
|
||||
# Visual/Logic side effects
|
||||
if is_attack_mode:
|
||||
is_attack_mode = false
|
||||
if is_charged_strike:
|
||||
is_charged_strike = false
|
||||
|
||||
SfxManager.play("pick_up_tekton_roaming")
|
||||
play_pickup_animation()
|
||||
@@ -2414,9 +2456,9 @@ func sync_grab_tekton(tekton_path: NodePath):
|
||||
self.is_carrying_tekton = true
|
||||
tekton.set_carried(true, self )
|
||||
|
||||
# Disposed of AttackMode upon grab
|
||||
if is_attack_mode:
|
||||
is_attack_mode = false
|
||||
# Disposed of Charged Strike upon grab
|
||||
if is_charged_strike:
|
||||
is_charged_strike = false
|
||||
|
||||
SfxManager.play("pick_up_tekton_roaming")
|
||||
play_pickup_animation()
|
||||
@@ -2558,38 +2600,17 @@ func sync_drop_tekton():
|
||||
|
||||
print("[Player %s] Dropped Tekton at %s" % [name, current_position])
|
||||
|
||||
# is_attack_mode is already declared at top of file (or inherited?)
|
||||
# Keeping is_knock_mode here for now or moving it up would be better, but let's just fix the error first.
|
||||
var is_knock_mode: bool = false:
|
||||
set(value):
|
||||
if is_knock_mode == value: return
|
||||
is_knock_mode = value
|
||||
if is_knock_mode:
|
||||
attack_mode_timer = MAX_ATTACK_MODE_TIME
|
||||
_refresh_player_visuals()
|
||||
|
||||
func enter_attack_mode():
|
||||
|
||||
func enter_charged_strike():
|
||||
if not is_multiplayer_authority(): return
|
||||
|
||||
if is_invisible:
|
||||
NotificationManager.send_message(self , "Cannot enter Attack Mode while in Ghost mode!", NotificationManager.MessageType.WARNING)
|
||||
NotificationManager.send_message(self , "Cannot use Charged Strike while in Ghost mode!", NotificationManager.MessageType.WARNING)
|
||||
return
|
||||
|
||||
is_attack_mode = true
|
||||
is_knock_mode = false # Mutually exclusive
|
||||
NotificationManager.send_message(self , "Attack Mode ACTIVATED (Red)", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
|
||||
func enter_knock_mode():
|
||||
if not is_multiplayer_authority(): return
|
||||
|
||||
if is_invisible:
|
||||
NotificationManager.send_message(self , "Cannot enter Knock Mode while in Ghost mode!", NotificationManager.MessageType.WARNING)
|
||||
return
|
||||
|
||||
is_knock_mode = true
|
||||
is_attack_mode = false # Mutually exclusive
|
||||
NotificationManager.send_message(self , "Knock Mode ACTIVATED (Yellow)", NotificationManager.MessageType.POWERUP)
|
||||
is_charged_strike = true
|
||||
NotificationManager.send_message(self , "Charged Strike ACTIVATED (Red)", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
|
||||
func update_active_player_indicator():
|
||||
@@ -2619,8 +2640,8 @@ func knock_tekton():
|
||||
if not is_multiplayer_authority() or is_frozen or is_stop_frozen or is_invisible:
|
||||
return
|
||||
|
||||
# Requirement: Full Powerup Bar (or we are already in knock mode)
|
||||
if not is_knock_mode and (not powerup_manager or not powerup_manager.can_use_special()):
|
||||
# Requirement: Full Powerup Bar (or we are already charged)
|
||||
if not is_charged_strike and (not powerup_manager or not powerup_manager.can_use_special()):
|
||||
NotificationManager.send_message(self , "Need Full Boost to Knock!", NotificationManager.MessageType.WARNING)
|
||||
return
|
||||
|
||||
@@ -2633,8 +2654,8 @@ func knock_tekton():
|
||||
if is_multiplayer_authority():
|
||||
rpc("sync_knock_tekton", tekton.get_path())
|
||||
|
||||
# Reset Knock Mode after successful hit
|
||||
is_knock_mode = false
|
||||
# Reset Charged Strike Mode after successful hit
|
||||
is_charged_strike = false
|
||||
NotificationManager.send_message(self , "Knock Successful!", NotificationManager.MessageType.POWERUP)
|
||||
update_active_player_indicator()
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user