Working airplane ability revamp
This commit is contained in:
@@ -1,5 +1,4 @@
|
||||
using AppleHills.Core.Settings;
|
||||
using Core;
|
||||
using Core;
|
||||
using Core.Settings;
|
||||
using Minigames.Airplane.Data;
|
||||
|
||||
@@ -38,7 +37,9 @@ namespace Minigames.Airplane.Abilities
|
||||
config.abilityIcon,
|
||||
config.cooldownDuration,
|
||||
config.jetSpeed,
|
||||
config.jetAngle
|
||||
config.jetAngle,
|
||||
config.maxActiveDuration,
|
||||
config.cooldownMultiplier
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,7 +62,8 @@ namespace Minigames.Airplane.Abilities
|
||||
config.abilityIcon,
|
||||
config.cooldownDuration,
|
||||
config.dropForce,
|
||||
config.dropDistance,
|
||||
config.maxActiveDuration,
|
||||
config.cooldownMultiplier,
|
||||
config.zeroHorizontalVelocity
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,10 @@ namespace Minigames.Airplane.Abilities
|
||||
protected readonly bool canReuse;
|
||||
protected bool showDebugLogs;
|
||||
|
||||
// Active duration configuration
|
||||
protected readonly float maxActiveDuration;
|
||||
protected readonly bool hasActiveDuration;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
@@ -28,13 +32,15 @@ namespace Minigames.Airplane.Abilities
|
||||
/// <summary>
|
||||
/// Base constructor for abilities. Called by subclasses.
|
||||
/// </summary>
|
||||
protected BaseAirplaneAbility(string name, Sprite icon, float cooldown, bool reusable = true)
|
||||
protected BaseAirplaneAbility(string name, Sprite icon, float cooldown, bool reusable = true, float activeDuration = 0f)
|
||||
{
|
||||
abilityName = name;
|
||||
abilityIcon = icon;
|
||||
cooldownDuration = cooldown;
|
||||
canReuse = reusable;
|
||||
showDebugLogs = false;
|
||||
maxActiveDuration = activeDuration;
|
||||
hasActiveDuration = activeDuration > 0f;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -45,6 +51,8 @@ namespace Minigames.Airplane.Abilities
|
||||
public Sprite AbilityIcon => abilityIcon;
|
||||
public float CooldownDuration => cooldownDuration;
|
||||
public bool CanReuse => canReuse;
|
||||
public bool HasActiveDuration => hasActiveDuration;
|
||||
public float MaxActiveDuration => maxActiveDuration;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -54,12 +62,22 @@ namespace Minigames.Airplane.Abilities
|
||||
protected bool isActive;
|
||||
protected bool isOnCooldown;
|
||||
protected float cooldownTimer;
|
||||
protected float initialCooldownDuration; // Track initial cooldown for fill calculation
|
||||
|
||||
// Active duration tracking
|
||||
protected float activeDurationTimer;
|
||||
protected float activeDurationUsed; // Track how long ability was active
|
||||
|
||||
public bool IsActive => isActive;
|
||||
public bool IsOnCooldown => isOnCooldown;
|
||||
public float CooldownRemaining => cooldownTimer;
|
||||
public float InitialCooldownDuration => initialCooldownDuration;
|
||||
public bool CanActivate => !isOnCooldown && !isActive && currentAirplane != null && currentAirplane.IsFlying;
|
||||
|
||||
// Active duration state
|
||||
public float ActiveDurationRemaining => activeDurationTimer;
|
||||
public float ActiveProgress => hasActiveDuration && maxActiveDuration > 0f ? activeDurationTimer / maxActiveDuration : 0f;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
@@ -67,6 +85,7 @@ namespace Minigames.Airplane.Abilities
|
||||
public event Action<BaseAirplaneAbility> OnAbilityActivated;
|
||||
public event Action<BaseAirplaneAbility> OnAbilityDeactivated;
|
||||
public event Action<float, float> OnCooldownChanged; // (remaining, total)
|
||||
public event Action<float, float> OnActiveDurationChanged; // (remaining, total)
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -82,6 +101,9 @@ namespace Minigames.Airplane.Abilities
|
||||
isActive = false;
|
||||
isOnCooldown = false;
|
||||
cooldownTimer = 0f;
|
||||
initialCooldownDuration = 0f;
|
||||
activeDurationTimer = 0f;
|
||||
activeDurationUsed = 0f;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
@@ -109,10 +131,37 @@ namespace Minigames.Airplane.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
OnCooldownChanged?.Invoke(cooldownTimer, cooldownDuration);
|
||||
OnCooldownChanged?.Invoke(cooldownTimer, initialCooldownDuration);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update active duration timer. Called every frame by ability manager when ability is active.
|
||||
/// Auto-deactivates when duration expires.
|
||||
/// </summary>
|
||||
public virtual void UpdateActiveDuration(float deltaTime)
|
||||
{
|
||||
if (!isActive || !hasActiveDuration) return;
|
||||
|
||||
activeDurationTimer -= deltaTime;
|
||||
activeDurationUsed += deltaTime;
|
||||
|
||||
if (activeDurationTimer <= 0f)
|
||||
{
|
||||
activeDurationTimer = 0f;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[{abilityName}] Active duration expired, auto-deactivating");
|
||||
}
|
||||
|
||||
// Auto-deactivate when duration expires
|
||||
Deactivate();
|
||||
}
|
||||
|
||||
OnActiveDurationChanged?.Invoke(activeDurationTimer, maxActiveDuration);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cleanup when airplane is destroyed or flight ends.
|
||||
/// </summary>
|
||||
@@ -127,6 +176,9 @@ namespace Minigames.Airplane.Abilities
|
||||
isActive = false;
|
||||
isOnCooldown = false;
|
||||
cooldownTimer = 0f;
|
||||
initialCooldownDuration = 0f;
|
||||
activeDurationTimer = 0f;
|
||||
activeDurationUsed = 0f;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
@@ -155,12 +207,26 @@ namespace Minigames.Airplane.Abilities
|
||||
isActive = false;
|
||||
OnAbilityDeactivated?.Invoke(this);
|
||||
|
||||
// Calculate and start dynamic cooldown
|
||||
float dynamicCooldown = CalculateDynamicCooldown();
|
||||
StartCooldown(dynamicCooldown);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[{abilityName}] Deactivated");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate cooldown duration based on ability usage.
|
||||
/// Override in subclasses for custom dynamic cooldown logic.
|
||||
/// Default: returns base cooldown duration.
|
||||
/// </summary>
|
||||
protected virtual float CalculateDynamicCooldown()
|
||||
{
|
||||
return cooldownDuration;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Protected Helpers
|
||||
@@ -180,6 +246,14 @@ namespace Minigames.Airplane.Abilities
|
||||
}
|
||||
|
||||
isActive = true;
|
||||
|
||||
// Reset active duration timer if ability has active duration
|
||||
if (hasActiveDuration)
|
||||
{
|
||||
activeDurationTimer = maxActiveDuration;
|
||||
activeDurationUsed = 0f;
|
||||
}
|
||||
|
||||
OnAbilityActivated?.Invoke(this);
|
||||
|
||||
if (showDebugLogs)
|
||||
@@ -191,15 +265,16 @@ namespace Minigames.Airplane.Abilities
|
||||
/// <summary>
|
||||
/// Start cooldown timer (called by subclasses after execution).
|
||||
/// </summary>
|
||||
protected virtual void StartCooldown()
|
||||
protected virtual void StartCooldown(float? customCooldown = null)
|
||||
{
|
||||
isOnCooldown = true;
|
||||
cooldownTimer = cooldownDuration;
|
||||
OnCooldownChanged?.Invoke(cooldownTimer, cooldownDuration);
|
||||
cooldownTimer = customCooldown ?? cooldownDuration;
|
||||
initialCooldownDuration = cooldownTimer; // Store the initial cooldown value
|
||||
OnCooldownChanged?.Invoke(cooldownTimer, initialCooldownDuration);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[{abilityName}] Cooldown started: {cooldownDuration}s");
|
||||
Logging.Debug($"[{abilityName}] Cooldown started: {cooldownTimer}s");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Core;
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.Airplane.Abilities
|
||||
@@ -23,7 +23,7 @@ namespace Minigames.Airplane.Abilities
|
||||
/// Create bobbing ability with configuration from settings.
|
||||
/// </summary>
|
||||
public BobbingAbility(string name, Sprite icon, float cooldown, Vector2 force)
|
||||
: base(name, icon, cooldown)
|
||||
: base(name, icon, cooldown, reusable: true, activeDuration: 0f)
|
||||
{
|
||||
bobForce = force;
|
||||
}
|
||||
@@ -52,9 +52,8 @@ namespace Minigames.Airplane.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
// Instant ability - deactivate immediately and start cooldown
|
||||
// Instant ability - deactivate immediately (base.Deactivate starts cooldown)
|
||||
base.Deactivate();
|
||||
StartCooldown();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
using System.Collections;
|
||||
using Core;
|
||||
using Core;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Minigames.Airplane.Abilities
|
||||
{
|
||||
/// <summary>
|
||||
/// Drop Plane Ability: Swipe down to drop straight down.
|
||||
/// Sustained ability - drops for fixed duration/distance.
|
||||
/// Drop Plane Ability: Press to drop straight down with strong downward force.
|
||||
/// Sustained ability - active for up to N seconds, can be interrupted early.
|
||||
/// Cooldown is proportional to usage time (full duration = N*2 cooldown).
|
||||
/// Good for precision strikes on targets.
|
||||
/// Configuration loaded from settings at runtime.
|
||||
/// </summary>
|
||||
@@ -15,8 +15,8 @@ namespace Minigames.Airplane.Abilities
|
||||
#region Configuration
|
||||
|
||||
private readonly float dropForce;
|
||||
private readonly float dropDistance;
|
||||
private readonly bool zeroHorizontalVelocity;
|
||||
private readonly float cooldownMultiplier;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -25,12 +25,12 @@ namespace Minigames.Airplane.Abilities
|
||||
/// <summary>
|
||||
/// Create drop ability with configuration from settings.
|
||||
/// </summary>
|
||||
public DropAbility(string name, Sprite icon, float cooldown, float force, float distance, bool zeroHorizontal = true)
|
||||
: base(name, icon, cooldown)
|
||||
public DropAbility(string name, Sprite icon, float cooldown, float force, float maxActiveDuration, float cooldownMult, bool zeroHorizontal = true)
|
||||
: base(name, icon, cooldown, reusable: true, activeDuration: maxActiveDuration)
|
||||
{
|
||||
dropForce = force;
|
||||
dropDistance = distance;
|
||||
zeroHorizontalVelocity = zeroHorizontal;
|
||||
cooldownMultiplier = cooldownMult;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -38,8 +38,6 @@ namespace Minigames.Airplane.Abilities
|
||||
#region State
|
||||
|
||||
private float originalXVelocity;
|
||||
private Vector3 dropStartPosition;
|
||||
private Coroutine dropCoroutine;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -66,17 +64,11 @@ namespace Minigames.Airplane.Abilities
|
||||
|
||||
// Apply strong downward force
|
||||
rb.AddForce(Vector2.down * dropForce, ForceMode2D.Impulse);
|
||||
|
||||
// Track drop distance
|
||||
dropStartPosition = currentAirplane.transform.position;
|
||||
|
||||
// Start monitoring drop distance
|
||||
dropCoroutine = currentAirplane.StartCoroutine(MonitorDropDistance());
|
||||
}
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[DropAbility] Activated - Force: {dropForce}, Distance: {dropDistance}");
|
||||
Logging.Debug($"[DropAbility] Activated - Force: {dropForce}, Max Duration: {maxActiveDuration}s");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,13 +76,6 @@ namespace Minigames.Airplane.Abilities
|
||||
{
|
||||
if (!isActive) return;
|
||||
|
||||
// Stop monitoring
|
||||
if (dropCoroutine != null && currentAirplane != null)
|
||||
{
|
||||
currentAirplane.StopCoroutine(dropCoroutine);
|
||||
dropCoroutine = null;
|
||||
}
|
||||
|
||||
// Restore horizontal velocity (optional)
|
||||
if (currentAirplane != null)
|
||||
{
|
||||
@@ -102,36 +87,32 @@ namespace Minigames.Airplane.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
base.Deactivate();
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[DropAbility] Deactivating after {activeDurationUsed:F2}s usage");
|
||||
}
|
||||
|
||||
// Start cooldown
|
||||
StartCooldown();
|
||||
// Base.Deactivate() will call CalculateDynamicCooldown and start cooldown
|
||||
base.Deactivate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate dynamic cooldown based on usage time.
|
||||
/// Full duration (maxActiveDuration) = maxActiveDuration * cooldownMultiplier cooldown.
|
||||
/// Partial usage = proportional cooldown.
|
||||
/// </summary>
|
||||
protected override float CalculateDynamicCooldown()
|
||||
{
|
||||
// Calculate proportional cooldown: usedTime * multiplier
|
||||
// Example: 3s max, 2x multiplier -> full use = 6s cooldown, 1.5s use = 3s cooldown
|
||||
float dynamicCooldown = activeDurationUsed * cooldownMultiplier;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug("[DropAbility] Deactivated, cooldown started");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Drop Monitoring
|
||||
|
||||
private IEnumerator MonitorDropDistance()
|
||||
{
|
||||
while (isActive && currentAirplane != null)
|
||||
{
|
||||
float distanceDropped = Mathf.Abs(dropStartPosition.y - currentAirplane.transform.position.y);
|
||||
|
||||
if (distanceDropped >= dropDistance)
|
||||
{
|
||||
// Drop distance reached - deactivate
|
||||
Deactivate();
|
||||
yield break;
|
||||
}
|
||||
|
||||
yield return null;
|
||||
Logging.Debug($"[DropAbility] CalculateDynamicCooldown: used={activeDurationUsed:F2}s, multiplier={cooldownMultiplier}, result={dynamicCooldown:F2}s");
|
||||
}
|
||||
|
||||
return dynamicCooldown;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -13,6 +13,7 @@ namespace Minigames.Airplane.Abilities
|
||||
|
||||
private readonly float jetSpeed;
|
||||
private readonly float jetAngle;
|
||||
private readonly float cooldownMultiplier;
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -21,11 +22,12 @@ namespace Minigames.Airplane.Abilities
|
||||
/// <summary>
|
||||
/// Create jet ability with configuration from settings.
|
||||
/// </summary>
|
||||
public JetAbility(string name, Sprite icon, float cooldown, float speed, float angle)
|
||||
: base(name, icon, cooldown)
|
||||
public JetAbility(string name, Sprite icon, float cooldown, float speed, float angle, float maxActiveDuration, float cooldownMult)
|
||||
: base(name, icon, cooldown, reusable: true, activeDuration: maxActiveDuration)
|
||||
{
|
||||
jetSpeed = speed;
|
||||
jetAngle = angle;
|
||||
cooldownMultiplier = cooldownMult;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -87,15 +89,32 @@ namespace Minigames.Airplane.Abilities
|
||||
currentAirplane.RotateToVelocity = originalRotateToVelocity;
|
||||
}
|
||||
|
||||
base.Deactivate();
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[JetAbility] Deactivating after {activeDurationUsed:F2}s usage");
|
||||
}
|
||||
|
||||
// Start cooldown after deactivation
|
||||
StartCooldown();
|
||||
// Base.Deactivate() will call CalculateDynamicCooldown and start cooldown
|
||||
base.Deactivate();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculate dynamic cooldown based on usage time.
|
||||
/// Full duration (maxActiveDuration) = maxActiveDuration * cooldownMultiplier cooldown.
|
||||
/// Partial usage = proportional cooldown.
|
||||
/// </summary>
|
||||
protected override float CalculateDynamicCooldown()
|
||||
{
|
||||
// Calculate proportional cooldown: usedTime * multiplier
|
||||
// Example: 5s max, 2x multiplier -> full use = 10s cooldown, 2.5s use = 5s cooldown
|
||||
float dynamicCooldown = activeDurationUsed * cooldownMultiplier;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug("[JetAbility] Deactivated, cooldown started");
|
||||
Logging.Debug($"[JetAbility] CalculateDynamicCooldown: used={activeDurationUsed:F2}s, multiplier={cooldownMultiplier}, result={dynamicCooldown:F2}s");
|
||||
}
|
||||
|
||||
return dynamicCooldown;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -176,9 +176,16 @@ namespace Minigames.Airplane.Core
|
||||
transform.rotation = Quaternion.Euler(0, 0, angle);
|
||||
}
|
||||
|
||||
// Update ability cooldown
|
||||
// Update ability timers
|
||||
if (currentAbility != null)
|
||||
{
|
||||
// Update active duration if ability is active
|
||||
if (currentAbility.IsActive)
|
||||
{
|
||||
currentAbility.UpdateActiveDuration(Time.deltaTime);
|
||||
}
|
||||
|
||||
// Update cooldown if on cooldown
|
||||
currentAbility.UpdateCooldown(Time.deltaTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ namespace Minigames.Airplane.Core.Spawning
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the bounds of an object from its Renderer or Collider.
|
||||
/// Get the bounds of an object from its Collider or Renderer.
|
||||
/// Prioritizes 2D colliders first for accurate bounds, then falls back to Renderer.
|
||||
/// Searches child objects if no component is found on the root.
|
||||
/// </summary>
|
||||
public static Bounds GetObjectBounds(GameObject obj)
|
||||
@@ -146,14 +147,14 @@ namespace Minigames.Airplane.Core.Spawning
|
||||
return new Bounds(Vector3.zero, Vector3.one);
|
||||
}
|
||||
|
||||
// Try renderer first (most common for sprites)
|
||||
Renderer objRenderer = obj.GetComponentInChildren<Renderer>();
|
||||
if (objRenderer != null) return objRenderer.bounds;
|
||||
|
||||
// Try 2D collider
|
||||
// Try 2D collider first (most accurate for ground snapping)
|
||||
Collider2D objCollider2D = obj.GetComponentInChildren<Collider2D>();
|
||||
if (objCollider2D != null) return objCollider2D.bounds;
|
||||
|
||||
// Try renderer (common for sprites without colliders)
|
||||
Renderer objRenderer = obj.GetComponentInChildren<Renderer>();
|
||||
if (objRenderer != null) return objRenderer.bounds;
|
||||
|
||||
// Try 3D collider
|
||||
Collider objCollider3D = obj.GetComponentInChildren<Collider>();
|
||||
if (objCollider3D != null) return objCollider3D.bounds;
|
||||
@@ -165,6 +166,7 @@ namespace Minigames.Airplane.Core.Spawning
|
||||
/// <summary>
|
||||
/// Draw debug visualization for object bounds after positioning.
|
||||
/// Shows the actual bounds in world space at the object's final position.
|
||||
/// Matches GetObjectBounds priority: 2D Collider → Renderer → 3D Collider.
|
||||
/// </summary>
|
||||
public static void DrawObjectBoundsDebug(GameObject obj, SpawnPositionMode mode)
|
||||
{
|
||||
@@ -173,20 +175,20 @@ namespace Minigames.Airplane.Core.Spawning
|
||||
Bounds bounds = default;
|
||||
string sourceType = "none";
|
||||
|
||||
// Get bounds from whatever component is available
|
||||
Renderer objRenderer = obj.GetComponentInChildren<Renderer>();
|
||||
if (objRenderer != null)
|
||||
// Get bounds from whatever component is available (matching GetObjectBounds priority)
|
||||
Collider2D objCollider2D = obj.GetComponentInChildren<Collider2D>();
|
||||
if (objCollider2D != null)
|
||||
{
|
||||
bounds = objRenderer.bounds;
|
||||
sourceType = "Renderer";
|
||||
bounds = objCollider2D.bounds;
|
||||
sourceType = "Collider2D";
|
||||
}
|
||||
else
|
||||
{
|
||||
Collider2D objCollider2D = obj.GetComponentInChildren<Collider2D>();
|
||||
if (objCollider2D != null)
|
||||
Renderer objRenderer = obj.GetComponentInChildren<Renderer>();
|
||||
if (objRenderer != null)
|
||||
{
|
||||
bounds = objCollider2D.bounds;
|
||||
sourceType = "Collider2D";
|
||||
bounds = objRenderer.bounds;
|
||||
sourceType = "Renderer";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -15,7 +15,7 @@ namespace Minigames.Airplane.Data
|
||||
[Tooltip("Icon for ability button")]
|
||||
public Sprite abilityIcon;
|
||||
|
||||
[Tooltip("Cooldown duration in seconds")]
|
||||
[Tooltip("Base cooldown duration in seconds (multiplied by cooldownMultiplier based on usage)")]
|
||||
public float cooldownDuration = 5f;
|
||||
|
||||
[Tooltip("Speed while ability is active")]
|
||||
@@ -23,6 +23,13 @@ namespace Minigames.Airplane.Data
|
||||
|
||||
[Tooltip("Direction angle (0 = right, 90 = up)")]
|
||||
public float jetAngle = 0f;
|
||||
|
||||
[Header("Active Duration Settings")]
|
||||
[Tooltip("Maximum duration ability can be active (seconds). Full usage = maxActiveDuration * cooldownMultiplier cooldown")]
|
||||
public float maxActiveDuration = 5f;
|
||||
|
||||
[Tooltip("Cooldown multiplier based on usage time. Full duration = cooldownMultiplier * maxActiveDuration cooldown")]
|
||||
public float cooldownMultiplier = 2f;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -58,14 +65,18 @@ namespace Minigames.Airplane.Data
|
||||
[Tooltip("Icon for ability button")]
|
||||
public Sprite abilityIcon;
|
||||
|
||||
[Tooltip("Cooldown duration in seconds")]
|
||||
[Tooltip("Base cooldown duration in seconds (multiplied by cooldownMultiplier based on usage)")]
|
||||
public float cooldownDuration = 4f;
|
||||
|
||||
[Tooltip("Downward force applied")]
|
||||
public float dropForce = 20f;
|
||||
|
||||
[Tooltip("Distance to drop before returning to normal flight")]
|
||||
public float dropDistance = 5f;
|
||||
[Header("Active Duration Settings")]
|
||||
[Tooltip("Maximum duration ability can be active (seconds). Full usage = maxActiveDuration * cooldownMultiplier cooldown")]
|
||||
public float maxActiveDuration = 3f;
|
||||
|
||||
[Tooltip("Cooldown multiplier based on usage time. Full duration = cooldownMultiplier * maxActiveDuration cooldown")]
|
||||
public float cooldownMultiplier = 2f;
|
||||
|
||||
[Tooltip("Should horizontal velocity be zeroed during drop?")]
|
||||
public bool zeroHorizontalVelocity = true;
|
||||
|
||||
@@ -23,6 +23,10 @@ namespace Minigames.Airplane.UI
|
||||
[SerializeField] private Image cooldownFill;
|
||||
[SerializeField] private TextMeshProUGUI cooldownText;
|
||||
|
||||
[Header("Active State Visual")]
|
||||
[Tooltip("Visual element shown when ability is active (e.g., glowing border, pulsing overlay)")]
|
||||
[SerializeField] private GameObject activeStateVisual;
|
||||
|
||||
[Header("Debug")]
|
||||
[SerializeField] private bool showDebugLogs;
|
||||
|
||||
@@ -46,6 +50,12 @@ namespace Minigames.Airplane.UI
|
||||
button.onClick.AddListener(OnButtonClick);
|
||||
}
|
||||
|
||||
// Hide active state visual by default
|
||||
if (activeStateVisual != null)
|
||||
{
|
||||
activeStateVisual.SetActive(false);
|
||||
}
|
||||
|
||||
// Hide by default
|
||||
gameObject.SetActive(false);
|
||||
}
|
||||
@@ -54,11 +64,29 @@ namespace Minigames.Airplane.UI
|
||||
{
|
||||
if (currentAbility == null) return;
|
||||
|
||||
// Update cooldown display
|
||||
if (currentAbility.IsOnCooldown)
|
||||
// Priority 1: Show active duration if ability is active
|
||||
if (currentAbility.IsActive && currentAbility.HasActiveDuration)
|
||||
{
|
||||
// Fill starts at 1 and reduces to 0 over cooldown duration
|
||||
float fillAmount = currentAbility.CooldownRemaining / currentAbility.CooldownDuration;
|
||||
// NO fill during active duration - only text and active visual
|
||||
if (cooldownFill != null)
|
||||
{
|
||||
cooldownFill.fillAmount = 0f;
|
||||
}
|
||||
|
||||
// Show remaining active time
|
||||
if (cooldownText != null)
|
||||
{
|
||||
cooldownText.text = $"{currentAbility.ActiveDurationRemaining:F1}s";
|
||||
}
|
||||
}
|
||||
// Priority 2: Show cooldown if on cooldown
|
||||
else if (currentAbility.IsOnCooldown)
|
||||
{
|
||||
// Fill always starts at 1 (full) and reduces to 0, regardless of actual cooldown duration
|
||||
float fillAmount = currentAbility.InitialCooldownDuration > 0f
|
||||
? currentAbility.CooldownRemaining / currentAbility.InitialCooldownDuration
|
||||
: 0f;
|
||||
|
||||
if (cooldownFill != null)
|
||||
{
|
||||
cooldownFill.fillAmount = fillAmount;
|
||||
@@ -72,7 +100,7 @@ namespace Minigames.Airplane.UI
|
||||
}
|
||||
else
|
||||
{
|
||||
// Cooldown complete - fill at 0, no text
|
||||
// Normal state - fill at 0, no text
|
||||
if (cooldownFill != null)
|
||||
cooldownFill.fillAmount = 0f;
|
||||
|
||||
@@ -89,6 +117,7 @@ namespace Minigames.Airplane.UI
|
||||
currentAbility.OnAbilityActivated -= HandleAbilityActivated;
|
||||
currentAbility.OnAbilityDeactivated -= HandleAbilityDeactivated;
|
||||
currentAbility.OnCooldownChanged -= HandleCooldownChanged;
|
||||
currentAbility.OnActiveDurationChanged -= HandleActiveDurationChanged;
|
||||
}
|
||||
|
||||
// Unregister from input system
|
||||
@@ -129,8 +158,8 @@ namespace Minigames.Airplane.UI
|
||||
cooldownText.text = "";
|
||||
}
|
||||
|
||||
// Check if this is a hold ability (Jet)
|
||||
isHoldAbility = ability is JetAbility;
|
||||
// Check if this is a hold ability (Jet or Drop - abilities with active duration that can be interrupted)
|
||||
isHoldAbility = ability is JetAbility || ability is DropAbility;
|
||||
|
||||
// Subscribe to ability events
|
||||
if (ability != null)
|
||||
@@ -138,6 +167,7 @@ namespace Minigames.Airplane.UI
|
||||
ability.OnAbilityActivated += HandleAbilityActivated;
|
||||
ability.OnAbilityDeactivated += HandleAbilityDeactivated;
|
||||
ability.OnCooldownChanged += HandleCooldownChanged;
|
||||
ability.OnActiveDurationChanged += HandleActiveDurationChanged;
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
@@ -164,6 +194,7 @@ namespace Minigames.Airplane.UI
|
||||
currentAbility.OnAbilityActivated -= HandleAbilityActivated;
|
||||
currentAbility.OnAbilityDeactivated -= HandleAbilityDeactivated;
|
||||
currentAbility.OnCooldownChanged -= HandleCooldownChanged;
|
||||
currentAbility.OnActiveDurationChanged -= HandleActiveDurationChanged;
|
||||
}
|
||||
|
||||
// Unregister from input system
|
||||
@@ -172,6 +203,9 @@ namespace Minigames.Airplane.UI
|
||||
InputManager.Instance.UnregisterOverrideConsumer(this);
|
||||
}
|
||||
|
||||
// Hide active state visual
|
||||
HideActiveState();
|
||||
|
||||
currentAbility = null;
|
||||
currentAirplane = null;
|
||||
isHolding = false;
|
||||
@@ -216,6 +250,12 @@ namespace Minigames.Airplane.UI
|
||||
|
||||
private void HandleAbilityActivated(BaseAirplaneAbility ability)
|
||||
{
|
||||
// Show active state visual if ability has active duration
|
||||
if (ability.HasActiveDuration)
|
||||
{
|
||||
ShowActiveState();
|
||||
}
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[AirplaneAbilityButton] Ability activated: {ability.AbilityName}");
|
||||
@@ -224,6 +264,9 @@ namespace Minigames.Airplane.UI
|
||||
|
||||
private void HandleAbilityDeactivated(BaseAirplaneAbility ability)
|
||||
{
|
||||
// Hide active state visual
|
||||
HideActiveState();
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[AirplaneAbilityButton] Ability deactivated: {ability.AbilityName}");
|
||||
@@ -249,6 +292,14 @@ namespace Minigames.Airplane.UI
|
||||
}
|
||||
}
|
||||
|
||||
private void HandleActiveDurationChanged(float remaining, float total)
|
||||
{
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug($"[AirplaneAbilityButton] OnActiveDurationChanged: remaining={remaining:F2}, total={total:F2}");
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region ITouchInputConsumer Implementation
|
||||
@@ -295,6 +346,43 @@ namespace Minigames.Airplane.UI
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Active State Visual API
|
||||
|
||||
/// <summary>
|
||||
/// Show active state visual (placeholder API).
|
||||
/// Override or assign activeStateVisual GameObject in inspector for custom visuals.
|
||||
/// </summary>
|
||||
private void ShowActiveState()
|
||||
{
|
||||
if (activeStateVisual != null)
|
||||
{
|
||||
activeStateVisual.SetActive(true);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug("[AirplaneAbilityButton] Active state visual shown");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hide active state visual (placeholder API).
|
||||
/// </summary>
|
||||
private void HideActiveState()
|
||||
{
|
||||
if (activeStateVisual != null)
|
||||
{
|
||||
activeStateVisual.SetActive(false);
|
||||
|
||||
if (showDebugLogs)
|
||||
{
|
||||
Logging.Debug("[AirplaneAbilityButton] Active state visual hidden");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user