public override void Start(Level level) { Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos); bool isClient = IsClient; if (!string.IsNullOrEmpty(monsterFile)) { for (int i = 0; i < monsterCount; i++) { monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false)); } } foreach (var monster in monsterFiles) { for (int i = 0; i < monster.Item2; i++) { monsters.Add(Character.Create(monster.Item1, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false)); } } monsters.ForEach(m => m.Enabled = false); SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); sonarPositions.Add(spawnPos); }
private void InitializeMonsters(IEnumerable <Character> monsters) { foreach (var monster in monsters) { monster.Enabled = false; if (monster.Params.AI.EnforceAggressiveBehaviorForMissions) { foreach (var targetParam in monster.Params.AI.Targets) { switch (targetParam.State) { case AIState.Avoid: case AIState.Escape: case AIState.Flee: case AIState.PassiveAggressive: targetParam.State = AIState.Attack; break; } } } } SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); foreach (Character monster in monsters) { tempSonarPositions.Add(monster.WorldPosition + Rand.Vector(maxSonarMarkerDistance)); } if (monsters.Count() != tempSonarPositions.Count) { throw new Exception($"monsters.Count != tempSonarPositions.Count ({monsters.Count()} != {tempSonarPositions.Count})"); } }
public override void Start(Level level) { Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos); bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient; for (int i = 0; i < monsterCount; i++) { monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false)); } monsters.ForEach(m => m.Enabled = false); SwarmBehavior.CreateSwarm(monsters.Cast <AICharacter>()); sonarPositions.Add(spawnPos); }
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(); } }
protected void Apply(float deltaTime, Entity entity, List <ISerializableEntity> targets, Vector2?worldPosition = null) { if (lifeTime > 0) { lifeTimer -= deltaTime; if (lifeTimer <= 0) { return; } } Hull hull = null; if (entity is Character) { hull = ((Character)entity).AnimController.CurrentHull; } else if (entity is Item) { hull = ((Item)entity).CurrentHull; } Vector2 position = worldPosition ?? entity.WorldPosition; if (targetLimb != LimbType.None) { if (entity is Character c) { Limb limb = c.AnimController.GetLimb(targetLimb); if (limb != null) { position = limb.WorldPosition; } } } foreach (ISerializableEntity serializableEntity in targets) { if (!(serializableEntity is Item item)) { continue; } Character targetCharacter = targets.FirstOrDefault(t => t is Character character && !character.Removed) as Character; if (targetCharacter == null) { foreach (var target in targets) { if (target is Limb limb && limb.character != null && !limb.character.Removed) { targetCharacter = ((Limb)target).character; } } } for (int i = 0; i < useItemCount; i++) { if (item.Removed) { continue; } item.Use(deltaTime, targetCharacter, targets.FirstOrDefault(t => t is Limb) as Limb); } } if (removeItem) { foreach (var target in targets) { if (target is Item item) { Entity.Spawner?.AddToRemoveQueue(item); } } } if (removeCharacter) { foreach (var target in targets) { if (target is Character character) { Entity.Spawner?.AddToRemoveQueue(character); } } } if (duration > 0.0f) { DurationListElement element = new DurationListElement { Parent = this, Timer = duration, Entity = entity, Targets = targets, User = user }; DurationList.Add(element); } else { foreach (ISerializableEntity target in targets) { if (target is Entity targetEntity) { if (targetEntity.Removed) { continue; } } for (int i = 0; i < propertyNames.Length; i++) { if (target == null || target.SerializableProperties == null || !target.SerializableProperties.TryGetValue(propertyNames[i], out SerializableProperty property)) { continue; } ApplyToProperty(target, property, propertyEffects[i], deltaTime); } } } if (explosion != null && entity != null) { explosion.Explode(position, damageSource: entity, attacker: user); } foreach (ISerializableEntity target in targets) { foreach (Affliction affliction in Afflictions) { Affliction multipliedAffliction = affliction; if (!disableDeltaTime) { multipliedAffliction = affliction.CreateMultiplied(deltaTime); } if (target is Character character) { if (character.Removed) { continue; } character.LastDamageSource = entity; foreach (Limb limb in character.AnimController.Limbs) { limb.character.DamageLimb(position, limb, new List <Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source); limb.character.TrySeverLimbJoints(limb, SeverLimbsProbability); //only apply non-limb-specific afflictions to the first limb if (!affliction.Prefab.LimbSpecific) { break; } } } else if (target is Limb limb) { if (limb.character.Removed) { continue; } limb.character.DamageLimb(position, limb, new List <Affliction>() { multipliedAffliction }, stun: 0.0f, playSound: false, attackImpulse: 0.0f, attacker: affliction.Source); limb.character.TrySeverLimbJoints(limb, SeverLimbsProbability); } } foreach (Pair <string, float> reduceAffliction in ReduceAffliction) { float reduceAmount = disableDeltaTime ? reduceAffliction.Second : reduceAffliction.Second * deltaTime; Limb targetLimb = null; Character targetCharacter = null; if (target is Character character) { targetCharacter = character; } else if (target is Limb limb) { targetLimb = limb; targetCharacter = limb.character; } if (targetCharacter != null && !targetCharacter.Removed) { float prevVitality = targetCharacter.Vitality; targetCharacter.CharacterHealth.ReduceAffliction(targetLimb, reduceAffliction.First, reduceAmount); #if SERVER GameMain.Server.KarmaManager.OnCharacterHealthChanged(targetCharacter, user, prevVitality - targetCharacter.Vitality); #endif } } } if (FireSize > 0.0f && entity != null) { var fire = new FireSource(position, hull); fire.Size = new Vector2(FireSize, fire.Size.Y); } bool isNotClient = GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient; if (isNotClient && entity != null && Entity.Spawner != null) //clients are not allowed to spawn entities { foreach (CharacterSpawnInfo characterSpawnInfo in spawnCharacters) { var characters = new List <Character>(); for (int i = 0; i < characterSpawnInfo.Count; i++) { Entity.Spawner.AddToSpawnQueue(characterSpawnInfo.SpeciesName, position + Rand.Vector(characterSpawnInfo.Spread, Rand.RandSync.Server), onSpawn: newCharacter => { characters.Add(newCharacter); if (characters.Count == characterSpawnInfo.Count) { SwarmBehavior.CreateSwarm(characters.Cast <AICharacter>()); } }); } } foreach (ItemSpawnInfo itemSpawnInfo in spawnItems) { switch (itemSpawnInfo.SpawnPosition) { case ItemSpawnInfo.SpawnPositionType.This: Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, position); break; case ItemSpawnInfo.SpawnPositionType.ThisInventory: { if (entity is Character character) { if (character.Inventory != null && character.Inventory.Items.Any(it => it == null)) { Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, character.Inventory); } } else if (entity is Item item) { var inventory = item?.GetComponent <ItemContainer>()?.Inventory; if (inventory != null && inventory.Items.Any(it => it == null)) { Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, inventory); } } } break; case ItemSpawnInfo.SpawnPositionType.ContainedInventory: { Inventory thisInventory = null; if (entity is Character character) { thisInventory = character.Inventory; } else if (entity is Item item) { thisInventory = item?.GetComponent <ItemContainer>()?.Inventory; } if (thisInventory != null) { foreach (Item item in thisInventory.Items) { if (item == null) { continue; } Inventory containedInventory = item.GetComponent <ItemContainer>()?.Inventory; if (containedInventory == null || !containedInventory.Items.Any(i => i == null)) { continue; } Entity.Spawner.AddToSpawnQueue(itemSpawnInfo.ItemPrefab, containedInventory); break; } } } break; } } } ApplyProjSpecific(deltaTime, entity, targets, hull, position); }
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.Invoke(() => { //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(); } }