2026.06.25 Fishing scene

This commit is contained in:
2026-06-25 17:39:42 +09:00
parent 3a3b3dcb41
commit e8f67c0609
44 changed files with 4061 additions and 133 deletions

View File

@@ -1,53 +1,255 @@
using System;
using System.Collections.Generic;
using UnityEngine;
public class FishingRewardSystem : MonoBehaviour
{
public enum RewardType
[Serializable]
public class LootEntry
{
Normal,
Rare
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);
}
}
[Header("Reward Chance")]
[Range(0f, 100f)]
[SerializeField] private float rareChance = 10f;
[Header("Reward Count")]
[SerializeField] private int normalMemory;
[SerializeField] private int rareMemory;
public int NormalMemory => normalMemory;
public int RareMemory => rareMemory;
public RewardType GiveReward()
public struct CatchResult
{
if (Random.value < rareChance / 100f)
return GiveRareMemory();
public FishingItemType ItemType;
public string DisplayName;
public bool CountsAsCleanupItem;
public bool IsMemoryPiece;
return GiveNormalMemory();
public CatchResult(FishingItemType itemType, string displayName, bool countsAsCleanupItem, bool isMemoryPiece)
{
ItemType = itemType;
DisplayName = displayName;
CountsAsCleanupItem = countsAsCleanupItem;
IsMemoryPiece = isMemoryPiece;
}
}
private RewardType GiveNormalMemory()
[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()
{
normalMemory++;
Debug.Log("ÀÏ¹Ý ±â¾ï +1");
return RewardType.Normal;
SetDefaultLootTables();
}
private RewardType GiveRareMemory()
private void Awake()
{
rareMemory++;
Debug.Log("Èñ±Í ±â¾ï +1");
return RewardType.Rare;
EnsureLootTables();
}
public void ResetRewardCount()
private void OnValidate()
{
normalMemory = 0;
rareMemory = 0;
EnsureLootTables();
ClampLootTable(dirtyPondLootTable);
ClampLootTable(cleanPondLootTable);
}
}
private void EnsureLootTables()
{
if (dirtyPondLootTable == null)
dirtyPondLootTable = new List<LootEntry>();
if (cleanPondLootTable == null)
cleanPondLootTable = new List<LootEntry>();
if (dirtyPondLootTable.Count == 0)
SetDefaultDirtyLootTable();
if (cleanPondLootTable.Count == 0)
SetDefaultCleanLootTable();
}
private void SetDefaultLootTables()
{
SetDefaultDirtyLootTable();
SetDefaultCleanLootTable();
}
private void SetDefaultDirtyLootTable()
{
dirtyPondLootTable = new List<LootEntry>
{
new LootEntry(FishingItemType.Fish, "생선", 25f),
new LootEntry(FishingItemType.RottenFish, "상한 생선", 8f),
new LootEntry(FishingItemType.Trash, "쓰레기", 28f),
new LootEntry(FishingItemType.Bottle, "병", 18f),
new LootEntry(FishingItemType.PlasticBag, "봉지", 18f),
new LootEntry(FishingItemType.JellyfishPowder, "해파리 가루", 6f),
new LootEntry(FishingItemType.OldCompass, "낡은 나침반", 4f)
};
}
private void SetDefaultCleanLootTable()
{
cleanPondLootTable = new List<LootEntry>
{
new LootEntry(FishingItemType.MemoryPiece, "기억의 조각", 30f),
new LootEntry(FishingItemType.Fish, "생선", 30f),
new LootEntry(FishingItemType.OldCompass, "낡은 나침반", 15f),
new LootEntry(FishingItemType.JellyfishPowder, "해파리 가루", 15f),
new LootEntry(FishingItemType.RottenFish, "상한 생선", 4f)
};
}
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);
if (string.IsNullOrWhiteSpace(table[i].displayName))
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(FishingItemType.Fish, GetDefaultDisplayName(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 (!pondCleaned && entry.itemType == FishingItemType.MemoryPiece)
return false;
return entry.itemType != FishingItemType.None;
}
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 ||
itemType == FishingItemType.Bottle ||
itemType == FishingItemType.PlasticBag;
}
public string GetDefaultDisplayName(FishingItemType itemType)
{
switch (itemType)
{
case FishingItemType.Fish:
return "생선";
case FishingItemType.RottenFish:
return "상한 생선";
case FishingItemType.Trash:
return "쓰레기";
case FishingItemType.Bottle:
return "병";
case FishingItemType.PlasticBag:
return "봉지";
case FishingItemType.JellyfishPowder:
return "해파리 가루";
case FishingItemType.OldCompass:
return "낡은 나침반";
case FishingItemType.MemoryPiece:
return "기억의 조각";
case FishingItemType.None:
default:
return "없음";
}
}
}