Merge branch 'develop' into feature/task-emote

This commit is contained in:
NguyenVanDat
2025-12-12 10:54:44 +07:00
28 changed files with 2518 additions and 1159 deletions
@@ -0,0 +1,230 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &4998203355105501952
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1628544740079825057}
- component: {fileID: 2396821685412871444}
- component: {fileID: 7258523983903280597}
- component: {fileID: 2200587088708437140}
- component: {fileID: 8677225758390490087}
m_Layer: 0
m_Name: FreeLook Camera
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!4 &1628544740079825057
Transform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4998203355105501952}
serializedVersion: 2
m_LocalRotation: {x: 0.23378472, y: -1.6779091e-15, z: 8.341431e-16, w: 0.9722884}
m_LocalPosition: {x: -736.68787, y: 50.89, z: -269.46097}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 0}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
--- !u!114 &2396821685412871444
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4998203355105501952}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f9dfa5b682dcd46bda6128250e975f58, type: 3}
m_Name:
m_EditorClassIdentifier:
Priority:
Enabled: 0
m_Value: 0
OutputChannel: 1
StandbyUpdate: 2
m_StreamingVersion: 20241001
m_LegacyPriority: 0
Target:
TrackingTarget: {fileID: 0}
LookAtTarget: {fileID: 0}
CustomLookAtTarget: 0
Lens:
FieldOfView: 60.000004
OrthographicSize: 5
NearClipPlane: 0.3
FarClipPlane: 200
Dutch: 0
ModeOverride: 0
PhysicalProperties:
GateFit: 2
SensorSize: {x: 21.946, y: 16.002}
LensShift: {x: 0, y: 0}
FocusDistance: 10
Iso: 200
ShutterSpeed: 0.005
Aperture: 16
BladeCount: 5
Curvature: {x: 2, y: 11}
BarrelClipping: 0.25
Anamorphism: 0
BlendHint: 0
--- !u!114 &7258523983903280597
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4998203355105501952}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3b5d7c088409d9a40b7b09aa707777f8, type: 3}
m_Name:
m_EditorClassIdentifier:
TargetOffset: {x: 0, y: 4, z: 0}
TrackerSettings:
BindingMode: 4
PositionDamping: {x: 1, y: 1, z: 1}
AngularDampingMode: 0
RotationDamping: {x: 1, y: 1, z: 1}
QuaternionDamping: 1.52
OrbitStyle: 1
Radius: 1
Orbits:
Top:
Radius: 0.03
Height: 7.26
Center:
Radius: 8.33
Height: 5.99
Bottom:
Radius: 4.29
Height: -4.04
SplineCurvature: 0
RecenteringTarget: 2
HorizontalAxis:
Value: 208
Center: 0
Range: {x: -360, y: 360}
Wrap: 0
Recentering:
Enabled: 0
Wait: 1
Time: 2
Restrictions: 0
VerticalAxis:
Value: -268
Center: 20
Range: {x: -360, y: 360}
Wrap: 1
Recentering:
Enabled: 0
Wait: 1
Time: 2
Restrictions: 0
RadialAxis:
Value: 1
Center: 1
Range: {x: 1, y: 1}
Wrap: 0
Recentering:
Enabled: 0
Wait: 1
Time: 2
Restrictions: 0
--- !u!114 &2200587088708437140
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4998203355105501952}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f38bda98361e1de48a4ca2bd86ea3c17, type: 3}
m_Name:
m_EditorClassIdentifier:
Composition:
ScreenPosition: {x: 0, y: 0}
DeadZone:
Enabled: 0
Size: {x: 0.2, y: 0.2}
HardLimits:
Enabled: 0
Size: {x: 0.8, y: 0.8}
Offset: {x: 0, y: 0}
CenterOnActivate: 1
TargetOffset: {x: 0, y: 1.5, z: 0}
Damping: {x: 0.5, y: 0.5}
Lookahead:
Enabled: 0
Time: 0
Smoothing: 0
IgnoreY: 0
--- !u!114 &8677225758390490087
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 4998203355105501952}
m_Enabled: 0
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 89875cdc57c54474a8a74efd9b2a3b5d, type: 3}
m_Name:
m_EditorClassIdentifier:
ScanRecursively: 1
SuppressInputWhileBlending: 1
IgnoreTimeScale: 0
m_ControllerManager:
Controllers:
- Name: Look Orbit X
Owner: {fileID: 7258523983903280597}
Enabled: 1
Input:
InputAction: {fileID: -5630151704836100654, guid: 1d6e640e716dc4ff6989b73d02023f2b, type: 3}
Gain: 1
LegacyInput:
LegacyGain: 1
CancelDeltaTime: 0
InputValue: 0
Driver:
AccelTime: 0.2
DecelTime: 0.2
- Name: Look Orbit Y
Owner: {fileID: 7258523983903280597}
Enabled: 1
Input:
InputAction: {fileID: -5630151704836100654, guid: 1d6e640e716dc4ff6989b73d02023f2b, type: 3}
Gain: -1
LegacyInput:
LegacyGain: 1
CancelDeltaTime: 0
InputValue: 0
Driver:
AccelTime: 0.2
DecelTime: 0.2
- Name: Orbit Scale
Owner: {fileID: 7258523983903280597}
Enabled: 1
Input:
InputAction: {fileID: -423771258819551211, guid: 1d6e640e716dc4ff6989b73d02023f2b, type: 3}
Gain: -1
LegacyInput:
LegacyGain: 1
CancelDeltaTime: 0
InputValue: 0
Driver:
AccelTime: 0
DecelTime: 0
PlayerIndex: -1
AutoEnableInputs: 0
@@ -0,0 +1,7 @@
fileFormatVersion: 2
guid: cc901dd976e0838499b18a0b802b81d7
PrefabImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,5 +1,98 @@
%YAML 1.1
%TAG !u! tag:unity3d.com,2011:
--- !u!1 &1907375868528687128
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 3488899534283412697}
- component: {fileID: 7907247812297230186}
- component: {fileID: 4758101108332602619}
- component: {fileID: 1387587181254949733}
m_Layer: 0
m_Name: AreaTouchCam
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &3488899534283412697
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1907375868528687128}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 3233441867675090637}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 0, y: 0}
m_SizeDelta: {x: 0, y: 0}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &7907247812297230186
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1907375868528687128}
m_CullTransparentMesh: 1
--- !u!114 &4758101108332602619
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1907375868528687128}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: fe87c0e1cc204ed48ad3b37840f39efc, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 0}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_Sprite: {fileID: 0}
m_Type: 0
m_PreserveAspect: 0
m_FillCenter: 1
m_FillMethod: 4
m_FillAmount: 1
m_FillClockwise: 1
m_FillOrigin: 0
m_UseSpriteMesh: 0
m_PixelsPerUnitMultiplier: 1
--- !u!114 &1387587181254949733
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 1907375868528687128}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: cbda204e0e5552e4692f0f5e234f062d, type: 3}
m_Name:
m_EditorClassIdentifier:
_cinemachineCamera: {fileID: 0}
orbital: {fileID: 0}
minSwipeDistance: 10
speedX: 300
speedY: 500
--- !u!1 &2486392142327362049
GameObject:
m_ObjectHideFlags: 0
@@ -30,7 +123,8 @@ RectTransform:
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 0, y: 0, z: 0}
m_ConstrainProportionsScale: 0
m_Children: []
m_Children:
- {fileID: 3488899534283412697}
m_Father: {fileID: 2780428059708698453}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -59,7 +153,7 @@ Canvas:
m_AdditionalShaderChannelsFlag: 0
m_UpdateRectTransformForStandalone: 0
m_SortingLayerID: 0
m_SortingOrder: 100
m_SortingOrder: -1
m_TargetDisplay: 0
--- !u!114 &1184637750286334292
MonoBehaviour:
@@ -152,3 +246,4 @@ MonoBehaviour:
currentTargetNPCID: 0
dialogResouce: {fileID: 11400000, guid: 540bc8e61556ba4479407a2d68e17580, type: 2}
canvasDlg: {fileID: 7894129013412138377}
cDlgQuickBar: {fileID: 0}
@@ -1,11 +1,45 @@
using Unity.Cinemachine;
using UnityEngine;
using UnityEngine.EventSystems;
namespace BrewMonster
{
public class CameraController : MonoBehaviour
public class CameraController : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler
{
[SerializeField]private CinemachineCamera _cinemachineCamera;
[SerializeField]private CinemachineOrbitalFollow orbital;
private Vector2 currentPos;
private bool fingerDown = false;
Vector2 delta = Vector2.zero;
public float minSwipeDistance = 10f;
public float speedX = 1f;
public float speedY = 1f;
public void OnDrag(PointerEventData eventData)
{
delta = eventData.position - currentPos;
if (delta.magnitude >= minSwipeDistance)
{
orbital.HorizontalAxis.Value += delta.normalized.x * speedX * Time.deltaTime;
//orbital.HorizontalAxis.Value = Mathf.Clamp(orbital.HorizontalAxis.Value, -360f, 360f);
orbital.VerticalAxis.Value += delta.normalized.y * speedY * Time.deltaTime;
orbital.VerticalAxis.Value = Mathf.Clamp(orbital.VerticalAxis.Value, -360f, 360f);
}
currentPos = eventData.position;
}
public void OnPointerDown(PointerEventData eventData)
{
currentPos = eventData.position;
fingerDown = true;
}
public void OnPointerUp(PointerEventData eventData)
{
fingerDown = false;
}
private void Update()
{
//todo: should not always update
@@ -13,6 +47,8 @@ namespace BrewMonster
{
_cinemachineCamera.Follow = CECGameRun.Instance.GetHostPlayer().transform;
_cinemachineCamera.ForceCameraPosition(CECGameRun.Instance.GetHostPlayer().transform.position, Quaternion.identity);
orbital.HorizontalAxis.Value = 208;
orbital.VerticalAxis.Value = -268;
}
}
}
@@ -16,6 +16,19 @@ namespace BrewMonster.Scripts
DEST_STANDJUMP = 4,
DEST_AUTOPF = 5; // Movement type
}
const float A3D_PI = 3.1415926535f;
static float DEG2RAD(float deg) => ((deg) * A3D_PI / 180.0f);
static float pitch_ang_wing => DEG2RAD(45.0f);
static float pitch_ang_fly_sword => DEG2RAD(25.0f);
static float lean_max_ang => DEG2RAD(45.0f);
static float ang_vel_fly => 1.0f / DEG2RAD(60.0f);
static float ang_vel_swim => 1.0f / DEG2RAD(180.0f);
static float pitch_co_wing => pitch_ang_wing / A3D_PI;
static float pitch_co_fly_sword => pitch_ang_fly_sword / A3D_PI;
const float sidle_co = .5f;
static float sidle_co_r => 1.0f - sidle_co;
static float push_pitch_vel_wing => pitch_ang_wing / 0.5f;
static float push_pitch_vel_fly_sword => pitch_ang_fly_sword / 0.5f;
private const uint MoveInputMask = 0x0F; // MD_FORWARD | MD_RIGHT | MD_BACK | MD_LEFT
@@ -865,8 +878,300 @@ namespace BrewMonster.Scripts
else
m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 0, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
else if (m_iDestType == DestTypes.DEST_2D)
{
Vector3 vPushDir = Vector3.zero;
m_pHost.GetPushDir(ref vPushDir, (uint)MOVE_DIR.MD_ALL, 0f);
vPushDir.x = vPushDir.z = 0.0f;
float fSpeed1H = m_pHost.m_vVelocity.MagnitudeH();
float fSpeed1V = m_pHost.m_vVelocity.y;
A3DVECTOR3 vMoveDirH = m_vMoveDest - vCurPos;
vMoveDirH.y = 0.0f;
float fDistH = vMoveDirH.Normalize();
float pa = 0.0f;
// Calculate the distance to reduce velocity to 0
float s = -0.5f * fSpeed1H * fSpeed1H / na;
if (fDistH > s - 0.01f)
pa = CECHostMove.EC_PUSH_ACCE;
float fSpeed2H = fSpeed1H + (pa + na) * fDeltaTime;
if (Math.Abs(pa - 0f) < float.Epsilon && fSpeed2H < 0.0f)
fSpeed2H = 0.0f; // Only resistance couldn't generate negative speed
else if (fSpeed2H > fMaxSpeed)
fSpeed2H = fMaxSpeed;
Glide(fDistH / fMaxSpeed, vMoveDirH, fDeltaTime, bInAir);
vMoveDirH = m_pHost.GetModelMoveDir();
vMoveDirH.y = 0;
vMoveDirH.Normalize();
// Vertical speed
float fSpeed2V = CalcFlySwimVertSpeed(fSpeed1V, vPushDir.y, CECHostMove.EC_PUSH_ACCE, fDeltaTime);
A3DVECTOR3 vVel2 = vMoveDirH * fSpeed2H + GPDataTypeHelper.g_vAxisY * fSpeed2V;
// Air/water move
vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vVel2, fDeltaTime, bInAir);
if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3)
{
vVel2.Clear();
Finish();
}
else
{
// Reached destination ?
A3DVECTOR3 vMoveDelta = vCurPos - m_pHost.GetPos();
vMoveDelta.y = 0.0f;
float fMoveDistH = vMoveDelta.Normalize();
if (fMoveDistH >= fDistH)
{
vVel2.x = vVel2.z = 0.0f;
if (Math.Abs(vVel2.y - 0f) < float.Epsilon)
{
Finish();
}
else
{
if (m_bUseAutoMoveDialog)
{
Finish();
vVel2.y = 0.0f;
}
m_iDestType = DestTypes.DEST_PUSH;
}
}
}
m_pHost.SetPos(EC_Utility.ToVector3(vCurPos));
m_pHost.m_vVelocity = vVel2;
if (m_bFinished)
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), vVel2.Magnitude(), iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
else
m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 0, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
else if (m_iDestType == DestTypes.DEST_3D)
{
float fSpeed1 = m_pHost.m_vVelocity.Magnitude();
A3DVECTOR3 vMoveDir = m_vMoveDest - vCurPos;
float fDist = vMoveDir.Normalize();
float pa = 0.0f;
// Calculate the distance to reduce velocity to 0
float s = -0.5f * fSpeed1 * fSpeed1 / na;
if (fDist > s - 0.01f)
pa = CECHostMove.EC_PUSH_ACCE;
float fSpeed2 = fSpeed1 + (pa + na) * fDeltaTime;
if (Math.Abs(pa - 0f) < float.Epsilon && fSpeed2 < 0.0f)
fSpeed2 = 0.0f; // Only resistance couldn't generate negative speed
AAssist.a_Clamp(ref fSpeed2, -fMaxSpeed, fMaxSpeed);
Vector3 vMoveDirH = new Vector3(vMoveDir.x, 0.0f, vMoveDir.z);
if (vMoveDirH != Vector3.zero)
{
//m_pHost.StartModelMove(vMoveDirH, g_vAxisY, 100);
//m_pHost.ChangeModelTargetDirAndUp(vMoveDirH, g_vAxisY);
m_pHost.SetRotationHP(vMoveDirH);
}
// Air/water move
// A3DVECTOR3 vVel1 = vMoveDir * fSpeed1;
A3DVECTOR3 vVel2 = vMoveDir * fSpeed2;
vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(vMoveDir, fSpeed2, fDeltaTime, bInAir, false);
if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3)
{
vVel2.Clear();
Finish();
}
else
{
// Reached destination ?
A3DVECTOR3 vMoveDelta = vCurPos - m_pHost.GetPos();
float fMoveDist = vMoveDelta.Normalize();
if (fMoveDist >= fDist)
{
vVel2.Clear();
Finish();
m_bUseAutoMoveDialog = false;
}
}
m_pHost.SetPos(EC_Utility.ToVector3(vCurPos));
m_pHost.m_vVelocity = vVel2;
if (m_bFinished)
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
else
m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 1, m_vMoveDest, vVel2, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
else if (m_iDestType == DestTypes.DEST_PUSH)
{
Vector3 vPushDir = Vector3.zero, vUp;
bool bPush = m_pHost.GetPushDir(ref vPushDir, (uint)(MOVE_DIR.MD_FORWARD | MOVE_DIR.MD_BACK | MOVE_DIR.MD_LEFT | MOVE_DIR.MD_RIGHT), fDeltaTime);
if (!bPush)
{
//vPushDir = m_pHost.GetCameraCoord().GetDir();
vPushDir.y = 0;
vPushDir.Normalize();
}
int nPitchDir = 0;
if ((m_pHost.m_dwMoveRelDir & (int)(MOVE_DIR.MD_LEFT | MOVE_DIR.MD_RIGHT)) == 0)
{
A3DVECTOR3 vOldDir = m_pHost.GetModelMoveDir();
vOldDir.y = 0;
vOldDir.Normalize();
A3DVECTOR3 vNewDir = EC_Utility.ToA3DVECTOR3(vPushDir);
vNewDir.y = 0;
vNewDir.Normalize();
float fAngle = A3DVECTOR3.DotProduct(vOldDir, vNewDir);
if (fAngle < 1.0f - 1e-4)
{
A3DVECTOR3 vUp_fAngle = A3DVECTOR3.CrossProduct(vOldDir, vNewDir);
if (vUp_fAngle.y > 0) nPitchDir = 1;
else nPitchDir = -1;
if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_BACK) != 0)
nPitchDir = -nPitchDir;
}
}
if (m_pHost.m_dwMoveRelDir != 0)
{
if ((m_pHost.m_dwMoveRelDir & ~(uint)(MOVE_DIR.MD_ABSDOWN | MOVE_DIR.MD_ABSUP)) != 0)
{
float fPitchDelta = (/*m_pHost.UsingWing()*/m_pHost.GetWingType() == enumWingType.WINGTYPE_WING ? push_pitch_vel_wing : push_pitch_vel_fly_sword) * fDeltaTime;
if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_LEFT) != 0 || nPitchDir == -1)
{
if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_BACK) != 0)
m_fPushPitch -= fPitchDelta;
else
m_fPushPitch += fPitchDelta;
}
else if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_RIGHT) != 0 || nPitchDir == 1)
{
if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_BACK) != 0)
m_fPushPitch += fPitchDelta;
else
m_fPushPitch -= fPitchDelta;
}
else if (m_fPushPitch > fPitchDelta)
m_fPushPitch -= fPitchDelta;
else if (m_fPushPitch < -fPitchDelta)
m_fPushPitch += fPitchDelta;
else
m_fPushPitch = 0;
// TO DO: fix after
//A3DVECTOR3 vRight = m_pHost.GetCameraCoord().GetRight();
//float fLean = -Math.Asin(m_pHost.GetCameraCoord().GetDir().y);
//AAssist.a_Clamp(ref fLean, -lean_max_ang, lean_max_ang);
//if (Math.Abs(fLean) > DEG2RAD(3.0f))
//{
// vPushDir = a3d_RotatePosAroundAxis(vPushDir, vRight, fLean);
// vUp = a3d_RotatePosAroundAxis(g_vAxisY, vRight, fLean);
//}
//else
// vUp = EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY);
//float pitch_ang = /*m_pHost.UsingWing()*/ m_pHost.GetWingType() == enumWingType.WINGTYPE_WING ? pitch_ang_wing : pitch_ang_fly_sword;
//AAssist.a_Clamp(ref m_fPushPitch, -pitch_ang, pitch_ang);
//if (Math.Abs(m_fPushPitch) > DEG2RAD(4.0f))
// vUp = a3d_RotatePosAroundAxis(vUp, vPushDir, m_fPushPitch);
//m_pHost.StartModelMove(vPushDir, vUp, 0);
}
// if (bPush)
if (bPush || (m_pHost.m_dwMoveRelDir & (uint)(MOVE_DIR.MD_ABSDOWN | MOVE_DIR.MD_ABSUP)) != 0)
{
// float pa = bPush ? EC_PUSH_ACCE : 0.0f;
float pa = CECHostMove.EC_PUSH_ACCE;
float na1 = m_pHost.m_iMoveEnv == (int)MoveEnvironment.MOVEENV_AIR ? CECHostMove.EC_NACCE_AIR : CECHostMove.EC_NACCE_WATER;
float fAccel = pa + na1;
float fSpeed1 = m_pHost.m_vVelocity.Magnitude();
float fSpeed2 = fSpeed1 + fAccel * fDeltaTime;
AAssist.a_Clamp(ref fSpeed2, 0.0f, fMaxSpeed);
// Air/water move
Vector3 vVelDir = Vector3.zero;
if (bPush)
vVelDir = vPushDir;
if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_ABSDOWN) != 0)
{
vVelDir += -EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY);
vVelDir.Normalize();
}
else if ((m_pHost.m_dwMoveRelDir & (uint)MOVE_DIR.MD_ABSUP) != 0)
{
vVelDir += EC_Utility.ToVector3(GPDataTypeHelper.g_vAxisY);
vVelDir.Normalize();
}
// A3DVECTOR3 vVel = vPushDir * fSpeed2;
Vector3 vVel = vVelDir * fSpeed2;
vCurPos = m_pHost.m_MoveCtrl.AirWaterMove(EC_Utility.ToA3DVECTOR3(vVel), fDeltaTime, bInAir);
if (m_pHost.m_MoveCtrl.MoveBlocked() >= 3)
{
Finish();
m_pHost.m_vVelocity.Clear();
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
else
{
m_pHost.SetPos(EC_Utility.ToVector3(vCurPos));
if (m_bUseAutoMoveDialog)
{
m_fAutoHeight = vCurPos.y / 10.0f;
}
m_pHost.m_vVelocity = EC_Utility.ToA3DVECTOR3(vVel);
m_pHost.m_MoveCtrl.SendMoveCmd(vCurPos, 2, GPDataTypeHelper.g_vOrigin, EC_Utility.ToA3DVECTOR3(vVel), iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
}
}
else
{
if (!m_bUseAutoMoveDialog)
Finish();
else
m_iDestType = DestTypes.DEST_2D;
m_fPushPitch = 0;
m_pHost.m_vVelocity.Clear();
m_pHost.m_MoveCtrl.SendStopMoveCmd(EC_Utility.ToVector3(vCurPos), fMaxSpeed, iMoveMode | (int)GPMoveMode.GP_MOVE_RUN);
}
}
else if (m_iDestType == DestTypes.DEST_STANDJUMP)
{
// If host player fly off when jumping up, code will go here. In the
// case, just stop move work is well.
Finish();
}
else if (IsAutoPF())
{
//CECIntelligentRoute::Instance().ResetSearch();
m_bSwitchTo2D = true;
}
return true;
}
// Start gliding
protected void Glide(float fMoveTime, A3DVECTOR3 vMoveDirH, float fDeltaTime, bool bFly)
{
@@ -53,6 +53,12 @@ namespace BrewMonster.Scripts.Managers
private static bool s_hasPendingCash;
private static int s_pendingCashAmount;
// Flags to prevent log spam for extended description warnings
// 防止扩展描述警告日志刷屏的标志
private static bool m_HasLoggedExtDescNull = false;
private static bool m_HasLoggedExtDescNotInit = false;
private static bool m_HasLoggedExtDescError = false;
private InventoryModel model;
private InventoryView view;
@@ -828,16 +834,81 @@ namespace BrewMonster.Scripts.Managers
{
// Fallback to legacy string-table description if centralised pipeline returns empty
string itemDescription = GetItemDescription(item.m_tid);
string itemExtendedDesc = GetItemExtendedDescription(item.m_tid);
descriptionText?.Set(itemDescription ?? "No description available");
extendedDescText?.Set(itemExtendedDesc ?? "");
}
// Get extended description exactly like C++ code: g_pGame->GetItemExtDesc(m_tid)
// C++ code doesn't check IsInitialized() - it just calls GetWideString() directly
// 完全按照C++代码获取扩展描述:g_pGame->GetItemExtDesc(m_tid)
// C++代码不检查IsInitialized() - 它直接调用GetWideString()
string szExtDesc = null;
try
{
var itemExtDescTab = EC_Game.GetItemExtDesc();
if (itemExtDescTab != null)
{
// First try to get mapped message ID (like TryGetItemExtDesc does)
// 首先尝试获取映射的消息ID(如TryGetItemExtDesc所做)
if (EC_Game.TryGetItemMsg(item.m_tid, out int messageId, out int displayMode))
{
szExtDesc = itemExtDescTab.GetWideString(messageId);
}
// Fallback: direct lookup using tid (exactly like C++: m_ItemExtDesc.GetWideString(tid))
// 回退:直接使用tid查找(完全像C++m_ItemExtDesc.GetWideString(tid)
if (string.IsNullOrEmpty(szExtDesc))
{
szExtDesc = itemExtDescTab.GetWideString(item.m_tid);
}
}
}
catch (System.Exception ex)
{
// Only log once to avoid spam
// 仅记录一次以避免垃圾日志
if (!m_HasLoggedExtDescError)
{
Debug.LogWarning($"[InventoryUI] Error getting extended description: {ex.Message}");
m_HasLoggedExtDescError = true;
}
}
// Display extended description if found (exactly like C++ checks: if (!szExtDesc || !szExtDesc[0]))
// 如果找到扩展描述则显示(完全像C++检查:if (!szExtDesc || !szExtDesc[0])
string displayText = !string.IsNullOrEmpty(szExtDesc)
? szExtDesc.Replace("\\r", "\n")
: "";
// Debug logging to diagnose issues
// 调试日志以诊断问题
if (string.IsNullOrEmpty(displayText))
{
Debug.Log($"[InventoryUI] Extended description is empty for tid={item.m_tid}. szExtDesc was null/empty.");
}
else
{
Debug.Log($"[InventoryUI] Found extended description for tid={item.m_tid}, length={displayText.Length}");
}
// Setup equip and drop buttons
SetupEquipButton(package, item);
SetupDropButton(package, item);
// Show panel first
// 先显示面板
ShowDetailPanel(true);
// Set text directly - if this causes rebuild issues, we'll use coroutine
// 直接设置文本 - 如果这导致重建问题,我们将使用协程
if (extendedDescText != null)
{
extendedDescText.Set(displayText);
Debug.Log($"[InventoryUI] Set extended description text, extendedDescText is {(extendedDescText == null ? "null" : "not null")}");
}
else
{
Debug.LogWarning("[InventoryUI] extendedDescText is null! Check Inspector assignment.");
}
}
private void SetupEquipButton(byte package, EC_IvtrItem item)
@@ -1251,14 +1251,180 @@ namespace BrewMonster.Scripts.Managers
m_strDesc += "\n";
}
// Add extend description to description string / 添加扩展描述到描述字符串
protected void AddExtDescText()
{
// Extend description from item_ext_desc.txt via EC_Game
string ext = TryGetItemExtDesc();
if (!string.IsNullOrEmpty(ext))
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
string szExtDesc = TryGetItemExtDesc();
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
// if (!szExtDesc || !szExtDesc[0])
// return;
m_strDesc += "\\r";
bool bAddLine = true;
// Add special properties description / 添加特殊属性描述
var pDescTab = EC_Game.GetItemDesc();
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
{
AddDescText(0, true, ext);
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
{
bAddLine = false;
if (pDescTab != null && pDescTab.IsInitialized())
{
string szCol = pDescTab.GetWideString(green);
if (!string.IsNullOrEmpty(szCol))
{
m_strDesc += szCol;
}
}
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
string desc = pDescTab.GetWideString(2000); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_DROP placeholder - adjust this value
string desc = pDescTab.GetWideString(2001); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2002); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2003); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
string desc = pDescTab.GetWideString(2004); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
string desc = pDescTab.GetWideString(2005); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
string desc = pDescTab.GetWideString(2006); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
string desc = pDescTab.GetWideString(2007); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
string desc = pDescTab.GetWideString(2008); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
{
m_strDesc += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
string desc = pDescTab.GetWideString(2009); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
m_strDesc += desc;
}
}
}
else
{
TrimLastReturn();
}
}
else
{
TrimLastReturn();
}
if (string.IsNullOrEmpty(szExtDesc))
return;
// Extend description is below special properties / 扩展描述在特殊属性下方
if (bAddLine)
m_strDesc += "\\r\\r";
else
m_strDesc += "\\r";
m_strDesc += szExtDesc;
}
protected void AddExpireTimeDesc()
@@ -1402,6 +1568,182 @@ namespace BrewMonster.Scripts.Managers
return string.Empty;
}
/// <summary>
/// Get extended description text for UI display.
/// This method mirrors AddExtDescText() logic but returns a string instead of modifying m_strDesc.
/// 此方法镜像AddExtDescText()逻辑,但返回字符串而不是修改m_strDesc
/// </summary>
public string GetExtendedDescText()
{
string result = string.Empty;
// Get extended description from item_ext_desc.txt using tid / 使用tid从item_ext_desc.txt获取扩展描述
string szExtDesc = TryGetItemExtDesc();
// Note: Original C++ had early return commented out / 注意:原始C++代码的早期返回被注释掉了
// if (!szExtDesc || !szExtDesc[0])
// return;
result += "\\r";
bool bAddLine = true;
// Add special properties description / 添加特殊属性描述
var pDescTab = EC_Game.GetItemDesc();
// Note: ITEMDESC_COL2_BRIGHTBLUE constant - adjust based on actual string table / 注意:ITEMDESC_COL2_BRIGHTBLUE常量 - 根据实际字符串表调整
int green = 1000; // ITEMDESC_COL2_BRIGHTBLUE placeholder - adjust this value
if (m_iCID != (int)InventoryClassId.ICID_GOBLIN) // goblin does not need to display these special properties / 地精不需要显示这些特殊属性
{
// Exact C++ logic: (PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
// 精确的C++逻辑:(PROC_NO_USER_TRASH) || (!PROC_BINDING && (PROC_DROPWHENDIE || ...))
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0
|| (!((m_iProcType & (int)ProcType.PROC_BINDING) != 0)
&& ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0
|| (m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_SELLABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0
|| (m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0
|| (m_iProcType & (int)ProcType.PROC_USE) != 0
|| (m_iProcType & (int)ProcType.PROC_DEADDROP) != 0
|| (m_iProcType & (int)ProcType.PROC_OFFLINE) != 0
|| (m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)))
{
bAddLine = false;
if (pDescTab != null && pDescTab.IsInitialized())
{
string szCol = pDescTab.GetWideString(green);
if (!string.IsNullOrEmpty(szCol))
{
result += szCol;
}
}
// Note: These message IDs are placeholders - adjust based on actual string table / 注意:这些消息ID是占位符 - 根据实际字符串表调整
if ((m_iProcType & (int)ProcType.PROC_DROPWHENDIE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DEAD_PROTECT placeholder - adjust this value
string desc = pDescTab.GetWideString(2000); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DROPPABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_DROP placeholder - adjust this value
string desc = pDescTab.GetWideString(2001); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_SELLABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2002); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_TRADEABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_PLAYER_TRADE placeholder - adjust this value
string desc = pDescTab.GetWideString(2003); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DISAPEAR) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_LEAVE_SCENE_DISAPEAR placeholder - adjust this value
string desc = pDescTab.GetWideString(2004); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_USE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_USE_AFTER_PICK_UP placeholder - adjust this value
string desc = pDescTab.GetWideString(2005); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_DEADDROP) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_DEAD placeholder - adjust this value
string desc = pDescTab.GetWideString(2006); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_OFFLINE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_DROP_WHEN_OFFLINE placeholder - adjust this value
string desc = pDescTab.GetWideString(2007); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_UNREPAIRABLE) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_UNREPAIRABLE placeholder - adjust this value
string desc = pDescTab.GetWideString(2008); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
if ((m_iProcType & (int)ProcType.PROC_NO_USER_TRASH) != 0)
{
result += "\\r";
if (pDescTab != null && pDescTab.IsInitialized())
{
// ITEMDESC_NO_USER_TRASH placeholder - adjust this value
string desc = pDescTab.GetWideString(2009); // Placeholder ID
if (!string.IsNullOrEmpty(desc))
result += desc;
}
}
}
}
if (string.IsNullOrEmpty(szExtDesc))
return result;
// Extend description is below special properties / 扩展描述在特殊属性下方
if (bAddLine)
result += "\\r\\r";
else
result += "\\r";
result += szExtDesc;
return result;
}
#endregion
}
@@ -363,7 +363,7 @@ namespace BrewMonster {
}
// Air/Water move
A3DVECTOR3 AirWaterMove(A3DVECTOR3 vDir, float fSpeed, float fTime, bool bInAir, bool bTrace/* false */)
public A3DVECTOR3 AirWaterMove(A3DVECTOR3 vDir, float fSpeed, float fTime, bool bInAir, bool bTrace/* false */)
{
A3DVECTOR3 vRealDir = vDir;
+253 -2
View File
@@ -1,4 +1,5 @@
using BrewMonster.Scripts.World;
using BrewMonster.Network;
using BrewMonster.Scripts.World;
using CSNetwork.GPDataType;
using System;
using UnityEngine;
@@ -11,8 +12,76 @@ namespace BrewMonster.Scripts
// Cho phép CECHostMove gán mask theo scene (giữ linh hoạt nhưng không phá cấu trúc)
public static LayerMask BrushMask { get; set; } = 1<<7;
public static LayerMask TerrainMask { get; set; } = 1<<6;
public static LayerMask WaterMask { get; set; } = 1<<8;
const float LOCAL_EPSILON = 1e-5f;
const float FLY_MAX_HEIGHT = 800.0f; // ·ÉÐеÄ×î´ó¸ß¶È£¡
// change this array when some new submap can go!
static uint[,] available_maps =
{
{0, 0, 0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 1, 1, 1, 1},
{0, 1, 1, 1, 1, 1, 1, 1},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 1, 1, 1, 1, 1, 1, 0},
{0, 0, 0, 0, 0, 1, 1, 0},
{1, 1, 0, 0, 0, 0, 0, 0},
};
static uint[,] available_maps4x4 =
{
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 1, 1, 0},
{0, 0, 0, 0},
};
static uint[,] available_maps3x3 =
{
{0, 0, 0},
{0, 1, 0},
{0, 0, 0},
};
static uint[,] available_maps2x2 =
{
{1, 1},
{0, 0},
};
static uint[,] available_maps_137 =
{
{0, 0, 0, 0, 0, 0},
{0, 1, 1, 1, 0, 0},
{0, 0, 0, 0, 0, 0},
};
static uint[,] available_maps_161=
{
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 0, 0, 0},
};
static uint[,] available_maps_162 =
{
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 0, 0, 0},
};
static uint[,] available_maps_163 =
{
{0, 0, 0, 0},
{0, 1, 1, 0},
{0, 1, 0, 0},
{0, 0, 0, 0},
};
//[Flags]
public class CDR_EVN
@@ -22,7 +91,7 @@ namespace BrewMonster.Scripts
CDR_WATER = 0x4;
}
static LayerMask UsedMask_Ground() => BrushMask | TerrainMask;
static LayerMask UsedMask_Ground() => BrushMask | TerrainMask | WaterMask;
static bool CollideWithEnv(env_trace_t pEnvTrc)
{
@@ -569,13 +638,195 @@ namespace BrewMonster.Scripts
static void AirMove(ref ON_AIR_CDR_INFO awmInfo)
{
float DIST_EPSILON = 1e-4f;
int MAX_TRY = 1;
float VEL_REFLECT = 0.0f;
float fTime = awmInfo.t;
//@todo : is it necessary to clamp the speed? By Kuiwu[20/9/2005]
float fSpeed = awmInfo.fSpeed;
if (fSpeed * fTime < DIST_EPSILON)
{
//@todo : set the output param. By Kuiwu[20/9/2005]
return;
}
A3DVECTOR3 vStart = new A3DVECTOR3(awmInfo.vCenter);
A3DVECTOR3 vExt = new A3DVECTOR3(awmInfo.vExtent);
A3DVECTOR3 vVelDir = new A3DVECTOR3(awmInfo.vVelDir);
float dtp = 0f;
A3DVECTOR3 vVelocity = new A3DVECTOR3(vVelDir* fSpeed);
if ((dtp = A3DVECTOR3.DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f)
{
//vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal*dtp * 0.01f) * fSpeed;
vVelocity = (vVelDir - awmInfo.vTPNormal * dtp) * fSpeed;
}
A3DVECTOR3 vDelta = new A3DVECTOR3(vVelocity* fTime),
vNormal = new A3DVECTOR3(),
vFinalPos = new A3DVECTOR3(vStart);
int nTry = 0;
bool bClear = true;
env_trace_t trcInfo;
trcInfo.bWaterSolid = true;
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER;
trcInfo.vExt = vExt;
RaycastHit hit;
while (nTry < MAX_TRY)
{
if (vDelta.SquaredMagnitude() < DIST_EPSILON)
{
break;
}
trcInfo.vStart = vStart;
trcInfo.vDelta = vDelta;
trcInfo.vTerStart = vStart;
trcInfo.vTerStart.y -= vExt.y;
trcInfo.vWatStart = vStart;
trcInfo.vWatStart.y -= vExt.y;
//bClear = !CollideWithEnv(&trcInfo);
bClear = !Physics.BoxCast(EC_Utility.ToVector3(vStart),
EC_Utility.ToVector3(vExt),
EC_Utility.ToVector3(vStart + vVelDir).normalized,
out hit,
Quaternion.identity,
EC_Utility.ToVector3(vDelta).magnitude,
UsedMask_Ground());
++nTry;
if (bClear)
{
vFinalPos = vStart + vDelta;
}
else
{
vFinalPos = EC_Utility.ToA3DVECTOR3(hit.point);
vNormal = EC_Utility.ToA3DVECTOR3(hit.normal);
}
//vStart += vDelta * trcInfo.fFraction;
//vFinalPos = vStart;
//fTime -= fTime * trcInfo.fFraction;
//vNormal = trcInfo.vHitNormal;
//fSpeed = A3DVECTOR3.Normalize(vVelocity,out vVelDir);
//fSpeed *= (1 - nTry * 0.1f);
//dtp = A3DVECTOR3.DotProduct(vNormal, vVelDir);
//vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed;
//vDelta = vVelocity * fTime;
}
//@note : prevent moving to the invalid area. By Kuiwu[20/9/2005]
if (!IsPosInAvailableMap(vFinalPos))
{
//@todo : set some flag to notify the caller? By Kuiwu[20/9/2005]
return;
}
//too high
if (vFinalPos.y > FLY_MAX_HEIGHT - 2.0f)
{
return;
}
//see if meet height thresh
Vector3 posVStart = EC_Utility.ToVector3(vFinalPos);
float fDeltaY = awmInfo.fHeightThresh + 0.1f;
if (!Physics.Raycast(posVStart,
(posVStart + Vector3.down * fDeltaY).normalized,
out hit,
fDeltaY,
UsedMask_Ground()))
{
awmInfo.vCenter = vFinalPos;
awmInfo.vTPNormal = vNormal;
return;
}
else
{
vFinalPos = EC_Utility.ToA3DVECTOR3(hit.point);
vNormal = EC_Utility.ToA3DVECTOR3(hit.normal);
awmInfo.vCenter = vFinalPos;
awmInfo.vTPNormal = vNormal;
}
}
static void WaterMove(ref ON_AIR_CDR_INFO awmInfo)
{
}
//////////////////////////////////////////////////////////////////////////
// Note by wenfeng, 05-09-16
// This function is only for the Big world but not applicable for the
// Instance world!
//
//////////////////////////////////////////////////////////////////////////
static bool IsPosInAvailableMap(A3DVECTOR3 vPos)
{
float x, z;
int su, sv;
//bool bFlag = true;
CECWorld pWorld = EC_Game.GetGameRun().GetWorld();
if(pWorld != null)
{
int idInst = pWorld.GetInstanceID();
CECInstance pInst = EC_Game.GetGameRun().GetInstance(idInst);
if (pInst == null)
return false;
x = vPos.x + pInst.GetColNum() * 512.0f;
z = pInst.GetRowNum() * 512.0f - vPos.z;
su = (int) (x / 1024.0f);
sv = (int) (z / 1024.0f);
if (su >= pInst.GetColNum() || su< 0 ||
sv >= pInst.GetRowNum() || sv< 0)
return false;
switch (idInst)
{
case 1:
return available_maps[sv, su] != 0 && vPos.x <= 3877.0f; // ½ûÖ¹ëÊ×åÁÙ½ü¿´µ½µØÍ¼±ßÔµ
case 121:
case 122:
return available_maps4x4[sv, su] != 0? true : false;
case 118:
case 119:
case 120:
case 123:
case 125:
return available_maps3x3[sv, su] != 0? true : false;
case 134:
return available_maps2x2[sv, su] != 0 ? true : false;
case 137:
return available_maps_137[sv, su] != 0 ? true : false;
case 161:
return available_maps_161[sv, su] != 0;
case 162:
return available_maps_162[sv, su] != 0 ? true : false;
case 163:
return available_maps_163[sv, su] != 0 ? true : false;
default:
return true;
}
}
else
return true;
}
}
public struct OtherPlayer_Move_Info
{
@@ -6,6 +6,7 @@ using UnityEngine;
using UnityEngine.UI;
using TMPro;
using PerfectWorld.Scripts.Shop;
using PerfectWorld.Scripts.Managers;
using BrewMonster.Scripts.Managers;
using BrewMonster.Network;
using CSNetwork.C2SCommand;
@@ -13,10 +14,9 @@ using CSNetwork.C2SCommand;
public class NPCShopDetailPanel : MonoBehaviour
{
[Header("UI Components")]
public TextMeshProUGUI itemNameText;
public TextMeshProUGUI itemDescriptionText;
public TextOutlet itemDescriptionText;
public Image itemIconImage;
public TextMeshProUGUI itemPriceText;
//public TextMeshProUGUI itemPriceText;
[Header("Action Buttons")]
public Button closeButton;
@@ -55,15 +55,11 @@ public class NPCShopDetailPanel : MonoBehaviour
return;
}
// Set item name
if (itemNameText != null)
itemNameText.text = currentItem.name;
// Set item description
if (itemDescriptionText != null)
{
string description = GetItemDescription((int)currentItem.id);
itemDescriptionText.text = description;
itemDescriptionText.Set(description);
}
// Set price (use first available price)
@@ -80,8 +76,8 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
if (itemPriceText != null)
itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
// if (itemPriceText != null)
// itemPriceText.text = price > 0 ? $"Price: {price}" : "Price: N/A";
// Load icon
if (itemIconImage != null)
@@ -114,7 +110,7 @@ public class NPCShopDetailPanel : MonoBehaviour
}
}
string GetItemDescription(int itemId)
string GetItemDescription(int itemId)
{
// First check if description is already in the item data
if (!string.IsNullOrEmpty(currentItem.desc))
@@ -122,27 +118,48 @@ public class NPCShopDetailPanel : MonoBehaviour
return currentItem.desc;
}
// Otherwise, build description using the same central pipeline as inventory:
try
// Try to use EC_IvtrEquip description for equipment items (like EC_InventoryUI does)
try
{
// Create EC_IvtrEquip for equipment items - this will work for weapons and armor
EC_IvtrEquip equipment = new EC_IvtrEquip(itemId, 0);
// For NPC shop items, we typically have static data only
// Try to get description using GetNormalDesc() which works with base stats
string equipDesc = equipment.GetNormalDesc();
if (!string.IsNullOrEmpty(equipDesc))
{
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
if (item != null)
// Replace C++ style "\r" line separators with real newlines for TMP
return equipDesc.Replace("\\r", "\n");
}
}
catch (System.Exception ex)
{
// If EC_IvtrEquip fails, fall through to EC_IvtrItem method
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get equipment description for item {itemId}: {ex.Message}");
}
// Fallback: build description using EC_IvtrItem (for non-equipment items)
try
{
EC_IvtrItem item = EC_IvtrItem.CreateItem(itemId, 0, 1);
if (item != null)
{
// For NPC shop, we typically have static data only, so load from local DB
item.GetDetailDataFromLocal();
string description = item.GetDesc();
if (!string.IsNullOrEmpty(description))
{
// For NPC shop, we typically have static data only, so load from local DB
item.GetDetailDataFromLocal();
string description = item.GetDesc();
if (!string.IsNullOrEmpty(description))
{
return description.Replace("\\r", "\n");
}
return description.Replace("\\r", "\n");
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
}
}
catch (System.Exception ex)
{
Debug.LogWarning($"[NPCShopDetailPanel] Failed to get description for item {itemId}: {ex.Message}");
}
return "No description available.";
return "No description available.";
}
void OnCloseClicked()
@@ -197,5 +214,25 @@ public class NPCShopDetailPanel : MonoBehaviour
if (buyButton != null)
buyButton.onClick.RemoveListener(OnBuyButtonClicked);
}
// === Text Outlet Helper ===
[System.Serializable]
public class TextOutlet
{
[SerializeField] public Text legacy;
[SerializeField] public TMPro.TextMeshProUGUI tmp;
public void Set(string value)
{
if (legacy != null)
{
legacy.text = EC_Utility.FormatForLegacyText(value ?? string.Empty);
}
if (tmp != null)
{
tmp.text = EC_Utility.FormatForTextMeshPro(value ?? string.Empty);
}
}
}
}
@@ -1,8 +1,11 @@
using System;
using BrewMonster.Network;
using NUnit.Framework;
using System;
using System.Collections.Generic;
using TMPro;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class ShopDetailPanel : MonoBehaviour
{
@@ -75,8 +78,10 @@ public class ShopDetailPanel : MonoBehaviour
itemNameText.text = currentItem.name;
if (itemDescriptionText != null)
itemDescriptionText.text = currentItem.desc;
{
RemoveCaretColor(itemDescriptionText.text = ConvertColorCodeOfText(currentItem.desc));
}
if (itemQuantityText != null)
itemQuantityText.text = $"Quantity: {currentItem.num}";
@@ -84,6 +89,12 @@ public class ShopDetailPanel : MonoBehaviour
Debug.Log($"[ShopDetailPanel] About to load icon. itemIconImage is null: {itemIconImage == null}");
Debug.Log($"[ShopDetailPanel] Current item icon field: '{currentItem.icon}'");
if (itemPriceText != null)
{
itemPriceText.text = currentItem.buy[0].price.ToString();
LayoutRebuilder.ForceRebuildLayoutImmediate(itemPriceText.transform as RectTransform);
}
if (itemIconImage != null)
{
LoadItemIcon(itemIconImage, currentItem.icon);
@@ -99,7 +110,93 @@ public class ShopDetailPanel : MonoBehaviour
// Update gift info
UpdateGiftInfo();
}
struct ColorText
{
public string hex;
public string content;
}
private string ConvertColorCodeOfText(string input)
{
/*string returnValue = "";
List<ColorText> colorTexts = new List<ColorText>();
ColorText currentColorText = new ColorText();
currentColorText.hex = "ffffff";
currentColorText.content = "";
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '^' && i + 6 < input.Length)
{
// Store the current segment before changing color
colorTexts.Add(currentColorText);
// Start a new color segment
currentColorText = new ColorText();
currentColorText.hex = input.Substring(i + 1, 6);
currentColorText.content = "";
i += 6;
}
else
{
currentColorText.content += input[i];
}
}
foreach ( var ct in colorTexts)
{
if (ct.hex.ToLower() != "ffffff")
{
returnValue += $"<color=#{ct.hex}>{ct.content}</color>";
}
else
{
returnValue += ct.content;
}
Debug.Log($"[ShopDetailPanel] Processed ColorText: Hex='{ct.hex}', Content='{ct.content}'");
}
return returnValue;*/
if (string.IsNullOrEmpty(input))
return input;
input = input.Replace("\\r\\n", "\n");
input = input.Replace("\\r", "\n");
input = input.Replace("\\n", "\n");
input = input.Replace("\r\n", "\n");
input = input.Replace("\r", "\n");
input = input.Replace("\n", "\n");
System.Text.StringBuilder sb = new System.Text. StringBuilder();
for (int i = 0; i < input.Length; i++)
{
if (input[i] == '^' && i + 6 < input.Length)
{
string hex = input.Substring(i + 1, 6);
if (hex.ToLower() != "ffffff")
{
sb.Append($"<color=#{hex}>");
}
i += 6;
}
else
{
sb.Append(input[i]);
}
}
sb.Append("</color>");
return sb.ToString();
}
public static string RemoveCaretColor(string input)
{
return System.Text.RegularExpressions.Regex.Replace(input, @"\^[0-9a-fA-F]{6}", "");
}
void UpdateBuyOptions()
{
for (int i = 0; i < buyOptionButtons.Length && i < currentItem.buy.Length; i++)
@@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: aa133c544a1b54b10bf7c8ab30d9dd6f
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -0,0 +1,92 @@
using UnityEngine;
using UnityEngine.UI;
using TMPro;
using System;
public class SubTypeButton : MonoBehaviour
{
public TextMeshProUGUI subTypeText;
public Button subTypeButton;
public Action<SubTypeButton> onSetActive;
private ShopUIManager shopUIManager;
private string subTypeName;
[Header("Button Images")]
[SerializeField]
private Sprite activeSprite;
[SerializeField]
private Sprite inactiveSprite;
[SerializeField]
private Image subTypeImage;
public void OnClick()
{
if (shopUIManager != null && !string.IsNullOrEmpty(subTypeName))
{
shopUIManager.SetSubTypeFilterByName(subTypeName);
onSetActive?.Invoke(this);
}
}
public void OnClickAll()
{
if (shopUIManager != null)
{
shopUIManager.SetSubTypeFilter(-1);
onSetActive?.Invoke(this);
}
}
public void SetButton(string text, ShopUIManager manager)
{
Debug.Log("SetButton: " + text);
subTypeText.text = text;
subTypeName = text;
shopUIManager = manager;
name = text;
// Setup click listener
subTypeButton.onClick.RemoveAllListeners();
subTypeButton.onClick.AddListener(OnClick);
gameObject.SetActive(true);
}
public void SetAllButton(ShopUIManager manager)
{
Debug.Log("SetAllButton: Tat ca");
subTypeText.text = "Tat ca";
subTypeName = ""; // Empty for "All" button
shopUIManager = manager;
name = "Tat ca";
// Setup click listener to set filter to -1
subTypeButton.onClick.RemoveAllListeners();
subTypeButton.onClick.AddListener(OnClickAll);
gameObject.SetActive(true);
SetImageActive(this);
}
public void ResetButton()
{
Debug.Log("ResetButton: " + name);
subTypeButton.onClick.RemoveAllListeners();
subTypeText.text = "";
subTypeName = "";
shopUIManager = null;
name = "";
SetImageActive(null);
gameObject.SetActive(false);
}
public void SetImageActive(SubTypeButton button)
{
if (button == this)
{
subTypeImage.sprite = activeSprite;
}
else
{
subTypeImage.sprite = inactiveSprite;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 926fe0d5d0a764271a3b764afbaf9abc
@@ -0,0 +1,57 @@
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class SubTypeShop : MonoBehaviour
{
public SubTypeButton subTypePrefab;
public List<SubTypeButton> subTypeButtons;
public void GenerateSubTypeShop(List<string> subTypeNames, ShopUIManager shopUIManager)
{
ResetSubTypeShop();
// Always add "Tat ca" (All) button first
SubTypeButton allButton = subTypeButtons.Find(b => !b.gameObject.activeSelf);
if (allButton == null)
{
allButton = Instantiate(subTypePrefab, transform);
subTypeButtons.Add(allButton);
}
allButton.SetAllButton(shopUIManager);
allButton.onSetActive += SetImageActive;
// Add other subType buttons
foreach (string subType in subTypeNames)
{
//get unactive from list subTypeButtons first
SubTypeButton button = subTypeButtons.Find(b => !b.gameObject.activeSelf);
if (button == null)
{
button = Instantiate(subTypePrefab, transform);
subTypeButtons.Add(button);
}
button.SetButton(subType, shopUIManager);
button.onSetActive += SetImageActive;
}
}
public void ResetSubTypeShop()
{
foreach (SubTypeButton button in subTypeButtons)
{
button.ResetButton();
button.onSetActive -= SetImageActive;
}
}
public void SetImageActive(SubTypeButton activeButton)
{
foreach (SubTypeButton button in subTypeButtons)
{
button.SetImageActive(activeButton);
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 80f96f49ed74549a6adce89d44aebb4e
+178 -8
View File
@@ -11,7 +11,7 @@ public class ShopUIManager : MonoBehaviour
[Header("UI Panels")]
public GameObject shopMainPanel;
public GameObject shopDetailPanel;
public GameObject subTypeShopPanel;
[Header("Item Display")]
public Transform itemContainer; // Parent for item panels
public GameObject itemPanelPrefab; // Prefab for individual item panels
@@ -31,14 +31,18 @@ public class ShopUIManager : MonoBehaviour
public TextMeshProUGUI detailItemQuantity;
public Button buyButton;
public Button closeDetailButton;
public ScrollRect detailScrollRect;
[Header("Main Panel Components")]
public Button closeShopButton;
private List<GameObject> currentItemPanels = new List<GameObject>();
private GShopItem selectedItem;
private int currentCategory = 0; // 0-5 for the 6 merged categories
[Header("Sub Type Shop")]
//-1 means all sub types
public SubTypeShop subTypeShop;
private int currentSubType = -1;
void Start()
{
InitializeUI();
@@ -65,6 +69,8 @@ public class ShopUIManager : MonoBehaviour
shopMainPanel.SetActive(false);
if (shopDetailPanel != null)
shopDetailPanel.SetActive(false);
if (subTypeShop != null)
subTypeShopPanel.SetActive(false);
}
void SetupEventListeners()
@@ -148,7 +154,6 @@ public class ShopUIManager : MonoBehaviour
// Get items for current category
List<GShopItem> categoryItems = GetItemsForCategory(currentCategory);
// Create item panels using pooling
int siblingIndexCounter = 0;
foreach (GShopItem item in categoryItems)
@@ -156,9 +161,52 @@ public class ShopUIManager : MonoBehaviour
CreateItemPanelFromPool(item, siblingIndexCounter);
siblingIndexCounter++;
}
List<string> subTypeNames = GetSubTypeNamesForCategory(currentCategory);
currentSubType = -1;
subTypeShopPanel.SetActive(subTypeNames.Count > 0);
if (subTypeShop != null)
subTypeShop.GenerateSubTypeShop(subTypeNames, this);
}
public void SetSubTypeFilter(int subTypeIndex)
{
currentSubType = subTypeIndex;
ApplySubTypeFilter();
}
List<GShopItem> GetItemsForCategory(int categoryIndex)
public void SetSubTypeFilterByName(string subTypeName)
{
int subTypeIndex = GetSubTypeIndexFromName(subTypeName);
SetSubTypeFilter(subTypeIndex);
}
public void ApplySubTypeFilter()
{
ReturnAllPanelsToPool();
if (shopLoader == null || shopLoader.primaryShop == null)
{
Debug.LogWarning("ShopLoader or primary shop data not available");
return;
}
if(currentSubType == -1)
{
RefreshShopDisplay();
return;
}
List<GShopItem> categoryItems = GetItemsForCategory(currentCategory, currentSubType);
int siblingIndexCounter = 0;
foreach (GShopItem item in categoryItems)
{
CreateItemPanelFromPool(item, siblingIndexCounter);
siblingIndexCounter++;
}
}
List<GShopItem> GetItemsForCategory(int categoryIndex, int subTypeIndex=-1)
{
List<GShopItem> items = new List<GShopItem>();
@@ -169,13 +217,51 @@ public class ShopUIManager : MonoBehaviour
{
if (IsItemInCategory(item, categoryIndex))
{
items.Add(item);
if(IsItemInSubType(item, subTypeIndex))
{
items.Add(item);
}
}
}
return items;
}
List<string> GetSubTypeNamesForCategory(int categoryIndex)
{
List<string> subTypeNames = new List<string>();
if (shopLoader?.primaryShop?.mainTypes == null)
return subTypeNames;
// Use the same mapping as IsItemInCategory: 0=0, 1=2, 2=5, 3=1+3+4, 4=6, 5=7
List<int> mainTypeIndices = new List<int>();
switch (categoryIndex)
{
case 0: mainTypeIndices.Add(0); break;
case 1: mainTypeIndices.Add(2); break;
case 2: mainTypeIndices.Add(5); break;
case 3: mainTypeIndices.Add(1); mainTypeIndices.Add(3); mainTypeIndices.Add(4); break;
case 4: mainTypeIndices.Add(6); break;
case 5: mainTypeIndices.Add(7); break;
default: break;
}
foreach (int mainTypeIndex in mainTypeIndices)
{
if (mainTypeIndex >= 0 && mainTypeIndex < shopLoader.primaryShop.mainTypes.Count)
{
foreach (string subType in shopLoader.primaryShop.mainTypes[mainTypeIndex].subTypes)
{
if (!subTypeNames.Contains(subType))
{
subTypeNames.Add(subType);
}
}
}
}
return subTypeNames;
}
bool IsItemInCategory(GShopItem item, int categoryIndex)
{
// Category mapping: 0=1, 1=2, 2=1+3+4, 3=6, 4=7, 5=8
@@ -190,7 +276,89 @@ public class ShopUIManager : MonoBehaviour
default: return false;
}
}
bool IsItemInSubType(GShopItem item, int combinedSubTypeIndex)
{
if(combinedSubTypeIndex == -1)
return true;
// Get the subType name for this item's mainType and subType index
if (shopLoader?.primaryShop?.mainTypes == null)
return false;
if (item.mainType < 0 || item.mainType >= shopLoader.primaryShop.mainTypes.Count)
return false;
var mainType = shopLoader.primaryShop.mainTypes[item.mainType];
if (item.subType < 0 || item.subType >= mainType.subTypes.Count)
return false;
string itemSubTypeName = mainType.subTypes[item.subType];
// Get the subType name for the filter index
string filterSubTypeName = GetSubTypeNameFromIndex(combinedSubTypeIndex);
return itemSubTypeName == filterSubTypeName;
}
int GetSubTypeIndexFromName(string subTypeName)
{
if (shopLoader?.primaryShop?.mainTypes == null || string.IsNullOrEmpty(subTypeName))
return -1;
// Find the subType name in the current category's mainTypes
List<int> mainTypeIndices = new List<int>();
switch (currentCategory)
{
case 0: mainTypeIndices.Add(0); break;
case 1: mainTypeIndices.Add(2); break;
case 2: mainTypeIndices.Add(5); break;
case 3: mainTypeIndices.Add(1); mainTypeIndices.Add(3); mainTypeIndices.Add(4); break;
case 4: mainTypeIndices.Add(6); break;
case 5: mainTypeIndices.Add(7); break;
default: break;
}
// Find the first matching subType across all relevant mainTypes
// We use a combined index: mainTypeIndex * 1000 + subTypeIndex
// This allows us to uniquely identify subTypes across different mainTypes
foreach (int mainTypeIndex in mainTypeIndices)
{
if (mainTypeIndex >= 0 && mainTypeIndex < shopLoader.primaryShop.mainTypes.Count)
{
var mainType = shopLoader.primaryShop.mainTypes[mainTypeIndex];
for (int i = 0; i < mainType.subTypes.Count; i++)
{
if (mainType.subTypes[i] == subTypeName)
{
// Return a combined index: mainTypeIndex * 1000 + subTypeIndex
return mainTypeIndex * 1000 + i;
}
}
}
}
return -1;
}
string GetSubTypeNameFromIndex(int combinedIndex)
{
if (combinedIndex < 0 || shopLoader?.primaryShop?.mainTypes == null)
return null;
int mainTypeIndex = combinedIndex / 1000;
int subTypeIndex = combinedIndex % 1000;
if (mainTypeIndex >= 0 && mainTypeIndex < shopLoader.primaryShop.mainTypes.Count)
{
var mainType = shopLoader.primaryShop.mainTypes[mainTypeIndex];
if (subTypeIndex >= 0 && subTypeIndex < mainType.subTypes.Count)
{
return mainType.subTypes[subTypeIndex];
}
}
return null;
}
void CreateItemPanelFromPool(GShopItem item, int targetSiblingIndex)
{
GameObject itemPanel = null;
@@ -273,6 +441,8 @@ public class ShopUIManager : MonoBehaviour
Debug.LogError("ShopDetailPanel script not found on shopDetailPanel GameObject!");
// Fallback to the old method
UpdateDetailPanel(item);
detailScrollRect.verticalNormalizedPosition = 1f;
LayoutRebuilder.ForceRebuildLayoutImmediate(detailScrollRect.content);
}
}
}
@@ -301,7 +471,7 @@ public class ShopUIManager : MonoBehaviour
if (detailItemPrice != null)
detailItemPrice.text = $"Price: {bestPrice}";
Debug.Log($"Detail item price: {detailItemPrice.text}, detail item price != null: {detailItemPrice != null}");
// Load icon based on item name (you'll need to implement icon loading)
if (detailItemIcon != null)
{
@@ -11,6 +11,7 @@ namespace BrewMonster.Scripts.World
protected A3DTerrain2 m_pA3DTerrain;
CECOrnamentMan m_pOnmtMan;
uint m_dwBornStamp = 0;
int m_idInst = 161; // id of instance
public uint GetBornStamp() { return m_dwBornStamp++; }
@@ -26,5 +27,8 @@ namespace BrewMonster.Scripts.World
{
return m_pOnmtMan;
}
}
// Get id of instance
public int GetInstanceID(){ return m_idInst; }
}
}
@@ -0,0 +1,48 @@
using BrewMonster.Common;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace BrewMonster.Scripts
{
public class CECInstance
{
int m_id = 161; // Instance ID
ushort[] m_strName; // Instance name
string m_strPath; // Path
int m_iRowNum = 3; // Number of map row
int m_iColNum = 4; // Number of map column
bool m_bLimitJump = false; // ÊÇ·ñÏÞÌø
List<string> m_routeFiles;
public CECInstance()
{
m_id = 161;
m_iRowNum = 3;
m_iColNum = 4;
m_bLimitJump = false;
}
// Get instance ID
public int GetID() { return m_id; }
// Get instance name
public ushort[] GetName() { return m_strName; }
// Get instance data path
public string GetPath() { return m_strPath; }
// Get row and column number of map
public int GetRowNum(){ return m_iRowNum; }
public int GetColNum(){ return m_iColNum; }
public bool GetLimitJump(){ return m_bLimitJump; }
public List<string> GetRouteFiles(){ return m_routeFiles; }
public bool GetPositionRelatedTexture(float x, float z, string filePath)
{
return true;
}
// Load instance information from file
public bool Load(AWScriptFile psf)
{
return true;
}
}
}
@@ -0,0 +1,2 @@
fileFormatVersion: 2
guid: 3d943e5add12fde4dbb6e3bea3dc4d65
+3 -1
View File
@@ -896,7 +896,9 @@ MonoBehaviour:
m_Name:
m_EditorClassIdentifier:
itemNameText: {fileID: 2676848775259769377}
itemDescriptionText: {fileID: 1509498505529622136}
itemDescriptionText:
legacy: {fileID: 0}
tmp: {fileID: 1509498505529622136}
itemIconImage: {fileID: 5729739685989188916}
itemPriceText: {fileID: 3533247489785110134}
closeButton: {fileID: 0}
+157 -5
View File
@@ -1899,7 +1899,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -22.97}
m_AnchoredPosition: {x: 21, y: -0}
m_SizeDelta: {x: 472.5032, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &2043307214318211948
@@ -1930,7 +1930,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: aaa
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
@@ -4442,6 +4442,7 @@ RectTransform:
m_Children:
- {fileID: 9106031791145292554}
- {fileID: 1001152567372181051}
- {fileID: 1333165094145940333}
m_Father: {fileID: 5834405183358786743}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
@@ -7163,6 +7164,157 @@ MonoBehaviour:
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!1 &5371522206622176611
GameObject:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
serializedVersion: 6
m_Component:
- component: {fileID: 1333165094145940333}
- component: {fileID: 7370426642012978972}
- component: {fileID: 37862130938576806}
- component: {fileID: 9183113038478938762}
m_Layer: 5
m_Name: Text (TMP) (2)
m_TagString: Untagged
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
--- !u!224 &1333165094145940333
RectTransform:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
m_Children: []
m_Father: {fileID: 7205431771786927886}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -57.98}
m_SizeDelta: {x: 465.7476, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &7370426642012978972
CanvasRenderer:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_CullTransparentMesh: 1
--- !u!114 &37862130938576806
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: f4688fdb7df04437aeb418b961361dc5, type: 3}
m_Name:
m_EditorClassIdentifier:
m_Material: {fileID: 0}
m_Color: {r: 1, g: 1, b: 1, a: 1}
m_RaycastTarget: 1
m_RaycastPadding: {x: 0, y: 0, z: 0, w: 0}
m_Maskable: 1
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text:
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_fontSharedMaterials: []
m_fontMaterial: {fileID: 0}
m_fontMaterials: []
m_fontColor32:
serializedVersion: 2
rgba: 4294967295
m_fontColor: {r: 1, g: 1, b: 1, a: 1}
m_enableVertexGradient: 0
m_colorMode: 3
m_fontColorGradient:
topLeft: {r: 1, g: 1, b: 1, a: 1}
topRight: {r: 1, g: 1, b: 1, a: 1}
bottomLeft: {r: 1, g: 1, b: 1, a: 1}
bottomRight: {r: 1, g: 1, b: 1, a: 1}
m_fontColorGradientPreset: {fileID: 0}
m_spriteAsset: {fileID: 0}
m_tintAllSprites: 0
m_StyleSheet: {fileID: 0}
m_TextStyleHashCode: -1183493901
m_overrideHtmlColors: 0
m_faceColor:
serializedVersion: 2
rgba: 4294967295
m_fontSize: 36
m_fontSizeBase: 36
m_fontWeight: 400
m_enableAutoSizing: 0
m_fontSizeMin: 18
m_fontSizeMax: 72
m_fontStyle: 0
m_HorizontalAlignment: 1
m_VerticalAlignment: 512
m_textAlignment: 65535
m_characterSpacing: 0
m_wordSpacing: 0
m_lineSpacing: 0
m_lineSpacingMax: 0
m_paragraphSpacing: 0
m_charWidthMaxAdj: 0
m_TextWrappingMode: 1
m_wordWrappingRatios: 0.4
m_overflowMode: 0
m_linkedTextComponent: {fileID: 0}
parentLinkedComponent: {fileID: 0}
m_enableKerning: 0
m_ActiveFontFeatures: 6e72656b
m_enableExtraPadding: 0
checkPaddingRequired: 0
m_isRichText: 1
m_EmojiFallbackSupport: 1
m_parseCtrlCharacters: 1
m_isOrthographic: 1
m_isCullingEnabled: 0
m_horizontalMapping: 0
m_verticalMapping: 0
m_uvLineOffset: 0
m_geometrySortingOrder: 0
m_IsTextObjectScaleStatic: 0
m_VertexBufferAutoSizeReduction: 0
m_useMaxVisibleDescender: 1
m_pageToDisplay: 1
m_margin: {x: 0, y: 0, z: 0, w: 0}
m_isUsingLegacyAnimationComponent: 0
m_isVolumetricText: 0
m_hasFontAssetChanged: 0
m_baseMaterial: {fileID: 0}
m_maskOffset: {x: 0, y: 0, z: 0, w: 0}
--- !u!114 &9183113038478938762
MonoBehaviour:
m_ObjectHideFlags: 0
m_CorrespondingSourceObject: {fileID: 0}
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5371522206622176611}
m_Enabled: 1
m_EditorHideFlags: 0
m_Script: {fileID: 11500000, guid: 3245ec927659c4140ac4f8d17403cc18, type: 3}
m_Name:
m_EditorClassIdentifier:
m_HorizontalFit: 0
m_VerticalFit: 2
--- !u!1 &5460797107099608431
GameObject:
m_ObjectHideFlags: 0
@@ -8046,13 +8198,13 @@ MonoBehaviour:
hideDetailOnStart: 1
nameText:
legacy: {fileID: 0}
tmp: {fileID: 7082730707602873357}
tmp: {fileID: 0}
descriptionText:
legacy: {fileID: 0}
tmp: {fileID: 6020258894941961325}
extendedDescText:
legacy: {fileID: 0}
tmp: {fileID: 0}
tmp: {fileID: 37862130938576806}
equipButton: {fileID: 472698755110594484}
dropButton: {fileID: 540159372834342487}
autoRefresh: 1
@@ -11506,7 +11658,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 21, y: -68.909996}
m_AnchoredPosition: {x: 21, y: -28.989998}
m_SizeDelta: {x: 465.7476, y: 0}
m_Pivot: {x: 0, y: 0.5}
--- !u!222 &5347950336050242333
File diff suppressed because it is too large Load Diff
+9 -9
View File
@@ -1943,9 +1943,9 @@ RectTransform:
- {fileID: 4504331075840543341}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 108.9562, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8804506040386004496
@@ -5768,9 +5768,9 @@ RectTransform:
- {fileID: 2027606699309904338}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 468.781, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6741821173640675138
@@ -18122,9 +18122,9 @@ RectTransform:
- {fileID: 911293677621153352}
m_Father: {fileID: 1361524257611413148}
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 0, y: 0}
m_AnchoredPosition: {x: 0, y: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 288.8686, y: -31.05}
m_SizeDelta: {x: 179.9124, y: 68.0217}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &5623009994815814977
+13 -13
View File
@@ -597,7 +597,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: -25.6, y: -19.5}
m_AnchoredPosition: {x: -25, y: -20}
m_SizeDelta: {x: 36, y: 24}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &6137065207749229268
@@ -676,7 +676,7 @@ RectTransform:
m_AnchorMin: {x: 0, y: 0}
m_AnchorMax: {x: 1, y: 1}
m_AnchoredPosition: {x: 38.34253, y: -20.45288}
m_SizeDelta: {x: -87.68497, y: -48.1007}
m_SizeDelta: {x: -87.68497, y: -48.1009}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &3282283742538992330
CanvasRenderer:
@@ -760,7 +760,7 @@ GameObject:
m_Icon: {fileID: 0}
m_NavMeshLayer: 0
m_StaticEditorFlags: 0
m_IsActive: 1
m_IsActive: 0
--- !u!224 &6862529442018877370
RectTransform:
m_ObjectHideFlags: 0
@@ -768,7 +768,7 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 5207380909195611422}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
@@ -778,8 +778,8 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 256.80002, y: -17.547}
m_SizeDelta: {x: 47.24, y: 35.094}
m_AnchoredPosition: {x: 267.715, y: -17.547}
m_SizeDelta: {x: 25.41, y: 35.094}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &9094358577136577940
CanvasRenderer:
@@ -809,7 +809,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 1000
m_text: 24
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
@@ -928,7 +928,7 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: -24.4, y: -19.5}
m_AnchoredPosition: {x: -25, y: -19.5}
m_SizeDelta: {x: 36, y: 24}
m_Pivot: {x: 0.5, y: 0.5}
--- !u!222 &8769089508493293562
@@ -1073,7 +1073,7 @@ RectTransform:
m_PrefabInstance: {fileID: 0}
m_PrefabAsset: {fileID: 0}
m_GameObject: {fileID: 8378347976037059457}
m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
m_LocalPosition: {x: 0, y: 0, z: 0}
m_LocalScale: {x: 1, y: 1, z: 1}
m_ConstrainProportionsScale: 0
@@ -1083,9 +1083,9 @@ RectTransform:
m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
m_AnchorMin: {x: 0, y: 1}
m_AnchorMax: {x: 0, y: 1}
m_AnchoredPosition: {x: 169.03502, y: -17.547}
m_SizeDelta: {x: 8.29, y: 35.094}
m_Pivot: {x: 0.5, y: 0.5}
m_AnchoredPosition: {x: 280.42, y: -17.547}
m_SizeDelta: {x: 59.66, y: 35.094}
m_Pivot: {x: 1, y: 0.5}
--- !u!222 &1217550950535449853
CanvasRenderer:
m_ObjectHideFlags: 0
@@ -1114,7 +1114,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 1
m_text: 10204
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
m_sharedMaterial: {fileID: 9092487103257209053, guid: 369c2e14814cc9a4b8e3ad4e37769134, type: 2}
+2 -2
View File
@@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1
oid sha256:94045fc7eb49643c229531f88aa7ac1d8a6f59d3e0d2bfb5a4939f9b3fb0ed04
size 200173095
oid sha256:8003b60a4af29a6f7767b8b3f782a1f7fd576ce667bdbcfd0124f3071e3e5c0d
size 200174193
+20
View File
@@ -5,6 +5,7 @@ using CSNetwork;
using CSNetwork.GPDataType;
using CSNetwork.GPDataType;
using CSNetwork.Protocols.RPCData;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Unity.Cinemachine;
@@ -27,6 +28,7 @@ public partial class CECGameRun
public CECWorld GetWorld() { return m_pWorld; }
private static Dictionary<int, CECInstance> m_InstTab = new Dictionary<int, CECInstance>();
public void Init()
{
Application.targetFrameRate = 60;
@@ -40,6 +42,13 @@ public partial class CECGameRun
// LoadPrefabs();
EC_ManMessage.RegisterHandler(this);
// Load instance information
//if (!LoadInstanceInfo("Configs\\instance.txt"))
//{
// glb_ErrorOutput(ECERR_FAILEDTOCALL, "CECGameRun::Init", __LINE__);
// return false;
//}
m_InstTab.Add(161, new CECInstance());
AddressableManager.Instance.OnDispose += Dispose;
}
@@ -317,4 +326,15 @@ public partial class CECGameRun
}
return m_pUIManager;
}
// Get instance by ID
public CECInstance GetInstance(int id)
{
if (m_InstTab.TryGetValue(id, out CECInstance value))
{
return value;
}
return null;
}
}
+70 -19
View File
@@ -54,35 +54,74 @@ namespace BrewMonster
protected bool LoadANSIStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
try
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
// If a real file path is provided (e.g. StreamingAssets), read directly from disk.
// 如果提供的是实际文件路径(例如 StreamingAssets),则直接从磁盘读取。
if (File.Exists(resourceName))
{
// ANSI tables are in CP936 in original PW; keep using CP936 decoder.
// 原版完美世界的ANSI表是CP936编码,这里保持一致。
byte[] bytes = File.ReadAllBytes(resourceName);
string content = ByteToStringUtils.ByteArrayToCP936String(bytes);
using var srFile = new StringReader(content);
return ParseIntoDict(srFile, isWide: false);
}
// Fallback to Resources (old behaviour).
// 回退到 Resources 加载(旧行为)。
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
string resContent = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var srRes = new StringReader(resContent);
return ParseIntoDict(srRes, isWide: false);
}
catch (Exception e)
{
Debug.LogError($"[CECStringTab] LoadANSIStrings failed for '{resourceName}': {e}");
return false;
}
// Giải mã bytes -> string (ANSI: dùng Encoding.Default)
string content = ByteToStringUtils.ByteArrayToCP936String(textAsset.bytes);
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: false);
}
protected bool LoadWideStrings(string resourceName)
{
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
try
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
// Support absolute / relative filesystem paths (e.g. StreamingAssets/configs/*.txt)
// 支持文件系统路径(例如 StreamingAssets/configs/*.txt
if (File.Exists(resourceName))
{
// String tables we ship in StreamingAssets are saved as UTF-8.
// 我们放在 StreamingAssets 里的字符串表保存为 UTF-8。
string content = File.ReadAllText(resourceName, Encoding.UTF8);
using var srFile = new StringReader(content);
return ParseIntoDict(srFile, isWide: true);
}
// Fallback to Resources-based loading (old behaviour)
// 回退到基于 Resources 的加载(旧行为)
TextAsset textAsset = Resources.Load<TextAsset>(resourceName);
if (textAsset == null)
{
Debug.LogError($"[CECStringTab] Resource not found: {resourceName}");
return false;
}
// Unity TextAsset.text is already UTF-8 decoded.
string resContent = textAsset.text;
using var srRes = new StringReader(resContent);
return ParseIntoDict(srRes, isWide: true);
}
catch (Exception e)
{
Debug.LogError($"[CECStringTab] LoadWideStrings failed for '{resourceName}': {e}");
return false;
}
// Unity TextAsset mặc định đã decode text UTF8 -> textAsset.text
// nhưng để chắc chắn BOM/Unicode thì đọc từ bytes
string content;
content = textAsset.text;
using var sr = new StringReader(content);
return ParseIntoDict(sr, isWide: true);
}
private static Encoding DetectEncoding(byte[] bom)
@@ -183,6 +222,18 @@ namespace BrewMonster
private void PutString(int id, string value, bool isWide)
{
if (string.IsNullOrEmpty(value))
return;
// Many PW string tables wrap the payload in double quotes, e.g.:
// 12345 "^ffcb4aSome text\rMore text"
// Strip a single leading/trailing quote pair to avoid showing raw quotes in UI.
// 许多字符串表会用双引号包裹内容,这里去掉首尾各一个引号以避免在UI中显示多余的引号。
if (value.Length >= 2 && value[0] == '"' && value[value.Length - 1] == '"')
{
value = value.Substring(1, value.Length - 2);
}
if (isWide) m_WStrTab[id] = value;
else m_AStrTab[id] = value;
}