629 lines
27 KiB
C#
629 lines
27 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.Text;
|
||
using BrewMonster.Network;
|
||
using BrewMonster.Managers;
|
||
using ModelRenderer.Scripts.Common;
|
||
using ModelRenderer.Scripts.GameData;
|
||
using BrewMonster.Scripts.Task;
|
||
using BrewMonster.Scripts;
|
||
using BrewMonster.Scripts.Chat;
|
||
using CSNetwork.GPDataType;
|
||
using CSNetwork;
|
||
using System.Numerics;
|
||
using UnityEngine;
|
||
|
||
namespace BrewMonster.Scripts.UI
|
||
{
|
||
public class CECUIHelper
|
||
{
|
||
public static string DlgTaskName = "Win_Quest";
|
||
|
||
public static bool DebugInstantTeleportToTaskNpc { get; private set; }
|
||
|
||
public static void SetDebugInstantTeleportToTaskNpc(bool enabled)
|
||
{
|
||
DebugInstantTeleportToTaskNpc = enabled;
|
||
UnityEngine.Debug.Log($"[CECUIHelper] DebugInstantTeleportToTaskNpc={(enabled ? "ON" : "OFF")}");
|
||
}
|
||
|
||
private static bool IsNpcTeleportTarget(int id)
|
||
{
|
||
if (id <= 0)
|
||
return false;
|
||
|
||
ATaskTemplMan pMan = EC_Game.GetTaskTemplateMan();
|
||
if (pMan != null && pMan.TryGetTaskNPCInfo((uint)id, out _))
|
||
return true;
|
||
|
||
elementdataman edm = ElementDataManProvider.GetElementDataMan();
|
||
if (edm != null)
|
||
{
|
||
DATA_TYPE dt = DATA_TYPE.DT_INVALID;
|
||
edm.get_data_ptr((uint)id, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
|
||
return dt == DATA_TYPE.DT_NPC_ESSENCE;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
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
|
||
|
||
A3DVECTOR3 ret = new A3DVECTOR3(0);
|
||
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;
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetTaskObjectCoordinates isLikelyRuntimeObjectId: objPos={objPos.x},{objPos.y},{objPos.z}, inTable={in_table}");
|
||
ret.Set(objPos.x, objPos.y, objPos.z);
|
||
return ret;
|
||
}
|
||
}
|
||
|
||
List<OBJECT_COORD> TargetTemp = new List<OBJECT_COORD>();
|
||
ret = EC_Game.GetGameRun()?.GetHostPlayer().GetObjectCoordinates(id, out TargetTemp, ref in_table) ??
|
||
new A3DVECTOR3(0);
|
||
|
||
// 1) Try live NPC/Monster in scene by template id (best match to "click name -> go to that entity")
|
||
// 1) 先尝试在场景中按模板ID查找活体NPC/怪物(最符合“点名字就去找它”)
|
||
|
||
//This only work in Major map not A61. Skip for now.
|
||
// if(!in_table)
|
||
// {
|
||
// 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;
|
||
// BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates npcMan: npcPos={npcPos.x},{npcPos.y},{npcPos.z}, inTable={in_table}");
|
||
// ret.Set(npcPos.x, npcPos.y, npcPos.z);
|
||
// }
|
||
// }
|
||
// }
|
||
// else
|
||
// {
|
||
// BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates in_table: ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||
// }
|
||
if (ret.x != 0 && ret.y != 0 && ret.z != 0)
|
||
{
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetHostPlayer GetObjectCoordinates True ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||
return ret;
|
||
}
|
||
else
|
||
{
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetHostPlayer TryGetFirstObjectCoord False ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||
}
|
||
|
||
// 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;
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: info.x={info.x}, info.z={info.z}, info.y={info.y}, inTable={in_table}");
|
||
return new A3DVECTOR3(info.x, info.z, info.y);
|
||
}
|
||
else
|
||
{
|
||
BMLogger.Log($"[CECUIHelper] GetTaskObjectCoordinates TryGetTaskNPCInfo: not found for id={id}");
|
||
}
|
||
|
||
// Fallback to coord_data.txt (C++: Configs/Coord_data.txt via CECGame::GetObjectCoord)
|
||
// 回退到 coord_data.txt(C++:Configs/Coord_data.txt,通过 CECGame::GetObjectCoord)
|
||
if (global::BrewMonster.Network.EC_Game.TryGetFirstObjectCoord(id.ToString(), out var coordPos, out var mapName))
|
||
{
|
||
in_table = true;
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetTaskObjectCoordinates TryGetFirstObjectCoord: coordPos.x={coordPos.x}, coordPos.y={coordPos.y}, coordPos.z={coordPos.z}, inTable={in_table}");
|
||
return new A3DVECTOR3(coordPos.x, coordPos.y, coordPos.z);
|
||
}
|
||
|
||
UnityEngine.Debug.LogWarning(
|
||
$"[CECUIHelper] GetTaskObjectCoordinates: Not found for id={id} (isLikelyRuntimeObjectId={isLikelyRuntimeObjectId}).");
|
||
BMLogger.Log(
|
||
$"[CECUIHelper] GetTaskObjectCoordinates default return ret={ret.x},{ret.y},{ret.z}, inTable={in_table}");
|
||
return ret;
|
||
|
||
// 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)
|
||
{
|
||
// If this task is a force-navigate task (or has force_navigate mapping), use bezier-force navigate
|
||
// instead of normal auto-move (which looks like a straight line).
|
||
// 如果该任务是强制导航任务(或在 force_navigate 中有映射),则使用贝塞尔强制导航,
|
||
// 而不是普通自动移动(看起来像直线)。
|
||
if (taskId > 0)
|
||
{
|
||
CECHostPlayer hostPlayer = EC_Game.GetGameRun()?.GetHostPlayer();
|
||
if (hostPlayer != null)
|
||
{
|
||
// Force-navigate dispatch is currently disabled here (see commented-out block below).
|
||
// 强制导航的分发目前在这里被禁用(见下方注释块)。
|
||
|
||
// 2) Fallback: if force_navigate.txt contains this task, treat it as force-navigate even if template flag is missing.
|
||
// 2) 回退:如果 force_navigate.txt 中存在该任务映射,即使模板标记缺失,也按强制导航处理。
|
||
// if (!shouldForceNavigate)
|
||
// {
|
||
// var np = hostPlayer.GetNavigatePlayer();
|
||
// var ctrl = np != null ? np.GetNavigateCtrl() : null;
|
||
// if (ctrl != null)
|
||
// {
|
||
// var tmp = new BrewMonster.Scripts.CECNavigateCtrl.INFO();
|
||
// if (ctrl.GetNavigateInfo(taskId, ref tmp))
|
||
// {
|
||
// shouldForceNavigate = true;
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// if (shouldForceNavigate)
|
||
// {
|
||
// UnityEngine.Debug.Log(
|
||
// $"[CECUIHelper] FollowCoord: taskId={taskId} => force navigate (bezier) instead of normal auto-move");
|
||
// hostPlayer.OnNaviageEvent(taskId,
|
||
// (int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_PREPARE);
|
||
// hostPlayer.OnNaviageEvent(taskId,
|
||
// (int)BrewMonster.Scripts.CECNavigateCtrl.NavigateEvent.EM_BEGIN);
|
||
// return true;
|
||
// }
|
||
|
||
}
|
||
}
|
||
|
||
// Resolve coordinates
|
||
bool inTable = false;
|
||
A3DVECTOR3 vPos = GetTaskObjectCoordinates(id, ref inTable);
|
||
BMLogger.Log($"[CECUIHelper] FollowCoord: vPos={vPos.x},{vPos.y},{vPos.z}, inTable={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);
|
||
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;
|
||
}
|
||
|
||
if (DebugInstantTeleportToTaskNpc && taskId > 0 && IsNpcTeleportTarget(id))
|
||
{
|
||
wm.FinishAllWork(true);
|
||
wm.ClearDelayedWork();
|
||
|
||
bool canUseServerGoto =
|
||
UnityGameSession.Instance != null &&
|
||
UnityGameSession.Instance.GameSession != null &&
|
||
UnityGameSession.Instance.GameSession.IsConnected;
|
||
|
||
if (canUseServerGoto)
|
||
{
|
||
// Online: use server-authoritative relocation to avoid snap-back by MSG_HST_CORRECTPOS.
|
||
UnityGameSession.c2s_CmdGoto(vPos.x, vPos.y, vPos.z);
|
||
|
||
var npcManOnline = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan : null;
|
||
var npcOnline = npcManOnline != null ? npcManOnline.FindNPCByTemplateID(id) : null;
|
||
if (npcOnline != null)
|
||
{
|
||
host.SelectTarget(npcOnline.GetNPCID());
|
||
}
|
||
|
||
UnityEngine.Debug.Log(
|
||
$"[CECUIHelper] Debug teleport requested via server goto to ({vPos.x},{vPos.y},{vPos.z}), templateId={id}, taskId={taskId}");
|
||
return true;
|
||
}
|
||
|
||
// Offline/local fallback: keep old behavior.
|
||
UnityEngine.Vector3 newPos = new UnityEngine.Vector3(vPos.x, vPos.y, vPos.z);
|
||
A3DVECTOR3 newPosA3D = new A3DVECTOR3(vPos.x, vPos.y, vPos.z);
|
||
|
||
host.SetPos(newPos);
|
||
|
||
// Sync move baseline to avoid "old server pos -> new pos" speed spike on next move packet.
|
||
if (host.m_MoveCtrl != null)
|
||
{
|
||
host.m_MoveCtrl.SetLastSevPos(newPosA3D);
|
||
host.m_MoveCtrl.SetHostLastPos(newPosA3D);
|
||
host.m_MoveCtrl.SendStopMoveCmd(newPos, 0f, (int)GPMoveMode.GP_MOVE_RUN);
|
||
}
|
||
|
||
var npcMan = EC_ManMessageMono.Instance != null ? EC_ManMessageMono.Instance.CECNPCMan : null;
|
||
var npc = npcMan != null ? npcMan.FindNPCByTemplateID(id) : null;
|
||
if (npc != null && host.SelectTarget(npc.GetNPCID()))
|
||
{
|
||
CECHPWorkTrace traceWork = wm.CreateNPCTraceWork(npc, taskId);
|
||
if (traceWork != null)
|
||
wm.StartWork_p2(traceWork, true);
|
||
}
|
||
|
||
UnityEngine.Debug.Log($"[CECUIHelper] Teleported locally to task NPC templateId={id}, taskId={taskId}");
|
||
return true;
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
// Prefer AutoPF intelligent route (original PW behavior) instead of naive straight-line.
|
||
// 优先使用 AutoPF 智能寻路(原版 PW 行为),而不是简单直线移动。
|
||
work.SetDestination(CECHPWorkMove.DestTypes.DEST_AUTOPF, vPos);
|
||
|
||
// If this coord link is to an NPC template id, store it so WorkMove can switch to WorkTrace near the NPC.
|
||
// 如果该坐标链接指向 NPC 模板ID,保存下来,以便 WorkMove 接近 NPC 时切换到 WorkTrace。
|
||
if (taskId > 0)
|
||
{
|
||
work.SetTaskNPCInfo(id, taskId);
|
||
}
|
||
|
||
wm.StartWork_p2(work);
|
||
return true;
|
||
}
|
||
|
||
// Follow coord using target coordinate list and trace name
|
||
// 使用目标坐标列表和追踪名称跟随坐标
|
||
public static bool FollowCoord(List<OBJECT_COORD> m_TargetCoord, string m_strTraceName)
|
||
{
|
||
// Validate inputs
|
||
// 验证输入
|
||
if (m_TargetCoord == null || m_TargetCoord.Count == 0)
|
||
{
|
||
UnityEngine.Debug.LogWarning(
|
||
$"[CECUIHelper] FollowCoord: m_TargetCoord is null or empty, traceName={m_strTraceName} (will not move)");
|
||
return false;
|
||
}
|
||
|
||
// Use the first coordinate from the list
|
||
// 使用列表中的第一个坐标
|
||
OBJECT_COORD targetCoord = m_TargetCoord[0];
|
||
A3DVECTOR3 vPos = targetCoord.vPos;
|
||
|
||
// Log map information if available (for debugging)
|
||
// 如果可用,记录地图信息(用于调试)
|
||
if (!string.IsNullOrEmpty(targetCoord.strMap))
|
||
{
|
||
UnityEngine.Debug.Log(
|
||
$"[CECUIHelper] FollowCoord: Target map='{targetCoord.strMap}', traceName={m_strTraceName}");
|
||
}
|
||
|
||
// 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, traceName={m_strTraceName}");
|
||
return false;
|
||
}
|
||
|
||
CECHPWorkMan wm = host.GetWorkMan();
|
||
if (wm == null)
|
||
{
|
||
UnityEngine.Debug.LogError($"[CECUIHelper] FollowCoord: WorkMan is null, traceName={m_strTraceName}");
|
||
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, traceName={m_strTraceName}");
|
||
return false;
|
||
}
|
||
|
||
// Prefer AutoPF intelligent route (original PW behavior) instead of naive straight-line.
|
||
// 优先使用 AutoPF 智能寻路(原版 PW 行为),而不是简单直线移动。
|
||
work.SetDestination(CECHPWorkMove.DestTypes.DEST_AUTOPF, vPos);
|
||
|
||
// If trace name is provided and there are multiple coordinates, we might want to trace through them
|
||
// 如果提供了追踪名称且有多个坐标,我们可能想要追踪它们
|
||
// Note: SetTaskNPCInfo might not be applicable here since we don't have taskId/id
|
||
// 注意:SetTaskNPCInfo 可能不适用于此,因为我们没有 taskId/id
|
||
|
||
wm.StartWork_p2(work);
|
||
|
||
UnityEngine.Debug.Log(
|
||
$"[CECUIHelper] FollowCoord: Started auto-move to ({vPos.x},{vPos.y},{vPos.z}) map={targetCoord.strMap}, traceName={m_strTraceName}, coordCount={m_TargetCoord.Count}");
|
||
return true;
|
||
}
|
||
|
||
public static void AutoMoveStartComplex(A3DVECTOR3 dst, int targetId = 0, int taskId = 0)
|
||
{
|
||
UnityEngine.Debug.Log(
|
||
$"[CECUIHelper] AutoMoveStartComplex: dst={dst}, targetId={targetId}, taskId={taskId}");
|
||
// TODO: Implement this method properly
|
||
// if( CECAutoPolicy.Instance.IsAutoPolicyEnabled() )
|
||
// return;
|
||
ECMSG msg = new ECMSG();
|
||
msg.iManager = 0;
|
||
msg.iSubID = 0;
|
||
msg.dwParam1 = (uint)dst.x;
|
||
msg.dwParam2 = (uint)dst.y;
|
||
msg.dwParam3 = (uint)dst.z;
|
||
msg.dwParam4 = new MsgDataAutoMove(0, targetId, taskId);
|
||
EC_ManMessage.PostMessage(0, 0, 0, msg);
|
||
}
|
||
|
||
/// <summary>
|
||
/// PW client: <c>CECUIHelper::FormatCoordText</c> — replaces <c>@essenceId@</c> (monster template) and
|
||
/// <c>$essenceId$</c> (NPC or mine template) with display names; when coordinates exist, wraps the name in a TMP
|
||
/// coord link like the main quest UI.
|
||
/// </summary>
|
||
public static string FormatCoordText(string szText)
|
||
{
|
||
if (string.IsNullOrEmpty(szText))
|
||
return string.Empty;
|
||
|
||
var sb = new StringBuilder(szText.Length + 48);
|
||
int len = szText.Length;
|
||
int i = 0;
|
||
elementdataman edm = global::BrewMonster.ElementDataManProvider.GetElementDataMan();
|
||
|
||
while (i < len)
|
||
{
|
||
int segStart = i;
|
||
while (i < len && szText[i] != '@' && szText[i] != '$')
|
||
i++;
|
||
sb.Append(szText, segStart, i - segStart);
|
||
if (i >= len)
|
||
break;
|
||
|
||
char open = szText[i];
|
||
i++;
|
||
int idStart = i;
|
||
while (i < len && szText[i] != '@' && szText[i] != '$')
|
||
i++;
|
||
if (i >= len)
|
||
{
|
||
sb.Append(open);
|
||
sb.Append(szText, idStart, i - idStart);
|
||
break;
|
||
}
|
||
|
||
char flag = szText[i];
|
||
string keyword = szText.Substring(idStart, i - idStart);
|
||
i++;
|
||
|
||
if (!uint.TryParse(keyword, out uint essenceId))
|
||
{
|
||
sb.Append(open);
|
||
sb.Append(keyword);
|
||
sb.Append(flag);
|
||
continue;
|
||
}
|
||
|
||
AppendEssencePlaceholder(sb, edm, essenceId, flag);
|
||
}
|
||
|
||
return sb.ToString();
|
||
}
|
||
|
||
static void AppendEssencePlaceholder(StringBuilder sb, elementdataman edm, uint essenceId, char flag)
|
||
{
|
||
string strName = null;
|
||
if (edm != null)
|
||
{
|
||
DATA_TYPE dt = default;
|
||
object p = edm.get_data_ptr(essenceId, ID_SPACE.ID_SPACE_ESSENCE, ref dt);
|
||
if (flag == '$')
|
||
{
|
||
if (dt == DATA_TYPE.DT_NPC_ESSENCE && p is NPC_ESSENCE npc)
|
||
strName = npc.Name;
|
||
else if (dt == DATA_TYPE.DT_MINE_ESSENCE && p is MINE_ESSENCE mine)
|
||
strName = ByteToStringUtils.UshortArrayToUnicodeString(mine.name);
|
||
}
|
||
else if (flag == '@')
|
||
{
|
||
if (dt == DATA_TYPE.DT_MONSTER_ESSENCE && p is MONSTER_ESSENCE mon)
|
||
strName = mon.Name;
|
||
}
|
||
}
|
||
|
||
if (string.IsNullOrEmpty(strName))
|
||
{
|
||
sb.Append("^00FF00?????^FFFFFF");
|
||
return;
|
||
}
|
||
|
||
bool inTable = false;
|
||
GetTaskObjectCoordinates((int)essenceId, ref inTable);
|
||
if (inTable)
|
||
sb.Append("<link=\"coord_").Append(essenceId).Append("\"><color=#00FF00>").Append(strName)
|
||
.Append("</color></link>");
|
||
else
|
||
sb.Append(strName);
|
||
}
|
||
|
||
public static string PolicySpecialCharReplace(
|
||
string szText,
|
||
CHAT_S2C.PolicyChatParameter pPolicyChatPara)
|
||
{
|
||
if (string.IsNullOrEmpty(szText))
|
||
return szText;
|
||
|
||
string result = szText;
|
||
|
||
//result = ReplaceNameInPolicyChat(result, pPolicyChatPara);
|
||
|
||
string subString;
|
||
string key;
|
||
string variable;
|
||
|
||
while (FindSpecialCharInPolicyChat(result, out subString, out key, out variable))
|
||
{
|
||
/*result = ReplaceSpecialCharInPolicyChat(
|
||
result,
|
||
subString,
|
||
key,
|
||
variable,
|
||
pPolicyChatPara);*/
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
public static bool FindSpecialCharInPolicyChat(
|
||
string srcPolicyChat,
|
||
out string subString,
|
||
out string keyInSubString,
|
||
out string variableInSubString)
|
||
{
|
||
subString = null;
|
||
keyInSubString = null;
|
||
variableInSubString = null;
|
||
|
||
if (string.IsNullOrEmpty(srcPolicyChat))
|
||
return false;
|
||
|
||
int posStart = srcPolicyChat.IndexOf("{");
|
||
if (posStart == -1)
|
||
return false;
|
||
|
||
int posEnd = srcPolicyChat.IndexOf("}", posStart);
|
||
if (posEnd == -1)
|
||
return false;
|
||
|
||
subString = srcPolicyChat.Substring(posStart, posEnd - posStart + 1);
|
||
|
||
// remove { }
|
||
string inner = subString.Substring(1, subString.Length - 2);
|
||
|
||
var parts = inner.Split(':');
|
||
|
||
if (parts.Length == 2)
|
||
{
|
||
keyInSubString = parts[0];
|
||
variableInSubString = parts[1];
|
||
return true;
|
||
}
|
||
|
||
return false;
|
||
}
|
||
|
||
public static void RemoveNameFlagFromNPCChat(string chat, out string conv)
|
||
{
|
||
if (string.IsNullOrEmpty(chat))
|
||
{
|
||
conv = string.Empty;
|
||
return;
|
||
}
|
||
|
||
conv = chat.Replace("&", "");
|
||
}
|
||
}
|
||
}
|