Implement Fort Fight minigame (#75)

Co-authored-by: Michal Pikulski <michal.a.pikulski@gmail.com>
Reviewed-on: #75
This commit is contained in:
2025-12-04 01:18:29 +00:00
parent bb8d600af2
commit e60d516e7e
127 changed files with 21544 additions and 128 deletions

View File

@@ -0,0 +1,55 @@
namespace Minigames.FortFight.Data
{
/// <summary>
/// Game mode for Fort Fight minigame
/// </summary>
public enum FortFightGameMode
{
SinglePlayer, // Player vs AI
TwoPlayer // Player vs Player
}
/// <summary>
/// Current turn state in the game
/// </summary>
public enum TurnState
{
PlayerOneTurn,
PlayerTwoTurn,
AITurn,
TransitioningTurn, // Transitioning between turns (projectile in flight, waiting for settle)
GameOver
}
/// <summary>
/// Material types for fort blocks
/// </summary>
public enum BlockMaterial
{
Cardboard,
Metal,
Glass
}
/// <summary>
/// Size categories for fort blocks
/// </summary>
public enum BlockSize
{
Small,
Medium,
Large
}
/// <summary>
/// Types of projectiles available
/// </summary>
public enum ProjectileType
{
Toaster, // Standard physics projectile
Vacuum, // Heavy, rolls on floor
CeilingFan, // Drops straight down
TrashBag // Explodes on impact
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 9891698193c757344bc2f3f26730248a

View File

@@ -0,0 +1,156 @@
using System;
using System.Collections.Generic;
namespace Minigames.FortFight.Data
{
/// <summary>
/// Encapsulates all ammunition state for a single player.
/// Tracks cooldowns, selection, and usage history per player.
/// </summary>
[Serializable]
public class PlayerAmmoState
{
#region Properties
public int PlayerIndex { get; private set; }
public ProjectileType SelectedAmmo { get; set; }
#endregion
#region State
// Cooldowns per projectile type (turns remaining)
private Dictionary<ProjectileType, int> cooldowns;
// Optional: Track usage for statistics/analytics
private Dictionary<ProjectileType, int> usageCount;
private ProjectileType lastUsedProjectile;
#endregion
#region Constructor
public PlayerAmmoState(int playerIndex, ProjectileType defaultAmmo)
{
PlayerIndex = playerIndex;
SelectedAmmo = defaultAmmo;
cooldowns = new Dictionary<ProjectileType, int>();
usageCount = new Dictionary<ProjectileType, int>();
lastUsedProjectile = defaultAmmo;
}
#endregion
#region Cooldown Management
/// <summary>
/// Initialize cooldown for a specific projectile type.
/// </summary>
public void InitializeCooldown(ProjectileType type)
{
if (!cooldowns.ContainsKey(type))
{
cooldowns[type] = 0;
}
}
/// <summary>
/// Set cooldown for a specific projectile type.
/// </summary>
public void SetCooldown(ProjectileType type, int turns)
{
cooldowns[type] = turns;
}
/// <summary>
/// Get remaining cooldown turns for a projectile type.
/// </summary>
public int GetCooldown(ProjectileType type)
{
return cooldowns.ContainsKey(type) ? cooldowns[type] : 0;
}
/// <summary>
/// Check if projectile type is available (not on cooldown).
/// </summary>
public bool IsAmmoAvailable(ProjectileType type)
{
return GetCooldown(type) == 0;
}
/// <summary>
/// Decrement all cooldowns by 1 turn.
/// Returns list of projectile types that completed cooldown this turn.
/// </summary>
public List<ProjectileType> DecrementCooldowns()
{
List<ProjectileType> completedCooldowns = new List<ProjectileType>();
List<ProjectileType> types = new List<ProjectileType>(cooldowns.Keys);
foreach (var type in types)
{
if (cooldowns[type] > 0)
{
cooldowns[type]--;
if (cooldowns[type] == 0)
{
completedCooldowns.Add(type);
}
}
}
return completedCooldowns;
}
#endregion
#region Usage Tracking
/// <summary>
/// Record that a projectile type was used.
/// </summary>
public void RecordUsage(ProjectileType type)
{
lastUsedProjectile = type;
if (!usageCount.ContainsKey(type))
{
usageCount[type] = 0;
}
usageCount[type]++;
}
/// <summary>
/// Get usage count for a projectile type.
/// </summary>
public int GetUsageCount(ProjectileType type)
{
return usageCount.ContainsKey(type) ? usageCount[type] : 0;
}
/// <summary>
/// Get the last projectile type used by this player.
/// </summary>
public ProjectileType LastUsedProjectile => lastUsedProjectile;
/// <summary>
/// Get total number of projectiles used by this player.
/// </summary>
public int TotalUsageCount
{
get
{
int total = 0;
foreach (var count in usageCount.Values)
{
total += count;
}
return total;
}
}
#endregion
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: fd7545bfc92d4096b53954bab9884b15
timeCreated: 1764797211

View File

@@ -0,0 +1,23 @@
using System;
namespace Minigames.FortFight.Data
{
/// <summary>
/// Represents a player in the Fort Fight minigame
/// </summary>
[Serializable]
public class PlayerData
{
public string PlayerName;
public bool IsAI;
public int PlayerIndex; // 0 for Player One, 1 for Player Two/AI
public PlayerData(string name, bool isAI, int playerIndex)
{
PlayerName = name;
IsAI = isAI;
PlayerIndex = playerIndex;
}
}
}

View File

@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: f310a90c43a9b3049b875c84f2d9043a

View File

@@ -0,0 +1,94 @@
using System;
using UnityEngine;
namespace Minigames.FortFight.Data
{
/// <summary>
/// Configuration data for a projectile type.
/// Stored centrally in FortFightSettings instead of individual ScriptableObject assets.
/// </summary>
[Serializable]
public class ProjectileConfig
{
[Header("Identity")]
[Tooltip("Type of projectile this config represents")]
public ProjectileType projectileType;
[Tooltip("Unique string identifier (auto-generated from type)")]
public string projectileId;
[Header("Prefab")]
[Tooltip("Prefab for this projectile (should have ProjectileBase component)")]
public GameObject prefab;
[Header("Ammunition System")]
[Tooltip("Cooldown in turns after use")]
public int cooldownTurns = 2;
[Header("UI")]
[Tooltip("Icon sprite for ammunition UI")]
public Sprite icon;
[Tooltip("Display name for this projectile type")]
public string displayName;
[Tooltip("Description of projectile behavior")]
[TextArea(2, 4)]
public string description;
[Header("Combat Stats")]
[Tooltip("Damage dealt on impact")]
public float damage = 20f;
[Header("Physics")]
[Tooltip("Mass for physics simulation (affects trajectory and force)")]
public float mass = 1f;
/// <summary>
/// Get the ProjectileBase component from the prefab
/// </summary>
public Projectiles.ProjectileBase GetProjectileComponent()
{
if (prefab == null) return null;
return prefab.GetComponent<Projectiles.ProjectileBase>();
}
/// <summary>
/// Get damage value from config
/// </summary>
public float GetDamage()
{
return damage;
}
/// <summary>
/// Get mass value from config
/// </summary>
public float GetMass()
{
return mass;
}
/// <summary>
/// Validate and auto-generate projectileId from type
/// </summary>
public void Validate()
{
if (string.IsNullOrEmpty(projectileId))
{
projectileId = GenerateIdFromType(projectileType);
}
if (string.IsNullOrEmpty(displayName))
{
displayName = projectileType.ToString();
}
}
private string GenerateIdFromType(ProjectileType type)
{
return type.ToString().ToLower();
}
}
}

View File

@@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 9d60235e77c7456380c10f9c145750bf
timeCreated: 1764778577