235 lines
7.8 KiB
GDScript
235 lines
7.8 KiB
GDScript
extends Node
|
|
|
|
# PowerUpManager - Handles Boost Meter (Time-based logic)
|
|
# Note: Inventory logic is now in SpecialTilesManager + PlayerboardManager
|
|
|
|
const MAX_BOOST: float = 100.0 # Fills to 100. Activation requires FULL bar (>= 100).
|
|
const BASE_FILL_RATE: float = 10.0 # 10 points per second (10s to full) - user requested faster/simpler
|
|
|
|
var player: Node3D
|
|
var enhanced_gridmap: Node
|
|
var goal_manager: Node
|
|
|
|
# Progressive Difficulty Settings
|
|
const FILL_TIMES: Array = [2.0, 3.0, 4.0, 5.0, 6.0, 7.5, 8.5, 10.0] # Seconds to full for Lvl 1-8 (2s to 10s)
|
|
var current_level: int = 1
|
|
|
|
# Boost State
|
|
var current_boost: float = 0.0
|
|
|
|
# Alias for compatibility with BotStrategicPlanner
|
|
var current_points: float:
|
|
get:
|
|
return current_boost
|
|
set(value):
|
|
current_boost = value
|
|
|
|
# Also alias MAX_POINTS
|
|
const MAX_POINTS = MAX_BOOST
|
|
|
|
signal points_changed(current: int, max_points: int) # Reused for UI (int casting)
|
|
signal bar_filled()
|
|
signal boost_reset()
|
|
|
|
func initialize(p_player: Node3D, p_gridmap: Node):
|
|
player = p_player
|
|
enhanced_gridmap = p_gridmap
|
|
|
|
# Find GoalManager
|
|
var main = player.get_tree().get_root().get_node_or_null("Main")
|
|
if main:
|
|
goal_manager = main.get_node_or_null("GoalManager")
|
|
|
|
set_process(true)
|
|
|
|
func _process(delta):
|
|
if not multiplayer.has_multiplayer_peer():
|
|
return
|
|
if not is_instance_valid(player) or not player.is_multiplayer_authority():
|
|
return
|
|
|
|
# Only fill if not full
|
|
if current_boost < MAX_BOOST:
|
|
var multiplier = 1.0
|
|
if goal_manager:
|
|
# Use authority ID for lookup
|
|
multiplier = goal_manager.get_boost_multiplier(player.get_multiplayer_authority())
|
|
|
|
# Calculate Dynamic Fill Rate based on Level
|
|
# Level 1 (Index 0) -> Level 8 (Index 7)
|
|
var level_idx = clamp(current_level - 1, 0, FILL_TIMES.size() - 1)
|
|
var fill_time = FILL_TIMES[level_idx]
|
|
var current_rate = MAX_BOOST / fill_time # e.g. 100/10 = 10/s, 100/60 = 1.66/s
|
|
|
|
current_boost += current_rate * multiplier * delta
|
|
current_boost = min(current_boost, MAX_BOOST)
|
|
|
|
# Update UI (Cast to int for compatibility with existing UI slider/bar)
|
|
emit_signal("points_changed", int(current_boost), int(MAX_BOOST))
|
|
|
|
if current_boost >= MAX_BOOST:
|
|
_on_boost_full()
|
|
|
|
func _on_boost_full():
|
|
# player.is_attack_mode = true # Removed auto-activate
|
|
emit_signal("bar_filled")
|
|
NotificationManager.send_message(player, NotificationManager.MESSAGES.ATTACK_MODE_READY, NotificationManager.MessageType.POWERUP)
|
|
print("[PowerUp] Player %s Boost Full! Ready for Attack Mode." % player.name)
|
|
|
|
if player.is_multiplayer_authority() and player.has_method("can_rpc") and player.can_rpc():
|
|
rpc("sync_boost", current_boost)
|
|
|
|
func reset_boost():
|
|
current_boost = 0.0
|
|
# player.is_attack_mode = false # Do not auto-disable here, managed by usage
|
|
emit_signal("points_changed", 0, int(MAX_BOOST))
|
|
emit_signal("boost_reset")
|
|
|
|
if player.is_multiplayer_authority() and player.has_method("can_rpc") and player.can_rpc():
|
|
rpc("sync_boost", 0.0)
|
|
|
|
# =============================================================================
|
|
# Sync
|
|
# =============================================================================
|
|
|
|
@rpc("any_peer", "call_local", "reliable")
|
|
func sync_boost(value: float):
|
|
current_boost = value
|
|
emit_signal("points_changed", int(current_boost), int(MAX_BOOST))
|
|
|
|
# Client-side Attack Mode visual check (?)
|
|
if current_boost >= MAX_BOOST:
|
|
# Could trigger visual effect here
|
|
pass
|
|
|
|
@rpc("authority", "call_local", "reliable")
|
|
func sync_boost_level(level: int):
|
|
current_level = level
|
|
var level_idx = clamp(current_level - 1, 0, FILL_TIMES.size() - 1)
|
|
print("[PowerUp] Difficulty synced: Level %d (Fill Time: %.1fs)" % [current_level, FILL_TIMES[level_idx]])
|
|
|
|
|
|
# =============================================================================
|
|
# Getters
|
|
# =============================================================================
|
|
|
|
func get_points() -> int:
|
|
return int(current_boost)
|
|
|
|
func get_max_points() -> int:
|
|
return int(MAX_BOOST)
|
|
|
|
func get_fill_percentage() -> float:
|
|
return current_boost / MAX_BOOST
|
|
|
|
func get_time_until_full() -> float:
|
|
if current_boost >= MAX_BOOST:
|
|
return 0.0
|
|
|
|
# Calculate remaining points needed
|
|
var remaining = MAX_BOOST - current_boost
|
|
|
|
# Get current fill rate
|
|
var level_idx = clamp(current_level - 1, 0, FILL_TIMES.size() - 1)
|
|
var fill_time = FILL_TIMES[level_idx]
|
|
var current_rate = MAX_BOOST / fill_time
|
|
|
|
if current_rate <= 0: return 0.0
|
|
|
|
return remaining / current_rate
|
|
|
|
func get_bars() -> int:
|
|
"""Returns the number of filled segments (0-4)."""
|
|
# Each bar is 25 points (100 / 4)
|
|
return int(current_boost / 25.0)
|
|
|
|
func can_use_special() -> bool:
|
|
# Use small epsilon for float comparison to avoid "99.999" issues
|
|
return current_boost >= (MAX_BOOST - 0.1)
|
|
|
|
func enable_attack_mode():
|
|
"""Enable Attack Mode without consuming boost."""
|
|
player.is_attack_mode = true
|
|
NotificationManager.send_message(player, NotificationManager.MESSAGES.ATTACK_MODE_READY, NotificationManager.MessageType.POWERUP)
|
|
print("[PowerUp] Attack Mode Enabled (Free).")
|
|
|
|
func use_special_effect() -> bool:
|
|
if not can_use_special():
|
|
return false
|
|
|
|
# Restriction: Cannot use attack mode while carrying a Tekton or in Ghost mode
|
|
if player.is_carrying_tekton:
|
|
NotificationManager.send_message(player, "Cannot enter Attack Mode while carrying a Tekton!", NotificationManager.MessageType.WARNING)
|
|
return false
|
|
|
|
if player.get("is_invisible"):
|
|
NotificationManager.send_message(player, "Cannot enter Attack Mode while in Ghost mode!", NotificationManager.MessageType.WARNING)
|
|
return false
|
|
|
|
# Enable Attack Mode explicitly
|
|
player.is_attack_mode = true
|
|
# Do NOT consume boost here. Boost acts as "fuel" for the attacks.
|
|
# Notification check handled by caller or signal
|
|
return true
|
|
|
|
func consume_boost(amount: float):
|
|
"""Consume a specific amount of boost."""
|
|
current_boost -= amount
|
|
if current_boost <= 0:
|
|
current_boost = 0.0
|
|
player.is_attack_mode = false # Out of fuel
|
|
emit_signal("boost_reset") # UI update for empty
|
|
print("[PowerUp] Boost depleted. Attack Mode OFF.")
|
|
|
|
emit_signal("points_changed", int(current_boost), int(MAX_BOOST))
|
|
|
|
if player.is_multiplayer_authority() and player.has_method("can_rpc") and player.can_rpc():
|
|
rpc("sync_boost", current_boost)
|
|
|
|
func spawn_boost_reward() -> bool:
|
|
"""Alternative Boost Usage: Spawn a PowerUp Tile properly.
|
|
Now requires carrying a Tekton (E shortcut)."""
|
|
if not player.is_carrying_tekton:
|
|
return false
|
|
|
|
if player.special_tiles_manager and player.special_tiles_manager.has_method("spawn_powerups_around"):
|
|
# Trigger the synced floor spawn animation
|
|
if player.has_method("rpc"):
|
|
player.rpc("sync_floor_spawn_animation")
|
|
elif player.has_method("play_floor_spawn"):
|
|
player.play_floor_spawn()
|
|
|
|
# Spawn tiles (Stop N Go gets only common, Free Mode gets 60/40 ratio)
|
|
player.special_tiles_manager.spawn_powerups_around(player.current_position, true, false, true)
|
|
|
|
# Drop the Tekton after spawning
|
|
if player.has_method("drop_tekton"):
|
|
player.drop_tekton()
|
|
|
|
reset_boost()
|
|
print("[PowerUp] %s used Tekton to SPAWN TILES and consumed Boost." % player.name)
|
|
return true
|
|
|
|
return false
|
|
|
|
func acquire_smash_bonus():
|
|
current_boost += 25.0 # Add 25% boost
|
|
current_boost = min(current_boost, MAX_BOOST)
|
|
emit_signal("points_changed", int(current_boost), int(MAX_BOOST))
|
|
if current_boost >= MAX_BOOST:
|
|
_on_boost_full()
|
|
|
|
func add_goal_completion_reward():
|
|
"""Called when player completes a goal board. Increases difficulty level."""
|
|
current_level += 1
|
|
if current_level > 8:
|
|
current_level = 8
|
|
|
|
var level_idx = clamp(current_level - 1, 0, FILL_TIMES.size() - 1)
|
|
print("[PowerUp] Player %s Completed Goal. Boost Level Up! Now: %d (Fill Time: %.1fs)" % [player.name, current_level, FILL_TIMES[level_idx]])
|
|
|
|
# Optional: Notify user of difficulty increase?
|
|
|
|
if multiplayer.is_server() and player.has_method("can_rpc") and player.can_rpc():
|
|
rpc("sync_boost_level", current_level)
|