private bool CanStartEventSet(EventSet eventSet) { ISpatialEntity refEntity = GetRefEntity(); float distFromStart = (float)Math.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(level.StartExitPosition.ToPoint(), level.StartPosition.ToPoint(), refEntity.WorldPosition.ToPoint())); float distFromEnd = (float)Math.Sqrt(MathUtils.LineSegmentToPointDistanceSquared(level.EndExitPosition.ToPoint(), level.EndPosition.ToPoint(), refEntity.WorldPosition.ToPoint())); //don't create new events if within 50 meters of the start/end of the level if (!eventSet.AllowAtStart) { if (distanceTraveled <= 0.0f || distFromStart * Physics.DisplayToRealWorldRatio < 50.0f || distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f) { return(false); } } if (eventSet.DelayWhenCrewAway) { if ((isCrewAway && crewAwayDuration < settings.FreezeDurationWhenCrewAway) || crewAwayResetTimer > 0.0f) { return(false); } } if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) && roundDuration < eventSet.MinMissionTime) { return(false); } if (CurrentIntensity < eventSet.MinIntensity || CurrentIntensity > eventSet.MaxIntensity) { return(false); } return(true); }
protected override void StartMissionSpecific(Level level) { if (items.Any()) { #if DEBUG throw new Exception($"items.Count > 0 ({items.Count})"); #else DebugConsole.AddWarning("Item list was not empty at the start of a nest mission. The mission instance may not have been ended correctly on previous rounds."); items.Clear(); #endif } if (!IsClient) { //ruin/cave/wreck items are allowed to spawn close to the sub float minDistance = spawnPositionType == Level.PositionType.Ruin || spawnPositionType == Level.PositionType.Cave || spawnPositionType == Level.PositionType.Wreck ? 0.0f : Level.Loaded.Size.X * 0.3f; nestPosition = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f); List <GraphEdge> spawnEdges = new List <GraphEdge>(); if (spawnPositionType == Level.PositionType.Cave) { Level.Cave closestCave = null; float closestCaveDist = float.PositiveInfinity; foreach (var cave in Level.Loaded.Caves) { float dist = Vector2.DistanceSquared(nestPosition, cave.Area.Center.ToVector2()); if (dist < closestCaveDist) { closestCave = cave; closestCaveDist = dist; } } if (closestCave != null) { closestCave.DisplayOnSonar = true; SpawnNestObjects(level, closestCave); #if SERVER selectedCave = closestCave; #endif } var nearbyCells = Level.Loaded.GetCells(nestPosition, searchDepth: 3); if (nearbyCells.Any()) { List <GraphEdge> validEdges = new List <GraphEdge>(); foreach (var edge in nearbyCells.SelectMany(c => c.Edges)) { if (!edge.NextToCave || !edge.IsSolid) { continue; } if (Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(edge.Center + edge.GetNormal(edge.Cell1 ?? edge.Cell2) * 100.0f))) { continue; } validEdges.Add(edge); } if (validEdges.Any()) { spawnEdges.AddRange(validEdges.Where(e => MathUtils.LineSegmentToPointDistanceSquared(e.Point1.ToPoint(), e.Point2.ToPoint(), nestPosition.ToPoint()) < itemSpawnRadius * itemSpawnRadius).Distinct()); } //no valid edges found close enough to the nest position, find the closest one if (!spawnEdges.Any()) { GraphEdge closestEdge = null; float closestDistSqr = float.PositiveInfinity; foreach (var edge in nearbyCells.SelectMany(c => c.Edges)) { if (!edge.NextToCave || !edge.IsSolid) { continue; } float dist = Vector2.DistanceSquared(edge.Center, nestPosition); if (dist < closestDistSqr) { closestEdge = edge; closestDistSqr = dist; } } if (closestEdge != null) { spawnEdges.Add(closestEdge); itemSpawnRadius = Math.Max(itemSpawnRadius, (float)Math.Sqrt(closestDistSqr) * 1.5f); } } } } foreach (XElement subElement in itemConfig.Elements()) { string itemIdentifier = subElement.GetAttributeString("identifier", ""); if (!(MapEntityPrefab.Find(null, itemIdentifier) is ItemPrefab itemPrefab)) { DebugConsole.ThrowError("Couldn't spawn item for nest mission: item prefab \"" + itemIdentifier + "\" not found"); continue; } Vector2 spawnPos = nestPosition; float rotation = 0.0f; if (spawnEdges.Any()) { var edge = spawnEdges.GetRandom(Rand.RandSync.Server); spawnPos = Vector2.Lerp(edge.Point1, edge.Point2, Rand.Range(0.1f, 0.9f, Rand.RandSync.Server)); Vector2 normal = Vector2.UnitY; if (edge.Cell1 != null && edge.Cell1.CellType == CellType.Solid) { normal = edge.GetNormal(edge.Cell1); } else if (edge.Cell2 != null && edge.Cell2.CellType == CellType.Solid) { normal = edge.GetNormal(edge.Cell2); } spawnPos += normal * 10.0f; rotation = MathUtils.VectorToAngle(normal) - MathHelper.PiOver2; } var item = new Item(itemPrefab, spawnPos, null); item.body.FarseerBody.BodyType = BodyType.Kinematic; item.body.SetTransformIgnoreContacts(item.body.SimPosition, rotation); item.FindHull(); items.Add(item); var statusEffectElement = subElement.Element("StatusEffectOnApproach") ?? subElement.Element("statuseffectonapproach"); if (statusEffectElement != null) { statusEffectOnApproach.Add(item, StatusEffect.Load(statusEffectElement, Prefab.Identifier)); } } } }
/// <summary> /// Returns a dictionary where the keys are the structures that took damage and the values are the amount of damage taken /// </summary> public static Dictionary <Structure, float> RangedStructureDamage(Vector2 worldPosition, float worldRange, float damage, float levelWallDamage, Character attacker = null) { List <Structure> structureList = new List <Structure>(); float dist = 600.0f; foreach (MapEntity entity in MapEntity.mapEntityList) { if (!(entity is Structure structure)) { continue; } if (structure.HasBody && !structure.IsPlatform && Vector2.Distance(structure.WorldPosition, worldPosition) < dist * 3.0f) { structureList.Add(structure); } } Dictionary <Structure, float> damagedStructures = new Dictionary <Structure, float>(); foreach (Structure structure in structureList) { for (int i = 0; i < structure.SectionCount; i++) { float distFactor = 1.0f - (Vector2.Distance(structure.SectionPosition(i, true), worldPosition) / worldRange); if (distFactor <= 0.0f) { continue; } structure.AddDamage(i, damage * distFactor, attacker); if (damagedStructures.ContainsKey(structure)) { damagedStructures[structure] += damage * distFactor; } else { damagedStructures.Add(structure, damage * distFactor); } } } if (Level.Loaded != null && !MathUtils.NearlyEqual(levelWallDamage, 0.0f)) { for (int i = Level.Loaded.ExtraWalls.Count - 1; i >= 0; i--) { if (!(Level.Loaded.ExtraWalls[i] is DestructibleLevelWall destructibleWall)) { continue; } foreach (var cell in destructibleWall.Cells) { if (cell.IsPointInside(worldPosition)) { destructibleWall.AddDamage(levelWallDamage, worldPosition); continue; } foreach (var edge in cell.Edges) { if (MathUtils.LineSegmentToPointDistanceSquared((edge.Point1 + cell.Translation).ToPoint(), (edge.Point2 + cell.Translation).ToPoint(), worldPosition.ToPoint()) < worldRange * worldRange) { destructibleWall.AddDamage(levelWallDamage, worldPosition); break; } } } } } return(damagedStructures); }
public void PlaceNestObjects(Level level, Level.Cave cave, Vector2 nestPosition, float nestRadius, int objectAmount) { Rand.SetSyncedSeed(ToolBox.StringToInt(level.Seed)); var availablePrefabs = new List <LevelObjectPrefab>(LevelObjectPrefab.List.FindAll(p => p.SpawnPos.HasFlag(LevelObjectPrefab.SpawnPosType.NestWall))); Dictionary <LevelObjectPrefab, List <SpawnPosition> > suitableSpawnPositions = new Dictionary <LevelObjectPrefab, List <SpawnPosition> >(); Dictionary <LevelObjectPrefab, List <float> > spawnPositionWeights = new Dictionary <LevelObjectPrefab, List <float> >(); List <SpawnPosition> availableSpawnPositions = new List <SpawnPosition>(); var caveCells = cave.Tunnels.SelectMany(t => t.Cells); List <VoronoiCell> caveWallCells = new List <VoronoiCell>(); foreach (var edge in caveCells.SelectMany(c => c.Edges)) { if (!edge.NextToCave) { continue; } if (MathUtils.LineSegmentToPointDistanceSquared(edge.Point1.ToPoint(), edge.Point2.ToPoint(), nestPosition.ToPoint()) > nestRadius * nestRadius) { continue; } if (edge.Cell1?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell1); } if (edge.Cell2?.CellType == CellType.Solid) { caveWallCells.Add(edge.Cell2); } } availableSpawnPositions.AddRange(GetAvailableSpawnPositions(caveWallCells.Distinct(), LevelObjectPrefab.SpawnPosType.CaveWall)); for (int i = 0; i < objectAmount; i++) { //get a random prefab and find a place to spawn it LevelObjectPrefab prefab = GetRandomPrefab(cave.CaveGenerationParams, availablePrefabs, requireCaveSpecificOverride: false); if (prefab == null) { continue; } if (!suitableSpawnPositions.ContainsKey(prefab)) { suitableSpawnPositions.Add(prefab, availableSpawnPositions.Where(sp => sp.Length >= prefab.MinSurfaceWidth && (sp.Alignment == Alignment.Any || prefab.Alignment.HasFlag(sp.Alignment))).ToList()); spawnPositionWeights.Add(prefab, suitableSpawnPositions[prefab].Select(sp => sp.GetSpawnProbability(prefab)).ToList()); } SpawnPosition spawnPosition = ToolBox.SelectWeightedRandom(suitableSpawnPositions[prefab], spawnPositionWeights[prefab], Rand.RandSync.Server); if (spawnPosition == null && prefab.SpawnPos != LevelObjectPrefab.SpawnPosType.None) { continue; } PlaceObject(prefab, spawnPosition, level); if (objects.Count(o => o.Prefab == prefab) >= prefab.MaxCount) { availablePrefabs.Remove(prefab); } } }