You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
388 lines
16 KiB
388 lines
16 KiB
using System;
|
|
using Entitas;
|
|
using Sog;
|
|
using UnityEngine;
|
|
using xFrame;
|
|
|
|
namespace CoreGame.Render
|
|
{
|
|
/// <summary>
|
|
/// 大杂烩,新老规则在一起的,不要动;动的话要注意 掩码的概念
|
|
/// </summary>
|
|
public class LevelWaveSystem : IExecuteSystem
|
|
{
|
|
private LevelWaveComponent m_LevelWave;
|
|
|
|
public LevelWaveSystem(CombatContext contexts)
|
|
{
|
|
}
|
|
|
|
public void Execute(float dt)
|
|
{
|
|
var levelWaveEntity = Contexts.Combat.LevelWave;
|
|
if (levelWaveEntity == null)
|
|
return;
|
|
|
|
m_LevelWave = levelWaveEntity.levelWave;
|
|
if (m_LevelWave.state == LevelSpawnState.Start)
|
|
{
|
|
m_LevelWave.state = LevelSpawnState.Spawning;
|
|
}
|
|
|
|
if (m_LevelWave.state == LevelSpawnState.Spawning)
|
|
{
|
|
TrySpawn(dt);
|
|
}
|
|
|
|
if (m_LevelWave.state == LevelSpawnState.End)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// 多长时间未过关,闯关失败
|
|
m_LevelWave.totalTimeAcc += dt;
|
|
}
|
|
|
|
private void TrySpawn(Fixed64 dt)
|
|
{
|
|
for (var i = 0; i < m_LevelWave.waveParams.Count; i++)
|
|
{
|
|
var waveParam = m_LevelWave.waveParams[i];
|
|
SpawnSingle(waveParam, i, dt);
|
|
}
|
|
|
|
// 超时失败
|
|
if (m_LevelWave.totalTime <= m_LevelWave.totalTimeAcc)
|
|
{
|
|
CoreUIBridge.CoreGamePushBattleEndData(m_LevelWave.battleId, CoreUIBridge.BattleEndType.LoseTimeEnd);
|
|
m_LevelWave.state = LevelSpawnState.End;
|
|
}
|
|
}
|
|
|
|
private void SpawnSingle(WaveParam waveParam, int paramIdx, Fixed64 dt)
|
|
{
|
|
if (waveParam.state == WaveSpawnState.End)
|
|
return;
|
|
|
|
if (waveParam.state == WaveSpawnState.TryStart || waveParam.state == WaveSpawnState.None)
|
|
{
|
|
if (m_LevelWave.CanStart(paramIdx))
|
|
{
|
|
// 激活下一波
|
|
RPCCaller.Call_BattleOption(ProtoCSClass.ChapterOption.ActiveMonster);
|
|
|
|
waveParam.state = WaveSpawnState.Spawning;
|
|
m_LevelWave.curStageId = waveParam.waveData.stageId;
|
|
var battleWaveData = SptPool<BattleWaveData>.Malloc();
|
|
battleWaveData.StageId = waveParam.waveData.stageId;
|
|
|
|
if (waveParam.waveData.stageType == ChapterBattleStageType.Boss)
|
|
{
|
|
// 这里发消息通知UI是BOSS波次
|
|
battleWaveData.BossId = waveParam.waveData.monster[0].List[0];
|
|
battleWaveData.BossHpLeft = 1f;
|
|
}
|
|
CoreUIBridge.CoreGamePushBattleWaveData(battleWaveData);
|
|
SptPool<BattleWaveData>.Free(ref battleWaveData);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (waveParam.state == WaveSpawnState.Spawning)
|
|
{
|
|
var waveData = waveParam.waveData;
|
|
waveParam.accTime += dt;
|
|
var noCheckJump = false;
|
|
// 遍历生成组 // 小波次可并行
|
|
for (int i = 0; i < waveData.monster.Length; i++)
|
|
{
|
|
var smallGroup = waveData.monster[i];
|
|
if (smallGroup == null)
|
|
continue;
|
|
|
|
var typeMask = (int)smallGroup.PositionType;
|
|
// 小波次时刻
|
|
var timeFrame = smallGroup.BornTime * BattleConst.TenThousandReverse;
|
|
// 小波次将要处理的索引,索引 Num, Local等
|
|
var spawnedIdx = waveParam.spawnedIdx[i];
|
|
// // 证明小波次没刷完
|
|
// if ((typeMask & 0b11110000) != 0)
|
|
// noCheckJump |= spawnedIdx > 0 && spawnedIdx < smallGroup.Local.Length;
|
|
//
|
|
// // 上一小波没完
|
|
// if (i > 0)
|
|
// noCheckJump |= waveParam.spawnedIdx[i - 1] == 0;
|
|
// 清场后跳时间,对齐最近 BornTime
|
|
if (!noCheckJump && m_LevelWave.lifeEid2WaveIdx.Count == 0)
|
|
{
|
|
// 最近一个时刻, 有可能跳过了
|
|
var ts = timeFrame - waveParam.accTime;
|
|
if ((typeMask & 0b11110000) != 0 && spawnedIdx < smallGroup.Local.Length)
|
|
{
|
|
ts += waveParam.newRuleTimeSpanAcc[i] + smallGroup.Local[spawnedIdx] * BattleConst.TenThousandReverse;
|
|
}
|
|
|
|
if (ts > 0)
|
|
{
|
|
waveParam.jumpTimeSpanAcc += ts;
|
|
noCheckJump = true;
|
|
}
|
|
}
|
|
// 老规则不动了吧
|
|
if ((typeMask & 0b1111) != 0)
|
|
{
|
|
// spawnedIdx 对应的是怪物的 小波次的 List索引值,不允许超上限
|
|
while (waveParam.accTime + waveParam.jumpTimeSpanAcc >= timeFrame)
|
|
{
|
|
if (spawnedIdx >= smallGroup.List.Length)
|
|
break;
|
|
|
|
SpawnMonster(smallGroup, spawnedIdx, paramIdx, waveData.stageId);
|
|
waveParam.lastSpawnCount -= smallGroup.Num[spawnedIdx];
|
|
// timeFrame = smallGroup.BornTime * BattleConst.TenThousandReverse;
|
|
waveParam.spawnedIdx[i] = ++spawnedIdx;
|
|
if (waveParam.lastSpawnCount <= 0)
|
|
{
|
|
waveParam.state = WaveSpawnState.Spawned;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else if ((typeMask & 0b11110000) != 0)
|
|
{
|
|
do
|
|
{
|
|
// Local 新规代表 时间便宜
|
|
if (spawnedIdx >= smallGroup.Local.Length)
|
|
break;
|
|
// 没到时刻
|
|
if (!(waveParam.accTime + waveParam.jumpTimeSpanAcc >=
|
|
timeFrame + waveParam.newRuleTimeSpanAcc[i] + smallGroup.Local[spawnedIdx] * BattleConst.TenThousandReverse))
|
|
break;
|
|
|
|
waveParam.newRuleTimeSpanAcc[i] += smallGroup.Local[spawnedIdx] * BattleConst.TenThousandReverse;
|
|
SpawnMonster(smallGroup, spawnedIdx, paramIdx, waveData.stageId);
|
|
waveParam.lastSpawnCount -= smallGroup.Num[spawnedIdx];
|
|
waveParam.spawnedIdx[i] = ++spawnedIdx;
|
|
|
|
if (waveParam.lastSpawnCount <= 0)
|
|
{
|
|
waveParam.state = WaveSpawnState.Spawned;
|
|
break;
|
|
}
|
|
} while (true);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (waveParam.state == WaveSpawnState.Spawned)
|
|
{
|
|
var waveData = waveParam.waveData;
|
|
waveParam.accTime += dt;
|
|
|
|
if (waveParam.accTime >= waveData.stageLimitTime)
|
|
{
|
|
waveParam.state = WaveSpawnState.End;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
private void SpawnMonster(ChapterBattleStageDesc.Monster groupData, int spawnedIdx, int paramIdx, int waveIdx)
|
|
{
|
|
int mask = (int)groupData.PositionType;
|
|
if ((mask & 0b11) != 0)
|
|
{
|
|
RandomSpawnMonster(groupData, spawnedIdx, paramIdx, waveIdx);
|
|
}
|
|
else if ((mask & 0b110000) != 0)
|
|
{
|
|
// 随机算法不合理
|
|
RandomGlobalSpawnPositions(groupData, spawnedIdx, paramIdx, waveIdx);
|
|
}
|
|
else if ((mask & 0b11000000) != 0)
|
|
{
|
|
LimitRandomGlobalSpawnPositions(groupData, spawnedIdx, paramIdx, waveIdx);
|
|
}
|
|
else
|
|
{
|
|
FixedSpawnMonster(groupData, spawnedIdx, paramIdx, waveIdx);
|
|
}
|
|
}
|
|
private void LimitRandomGlobalSpawnPositions(ChapterBattleStageDesc.Monster groupData, int smallSpawnedIdx, int paramIdx, int waveIdx)
|
|
{
|
|
MonsterData monsterData;
|
|
// todo : 规则看只能有一个
|
|
monsterData.unitId = groupData.List[0];
|
|
monsterData.pushRadius = groupData.Range[1];
|
|
Fixed64Vector2 center = new Fixed64Vector2(groupData.Position[0], groupData.Position[1]);
|
|
Fixed64Vector2 localPos = Fixed64Vector2.zero;
|
|
var singleEntity = Contexts.Combat.LocalPlayer;
|
|
if (singleEntity.IsValid())
|
|
localPos = singleEntity.logicTransform.position;
|
|
if (groupData.PositionType == ChapterBattleStagePositionType.RandomLimitAround)
|
|
center += localPos;
|
|
|
|
monsterData.srcPos = center;
|
|
Vector2 angle = new Vector2(groupData.LocalSize[0], groupData.LocalSize[1]);
|
|
if (angle.x > angle.y)
|
|
angle.y += 360;
|
|
|
|
Fixed64 r = RandomSrv.Range(groupData.Range[1], groupData.Range[0]);
|
|
Fixed64 r2 = groupData.Range[2];
|
|
var randRange = RandomSrv.Range(angle.x, angle.y) * Mathf.Deg2Rad;
|
|
var x = center.x + r * Mathf.Cos(randRange);
|
|
var y = center.y + r * Mathf.Sin(randRange);
|
|
Fixed64Vector2 target = new(x, y);
|
|
Fixed64Vector2 dir = target - center;
|
|
Fixed64 angleDir = FixedMath.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
|
|
var smallAngle = groupData.Range[2] * 0.5f;
|
|
Fixed64Vector2 angle2 = new Fixed64Vector2(angleDir - smallAngle, angleDir + smallAngle);
|
|
if (angle2.x > angle2.y)
|
|
angle2.y += 360;
|
|
|
|
for (int i = groupData.Num[smallSpawnedIdx] - 1; i >= 0; i--)
|
|
{
|
|
var randRange2 = RandomSrv.Range(angle2.x, angle2.y) * Mathf.Deg2Rad;
|
|
var randR2 = RandomSrv.Range(0, r2);
|
|
x = target.x + randR2 * FixedMath.Cos(randRange2);
|
|
y = target.y + randR2 * FixedMath.Sin(randRange2);
|
|
monsterData.position = new Fixed64Vector2(x, y);
|
|
SpawnOne(paramIdx, waveIdx, ref monsterData);
|
|
}
|
|
}
|
|
private void RandomGlobalSpawnPositions(ChapterBattleStageDesc.Monster groupData, int smallSpawnedIdx, int paramIdx, int waveIdx)
|
|
{
|
|
MonsterData monsterData;
|
|
// todo : 规则看只能有一个
|
|
monsterData.unitId = groupData.List[0];
|
|
monsterData.pushRadius = groupData.Range[1];
|
|
Fixed64Vector2 center = new Fixed64Vector2(groupData.Position[0], groupData.Position[1]);
|
|
if (groupData.PositionType == ChapterBattleStagePositionType.RandomAround)
|
|
{
|
|
var singleEntity = Contexts.Combat.LocalPlayer;
|
|
if (singleEntity.IsValid())
|
|
center += singleEntity.logicTransform.position;
|
|
}
|
|
monsterData.srcPos = center;
|
|
|
|
Fixed64Vector2 angle = new Fixed64Vector2(groupData.LocalSize[0], groupData.LocalSize[1]);
|
|
if (angle.x > angle.y)
|
|
angle.y += 360;
|
|
|
|
Fixed64 r = RandomSrv.Range(groupData.Range[1], groupData.Range[0]);
|
|
for (int i = groupData.Num[smallSpawnedIdx] - 1; i >= 0; i--)
|
|
{
|
|
var deg2Rad = RandomSrv.Range(angle.x, angle.y) * Mathf.Deg2Rad;
|
|
var x = center.x + r * FixedMath.Cos(deg2Rad);
|
|
var y = center.y + r * FixedMath.Sin(deg2Rad);
|
|
monsterData.position = new Fixed64Vector2(x, y);
|
|
SpawnOne(paramIdx, waveIdx, ref monsterData);
|
|
}
|
|
}
|
|
private void FixedSpawnMonster(ChapterBattleStageDesc.Monster groupData, int smallSpawnedIdx, int paramIdx,
|
|
int waveIdx)
|
|
{
|
|
MonsterData monsterData = default;
|
|
monsterData.unitId = groupData.List[smallSpawnedIdx];
|
|
if (groupData.Range.Length > 0)
|
|
monsterData.pushRadius = groupData.Range[0];
|
|
int arrayLeftBound = 0;
|
|
// stupid, but enough
|
|
for (int i = 0; i < smallSpawnedIdx; i++)
|
|
arrayLeftBound += groupData.LocalSize[i];
|
|
arrayLeftBound *= 2;
|
|
|
|
// 从这些位置中随机抓取
|
|
var spanSize = groupData.LocalSize[smallSpawnedIdx];
|
|
Span<int> posFilter = stackalloc int[spanSize];
|
|
// 随机移除,碰撞不管
|
|
for (int ind = groupData.Num[smallSpawnedIdx]; ind < spanSize; ind++)
|
|
{
|
|
posFilter[RandomSrv.Range(0, spanSize)] = 1;
|
|
}
|
|
int numCnt = 0;
|
|
var offset = Fixed64Vector2.zero;
|
|
if (groupData.PositionType == ChapterBattleStagePositionType.FixedAround)
|
|
{
|
|
var singleEntity = Contexts.Combat.LocalPlayer;
|
|
offset = singleEntity.logicTransform.position;
|
|
}
|
|
monsterData.srcPos = offset;
|
|
|
|
if (groupData.Local.Length < arrayLeftBound + groupData.Num[smallSpawnedIdx] * 2)
|
|
{
|
|
XLog.LogWarning($"位置数据不足: {waveIdx} , ind {smallSpawnedIdx}");
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < spanSize; i++)
|
|
{
|
|
if (posFilter[i] != 1)
|
|
{
|
|
monsterData.position.x = groupData.Local[arrayLeftBound];
|
|
monsterData.position.y = groupData.Local[arrayLeftBound + 1];
|
|
monsterData.position += offset;
|
|
SpawnOne(paramIdx, waveIdx, ref monsterData);
|
|
numCnt++;
|
|
if (numCnt >= groupData.Num[smallSpawnedIdx])
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
arrayLeftBound += 2;
|
|
}
|
|
}
|
|
|
|
|
|
private void RandomSpawnMonster(ChapterBattleStageDesc.Monster groupData, int smallSpawnedIdx, int paramIdx,
|
|
int waveIdx)
|
|
{
|
|
MonsterData monsterData = default;
|
|
monsterData.unitId = groupData.List[smallSpawnedIdx];
|
|
if (groupData.Range.Length > 0)
|
|
monsterData.pushRadius = groupData.Range[0];
|
|
|
|
monsterData.srcPos = new Fixed64Vector2(groupData.Position[0], groupData.Position[1]);
|
|
for (int i = 0; i < groupData.Num[smallSpawnedIdx]; i++)
|
|
{
|
|
if (monsterData.unitId != 0)
|
|
{
|
|
monsterData.position = RandomSrv.insideUnitCircle * groupData.Range[0];
|
|
monsterData.position.x += groupData.Position[0];
|
|
monsterData.position.y += groupData.Position[1];
|
|
if (groupData.PositionType == ChapterBattleStagePositionType.Around)
|
|
{
|
|
var singleEntity = Contexts.Combat.LocalPlayer;
|
|
if (singleEntity != null)
|
|
{
|
|
monsterData.position += singleEntity.logicTransform.position;
|
|
}
|
|
}
|
|
|
|
SpawnOne(paramIdx, waveIdx, ref monsterData);
|
|
}
|
|
else
|
|
{
|
|
BattleLogger.LogInfo($"SpawnSingle: {paramIdx} monsterId is 0");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void SpawnOne(int paramIdx, int waveIdx, ref MonsterData monsterData)
|
|
{
|
|
var monsterEntity = EntityCreateSrv.BronMonsterEntity(Contexts.Combat, null, ref monsterData);
|
|
if (monsterEntity == null)
|
|
{
|
|
XLog.LogWarning($"SpawnSingle: {paramIdx} , monsterId {monsterData.unitId}");
|
|
return;
|
|
}
|
|
|
|
XLog.LogDebug(
|
|
$"SpawnSingle: {paramIdx} , monsterId {monsterData.unitId} pos {monsterData.position} eid {monsterEntity}");
|
|
|
|
monsterEntity.blackboard.largeWaveID = waveIdx;
|
|
m_LevelWave.AddLifeEnt(monsterEntity.creationIndex, paramIdx);
|
|
}
|
|
}
|
|
}
|