using System.Collections.Generic;
using UnityEngine;
namespace ServiceSim
{
///
/// 1卓ぶんの椅子の出し入れ。座っている人は人として置かないので、
/// この script が「その卓の椅子」だけを持つ。
///
/// 引くきっかけは人の動きに合わせる(B6)。GroupManager が次のときに呼ぶ。
/// 座るとき/ドリンクバーへ立つとき/トイレへ立つとき/会計で立つとき/退店するとき/戻って座り直すとき
/// 食事中に何もなく引く分は、別の長い間隔(spec.chairPullIdleMeanIntervalSec)で少しだけ起こす。
///
/// この script は椅子の出し入れだけを扱う。いつ引くかは GroupManager が決め、
/// 椅子1脚の状態は Chair が持つ。数字は PeopleSpec の .asset に置く。
///
public class SeatedGuest : MonoBehaviour
{
/// 引いたきっかけ(記録に出す)
public const string ReasonSit = "座る";
public const string ReasonDrinkBar = "ドリンクバー";
public const string ReasonToilet = "トイレ";
public const string ReasonPay = "会計";
public const string ReasonLeave = "退店";
public const string ReasonSitBack = "戻って座る";
public const string ReasonIdle = "食事中";
[Header("設定")]
public PeopleSpec spec;
[Header("いまの様子(読むだけ)")]
[SerializeField] int _pulledNow;
[SerializeField] float _nextIdlePullIn;
[SerializeField] int _pulls;
readonly List _aisleChairs = new List();
/// _aisleChairs と同じ並び。0 より大きければ、その秒数だけ引いたまま
readonly List _holdLeft = new List();
System.Random _rng;
ServiceOps _ops;
int _tableIndex = -1;
// ---- 座っている客の形(見た目だけ。当たり・PersonBody・NavMeshAgent を持たない)----
GameObject _bodyPrefab;
readonly List _seatPoints = new List();
readonly List _bodies = new List();
int _shown;
bool _warnedNoBody; // 出せないことを1回だけ知らせる
/// いま出している座った形の数(読むだけ)
public int SeatedShown { get { return _shown; } }
/// この卓の席の数(座った形を出せる上限)
public int SeatPointCount { get { return _seatPoints.Count; } }
/// いま1脚でも引いているか
public bool IsPulling { get { return _pulledNow > 0; } }
/// この卓で引いた回数
public int Pulls { get { return _pulls; } }
/// 引ける椅子の数
public int AisleChairCount { get { return _aisleChairs.Count; } }
/// この卓の引ける椅子と、乱数の種を渡す
public void Configure(PeopleSpec peopleSpec, IList aisleChairs, int seed,
ServiceOps ops, int tableIndex)
{
Configure(peopleSpec, aisleChairs, seed, ops, tableIndex, null, null);
}
/// 席の位置と、座った形の prefab も渡す
public void Configure(PeopleSpec peopleSpec, IList aisleChairs, int seed,
ServiceOps ops, int tableIndex,
GameObject seatedBodyPrefab, IList seatPoints)
{
_ops = ops;
_tableIndex = tableIndex;
spec = peopleSpec;
_bodyPrefab = seatedBodyPrefab;
_seatPoints.Clear();
if (seatPoints != null)
for (int i = 0; i < seatPoints.Count; i++) _seatPoints.Add(seatPoints[i]);
ClearBodies();
_aisleChairs.Clear();
_holdLeft.Clear();
if (aisleChairs != null)
{
for (int i = 0; i < aisleChairs.Count; i++)
{
if (aisleChairs[i] == null) continue;
_aisleChairs.Add(aisleChairs[i]);
_holdLeft.Add(0f);
}
}
_rng = new System.Random(seed);
_pulledNow = 0;
_pulls = 0;
_nextIdlePullIn = NextIdleInterval();
}
///
/// 人が動いたので、その人の席の椅子を引く。
/// seatOrder はその卓の何人目か(同じ人がいつも同じ椅子を引くようにする)。
///
public void PullFor(string reason, int seatOrder)
{
if (spec == null || _aisleChairs.Count == 0) return;
int i = Mathf.Abs(seatOrder) % _aisleChairs.Count;
Pull(i, reason);
}
void Pull(int i, string reason)
{
if (i < 0 || i >= _aisleChairs.Count) return;
Chair c = _aisleChairs[i];
if (c == null) return;
// すでに引いてあるなら、引いたままの時間だけ延ばす(二重に数えない)
if (_holdLeft[i] > 0f)
{
_holdLeft[i] = Mathf.Max(_holdLeft[i], spec.chairPullHoldSec);
return;
}
c.Pull(spec.chairPullDistance);
_holdLeft[i] = spec.chairPullHoldSec;
_pulledNow++;
_pulls++;
if (_ops != null) _ops.AddChairPull(reason, TableIdOf());
}
int TableIdOf()
{
if (_ops == null || _tableIndex < 0 || _tableIndex >= _ops.Tables.Count) return 0;
return _ops.Tables[_tableIndex].tableId;
}
float NextIdleInterval()
{
if (spec == null || spec.chairPullIdleMeanIntervalSec <= 0f) return float.MaxValue;
double u = _rng.NextDouble();
if (u <= 1e-9) u = 1e-9;
return (float)(-spec.chairPullIdleMeanIntervalSec * System.Math.Log(u));
}
void Update()
{
if (spec == null || _rng == null) return;
float dt = Time.deltaTime;
// 引いたままの時間が切れた椅子を戻す
for (int i = 0; i < _holdLeft.Count; i++)
{
if (_holdLeft[i] <= 0f) continue;
_holdLeft[i] -= dt;
if (_holdLeft[i] > 0f) continue;
_holdLeft[i] = 0f;
if (_aisleChairs[i] != null) _aisleChairs[i].Release();
_pulledNow = Mathf.Max(0, _pulledNow - 1);
}
// 食事中に何もなく引く分(少しだけ)
if (_nextIdlePullIn == float.MaxValue) return;
_nextIdlePullIn -= dt;
if (_nextIdlePullIn > 0f) return;
_nextIdlePullIn = NextIdleInterval();
if (!Occupied()) return;
if (_aisleChairs.Count == 0) return;
Pull(_rng.Next(_aisleChairs.Count), ReasonIdle);
}
/// この卓に組が座っているか
bool Occupied()
{
if (_ops == null || _tableIndex < 0) return true; // 分からないときは引く
if (_tableIndex >= _ops.Tables.Count) return true;
return _ops.Tables[_tableIndex].groupId >= 0;
}
// ---------------- 座っている客の形(見た目だけ)----------------
///
/// 座っている客の形を count 人ぶん出す(0 なら全部消す)。
/// 形は席の位置に置くだけ。当たり判定も PersonBody も NavMeshAgent も持たないので、
/// NavMesh・前のセンサー・人の数の記録・物理にはいっさい効かない。
///
public void ShowSeated(int count)
{
// 出せないときは黙って戻らない。
// 10/21 の分が2日間だれにも気づかれなかったのは、ここが黙って戻っていたため
if (_bodyPrefab == null || _seatPoints.Count == 0)
{
if (count > 0 && !_warnedNoBody)
{
_warnedNoBody = true;
Debug.LogWarning("[ServiceSim] 座っている客の形を出せません(卓 " + _tableIndex + ")。" +
(_bodyPrefab == null
? "形の prefab が入っていません(PeopleSpawner.seatedBodyPrefab)。"
+ "ServiceSim/座っている客の形を用意する を押してください"
: "この卓に座る位置(seat_points)がありません"));
}
_shown = 0;
return;
}
int want = Mathf.Clamp(count, 0, _seatPoints.Count);
if (want == _shown) return;
EnsureBodies();
for (int i = 0; i < _bodies.Count; i++)
{
if (_bodies[i] == null) continue;
bool on = i < want;
if (_bodies[i].activeSelf != on) _bodies[i].SetActive(on);
}
_shown = want;
}
void EnsureBodies()
{
if (_bodies.Count == _seatPoints.Count) return;
ClearBodies();
for (int i = 0; i < _seatPoints.Count; i++)
{
GameObject go = Instantiate(_bodyPrefab, transform);
go.name = "SeatedBody" + (i + 1).ToString("00");
go.transform.position = _seatPoints[i];
go.SetActive(false);
_bodies.Add(go);
}
}
void ClearBodies()
{
for (int i = 0; i < _bodies.Count; i++)
{
if (_bodies[i] == null) continue;
if (Application.isPlaying) Destroy(_bodies[i]);
else DestroyImmediate(_bodies[i]);
}
_bodies.Clear();
_shown = 0;
}
void OnDisable()
{
ShowSeated(0);
for (int i = 0; i < _holdLeft.Count; i++)
{
_holdLeft[i] = 0f;
if (_aisleChairs[i] != null) _aisleChairs[i].Release();
}
_pulledNow = 0;
}
}
}