(f0d812055) v0.9.9.0
This commit is contained in:
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -53,7 +51,7 @@ namespace Barotrauma
|
||||
public override void Init(bool affectSubImmediately)
|
||||
{
|
||||
spawnPos = Level.Loaded.GetRandomItemPos(
|
||||
(Rand.Range(0.0f, 1.0f, Rand.RandSync.Server) < 0.5f) ? Level.PositionType.MainPath : Level.PositionType.Cave | Level.PositionType.Ruin,
|
||||
(Rand.Value(Rand.RandSync.Server) < 0.5f) ? Level.PositionType.MainPath : Level.PositionType.Cave | Level.PositionType.Ruin,
|
||||
500.0f, 10000.0f, 30.0f);
|
||||
|
||||
spawnPending = true;
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -219,13 +218,44 @@ namespace Barotrauma
|
||||
|
||||
private void CreateEvents(ScriptedEventSet eventSet)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
int applyCount = 1;
|
||||
if (eventSet.PerRuin)
|
||||
{
|
||||
if (eventSet.EventPrefabs.Count > 0)
|
||||
applyCount = Level.Loaded.Ruins.Count();
|
||||
}
|
||||
else if (eventSet.PerWreck)
|
||||
{
|
||||
applyCount = Submarine.Loaded.Count(s => s.Info.IsWreck && (s.WreckAI == null || !s.WreckAI.IsAlive));
|
||||
}
|
||||
for (int i = 0; i < applyCount; i++)
|
||||
{
|
||||
if (eventSet.ChooseRandom)
|
||||
{
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(eventSet.EventPrefabs, eventSet.EventPrefabs.Select(e => e.Commonness).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
if (eventSet.EventPrefabs.Count > 0)
|
||||
{
|
||||
MTRandom rand = new MTRandom(ToolBox.StringToInt(level.Seed));
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(eventSet.EventPrefabs, eventSet.EventPrefabs.Select(e => e.Commonness).ToList(), rand);
|
||||
if (eventPrefab != null)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
selectedEvents.Add(eventSet, new List<ScriptedEvent>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
}
|
||||
if (eventSet.ChildSets.Count > 0)
|
||||
{
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
|
||||
if (newEventSet != null) { CreateEvents(newEventSet); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ScriptedEventPrefab eventPrefab in eventSet.EventPrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
@@ -236,30 +266,11 @@ namespace Barotrauma
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
}
|
||||
if (eventSet.ChildSets.Count > 0)
|
||||
{
|
||||
var newEventSet = SelectRandomEvents(eventSet.ChildSets);
|
||||
if (newEventSet != null) { CreateEvents(newEventSet); }
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (ScriptedEventPrefab eventPrefab in eventSet.EventPrefabs)
|
||||
{
|
||||
var newEvent = eventPrefab.CreateInstance();
|
||||
newEvent.Init(true);
|
||||
DebugConsole.Log("Initialized event " + newEvent.ToString());
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
selectedEvents.Add(eventSet, new List<ScriptedEvent>());
|
||||
}
|
||||
selectedEvents[eventSet].Add(newEvent);
|
||||
}
|
||||
|
||||
foreach (ScriptedEventSet childEventSet in eventSet.ChildSets)
|
||||
{
|
||||
CreateEvents(childEventSet);
|
||||
foreach (ScriptedEventSet childEventSet in eventSet.ChildSets)
|
||||
{
|
||||
CreateEvents(childEventSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -296,11 +307,14 @@ namespace Barotrauma
|
||||
0.0f, 1.0f);
|
||||
|
||||
//don't create new events if within 50 meters of the start/end of the level
|
||||
if (distanceTraveled <= 0.0f ||
|
||||
distFromStart * Physics.DisplayToRealWorldRatio < 50.0f ||
|
||||
distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f)
|
||||
if (!eventSet.AllowAtStart)
|
||||
{
|
||||
return false;
|
||||
if (distanceTraveled <= 0.0f ||
|
||||
distFromStart * Physics.DisplayToRealWorldRatio < 50.0f ||
|
||||
distFromEnd * Physics.DisplayToRealWorldRatio < 50.0f)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if ((Submarine.MainSub == null || distanceTraveled < eventSet.MinDistanceTraveled) &&
|
||||
@@ -368,17 +382,15 @@ namespace Barotrauma
|
||||
|
||||
pendingEventSets.RemoveAt(i);
|
||||
|
||||
if (!selectedEvents.ContainsKey(eventSet))
|
||||
if (selectedEvents.ContainsKey(eventSet))
|
||||
{
|
||||
//no events selected from this event set
|
||||
continue;
|
||||
//start events in this set
|
||||
foreach (ScriptedEvent scriptedEvent in selectedEvents[eventSet])
|
||||
{
|
||||
activeEvents.Add(scriptedEvent);
|
||||
}
|
||||
}
|
||||
|
||||
//start events in this set
|
||||
foreach (ScriptedEvent scriptedEvent in selectedEvents[eventSet])
|
||||
{
|
||||
activeEvents.Add(scriptedEvent);
|
||||
}
|
||||
//add child event sets to pending
|
||||
foreach (ScriptedEventSet childEventSet in eventSet.ChildSets)
|
||||
{
|
||||
@@ -431,7 +443,7 @@ namespace Barotrauma
|
||||
enemyDanger = 0.0f;
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.IsDead || character.IsUnconscious || !character.Enabled) continue;
|
||||
if (character.IsDead || character.IsIncapacitated || !character.Enabled) continue;
|
||||
|
||||
EnemyAIController enemyAI = character.AIController as EnemyAIController;
|
||||
if (enemyAI == null) continue;
|
||||
@@ -458,7 +470,7 @@ namespace Barotrauma
|
||||
int hullCount = 0;
|
||||
foreach (Hull hull in Hull.hullList)
|
||||
{
|
||||
if (hull.Submarine == null || hull.Submarine.IsOutpost) { continue; }
|
||||
if (hull.Submarine == null || hull.Submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; }
|
||||
hullCount++;
|
||||
foreach (Gap gap in hull.ConnectedGaps)
|
||||
{
|
||||
|
||||
@@ -69,7 +69,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
WayPoint cargoSpawnPos = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub, true);
|
||||
WayPoint cargoSpawnPos = WayPoint.GetRandom(SpawnType.Cargo, null, Submarine.MainSub, useSyncedRand: true);
|
||||
if (cargoSpawnPos == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn items for cargo mission, cargo spawnpoint not found");
|
||||
|
||||
@@ -176,11 +176,11 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Pair<string, string> allowedLocationType in AllowedLocationTypes)
|
||||
{
|
||||
if (allowedLocationType.First.ToLowerInvariant() == "any" ||
|
||||
allowedLocationType.First.ToLowerInvariant() == from.Type.Identifier.ToLowerInvariant())
|
||||
if (allowedLocationType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowedLocationType.Second.ToLowerInvariant() == "any" ||
|
||||
allowedLocationType.Second.ToLowerInvariant() == to.Type.Identifier.ToLowerInvariant())
|
||||
if (allowedLocationType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
using FarseerPhysics;
|
||||
using Barotrauma.Extensions;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -13,6 +15,17 @@ namespace Barotrauma
|
||||
private Item item;
|
||||
|
||||
private readonly Level.PositionType spawnPositionType;
|
||||
|
||||
private readonly string containerTag;
|
||||
|
||||
private readonly string existingItemTag;
|
||||
|
||||
private readonly bool showMessageWhenPickedUp;
|
||||
|
||||
/// <summary>
|
||||
/// Status effects executed on the target item when the mission starts. A random effect is chosen from each child list.
|
||||
/// </summary>
|
||||
private readonly List<List<StatusEffect>> statusEffects = new List<List<StatusEffect>>();
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
@@ -24,7 +37,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return ConvertUnits.ToDisplayUnits(item.SimPosition);
|
||||
yield return item.WorldPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -32,6 +45,8 @@ namespace Barotrauma
|
||||
public SalvageMission(MissionPrefab prefab, Location[] locations)
|
||||
: base(prefab, locations)
|
||||
{
|
||||
containerTag = prefab.ConfigElement.GetAttributeString("containertag", "");
|
||||
|
||||
if (prefab.ConfigElement.Attribute("itemname") != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in SalvageMission - use item identifier instead of the name of the item.");
|
||||
@@ -52,32 +67,118 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
existingItemTag = prefab.ConfigElement.GetAttributeString("existingitemtag", "");
|
||||
showMessageWhenPickedUp = prefab.ConfigElement.GetAttributeBool("showmessagewhenpickedup", false);
|
||||
|
||||
string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) ||
|
||||
!Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType))
|
||||
{
|
||||
spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
|
||||
}
|
||||
|
||||
foreach (XElement element in prefab.ConfigElement.Elements())
|
||||
{
|
||||
switch (element.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "statuseffect":
|
||||
{
|
||||
var newEffect = StatusEffect.Load(element, parentDebugName: prefab.Name);
|
||||
if (newEffect == null) { continue; }
|
||||
statusEffects.Add(new List<StatusEffect> { newEffect });
|
||||
break;
|
||||
}
|
||||
case "chooserandom":
|
||||
statusEffects.Add(new List<StatusEffect>());
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
var newEffect = StatusEffect.Load(subElement, parentDebugName: prefab.Name);
|
||||
if (newEffect == null) { continue; }
|
||||
statusEffects.Last().Add(newEffect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
if (!IsClient)
|
||||
{
|
||||
//ruin items are allowed to spawn close to the sub
|
||||
float minDistance = spawnPositionType == Level.PositionType.Ruin ? 0.0f : Level.Loaded.Size.X * 0.3f;
|
||||
//ruin/wreck items are allowed to spawn close to the sub
|
||||
float minDistance = spawnPositionType == Level.PositionType.Ruin || spawnPositionType == Level.PositionType.Wreck ?
|
||||
0.0f : Level.Loaded.Size.X * 0.3f;
|
||||
Vector2 position = Level.Loaded.GetRandomItemPos(spawnPositionType, 100.0f, minDistance, 30.0f);
|
||||
|
||||
item = new Item(itemPrefab, position, null);
|
||||
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
|
||||
if (item.HasTag("alien"))
|
||||
if (!string.IsNullOrEmpty(existingItemTag))
|
||||
{
|
||||
var suitableItems = Item.ItemList.Where(it => it.HasTag(existingItemTag));
|
||||
switch (spawnPositionType)
|
||||
{
|
||||
case Level.PositionType.Cave:
|
||||
case Level.PositionType.MainPath:
|
||||
item = suitableItems.FirstOrDefault(it => Vector2.DistanceSquared(it.WorldPosition, position) < 1000.0f);
|
||||
break;
|
||||
case Level.PositionType.Ruin:
|
||||
item = suitableItems.FirstOrDefault(it => it.ParentRuin != null && it.ParentRuin.Area.Contains(position));
|
||||
break;
|
||||
case Level.PositionType.Wreck:
|
||||
foreach (Item it in suitableItems)
|
||||
{
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineInfo.SubmarineType.Wreck) { continue; }
|
||||
Rectangle worldBorders = it.Submarine.Borders;
|
||||
worldBorders.Location += it.Submarine.WorldPosition.ToPoint();
|
||||
if (Submarine.RectContains(worldBorders, it.WorldPosition))
|
||||
{
|
||||
item = it;
|
||||
#if SERVER
|
||||
usedExistingItem = true;
|
||||
#endif
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (item == null)
|
||||
{
|
||||
item = new Item(itemPrefab, position, null);
|
||||
item.body.FarseerBody.BodyType = BodyType.Kinematic;
|
||||
item.FindHull();
|
||||
}
|
||||
|
||||
for (int i = 0; i < statusEffects.Count; i++)
|
||||
{
|
||||
List<StatusEffect> effectList = statusEffects[i];
|
||||
if (effectList.Count == 0) { continue; }
|
||||
int effectIndex = Rand.Int(effectList.Count);
|
||||
var selectedEffect = effectList[effectIndex];
|
||||
item.ApplyStatusEffect(selectedEffect, selectedEffect.type, deltaTime: 1.0f, worldPosition: item.Position);
|
||||
#if SERVER
|
||||
executedEffectIndices.Add(new Pair<int, int>(i, effectIndex));
|
||||
#endif
|
||||
}
|
||||
|
||||
//try to find a container and place the item inside it
|
||||
if (!string.IsNullOrEmpty(containerTag) && item.ParentInventory == null)
|
||||
{
|
||||
//try to find an artifact holder and place the artifact inside it
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
if (it.Submarine != null || !it.HasTag("artifactholder")) continue;
|
||||
|
||||
if (!it.HasTag(containerTag)) { continue; }
|
||||
switch (spawnPositionType)
|
||||
{
|
||||
case Level.PositionType.Cave:
|
||||
case Level.PositionType.MainPath:
|
||||
if (it.Submarine != null || it.ParentRuin != null) { continue; }
|
||||
break;
|
||||
case Level.PositionType.Ruin:
|
||||
if (it.ParentRuin == null) { continue; }
|
||||
break;
|
||||
case Level.PositionType.Wreck:
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineInfo.SubmarineType.Wreck) { continue; }
|
||||
break;
|
||||
}
|
||||
var itemContainer = it.GetComponent<Items.Components.ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
if (itemContainer.Combine(item, user: null)) { break; } // Placement successful
|
||||
@@ -88,28 +189,46 @@ namespace Barotrauma
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError("Error in salvage mission " + Prefab.Identifier + " (item was null)");
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsClient)
|
||||
{
|
||||
if (item.ParentInventory != null) { item.body.FarseerBody.BodyType = BodyType.Dynamic; }
|
||||
if (item.ParentInventory != null && item.body != null) { item.body.FarseerBody.BodyType = BodyType.Dynamic; }
|
||||
return;
|
||||
}
|
||||
switch (State)
|
||||
{
|
||||
case 0:
|
||||
if (item.ParentInventory != null) { item.body.FarseerBody.BodyType = BodyType.Dynamic; }
|
||||
if (item.CurrentHull?.Submarine == null) { return; }
|
||||
if (item.ParentInventory != null && item.body != null) { item.body.FarseerBody.BodyType = BodyType.Dynamic; }
|
||||
if (showMessageWhenPickedUp)
|
||||
{
|
||||
if (!(item.ParentInventory?.Owner is Character)) { return; }
|
||||
}
|
||||
else
|
||||
{
|
||||
if (item.CurrentHull?.Submarine == null || item.CurrentHull.Submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { return; }
|
||||
}
|
||||
State = 1;
|
||||
break;
|
||||
case 1:
|
||||
if (!Submarine.MainSub.AtEndPosition && !Submarine.MainSub.AtStartPosition) { return; }
|
||||
State = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (item.CurrentHull?.Submarine == null || !item.CurrentHull.Submarine.AtEndPosition || item.Removed) { return; }
|
||||
if (item.CurrentHull?.Submarine == null || (!item.CurrentHull.Submarine.AtEndPosition && !item.CurrentHull.Submarine.AtStartPosition) || item.Removed)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
item?.Remove();
|
||||
item = null;
|
||||
|
||||
@@ -1,50 +1,46 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class MonsterEvent : ScriptedEvent
|
||||
{
|
||||
private string speciesName;
|
||||
|
||||
private int minAmount, maxAmount;
|
||||
|
||||
private readonly string speciesName;
|
||||
private readonly int minAmount, maxAmount;
|
||||
private List<Character> monsters;
|
||||
|
||||
private bool spawnDeep;
|
||||
private readonly bool spawnDeep;
|
||||
|
||||
private Vector2? spawnPos;
|
||||
|
||||
private bool disallowed;
|
||||
|
||||
private Level.PositionType spawnPosType;
|
||||
private readonly bool disallowed;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
|
||||
private bool spawnPending;
|
||||
|
||||
private string characterFileName;
|
||||
|
||||
public override Vector2 DebugDrawPos
|
||||
{
|
||||
get { return spawnPos.HasValue ? spawnPos.Value : Vector2.Zero; }
|
||||
get { return spawnPos ?? Vector2.Zero; }
|
||||
}
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
if (maxAmount <= 1)
|
||||
{
|
||||
return "MonsterEvent (" + characterFileName + ")";
|
||||
return "MonsterEvent (" + speciesName + ")";
|
||||
}
|
||||
else if (minAmount < maxAmount)
|
||||
{
|
||||
return "MonsterEvent (" + characterFileName + " x" + minAmount + "-" + maxAmount + ")";
|
||||
return "MonsterEvent (" + speciesName + " x" + minAmount + "-" + maxAmount + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "MonsterEvent (" + characterFileName + " x" + maxAmount + ")";
|
||||
return "MonsterEvent (" + speciesName + " x" + maxAmount + ")";
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,7 +72,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
spawnDeep = prefab.ConfigElement.GetAttributeBool("spawndeep", false);
|
||||
characterFileName = Path.GetFileName(Path.GetDirectoryName(speciesName)).ToLower();
|
||||
|
||||
if (GameMain.NetworkMember != null)
|
||||
{
|
||||
@@ -85,7 +80,10 @@ namespace Barotrauma
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(tryKey))
|
||||
{
|
||||
if (!GameMain.NetworkMember.ServerSettings.MonsterEnabled[tryKey]) disallowed = true; //spawn was disallowed by host
|
||||
if (!GameMain.NetworkMember.ServerSettings.MonsterEnabled[tryKey])
|
||||
{
|
||||
disallowed = true; //spawn was disallowed by host
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -106,18 +104,8 @@ namespace Barotrauma
|
||||
|
||||
public override bool CanAffectSubImmediately(Level level)
|
||||
{
|
||||
float maxRange = Items.Components.Sonar.DefaultSonarRange * 0.8f;
|
||||
|
||||
List<Vector2> positions = GetAvailableSpawnPositions();
|
||||
foreach (Vector2 position in positions)
|
||||
{
|
||||
if (Vector2.DistanceSquared(position, Submarine.MainSub.WorldPosition) < maxRange * maxRange)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
float maxRange = Sonar.DefaultSonarRange * 0.8f;
|
||||
return GetAvailableSpawnPositions().Any(p => Vector2.DistanceSquared(p.Position.ToVector2(), Submarine.MainSub.WorldPosition) < maxRange * maxRange);
|
||||
}
|
||||
|
||||
public override void Init(bool affectSubImmediately)
|
||||
@@ -128,28 +116,44 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private List<Vector2> GetAvailableSpawnPositions()
|
||||
private List<Level.InterestingPosition> GetAvailableSpawnPositions()
|
||||
{
|
||||
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p => spawnPosType.HasFlag(p.PositionType));
|
||||
|
||||
List<Vector2> positions = new List<Vector2>();
|
||||
foreach (var allowedPosition in availablePositions)
|
||||
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p => spawnPosType.HasFlag(p.PositionType) && !Level.Loaded.UsedPositions.Contains(p));
|
||||
var removals = new List<Level.InterestingPosition>();
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
if (Level.Loaded.ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(allowedPosition.Position.ToVector2())))) { continue; }
|
||||
positions.Add(allowedPosition.Position.ToVector2());
|
||||
}
|
||||
|
||||
if (spawnDeep)
|
||||
{
|
||||
for (int i = 0; i < positions.Count; i++)
|
||||
if (position.Submarine != null)
|
||||
{
|
||||
positions[i] = new Vector2(positions[i].X, positions[i].Y - Level.Loaded.Size.Y);
|
||||
if (position.Submarine.WreckAI != null && position.Submarine.WreckAI.IsAlive)
|
||||
{
|
||||
removals.Add(position);
|
||||
}
|
||||
else
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (position.PositionType != Level.PositionType.MainPath) { continue; }
|
||||
if (Level.Loaded.ExtraWalls.Any(w => w.Cells.Any(c => c.IsPointInside(position.Position.ToVector2()))))
|
||||
{
|
||||
removals.Add(position);
|
||||
}
|
||||
if (spawnDeep)
|
||||
{
|
||||
for (int i = 0; i < availablePositions.Count; i++)
|
||||
{
|
||||
var pos = availablePositions[i].Position;
|
||||
pos = new Point(pos.X, pos.Y - Level.Loaded.Size.Y);
|
||||
availablePositions[i] = new Level.InterestingPosition(pos, availablePositions[i].PositionType);
|
||||
}
|
||||
}
|
||||
if (position.Position.Y < Level.Loaded.GetBottomPosition(position.Position.X).Y)
|
||||
{
|
||||
removals.Add(position);
|
||||
}
|
||||
}
|
||||
|
||||
positions.RemoveAll(pos => pos.Y < Level.Loaded.GetBottomPosition(pos.X).Y);
|
||||
|
||||
return positions;
|
||||
removals.ForEach(r => availablePositions.Remove(r));
|
||||
return availablePositions;
|
||||
}
|
||||
|
||||
private void FindSpawnPosition(bool affectSubImmediately)
|
||||
@@ -158,67 +162,100 @@ namespace Barotrauma
|
||||
|
||||
spawnPos = Vector2.Zero;
|
||||
var availablePositions = GetAvailableSpawnPositions();
|
||||
if (affectSubImmediately && spawnPosType != Level.PositionType.Ruin)
|
||||
var chosenPosition = new Level.InterestingPosition(Point.Zero, Level.PositionType.MainPath, isValid: false);
|
||||
var removedPositions = new List<Level.InterestingPosition>();
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
if (availablePositions.Count == 0)
|
||||
if (Rand.Value(Rand.RandSync.Server) > prefab.SpawnProbability)
|
||||
{
|
||||
removedPositions.Add(position);
|
||||
if (prefab.AllowOnlyOnce)
|
||||
{
|
||||
Level.Loaded.UsedPositions.Add(position);
|
||||
}
|
||||
}
|
||||
}
|
||||
removedPositions.ForEach(p => availablePositions.Remove(p));
|
||||
bool isSubOrWreck = spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Wreck;
|
||||
if (affectSubImmediately && !isSubOrWreck)
|
||||
{
|
||||
if (availablePositions.None())
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
float closestDist = float.PositiveInfinity;
|
||||
//find the closest spawnposition that isn't too close to any of the subs
|
||||
foreach (Vector2 position in availablePositions)
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(position, Submarine.MainSub.WorldPosition);
|
||||
Vector2 pos = position.Position.ToVector2();
|
||||
float dist = Vector2.DistanceSquared(pos, Submarine.MainSub.WorldPosition);
|
||||
foreach (Submarine sub in Submarine.Loaded)
|
||||
{
|
||||
if (sub.IsOutpost) { continue; }
|
||||
if (sub.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; }
|
||||
float minDistToSub = GetMinDistanceToSub(sub);
|
||||
if (dist > minDistToSub * minDistToSub && dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
spawnPos = position;
|
||||
chosenPosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//only found a spawnpos that's very far from the sub, pick one that's closer
|
||||
//and wait for the sub to move further before spawning
|
||||
if (closestDist > 15000.0f * 15000.0f)
|
||||
{
|
||||
foreach (Vector2 position in availablePositions)
|
||||
foreach (var position in availablePositions)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(position, Submarine.MainSub.WorldPosition);
|
||||
float dist = Vector2.DistanceSquared(position.Position.ToVector2(), Submarine.MainSub.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
spawnPos = position;
|
||||
chosenPosition = position;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
float minDist = spawnPosType == Level.PositionType.Ruin ? 0.0f : 20000.0f;
|
||||
availablePositions.RemoveAll(p => Vector2.Distance(Submarine.MainSub.WorldPosition, p) < minDist);
|
||||
if (availablePositions.Count == 0)
|
||||
if (!isSubOrWreck)
|
||||
{
|
||||
float minDistance = 20000;
|
||||
availablePositions.RemoveAll(p => Vector2.DistanceSquared(Submarine.MainSub.WorldPosition, p.Position.ToVector2()) < minDistance * minDistance);
|
||||
}
|
||||
if (availablePositions.None())
|
||||
{
|
||||
//no suitable position found, disable the event
|
||||
Finished();
|
||||
return;
|
||||
}
|
||||
|
||||
spawnPos = availablePositions[Rand.Int(availablePositions.Count, Rand.RandSync.Server)];
|
||||
chosenPosition = availablePositions.GetRandom();
|
||||
}
|
||||
if (chosenPosition.IsValid)
|
||||
{
|
||||
spawnPos = chosenPosition.Position.ToVector2();
|
||||
if (chosenPosition.Submarine != null || chosenPosition.Ruin != null)
|
||||
{
|
||||
var spawnPoint = WayPoint.GetRandom(SpawnType.Enemy, sub: chosenPosition.Submarine, ruin: chosenPosition.Ruin, useSyncedRand: false);
|
||||
if (spawnPoint != null)
|
||||
{
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.Submarine == chosenPosition.Submarine);
|
||||
System.Diagnostics.Debug.Assert(spawnPoint.ParentRuin == chosenPosition.Ruin);
|
||||
spawnPos = spawnPoint.WorldPosition;
|
||||
}
|
||||
}
|
||||
spawnPending = true;
|
||||
if (prefab.AllowOnlyOnce)
|
||||
{
|
||||
Level.Loaded.UsedPositions.Add(chosenPosition);
|
||||
}
|
||||
}
|
||||
spawnPending = true;
|
||||
}
|
||||
|
||||
private float GetMinDistanceToSub(Submarine submarine)
|
||||
{
|
||||
//9000 units is slightly less than the default range of the sonar
|
||||
return Math.Max(Math.Max(submarine.Borders.Width, submarine.Borders.Height), 9000.0f);
|
||||
return Math.Max(Math.Max(submarine.Borders.Width, submarine.Borders.Height), Sonar.DefaultSonarRange * 0.9f);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -243,9 +280,38 @@ namespace Barotrauma
|
||||
//wait until there are no submarines at the spawnpos
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (submarine.IsOutpost) { continue; }
|
||||
if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; }
|
||||
float minDist = GetMinDistanceToSub(submarine);
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) return;
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist) { return; }
|
||||
}
|
||||
|
||||
//if spawning in a ruin/cave, wait for someone to be close to it to spawning
|
||||
//unnecessary monsters in places the players might never visit during the round
|
||||
if (spawnPosType == Level.PositionType.Ruin || spawnPosType == Level.PositionType.Cave || spawnPosType == Level.PositionType.Wreck)
|
||||
{
|
||||
bool someoneNearby = false;
|
||||
float minDist = Sonar.DefaultSonarRange * 0.8f;
|
||||
foreach (Submarine submarine in Submarine.Loaded)
|
||||
{
|
||||
if (submarine.Info.Type != SubmarineInfo.SubmarineType.Player) { continue; }
|
||||
if (Vector2.DistanceSquared(submarine.WorldPosition, spawnPos.Value) < minDist * minDist)
|
||||
{
|
||||
someoneNearby = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c == Character.Controlled || c.IsRemotePlayer)
|
||||
{
|
||||
if (Vector2.DistanceSquared(c.WorldPosition, spawnPos.Value) < minDist * minDist)
|
||||
{
|
||||
someoneNearby = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!someoneNearby) { return; }
|
||||
}
|
||||
|
||||
spawnPending = false;
|
||||
@@ -280,7 +346,7 @@ namespace Barotrauma
|
||||
|
||||
Entity targetEntity = Submarine.FindClosest(GameMain.GameScreen.Cam.WorldViewCenter);
|
||||
#if CLIENT
|
||||
if (Character.Controlled != null) targetEntity = (Entity)Character.Controlled;
|
||||
if (Character.Controlled != null) { targetEntity = Character.Controlled; }
|
||||
#endif
|
||||
|
||||
bool monstersDead = true;
|
||||
@@ -297,7 +363,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (monstersDead) Finished();
|
||||
if (monstersDead) { Finished(); }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -8,7 +7,7 @@ namespace Barotrauma
|
||||
{
|
||||
protected bool isFinished;
|
||||
|
||||
private readonly ScriptedEventPrefab prefab;
|
||||
protected readonly ScriptedEventPrefab prefab;
|
||||
|
||||
public bool IsFinished
|
||||
{
|
||||
|
||||
@@ -7,12 +7,11 @@ namespace Barotrauma
|
||||
{
|
||||
class ScriptedEventPrefab
|
||||
{
|
||||
public readonly XElement ConfigElement;
|
||||
|
||||
public readonly Type EventType;
|
||||
|
||||
public readonly XElement ConfigElement;
|
||||
public readonly Type EventType;
|
||||
public readonly string MusicType;
|
||||
|
||||
public readonly float SpawnProbability;
|
||||
public readonly bool AllowOnlyOnce;
|
||||
public float Commonness;
|
||||
|
||||
public ScriptedEventPrefab(XElement element)
|
||||
@@ -34,6 +33,8 @@ namespace Barotrauma
|
||||
DebugConsole.ThrowError("Could not find an event class of the type \"" + ConfigElement.Name + "\".");
|
||||
}
|
||||
Commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
SpawnProbability = Math.Clamp(element.GetAttributeFloat("spawnprobability", 1.0f), 0, 1);
|
||||
AllowOnlyOnce = element.GetAttributeBool("allowonlyonce", false);
|
||||
}
|
||||
|
||||
public ScriptedEvent CreateInstance()
|
||||
|
||||
@@ -25,6 +25,11 @@ namespace Barotrauma
|
||||
//the events in this set are delayed if the current EventManager intensity is not between these values
|
||||
public readonly float MinIntensity, MaxIntensity;
|
||||
|
||||
public readonly bool AllowAtStart;
|
||||
|
||||
public readonly bool PerRuin;
|
||||
public readonly bool PerWreck;
|
||||
|
||||
public readonly Dictionary<string, float> Commonness;
|
||||
|
||||
public readonly List<ScriptedEventPrefab> EventPrefabs;
|
||||
@@ -54,6 +59,10 @@ namespace Barotrauma
|
||||
MinDistanceTraveled = element.GetAttributeFloat("mindistancetraveled", 0.0f);
|
||||
MinMissionTime = element.GetAttributeFloat("minmissiontime", 0.0f);
|
||||
|
||||
AllowAtStart = element.GetAttributeBool("allowatstart", false);
|
||||
PerRuin = element.GetAttributeBool("perruin", false);
|
||||
PerWreck = element.GetAttributeBool("perwreck", false);
|
||||
|
||||
Commonness[""] = 1.0f;
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
@@ -63,7 +72,7 @@ namespace Barotrauma
|
||||
Commonness[""] = subElement.GetAttributeFloat("commonness", 0.0f);
|
||||
foreach (XElement overrideElement in subElement.Elements())
|
||||
{
|
||||
if (overrideElement.Name.ToString().ToLowerInvariant() == "override")
|
||||
if (overrideElement.Name.ToString().Equals("override", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
string levelType = overrideElement.GetAttributeString("leveltype", "");
|
||||
if (!Commonness.ContainsKey(levelType))
|
||||
@@ -116,7 +125,7 @@ namespace Barotrauma
|
||||
int i = 0;
|
||||
foreach (XElement element in doc.Root.Elements())
|
||||
{
|
||||
if (element.Name.ToString().ToLowerInvariant() != "eventset") { continue; }
|
||||
if (!element.Name.ToString().Equals("eventset", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
List.Add(new ScriptedEventSet(element, i.ToString()));
|
||||
i++;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user