189 lines
9.5 KiB
C#
189 lines
9.5 KiB
C#
using BrewMonster.Network;
|
||
using BrewMonster.Managers;
|
||
using BrewMonster.Scripts.Task;
|
||
using BrewMonster.Scripts;
|
||
using CSNetwork.GPDataType;
|
||
|
||
namespace BrewMonster.Scripts.UI
|
||
{
|
||
public class CECUIHelper
|
||
{
|
||
public static string DlgTaskName = "Win_Quest";
|
||
|
||
public static A3DVECTOR3 GetTaskObjectCoordinates(int id, ref bool in_table)
|
||
{
|
||
in_table = false;
|
||
|
||
// 0) If the id is actually a runtime object id (npc/player/matter), fetch it from world directly.
|
||
// IMPORTANT: GPDataTypeHelper.ISPLAYERID returns true for ANY positive int, so we must not use it to
|
||
// decide whether something is an "object id". Use a heuristic instead.
|
||
// 0) 如果这个id其实是运行时对象ID(NPC/玩家/物品),直接从World取坐标。
|
||
// 重要:ISPLAYERID 对任何正整数都为 true,不能用它判断“是否对象ID”,这里用启发式判断。
|
||
bool isLikelyRuntimeObjectId =
|
||
GPDataTypeHelper.ISNPCID(id) ||
|
||
GPDataTypeHelper.ISMATTERID(id) ||
|
||
(id > 100000000); // player ids are typically huge; template ids are usually small
|
||
|
||
var world = EC_Game.GetGameRun()?.GetWorld();
|
||
if (world != null && isLikelyRuntimeObjectId)
|
||
{
|
||
var obj = world.GetObject(id, 0);
|
||
if (obj != null)
|
||
{
|
||
var objPos = obj.transform.position;
|
||
in_table = true;
|
||
return new A3DVECTOR3(objPos.x, objPos.y, objPos.z);
|
||
}
|
||
}
|
||
|
||
// 1) Try live NPC/Monster in scene by template id (best match to "click name -> go to that entity")
|
||
// 1) 先尝试在场景中按模板ID查找活体NPC/怪物(最符合“点名字就去找它”)
|
||
var npcMan = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan : null;
|
||
if (npcMan != null)
|
||
{
|
||
var npc = npcMan.FindNPCByTemplateID(id);
|
||
if (npc != null)
|
||
{
|
||
UnityEngine.Vector3 npcPos = npc.transform.position;
|
||
in_table = true;
|
||
return new A3DVECTOR3(npcPos.x, npcPos.y, npcPos.z);
|
||
}
|
||
}
|
||
|
||
// Fallback to task_npc table (C++: ATaskTemplMan::GetTaskNPCInfo)
|
||
// 回退到task_npc表(C++:ATaskTemplMan::GetTaskNPCInfo)
|
||
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
|
||
if (pMan != null && pMan.TryGetTaskNPCInfo((uint)id, out NPC_INFO info))
|
||
{
|
||
// NOTE: Keep original PW coordinate mapping: ret.Set(x, z, y)
|
||
// 注意:保持原版坐标映射:ret.Set(x, z, y)
|
||
in_table = true;
|
||
return new A3DVECTOR3(info.x, info.z, info.y);
|
||
}
|
||
|
||
// Fallback to coord_data.txt (C++: Configs/Coord_data.txt via CECGame::GetObjectCoord)
|
||
// 回退到 coord_data.txt(C++:Configs/Coord_data.txt,通过 CECGame::GetObjectCoord)
|
||
if (BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName))
|
||
{
|
||
UnityEngine.Debug.Log($"[CECUIHelper] GetTaskObjectCoordinates: Resolved id={id} via coord_data.txt map={mapName} pos=({coordPos.x:F2},{coordPos.y:F2},{coordPos.z:F2})");
|
||
in_table = true;
|
||
return new A3DVECTOR3(coordPos.x, coordPos.y, coordPos.z);
|
||
}
|
||
|
||
UnityEngine.Debug.LogWarning($"[CECUIHelper] GetTaskObjectCoordinates: Not found for id={id} (isLikelyRuntimeObjectId={isLikelyRuntimeObjectId}).");
|
||
return new A3DVECTOR3(0);
|
||
|
||
// TODO: Implement this method properly
|
||
// A3DVECTOR3 ret(0.f);
|
||
// in_table = false;
|
||
// CECGame::ObjectCoords TargetTemp;
|
||
// ret = g_pGame->GetGameRun()->GetHostPlayer()->GetObjectCoordinates(
|
||
// id, TargetTemp, in_table);
|
||
// if (!in_table && MAJOR_MAP == g_pGame->GetGameRun()->GetWorld()->GetInstanceID()) {
|
||
// ATaskTemplMan *pMan = g_pGame->GetTaskTemplateMan();
|
||
// const NPC_INFO* pInfo = pMan->GetTaskNPCInfo(id);
|
||
// if(pInfo) {
|
||
// ret.Set(pInfo->x, pInfo->z, pInfo->y);
|
||
// in_table = true;
|
||
// }
|
||
// }
|
||
// return ret;
|
||
}
|
||
|
||
// Follow coord like C++ CECUIHelper::FollowCoord(enumEICoord, taskId)
|
||
// 像C++的CECUIHelper::FollowCoord(enumEICoord, taskId)一样跟随坐标
|
||
public static bool FollowCoord(int id, int taskId)
|
||
{
|
||
// Resolve coordinates
|
||
bool inTable = false;
|
||
A3DVECTOR3 vPos = GetTaskObjectCoordinates(id, ref inTable);
|
||
if (!inTable)
|
||
{
|
||
// Fallback: use task regions if available (move to the center of the first region)
|
||
// 回退:如果任务有区域信息,则移动到第一个区域的中心
|
||
if (taskId > 0)
|
||
{
|
||
var taskMan = EC_Game.GetTaskTemplateMan();
|
||
var templ = taskMan != null ? taskMan.GetTaskTemplByID((uint)taskId) : null;
|
||
var world = EC_Game.GetGameRun()?.GetWorld();
|
||
int curWorldId = world != null ? world.GetInstanceID() : 0;
|
||
|
||
if (templ != null)
|
||
{
|
||
// Helper local function: pick first region center if in current world
|
||
bool TryUseRegion(uint worldId, uint cnt, BrewMonster.Scripts.Task.Task_Region[] regions, string tag, out A3DVECTOR3 pos)
|
||
{
|
||
pos = new A3DVECTOR3(0);
|
||
if (cnt == 0 || regions == null || regions.Length == 0) return false;
|
||
if (curWorldId != 0 && worldId != 0 && worldId != (uint)curWorldId) return false;
|
||
|
||
var r = regions[0];
|
||
float cx = (r.zvMin.x + r.zvMax.x) * 0.5f;
|
||
float cy = (r.zvMin.y + r.zvMax.y) * 0.5f;
|
||
float cz = (r.zvMin.z + r.zvMax.z) * 0.5f;
|
||
pos = new A3DVECTOR3(cx, cy, cz);
|
||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Fallback {tag} region center=({cx:F2},{cy:F2},{cz:F2}) worldId={worldId} curWorldId={curWorldId}");
|
||
return true;
|
||
}
|
||
|
||
// 1) Deliver zone (often where quest giver is)
|
||
if (templ.m_FixedData.m_bDelvInZone &&
|
||
TryUseRegion(templ.m_FixedData.m_ulDelvWorld, templ.m_FixedData.m_ulDelvRegionCnt, templ.m_FixedData.m_pDelvRegion, "DelvInZone", out vPos))
|
||
{
|
||
inTable = true;
|
||
}
|
||
// 2) Reach-site regions (for reach-site tasks / also can be used as guidance)
|
||
else if (TryUseRegion(templ.m_FixedData.m_ulReachSiteId, templ.m_FixedData.m_ulReachSiteCnt, templ.m_FixedData.m_pReachSite, "ReachSite", out vPos))
|
||
{
|
||
inTable = true;
|
||
}
|
||
// 3) Enter region / leave region zones (some tasks use these to define where objectives happen)
|
||
else if (TryUseRegion(templ.m_FixedData.m_ulEnterRegionWorld, templ.m_FixedData.m_ulEnterRegionCnt, templ.m_FixedData.m_pEnterRegion, "EnterRegion", out vPos))
|
||
{
|
||
inTable = true;
|
||
}
|
||
else if (TryUseRegion(templ.m_FixedData.m_ulLeaveRegionWorld, templ.m_FixedData.m_ulLeaveRegionCnt, templ.m_FixedData.m_pLeaveRegion, "LeaveRegion", out vPos))
|
||
{
|
||
inTable = true;
|
||
}
|
||
}
|
||
}
|
||
|
||
if (!inTable)
|
||
{
|
||
UnityEngine.Debug.LogWarning($"[CECUIHelper] FollowCoord: No coordinates for id={id}, taskId={taskId} (will not move)");
|
||
return false;
|
||
}
|
||
}
|
||
|
||
// Start auto-move work to destination (this is what actually moves the player in this project)
|
||
// 启动自动移动工作(这才是本项目中真正驱动角色移动的系统)
|
||
CECHostPlayer host = EC_Game.GetGameRun()?.GetHostPlayer();
|
||
if (host == null)
|
||
{
|
||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Host player is null");
|
||
return false;
|
||
}
|
||
|
||
CECHPWorkMan wm = host.GetWorkMan();
|
||
if (wm == null)
|
||
{
|
||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: WorkMan is null");
|
||
return false;
|
||
}
|
||
|
||
CECHPWorkMove work = wm.CreateWork(CECHPWork.Host_work_ID.WORK_MOVETOPOS) as CECHPWorkMove;
|
||
if (work == null)
|
||
{
|
||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: Failed to create WORK_MOVETOPOS");
|
||
return false;
|
||
}
|
||
|
||
work.SetDestination(CECHPWorkMove.DestTypes.DEST_2D, vPos);
|
||
wm.StartWork_p2(work);
|
||
|
||
UnityEngine.Debug.Log($"[CECUIHelper] FollowCoord: Started auto-move to ({vPos.x},{vPos.y},{vPos.z}) for id={id}, taskId={taskId}");
|
||
return true;
|
||
}
|
||
}
|
||
} |