public void AddObjective <T>(T objective, float delay, Action callback = null) where T : AIObjective { if (objective == null) { #if DEBUG DebugConsole.ThrowError($"{character.Name}: Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace.CleanupStackTrace()); #endif return; } if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine)) { CoroutineManager.StopCoroutines(coroutine); DelayedObjectives.Remove(objective); } coroutine = CoroutineManager.InvokeAfter(() => { //round ended before the coroutine finished #if CLIENT if (GameMain.GameSession == null || Level.Loaded == null && !(GameMain.GameSession.GameMode is TestGameMode)) { return; } #else if (GameMain.GameSession == null || Level.Loaded == null) { return; } #endif DelayedObjectives.Remove(objective); AddObjective(objective); callback?.Invoke(); }, delay); DelayedObjectives.Add(objective, coroutine); }
public override void Update(float deltaTime) { if (IsClient) { return; } if (!swarmSpawned && level.CheckBeaconActive()) { State = 1; Vector2 spawnPos = level.BeaconStation.WorldPosition; spawnPos.Y += level.BeaconStation.GetDockedBorders().Height * 1.5f; var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p => p.PositionType == Level.PositionType.MainPath || p.PositionType == Level.PositionType.SidePath); availablePositions.RemoveAll(p => Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(p.Position.ToVector2()))); availablePositions.RemoveAll(p => Submarine.FindContaining(p.Position.ToVector2()) != null); if (availablePositions.Any()) { Level.InterestingPosition?closestPos = null; float closestDist = float.PositiveInfinity; foreach (var pos in availablePositions) { float dist = Vector2.DistanceSquared(pos.Position.ToVector2(), level.BeaconStation.WorldPosition); if (dist < closestDist) { closestDist = dist; closestPos = pos; } } if (closestPos.HasValue) { spawnPos = closestPos.Value.Position.ToVector2(); } } int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1); for (int i = 0; i < amount; i++) { CoroutineManager.InvokeAfter(() => { //round ended before the coroutine finished if (GameMain.GameSession == null || Level.Loaded == null) { return; } Entity.Spawner.AddToSpawnQueue(monsterSpeciesName, spawnPos); }, Rand.Range(0f, amount)); } swarmSpawned = true; } }
public void AddObjective(AIObjective objective, float delay, Action callback = null) { if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine)) { CoroutineManager.StopCoroutines(coroutine); DelayedObjectives.Remove(objective); } coroutine = CoroutineManager.InvokeAfter(() => { DelayedObjectives.Remove(objective); AddObjective(objective); callback?.Invoke(); }, delay); DelayedObjectives.Add(objective, coroutine); }
public void AddObjective(AIObjective objective, float delay, Action callback = null) { if (objective == null) { #if DEBUG DebugConsole.ThrowError("Attempted to add a null objective to AIObjectiveManager\n" + Environment.StackTrace); #endif return; } if (DelayedObjectives.TryGetValue(objective, out CoroutineHandle coroutine)) { CoroutineManager.StopCoroutines(coroutine); DelayedObjectives.Remove(objective); } coroutine = CoroutineManager.InvokeAfter(() => { DelayedObjectives.Remove(objective); AddObjective(objective); callback?.Invoke(); }, delay); DelayedObjectives.Add(objective, coroutine); }
public override void Update(float deltaTime) { if (disallowed) { Finished(); return; } if (isFinished) { return; } if (spawnPos == null) { FindSpawnPosition(affectSubImmediately: true); spawnPending = true; } bool spawnReady = false; if (spawnPending) { //if spawning in a ruin/cave, wait for someone to be close to it to spawning //unnecessary monsters in places the players might never visit during the round if (spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Cave || spawnPosType == Level.PositionType.Wreck) { bool someoneNearby = false; float minDist = Sonar.DefaultSonarRange * 0.8f; foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; } if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { someoneNearby = true; break; } } foreach (Character c in Character.CharacterList) { if (c == Character.Controlled || c.IsRemotePlayer) { if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist) { someoneNearby = true; break; } } } if (!someoneNearby) { return; } } else { //wait until there are no submarines at the spawnpos foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; } float minDist = GetMinDistanceToSub(submarine); if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { return; } } } spawnPending = false; //+1 because Range returns an integer less than the max value int amount = Rand.Range(minAmount, maxAmount + 1); monsters = new List <Character>(); float offsetAmount = spawnPosType == Level.PositionType.MainPath ? scatter : 100; for (int i = 0; i < amount; i++) { CoroutineManager.InvokeAfter(() => { //round ended before the coroutine finished if (GameMain.GameSession == null || Level.Loaded == null) { return; } System.Diagnostics.Debug.Assert(GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer, "Clients should not create monster events."); Vector2 pos = spawnPos.Value + Rand.Vector(offsetAmount); if (spawnPosType == Level.PositionType.MainPath) { if (Submarine.Loaded.Any(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(pos))) { // Can't use the offset position, let's use the exact spawn position. pos = spawnPos.Value; } else if (Level.Loaded.Ruins.Any(r => ToolBox.GetWorldBounds(r.Area.Center, r.Area.Size).ContainsWorld(pos))) { // Can't use the offset position, let's use the exact spawn position. pos = spawnPos.Value; } } monsters.Add(Character.Create(speciesName, pos, Level.Loaded.Seed + i.ToString(), null, false, true, true)); if (monsters.Count == amount) { spawnReady = true; //this will do nothing if the monsters have no swarm behavior defined, //otherwise it'll make the spawned characters act as a swarm SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); } }, Rand.Range(0f, amount / 2)); } } if (!spawnReady) { return; } Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter); #if CLIENT if (Character.Controlled != null) { targetEntity = Character.Controlled; } #endif bool monstersDead = true; foreach (Character monster in monsters) { if (!monster.IsDead) { monstersDead = false; if (targetEntity != null && Vector2.DistanceSquared(monster.WorldPosition, targetEntity.WorldPosition) < 5000.0f * 5000.0f) { break; } } } if (monstersDead) { Finished(); } }
public override void Update(float deltaTime) { if (disallowed) { Finished(); return; } if (isFinished) { return; } //isActive = false; bool spawnReady = false; if (spawnPending) { //wait until there are no submarines at the spawnpos foreach (Submarine submarine in Submarine.Loaded) { if (submarine.IsOutpost) { continue; } float minDist = GetMinDistanceToSub(submarine); if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos) < minDist * minDist) { return; } } spawnPending = false; //+1 because Range returns an integer less than the max value int amount = Rand.Range(minAmount, maxAmount + 1, Rand.RandSync.Server); monsters = new List <Character>(); float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100; for (int i = 0; i < amount; i++) { CoroutineManager.InvokeAfter(() => { bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true)); if (monsters.Count == amount) { spawnReady = true; //this will do nothing if the monsters have no swarm behavior defined, //otherwise it'll make the spawned characters act as a swarm SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); } }, Rand.Range(0f, amount / 2, Rand.RandSync.Server)); } } if (!spawnReady) { return; } Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter); #if CLIENT if (Character.Controlled != null) { targetEntity = (Entity)Character.Controlled; } #endif bool monstersDead = true; foreach (Character monster in monsters) { if (!monster.IsDead) { monstersDead = false; if (targetEntity != null && Vector2.DistanceSquared(monster.WorldPosition, targetEntity.WorldPosition) < 5000.0f * 5000.0f) { break; } } } if (monstersDead) { Finished(); } }
public override void Update(float deltaTime) { if (disallowed) { Finished(); return; } if (isFinished) { return; } if (spawnPos == null) { if (maxAmountPerLevel < int.MaxValue) { if (Character.CharacterList.Count(c => c.SpeciesName == speciesName) >= maxAmountPerLevel) { disallowed = true; return; } } FindSpawnPosition(affectSubImmediately: true); //the event gets marked as finished if a spawn point is not found if (isFinished) { return; } spawnPending = true; } bool spawnReady = false; if (spawnPending) { //wait until there are no submarines at the spawnpos if (spawnPosType.HasFlag(Level.PositionType.MainPath) || spawnPosType.HasFlag(Level.PositionType.SidePath) || spawnPosType.HasFlag(Level.PositionType.Abyss)) { foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineType.Player) { continue; } float minDist = GetMinDistanceToSub(submarine); if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { return; } } } //if spawning in a ruin/cave, wait for someone to be close to it to spawning //unnecessary monsters in places the players might never visit during the round if (spawnPosType.HasFlag(Level.PositionType.Ruin) || spawnPosType.HasFlag(Level.PositionType.Cave) || spawnPosType.HasFlag(Level.PositionType.Wreck)) { bool someoneNearby = false; float minDist = Sonar.DefaultSonarRange * 0.8f; foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineType.Player) { continue; } if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { someoneNearby = true; break; } } foreach (Character c in Character.CharacterList) { if (c == Character.Controlled || c.IsRemotePlayer) { if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist) { someoneNearby = true; break; } } } if (!someoneNearby) { return; } } if (spawnPosType.HasFlag(Level.PositionType.Abyss) || spawnPosType.HasFlag(Level.PositionType.AbyssCave)) { bool anyInAbyss = false; foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineType.Player || submarine == GameMain.NetworkMember?.RespawnManager?.RespawnShuttle) { continue; } if (submarine.WorldPosition.Y < 0) { anyInAbyss = true; break; } } if (!anyInAbyss) { return; } } spawnPending = false; //+1 because Range returns an integer less than the max value int amount = Rand.Range(minAmount, maxAmount + 1); monsters = new List <Character>(); float scatterAmount = scatter; if (spawnPosType.HasFlag(Level.PositionType.SidePath)) { var sidePaths = Level.Loaded.Tunnels.Where(t => t.Type == Level.TunnelType.SidePath); if (sidePaths.Any()) { scatterAmount = Math.Min(scatter, sidePaths.Min(t => t.MinWidth) / 2); } else { scatterAmount = scatter; } } else if (!spawnPosType.HasFlag(Level.PositionType.MainPath)) { scatterAmount = 0; } for (int i = 0; i < amount; i++) { string seed = Level.Loaded.Seed + i.ToString(); CoroutineManager.InvokeAfter(() => { //round ended before the coroutine finished if (GameMain.GameSession == null || Level.Loaded == null) { return; } System.Diagnostics.Debug.Assert(GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer, "Clients should not create monster events."); Vector2 pos = spawnPos.Value + Rand.Vector(scatterAmount); if (scatterAmount > 0) { if (Submarine.Loaded.Any(s => ToolBox.GetWorldBounds(s.Borders.Center, s.Borders.Size).ContainsWorld(pos))) { // Can't use the offset position, let's use the exact spawn position. pos = spawnPos.Value; } else if (Level.Loaded.Ruins.Any(r => ToolBox.GetWorldBounds(r.Area.Center, r.Area.Size).ContainsWorld(pos))) { // Can't use the offset position, let's use the exact spawn position. pos = spawnPos.Value; } } Character createdCharacter = Character.Create(speciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true); if (GameMain.GameSession.IsCurrentLocationRadiated()) { AfflictionPrefab radiationPrefab = AfflictionPrefab.RadiationSickness; Affliction affliction = new Affliction(radiationPrefab, radiationPrefab.MaxStrength); createdCharacter?.CharacterHealth.ApplyAffliction(null, affliction); // TODO test multiplayer createdCharacter?.Kill(CauseOfDeathType.Affliction, affliction, log: false); } monsters.Add(createdCharacter); if (monsters.Count == amount) { spawnReady = true; //this will do nothing if the monsters have no swarm behavior defined, //otherwise it'll make the spawned characters act as a swarm SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); } }, Rand.Range(0f, amount / 2f)); } } if (!spawnReady) { return; } Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter); #if CLIENT if (Character.Controlled != null) { targetEntity = Character.Controlled; } #endif bool monstersDead = true; foreach (Character monster in monsters) { if (!monster.IsDead) { monstersDead = false; if (targetEntity != null && Vector2.DistanceSquared(monster.WorldPosition, targetEntity.WorldPosition) < 5000.0f * 5000.0f) { break; } } } if (monstersDead) { Finished(); } }