using System.Collections.Generic; using Unity.VisualScripting; using UnityEngine; namespace MazeGen { /// /// Monster size for combat positioning. Determines grid footprint and spatial presence. /// Small: 4 creatures fit in 1 grid square (fractionally tracked) /// Medium: 1 creature = 1 grid square /// Large: 1 creature = 4x4 grid squares /// public enum MonsterSize { Small, // 4 per square Medium, // 1 per square (1x1) Large // 4x4 squares per creature } /// /// Defines a loot entry with an item and its min/max drop count. /// E.g., a Goblin might drop 0-2 Health Potions. /// [System.Serializable] public class LootEntry { public Item Item; [Tooltip("Minimum number of this item to drop (0 means it might not drop at all)")] public int MinCount = 0; [Tooltip("Maximum number of this item to drop")] public int MaxCount = 1; public LootEntry() { } public LootEntry(Item item, int minCount, int maxCount) { Item = item; MinCount = minCount; MaxCount = maxCount; } } /// /// A ScriptableObject that defines a monster template (stats, appearance, loot). /// Monsters are instantiated from this template when they spawn in a room. /// public class Monster : ScriptableObject { [SerializeField] private string _monsterName = "Goblin"; [SerializeField] private Color _color = Color.green; [SerializeField] private CharacterStats _baseStats = new CharacterStats(); [SerializeField] private MonsterSize _size = MonsterSize.Medium; // Optional starting equipment [SerializeField] private Weapon _startingWeapon; [SerializeField] private Armor _startingArmor; // Loot table: what items might this monster drop with min/max counts [SerializeField] private LootEntry[] _lootTable; // Loot options [SerializeField] private bool _includeStartingWeaponAsLoot = false; [SerializeField] private bool _includeStartingArmorAsLoot = false; [SerializeField] [Range(0f, 1f)] private float _lootDropChance = 1.0f; // Chance to drop any loot at all public string MonsterName => _monsterName; public Color Color => _color; public CharacterStats BaseStats => _baseStats; public MonsterSize Size => _size; public Weapon StartingWeapon => _startingWeapon; public Armor StartingArmor => _startingArmor; public LootEntry[] LootTable => _lootTable; public bool IncludeStartingWeaponAsLoot => _includeStartingWeaponAsLoot; public bool IncludeStartingArmorAsLoot => _includeStartingArmorAsLoot; public float LootDropChance => _lootDropChance; /// Build the final loot pool for this monster instance based on min/max ranges. public List GenerateLootPool() { var pool = new List(); // Determine if this monster drops any loot at all if (Random.value > _lootDropChance) return pool; // Return empty pool (no loot) // Add items from the loot table based on min/max rolls if (_lootTable != null) { foreach (var entry in _lootTable) { if (entry?.Item == null) continue; int count = Random.Range(entry.MinCount, entry.MaxCount + 1); for (int i = 0; i < count; i++) { pool.Add(entry.Item); } } } // Optionally add starting equipment if (_includeStartingWeaponAsLoot && _startingWeapon != null) { pool.Add(_startingWeapon); } if (_includeStartingArmorAsLoot && _startingArmor != null) { pool.Add(_startingArmor); } return pool; } /// Create a fresh character sheet for a monster instance (with a copy of stats). public CharacterSheet CreateCharacterSheet() { // Pass null GameObject (Monster is a ScriptableObject, not a MonoBehaviour) // CharacterSheet will use a default name instead var sheet = new CharacterSheet(null, _baseStats.Clone()); // Override with the monster template's name sheet.Name = _monsterName; if (_startingWeapon != null) sheet.EquipWeapon(_startingWeapon); if (_startingArmor != null) sheet.EquipArmor(_startingArmor); return sheet; } #if UNITY_EDITOR private void OnValidate() { if (_baseStats == null) _baseStats = new CharacterStats(); } #endif } }