using BrewMonster.Network;
using BrewMonster.Scripts;
using BrewMonster.Scripts.World;
using CSNetwork.GPDataType;
using System;
using UnityEngine;
using WORD = System.UInt16;
namespace BrewMonster
{
public static class EC_CDR
{
// 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;
public static RaycastHit[] hits = new RaycastHit[5];
public static RaycastHit[] fHitsTerrain = new RaycastHit[5];
public static RaycastHit[] fHitsWater = new RaycastHit[5];
const float LOCAL_EPSILON = 1e-5f;
const float FLY_MAX_HEIGHT = 800.0f; // ·ÉÐеÄ×î´ó¸ß¶È£¡
const float VEL_EPSILON = 1e-4f;
const float DIST_EPSILON = 1e-4f;
const float NORMAL_EPSILON = 1e-2f;
const float VEL_MAX_SPEED = 200f;
const float VEL_REFLECT = 0.3f;
// 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 static class CDR_EVN
{
public const int CDR_BRUSH = 0x1,
CDR_TERRAIN = 0x2,
CDR_WATER = 0x4;
}
static LayerMask UsedMask_Ground() => TerrainMask;
static RaycastHitDistanceComparer raycastHitDistanceComparer = new RaycastHitDistanceComparer();
public static bool CollideWithEnv(ref env_trace_t pEnvTrc, bool isNeedCheckGr = true)
{
pEnvTrc.fFraction = 100.0f;
pEnvTrc.bStartSolid = false;
pEnvTrc.dwClsFlag = 0;
Vector3 vStart = EC_Utility.ToVector3(pEnvTrc.vStart);
Vector3 vExt = EC_Utility.ToVector3(pEnvTrc.vExt);
Vector3 vDelta = EC_Utility.ToVector3(pEnvTrc.vDelta);
Vector3 vTerStart = EC_Utility.ToVector3(pEnvTrc.vTerStart);
Vector3 vWatStart = EC_Utility.ToVector3(pEnvTrc.vWatStart);
Vector3 dir = Vector3.zero;
int countHits = 0;
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_BRUSH) == CDR_EVN.CDR_BRUSH)
{
float fFractionBrush = 100f;
Vector3 v3Normal = Vector3.zero;
dir = vDelta;
countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask);
for(int i = 0; i < countHits; i++)
{
if (hits[i].distance > 0f)
{
float fFraction = (hits[i].distance) / vDelta.magnitude;
if(fFraction < fFractionBrush)
{
fFractionBrush = fFraction;
v3Normal = hits[i].normal;
}
}
}
pEnvTrc.fFraction = fFractionBrush;
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(v3Normal);
pEnvTrc.dwClsFlag = CDR_EVN.CDR_BRUSH;
}
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_TERRAIN) == CDR_EVN.CDR_TERRAIN)
{
float fFractionTerrain = 100f;
dir = vTerStart + vDelta;
countHits = 0;
if (isNeedCheckGr)
{
countHits = Physics.RaycastNonAlloc(vStart, Vector3.down, fHitsTerrain, vExt.y, TerrainMask);
if(countHits > 0 && fHitsTerrain[0].point.y >= vTerStart.y)
{
vTerStart.y = fHitsTerrain[0].point.y;
}
}
countHits = Physics.RaycastNonAlloc(vTerStart, dir.normalized, fHitsTerrain, vDelta.magnitude, TerrainMask);
if ((countHits > 0 && Vector3.Distance(vTerStart, fHitsTerrain[0].point) > 0.0009f))
{
fFractionTerrain = (fHitsTerrain[0].distance) / vDelta.magnitude;
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(fHitsTerrain[0].normal);
pEnvTrc.dwClsFlag = CDR_EVN.CDR_TERRAIN;
}
if (fFractionTerrain >= (1.0f + 1E-4f) && fFractionTerrain < pEnvTrc.fFraction)
{
hits = fHitsTerrain;
pEnvTrc.fFraction = fFractionTerrain;
}
}
if ((pEnvTrc.dwCheckFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER)
{
float fFraction = 100f;
Vector3 vWatNormal = Vector3.zero;
bool bStart = false;
if (pEnvTrc.bWaterSolid && (vDelta.y > LOCAL_EPSILON))
{
fFraction = 0;
}
else if (!pEnvTrc.bWaterSolid && (vDelta.y < -LOCAL_EPSILON))
{
fFraction = 0;
}
else if (vDelta.y < LOCAL_EPSILON && vDelta.y > -LOCAL_EPSILON)
{ //parallel the water plane
fFraction = 0;
}
else
{
float h0 = 0f;
int countHits0 = Physics.RaycastNonAlloc(vWatStart + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask);
if (countHits0 > 0)
{
h0 = fHitsWater[0].point.y;
}
float h1 = 0f;
countHits0 = Physics.RaycastNonAlloc((vWatStart + vDelta) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask);
if(countHits0 > 0)
{
h1 = fHitsWater[0].point.y;
}
float hWater = Mathf.Max(h0, h1);
vWatNormal = Vector3.up;
float t = (hWater - vWatStart.y) / pEnvTrc.vDelta.y;
if (t >= 0.0f && t <= 1.0f)
{
fFraction = Mathf.Max(0.0f, t - 1E-2f);
if (pEnvTrc.bWaterSolid && h0 > vStart.y)
{
fFraction = 0.0f;
bStart = true;
}
if (!pEnvTrc.bWaterSolid && h0 < vStart.y)
{
fFraction = 0.0f;
bStart = true;
}
if(fFraction < pEnvTrc.fFraction)
{
pEnvTrc.fFraction = fFraction;
pEnvTrc.vHitNormal = EC_Utility.ToA3DVECTOR3(vWatNormal);
pEnvTrc.bStartSolid = bStart;
pEnvTrc.dwClsFlag = CDR_EVN.CDR_WATER;
}
}
}
}
return (pEnvTrc.fFraction < 1.0f + 1E-4f);
}
// == Thay RetrieveSupportPlane (C++) bằng Raycast xuống ==
static bool DoGroundProbe(Vector3 vStart, Vector3 vExt, float fDeltaY, LayerMask mask,
out Vector3 vEnd, out Vector3 vHitNormal, out bool bSupport,
float skin = 0.01f)
{
vEnd = vStart;
vHitNormal = Vector3.zero;
bSupport = false;
float dist = fDeltaY + vExt.y;//Mathf.Max(fDeltaY, 0f) + vExt.y;
Vector3 origin = vStart /*+ Vector3.down * vExt.y*/;
int countHits = Physics.RaycastNonAlloc(origin, Vector3.down, hits, dist, mask);
if (countHits > 0)
{
float minDistance = 100f;
int idx = 0;
for (int i = 0; i < countHits; i++)
{
if (hits[i].collider != null && hits[i].distance > 0f)
{
if(hits[i].distance < minDistance)
{
minDistance = hits[i].distance;
idx = i;
}
}
}
vHitNormal = hits[idx].normal;
vEnd = new Vector3(vStart.x, hits[idx].point.y + vExt.y, vStart.z);
bSupport = (vHitNormal.y >= 0f);
return true;
}
return true;
}
// ======= STATIC OnGroundMove – GIỮ NGUYÊN VAI TRÒ TOÀN CỤC (C API) =======
//public static void OnGroundMove(ref CDR_INFO CDRInfo)
//{
// CDRInfo.fMoveDist = 0.0f;
// bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh);
// if (CDRInfo.fYVel < VEL_EPSILON && CDRInfo.fYVel > -VEL_EPSILON && CDRInfo.fSpeed < VEL_EPSILON && CDRInfo.fSpeed > -VEL_EPSILON && !bFreeFall)
// return;
// float fYVel = CDRInfo.fYVel;
// bool bJump = (fYVel > 0.5f);
// Vector3 vVelocity = CDRInfo.fSpeed * EC_Utility.ToVector3(CDRInfo.vXOZVelDir) + fYVel * Vector3.up;
// if (bFreeFall)
// {
// vVelocity += -CDRInfo.fGravityAccel * CDRInfo.t * Vector3.up;
// fYVel += -CDRInfo.fGravityAccel * CDRInfo.t;
// }
// A3DVECTOR3 vVelDir = EC_Utility.ToA3DVECTOR3(vVelocity);
// float fVelSpeed = vVelDir.Normalize();
// if (!bFreeFall)
// {
// if (fVelSpeed > VEL_MAX_SPEED)
// {
// fVelSpeed = VEL_MAX_SPEED;
// }
// }
// vVelocity = EC_Utility.ToVector3(vVelDir) * fVelSpeed;
// float dtp = DotProduct(vVelDir, CDRInfo.vTPNormal);
// if (dtp < 0f || !bJump)
// {
// vVelocity = EC_Utility.ToVector3((vVelDir -(CDRInfo.vTPNormal) * dtp - (CDRInfo.vTPNormal) * dtp * 0.01f) * fVelSpeed);
// }
// CDRInfo.vAbsVelocity = EC_Utility.ToA3DVECTOR3(vVelocity);
// Vector3 vStart = EC_Utility.ToVector3(CDRInfo.vCenter);
// Vector3 vExt = EC_Utility.ToVector3(CDRInfo.vExtent);
// float fTime = CDRInfo.t;
// Vector3 vDelta, vNormal = Vector3.zero, vFinalPos = vStart;
// bool bPull = false;
// bool bTryPull = false;
// int nTry = 0;
// LayerMask mask = UsedMask_Ground() | BrushMask;
// env_trace_t trcInfo = new env_trace_t();
// trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH;
// trcInfo.vExt = CDRInfo.vExtent;
// int countHits = 0;
// while (nTry < 1)
// {
// vDelta = vVelocity * (fTime);
// float fDeltaDist = vDelta.magnitude;
// Vector3 posFoot = vStart - Vector3.up * vExt.y;
// vFinalPos = vStart;
// if (fDeltaDist < DIST_EPSILON) break;
// countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down * vExt.y).normalized, hits, vExt.y, mask);
// if (countHits > 0)
// {
// if (hits[0].point.y >= posFoot.y)
// {
// posFoot.y = hits[0].point.y;
// }
// }
// //Debug.LogError("fDeltaDist = " + fDeltaDist + " vVelocity = " + vVelocity + " fTime = " + fTime + " speed = " + (fDeltaDist / fTime) + " posFoot + vDelta = " + (posFoot + vDelta) + " posFoot = " + posFoot);
// countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vDelta).normalized, hits, fDeltaDist, mask);
// bool bClear = !(countHits > 0);
// nTry++;
// if (bClear || (countHits > 0 && Vector3.Distance(hits[0].point, posFoot) < 0.0009f)) // Is 0.0009f the tolerance used to check if two points are the same?
// {
// vFinalPos = vStart + vDelta;
// CDRInfo.fMoveDist += fDeltaDist;
// break;
// }
// vStart = hits[0].point + Vector3.up * vExt.y;
// vFinalPos = vStart;
// countHits = Physics.RaycastNonAlloc(vStart, (Vector3.down).normalized, hits, vExt.y, mask);
// if (countHits > 0)
// {
// vNormal = hits[0].normal;
// }
// else
// {
// vNormal = Vector3.zero;
// }
// // Step-up (giữ tinh thần bản gốc)
// if (!bFreeFall && !bTryPull && !bJump)
// {
// posFoot = vStart - Vector3.up * vExt.y;
// countHits = Physics.RaycastNonAlloc(vStart, (vStart + Vector3.down).normalized, hits, vExt.y, mask);
// if (countHits > 0)
// {
// if (hits[0].point.y > posFoot.y)
// {
// posFoot.y = hits[0].point.y;
// }
// }
// countHits = Physics.RaycastNonAlloc(posFoot, (Vector3.up).normalized, hits, CDRInfo.fStepHeight, mask);
// bPull = !(countHits > 0);
// if (bPull)
// {
// vStart += Vector3.up * CDRInfo.fStepHeight;
// posFoot = vStart - Vector3.up * vExt.y;
// fDeltaDist = (vVelocity.normalized).magnitude;
// countHits = Physics.RaycastNonAlloc(posFoot, (posFoot + vVelocity).normalized, hits, fDeltaDist, mask);
// bool bMove = !(countHits > 0);
// if (!bMove)
// {
// fDeltaDist *= Vector3.Distance(vFinalPos, (hits[0].point + Vector3.up * vExt.y)) / fDeltaDist;
// vFinalPos = hits[0].point + Vector3.up * vExt.y;
// }
// else
// {
// vFinalPos += vDelta;
// }
// if (fDeltaDist < (vExt.x * vExt.x * 4))
// {
// vFinalPos.y -= CDRInfo.fStepHeight;
// bPull = false;
// }
// }
// bTryPull = true;
// }
// if (!bPull)
// {
// fVelSpeed = Normalize(EC_Utility.ToA3DVECTOR3(vVelocity), ref vVelDir);
// fVelSpeed *= (1 - nTry * 0.1f);
// dtp = Vector3.Dot(vNormal, EC_Utility.ToVector3(vVelDir));
// float fRelSpeed = Mathf.Min(fVelSpeed, 5.0f);
// if (dtp >= 0f && dtp < 1e-4f)
// {
// vVelocity += vNormal * VEL_REFLECT * fRelSpeed;
// }
// else
// {
// vVelocity = (EC_Utility.ToVector3(vVelDir) - vNormal * dtp) * fVelSpeed - vNormal * dtp * VEL_REFLECT * fRelSpeed;
// }
// if (fYVel > VEL_EPSILON)
// {
// if (vNormal.y >= CDRInfo.fSlopeThresh || vNormal.y < -NORMAL_EPSILON)
// {
// fYVel = 0f;
// }
// }
// else if (fYVel < -VEL_EPSILON)
// {
// if (vNormal.y >= CDRInfo.fSlopeThresh)
// {
// fYVel = 0f;
// }
// }
// }
// }
// // “vertical ground trace” – thay RetrieveSupportPlane
// Vector3 vTPNormal = Vector3.zero;
// Vector3 vFinal = vFinalPos;
// //mask = BrushMask;
// float downDist = 0.3f;
// if (bPull) downDist = CDRInfo.fStepHeight + 0.1f;
// if (bJump) downDist = 0.0f;
// if (downDist > 0f)
// {
// if (!DoGroundProbe(vFinalPos, vExt, downDist, mask, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
// {
// CDRInfo.fMoveDist = 0f;
// CDRInfo.vTPNormal = new A3DVECTOR3(0, 1, 0);
// return;
// }
// if (bSupport)
// {
// vFinal = vEnd;
// if (!bJump)
// {
// vTPNormal = groundNormal;
// }
// }
// }
// if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f) || (!bJump && fYVel > 0.0f))
// {
// fYVel = 0.0f;
// }
// vDelta = vFinalPos - EC_Utility.ToVector3(CDRInfo.vCenter);
// CDRInfo.fMoveDist = vDelta.magnitude;
// CDRInfo.vCenter = EC_Utility.ToA3DVECTOR3(vFinal);
// CDRInfo.fYVel = fYVel;
// CDRInfo.vTPNormal = EC_Utility.ToA3DVECTOR3(vTPNormal);
//}
public static void OnGroundMove(ref CDR_INFO CDRInfo)
{
float VEL_EPSILON = 1E-4f;
float DIST_EPSILON = 1e-4f;
float NORMAL_EPSILON = 1e-2f;
float MAX_TRY = 4;
float VEL_MAX_SPEED = 200.0f;
float VEL_REFLECT = 0.3f;
CDRInfo.fMoveDist = 0.0f; //clear the moving dist
bool bFreeFall = (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh);
if (CDRInfo.fYVel < VEL_EPSILON && CDRInfo.fYVel > -VEL_EPSILON && CDRInfo.fSpeed < VEL_EPSILON && CDRInfo.fSpeed > -VEL_EPSILON && !bFreeFall)
{
return;
}
float fYVel = CDRInfo.fYVel; //save the y velocity;
//@todo : refine the speed to determine the jumping state. By Kuiwu[14/9/2005]
bool bJump = (fYVel > 0.5f);
//bool bJump = (fYVel > CDRInfo.fGravityAccel * 0.1f);
A3DVECTOR3 vVelocity = new A3DVECTOR3(CDRInfo.fSpeed* CDRInfo.vXOZVelDir +fYVel * GPDataTypeHelper.g_vAxisY );
if (bFreeFall)
{
vVelocity -= (CDRInfo.fGravityAccel * CDRInfo.t * GPDataTypeHelper.g_vAxisY);
fYVel -= CDRInfo.fGravityAccel * CDRInfo.t;
}
A3DVECTOR3 vVelDir = (vVelocity);
float fVelSpeed = vVelDir.Normalize();
if (!bFreeFall)
{
AAssist.a_ClampRoof(ref fVelSpeed, VEL_MAX_SPEED);
}
vVelocity = vVelDir * fVelSpeed;
//@note : clip the velocity or dir? By Kuiwu[8/9/2005]
float dtp = DotProduct(vVelDir, CDRInfo.vTPNormal);
if (dtp < 0.0f || !bJump)
{
vVelocity = (vVelDir - CDRInfo.vTPNormal * dtp - CDRInfo.vTPNormal * dtp * 0.01f) * fVelSpeed;
//a_LogOutput(1, "dtp < 0.0f || !bJump vVelocity = (%f, %f, %f) || CDRInfo.vTPNormal = (%f, %f, %f)", vVelocity.x, vVelocity.y, vVelocity.z, CDRInfo.vTPNormal.x, CDRInfo.vTPNormal.y, CDRInfo.vTPNormal.z);
}
CDRInfo.vAbsVelocity = vVelocity;
A3DVECTOR3 vStart = (CDRInfo.vCenter);
A3DVECTOR3 vExt = (CDRInfo.vExtent);
float fTime = CDRInfo.t;
//A3DVECTOR3 vDelta(vVelocity * fTime);
A3DVECTOR3 vDelta;
bool bClear = true;
int nTry = 0;
env_trace_t trcInfo = new env_trace_t();
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH;
trcInfo.vExt = vExt;
//A3DVECTOR3 vNormal, vFinalPos(vStart);
A3DVECTOR3 vNormal, vFinalPos = new A3DVECTOR3();
bool bPull = false;
bool bTryPull = false;
while (nTry < MAX_TRY)
{
//hits = new RaycastHit[5];
vDelta = vVelocity * fTime;
vFinalPos = vStart;
float fDeltaDist = vDelta.Magnitude();
//a_LogOutput(1, "fDeltaDist = %f || vVelocity = (%f, %f, %f) || fTime = %f", fDeltaDist, vVelocity.x, vVelocity.y, vVelocity.z, fTime);
//if (vDelta.SquaredMagnitude() < DIST_EPSILON )
if (fDeltaDist < DIST_EPSILON)
{
break;
}
trcInfo.vStart = vStart;
trcInfo.vDelta = vDelta;
trcInfo.vTerStart = vStart;
trcInfo.vTerStart.y -= vExt.y; //foot
bClear = !CollideWithEnv(ref trcInfo);
++nTry;
if (trcInfo.bStartSolid)
{
CDRInfo.fMoveDist = 0.0f;
if (CDRInfo.vTPNormal.y < CDRInfo.fSlopeThresh)
{
CDRInfo.vTPNormal = new A3DVECTOR3(0.0f, 1.0f, 0.0f);
}
//a_LogOutput(1, "bClear = true but trcInfo.bStartSolid = true");
return;
}
if (bClear)
{
//a_LogOutput(1, "bClear = true");
vFinalPos = vStart + vDelta;
break;
}
vStart += vDelta * trcInfo.fFraction;
fTime -= fTime * trcInfo.fFraction;
vNormal = trcInfo.vHitNormal;
if (!bFreeFall && !bTryPull && !bJump)
{
env_trace_t tmpInfo = new env_trace_t();
tmpInfo.vStart = vStart;
tmpInfo.vDelta = new A3DVECTOR3(0.0f, CDRInfo.fStepHeight, 0.0f);
tmpInfo.vExt = new A3DVECTOR3(vExt.x, 0.01f, vExt.z);
//@note : need check terrain?? By Kuiwu[8/10/2005]
tmpInfo.dwCheckFlag = CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_TERRAIN;
tmpInfo.vTerStart = vStart;
tmpInfo.vTerStart.y -= vExt.y;
//bPull = !CollideWithEnv(ref tmpInfo);
bPull = !CheckFootStepUp(tmpInfo);
//int count1 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(tmpInfo.vStart), EC_Utility.ToVector3(tmpInfo.vDelta).normalized, hits, EC_Utility.ToVector3(tmpInfo.vDelta).magnitude, BrushMask | TerrainMask);
//bPull = !(count1 < 0);
//if(count1 > 0)
//{
// Debug.DrawLine(EC_Utility.ToVector3(tmpInfo.vStart), hits[0].point, Color.yellow, 10f);
//}
//if(bPull == false)
//{
// BoxCastDrawer.Draw(EC_Utility.ToVector3(tmpInfo.vStart), EC_Utility.ToVector3(tmpInfo.vExt), EC_Utility.ToVector3(tmpInfo.vDelta).normalized,
// Quaternion.identity, EC_Utility.ToVector3(tmpInfo.vDelta).magnitude, hits, 1, nTry == 0 ? Color.red : Color.green, 10f);
//}
if (bPull)
{
vStart.y += CDRInfo.fStepHeight;
//vDelta = vVelocity * fTime;
vDelta = vVelocity;
tmpInfo.vStart = vStart;
tmpInfo.vDelta = vDelta;
tmpInfo.vTerStart = vStart;
tmpInfo.vTerStart.y -= vExt.y;
bool bMove = !CollideWithEnv(ref tmpInfo, false);
if (!bMove)
{
vDelta *= tmpInfo.fFraction;
}
float value = vDelta.SquaredMagnitude();
if (value < (vExt.x * vExt.x * 4))
{
vStart.y -= CDRInfo.fStepHeight;
bPull = false;
}
}
bTryPull = true;
}
if (!bPull)
{
fVelSpeed = Normalize(vVelocity, ref vVelDir);
fVelSpeed *= (1 - nTry * 0.1f);
dtp = DotProduct(vNormal, vVelDir);
float fRelSpeed = EC_Utility.a_Min(fVelSpeed, 5.0f);
if ((dtp < 1E-4f) && (dtp >= 0.0f))
{//@note : special parallel tangent plane case, rarely happen. By Kuiwu[20/10/2005]
vVelocity += vNormal * VEL_REFLECT * fRelSpeed;
}
else
{
vVelocity = (vVelDir - vNormal * dtp) * fVelSpeed - vNormal * dtp * VEL_REFLECT * fRelSpeed;
//vVelocity = (vVelDir - vNormal * dtp - vNormal*dtp * VEL_REFLECT) * fVelSpeed;
}
//CDRInfo.fYVel = vVelocity.y;
if (fYVel > VEL_EPSILON)
{
if ((vNormal.y >= CDRInfo.fSlopeThresh || vNormal.y < -NORMAL_EPSILON))
{
fYVel = 0.0f;
}
}
else if (fYVel < -VEL_EPSILON)
{
if ((vNormal.y >= CDRInfo.fSlopeThresh))
{
fYVel = 0.0f;
}
}
else
{
//@note : additional handle something??? By Kuiwu[13/9/2005]
}
}
}
////@note : prevent moving to the invalid area. By Kuiwu[20/9/2005]
//if (!IsPosInAvailableMap(vFinalPos))
//{
// CDRInfo.fMoveDist = 0.0f;
// return;
//}
A3DVECTOR3 vTPNormal = new A3DVECTOR3();
vTPNormal.Clear();
ground_trace_t groundTrc = new ground_trace_t();
groundTrc.vStart = vFinalPos;
groundTrc.vExt = vExt;
groundTrc.fDeltaY = 0.3f;
if (bPull)
{
groundTrc.fDeltaY = CDRInfo.fStepHeight + 0.1f;
}
if (bJump)
{
groundTrc.fDeltaY = 0.0f;
}
if (!RetrieveSupportPlane(ref groundTrc, true))
{//@note : do NOT change position. By Kuiwu[14/9/2005]
CDRInfo.fMoveDist = 0.0f;
// if (groundTrc.bSupport)
// {
// CDRInfo.vTPNormal = groundTrc.vHitNormal;
// }
CDRInfo.vTPNormal = new A3DVECTOR3(0.0f, 1.0f, 0.0f);
return;
}
if (groundTrc.bSupport)
{
vFinalPos = groundTrc.vEnd;
if (!bJump)
{
vTPNormal = groundTrc.vHitNormal;
}
}
if ((vTPNormal.y >= CDRInfo.fSlopeThresh && fYVel < 0.0f)
|| (!bJump && fYVel > 0.0f))
{
fYVel = 0.0f;
}
CDRInfo.vCenter = vFinalPos;
CDRInfo.fYVel = fYVel; //set back the y velocity
CDRInfo.vTPNormal = vTPNormal;
}
private static bool CheckFootStepUp(env_trace_t pEnvTrc)
{
Vector3 vExt = EC_Utility.ToVector3(pEnvTrc.vExt);
Vector3 vStart = EC_Utility.ToVector3(pEnvTrc.vStart);
Vector3 vDelta = EC_Utility.ToVector3(pEnvTrc.vDelta);
Vector3 dir = vDelta;
int countHits = 0;
countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask);
if (countHits > 0)
{
for(int i = 0; i < countHits; i++)
{
if (hits[i].collider != null && hits[i].point.y > (vStart.y + vExt.y))
{
return true;
}
}
}
return false;
}
//@desc : used to retrieve support plane (ground or brush), By Kuiwu[12/9/2005]
public struct ground_trace_t
{
public A3DVECTOR3 vStart;
public A3DVECTOR3 vExt;
public float fDeltaY; //down (-y)
public A3DVECTOR3 vEnd;
public A3DVECTOR3 vHitNormal;
public bool bSupport; //false if ground missed
};
public static bool RetrieveSupportPlane(ref ground_trace_t pTrc, bool check = false)
{
A3DVECTOR3 vTerrainPos = new A3DVECTOR3(), vTerrainNormal = new A3DVECTOR3();
GetTerrainInfo(pTrc.vStart, ref vTerrainPos, ref vTerrainNormal);
pTrc.bSupport = false;
// pTrc.vEnd = pTrc.vStart;
// pTrc.vEnd.y -= pTrc.fDeltaY;
BrushTraceInfo trcInfo = new BrushTraceInfo();
trcInfo.Init(pTrc.vStart, new A3DVECTOR3(0.0f, -pTrc.fDeltaY, 0.0f), pTrc.vExt);
Vector3 hitPoint = new Vector3();
if (AABBCollideWithBrush(ref trcInfo, ref hitPoint, check))
{
if (trcInfo.bStartSolid)
{
return false;
}
pTrc.vEnd = trcInfo.vStart + trcInfo.vDelta * trcInfo.fFraction;
pTrc.vEnd.y = hitPoint.y + pTrc.vExt.y + 0.1f;
//if((pTrc.vEnd.y + 0.17f) < (hitPoint.y + pTrc.vExt.y + 0.1f))
//{
// pTrc.vEnd.y = hitPoint.y + pTrc.vExt.y + 0.1f; // need add delta Y for Support Plane when check with brush mask.
//}
//else
//{
// pTrc.vEnd.y += 0.17f;
//}
pTrc.vHitNormal = trcInfo.normal;
float fUp = pTrc.vExt.y;
if (pTrc.vEnd.y > vTerrainPos.y + fUp)
{
pTrc.vStart = trcInfo.vStart;
pTrc.bSupport = true;
return true;
}
}
//if (vTerrainPos.y > pTrc.vStart.y- pTrc.vExt.y )
if (vTerrainPos.y > pTrc.vStart.y - pTrc.vExt.y || (vTerrainPos.y <= pTrc.vStart.y - pTrc.vExt.y && vTerrainPos.y >= pTrc.vStart.y - pTrc.vExt.y - pTrc.fDeltaY))
{
pTrc.vEnd = vTerrainPos;
//@note : maybe sink in the sleep ground, but this is what I need. By Kuiwu[14/9/2005]
pTrc.vEnd.y += (pTrc.vExt.y + 0.01f);
A3DVECTOR3 vDelta = new A3DVECTOR3(pTrc.vEnd -pTrc.vStart);
trcInfo.Init(pTrc.vStart, vDelta, pTrc.vExt);
pTrc.vHitNormal = vTerrainNormal;
pTrc.bSupport = true;
return !AABBCollideWithBrush(ref trcInfo, ref hitPoint, check);
}
return true;
}
public static void GetTerrainInfo(A3DVECTOR3 vPos, ref A3DVECTOR3 vPosOnSurface, ref A3DVECTOR3 vNormal)
{
vPosOnSurface = vPos;
int countHits = 0;
countHits = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vPosOnSurface), Vector3.down, fHitsTerrain, maxDistance: 1000f, TerrainMask);
if (countHits > 0)
{
int idx = 0;
float maxDistance = 100f;
//System.Array.Sort(fHitsTerrain, 0, fHitsTerrain.Length, raycastHitDistanceComparer);
for (int i = 0; i < countHits; i++)
{
if (fHitsTerrain[i].collider != null && fHitsTerrain[i].distance > 0f)
{
if(fHitsTerrain[i].distance < maxDistance)
{
maxDistance = fHitsTerrain[i].distance;
idx = i;
}
}
}
vPosOnSurface.y = fHitsTerrain[idx].point.y;
vNormal = EC_Utility.ToA3DVECTOR3(fHitsTerrain[idx].normal);
return;
}
}
// Get normalize
static float Normalize(A3DVECTOR3 vIn, ref A3DVECTOR3 vOut)
{
float fMag = vIn.Magnitude();
if (fMag < 1e-6 && fMag > -1e-6)
{
vOut.Clear();
fMag = 0.0f;
}
else
{
float f = 1.0f / fMag;
vOut = vIn * f;
}
return fMag;
}
static bool SegmentTriangleIntersect(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, A3DVECTOR3[] vert, ref float fFraction, bool bCull)
{
float dist = 0f;
A3DVECTOR3 vDir = new A3DVECTOR3(vDelta);
dist = vDir.Normalize();
if (dist < 1E-5f)
{
//assert(0 && "too small dist!");
fFraction = 0.0f;
return true;
}
float t = 0f, u = 0f, v = 0f;
if (RayTriangleIntersect(vStart, vDir, vert, ref t, ref u, ref v, bCull) && (t >= 0.0f) && (t <= dist))
{
//fFraction = t/dist;
//fFraction = a_Max( 0.0f, fFraction -1E-4f); //put back
fFraction = (t - 5E-4f) / dist;
AAssist.a_ClampFloor(ref fFraction, 0.0f);
return true;
}
return false;
}
static bool RayTriangleIntersect(A3DVECTOR3 vOrigin, A3DVECTOR3 vDir, A3DVECTOR3[] vert, ref float t, ref float u, ref float v, bool bCull)
{
// find vectors for two edges sharing vert0
A3DVECTOR3 edge1 = vert[1] - vert[0];
A3DVECTOR3 edge2 = vert[2] - vert[0];
// begin calculating determinant - also used to calculate U parameter
A3DVECTOR3 pvec = A3DVECTOR3.CrossProduct(vDir, edge2);
// if determinant is near zero, ray lies in plane of triangle
float det = A3DVECTOR3.DotProduct(edge1, pvec);
if (bCull)
{
if (det < LOCAL_EPSILON)
return false;
// From here, det is > 0.
// Calculate distance from vert0 to ray origin
A3DVECTOR3 tvec = vOrigin - vert[0];
// Calculate U parameter and test bounds
u = A3DVECTOR3.DotProduct(tvec, pvec);
if ((u < 0.0f) || (u > det))
return false;
// prepare to test V parameter
A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1);
// calculate V parameter and test bounds
v = A3DVECTOR3.DotProduct(vDir, qvec);
if ((v < 0.0f) || (u + v > det))
return false;
// calculate t, ray intersects triangle
t = A3DVECTOR3.DotProduct(edge2, qvec);
// Det > 0 so we can early exit here
// Intersection point is valid if distance is positive
// (else it can just be a face behind the orig point)
if (t < 0.0f)
{
return false;
}
float OneOverDet = 1.0f / det;
t *= OneOverDet;
u *= OneOverDet;
v *= OneOverDet;
}
else
{
if (det > -LOCAL_EPSILON && det < LOCAL_EPSILON)
return false;
float OneOverDet = 1.0f / det;
// Calculate distance from vert0 to ray origin
A3DVECTOR3 tvec = vOrigin - vert[0];
// calculate U parameter and test bounds
u = A3DVECTOR3.DotProduct(tvec, pvec) * OneOverDet;
if ((u < 0.0f) || (u > 1.0f))
return false;
// prepare to test V parameter
A3DVECTOR3 qvec = A3DVECTOR3.CrossProduct(tvec, edge1);
// calculate V parameter and test bounds
v = A3DVECTOR3.DotProduct(vDir, qvec) * OneOverDet;
if ((v < 0.0f) || (u + v > 1.0f))
return false;
// calculate t, ray intersects triangle
t = A3DVECTOR3.DotProduct(edge2, qvec) * OneOverDet;
}
return true;
}
public static bool CollideWithTerrain(A3DVECTOR3 vStart, A3DVECTOR3 vDelta, ref float fFraction, ref A3DVECTOR3 vHitNormal, ref bool bStart)
{
CECWorld pWorld = CECGameRun.Instance.GetWorld(); //g_pGame.GetGameRun().GetWorld();
A3DTerrain2 pTerrain = pWorld.GetTerrain();
bStart = false;
float h1 = pTerrain.GetPosHeight(vStart, ref vHitNormal);
if (h1 > vStart.y + 1E-4f)
{//start under terrain
bStart = true;
fFraction = 0.0f;
return true;
}
int nWid, nHei; // in grid, 2 meters
float fMag = vDelta.Magnitude();
nWid = (int)Math.Ceiling(fMag / 2.0f);
nWid = Math.Max(3, nWid);
nHei = nWid;
int nTriangles = nWid * nHei * 2;
A3DVECTOR3[] pVerts = new A3DVECTOR3[(nWid + 1) * (nHei + 1)];
//assert(pVerts != NULL);
//memset(pVerts, 0, sizeof(A3DVECTOR3)* (nWid + 1) * (nHei + 1));
WORD[] pIndices = new WORD[nTriangles * 3];
//assert(pIndices != NULL);
//memset(pIndices, 0, sizeof(WORD)* nTriangles * 3);
if (!pTerrain.GetFacesOfArea(vStart, nWid, nHei, ref pVerts, ref pIndices))
{
//a_freetemp(pVerts);
// a_freetemp(pIndices);
return false;
}
int i;
A3DVECTOR3[] vert = new A3DVECTOR3[3];
//@note : Here init the fraction. By Kuiwu[9/10/2005]
fFraction = 100.0f;
float tmpFraction = fFraction;
for (i = 0; i < nTriangles; i++)
{
vert[0] = pVerts[pIndices[i * 3]];
vert[1] = pVerts[pIndices[i * 3 + 1]];
vert[2] = pVerts[pIndices[i * 3 + 2]];
//A3DVECTOR3 vPt;
//@note: Tomas Moller's JGT code : By Kuiwu[9/10/2005]
//@note: discard the engine version because it put back the hit point too much. By Kuiwu[13/10/2005]
// if(CLS_RayToTriangle(vStart, vDelta, *vert[0], *vert[1], *vert[2], vPt, true, &tmpFraction)
// && (tmpFraction <= 1.0f) && (tmpFraction < fFraction))
if (SegmentTriangleIntersect(vStart, vDelta, vert, ref tmpFraction, true) && (tmpFraction < fFraction))
{
//get the triangle normal
A3DVECTOR3 vEdge1 = vert[1] - vert[0];
A3DVECTOR3 vEdge2 = vert[2] - vert[0];
vHitNormal = A3DVECTOR3.CrossProduct(vEdge1, vEdge2);
vHitNormal.Normalize();
//@note : may be redundant, but to assure. By Kuiwu[17/10/2005]
A3DVECTOR3 vDir = new A3DVECTOR3();
A3DVECTOR3.Normalize(vDelta, out vDir);
if (A3DVECTOR3.DotProduct(vHitNormal, vDir) > 0.01f)
{//leave the hit plane
//assert(0 && "hit a plane with same direction!");
continue;
}
fFraction = Math.Max(0.0f, tmpFraction);
}
}
//a_freetemp(pVerts);
// a_freetemp(pIndices);
return (fFraction <= 1.0f);
}
public static bool AABBCollideWithBrush(ref BrushTraceInfo brushTraceInfo)
{
Vector3 vExt = EC_Utility.ToVector3(brushTraceInfo.vExtents);
Vector3 vStart = EC_Utility.ToVector3(brushTraceInfo.vStart);
Vector3 vDelta = EC_Utility.ToVector3(brushTraceInfo.vDelta);
Vector3 dir = vDelta;
int countHits = 0;
float fFraction = 100f;
Vector3 normal = Vector3.zero;
Vector3 hitP = Vector3.zero;
countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask);
for (int i = 0; i < countHits; i++)
{
if (hits[i].collider != null && hits[i].distance > 0f)
{
float value = (hits[i].distance) / vDelta.magnitude;
if (value < fFraction)
{
fFraction = value;
normal = hits[i].normal;
hitP = hits[i].point;
}
}
}
if (fFraction < 100f)
{
if (fFraction > 0f && fFraction <= 1f)
{
brushTraceInfo.fFraction = fFraction;
brushTraceInfo.normal = EC_Utility.ToA3DVECTOR3(normal);
}
if (Math.Abs(fFraction - 0f) < float.Epsilon)
{
brushTraceInfo.bStartSolid = true;
}
return true;
}
brushTraceInfo.normal = new A3DVECTOR3(0f);
brushTraceInfo.fFraction = 100f;
return false;
}
public static bool AABBCollideWithBrush(ref BrushTraceInfo brushTraceInfo, ref Vector3 hitPoint, bool check)
{
Vector3 vExt = EC_Utility.ToVector3(brushTraceInfo.vExtents);
Vector3 vStart = EC_Utility.ToVector3(brushTraceInfo.vStart);
Vector3 vDelta = EC_Utility.ToVector3(brushTraceInfo.vDelta);
Vector3 dir = vDelta;
int countHits = 0;
float fFraction = 100f;
Vector3 normal = Vector3.zero;
Vector3 hitP = Vector3.zero;
countHits = Physics.BoxCastNonAlloc(vStart, vExt, dir.normalized, hits, Quaternion.identity, vDelta.magnitude, BrushMask);
for (int i = 0; i < countHits; i++)
{
if (hits[i].collider != null && hits[i].distance > 0f)
{
float value = (hits[i].distance) / vDelta.magnitude;
if(value < fFraction)
{
fFraction = value;
normal = hits[i].normal;
hitP = hits[i].point;
}
}
}
if(fFraction < 100f)
{
if(fFraction > 0f && fFraction <= 1f)
{
brushTraceInfo.fFraction = fFraction;
brushTraceInfo.normal = EC_Utility.ToA3DVECTOR3(normal);
hitPoint = hitP;
}
if (Math.Abs(fFraction - 0f) < float.Epsilon)
{
brushTraceInfo.bStartSolid = true;
}
return true;
}
brushTraceInfo.normal = new A3DVECTOR3(0f);
brushTraceInfo.fFraction = 100f;
hitPoint = Vector3.zero;
return false;
}
public static void OnAirMove(ref ON_AIR_CDR_INFO awmInfo)
{
//assert(0 && "Not ready yet");
if (awmInfo.bOnAir)
{
AirMove(ref awmInfo);
}
else
{
WaterMove(ref awmInfo);
}
}
static void AirMove(ref ON_AIR_CDR_INFO awmInfo)
{
float DIST_EPSILON = 1e-4f;
int MAX_TRY = 4;
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 = new env_trace_t();
trcInfo.bWaterSolid = true;
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER;
trcInfo.vExt = vExt;
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(ref trcInfo);
++nTry;
if (bClear)
{
vFinalPos = vStart + vDelta;
break;
}
vStart += vDelta * trcInfo.fFraction;
vFinalPos = vStart;
fTime -= fTime * trcInfo.fFraction;
vNormal = trcInfo.vHitNormal;
fSpeed = Normalize(vVelocity,ref vVelDir);
fSpeed *= (1 - nTry * 0.1f);
dtp = 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
float fDeltaY = awmInfo.fHeightThresh + 0.1f;
LayerMask mask = TerrainMask | BrushMask;
if (!DoGroundProbe(EC_Utility.ToVector3(vFinalPos), EC_Utility.ToVector3(vExt), fDeltaY, mask, out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
{
return;
}
A3DVECTOR3 vTpNormal = new A3DVECTOR3(0.0f);
A3DVECTOR3 vOverTp = vFinalPos;
bool bAdjust = false;
awmInfo.bMeetHeightThresh = true;
float fHWater = 0f;
int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vFinalPos) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask);
if (countHits0 > 0)
{
fHWater = fHitsWater[0].point.y;
}
if (bSupport)
{
bAdjust = true;
vOverTp = EC_Utility.ToA3DVECTOR3(vEnd);
vTpNormal = EC_Utility.ToA3DVECTOR3(groundNormal);
if (fHWater > vEnd.y)
{
vOverTp.y = fHWater;
vTpNormal = GPDataTypeHelper.g_vAxisY;
}
}
else if (vFinalPos.y < fHWater + awmInfo.fHeightThresh)
{
bAdjust = true;
vOverTp = vFinalPos;
vOverTp.y = fHWater;
vTpNormal = GPDataTypeHelper.g_vAxisY;
}
if (bAdjust && (vOverTp.y + awmInfo.fHeightThresh > vFinalPos.y))
{
BrushTraceInfo brushTrc = new BrushTraceInfo();
vDelta.Clear();
vDelta.y = vOverTp.y + awmInfo.fHeightThresh - vFinalPos.y;
float fAllow = (float)Math.Abs(awmInfo.vCenter.y - vFinalPos.y) + 0.001f;
fAllow = EC_Utility.a_Max(fAllow, 0.15f);
AAssist.a_ClampRoof(ref vDelta.y, fAllow);
brushTrc.Init(vFinalPos, vDelta, vExt);
if (AABBCollideWithBrush(ref brushTrc))
{
vFinalPos += (vDelta * brushTrc.fFraction);
}
else
{
vFinalPos += vDelta;
}
awmInfo.bMeetHeightThresh = (vFinalPos.y - vOverTp.y > awmInfo.fHeightThresh);
}
awmInfo.vCenter = vFinalPos;
awmInfo.vTPNormal = vTpNormal;
}
static void WaterMove(ref ON_AIR_CDR_INFO awmInfo)
{
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 = awmInfo.vCenter;
A3DVECTOR3 vExt = awmInfo.vExtent;
A3DVECTOR3 vVelDir = awmInfo.vVelDir;
float dtp = 0f;
A3DVECTOR3 vVelocity = vVelDir* fSpeed;
if ((dtp = DotProduct(vVelDir, awmInfo.vTPNormal)) < 0.0f)
{
vVelocity = (vVelDir - awmInfo.vTPNormal * dtp - awmInfo.vTPNormal * dtp * 0.01f) * fSpeed;
}
A3DVECTOR3 vDelta = (vVelocity* fTime), vNormal, vFinalPos = vStart;
int nTry = 0;
bool bClear = true;
env_trace_t trcInfo = new env_trace_t();
trcInfo.bWaterSolid = false;
trcInfo.dwCheckFlag = CDR_EVN.CDR_TERRAIN | CDR_EVN.CDR_BRUSH | CDR_EVN.CDR_WATER;
trcInfo.vExt = vExt;
while (nTry < 4)
{
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 += awmInfo.fUnderWaterDistThresh; //shoulder
bClear = !CollideWithEnv(ref trcInfo);
++nTry;
if (bClear || (trcInfo.bStartSolid && ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) != CDR_EVN.CDR_WATER)))
{
vFinalPos = vStart + vDelta;
break;
}
vStart += vDelta * trcInfo.fFraction;
fTime -= fTime * trcInfo.fFraction;
fSpeed = Normalize(vVelocity,ref vVelDir);
fSpeed *= (1 - nTry * 0.1f);
if ((trcInfo.dwClsFlag & CDR_EVN.CDR_WATER) == CDR_EVN.CDR_WATER)
{
if (trcInfo.bStartSolid)
{//rescue from solid
//@note : it may cause some problems. By Kuiwu[11/10/2005]
float fHWater = 0f;
int countHits0 = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vStart) + Vector3.up * 500f, Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask);
if(countHits0 > 0)
{
fHWater = fHitsWater[0].point.y;
}
vStart.y = fHWater;
}
vVelDir.y = 0.0f;
vVelocity = vVelDir * fSpeed;
}
else
{
vNormal = trcInfo.vHitNormal;
dtp = DotProduct(vNormal, vVelDir);
vVelocity = (vVelDir - vNormal * dtp - vNormal * dtp * VEL_REFLECT) * fSpeed;
}
vDelta = vVelocity * fTime;
vFinalPos = vStart;
}
//@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;
}
//see if meet height thresh
Vector3 v3Start = EC_Utility.ToVector3(vFinalPos);
Vector3 v3Ext = EC_Utility.ToVector3(vExt);
float fDeltaY = awmInfo.fHeightThresh + 0.1f;
if (!DoGroundProbe(v3Start, v3Ext, fDeltaY, UsedMask_Ground(), out Vector3 vEnd, out Vector3 groundNormal, out bool bSupport))
{
return;
}
A3DVECTOR3 vTpNormal = new A3DVECTOR3(0.0f);
A3DVECTOR3 vOverTp = vFinalPos;
bool bAdjust = false;
awmInfo.bMeetHeightThresh = true;
if (bSupport)
{
bAdjust = true;
vOverTp = EC_Utility.ToA3DVECTOR3(vEnd);
vTpNormal = EC_Utility.ToA3DVECTOR3(groundNormal);
}
if (bAdjust && (vOverTp.y + awmInfo.fHeightThresh > vFinalPos.y))
{
float fHWater = 0f;
int countHits = Physics.RaycastNonAlloc(EC_Utility.ToVector3(vFinalPos), Vector3.down, fHitsWater, maxDistance: 1000f, WaterMask);
if (countHits > 0)
{
fHWater = fHitsWater[0].point.y;
}
float fAllow = fHWater - awmInfo.fUnderWaterDistThresh - vFinalPos.y;
if (fAllow > 1E-4f)
{
vDelta.Clear();
vDelta.y = vOverTp.y + awmInfo.fHeightThresh - vFinalPos.y;
fAllow = EC_Utility.a_Min(fAllow, 0.15f);
AAssist.a_ClampRoof(ref vDelta.y, fAllow);
BrushTraceInfo brushTrc = new BrushTraceInfo();
brushTrc.Init(vFinalPos, vDelta, vExt);
if (AABBCollideWithBrush(ref brushTrc))
{
vFinalPos += (vDelta * brushTrc.fFraction);
}
else
{
vFinalPos += vDelta;
}
awmInfo.bMeetHeightThresh = (vFinalPos.y - vOverTp.y > awmInfo.fHeightThresh);
}
}
awmInfo.vCenter = vFinalPos;
awmInfo.vTPNormal = vTpNormal;
}
//////////////////////////////////////////////////////////////////////////
// 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;
}
static float DotProduct(A3DVECTOR3 v1, A3DVECTOR3 v2) { return v1.x* v2.x + v1.y* v2.y + v1.z* v2.z;}
}
public struct OtherPlayer_Move_Info
{
// Bounding sphere of avator
public A3DVECTOR3 vCenter;
public A3DVECTOR3 vExts;
public float fStepHeight;
public A3DVECTOR3 vVelocity;
public float t;
public bool bTraceGround; // Whether trace the ground
public bool bTestTrnOnly; // Trace terrain only
public A3DVECTOR3 vecGroundNormal; // if bTraceGround is true, this will contain the ground normal when returned
};
//@desc :used to trace the environment, brush&terrain&water By Kuiwu[8/10/2005]
public struct env_trace_t
{
public A3DVECTOR3 vStart; // brush start
public A3DVECTOR3 vExt;
public A3DVECTOR3 vDelta;
public A3DVECTOR3 vTerStart;
public A3DVECTOR3 vWatStart;
public uint dwCheckFlag;
public bool bWaterSolid;
public float fFraction;
public A3DVECTOR3 vHitNormal;
public bool bStartSolid; //start in solid
public uint dwClsFlag; //collision flag
};
// for on-air move case
//@note : change to AABB. By Kuiwu[22/9/2005]
public struct ON_AIR_CDR_INFO
{
public A3DVECTOR3 vCenter;
public A3DVECTOR3 vExtent;
// Hold a height from the surface of terrain or building
public float fHeightThresh;
// Velocity Info
public A3DVECTOR3 vVelDir;
public float fSpeed;
// time span ( sec )
public float t;
//@note : SlopeThresh seems useless on air or under water. By Kuiwu[22/9/2005]
// Slope Thresh
//float fSlopeThresh;
// Distance Thresh under the water surface
public float fUnderWaterDistThresh;
public A3DVECTOR3 vTPNormal;
// On air or water, true if on air, false for on water case.
public bool bOnAir;
// After the move action is done, If the fHeightThresh
// still be satisfied, bMeetHeightThresh is set to true.
public bool bMeetHeightThresh;
};
public static class BoxCastDrawer
{
///
/// Vẽ BoxCast để Debug
///
/// Thời gian tồn tại của đường kẻ (giây). Mặc định là 0 (1 frame)
public static void Draw(
Vector3 center,
Vector3 halfExtents,
Vector3 direction,
Quaternion orientation,
float distance,
RaycastHit[] hits,
int hitCount,
Color color,
float duration = 0f) // Thêm tham số duration ở đây
{
// 1. Tính toán các góc
Vector3[] startCorners = GetBoxCorners(center, halfExtents, orientation);
float drawDistance = distance;
if (hitCount > 0)
{
float minDistance = distance;
for (int i = 0; i < hitCount; i++)
{
if (hits[i].distance < minDistance && hits[i].distance > 0)
minDistance = hits[i].distance;
}
drawDistance = minDistance;
}
Vector3 endCenter = center + direction.normalized * drawDistance;
Vector3[] endCorners = GetBoxCorners(endCenter, halfExtents, orientation);
// 2. Thiết lập màu sắc
//Color color = hitCount > 0 ? Color.red : Color.green;
//Color sweepColor = new Color(0.5f, 0.5f, 0.5f, 0.5f); // Màu xám mờ
Color sweepColor = color; // Màu xám mờ
// 3. VẼ TÂM (CENTER) - Hiển thị trục tọa độ Local của hộp
DrawCenterAxis(center, orientation, 0.3f, duration); // Tâm bắt đầu
DrawCenterAxis(endCenter, orientation, 0.3f, duration); // Tâm kết thúc
// 3. Vẽ các đường Line với Duration
for (int i = 0; i < 4; i++)
{
// Vẽ hộp bắt đầu
Debug.DrawLine(startCorners[i], startCorners[(i + 1) % 4], color, duration);
Debug.DrawLine(startCorners[i + 4], startCorners[((i + 1) % 4) + 4], color, duration);
Debug.DrawLine(startCorners[i], startCorners[i + 4], color, duration);
// Vẽ hộp kết thúc/va chạm
Debug.DrawLine(endCorners[i], endCorners[(i + 1) % 4], color, duration);
Debug.DrawLine(endCorners[i + 4], endCorners[((i + 1) % 4) + 4], color, duration);
Debug.DrawLine(endCorners[i], endCorners[i + 4], color, duration);
// Vẽ các đường nối (Sweep lines)
Debug.DrawLine(startCorners[i], endCorners[i], sweepColor, duration);
Debug.DrawLine(startCorners[i + 4], endCorners[i + 4], sweepColor, duration);
}
// 4. Vẽ chi tiết va chạm
for (int i = 0; i < hitCount; i++)
{
// Vẽ pháp tuyến (Normal) tại điểm chạm
Debug.DrawRay(hits[i].point, hits[i].normal * 0.5f, Color.yellow, duration);
// Vẽ một dấu X nhỏ tại điểm va chạm chính xác
DrawCross(hits[i].point, 0.1f, Color.cyan, duration);
}
}
private static void DrawCross(Vector3 point, float size, Color color, float duration)
{
Debug.DrawLine(point + Vector3.up * size, point - Vector3.up * size, color, duration);
Debug.DrawLine(point + Vector3.right * size, point - Vector3.right * size, color, duration);
Debug.DrawLine(point + Vector3.forward * size, point - Vector3.forward * size, color, duration);
}
private static Vector3[] GetBoxCorners(Vector3 center, Vector3 halfExtents, Quaternion orientation)
{
Vector3[] corners = new Vector3[8];
Vector3 h = halfExtents;
corners[0] = center + orientation * new Vector3(-h.x, -h.y, h.z);
corners[1] = center + orientation * new Vector3(h.x, -h.y, h.z);
corners[2] = center + orientation * new Vector3(h.x, h.y, h.z);
corners[3] = center + orientation * new Vector3(-h.x, h.y, h.z);
corners[4] = center + orientation * new Vector3(-h.x, -h.y, -h.z);
corners[5] = center + orientation * new Vector3(h.x, -h.y, -h.z);
corners[6] = center + orientation * new Vector3(h.x, h.y, -h.z);
corners[7] = center + orientation * new Vector3(-h.x, h.y, -h.z);
return corners;
}
public static void DrawCenterAxis(Vector3 pos, Quaternion rot, float size, float duration)
{
Debug.DrawRay(pos, rot * Vector3.right * size, Color.red, duration);
Debug.DrawRay(pos, rot * Vector3.up * size, Color.green, duration);
Debug.DrawRay(pos, rot * Vector3.forward * size, Color.blue, duration);
// Vẽ thêm một khối diamond nhỏ màu trắng tại tâm để dễ nhận diện
float s = size * 0.2f;
Debug.DrawLine(pos + rot * Vector3.up * s, pos + rot * Vector3.right * s, Color.white, duration);
Debug.DrawLine(pos + rot * Vector3.right * s, pos - rot * Vector3.up * s, Color.white, duration);
Debug.DrawLine(pos - rot * Vector3.up * s, pos - rot * Vector3.left * s, Color.white, duration);
Debug.DrawLine(pos - rot * Vector3.left * s, pos + rot * Vector3.up * s, Color.white, duration);
}
}
}