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
}
}