public static void CreateItems(List <PurchasedItem> itemsToSpawn, Submarine sub) { if (itemsToSpawn.Count == 0) { return; } WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, sub); if (wp == null) { DebugConsole.ThrowError("The submarine must have a waypoint marked as Cargo for bought items to be placed correctly!"); return; } Hull cargoRoom = Hull.FindHull(wp.WorldPosition); if (cargoRoom == null) { DebugConsole.ThrowError("A waypoint marked as Cargo must be placed inside a room!"); return; } if (sub == Submarine.MainSub) { #if CLIENT new GUIMessageBox("", TextManager.GetWithVariable("CargoSpawnNotification", "[roomname]", cargoRoom.DisplayName, true), new string[0], type: GUIMessageBox.Type.InGame, iconStyle: "StoreShoppingCrateIcon"); #else foreach (Client client in GameMain.Server.ConnectedClients) { ChatMessage msg = ChatMessage.Create("", TextManager.ContainsTag(cargoRoom.RoomName) ? $"CargoSpawnNotification~[roomname]=§{cargoRoom.RoomName}" : $"CargoSpawnNotification~[roomname]={cargoRoom.RoomName}", ChatMessageType.ServerMessageBoxInGame, null); msg.IconStyle = "StoreShoppingCrateIcon"; GameMain.Server.SendDirectChatMessage(msg, client); } #endif } List <ItemContainer> availableContainers = new List <ItemContainer>(); ItemPrefab containerPrefab = null; foreach (PurchasedItem pi in itemsToSpawn) { Vector2 position = GetCargoPos(cargoRoom, pi.ItemPrefab); for (int i = 0; i < pi.Quantity; i++) { ItemContainer itemContainer = null; if (!string.IsNullOrEmpty(pi.ItemPrefab.CargoContainerIdentifier)) { itemContainer = availableContainers.Find(ac => ac.Inventory.CanBePut(pi.ItemPrefab) && (ac.Item.Prefab.Identifier == pi.ItemPrefab.CargoContainerIdentifier || ac.Item.Prefab.Tags.Contains(pi.ItemPrefab.CargoContainerIdentifier.ToLowerInvariant()))); if (itemContainer == null) { containerPrefab = ItemPrefab.Prefabs.Find(ep => ep.Identifier == pi.ItemPrefab.CargoContainerIdentifier || (ep.Tags != null && ep.Tags.Contains(pi.ItemPrefab.CargoContainerIdentifier.ToLowerInvariant()))); if (containerPrefab == null) { DebugConsole.ThrowError("Cargo spawning failed - could not find the item prefab for container \"" + pi.ItemPrefab.CargoContainerIdentifier + "\"!"); continue; } Item containerItem = new Item(containerPrefab, position, wp.Submarine); itemContainer = containerItem.GetComponent <ItemContainer>(); if (itemContainer == null) { DebugConsole.ThrowError("Cargo spawning failed - container \"" + containerItem.Name + "\" does not have an ItemContainer component!"); continue; } availableContainers.Add(itemContainer); #if SERVER if (GameMain.Server != null) { Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false); } #endif } } if (itemContainer == null) { //no container, place at the waypoint if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, position, wp.Submarine, onSpawned: itemSpawned); } else { var item = new Item(pi.ItemPrefab, position, wp.Submarine); itemSpawned(item); } continue; } //place in the container if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, itemContainer.Inventory, onSpawned: itemSpawned); } else { var item = new Item(pi.ItemPrefab, position, wp.Submarine); itemContainer.Inventory.TryPutItem(item, null); itemSpawned(item); }
partial void ExplodeProjSpecific(Vector2 worldPosition, Hull hull);
public void Explode(Vector2 worldPosition, Entity damageSource, Character attacker = null) { prevExplosions.Add(new Triplet <Explosion, Vector2, float>(this, worldPosition, (float)Timing.TotalTime)); if (prevExplosions.Count > 100) { prevExplosions.RemoveAt(0); } Hull hull = Hull.FindHull(worldPosition); ExplodeProjSpecific(worldPosition, hull); float displayRange = attack.Range; if (displayRange < 0.1f) { return; } Vector2 cameraPos = Character.Controlled != null ? Character.Controlled.WorldPosition : GameMain.GameScreen.Cam.Position; float cameraDist = Vector2.Distance(cameraPos, worldPosition) / 2.0f; GameMain.GameScreen.Cam.Shake = CameraShake * Math.Max((displayRange - cameraDist) / displayRange, 0.0f); if (attack.GetStructureDamage(1.0f) > 0.0f) { RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f), attacker); } if (empStrength > 0.0f) { float displayRangeSqr = displayRange * displayRange; foreach (Item item in Item.ItemList) { float distSqr = Vector2.DistanceSquared(item.WorldPosition, worldPosition); if (distSqr > displayRangeSqr) { continue; } float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange; //damage repairable power-consuming items var powered = item.GetComponent <Powered>(); if (powered == null || !powered.VulnerableToEMP) { continue; } if (item.Repairables.Any()) { item.Condition -= 100 * empStrength * distFactor; } //discharge batteries var powerContainer = item.GetComponent <PowerContainer>(); if (powerContainer != null) { powerContainer.Charge -= powerContainer.Capacity * empStrength * distFactor; } } } if (MathUtils.NearlyEqual(force, 0.0f) && MathUtils.NearlyEqual(attack.Stun, 0.0f) && MathUtils.NearlyEqual(attack.GetTotalDamage(false), 0.0f)) { return; } DamageCharacters(worldPosition, attack, force, damageSource, attacker); if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) { if (flames) { foreach (Item item in Item.ItemList) { if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f) { continue; } //don't apply OnFire effects if the item is inside a fireproof container //(or if it's inside a container that's inside a fireproof container, etc) Item container = item.Container; bool fireProof = false; while (container != null) { if (container.FireProof) { fireProof = true; break; } container = container.Container; } if (fireProof || Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.5f) { continue; } item.ApplyStatusEffects(ActionType.OnFire, 1.0f); if (item.Condition <= 0.0f && GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer) { GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire }); } if (item.Prefab.DamagedByExplosions && !item.Indestructible) { float limbRadius = item.body == null ? 0.0f : item.body.GetMaxExtent(); float dist = Vector2.Distance(item.WorldPosition, worldPosition); dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius)); if (dist > attack.Range) { continue; } float distFactor = 1.0f - dist / attack.Range; float damageAmount = attack.GetItemDamage(1.0f); item.Condition -= damageAmount * distFactor; } } } } }
void UpdateRoomToRoom(float deltaTime, Hull hull1, Hull hull2) { Vector2 subOffset = Vector2.Zero; if (hull1.Submarine != Submarine) { subOffset = Submarine.Position - hull1.Submarine.Position; } else if (hull2.Submarine != Submarine) { subOffset = hull2.Submarine.Position - Submarine.Position; } if (hull1.WaterVolume <= 0.0 && hull2.WaterVolume <= 0.0) { return; } float size = IsHorizontal ? rect.Height : rect.Width; //a variable affecting the water flow through the gap //the larger the gap is, the faster the water flows float sizeModifier = size / 100.0f * open; //horizontal gap (such as a regular door) if (IsHorizontal) { higherSurface = Math.Max(hull1.Surface, hull2.Surface + subOffset.Y); float delta = 0.0f; //water level is above the lower boundary of the gap if (Math.Max(hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1], hull2.Surface + subOffset.Y + hull2.WaveY[0]) > rect.Y - size) { int dir = (hull1.Pressure > hull2.Pressure + subOffset.Y) ? 1 : -1; //water flowing from the righthand room to the lefthand room if (dir == -1) { if (!(hull2.WaterVolume > 0.0f)) { return; } lowerSurface = hull1.Surface - hull1.WaveY[hull1.WaveY.Length - 1]; //delta = Math.Min((room2.water.pressure - room1.water.pressure) * sizeModifier, Math.Min(room2.water.Volume, room2.Volume)); //delta = Math.Min(delta, room1.Volume - room1.water.Volume + Water.MaxCompress); flowTargetHull = hull1; //make sure not to move more than what the room contains delta = Math.Min(((hull2.Pressure + subOffset.Y) - hull1.Pressure) * 5.0f * sizeModifier, Math.Min(hull2.WaterVolume, hull2.Volume)); //make sure not to place more water to the target room than it can hold delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - (hull1.WaterVolume)); hull1.WaterVolume += delta; hull2.WaterVolume -= delta; if (hull1.WaterVolume > hull1.Volume) { hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + hull2.Pressure + subOffset.Y) / 2); } flowForce = new Vector2(-delta, 0.0f); } else if (dir == 1) { if (!(hull1.WaterVolume > 0.0f)) { return; } lowerSurface = hull2.Surface - hull2.WaveY[hull2.WaveY.Length - 1]; flowTargetHull = hull2; //make sure not to move more than what the room contains delta = Math.Min((hull1.Pressure - (hull2.Pressure + subOffset.Y)) * 5.0f * sizeModifier, Math.Min(hull1.WaterVolume, hull1.Volume)); //make sure not to place more water to the target room than it can hold delta = Math.Min(delta, hull2.Volume * Hull.MaxCompress - (hull2.WaterVolume)); hull1.WaterVolume -= delta; hull2.WaterVolume += delta; if (hull2.WaterVolume > hull2.Volume) { hull2.Pressure = Math.Max(hull2.Pressure, ((hull1.Pressure - subOffset.Y) + hull2.Pressure) / 2); } flowForce = new Vector2(delta, 0.0f); } if (delta > 100.0f && subOffset == Vector2.Zero) { float avg = (hull1.Surface + hull2.Surface) / 2.0f; if (hull1.WaterVolume < hull1.Volume / Hull.MaxCompress && hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1] < rect.Y) { hull1.WaveVel[hull1.WaveY.Length - 1] = (avg - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f; hull1.WaveVel[hull1.WaveY.Length - 2] = hull1.WaveVel[hull1.WaveY.Length - 1]; } if (hull2.WaterVolume < hull2.Volume / Hull.MaxCompress && hull2.Surface + hull2.WaveY[0] < rect.Y) { hull2.WaveVel[0] = (avg - (hull2.Surface + hull2.WaveY[0])) * 0.1f; hull2.WaveVel[1] = hull2.WaveVel[0]; } } } } else { //lower room is full of water if (hull2.Pressure + subOffset.Y > hull1.Pressure && hull2.WaterVolume > 0.0f) { float delta = Math.Min(hull2.WaterVolume - hull2.Volume + (hull2.Volume * Hull.MaxCompress), deltaTime * 8000.0f * sizeModifier); //make sure not to place more water to the target room than it can hold if (hull1.WaterVolume + delta > hull1.Volume * Hull.MaxCompress) { delta -= (hull1.WaterVolume + delta) - (hull1.Volume * Hull.MaxCompress); } delta = Math.Max(delta, 0.0f); hull1.WaterVolume += delta; hull2.WaterVolume -= delta; flowForce = new Vector2( 0.0f, Math.Min(Math.Min((hull2.Pressure + subOffset.Y) - hull1.Pressure, 200.0f), delta)); flowTargetHull = hull1; if (hull1.WaterVolume > hull1.Volume) { hull1.Pressure = Math.Max(hull1.Pressure, (hull1.Pressure + (hull2.Pressure + subOffset.Y)) / 2); } } //there's water in the upper room, drop to lower else if (hull1.WaterVolume > 0) { flowTargetHull = hull2; //make sure the amount of water moved isn't more than what the room contains float delta = Math.Min(hull1.WaterVolume, deltaTime * 25000f * sizeModifier); //make sure not to place more water to the target room than it can hold if (hull2.WaterVolume + delta > hull2.Volume * Hull.MaxCompress) { delta -= (hull2.WaterVolume + delta) - (hull2.Volume * Hull.MaxCompress); } hull1.WaterVolume -= delta; hull2.WaterVolume += delta; flowForce = new Vector2( hull1.WaveY[hull1.GetWaveIndex(rect.X)] - hull1.WaveY[hull1.GetWaveIndex(rect.Right)], MathHelper.Clamp(-delta, -200.0f, 0.0f)); if (hull2.WaterVolume > hull2.Volume) { hull2.Pressure = Math.Max(hull2.Pressure, ((hull1.Pressure - subOffset.Y) + hull2.Pressure) / 2); } } } if (open > 0.0f) { if (hull1.WaterVolume > hull1.Volume / Hull.MaxCompress && hull2.WaterVolume > hull2.Volume / Hull.MaxCompress) { float avgLethality = (hull1.LethalPressure + hull2.LethalPressure) / 2.0f; hull1.LethalPressure = avgLethality; hull2.LethalPressure = avgLethality; } else { hull1.LethalPressure = 0.0f; hull2.LethalPressure = 0.0f; } } }
public DummyFireSource(Vector2 maxSize, Vector2 worldPosition, Hull spawningHull = null, bool isNetworkMessage = false) : base(worldPosition, spawningHull, isNetworkMessage) { this.maxSize = maxSize; }
protected override Vector2 DoSteeringSeek(Vector2 target, float weight) { bool needsNewPath = currentPath != null && currentPath.Unreachable || Vector2.DistanceSquared(target, currentTarget) > 1; //find a new path if one hasn't been found yet or the target is different from the current target if (currentPath == null || needsNewPath || findPathTimer < -1.0f) { IsPathDirty = true; if (findPathTimer > 0.0f) { return(Vector2.Zero); } currentTarget = target; Vector2 pos = host.SimPosition; // TODO: remove this and handle differently? if (character != null && character.Submarine == null) { var targetHull = Hull.FindHull(FarseerPhysics.ConvertUnits.ToDisplayUnits(target), null, false); if (targetHull != null && targetHull.Submarine != null) { pos -= targetHull.Submarine.SimPosition; } } var newPath = pathFinder.FindPath(pos, target, character.Submarine, "(Character: " + character.Name + ")"); bool useNewPath = currentPath == null || needsNewPath; if (!useNewPath && currentPath != null && currentPath.CurrentNode != null && newPath.Nodes.Any() && !newPath.Unreachable) { // It's possible that the current path was calculated from a start point that is no longer valid. // Therefore, let's accept also paths with a greater cost than the current, if the current node is much farther than the new start node. useNewPath = newPath.Cost <currentPath.Cost || Vector2.DistanceSquared(character.WorldPosition, currentPath.CurrentNode.WorldPosition)> Math.Pow(Vector2.Distance(character.WorldPosition, newPath.Nodes.First().WorldPosition) * 2, 2); } if (useNewPath) { currentPath = newPath; } findPathTimer = Rand.Range(1.0f, 1.2f); IsPathDirty = false; return(DiffToCurrentNode()); } Vector2 diff = DiffToCurrentNode(); var collider = character.AnimController.Collider; //if not in water and the waypoint is between the top and bottom of the collider, no need to move vertically if (!character.AnimController.InWater && !character.IsClimbing && diff.Y < collider.height / 2 + collider.radius) { diff.Y = 0.0f; } if (diff.LengthSquared() < 0.001f) { return(-host.Steering); } return(Vector2.Normalize(diff) * weight); }
public void Explode(Vector2 worldPosition) { Hull hull = Hull.FindHull(worldPosition); if (shockwave) { GameMain.ParticleManager.CreateParticle("shockwave", worldPosition, Vector2.Zero, 0.0f, hull); } for (int i = 0; i < attack.Range * 0.1f; i++) { Vector2 bubblePos = Rand.Vector(attack.Range * 0.5f); GameMain.ParticleManager.CreateParticle("bubbles", worldPosition + bubblePos, bubblePos, 0.0f, hull); if (sparks) { GameMain.ParticleManager.CreateParticle("spark", worldPosition, Rand.Vector(Rand.Range(500.0f, 800.0f)), 0.0f, hull); } if (flames) { GameMain.ParticleManager.CreateParticle("explosionfire", ClampParticlePos(worldPosition + Rand.Vector(50f), hull), Rand.Vector(Rand.Range(50.0f, 100.0f)), 0.0f, hull); } if (smoke) { GameMain.ParticleManager.CreateParticle("smoke", ClampParticlePos(worldPosition + Rand.Vector(50f), hull), Rand.Vector(Rand.Range(1.0f, 10.0f)), 0.0f, hull); } } float displayRange = attack.Range; if (displayRange < 0.1f) { return; } var light = new LightSource(worldPosition, displayRange, Color.LightYellow, null); CoroutineManager.StartCoroutine(DimLight(light)); float cameraDist = Vector2.Distance(GameMain.GameScreen.Cam.Position, worldPosition) / 2.0f; GameMain.GameScreen.Cam.Shake = CameraShake * Math.Max((displayRange - cameraDist) / displayRange, 0.0f); if (attack.GetStructureDamage(1.0f) > 0.0f) { RangedStructureDamage(worldPosition, displayRange, attack.GetStructureDamage(1.0f)); } if (force == 0.0f && attack.Stun == 0.0f && attack.GetDamage(1.0f) == 0.0f) { return; } ApplyExplosionForces(worldPosition, attack.Range, force, attack.GetDamage(1.0f), attack.Stun); if (flames && GameMain.Client == null) { foreach (Item item in Item.ItemList) { if (item.CurrentHull != hull || item.FireProof || item.Condition <= 0.0f) { continue; } if (Vector2.Distance(item.WorldPosition, worldPosition) > attack.Range * 0.1f) { continue; } item.ApplyStatusEffects(ActionType.OnFire, 1.0f); if (item.Condition <= 0.0f && GameMain.Server != null) { GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ApplyStatusEffect, ActionType.OnFire }); } } } }
public static SoundChannel PlaySound(string soundTag, float volume, float range, Vector2 position, Hull hullGuess = null) { var sound = GetSound(soundTag); if (sound == null) { return(null); } return(PlaySound(sound, sound.BaseGain * volume, range, position, hullGuess)); }
public static SoundChannel PlaySound(Sound sound, float volume, float range, Vector2 position, Hull hullGuess = null) { if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > range * range) { return(null); } return(sound.Play(sound.BaseGain * volume, range, position, muffle: ShouldMuffleSound(Character.Controlled, position, range, hullGuess))); }
/// <summary> /// Play a sound defined in a sound xml file. If the volume or range parameters are omitted, the volume and range defined in the sound xml are used. /// </summary> public static SoundChannel PlaySound(string soundTag, Vector2 position, float?volume = null, float?range = null, Hull hullGuess = null) { var sound = GetSound(soundTag); if (sound == null) { return(null); } return(PlaySound(sound, position, volume ?? sound.BaseGain, range ?? sound.BaseFar, hullGuess)); }
public static SoundChannel PlaySound(Sound sound, Vector2 position, float?volume = null, float?range = null, Hull hullGuess = null) { if (sound == null) { string errorMsg = "Error in SoundPlayer.PlaySound (sound was null)\n" + Environment.StackTrace; GameAnalyticsManager.AddErrorEventOnce("SoundPlayer.PlaySound:SoundNull" + Environment.StackTrace, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); return(null); } float far = range ?? sound.BaseFar; if (Vector2.DistanceSquared(new Vector2(GameMain.SoundManager.ListenerPosition.X, GameMain.SoundManager.ListenerPosition.Y), position) > far * far) { return(null); } bool muffle = !sound.IgnoreMuffling && ShouldMuffleSound(Character.Controlled, position, far, hullGuess); return(sound.Play(volume ?? sound.BaseGain, far, position, muffle: muffle)); }
public static bool NeedsDivingGear(Hull hull) => hull == null || hull.OxygenPercentage < 50 || hull.WaterPercentage > 50;
static DebugConsole() { commands.Add(new Command("help", "", (string[] args) => { if (args.Length == 0) { foreach (Command c in commands) { if (string.IsNullOrEmpty(c.help)) { continue; } NewMessage(c.help, Color.Cyan); } } else { var matchingCommand = commands.Find(c => c.names.Any(name => name == args[0])); if (matchingCommand == null) { NewMessage("Command " + args[0] + " not found.", Color.Red); } else { NewMessage(matchingCommand.help, Color.Cyan); } } })); commands.Add(new Command("clientlist", "clientlist: List all the clients connected to the server.", (string[] args) => { if (GameMain.Server == null) { return; } NewMessage("***************", Color.Cyan); foreach (Client c in GameMain.Server.ConnectedClients) { NewMessage("- " + c.ID.ToString() + ": " + c.name + ", " + c.Connection.RemoteEndPoint.Address.ToString(), Color.Cyan); } NewMessage("***************", Color.Cyan); })); commands.Add(new Command("createfilelist", "", (string[] args) => { UpdaterUtil.SaveFileList("filelist.xml"); })); commands.Add(new Command("spawn|spawncharacter", "spawn [creaturename] [near/inside/outside]: Spawn a creature at a random spawnpoint (use the second parameter to only select spawnpoints near/inside/outside the submarine).", (string[] args) => { if (args.Length == 0) { return; } Character spawnedCharacter = null; Vector2 spawnPosition = Vector2.Zero; WayPoint spawnPoint = null; if (args.Length > 1) { switch (args[1].ToLowerInvariant()) { case "inside": spawnPoint = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); break; case "outside": spawnPoint = WayPoint.GetRandom(SpawnType.Enemy); break; case "near": case "close": float closestDist = -1.0f; foreach (WayPoint wp in WayPoint.WayPointList) { if (wp.Submarine != null) { continue; } //don't spawn inside hulls if (Hull.FindHull(wp.WorldPosition, null) != null) { continue; } float dist = Vector2.Distance(wp.WorldPosition, GameMain.GameScreen.Cam.WorldViewCenter); if (closestDist < 0.0f || dist < closestDist) { spawnPoint = wp; closestDist = dist; } } break; case "cursor": spawnPosition = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); break; default: spawnPoint = WayPoint.GetRandom(args[0].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); break; } } else { spawnPoint = WayPoint.GetRandom(args[0].ToLowerInvariant() == "human" ? SpawnType.Human : SpawnType.Enemy); } if (string.IsNullOrWhiteSpace(args[0])) { return; } if (spawnPoint != null) { spawnPosition = spawnPoint.WorldPosition; } if (args[0].ToLowerInvariant() == "human") { spawnedCharacter = Character.Create(Character.HumanConfigFile, spawnPosition); #if CLIENT if (GameMain.GameSession != null) { SinglePlayerMode mode = GameMain.GameSession.gameMode as SinglePlayerMode; if (mode != null) { Character.Controlled = spawnedCharacter; GameMain.GameSession.CrewManager.AddCharacter(Character.Controlled); GameMain.GameSession.CrewManager.SelectCharacter(null, Character.Controlled); } } #endif } else { spawnedCharacter = Character.Create( "Content/Characters/" + args[0].First().ToString().ToUpper() + args[0].Substring(1) + "/" + args[0].ToLower() + ".xml", spawnPosition); } })); commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory]: Spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted.", (string[] args) => { if (args.Length < 1) { return; } Vector2?spawnPos = null; Inventory spawnInventory = null; int extraParams = 0; switch (args.Last()) { case "cursor": extraParams = 1; spawnPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); break; case "inventory": extraParams = 1; spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory; break; default: extraParams = 0; break; } string itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant(); var itemPrefab = MapEntityPrefab.list.Find(ip => ip.Name.ToLowerInvariant() == itemName) as ItemPrefab; if (itemPrefab == null) { ThrowError("Item \"" + itemName + "\" not found!"); return; } if (spawnPos == null && spawnInventory == null) { var wp = WayPoint.GetRandom(SpawnType.Human, null, Submarine.MainSub); spawnPos = wp == null ? Vector2.Zero : wp.WorldPosition; } if (spawnPos != null) { Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); } else if (spawnInventory != null) { Entity.Spawner.AddToSpawnQueue(itemPrefab, spawnInventory); } })); commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) => { HumanAIController.DisableCrewAI = true; NewMessage("Crew AI disabled", Color.White); })); commands.Add(new Command("enablecrewai", "enablecrewai: Enable the AI of the NPCs in the crew.", (string[] args) => { HumanAIController.DisableCrewAI = false; NewMessage("Crew AI enabled", Color.White); })); commands.Add(new Command("kick", "kick [name]: Kick a player out of the server.", (string[] args) => { if (GameMain.NetworkMember == null || args.Length == 0) { return; } string playerName = string.Join(" ", args); ShowQuestionPrompt("Reason for kicking \"" + playerName + "\"?", (reason) => { GameMain.NetworkMember.KickPlayer(playerName, reason); }); })); commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) => { if (GameMain.Server == null || args.Length == 0) { return; } int id = 0; int.TryParse(args[0], out id); var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); if (client == null) { ThrowError("Client id \"" + id + "\" not found."); return; } ShowQuestionPrompt("Reason for kicking \"" + client.name + "\"?", (reason) => { GameMain.Server.KickPlayer(client.name, reason); }); })); commands.Add(new Command("ban", "ban [name]: Kick and ban the player from the server.", (string[] args) => { if (GameMain.NetworkMember == null || args.Length == 0) { return; } string clientName = string.Join(" ", args); ShowQuestionPrompt("Reason for banning \"" + clientName + "\"?", (reason) => { ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => { TimeSpan?banDuration = null; if (!string.IsNullOrWhiteSpace(duration)) { TimeSpan parsedBanDuration; if (!TryParseTimeSpan(duration, out parsedBanDuration)) { ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); return; } banDuration = parsedBanDuration; } GameMain.NetworkMember.BanPlayer(clientName, reason, false, banDuration); }); }); })); commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) => { if (GameMain.Server == null || args.Length == 0) { return; } int id = 0; int.TryParse(args[0], out id); var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); if (client == null) { ThrowError("Client id \"" + id + "\" not found."); return; } ShowQuestionPrompt("Reason for banning \"" + client.name + "\"?", (reason) => { ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => { TimeSpan?banDuration = null; if (!string.IsNullOrWhiteSpace(duration)) { TimeSpan parsedBanDuration; if (!TryParseTimeSpan(duration, out parsedBanDuration)) { ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); return; } banDuration = parsedBanDuration; } GameMain.Server.BanPlayer(client.name, reason, false, banDuration); }); }); })); commands.Add(new Command("banip", "banip [ip]: Ban the IP address from the server.", (string[] args) => { if (GameMain.Server == null || args.Length == 0) { return; } ShowQuestionPrompt("Reason for banning the ip \"" + commands[1] + "\"?", (reason) => { ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) => { TimeSpan?banDuration = null; if (!string.IsNullOrWhiteSpace(duration)) { TimeSpan parsedBanDuration; if (!TryParseTimeSpan(duration, out parsedBanDuration)) { ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); return; } banDuration = parsedBanDuration; } var client = GameMain.Server.ConnectedClients.Find(c => c.Connection.RemoteEndPoint.Address.ToString() == args[0]); if (client == null) { GameMain.Server.BanList.BanPlayer("Unnamed", args[0], reason, banDuration); } else { GameMain.Server.KickClient(client, reason); } }); }); })); commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) => { Character tpCharacter = null; if (args.Length == 0) { tpCharacter = Character.Controlled; } else { tpCharacter = FindMatchingCharacter(args, false); } if (tpCharacter == null) { return; } var cam = GameMain.GameScreen.Cam; tpCharacter.AnimController.CurrentHull = null; tpCharacter.Submarine = null; tpCharacter.AnimController.SetPosition(ConvertUnits.ToSimUnits(cam.ScreenToWorld(PlayerInput.MousePosition))); tpCharacter.AnimController.FindHull(cam.ScreenToWorld(PlayerInput.MousePosition), true); })); commands.Add(new Command("godmode", "godmode: Toggle submarine godmode. Makes the main submarine invulnerable to damage.", (string[] args) => { if (Submarine.MainSub == null) { return; } Submarine.MainSub.GodMode = !Submarine.MainSub.GodMode; NewMessage(Submarine.MainSub.GodMode ? "Godmode on" : "Godmode off", Color.White); })); commands.Add(new Command("lockx", "lockx: Lock horizontal movement of the main submarine.", (string[] args) => { Submarine.LockX = !Submarine.LockX; })); commands.Add(new Command("locky", "loxky: Lock vertical movement of the main submarine.", (string[] args) => { Submarine.LockY = !Submarine.LockY; })); commands.Add(new Command("dumpids", "", (string[] args) => { try { int count = args.Length == 0 ? 10 : int.Parse(args[0]); Entity.DumpIds(count); } catch (Exception e) { ThrowError("Failed to dump ids", e); } })); commands.Add(new Command("heal", "heal [character name]: Restore the specified character to full health. If the name parameter is omitted, the controlled character will be healed.", (string[] args) => { Character healedCharacter = null; if (args.Length == 0) { healedCharacter = Character.Controlled; } else { healedCharacter = FindMatchingCharacter(args); } if (healedCharacter != null) { healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); healedCharacter.Oxygen = 100.0f; healedCharacter.Bleeding = 0.0f; healedCharacter.SetStun(0.0f, true); } })); commands.Add(new Command("revive", "revive [character name]: Bring the specified character back from the dead. If the name parameter is omitted, the controlled character will be revived.", (string[] args) => { Character revivedCharacter = null; if (args.Length == 0) { revivedCharacter = Character.Controlled; } else { revivedCharacter = FindMatchingCharacter(args); } if (revivedCharacter == null) { return; } revivedCharacter.Revive(false); if (GameMain.Server != null) { foreach (Client c in GameMain.Server.ConnectedClients) { if (c.Character != revivedCharacter) { continue; } //clients stop controlling the character when it dies, force control back GameMain.Server.SetClientCharacter(c, revivedCharacter); break; } } })); commands.Add(new Command("freeze", "", (string[] args) => { if (Character.Controlled != null) { Character.Controlled.AnimController.Frozen = !Character.Controlled.AnimController.Frozen; } })); commands.Add(new Command("freecamera|freecam", "freecam: Detach the camera from the controlled character.", (string[] args) => { Character.Controlled = null; GameMain.GameScreen.Cam.TargetPos = Vector2.Zero; })); commands.Add(new Command("water|editwater", "water/editwater: Toggle water editing. Allows adding water into rooms by holding the left mouse button and removing it by holding the right mouse button.", (string[] args) => { if (GameMain.Client == null) { Hull.EditWater = !Hull.EditWater; NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White); } })); commands.Add(new Command("fire|editfire", "fire/editfire: Allows putting up fires by left clicking.", (string[] args) => { if (GameMain.Client == null) { Hull.EditFire = !Hull.EditFire; NewMessage(Hull.EditFire ? "Fire spawning on" : "Fire spawning off", Color.White); } })); commands.Add(new Command("explosion", "explosion [range] [force] [damage] [structuredamage]: Creates an explosion at the position of the cursor.", (string[] args) => { Vector2 explosionPos = GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition); float range = 500, force = 10, damage = 50, structureDamage = 10; if (args.Length > 0) { float.TryParse(args[0], out range); } if (args.Length > 1) { float.TryParse(args[1], out force); } if (args.Length > 2) { float.TryParse(args[2], out damage); } if (args.Length > 3) { float.TryParse(args[3], out structureDamage); } new Explosion(range, force, damage, structureDamage).Explode(explosionPos); })); commands.Add(new Command("fixitems", "fixitems: Repairs all items and restores them to full condition.", (string[] args) => { foreach (Item it in Item.ItemList) { it.Condition = it.Prefab.Health; } })); commands.Add(new Command("fixhulls|fixwalls", "fixwalls/fixhulls: Fixes all walls.", (string[] args) => { foreach (Structure w in Structure.WallList) { for (int i = 0; i < w.SectionCount; i++) { w.AddDamage(i, -100000.0f); } } })); commands.Add(new Command("power", "power [temperature]: Immediately sets the temperature of the nuclear reactor to the specified value.", (string[] args) => { Item reactorItem = Item.ItemList.Find(i => i.GetComponent <Reactor>() != null); if (reactorItem == null) { return; } float power = 5000.0f; if (args.Length > 0) { float.TryParse(args[0], out power); } var reactor = reactorItem.GetComponent <Reactor>(); reactor.ShutDownTemp = power == 0 ? 0 : 7000.0f; reactor.AutoTemp = true; reactor.Temperature = power; if (GameMain.Server != null) { reactorItem.CreateServerEvent(reactor); } })); commands.Add(new Command("oxygen|air", "oxygen/air: Replenishes the oxygen levels in every room to 100%.", (string[] args) => { foreach (Hull hull in Hull.hullList) { hull.OxygenPercentage = 100.0f; } })); commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) => { foreach (Character c in Character.CharacterList) { if (!(c.AIController is EnemyAIController)) { continue; } c.AddDamage(CauseOfDeath.Damage, 10000.0f, null); } })); commands.Add(new Command("netstats", "netstats: Toggles the visibility of the network statistics UI.", (string[] args) => { if (GameMain.Server == null) { return; } GameMain.Server.ShowNetStats = !GameMain.Server.ShowNetStats; })); commands.Add(new Command("setclientcharacter", "setclientcharacter [client name] ; [character name]: Gives the client control of the specified character.", (string[] args) => { if (GameMain.Server == null) { return; } int separatorIndex = Array.IndexOf(args, ";"); if (separatorIndex == -1 || args.Length < 3) { ThrowError("Invalid parameters. The command should be formatted as \"setclientcharacter [client] ; [character]\""); return; } string[] argsLeft = args.Take(separatorIndex).ToArray(); string[] argsRight = args.Skip(separatorIndex).ToArray(); string clientName = String.Join(" ", argsLeft); var client = GameMain.Server.ConnectedClients.Find(c => c.name == clientName); if (client == null) { ThrowError("Client \"" + clientName + "\" not found."); } var character = FindMatchingCharacter(argsRight, false); GameMain.Server.SetClientCharacter(client, character); })); #if DEBUG commands.Add(new Command("spamevents", "A debug command that immediately creates entity events for all items, characters and structures.", (string[] args) => { foreach (Item item in Item.ItemList) { for (int i = 0; i < item.components.Count; i++) { if (item.components[i] is IServerSerializable) { GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ComponentState, i }); } var itemContainer = item.GetComponent <ItemContainer>(); if (itemContainer != null) { GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.InventoryState }); } GameMain.Server.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.Status }); item.NeedsPositionUpdate = true; } } foreach (Character c in Character.CharacterList) { GameMain.Server.CreateEntityEvent(c, new object[] { NetEntityEvent.Type.Status }); } foreach (Structure wall in Structure.WallList) { GameMain.Server.CreateEntityEvent(wall); } })); #endif InitProjectSpecific(); commands.Sort((c1, c2) => c1.names[0].CompareTo(c2.names[0])); }
//returns the water block which contains the point (or null if it isn't inside any) public static Hull FindHullOld(Vector2 position, Hull guess = null, bool useWorldCoordinates = true, bool inclusive = false) { return(FindHullOld(position, hullList, guess, useWorldCoordinates, inclusive)); }
public Hull FindBestHull(IEnumerable <Hull> ignoredHulls = null, bool allowChangingTheSubmarine = true) { Hull bestHull = null; float bestValue = 0; foreach (Hull hull in Hull.hullList) { if (hull.Submarine == null) { continue; } if (!allowChangingTheSubmarine && hull.Submarine != character.Submarine) { continue; } if (ignoredHulls != null && ignoredHulls.Contains(hull)) { continue; } if (unreachable.Contains(hull)) { continue; } float hullSafety = 0; if (character.CurrentHull != null && character.Submarine != null) { // Inside if (!character.Submarine.IsConnectedTo(hull.Submarine)) { continue; } hullSafety = HumanAIController.GetHullSafety(hull, character); // Vertical distance matters more than horizontal (climbing up/down is harder than moving horizontally) float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f; float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist)); hullSafety *= distanceFactor; //skip the hull if the safety is already less than the best hull //(no need to do the expensive pathfinding if we already know we're not going to choose this hull) if (hullSafety < bestValue) { continue; } var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition); if (path.Unreachable) { unreachable.Add(hull); continue; } // Each unsafe node reduces the hull safety value. // Ignore the current hull, because otherwise we couldn't find a path out. int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull)); hullSafety /= 1 + unsafeNodes; // If the target is not inside a friendly submarine, considerably reduce the hull safety. if (!character.Submarine.IsEntityFoundOnThisSub(hull, true)) { hullSafety /= 10; } } else { // Outside if (hull.RoomName != null && hull.RoomName.ToLowerInvariant().Contains("airlock")) { hullSafety = 100; } else { // TODO: could also target gaps that get us inside? foreach (Item item in Item.ItemList) { if (item.CurrentHull != hull && item.HasTag("airlock")) { hullSafety = 100; break; } } } // TODO: could we get a closest door to the outside and target the flowing hull if no airlock is found? // Huge preference for closer targets float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition); float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance)); hullSafety *= distanceFactor; // If the target is not inside a friendly submarine, considerably reduce the hull safety. if (hull.Submarine.TeamID != character.TeamID && hull.Submarine.TeamID != Character.TeamType.FriendlyNPC) { hullSafety /= 10; } } if (hullSafety > bestValue) { bestHull = hull; bestValue = hullSafety; } } return(bestHull); }
public static bool ShouldMuffleSound(Character listener, Vector2 soundWorldPos, float range, Hull hullGuess) { if (listener == null) { return(false); } float lowpassHFGain = 1.0f; AnimController animController = listener.AnimController; if (animController.HeadInWater) { lowpassHFGain = 0.2f; } lowpassHFGain *= Character.Controlled.LowPassMultiplier; if (lowpassHFGain < 0.5f) { return(true); } Hull targetHull = Hull.FindHull(soundWorldPos, hullGuess, true); if (listener.CurrentHull == null || targetHull == null) { return(listener.CurrentHull != targetHull); } Vector2 soundPos = soundWorldPos; if (targetHull.Submarine != null) { soundPos += -targetHull.Submarine.WorldPosition + targetHull.Submarine.HiddenSubPosition; } return(listener.CurrentHull.GetApproximateDistance(listener.Position, soundPos, targetHull, range) > range); }
protected override void Act(float deltaTime) { var currentHull = character.AnimController.CurrentHull; bool needsDivingGear = HumanAIController.NeedsDivingGear(currentHull); bool needsDivingSuit = needsDivingGear && (currentHull == null || currentHull.WaterPercentage > 90); bool needsEquipment = false; if (needsDivingSuit) { needsEquipment = !HumanAIController.HasDivingSuit(character); } else if (needsDivingGear) { needsEquipment = !HumanAIController.HasDivingMask(character); } if (needsEquipment) { TryAddSubObjective(ref divingGearObjective, () => new AIObjectiveFindDivingGear(character, needsDivingSuit, objectiveManager), onAbandon: () => searchHullTimer = Math.Min(1, searchHullTimer)); } else { if (divingGearObjective != null && divingGearObjective.IsCompleted()) { // Reset the devotion. Priority = 0; divingGearObjective = null; } if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD) { searchHullTimer = Math.Min(1, searchHullTimer); } if (searchHullTimer > 0.0f) { searchHullTimer -= deltaTime; } else { searchHullTimer = SearchHullInterval; previousSafeHull = currentSafeHull; currentSafeHull = FindBestHull(); if (currentSafeHull == null) { currentSafeHull = previousSafeHull; } if (currentSafeHull != null && currentSafeHull != currentHull) { if (goToObjective?.Target != currentSafeHull) { goToObjective = null; } TryAddSubObjective(ref goToObjective, constructor: () => new AIObjectiveGoTo(currentSafeHull, character, objectiveManager, getDivingGearIfNeeded: true) { AllowGoingOutside = HumanAIController.HasDivingSuit(character) }, onAbandon: () => unreachable.Add(goToObjective.Target as Hull)); } else { goToObjective = null; } } if (goToObjective != null) { if (goToObjective.IsCompleted()) { objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } Priority = 0; return; } if (currentHull == null) { return; } //goto objective doesn't exist (a safe hull not found, or a path to a safe hull not found) // -> attempt to manually steer away from hazards Vector2 escapeVel = Vector2.Zero; // TODO: optimize foreach (FireSource fireSource in HumanAIController.VisibleHulls.SelectMany(h => h.FireSources)) { Vector2 dir = character.Position - fireSource.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(fireSource.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } foreach (Character enemy in Character.CharacterList) { if (enemy.IsDead || enemy.IsUnconscious || enemy.Removed || HumanAIController.IsFriendly(enemy)) { continue; } if (HumanAIController.VisibleHulls.Contains(enemy.CurrentHull)) { Vector2 dir = character.Position - enemy.Position; float distMultiplier = MathHelper.Clamp(100.0f / Vector2.Distance(enemy.Position, character.Position), 0.1f, 10.0f); escapeVel += new Vector2(Math.Sign(dir.X) * distMultiplier, !character.IsClimbing ? 0 : Math.Sign(dir.Y) * distMultiplier); } } if (escapeVel != Vector2.Zero) { float left = currentHull.Rect.X + 50; float right = currentHull.Rect.Right - 50; //only move if we haven't reached the edge of the room if (escapeVel.X < 0 && character.Position.X > left || escapeVel.X > 0 && character.Position.X < right) { character.AIController.SteeringManager.SteeringManual(deltaTime, escapeVel); } else { character.AnimController.TargetDir = escapeVel.X < 0.0f ? Direction.Right : Direction.Left; character.AIController.SteeringManager.Reset(); } } else { Priority = 0; objectiveManager.GetObjective <AIObjectiveIdle>()?.Wander(deltaTime); } } }
public void Load(bool unloadPrevious, XElement submarineElement = null) { if (unloadPrevious) { Unload(); } Loading = true; if (submarineElement == null) { XDocument doc = OpenFile(filePath); if (doc == null || doc.Root == null) { return; } submarineElement = doc.Root; } Description = submarineElement.GetAttributeString("description", ""); Enum.TryParse(submarineElement.GetAttributeString("tags", ""), out tags); //place the sub above the top of the level HiddenSubPosition = HiddenSubStartPosition; if (GameMain.GameSession != null && GameMain.GameSession.Level != null) { HiddenSubPosition += Vector2.UnitY * GameMain.GameSession.Level.Size.Y; } foreach (Submarine sub in Submarine.loaded) { HiddenSubPosition += Vector2.UnitY * (sub.Borders.Height + 5000.0f); } IdOffset = 0; foreach (MapEntity me in MapEntity.mapEntityList) { IdOffset = Math.Max(IdOffset, me.ID); } foreach (XElement element in submarineElement.Elements()) { string typeName = element.Name.ToString(); Type t; try { t = Type.GetType("Barotrauma." + typeName, true, true); if (t == null) { DebugConsole.ThrowError("Error in " + filePath + "! Could not find a entity of the type \"" + typeName + "\"."); continue; } } catch (Exception e) { DebugConsole.ThrowError("Error in " + filePath + "! Could not find a entity of the type \"" + typeName + "\".", e); continue; } try { MethodInfo loadMethod = t.GetMethod("Load"); loadMethod.Invoke(t, new object[] { element, this }); } catch (Exception e) { DebugConsole.ThrowError("Could not find the method \"Load\" in " + t + ".", e); } } Vector2 center = Vector2.Zero; var matchingHulls = Hull.hullList.FindAll(h => h.Submarine == this); if (matchingHulls.Any()) { Vector2 topLeft = new Vector2(matchingHulls[0].Rect.X, matchingHulls[0].Rect.Y); Vector2 bottomRight = new Vector2(matchingHulls[0].Rect.X, matchingHulls[0].Rect.Y); foreach (Hull hull in matchingHulls) { if (hull.Rect.X < topLeft.X) { topLeft.X = hull.Rect.X; } if (hull.Rect.Y > topLeft.Y) { topLeft.Y = hull.Rect.Y; } if (hull.Rect.Right > bottomRight.X) { bottomRight.X = hull.Rect.Right; } if (hull.Rect.Y - hull.Rect.Height < bottomRight.Y) { bottomRight.Y = hull.Rect.Y - hull.Rect.Height; } } center = (topLeft + bottomRight) / 2.0f; center.X -= center.X % GridSize.X; center.Y -= center.Y % GridSize.Y; if (center != Vector2.Zero) { foreach (Item item in Item.ItemList) { if (item.Submarine != this) { continue; } var wire = item.GetComponent <Items.Components.Wire>(); if (wire != null) { wire.MoveNodes(-center); } } for (int i = 0; i < MapEntity.mapEntityList.Count; i++) { if (MapEntity.mapEntityList[i].Submarine != this) { continue; } MapEntity.mapEntityList[i].Move(-center); } } } subBody = new SubmarineBody(this); subBody.SetPosition(HiddenSubPosition); loaded.Add(this); if (entityGrid != null) { Hull.EntityGrids.Remove(entityGrid); entityGrid = null; } entityGrid = Hull.GenerateEntityGrid(this); for (int i = 0; i < MapEntity.mapEntityList.Count; i++) { if (MapEntity.mapEntityList[i].Submarine != this) { continue; } MapEntity.mapEntityList[i].Move(HiddenSubPosition); } Loading = false; MapEntity.MapLoaded(this); //WayPoint.GenerateSubWaypoints(); #if CLIENT GameMain.LightManager.OnMapLoaded(); #endif ID = (ushort)(ushort.MaxValue - Submarine.loaded.IndexOf(this)); }
private void FindHulls() { Hull[] hulls = new Hull[2]; foreach (var linked in linkedTo) { if (linked is Hull hull) { hull.ConnectedGaps.Remove(this); } } linkedTo.Clear(); Vector2[] searchPos = new Vector2[2]; if (IsHorizontal) { searchPos[0] = new Vector2(rect.X, rect.Y - rect.Height / 2); searchPos[1] = new Vector2(rect.Right, rect.Y - rect.Height / 2); } else { searchPos[0] = new Vector2(rect.Center.X, rect.Y); searchPos[1] = new Vector2(rect.Center.X, rect.Y - rect.Height); } for (int i = 0; i < 2; i++) { hulls[i] = Hull.FindHullUnoptimized(searchPos[i], null, false); if (hulls[i] == null) { hulls[i] = Hull.FindHullUnoptimized(searchPos[i], null, false, true); } } if (hulls[0] == null && hulls[1] == null) { return; } if (hulls[0] == null && hulls[1] != null) { Hull temp = hulls[0]; hulls[0] = hulls[1]; hulls[1] = temp; } flowTargetHull = hulls[0]; for (int i = 0; i < 2; i++) { if (hulls[i] == null) { continue; } linkedTo.Add(hulls[i]); if (!hulls[i].ConnectedGaps.Contains(this)) { hulls[i].ConnectedGaps.Add(this); } } }
public void FlipX(List <Submarine> parents = null) { if (parents == null) { parents = new List <Submarine>(); } parents.Add(this); flippedX = !flippedX; Item.UpdateHulls(); List <Item> bodyItems = Item.ItemList.FindAll(it => it.Submarine == this && it.body != null); List <MapEntity> subEntities = MapEntity.mapEntityList.FindAll(me => me.Submarine == this); foreach (MapEntity e in subEntities) { if (e.MoveWithLevel || e is Item) { continue; } if (e is LinkedSubmarine) { Submarine sub = ((LinkedSubmarine)e).Sub; if (!parents.Contains(sub)) { Vector2 relative1 = sub.SubBody.Position - SubBody.Position; relative1.X = -relative1.X; sub.SetPosition(relative1 + SubBody.Position); sub.FlipX(parents); } } else { e.FlipX(); } } foreach (MapEntity mapEntity in subEntities) { mapEntity.Move(-HiddenSubPosition); } Vector2 pos = new Vector2(subBody.Position.X, subBody.Position.Y); subBody.Body.Remove(); subBody = new SubmarineBody(this); SetPosition(pos); if (entityGrid != null) { Hull.EntityGrids.Remove(entityGrid); entityGrid = null; } entityGrid = Hull.GenerateEntityGrid(this); foreach (MapEntity mapEntity in subEntities) { mapEntity.Move(HiddenSubPosition); } foreach (Item item in Item.ItemList) { if (bodyItems.Contains(item)) { item.Submarine = this; if (Position == Vector2.Zero) { item.Move(-HiddenSubPosition); } } else if (item.Submarine != this) { continue; } item.FlipX(); } Item.UpdateHulls(); Gap.UpdateHulls(); }
public override void Update(float deltaTime, Camera cam) { flowForce = Vector2.Zero; outsideColliderRaycastTimer -= deltaTime; if (open == 0.0f || linkedTo.Count == 0) { lerpedFlowForce = Vector2.Zero; return; } Hull hull1 = (Hull)linkedTo[0]; Hull hull2 = linkedTo.Count < 2 ? null : (Hull)linkedTo[1]; if (hull1 == hull2) { return; } UpdateOxygen(hull1, hull2); if (linkedTo.Count == 1) { //gap leading from a room to outside UpdateRoomToOut(deltaTime, hull1); } else if (linkedTo.Count == 2) { //gap leading from a room to another UpdateRoomToRoom(deltaTime, hull1, hull2); } flowForce.X = MathHelper.Clamp(flowForce.X, -MaxFlowForce, MaxFlowForce); flowForce.Y = MathHelper.Clamp(flowForce.Y, -MaxFlowForce, MaxFlowForce); lerpedFlowForce = Vector2.Lerp(lerpedFlowForce, flowForce, deltaTime * 5.0f); EmitParticles(deltaTime); if (flowTargetHull != null && lerpedFlowForce.LengthSquared() > 0.0001f) { foreach (Character character in Character.CharacterList) { if (character.CurrentHull == null) { continue; } if (character.CurrentHull != linkedTo[0] as Hull && (linkedTo.Count < 2 || character.CurrentHull != linkedTo[1] as Hull)) { continue; } foreach (Limb limb in character.AnimController.Limbs) { if (!limb.inWater) { continue; } float dist = Vector2.Distance(limb.WorldPosition, WorldPosition); if (dist > lerpedFlowForce.Length()) { continue; } Vector2 force = lerpedFlowForce / (float)Math.Max(Math.Sqrt(dist), 20.0f) * 0.025f; //vertical gaps only apply forces if the character is roughly above/below the gap if (!IsHorizontal) { float xDist = Math.Abs(limb.WorldPosition.X - WorldPosition.X); if (xDist > rect.Width || rect.Width == 0) { break; } force *= 1.0f - xDist / rect.Width; } if (!MathUtils.IsValid(force)) { string errorMsg = "Attempted to apply invalid flow force to the character \"" + character.Name + "\", gap pos: " + WorldPosition + ", limb pos: " + limb.WorldPosition + ", flowforce: " + flowForce + ", lerpedFlowForce:" + lerpedFlowForce + ", dist: " + dist; DebugConsole.Log(errorMsg); GameAnalyticsManager.AddErrorEventOnce("Gap.Update:InvalidFlowForce:" + character.Name, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg); continue; } character.AnimController.Collider.ApplyForce(force * limb.body.Mass, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } } }
public static void GenerateSubWaypoints(Submarine submarine) { if (!Hull.hullList.Any()) { DebugConsole.ThrowError("Couldn't generate waypoints: no hulls found."); return; } List <WayPoint> existingWaypoints = WayPointList.FindAll(wp => wp.spawnType == SpawnType.Path); foreach (WayPoint wayPoint in existingWaypoints) { wayPoint.Remove(); } //find all open doors and temporarily activate their bodies to prevent visibility checks //from ignoring the doors and generating waypoint connections that go straight through the door List <Door> openDoors = new List <Door>(); foreach (Item item in Item.ItemList) { var door = item.GetComponent <Door>(); if (door != null && !door.Body.Enabled) { openDoors.Add(door); door.Body.Enabled = true; } } float minDist = 150.0f; float heightFromFloor = 110.0f; foreach (Hull hull in Hull.hullList) { if (hull.Rect.Height < 150) { continue; } WayPoint prevWaypoint = null; if (hull.Rect.Width < minDist * 3.0f) { new WayPoint( new Vector2(hull.Rect.X + hull.Rect.Width / 2.0f, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine); continue; } for (float x = hull.Rect.X + minDist; x <= hull.Rect.Right - minDist; x += minDist) { var wayPoint = new WayPoint(new Vector2(x, hull.Rect.Y - hull.Rect.Height + heightFromFloor), SpawnType.Path, submarine); if (prevWaypoint != null) { wayPoint.ConnectTo(prevWaypoint); } prevWaypoint = wayPoint; } } float outSideWaypointInterval = 200.0f; int outsideWaypointDist = 100; Rectangle borders = Hull.GetBorders(); borders.X -= outsideWaypointDist; borders.Y += outsideWaypointDist; borders.Width += outsideWaypointDist * 2; borders.Height += outsideWaypointDist * 2; borders.Location -= MathUtils.ToPoint(submarine.HiddenSubPosition); if (borders.Width <= outSideWaypointInterval * 2) { borders.Inflate(outSideWaypointInterval * 2 - borders.Width, 0); } if (borders.Height <= outSideWaypointInterval * 2) { int inflateAmount = (int)(outSideWaypointInterval * 2) - borders.Height; borders.Y += inflateAmount / 2; borders.Height += inflateAmount; } WayPoint[,] cornerWaypoint = new WayPoint[2, 2]; for (int i = 0; i < 2; i++) { for (float x = borders.X + outSideWaypointInterval; x < borders.Right - outSideWaypointInterval; x += outSideWaypointInterval) { var wayPoint = new WayPoint( new Vector2(x, borders.Y - borders.Height * i) + submarine.HiddenSubPosition, SpawnType.Path, submarine); if (x == borders.X + outSideWaypointInterval) { cornerWaypoint[i, 0] = wayPoint; } else { wayPoint.ConnectTo(WayPointList[WayPointList.Count - 2]); } } cornerWaypoint[i, 1] = WayPointList[WayPointList.Count - 1]; } for (int i = 0; i < 2; i++) { WayPoint wayPoint = null; for (float y = borders.Y - borders.Height; y < borders.Y; y += outSideWaypointInterval) { wayPoint = new WayPoint( new Vector2(borders.X + borders.Width * i, y) + submarine.HiddenSubPosition, SpawnType.Path, submarine); if (y == borders.Y - borders.Height) { wayPoint.ConnectTo(cornerWaypoint[1, i]); } else { wayPoint.ConnectTo(WayPoint.WayPointList[WayPointList.Count - 2]); } } wayPoint.ConnectTo(cornerWaypoint[0, i]); } List <Structure> stairList = new List <Structure>(); foreach (MapEntity me in mapEntityList) { Structure stairs = me as Structure; if (stairs == null) { continue; } if (stairs.StairDirection != Direction.None) { stairList.Add(stairs); } } foreach (Structure stairs in stairList) { WayPoint[] stairPoints = new WayPoint[3]; stairPoints[0] = new WayPoint( new Vector2(stairs.Rect.X - 32.0f, stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? 80 : stairs.Rect.Height) + heightFromFloor), SpawnType.Path, submarine); stairPoints[1] = new WayPoint( new Vector2(stairs.Rect.Right + 32.0f, stairs.Rect.Y - (stairs.StairDirection == Direction.Left ? stairs.Rect.Height : 80) + heightFromFloor), SpawnType.Path, submarine); for (int i = 0; i < 2; i++) { for (int dir = -1; dir <= 1; dir += 2) { WayPoint closest = stairPoints[i].FindClosest(dir, true, new Vector2(-30.0f, 30f)); if (closest == null) { continue; } stairPoints[i].ConnectTo(closest); } } stairPoints[2] = new WayPoint((stairPoints[0].Position + stairPoints[1].Position) / 2, SpawnType.Path, submarine); stairPoints[0].ConnectTo(stairPoints[2]); stairPoints[2].ConnectTo(stairPoints[1]); } foreach (Item item in Item.ItemList) { var ladders = item.GetComponent <Ladder>(); if (ladders == null) { continue; } List <WayPoint> ladderPoints = new List <WayPoint>(); ladderPoints.Add(new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - item.Rect.Height + heightFromFloor), SpawnType.Path, submarine)); WayPoint prevPoint = ladderPoints[0]; Vector2 prevPos = prevPoint.SimPosition; List <Body> ignoredBodies = new List <Body>(); for (float y = ladderPoints[0].Position.Y + 100.0f; y < item.Rect.Y - 1.0f; y += 100.0f) { //first check if there's a door in the way //(we need to create a waypoint linked to the door for NPCs to open it) Body pickedBody = Submarine.PickBody( ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)), prevPos, ignoredBodies, Physics.CollisionWall, false, (Fixture f) => f.Body.UserData is Item && ((Item)f.Body.UserData).GetComponent <Door>() != null); Door pickedDoor = null; if (pickedBody != null) { pickedDoor = (pickedBody?.UserData as Item).GetComponent <Door>(); } else { //no door, check for walls pickedBody = Submarine.PickBody( ConvertUnits.ToSimUnits(new Vector2(ladderPoints[0].Position.X, y)), prevPos, ignoredBodies, null, false); } if (pickedBody == null) { prevPos = Submarine.LastPickedPosition; continue; } else { ignoredBodies.Add(pickedBody); } if (pickedDoor != null) { WayPoint newPoint = new WayPoint(pickedDoor.Item.Position, SpawnType.Path, submarine); ladderPoints.Add(newPoint); newPoint.ConnectedGap = pickedDoor.LinkedGap; newPoint.ConnectTo(prevPoint); prevPoint = newPoint; prevPos = new Vector2(prevPos.X, ConvertUnits.ToSimUnits(pickedDoor.Item.Position.Y - pickedDoor.Item.Rect.Height)); } else { WayPoint newPoint = new WayPoint(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * heightFromFloor, SpawnType.Path, submarine); ladderPoints.Add(newPoint); newPoint.ConnectTo(prevPoint); prevPoint = newPoint; prevPos = ConvertUnits.ToSimUnits(newPoint.Position); } } if (prevPoint.rect.Y < item.Rect.Y - 10.0f) { WayPoint newPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine); ladderPoints.Add(newPoint); newPoint.ConnectTo(prevPoint); } //connect ladder waypoints to hull points at the right and left side foreach (WayPoint ladderPoint in ladderPoints) { ladderPoint.Ladders = ladders; //don't connect if the waypoint is at a gap (= at the boundary of hulls and/or at a hatch) if (ladderPoint.ConnectedGap != null) { continue; } for (int dir = -1; dir <= 1; dir += 2) { WayPoint closest = ladderPoint.FindClosest(dir, true, new Vector2(-150.0f, 10f)); if (closest == null) { continue; } ladderPoint.ConnectTo(closest); } } } foreach (Gap gap in Gap.GapList) { if (!gap.IsHorizontal) { continue; } //too small to walk through if (gap.Rect.Height < 150.0f) { continue; } var wayPoint = new WayPoint( new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height + heightFromFloor), SpawnType.Path, submarine, gap); for (int dir = -1; dir <= 1; dir += 2) { float tolerance = gap.IsRoomToRoom ? 50.0f : outSideWaypointInterval / 2.0f; WayPoint closest = wayPoint.FindClosest( dir, true, new Vector2(-tolerance, tolerance), gap.ConnectedDoor?.Body.FarseerBody); if (closest != null) { wayPoint.ConnectTo(closest); } } } foreach (Gap gap in Gap.GapList) { if (gap.IsHorizontal || gap.IsRoomToRoom || !gap.linkedTo.Any(l => l is Hull)) { continue; } //too small to walk through if (gap.Rect.Width < 100.0f) { continue; } var wayPoint = new WayPoint( new Vector2(gap.Rect.Center.X, gap.Rect.Y - gap.Rect.Height / 2), SpawnType.Path, submarine, gap); float tolerance = outSideWaypointInterval / 2.0f; Hull connectedHull = (Hull)gap.linkedTo.First(l => l is Hull); int dir = Math.Sign(connectedHull.Position.Y - gap.Position.Y); WayPoint closest = wayPoint.FindClosest( dir, false, new Vector2(-tolerance, tolerance), gap.ConnectedDoor?.Body.FarseerBody); if (closest != null) { wayPoint.ConnectTo(closest); } } var orphans = WayPointList.FindAll(w => w.spawnType == SpawnType.Path && !w.linkedTo.Any()); foreach (WayPoint wp in orphans) { wp.Remove(); } //re-disable the bodies of the doors that are supposed to be open foreach (Door door in openDoors) { door.Body.Enabled = false; } }
void UpdateRoomToOut(float deltaTime, Hull hull1) { float size = IsHorizontal ? rect.Height : rect.Width; //a variable affecting the water flow through the gap //the larger the gap is, the faster the water flows float sizeModifier = size * open * open; float delta = 500.0f * sizeModifier * deltaTime; //make sure not to place more water to the target room than it can hold delta = Math.Min(delta, hull1.Volume * Hull.MaxCompress - hull1.WaterVolume); hull1.WaterVolume += delta; if (hull1.WaterVolume > hull1.Volume) { hull1.Pressure += 0.5f; } flowTargetHull = hull1; if (IsHorizontal) { //water flowing from right to left if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) { flowForce = new Vector2(-delta, 0.0f); } else { flowForce = new Vector2(delta, 0.0f); } higherSurface = hull1.Surface; lowerSurface = rect.Y; if (hull1.WaterVolume < hull1.Volume / Hull.MaxCompress && hull1.Surface < rect.Y) { if (rect.X > hull1.Rect.X + hull1.Rect.Width / 2.0f) { float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[hull1.WaveY.Length - 1])) * 0.1f; vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f); hull1.WaveVel[hull1.WaveY.Length - 1] += vel; hull1.WaveVel[hull1.WaveY.Length - 2] += vel; } else { float vel = ((rect.Y - rect.Height / 2) - (hull1.Surface + hull1.WaveY[0])) * 0.1f; vel *= Math.Min(Math.Abs(flowForce.X) / 200.0f, 1.0f); hull1.WaveVel[0] += vel; hull1.WaveVel[1] += vel; } } else { hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime; } } else { if (rect.Y > hull1.Rect.Y - hull1.Rect.Height / 2.0f) { flowForce = new Vector2(0.0f, -delta); } else { flowForce = new Vector2(0.0f, delta); } if (hull1.WaterVolume >= hull1.Volume / Hull.MaxCompress) { hull1.LethalPressure += (Submarine != null && Submarine.AtDamageDepth) ? 100.0f * deltaTime : 10.0f * deltaTime; } } }
partial void UpdateNetPlayerPositionProjSpecific(float deltaTime, float lowestSubPos) { if (character != GameMain.Client.Character || !character.CanMove) { //remove states without a timestamp (there may still be ID-based states //in the list when the controlled character switches to timestamp-based interpolation) character.MemState.RemoveAll(m => m.Timestamp == 0.0f); //use simple interpolation for other players' characters and characters that can't move if (character.MemState.Count > 0) { CharacterStateInfo serverPos = character.MemState.Last(); if (!character.isSynced) { SetPosition(serverPos.Position, false); Collider.LinearVelocity = Vector2.Zero; character.MemLocalState.Clear(); character.LastNetworkUpdateID = serverPos.ID; character.isSynced = true; return; } if (character.MemState[0].SelectedCharacter == null || character.MemState[0].SelectedCharacter.Removed) { character.DeselectCharacter(); } else if (character.MemState[0].SelectedCharacter != null) { character.SelectCharacter(character.MemState[0].SelectedCharacter); } if (character.MemState[0].SelectedItem == null || character.MemState[0].SelectedItem.Removed) { character.SelectedConstruction = null; } else { if (character.SelectedConstruction != character.MemState[0].SelectedItem) { foreach (var ic in character.MemState[0].SelectedItem.Components) { if (ic.CanBeSelected) { ic.Select(character); } } } character.SelectedConstruction = character.MemState[0].SelectedItem; } if (character.MemState[0].Animation == AnimController.Animation.CPR) { character.AnimController.Anim = AnimController.Animation.CPR; } else if (character.AnimController.Anim == AnimController.Animation.CPR) { character.AnimController.Anim = AnimController.Animation.None; } Vector2 newVelocity = Collider.LinearVelocity; Vector2 newPosition = Collider.SimPosition; float newRotation = Collider.Rotation; float newAngularVelocity = Collider.AngularVelocity; Collider.CorrectPosition(character.MemState, out newPosition, out newVelocity, out newRotation, out newAngularVelocity); newVelocity = newVelocity.ClampLength(100.0f); if (!MathUtils.IsValid(newVelocity)) { newVelocity = Vector2.Zero; } overrideTargetMovement = newVelocity.LengthSquared() > 0.01f ? newVelocity : Vector2.Zero; Collider.LinearVelocity = newVelocity; Collider.AngularVelocity = newAngularVelocity; float distSqrd = Vector2.DistanceSquared(newPosition, Collider.SimPosition); float errorTolerance = character.CanMove ? 0.01f : 0.2f; if (distSqrd > errorTolerance) { if (distSqrd > 10.0f || !character.CanMove) { Collider.TargetRotation = newRotation; SetPosition(newPosition, lerp: distSqrd < 5.0f, ignorePlatforms: false); } else { Collider.TargetRotation = newRotation; Collider.TargetPosition = newPosition; Collider.MoveToTargetPosition(true); } } //immobilized characters can't correct their position using AnimController movement // -> we need to correct it manually if (!character.CanMove) { float mainLimbDistSqrd = Vector2.DistanceSquared(MainLimb.PullJointWorldAnchorA, Collider.SimPosition); float mainLimbErrorTolerance = 0.1f; //if the main limb is roughly at the correct position and the collider isn't moving (much at least), //don't attempt to correct the position. if (mainLimbDistSqrd > mainLimbErrorTolerance || Collider.LinearVelocity.LengthSquared() > 0.05f) { MainLimb.PullJointWorldAnchorB = Collider.SimPosition; MainLimb.PullJointEnabled = true; } } } character.MemLocalState.Clear(); } else { //remove states with a timestamp (there may still timestamp-based states //in the list if the controlled character switches from timestamp-based interpolation to ID-based) character.MemState.RemoveAll(m => m.Timestamp > 0.0f); for (int i = 0; i < character.MemLocalState.Count; i++) { if (character.Submarine == null) { //transform in-sub coordinates to outside coordinates if (character.MemLocalState[i].Position.Y > lowestSubPos) { character.MemLocalState[i].TransformInToOutside(); } } else if (currentHull?.Submarine != null) { //transform outside coordinates to in-sub coordinates if (character.MemLocalState[i].Position.Y < lowestSubPos) { character.MemLocalState[i].TransformOutToInside(currentHull.Submarine); } } } if (character.MemState.Count < 1) { return; } overrideTargetMovement = Vector2.Zero; CharacterStateInfo serverPos = character.MemState.Last(); if (!character.isSynced) { SetPosition(serverPos.Position, false); Collider.LinearVelocity = Vector2.Zero; character.MemLocalState.Clear(); character.LastNetworkUpdateID = serverPos.ID; character.isSynced = true; return; } int localPosIndex = character.MemLocalState.FindIndex(m => m.ID == serverPos.ID); if (localPosIndex > -1) { CharacterStateInfo localPos = character.MemLocalState[localPosIndex]; //the entity we're interacting with doesn't match the server's if (localPos.SelectedCharacter != serverPos.SelectedCharacter) { if (serverPos.SelectedCharacter == null || serverPos.SelectedCharacter.Removed) { character.DeselectCharacter(); } else if (serverPos.SelectedCharacter != null) { character.SelectCharacter(serverPos.SelectedCharacter); } } if (localPos.SelectedItem != serverPos.SelectedItem) { if (serverPos.SelectedItem == null || serverPos.SelectedItem.Removed) { character.SelectedConstruction = null; } else if (serverPos.SelectedItem != null) { if (character.SelectedConstruction != serverPos.SelectedItem) { serverPos.SelectedItem.TryInteract(character, true, true); } character.SelectedConstruction = serverPos.SelectedItem; } } if (localPos.Animation != serverPos.Animation) { if (serverPos.Animation == AnimController.Animation.CPR) { character.AnimController.Anim = AnimController.Animation.CPR; } else if (character.AnimController.Anim == AnimController.Animation.CPR) { character.AnimController.Anim = AnimController.Animation.None; } } Hull serverHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(serverPos.Position), character.CurrentHull, serverPos.Position.Y < lowestSubPos); Hull clientHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(localPos.Position), serverHull, localPos.Position.Y < lowestSubPos); if (serverHull != null && clientHull != null && serverHull.Submarine != clientHull.Submarine) { //hull subs don't match => teleport the camera to the other sub character.Submarine = serverHull.Submarine; character.CurrentHull = CurrentHull = serverHull; SetPosition(serverPos.Position); character.MemLocalState.Clear(); } else { Vector2 positionError = serverPos.Position - localPos.Position; float rotationError = serverPos.Rotation.HasValue && localPos.Rotation.HasValue ? serverPos.Rotation.Value - localPos.Rotation.Value : 0.0f; for (int i = localPosIndex; i < character.MemLocalState.Count; i++) { Hull pointHull = Hull.FindHull(ConvertUnits.ToDisplayUnits(character.MemLocalState[i].Position), clientHull, character.MemLocalState[i].Position.Y < lowestSubPos); if (pointHull != clientHull && ((pointHull == null) || (clientHull == null) || (pointHull.Submarine == clientHull.Submarine))) { break; } character.MemLocalState[i].Translate(positionError, rotationError); } float errorMagnitude = positionError.Length(); if (errorMagnitude > 0.5f) { character.MemLocalState.Clear(); SetPosition(serverPos.Position, lerp: true, ignorePlatforms: false); } else if (errorMagnitude > 0.01f) { Collider.TargetPosition = Collider.SimPosition + positionError; Collider.TargetRotation = Collider.Rotation + rotationError; Collider.MoveToTargetPosition(lerp: true); } } } if (character.MemLocalState.Count > 120) { character.MemLocalState.RemoveRange(0, character.MemLocalState.Count - 120); } character.MemState.Clear(); } }
public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch, double deltaTime) { foreach (Submarine sub in Submarine.Loaded) { sub.UpdateTransform(); } GameMain.ParticleManager.UpdateTransforms(); GameMain.LightManager.ObstructVision = Character.Controlled != null && Character.Controlled.ObstructVision; if (Character.Controlled != null) { GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled.CursorWorldPosition); } //------------------------------------------------------------------------ graphics.SetRenderTarget(renderTarget); graphics.Clear(Color.Transparent); //Draw resizeable background structures (= background walls) and wall background sprites //(= the background texture that's revealed when a wall is destroyed) into the background render target //These will be visible through the LOS effect. //Could be drawn with one Submarine.DrawBack call, but we can avoid sorting by depth by doing it like this. spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (s.ResizeVertical || s.ResizeHorizontal) && !s.DrawDamageEffect); Submarine.DrawBack(spriteBatch, false, e => e is Structure s && !(s.ResizeVertical && s.ResizeHorizontal) && s.Prefab.BackgroundSprite != null); spriteBatch.End(); graphics.SetRenderTarget(null); GameMain.LightManager.UpdateLightMap(graphics, spriteBatch, cam, renderTarget); //------------------------------------------------------------------------ graphics.SetRenderTarget(renderTargetBackground); if (Level.Loaded == null) { graphics.Clear(new Color(11, 18, 26, 255)); } else { //graphics.Clear(new Color(255, 255, 255, 255)); Level.Loaded.DrawBack(graphics, spriteBatch, cam); } //draw alpha blended particles that are in water and behind subs #if LINUX || OSX spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); #else spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, cam.Transform); #endif GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); //draw additive particles that are in water and behind subs spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive); spriteBatch.End(); //Draw resizeable background structures (= background walls) and wall background sprites //(= the background texture that's revealed when a wall is destroyed) into the background render target //These will be visible through the LOS effect. //Could be drawn with one Submarine.DrawBack call, but we can avoid sorting by depth by doing it like this. spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.None); spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.End(); //---------------------------------------------------------------------------- //Start drawing to the normal render target (stuff that can't be seen through the LOS effect) graphics.SetRenderTarget(renderTarget); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, null); spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.End(); //Draw the rest of the structures, characters and front structures spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawBack(spriteBatch, false, s => !(s is Structure) || !(s.ResizeVertical && s.ResizeHorizontal)); foreach (Character c in Character.CharacterList) { if (c.AnimController.Limbs.Any(l => l.DeformSprite != null)) { continue; } c.Draw(spriteBatch, Cam); } spriteBatch.End(); //draw characters with deformable limbs last, because they can't be batched into SpriteBatch //pretty hacky way of preventing draw order issues between normal and deformable sprites spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, cam.Transform); foreach (Character c in Character.CharacterList) { if (c.AnimController.Limbs.All(l => l.DeformSprite == null)) { continue; } c.Draw(spriteBatch, Cam); } spriteBatch.End(); //draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater graphics.SetRenderTarget(renderTargetWater); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque); spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White);// waterColor); spriteBatch.End(); //draw alpha blended particles that are inside a sub #if LINUX || OSX spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform); #else spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.DepthRead, null, null, cam.Transform); #endif GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); graphics.SetRenderTarget(renderTarget); //draw alpha blended particles that are not in water #if LINUX || OSX spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform); #else spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, null, DepthStencilState.DepthRead, null, null, cam.Transform); #endif GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); //draw additive particles that are not in water spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive); spriteBatch.End(); graphics.DepthStencilState = DepthStencilState.DepthRead; graphics.SetRenderTarget(renderTargetFinal); WaterRenderer.Instance.ResetBuffers(); Hull.UpdateVertices(graphics, cam, WaterRenderer.Instance); WaterRenderer.Instance.RenderWater(spriteBatch, renderTargetWater, cam); WaterRenderer.Instance.RenderAir(graphics, cam, renderTarget, Cam.ShaderTransform); graphics.DepthStencilState = DepthStencilState.None; spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, null, null, damageEffect, cam.Transform); Submarine.DrawDamageable(spriteBatch, damageEffect, false); spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.AlphaBlend, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawFront(spriteBatch, false, null); spriteBatch.End(); //draw additive particles that are inside a sub spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.Default, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive); foreach (var discharger in Items.Components.ElectricalDischarger.List) { discharger.DrawElectricity(spriteBatch); } spriteBatch.End(); if (GameMain.LightManager.LightingEnabled) { spriteBatch.Begin(SpriteSortMode.Deferred, Lights.CustomBlendStates.Multiplicative, null, DepthStencilState.None, null, null, null); spriteBatch.Draw(GameMain.LightManager.LightMap, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.End(); } spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.AlphaBlend, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform); foreach (Character c in Character.CharacterList) { c.DrawFront(spriteBatch, cam); } if (Level.Loaded != null) { Level.Loaded.DrawFront(spriteBatch, cam); } if (GameMain.DebugDraw && GameMain.GameSession?.EventManager != null) { GameMain.GameSession.EventManager.DebugDraw(spriteBatch); } spriteBatch.End(); if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Character.Controlled != null) { GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"]; GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground); GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture); Color losColor; if (GameMain.LightManager.LosMode == LosMode.Transparent) { //convert the los color to HLS and make sure the luminance of the color is always the same //as the luminance of the ambient light color float r = Character.Controlled?.CharacterHealth == null ? 0.0f : Math.Min(Character.Controlled.CharacterHealth.DamageOverlayTimer * 0.5f, 0.5f); Vector3 ambientLightHls = GameMain.LightManager.AmbientLight.RgbToHLS(); Vector3 losColorHls = Color.Lerp(GameMain.LightManager.AmbientLight, Color.Red, r).RgbToHLS(); losColorHls.Y = ambientLightHls.Y; losColor = ToolBox.HLSToRGB(losColorHls); } else { losColor = Color.Black; } spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.PointClamp, null, null, GameMain.LightManager.LosEffect, null); spriteBatch.Draw(renderTargetBackground, new Rectangle(0, 0, spriteBatch.GraphicsDevice.Viewport.Width, spriteBatch.GraphicsDevice.Viewport.Height), losColor); spriteBatch.End(); } graphics.SetRenderTarget(null); float BlurStrength = 0.0f; float DistortStrength = 0.0f; Vector3 chromaticAberrationStrength = GameMain.Config.ChromaticAberrationEnabled ? new Vector3(-0.02f, -0.01f, 0.0f) : Vector3.Zero; if (Character.Controlled != null) { BlurStrength = Character.Controlled.BlurStrength * 0.005f; DistortStrength = Character.Controlled.DistortStrength; chromaticAberrationStrength -= Vector3.One * Character.Controlled.RadialDistortStrength; chromaticAberrationStrength += new Vector3(-0.03f, -0.015f, 0.0f) * Character.Controlled.ChromaticAberrationStrength; } else { BlurStrength = 0.0f; DistortStrength = 0.0f; } string postProcessTechnique = ""; if (BlurStrength > 0.0f) { postProcessTechnique += "Blur"; postProcessEffect.Parameters["blurDistance"].SetValue(BlurStrength); } if (chromaticAberrationStrength != Vector3.Zero) { postProcessTechnique += "ChromaticAberration"; postProcessEffect.Parameters["chromaticAberrationStrength"].SetValue(chromaticAberrationStrength); } if (DistortStrength > 0.0f) { postProcessTechnique += "Distort"; postProcessEffect.Parameters["distortScale"].SetValue(Vector2.One * DistortStrength); postProcessEffect.Parameters["distortUvOffset"].SetValue(WaterRenderer.Instance.WavePos * 0.001f); #if LINUX || OSX postProcessEffect.Parameters["xTexture"].SetValue(distortTexture); #else postProcessEffect.Parameters["xTexture"].SetValue(renderTargetFinal); #endif } if (string.IsNullOrEmpty(postProcessTechnique)) { spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None); } else { postProcessEffect.CurrentTechnique = postProcessEffect.Techniques[postProcessTechnique]; postProcessEffect.CurrentTechnique.Passes[0].Apply(); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Opaque, SamplerState.PointClamp, DepthStencilState.None, effect: postProcessEffect); } #if LINUX || OSX spriteBatch.Draw(renderTargetFinal, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); #else spriteBatch.Draw(DistortStrength > 0.0f ? distortTexture : renderTargetFinal, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); #endif spriteBatch.End(); }
public override void Draw(SpriteBatch spriteBatch, bool editing, bool back = true) { if (back && Screen.Selected != GameMain.SubEditorScreen) { DrawDecals(spriteBatch); return; } /*if (!Visible) * { * drawRect = * Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); * * GUI.DrawRectangle(spriteBatch, * new Vector2(drawRect.X, -drawRect.Y), * new Vector2(rect.Width, rect.Height), * Color.Black, true, * 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); * }*/ if (!ShowHulls && !GameMain.DebugDraw) { return; } if (!editing && !GameMain.DebugDraw) { return; } if (aiTarget != null) { aiTarget.Draw(spriteBatch); } Rectangle drawRect = Submarine == null ? rect : new Rectangle((int)(Submarine.DrawPosition.X + rect.X), (int)(Submarine.DrawPosition.Y + rect.Y), rect.Width, rect.Height); GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X, -drawRect.Y), new Vector2(rect.Width, rect.Height), Color.Blue, false, (ID % 255) * 0.000001f, (int)Math.Max((1.5f / Screen.Selected.Cam.Zoom), 1.0f)); GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.X, -drawRect.Y, rect.Width, rect.Height), Color.Red * ((100.0f - OxygenPercentage) / 400.0f), true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); if (GameMain.DebugDraw) { GUI.SmallFont.DrawString(spriteBatch, "Pressure: " + ((int)pressure - rect.Y).ToString() + " - Oxygen: " + ((int)OxygenPercentage), new Vector2(drawRect.X + 5, -drawRect.Y + 5), Color.White); GUI.SmallFont.DrawString(spriteBatch, waterVolume + " / " + Volume, new Vector2(drawRect.X + 5, -drawRect.Y + 20), Color.White); GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * Math.Min(waterVolume / Volume, 1.0f))), Color.Cyan, true); if (WaterVolume > Volume) { GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, (int)(100 * (waterVolume - Volume) / MaxCompress)), Color.Red, true); } GUI.DrawRectangle(spriteBatch, new Rectangle(drawRect.Center.X, -drawRect.Y + drawRect.Height / 2, 10, 100), Color.Black); foreach (FireSource fs in FireSources) { Rectangle fireSourceRect = new Rectangle((int)fs.WorldPosition.X, -(int)fs.WorldPosition.Y, (int)fs.Size.X, (int)fs.Size.Y); GUI.DrawRectangle(spriteBatch, fireSourceRect, Color.Orange, false, 0, 5); //GUI.DrawRectangle(spriteBatch, new Rectangle((int)fs.LastExtinguishPos.X, (int)-fs.LastExtinguishPos.Y, 5,5), Color.Yellow, true); } /*GUI.DrawLine(spriteBatch, new Vector2(drawRect.X, -WorldSurface), new Vector2(drawRect.Right, -WorldSurface), Color.Cyan * 0.5f); * for (int i = 0; i < waveY.Length - 1; i++) * { * GUI.DrawLine(spriteBatch, * new Vector2(drawRect.X + WaveWidth * i, -WorldSurface - waveY[i] - 10), * new Vector2(drawRect.X + WaveWidth * (i + 1), -WorldSurface - waveY[i + 1] - 10), Color.Blue * 0.5f); * }*/ } if ((IsSelected || IsHighlighted) && editing) { GUI.DrawRectangle(spriteBatch, new Vector2(drawRect.X + 5, -drawRect.Y + 5), new Vector2(rect.Width - 10, rect.Height - 10), IsHighlighted ? Color.LightBlue * 0.5f : Color.Red * 0.5f, true, 0, (int)Math.Max((1.5f / GameScreen.Selected.Cam.Zoom), 1.0f)); } foreach (MapEntity e in linkedTo) { if (e is Hull) { Hull linkedHull = (Hull)e; Rectangle connectedHullRect = e.Submarine == null ? linkedHull.rect : new Rectangle( (int)(Submarine.DrawPosition.X + linkedHull.WorldPosition.X), (int)(Submarine.DrawPosition.Y + linkedHull.WorldPosition.Y), linkedHull.WorldRect.Width, linkedHull.WorldRect.Height); //center of the hull Rectangle currentHullRect = Submarine == null ? WorldRect : new Rectangle( (int)(Submarine.DrawPosition.X + WorldPosition.X), (int)(Submarine.DrawPosition.Y + WorldPosition.Y), WorldRect.Width, WorldRect.Height); GUI.DrawLine(spriteBatch, new Vector2(currentHullRect.X, -currentHullRect.Y), new Vector2(connectedHullRect.X, -connectedHullRect.Y), Color.Green, width: 2); } } }
public static void DamageCharacters(Vector2 worldPosition, Attack attack, float force, Entity damageSource, Character attacker) { if (attack.Range <= 0.0f) { return; } //long range for the broad distance check, because large characters may still be in range even if their collider isn't float broadRange = Math.Max(attack.Range * 10.0f, 10000.0f); foreach (Character c in Character.CharacterList) { if (!c.Enabled || Math.Abs(c.WorldPosition.X - worldPosition.X) > broadRange || Math.Abs(c.WorldPosition.Y - worldPosition.Y) > broadRange) { continue; } Vector2 explosionPos = worldPosition; if (c.Submarine != null) { explosionPos -= c.Submarine.Position; } Hull hull = Hull.FindHull(ConvertUnits.ToDisplayUnits(explosionPos), null, false); bool underWater = hull == null || explosionPos.Y < hull.Surface; explosionPos = ConvertUnits.ToSimUnits(explosionPos); Dictionary <Limb, float> distFactors = new Dictionary <Limb, float>(); foreach (Limb limb in c.AnimController.Limbs) { float dist = Vector2.Distance(limb.WorldPosition, worldPosition); //calculate distance from the "outer surface" of the physics body //doesn't take the rotation of the limb into account, but should be accurate enough for this purpose float limbRadius = Math.Max(Math.Max(limb.body.width * 0.5f, limb.body.height * 0.5f), limb.body.radius); dist = Math.Max(0.0f, dist - ConvertUnits.ToDisplayUnits(limbRadius)); if (dist > attack.Range) { continue; } float distFactor = 1.0f - dist / attack.Range; //solid obstacles between the explosion and the limb reduce the effect of the explosion by 90% if (Submarine.CheckVisibility(limb.SimPosition, explosionPos) != null) { distFactor *= 0.1f; } distFactors.Add(limb, distFactor); List <Affliction> modifiedAfflictions = new List <Affliction>(); foreach (Affliction affliction in attack.Afflictions.Keys) { modifiedAfflictions.Add(affliction.CreateMultiplied(distFactor / c.AnimController.Limbs.Length)); } c.LastDamageSource = damageSource; if (attacker == null) { if (damageSource is Item item) { attacker = item.GetComponent <Projectile>()?.User; if (attacker == null) { attacker = item.GetComponent <MeleeWeapon>()?.User; } } } //use a position slightly from the limb's position towards the explosion //ensures that the attack hits the correct limb and that the direction of the hit can be determined correctly in the AddDamage methods Vector2 hitPos = limb.WorldPosition + (worldPosition - limb.WorldPosition) / dist * 0.01f; c.AddDamage(hitPos, modifiedAfflictions, attack.Stun * distFactor, false, attacker: attacker); if (attack.StatusEffects != null && attack.StatusEffects.Any()) { attack.SetUser(attacker); var statusEffectTargets = new List <ISerializableEntity>() { c, limb }; foreach (StatusEffect statusEffect in attack.StatusEffects) { statusEffect.Apply(ActionType.OnUse, 1.0f, damageSource, statusEffectTargets); statusEffect.Apply(ActionType.Always, 1.0f, damageSource, statusEffectTargets); statusEffect.Apply(underWater ? ActionType.InWater : ActionType.NotInWater, 1.0f, damageSource, statusEffectTargets); } } if (limb.WorldPosition != worldPosition && !MathUtils.NearlyEqual(force, 0.0f)) { Vector2 limbDiff = Vector2.Normalize(limb.WorldPosition - worldPosition); if (!MathUtils.IsValid(limbDiff)) { limbDiff = Rand.Vector(1.0f); } Vector2 impulse = limbDiff * distFactor * force; Vector2 impulsePoint = limb.SimPosition - limbDiff * limbRadius; limb.body.ApplyLinearImpulse(impulse, impulsePoint, maxVelocity: NetConfig.MaxPhysicsBodyVelocity); } } //sever joints if (c.IsDead && attack.SeverLimbsProbability > 0.0f) { foreach (Limb limb in c.AnimController.Limbs) { if (!distFactors.ContainsKey(limb)) { continue; } if (Rand.Range(0.0f, 1.0f) < attack.SeverLimbsProbability * distFactors[limb]) { c.TrySeverLimbJoints(limb, 1.0f); } } } } }
protected void Apply(float deltaTime, Entity entity, List <IPropertyObject> targets) { #if CLIENT if (sound != null) { sound.Play(1.0f, 1000.0f, entity.WorldPosition); } #endif if (useItem) { foreach (Item item in targets.FindAll(t => t is Item).Cast <Item>()) { item.Use(deltaTime, targets.FirstOrDefault(t => t is Character) as Character); } } foreach (IPropertyObject target in targets) { for (int i = 0; i < propertyNames.Length; i++) { ObjectProperty property; if (!target.ObjectProperties.TryGetValue(propertyNames[i], out property)) { continue; } if (duration > 0.0f) { CoroutineManager.StartCoroutine( ApplyToPropertyOverDuration(duration, property, propertyEffects[i]), "statuseffect"); } else { ApplyToProperty(property, propertyEffects[i], deltaTime); } } } if (explosion != null) { explosion.Explode(entity.WorldPosition); } Hull hull = null; if (entity is Character) { hull = ((Character)entity).AnimController.CurrentHull; } else if (entity is Item) { hull = ((Item)entity).CurrentHull; } if (FireSize > 0.0f) { var fire = new FireSource(entity.WorldPosition, hull); fire.Size = new Vector2(FireSize, fire.Size.Y); } #if CLIENT foreach (ParticleEmitterPrefab emitter in particleEmitters) { emitter.Emit(entity.WorldPosition, hull); } #endif }
public void DrawMap(GraphicsDevice graphics, SpriteBatch spriteBatch, double deltaTime) { foreach (Submarine sub in Submarine.Loaded) { sub.UpdateTransform(); } GameMain.ParticleManager.UpdateTransforms(); GameMain.LightManager.ObstructVision = Character.Controlled != null && Character.Controlled.ObstructVision && (Character.Controlled.ViewTarget == Character.Controlled || Character.Controlled.ViewTarget == null); GameMain.LightManager.UpdateObstructVision(graphics, spriteBatch, cam, Character.Controlled?.CursorWorldPosition ?? Vector2.Zero); //------------------------------------------------------------------------ graphics.SetRenderTarget(renderTarget); graphics.Clear(Color.Transparent); //Draw background structures and wall background sprites //(= the background texture that's revealed when a wall is destroyed) into the background render target //These will be visible through the LOS effect. spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawBack(spriteBatch, false, e => e is Structure s && (e.SpriteDepth >= 0.9f || s.Prefab.BackgroundSprite != null)); Submarine.DrawPaintedColors(spriteBatch, false); spriteBatch.End(); graphics.SetRenderTarget(null); GameMain.LightManager.RenderLightMap(graphics, spriteBatch, cam, renderTarget); //------------------------------------------------------------------------ graphics.SetRenderTarget(renderTargetBackground); if (Level.Loaded == null) { graphics.Clear(new Color(11, 18, 26, 255)); } else { //graphics.Clear(new Color(255, 255, 255, 255)); Level.Loaded.DrawBack(graphics, spriteBatch, cam); } //draw alpha blended particles that are in water and behind subs #if LINUX || OSX spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); #else spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); #endif GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); //draw additive particles that are in water and behind subs spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, true, false, Particles.ParticleBlendState.Additive); spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None); spriteBatch.Draw(renderTarget, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.White); spriteBatch.End(); //---------------------------------------------------------------------------- //Start drawing to the normal render target (stuff that can't be seen through the LOS effect) graphics.SetRenderTarget(renderTarget); graphics.BlendState = BlendState.NonPremultiplied; graphics.SamplerStates[0] = SamplerState.LinearWrap; Quad.UseBasicEffect(renderTargetBackground); Quad.Render(); //Draw the rest of the structures, characters and front structures spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawBack(spriteBatch, false, e => !(e is Structure) || e.SpriteDepth < 0.9f); foreach (Character c in Character.CharacterList) { if (!c.IsVisible || c.AnimController.Limbs.Any(l => l.DeformSprite != null)) { continue; } c.Draw(spriteBatch, Cam); } spriteBatch.End(); //draw characters with deformable limbs last, because they can't be batched into SpriteBatch //pretty hacky way of preventing draw order issues between normal and deformable sprites spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); //backwards order to render the most recently spawned characters in front (characters spawned later have a larger sprite depth) for (int i = Character.CharacterList.Count - 1; i >= 0; i--) { Character c = Character.CharacterList[i]; if (!c.IsVisible || c.AnimController.Limbs.All(l => l.DeformSprite == null)) { continue; } c.Draw(spriteBatch, Cam); } spriteBatch.End(); Level.Loaded?.DrawFront(spriteBatch, cam); //draw the rendertarget and particles that are only supposed to be drawn in water into renderTargetWater graphics.SetRenderTarget(renderTargetWater); graphics.BlendState = BlendState.Opaque; graphics.SamplerStates[0] = SamplerState.LinearWrap; Quad.UseBasicEffect(renderTarget); Quad.Render(); //draw alpha blended particles that are inside a sub spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); graphics.SetRenderTarget(renderTarget); //draw alpha blended particles that are not in water spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, null, DepthStencilState.DepthRead, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.AlphaBlend); spriteBatch.End(); //draw additive particles that are not in water spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.None, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, false, null, Particles.ParticleBlendState.Additive); spriteBatch.End(); graphics.DepthStencilState = DepthStencilState.DepthRead; graphics.SetRenderTarget(renderTargetFinal); WaterRenderer.Instance.ResetBuffers(); Hull.UpdateVertices(cam, WaterRenderer.Instance); WaterRenderer.Instance.RenderWater(spriteBatch, renderTargetWater, cam); WaterRenderer.Instance.RenderAir(graphics, cam, renderTarget, Cam.ShaderTransform); graphics.DepthStencilState = DepthStencilState.None; spriteBatch.Begin(SpriteSortMode.Immediate, BlendState.NonPremultiplied, SamplerState.LinearWrap, null, null, damageEffect, cam.Transform); Submarine.DrawDamageable(spriteBatch, damageEffect, false); spriteBatch.End(); spriteBatch.Begin(SpriteSortMode.BackToFront, BlendState.NonPremultiplied, null, DepthStencilState.None, null, null, cam.Transform); Submarine.DrawFront(spriteBatch, false, null); spriteBatch.End(); //draw additive particles that are inside a sub spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.Additive, null, DepthStencilState.Default, null, null, cam.Transform); GameMain.ParticleManager.Draw(spriteBatch, true, true, Particles.ParticleBlendState.Additive); foreach (var discharger in Items.Components.ElectricalDischarger.List) { discharger.DrawElectricity(spriteBatch); } spriteBatch.End(); if (GameMain.LightManager.LightingEnabled) { graphics.DepthStencilState = DepthStencilState.None; graphics.SamplerStates[0] = SamplerState.LinearWrap; graphics.BlendState = Lights.CustomBlendStates.Multiplicative; Quad.UseBasicEffect(GameMain.LightManager.LightMap); Quad.Render(); } spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, SamplerState.LinearWrap, DepthStencilState.None, null, null, cam.Transform); foreach (Character c in Character.CharacterList) { c.DrawFront(spriteBatch, cam); } Level.Loaded?.DrawDebugOverlay(spriteBatch, cam); if (GameMain.DebugDraw) { MapEntity.mapEntityList.ForEach(me => me.AiTarget?.Draw(spriteBatch)); Character.CharacterList.ForEach(c => c.AiTarget?.Draw(spriteBatch)); if (GameMain.GameSession?.EventManager != null) { GameMain.GameSession.EventManager.DebugDraw(spriteBatch); } } spriteBatch.End(); if (GameMain.LightManager.LosEnabled && GameMain.LightManager.LosMode != LosMode.None && Lights.LightManager.ViewTarget != null) { GameMain.LightManager.LosEffect.CurrentTechnique = GameMain.LightManager.LosEffect.Techniques["LosShader"]; GameMain.LightManager.LosEffect.Parameters["xTexture"].SetValue(renderTargetBackground); GameMain.LightManager.LosEffect.Parameters["xLosTexture"].SetValue(GameMain.LightManager.LosTexture); GameMain.LightManager.LosEffect.Parameters["xLosAlpha"].SetValue(GameMain.LightManager.LosAlpha); Color losColor; if (GameMain.LightManager.LosMode == LosMode.Transparent) { //convert the los color to HLS and make sure the luminance of the color is always the same //as the luminance of the ambient light color float r = Character.Controlled?.CharacterHealth == null ? 0.0f : Math.Min(Character.Controlled.CharacterHealth.DamageOverlayTimer * 0.5f, 0.5f); Vector3 ambientLightHls = GameMain.LightManager.AmbientLight.RgbToHLS(); Vector3 losColorHls = Color.Lerp(GameMain.LightManager.AmbientLight, Color.Red, r).RgbToHLS(); losColorHls.Y = ambientLightHls.Y; losColor = ToolBox.HLSToRGB(losColorHls); } else { losColor = Color.Black; } GameMain.LightManager.LosEffect.Parameters["xColor"].SetValue(losColor.ToVector4()); graphics.BlendState = BlendState.NonPremultiplied; graphics.SamplerStates[0] = SamplerState.PointClamp; GameMain.LightManager.LosEffect.CurrentTechnique.Passes[0].Apply(); Quad.Render(); } float grainStrength = Character.Controlled?.GrainStrength ?? 0; if (grainStrength > 0) { Rectangle screenRect = new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight); spriteBatch.Begin(SpriteSortMode.Deferred, BlendState.NonPremultiplied, effect: GrainEffect); GUI.DrawRectangle(spriteBatch, screenRect, Color.White * grainStrength, isFilled: true); GrainEffect.Parameters["seed"].SetValue(Rand.Range(0f, 1f, Rand.RandSync.Unsynced)); spriteBatch.End(); } graphics.SetRenderTarget(null); float BlurStrength = 0.0f; float DistortStrength = 0.0f; Vector3 chromaticAberrationStrength = GameMain.Config.ChromaticAberrationEnabled ? new Vector3(-0.02f, -0.01f, 0.0f) : Vector3.Zero; if (Character.Controlled != null) { BlurStrength = Character.Controlled.BlurStrength * 0.005f; DistortStrength = Character.Controlled.DistortStrength; if (GameMain.Config.EnableRadialDistortion) { chromaticAberrationStrength -= Vector3.One * Character.Controlled.RadialDistortStrength; } chromaticAberrationStrength += new Vector3(-0.03f, -0.015f, 0.0f) * Character.Controlled.ChromaticAberrationStrength; } else { BlurStrength = 0.0f; DistortStrength = 0.0f; } string postProcessTechnique = ""; if (BlurStrength > 0.0f) { postProcessTechnique += "Blur"; PostProcessEffect.Parameters["blurDistance"].SetValue(BlurStrength); } if (chromaticAberrationStrength != Vector3.Zero) { postProcessTechnique += "ChromaticAberration"; PostProcessEffect.Parameters["chromaticAberrationStrength"].SetValue(chromaticAberrationStrength); } if (DistortStrength > 0.0f) { postProcessTechnique += "Distort"; PostProcessEffect.Parameters["distortScale"].SetValue(Vector2.One * DistortStrength); PostProcessEffect.Parameters["distortUvOffset"].SetValue(WaterRenderer.Instance.WavePos * 0.001f); } graphics.BlendState = BlendState.Opaque; graphics.SamplerStates[0] = SamplerState.LinearClamp; graphics.DepthStencilState = DepthStencilState.None; if (string.IsNullOrEmpty(postProcessTechnique)) { Quad.UseBasicEffect(renderTargetFinal); } else { PostProcessEffect.Parameters["MatrixTransform"].SetValue(Matrix.Identity); PostProcessEffect.Parameters["xTexture"].SetValue(renderTargetFinal); PostProcessEffect.CurrentTechnique = PostProcessEffect.Techniques[postProcessTechnique]; PostProcessEffect.CurrentTechnique.Passes[0].Apply(); } Quad.Render(); if (fadeToBlackState > 0.0f) { spriteBatch.Begin(SpriteSortMode.Deferred); GUI.DrawRectangle(spriteBatch, new Rectangle(0, 0, GameMain.GraphicsWidth, GameMain.GraphicsHeight), Color.Lerp(Color.TransparentBlack, Color.Black, fadeToBlackState), isFilled: true); spriteBatch.End(); } }
public static void CreateItems(List <PurchasedItem> itemsToSpawn) { if (itemsToSpawn.Count == 0) { return; } WayPoint wp = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub); if (wp == null) { DebugConsole.ThrowError("The submarine must have a waypoint marked as Cargo for bought items to be placed correctly!"); return; } Hull cargoRoom = Hull.FindHull(wp.WorldPosition); if (cargoRoom == null) { DebugConsole.ThrowError("A waypoint marked as Cargo must be placed inside a room!"); return; } #if CLIENT new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.DisplayName)); #endif Dictionary <ItemContainer, int> availableContainers = new Dictionary <ItemContainer, int>(); ItemPrefab containerPrefab = null; foreach (PurchasedItem pi in itemsToSpawn) { Vector2 position = new Vector2( Rand.Range(cargoRoom.Rect.X + 20, cargoRoom.Rect.Right - 20), cargoRoom.Rect.Y - cargoRoom.Rect.Height + pi.ItemPrefab.Size.Y / 2); ItemContainer itemContainer = null; if (!string.IsNullOrEmpty(pi.ItemPrefab.CargoContainerIdentifier)) { itemContainer = availableContainers.Keys.ToList().Find(ac => ac.Item.Prefab.Identifier == pi.ItemPrefab.CargoContainerIdentifier || ac.Item.Prefab.Tags.Contains(pi.ItemPrefab.CargoContainerIdentifier.ToLowerInvariant())); if (itemContainer == null) { containerPrefab = MapEntityPrefab.List.Find(ep => ep.Identifier == pi.ItemPrefab.CargoContainerIdentifier || (ep.Tags != null && ep.Tags.Contains(pi.ItemPrefab.CargoContainerIdentifier.ToLowerInvariant()))) as ItemPrefab; if (containerPrefab == null) { DebugConsole.ThrowError("Cargo spawning failed - could not find the item prefab for container \"" + containerPrefab.Name + "\"!"); continue; } Item containerItem = new Item(containerPrefab, position, wp.Submarine); itemContainer = containerItem.GetComponent <ItemContainer>(); if (itemContainer == null) { DebugConsole.ThrowError("Cargo spawning failed - container \"" + containerItem.Name + "\" does not have an ItemContainer component!"); continue; } availableContainers.Add(itemContainer, itemContainer.Capacity); #if SERVER if (GameMain.Server != null) { Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false); } #endif } } for (int i = 0; i < pi.Quantity; i++) { if (itemContainer == null) { //no container, place at the waypoint #if SERVER if (GameMain.Server != null) { Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, position, wp.Submarine); } else { #endif new Item(pi.ItemPrefab, position, wp.Submarine); #if SERVER } #endif continue; } //if the intial container has been removed due to it running out of space, add a new container //of the same type and begin filling it if (!availableContainers.ContainsKey(itemContainer)) { Item containerItemOverFlow = new Item(containerPrefab, position, wp.Submarine); itemContainer = containerItemOverFlow.GetComponent <ItemContainer>(); availableContainers.Add(itemContainer, itemContainer.Capacity); #if SERVER if (GameMain.Server != null) { Entity.Spawner.CreateNetworkEvent(itemContainer.Item, false); } #endif } //place in the container #if SERVER if (GameMain.Server != null) { Entity.Spawner.AddToSpawnQueue(pi.ItemPrefab, itemContainer.Inventory); } else { #endif var item = new Item(pi.ItemPrefab, position, wp.Submarine); itemContainer.Inventory.TryPutItem(item, null); #if SERVER } #endif //reduce the number of available slots in the container //if there is a container if (availableContainers.ContainsKey(itemContainer)) { availableContainers[itemContainer]--; } if (availableContainers.ContainsKey(itemContainer) && availableContainers[itemContainer] <= 0) { availableContainers.Remove(itemContainer); } } } itemsToSpawn.Clear(); }