private void UpdateEating(float deltaTime) { if (selectedAiTarget == null || selectedAiTarget.Entity == null || selectedAiTarget.Entity.Removed) { state = AIState.None; return; } Limb mouthLimb = Array.Find(Character.AnimController.Limbs, l => l != null && l.MouthPos.HasValue); if (mouthLimb == null) { mouthLimb = Character.AnimController.GetLimb(LimbType.Head); } if (mouthLimb == null) { DebugConsole.ThrowError("Character \"" + Character.SpeciesName + "\" failed to eat a target (a head or a limb with a mouthpos required)"); state = AIState.None; return; } Character targetCharacter = selectedAiTarget.Entity as Character; float eatSpeed = Character.Mass / targetCharacter.Mass * 0.1f; eatTimer += deltaTime * eatSpeed; Vector2 mouthPos = mouthLimb.SimPosition; if (mouthLimb.MouthPos.HasValue) { float cos = (float)Math.Cos(mouthLimb.Rotation); float sin = (float)Math.Sin(mouthLimb.Rotation); mouthPos += new Vector2( mouthLimb.MouthPos.Value.X * cos - mouthLimb.MouthPos.Value.Y * sin, mouthLimb.MouthPos.Value.X * sin + mouthLimb.MouthPos.Value.Y * cos); } Vector2 attackSimPosition = Character.Submarine == null?ConvertUnits.ToSimUnits(selectedAiTarget.WorldPosition) : selectedAiTarget.SimPosition; Vector2 limbDiff = attackSimPosition - mouthPos; float limbDist = limbDiff.Length(); if (limbDist < 1.0f) { //pull the target character to the position of the mouth //(+ make the force fluctuate to waggle the character a bit) targetCharacter.AnimController.MainLimb.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); targetCharacter.AnimController.MainLimb.body.SmoothRotate(mouthLimb.Rotation); targetCharacter.AnimController.Collider.MoveToPos(mouthPos, (float)(Math.Sin(eatTimer) + 10.0f)); //pull the character's mouth to the target character (again with a fluctuating force) float pullStrength = (float)(Math.Sin(eatTimer) * Math.Max(Math.Sin(eatTimer * 0.5f), 0.0f)); steeringManager.SteeringManual(deltaTime, limbDiff * pullStrength); mouthLimb.body.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f * pullStrength); //GameMain.NilMod.CreatureHealthGainEatingPercent; if (eatTimer % 1.0f < 0.5f && (eatTimer - deltaTime * eatSpeed) % 1.0f > 0.5f) { //Kill living players that control the character if being actively consumed. //Before this point is reached a fish could be prevented with stun from severing limbs and thus still save a player. if (!targetCharacter.IsDead && targetCharacter.IsRemotePlayer) { targetCharacter.Kill(CauseOfDeath.Damage, true); } //apply damage to the target character to get some blood particles flying targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); //Lets make this extra bloody, perhaps a client will sync it. targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); targetCharacter.AnimController.MainLimb.AddDamage(targetCharacter.SimPosition, DamageType.None, Rand.Range(10.0f, 25.0f), Rand.Range(15.0f, 40.0f), false); //keep severing joints until there is only one limb left LimbJoint[] nonSeveredJoints = Array.FindAll(targetCharacter.AnimController.LimbJoints, l => !l.IsSevered && l.CanBeSevered); if (nonSeveredJoints.Length == 0) { //only one limb left, the character is now full eaten if (GameMain.Server != null) { GameMain.Server.ServerLog.WriteLine(Character.Name + " has finished eating " + targetCharacter.Name, Networking.ServerLog.MessageType.Spawns); } Entity.Spawner.AddToRemoveQueue(targetCharacter); selectedAiTarget = null; state = AIState.None; } else //sever a random joint { targetCharacter.AnimController.SeverLimbJoint(nonSeveredJoints[Rand.Int(nonSeveredJoints.Length)]); if (GameMain.Server != null) { GameMain.Server.CreateEntityEvent(targetCharacter, new object[] { Barotrauma.Networking.NetEntityEvent.Type.Status }); } } } } else if (limbDist < 2.0f) { steeringManager.SteeringManual(deltaTime, limbDiff); Character.AnimController.Collider.ApplyForce(limbDiff * mouthLimb.Mass * 50.0f, mouthPos); } else { steeringManager.SteeringSeek(attackSimPosition - (mouthPos - SimPosition), 3); } }
public void RunIngameCommand(string Command, Object[] Arguments) { Vector2 WorldCoordinate = new Vector2(0, 0); Character character = null; //Server Commands if (GameMain.Server != null) { switch (Command) { case "spawncreature": //ARG0 = Character, ARG1 = WorldPosX, ARG2 = WorldPosY WorldCoordinate = new Vector2(float.Parse(Arguments[1].ToString()), float.Parse(Arguments[2].ToString())); if (Arguments[0].ToString().ToLowerInvariant() == "human") { character = Character.Create(Character.HumanConfigFile, WorldCoordinate); } else { System.Collections.Generic.List <string> characterFiles = GameMain.Config.SelectedContentPackage.GetFilesOfType(ContentType.Character); foreach (string characterFile in characterFiles) { if (System.IO.Path.GetFileNameWithoutExtension(characterFile).ToLowerInvariant() == Arguments[0].ToString().ToLowerInvariant()) { character = Character.Create( characterFile, WorldCoordinate); } } } break; case "control": //ARG0 = client ARG1 = Character if ((Barotrauma.Networking.Client)Arguments[0] != null) { GameMain.Server.SetClientCharacter((Barotrauma.Networking.Client)Arguments[0], null); } character = (Character)Arguments[1]; if (character != null) { Character.Controlled = character; } break; case "shield": //ARG0 = Character, ARG1 = shield state character = (Character)Arguments[0]; character.Shielded = (Boolean)Arguments[1]; if (GameMain.Server != null) { GameMain.NetworkMember.AddChatMessage( ((Boolean)Arguments[1] ? character.LogName + " is now shielded from health/oxygen/pressure." : character.LogName + " is no longer shielded.") , Barotrauma.Networking.ChatMessageType.Private); } break; case "heal": //ARG0 = Character character = (Character)Arguments[0]; character.Heal(); break; case "revive": //ARG0 = Character character = (Character)Arguments[0]; character.Revive(true); break; case "kill": //ARG0 = Character character = (Character)Arguments[0]; character.Kill(CauseOfDeath.Disconnected, true); break; case "removecorpse": //ARG0 = Character character = (Character)Arguments[0]; Entity.Spawner.AddToRemoveQueue(character); break; case "teleportsub": //ARG0 = SUBID, ARG1 = WorldPosX, ARG2 = WorldPosY WorldCoordinate = new Vector2(float.Parse(Arguments[1].ToString()), float.Parse(Arguments[2].ToString())); GameMain.Server.MoveSub(int.Parse(Arguments[0].ToString()), WorldCoordinate); break; case "relocate": //ARG0 = Character, ARG1 = WorldPosX, ARG2 = WorldPosY character = (Character)Arguments[0]; if (!character.Enabled) { character.Enabled = true; } WorldCoordinate = new Vector2(float.Parse(Arguments[1].ToString()), float.Parse(Arguments[2].ToString())); character.AnimController.CurrentHull = null; character.Submarine = null; character.AnimController.SetPosition(FarseerPhysics.ConvertUnits.ToSimUnits(WorldCoordinate)); character.AnimController.FindHull(WorldCoordinate, true); break; //case "handcuff": //ARG0 = Character //break; case "freeze": //ARG0 = Character character = (Character)Arguments[0]; if (GameMain.NilMod.FrozenCharacters.Find(c => c == character) != null) { GameMain.NilMod.FrozenCharacters.Remove(character); if (GameMain.Server.ConnectedClients.Find(c => c.Character == character) != null) { var chatMsg = Barotrauma.Networking.ChatMessage.Create( "Server Message", ("You have been frozen by the server\n\nYou may now move again and perform actions."), (Barotrauma.Networking.ChatMessageType)Barotrauma.Networking.ChatMessageType.MessageBox, null); GameMain.Server.SendChatMessage(chatMsg, GameMain.Server.ConnectedClients.Find(c => c.Character == character)); } } else { GameMain.NilMod.FrozenCharacters.Add(character); if (GameMain.Server.ConnectedClients.Find(c => c.Character == character) != null) { var chatMsg = Barotrauma.Networking.ChatMessage.Create( "Server Message", ("You have been frozen by the server\n\nYou may still talk if able, but no longer perform any actions or movements."), (Barotrauma.Networking.ChatMessageType)Barotrauma.Networking.ChatMessageType.MessageBox, null); GameMain.Server.SendChatMessage(chatMsg, GameMain.Server.ConnectedClients.Find(c => c.Character == character)); } } break; case "setclientcharacter": //ARG0 = Client, ARG1 = Character GameMain.Server.SetClientCharacter((Barotrauma.Networking.Client)Arguments[0], (Character)Arguments[1]); break; default: DebugConsole.ThrowError(@"NILMOD Error: Unrecognized Command Execution: """ + Command + @""""); break; } } }
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 == Level.PositionType.MainPath || spawnPosType == Level.PositionType.SidePath || spawnPosType == 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 == 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 != 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 == Level.PositionType.Abyss || spawnPosType == Level.PositionType.AbyssCave) { foreach (Submarine submarine in Submarine.Loaded) { if (submarine.Info.Type != SubmarineType.Player) { continue; } if (submarine.WorldPosition.Y > 0) { 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 || spawnPosType == Level.PositionType.SidePath ? scatter : 100; 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(offsetAmount); if (spawnPosType == Level.PositionType.MainPath || spawnPosType == Level.PositionType.SidePath) { 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(); } }