145 lines
7.3 KiB
C#
145 lines
7.3 KiB
C#
// ─────────────────────────────────────────────────────────────────────────────
|
|
// WLDungeonGate.cs — 섬 위의 던전 입구(**더미**) + 「들어가기」
|
|
//
|
|
// PD 추가 지시(발주서 §1-B-1) 「던전 오브젝트는 우선 더미 오브젝트를 쓰고 추후 교체할게」
|
|
// → 모양에 시간을 쓰지 않는다. 단순 형태(문틀 + 포털판) + 이름표 + 「들어가기」면 끝이다.
|
|
// → 교체는 값 하나 — `WLDungeonDef.gatePrefabPath` 에 프리팹 경로를 넣으면 그것이 대신 놓인다.
|
|
//
|
|
// ■ 룩 — 아레나(던전)와 같은 계열로 맞춘다(Lead 지시 3항).
|
|
// `WLReferenceLookSettings` 가 지정한 Toon/URP 머티리얼을 찾아 쓰고, 없으면 URP/Lit 로 만든다.
|
|
// 🔴 배경 머티리얼·조명은 816a 소유라 **읽기만** 한다(어떤 기존 머티리얼도 수정하지 않는다).
|
|
//
|
|
// 🔴 Debug.LogError / Debug.LogException 을 쓰지 않는다.
|
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using UnityEngine.SceneManagement;
|
|
|
|
namespace WL.Island
|
|
{
|
|
/// <summary>던전 입구 1개. 트리거가 아니라 거리 판정(FI 플레이어가 CharacterController 라 안전하다).</summary>
|
|
public sealed class WLDungeonGate : MonoBehaviour
|
|
{
|
|
public WLDungeonDef Def;
|
|
public Transform Player;
|
|
public bool InRange;
|
|
|
|
static readonly List<WLDungeonGate> s_all = new List<WLDungeonGate>(8);
|
|
static WLDungeonGate s_active;
|
|
|
|
/// <summary>지금 「들어가기」가 떠 있는 입구(없으면 null).</summary>
|
|
public static WLDungeonGate Active { get { return s_active; } }
|
|
|
|
void OnEnable() { if (!s_all.Contains(this)) s_all.Add(this); }
|
|
void OnDisable() { s_all.Remove(this); if (s_active == this) s_active = null; }
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// 만들기
|
|
// ─────────────────────────────────────────────────────────────────
|
|
public static WLDungeonGate Create(WLIslandSettings cfg, WLDungeonDef d, Scene scene)
|
|
{
|
|
GameObject root = null;
|
|
|
|
if (!string.IsNullOrEmpty(d.gatePrefabPath))
|
|
{
|
|
var prefab = WLIslandAssets.LoadPrefab(d.gatePrefabPath);
|
|
if (prefab != null) root = Object.Instantiate(prefab);
|
|
}
|
|
if (root == null) root = BuildDummy(cfg, d);
|
|
|
|
root.name = "~WL_DungeonGate_" + d.id;
|
|
root.transform.position = d.gatePosition;
|
|
root.transform.rotation = Quaternion.Euler(0f, d.gateYaw, 0f);
|
|
if (scene.IsValid()) SceneManager.MoveGameObjectToScene(root, scene);
|
|
|
|
var gate = root.AddComponent<WLDungeonGate>();
|
|
gate.Def = d;
|
|
return gate;
|
|
}
|
|
|
|
/// <summary>내장 더미 — 문틀 2개 + 상인방 + 포털판 + 이름표. (PD: 더미로 충분하다)</summary>
|
|
static GameObject BuildDummy(WLIslandSettings cfg, WLDungeonDef d)
|
|
{
|
|
float s = d.gateScale <= 0f ? 1f : d.gateScale;
|
|
float h = cfg.gateHeight * s;
|
|
float w = cfg.gateWidth * s;
|
|
float t = 0.35f * s;
|
|
|
|
var root = new GameObject("gate");
|
|
var frameMat = WLIslandLook.GateMaterial(d.gateColor * 0.55f);
|
|
var portalMat = WLIslandLook.GateMaterial(d.gateColor);
|
|
|
|
MakeBox(root.transform, "post_L", new Vector3(-w * 0.5f, h * 0.5f, 0f), new Vector3(t, h, t), frameMat);
|
|
MakeBox(root.transform, "post_R", new Vector3(w * 0.5f, h * 0.5f, 0f), new Vector3(t, h, t), frameMat);
|
|
MakeBox(root.transform, "lintel", new Vector3(0f, h + t * 0.5f, 0f), new Vector3(w + t, t, t), frameMat);
|
|
MakeBox(root.transform, "portal", new Vector3(0f, h * 0.5f, 0f), new Vector3(w - t * 0.5f, h, t * 0.35f), portalMat);
|
|
|
|
WLIslandLabel.Attach(root.transform, new Vector3(0f, h + t * 1.8f, 0f), d.displayName);
|
|
return root;
|
|
}
|
|
|
|
static void MakeBox(Transform parent, string name, Vector3 pos, Vector3 size, Material mat)
|
|
{
|
|
var go = GameObject.CreatePrimitive(PrimitiveType.Cube);
|
|
go.name = name;
|
|
go.transform.SetParent(parent, false);
|
|
go.transform.localPosition = pos;
|
|
go.transform.localScale = size;
|
|
var col = go.GetComponent<Collider>();
|
|
if (col != null) Object.Destroy(col); // 더미 — 길을 막지 않는다(NavMesh 재베이크 회피)
|
|
var r = go.GetComponent<Renderer>();
|
|
if (r != null && mat != null) r.sharedMaterial = mat;
|
|
}
|
|
|
|
// ─────────────────────────────────────────────────────────────────
|
|
// 거리 판정 · 입력 (러너의 Update 에서 한 번만 돈다)
|
|
// ─────────────────────────────────────────────────────────────────
|
|
public static void TickAll(WLIslandSettings cfg)
|
|
{
|
|
if (s_all.Count == 0) { if (s_active != null) { s_active = null; WLIslandPrompt.Hide(); } return; }
|
|
|
|
var player = FindPlayer();
|
|
WLDungeonGate best = null;
|
|
float bestSq = cfg.interactRadius * cfg.interactRadius;
|
|
|
|
for (int i = 0; i < s_all.Count; i++)
|
|
{
|
|
var g = s_all[i];
|
|
if (g == null) continue;
|
|
g.Player = player;
|
|
g.InRange = false;
|
|
if (player == null) continue;
|
|
float sq = (player.position - g.transform.position).sqrMagnitude;
|
|
if (sq <= bestSq) { bestSq = sq; best = g; }
|
|
}
|
|
|
|
if (best != null) best.InRange = true;
|
|
|
|
if (best != s_active)
|
|
{
|
|
s_active = best;
|
|
if (best == null) WLIslandPrompt.Hide();
|
|
else WLIslandPrompt.Show(cfg, string.Format(cfg.promptFormat, best.Def.displayName), best);
|
|
}
|
|
|
|
if (s_active != null && Input.GetKeyDown(cfg.enterKey)) s_active.Enter();
|
|
}
|
|
|
|
static Transform FindPlayer()
|
|
{
|
|
var fi = Object.FindFirstObjectByType<CryingSnow.FarmingIsland.PlayerController>();
|
|
if (fi != null) return fi.transform;
|
|
if (MyValue.bMyPC && MyValue.MyPC != null) return MyValue.MyPC.transform;
|
|
return null;
|
|
}
|
|
|
|
public void Enter()
|
|
{
|
|
WLIslandPrompt.Hide();
|
|
s_active = null;
|
|
WLIslandBridge.EnterDungeon(Def);
|
|
}
|
|
}
|
|
}
|