using System; using Entitas; using Sog; using UnityEngine; using xFrame; namespace CoreGame.Render { /// /// 大杂烩,新老规则在一起的,不要动;动的话要注意 掩码的概念 /// 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.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.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 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); } } }