VSClone/Assets/Scripts/Sim/Game.Story.cs

89 lines
4.6 KiB
C#

using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using UnityEngine;
// 스토리(StoryLogic 26장 · StoryLocale) — 조건 충족 시 대화 장면 1회 (트리거 문자열은 원작 표 그대로 파싱)
namespace VS.Sim
{
[Serializable] public class StoryLineRow { public string key; public int characterId; public string emotion; public string lineEvent; }
[Serializable] public class StoryChapterRow { public string id; public string trigger; public string backgroundId; public bool useLiteUi; public List<StoryLineRow> lines; }
[Serializable] public class StoryLogicTable { public List<StoryChapterRow> chapters; }
[Serializable] public class StoryLocaleLine { public string key; public string speakerName; public string text; }
[Serializable] public class StoryCharName { public string id; public string name; }
[Serializable] public class StoryLocaleTable { public List<StoryLocaleLine> lines; public List<StoryCharName> characterNames; }
public partial class Game
{
public StoryLogicTable Story;
Dictionary<string, StoryLocaleLine> storyText;
static readonly Regex RxItem = new Regex(@"^ITEM_(\d+)_HOUSE_(\d+)_LEVEL_?\s*>=\s*(\d+)$", RegexOptions.IgnoreCase);
static readonly Regex RxHouseItem = new Regex(@"^ITEM_HOUSE_(\d+)_LEVEL_?\s*>=\s*(\d+)$", RegexOptions.IgnoreCase);
static readonly Regex RxUnlock = new Regex(@"^UNLOCK_ROOM_(\d+)$", RegexOptions.IgnoreCase);
static readonly Regex RxFollower = new Regex(@"^FOLLOWERS?_?\s*>=\s*(\d+)$", RegexOptions.IgnoreCase);
void InitStory()
{
try
{
if (!string.IsNullOrEmpty(T.StoryLogicJson)) Story = JsonUtility.FromJson<StoryLogicTable>(T.StoryLogicJson);
storyText = new Dictionary<string, StoryLocaleLine>();
if (!string.IsNullOrEmpty(T.StoryLocaleJson))
{
var loc = JsonUtility.FromJson<StoryLocaleTable>(T.StoryLocaleJson);
foreach (var l in loc.lines) storyText[l.key] = l;
}
}
catch (Exception e) { Say("story load failed " + e.Message); }
}
public StoryLocaleLine StoryLine(string key) => storyText != null && storyText.TryGetValue(key, out var l) ? l : null;
/// 트리거 평가 — 아이템 번호·레벨은 화면 표기(1부터 · 레벨 = 저장값+1) 기준 (추정: 시각 단계 문턱 24·49 ↔ 표 25·50)
public bool StoryTriggerMet(string trig)
{
if (string.IsNullOrEmpty(trig)) return false;
trig = trig.Trim();
if (trig.Equals("START_GAME", StringComparison.OrdinalIgnoreCase)) return true;
var m = RxItem.Match(trig);
if (m.Success)
{
int item = int.Parse(m.Groups[1].Value) - 1, house = int.Parse(m.Groups[2].Value) - 1, lv = int.Parse(m.Groups[3].Value);
return house == S.house && RoomLevel(item) + 1 >= lv || house < S.house;
}
m = RxHouseItem.Match(trig);
if (m.Success)
{
// ItemHouseLevel: 지난 집이면 즉시 · 같은 집이면 11개 모두 구매 + 최솟값(표시 레벨) ≥ L (TryGetMinHouseDisplayItemLevel RVA 0x25C434C)
int house = int.Parse(m.Groups[1].Value) - 1, lv = int.Parse(m.Groups[2].Value);
if (house < S.house) return true;
if (house > S.house) return false;
int min = int.MaxValue;
foreach (var it in Room.Items) { int l = RoomLevel(it.Index); if (l < 0) return false; min = Math.Min(min, l + 1); }
return min >= lv;
}
m = RxUnlock.Match(trig);
if (m.Success) return S.house >= int.Parse(m.Groups[1].Value) - 1;
m = RxFollower.Match(trig);
if (m.Success) return Follower >= System.Numerics.BigInteger.Parse(m.Groups[1].Value);
return false;
}
/// 다음에 보여 줄 장(원작 순서 = 표 순서 · 이미 본 장 제외)
public StoryChapterRow NextStory()
{
if (Story == null || !T.Cfg.remote.OnOffStory) return null;
foreach (var c in Story.chapters)
{
if (S.seenStories.Contains(c.id)) continue;
if (StoryTriggerMet(c.trigger)) return c;
// 앞 장을 안 봤으면 뒤 장은 보류(표 순서 유지)
return null;
}
return null;
}
public void MarkStorySeen(string id) { if (!S.seenStories.Contains(id)) S.seenStories.Add(id); }
}
}