279 lines
8.2 KiB
C#
279 lines
8.2 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
public class FishingRewardSystem : MonoBehaviour
|
|
{
|
|
[Serializable]
|
|
public class LootEntry
|
|
{
|
|
public FishingItemType itemType = FishingItemType.Fish;
|
|
public string displayName = "물고기";
|
|
[Min(0f)] public float weight = 1f;
|
|
|
|
public LootEntry(FishingItemType itemType, string displayName, float weight)
|
|
{
|
|
this.itemType = itemType;
|
|
this.displayName = displayName;
|
|
this.weight = Mathf.Max(0f, weight);
|
|
}
|
|
}
|
|
|
|
public struct CatchResult
|
|
{
|
|
public FishingItemType ItemType;
|
|
public string DisplayName;
|
|
public bool CountsAsCleanupItem;
|
|
public bool IsMemoryPiece;
|
|
|
|
public CatchResult(FishingItemType itemType, string displayName, bool countsAsCleanupItem, bool isMemoryPiece)
|
|
{
|
|
ItemType = itemType;
|
|
DisplayName = displayName;
|
|
CountsAsCleanupItem = countsAsCleanupItem;
|
|
IsMemoryPiece = isMemoryPiece;
|
|
}
|
|
}
|
|
|
|
[Header("Loot Table - Dirty Pond")]
|
|
[Tooltip("연못이 정화되기 전에는 물고기와 쓰레기만 낚입니다. 기억의 조각은 자동 제외됩니다.")]
|
|
[SerializeField] private List<LootEntry> dirtyPondLootTable = new List<LootEntry>();
|
|
|
|
[Header("Loot Table - Clean Pond")]
|
|
[Tooltip("연못이 정화된 뒤에는 기억의 조각이 낚입니다.")]
|
|
[SerializeField] private List<LootEntry> cleanPondLootTable = new List<LootEntry>();
|
|
|
|
[Header("Debug")]
|
|
[SerializeField] private bool showDebugLog = true;
|
|
|
|
public IReadOnlyList<LootEntry> DirtyPondLootTable => dirtyPondLootTable;
|
|
public IReadOnlyList<LootEntry> CleanPondLootTable => cleanPondLootTable;
|
|
|
|
private void Reset()
|
|
{
|
|
SetDefaultLootTables();
|
|
}
|
|
|
|
private void Awake()
|
|
{
|
|
EnsureLootTables();
|
|
}
|
|
|
|
private void OnValidate()
|
|
{
|
|
EnsureLootTables();
|
|
ClampLootTable(dirtyPondLootTable);
|
|
ClampLootTable(cleanPondLootTable);
|
|
}
|
|
|
|
private void EnsureLootTables()
|
|
{
|
|
if (dirtyPondLootTable == null)
|
|
dirtyPondLootTable = new List<LootEntry>();
|
|
|
|
if (cleanPondLootTable == null)
|
|
cleanPondLootTable = new List<LootEntry>();
|
|
|
|
// 이전 버전의 상한 생선/병/봉지/나침반 등이 인스펙터에 남아 있어도
|
|
// 런타임에서는 물고기 / 쓰레기 / 기억의 조각 3종만 남도록 정리합니다.
|
|
SanitizeLootTables();
|
|
|
|
if (dirtyPondLootTable.Count == 0)
|
|
SetDefaultDirtyLootTable();
|
|
|
|
if (cleanPondLootTable.Count == 0)
|
|
SetDefaultCleanLootTable();
|
|
}
|
|
|
|
private void SanitizeLootTables()
|
|
{
|
|
SanitizeTable(dirtyPondLootTable, allowFish: true, allowTrash: true, allowMemoryPiece: false);
|
|
SanitizeTable(cleanPondLootTable, allowFish: false, allowTrash: false, allowMemoryPiece: true);
|
|
}
|
|
|
|
private void SanitizeTable(List<LootEntry> table, bool allowFish, bool allowTrash, bool allowMemoryPiece)
|
|
{
|
|
if (table == null)
|
|
return;
|
|
|
|
for (int i = table.Count - 1; i >= 0; i--)
|
|
{
|
|
LootEntry entry = table[i];
|
|
|
|
if (entry == null || !IsAllowedInTable(entry.itemType, allowFish, allowTrash, allowMemoryPiece))
|
|
table.RemoveAt(i);
|
|
}
|
|
}
|
|
|
|
private bool IsAllowedInTable(FishingItemType itemType, bool allowFish, bool allowTrash, bool allowMemoryPiece)
|
|
{
|
|
switch (itemType)
|
|
{
|
|
case FishingItemType.Fish:
|
|
return allowFish;
|
|
|
|
case FishingItemType.Trash:
|
|
return allowTrash;
|
|
|
|
case FishingItemType.MemoryPiece:
|
|
return allowMemoryPiece;
|
|
|
|
case FishingItemType.None:
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private void SetDefaultLootTables()
|
|
{
|
|
SetDefaultDirtyLootTable();
|
|
SetDefaultCleanLootTable();
|
|
}
|
|
|
|
private void SetDefaultDirtyLootTable()
|
|
{
|
|
dirtyPondLootTable = new List<LootEntry>
|
|
{
|
|
new LootEntry(FishingItemType.Fish, "물고기", 35f),
|
|
new LootEntry(FishingItemType.Trash, "쓰레기", 65f)
|
|
};
|
|
}
|
|
|
|
private void SetDefaultCleanLootTable()
|
|
{
|
|
cleanPondLootTable = new List<LootEntry>
|
|
{
|
|
new LootEntry(FishingItemType.MemoryPiece, "기억의 조각", 100f)
|
|
};
|
|
}
|
|
|
|
private void ClampLootTable(List<LootEntry> table)
|
|
{
|
|
if (table == null)
|
|
return;
|
|
|
|
for (int i = 0; i < table.Count; i++)
|
|
{
|
|
if (table[i] == null)
|
|
{
|
|
table[i] = new LootEntry(FishingItemType.Fish, "물고기", 1f);
|
|
continue;
|
|
}
|
|
|
|
table[i].weight = Mathf.Max(0f, table[i].weight);
|
|
|
|
// 이전 버전에서 남은 표시 이름을 3종 아이템 이름으로 정리합니다.
|
|
table[i].displayName = GetDefaultDisplayName(table[i].itemType);
|
|
}
|
|
}
|
|
|
|
public CatchResult RollCatch(bool pondCleaned, bool forceMemoryPiece)
|
|
{
|
|
if (forceMemoryPiece)
|
|
return CreateCatchResult(FishingItemType.MemoryPiece, GetDefaultDisplayName(FishingItemType.MemoryPiece));
|
|
|
|
List<LootEntry> table = pondCleaned ? cleanPondLootTable : dirtyPondLootTable;
|
|
LootEntry selectedEntry = RollFromTable(table, pondCleaned);
|
|
|
|
if (selectedEntry == null)
|
|
return CreateCatchResult(pondCleaned ? FishingItemType.MemoryPiece : FishingItemType.Fish,
|
|
GetDefaultDisplayName(pondCleaned ? FishingItemType.MemoryPiece : FishingItemType.Fish));
|
|
|
|
return CreateCatchResult(selectedEntry.itemType, selectedEntry.displayName);
|
|
}
|
|
|
|
private LootEntry RollFromTable(List<LootEntry> table, bool pondCleaned)
|
|
{
|
|
if (table == null || table.Count == 0)
|
|
return null;
|
|
|
|
float totalWeight = 0f;
|
|
|
|
for (int i = 0; i < table.Count; i++)
|
|
{
|
|
LootEntry entry = table[i];
|
|
|
|
if (!IsRollableEntry(entry, pondCleaned))
|
|
continue;
|
|
|
|
totalWeight += entry.weight;
|
|
}
|
|
|
|
if (totalWeight <= 0f)
|
|
return null;
|
|
|
|
float roll = UnityEngine.Random.Range(0f, totalWeight);
|
|
float current = 0f;
|
|
|
|
for (int i = 0; i < table.Count; i++)
|
|
{
|
|
LootEntry entry = table[i];
|
|
|
|
if (!IsRollableEntry(entry, pondCleaned))
|
|
continue;
|
|
|
|
current += entry.weight;
|
|
|
|
if (roll <= current)
|
|
return entry;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
private bool IsRollableEntry(LootEntry entry, bool pondCleaned)
|
|
{
|
|
if (entry == null)
|
|
return false;
|
|
|
|
if (entry.weight <= 0f)
|
|
return false;
|
|
|
|
if (entry.itemType == FishingItemType.None)
|
|
return false;
|
|
|
|
if (!pondCleaned && entry.itemType == FishingItemType.MemoryPiece)
|
|
return false;
|
|
|
|
return entry.itemType == FishingItemType.Fish ||
|
|
entry.itemType == FishingItemType.Trash ||
|
|
entry.itemType == FishingItemType.MemoryPiece;
|
|
}
|
|
|
|
private CatchResult CreateCatchResult(FishingItemType itemType, string displayName)
|
|
{
|
|
string finalDisplayName = string.IsNullOrWhiteSpace(displayName) ? GetDefaultDisplayName(itemType) : displayName;
|
|
bool countsAsCleanupItem = IsCleanupItem(itemType);
|
|
bool isMemoryPiece = itemType == FishingItemType.MemoryPiece;
|
|
|
|
if (showDebugLog)
|
|
Debug.Log($"낚시 획득: {finalDisplayName}");
|
|
|
|
return new CatchResult(itemType, finalDisplayName, countsAsCleanupItem, isMemoryPiece);
|
|
}
|
|
|
|
public bool IsCleanupItem(FishingItemType itemType)
|
|
{
|
|
return itemType == FishingItemType.Trash;
|
|
}
|
|
|
|
public string GetDefaultDisplayName(FishingItemType itemType)
|
|
{
|
|
switch (itemType)
|
|
{
|
|
case FishingItemType.Fish:
|
|
return "물고기";
|
|
|
|
case FishingItemType.Trash:
|
|
return "쓰레기";
|
|
|
|
case FishingItemType.MemoryPiece:
|
|
return "기억의 조각";
|
|
|
|
case FishingItemType.None:
|
|
default:
|
|
return "없음";
|
|
}
|
|
}
|
|
}
|