Working MVP

This commit is contained in:
Michal Pikulski
2025-12-03 22:17:12 +01:00
parent d5ab69d944
commit 0b8d2a279f
58 changed files with 11037 additions and 1299 deletions

View File

@@ -16,13 +16,6 @@ namespace Minigames.FortFight.Projectiles
{
#region Inspector Properties
[Header("Projectile Stats")]
[Tooltip("Base damage dealt on impact")]
[SerializeField] protected float damage = 20f;
[Tooltip("Mass for physics (affects trajectory)")]
[SerializeField] protected float mass = 1f;
[Header("Visuals")]
[Tooltip("Sprite renderer for projectile")]
[SerializeField] protected SpriteRenderer spriteRenderer;
@@ -54,7 +47,10 @@ namespace Minigames.FortFight.Projectiles
#region Properties
public float Damage => damage;
public float Damage { get; protected set; }
public float Mass { get; protected set; }
public Data.ProjectileType ProjectileType { get; protected set; }
public bool IsLaunched { get; protected set; }
public bool AbilityActivated { get; protected set; }
public Vector2 LaunchDirection { get; protected set; }
@@ -71,10 +67,59 @@ namespace Minigames.FortFight.Projectiles
#region Lifecycle
/// <summary>
/// Initialize the projectile with its type and load stats from settings.
/// Must be called after instantiation, before Launch.
/// </summary>
public void Initialize(Data.ProjectileType projectileType)
{
ProjectileType = projectileType;
// Load damage and mass from settings
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
if (settings != null)
{
var config = settings.GetProjectileConfig(projectileType);
if (config != null)
{
Damage = config.damage;
Mass = config.mass;
// Update rigidbody mass if already initialized
if (rb2D != null)
{
rb2D.mass = Mass;
}
Logging.Debug($"[ProjectileBase] Initialized {projectileType} - Damage: {Damage}, Mass: {Mass}");
}
else
{
Logging.Warning($"[ProjectileBase] No config found for {projectileType}, using defaults");
Damage = 20f;
Mass = 1f;
}
}
else
{
Logging.Warning($"[ProjectileBase] Settings not found, using default damage and mass");
Damage = 20f;
Mass = 1f;
}
}
internal override void OnManagedAwake()
{
base.OnManagedAwake();
// Automatically assign projectile to correct layer from settings
var settings = GameManager.GetSettingsObject<AppleHills.Core.Settings.IFortFightSettings>();
if (settings != null && settings.ProjectileLayer >= 0 && gameObject.layer != settings.ProjectileLayer)
{
gameObject.layer = settings.ProjectileLayer;
Logging.Debug($"[ProjectileBase] Assigned {gameObject.name} to layer {LayerMask.LayerToName(settings.ProjectileLayer)}");
}
// Cache components
rb2D = GetComponent<Rigidbody2D>();
projectileCollider = GetComponent<Collider2D>();
@@ -84,11 +129,18 @@ namespace Minigames.FortFight.Projectiles
spriteRenderer = GetComponent<SpriteRenderer>();
}
// Configure rigidbody
// Configure rigidbody (mass will be set by Initialize if called, otherwise use defaults)
if (rb2D != null)
{
rb2D.mass = mass;
rb2D.gravityScale = 1f;
// If Initialize hasn't been called yet, use default mass
if (Mass == 0f)
{
Mass = 1f;
Damage = 20f;
}
rb2D.mass = Mass;
rb2D.gravityScale = settings?.ProjectileGravityScale ?? 1f;
rb2D.collisionDetectionMode = CollisionDetectionMode2D.Continuous;
}
}
@@ -117,7 +169,14 @@ namespace Minigames.FortFight.Projectiles
if (rb2D != null)
{
rb2D.AddForce(LaunchDirection * LaunchForce, ForceMode2D.Impulse);
Logging.Debug($"[ProjectileBase] Launched {gameObject.name} with force {LaunchForce} in direction {LaunchDirection}");
// Debug: Log actual mass and resulting velocity for trajectory verification
float actualMass = rb2D.mass;
float expectedVelocity = LaunchForce / actualMass;
Logging.Debug($"[Projectile] Launched {gameObject.name} - Force: {LaunchForce:F2}, Mass: {actualMass:F2}, Expected Velocity: {expectedVelocity:F2}, Dir: {LaunchDirection}");
// After physics applies, log actual velocity (next frame would show it, but we log expectation)
// Note: Actual velocity will be set by Unity physics engine as: velocity = impulse / mass
}
// Fire event
@@ -163,36 +222,32 @@ namespace Minigames.FortFight.Projectiles
Logging.Debug($"[ProjectileBase] {gameObject.name} hit {collision.gameObject.name}");
// Check if hit a fort block
FortBlock block = collision.gameObject.GetComponent<FortBlock>();
if (block != null)
{
// Deal damage to block
block.TakeDamage(damage);
Logging.Debug($"[ProjectileBase] Dealt {damage} damage to {block.gameObject.name}");
}
// Spawn impact effect
SpawnImpactEffect(collision.contacts[0].point);
// Fire impact event
OnImpact?.Invoke(this, collision.collider);
// Call subclass-specific hit behavior
OnHit(collision.collider);
// Destroy projectile after hit (subclasses can override)
DestroyProjectile();
// Delegate to subclass - they handle everything (damage, effects, destruction)
OnHit(collision);
}
/// <summary>
/// Called when projectile hits something.
/// Override in subclasses for projectile-specific behavior.
/// Override in subclasses to implement full projectile behavior.
/// Default implementation: Deal damage to blocks and destroy projectile.
/// Subclasses should call DestroyProjectile() when they want to be destroyed.
/// </summary>
protected virtual void OnHit(Collider2D hit)
/// <param name="collision">Collision data including contact points and normals</param>
protected virtual void OnHit(Collision2D collision)
{
// Subclasses override for special behavior
// e.g., Vacuum continues sliding, TrashBag splits, etc.
// Default behavior: Deal damage to blocks and destroy
FortBlock block = collision.gameObject.GetComponent<FortBlock>();
if (block != null)
{
block.TakeDamage(Damage);
Logging.Debug($"[ProjectileBase] Dealt {Damage} damage to {block.gameObject.name}");
}
// Default: Destroy on hit
DestroyProjectile();
}
#endregion
@@ -207,10 +262,37 @@ namespace Minigames.FortFight.Projectiles
if (impactEffectPrefab != null)
{
GameObject effect = Instantiate(impactEffectPrefab, position, Quaternion.identity);
Destroy(effect, 2f); // Auto-cleanup
// Dynamically determine cleanup time from particle system
float lifetime = GetEffectLifetime(effect);
Destroy(effect, lifetime);
}
}
/// <summary>
/// Get the lifetime of an effect by reading particle system StartLifetime.
/// Falls back to 2 seconds if no particle system found.
/// </summary>
private float GetEffectLifetime(GameObject effect)
{
// Try to read from ParticleSystem
ParticleSystem ps = effect.GetComponent<ParticleSystem>();
if (ps != null)
{
return ps.main.startLifetime.constantMax + 0.5f; // Add small buffer
}
// Try to read from child particle systems
ParticleSystem childPs = effect.GetComponentInChildren<ParticleSystem>();
if (childPs != null)
{
return childPs.main.startLifetime.constantMax + 0.5f;
}
// Fallback for non-particle effects (sprites, etc.)
return 2f;
}
#endregion
#region Destruction