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

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);
}
}
}