Unstable v0.1300.0.0 (February 19th 2021)
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
using Barotrauma.Extensions;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class AbandonedOutpostMission : Mission
|
||||
{
|
||||
private readonly XElement characterConfig;
|
||||
|
||||
private readonly List<Character> characters = new List<Character>();
|
||||
private readonly Dictionary<Character, List<Item>> characterItems = new Dictionary<Character, List<Item>>();
|
||||
|
||||
private readonly string itemTag;
|
||||
|
||||
private Item itemToDestroy;
|
||||
|
||||
public AbandonedOutpostMission(MissionPrefab prefab, Location[] locations) :
|
||||
base(prefab, locations)
|
||||
{
|
||||
characterConfig = prefab.ConfigElement.Element("Characters");
|
||||
|
||||
itemTag = prefab.ConfigElement.GetAttributeString("targetitem", "");
|
||||
if (string.IsNullOrEmpty(itemTag))
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission prefab \"{prefab.Identifier}\". Target item not defined.");
|
||||
}
|
||||
}
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
itemToDestroy = null;
|
||||
itemToDestroy = Item.ItemList.Find(it => it.Submarine?.Info.Type != SubmarineType.Player && it.HasTag(itemTag));
|
||||
if (itemToDestroy == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in mission \"{Prefab.Identifier}\". Could not find an item with the tag \"{itemTag}\".");
|
||||
}
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
InitCharacters();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitCharacters()
|
||||
{
|
||||
characters.Clear();
|
||||
characterItems.Clear();
|
||||
|
||||
if (characterConfig == null) { return; }
|
||||
|
||||
var submarine = Submarine.Loaded.Find(s => s.Info.Type == SubmarineType.Outpost) ?? Submarine.MainSub;
|
||||
if (submarine.Info.Type == SubmarineType.Outpost)
|
||||
{
|
||||
submarine.TeamID = CharacterTeamType.None;
|
||||
}
|
||||
|
||||
foreach (XElement element in characterConfig.Elements())
|
||||
{
|
||||
string characterIdentifier = element.GetAttributeString("identifier", "");
|
||||
string characterFrom = element.GetAttributeString("from", "");
|
||||
HumanPrefab humanPrefab = NPCSet.Get(characterFrom, characterIdentifier);
|
||||
if (humanPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Couldn't spawn character for abandoned outpost mission: character prefab \"" + characterIdentifier + "\" not found");
|
||||
return;
|
||||
}
|
||||
|
||||
string[] moduleFlags = element.GetAttributeStringArray("moduleflags", null);
|
||||
string[] spawnPointTags = element.GetAttributeStringArray("spawnpointtags", null);
|
||||
ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(
|
||||
SpawnAction.SpawnLocationType.Outpost, SpawnType.Human,
|
||||
moduleFlags ?? humanPrefab.GetModuleFlags(),
|
||||
spawnPointTags ?? humanPrefab.GetSpawnPointTags());
|
||||
if (spawnPos == null)
|
||||
{
|
||||
spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandom();
|
||||
}
|
||||
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.Server), randSync: Rand.RandSync.Server);
|
||||
Character spawnedCharacter = Character.Create(characterInfo.SpeciesName, spawnPos.WorldPosition, ToolBox.RandomSeed(8), characterInfo, createNetworkEvent: false);
|
||||
spawnedCharacter.TeamID = CharacterTeamType.None;
|
||||
humanPrefab.InitializeCharacter(spawnedCharacter, spawnPos);
|
||||
humanPrefab.GiveItems(spawnedCharacter, Submarine.MainSub, Rand.RandSync.Server, createNetworkEvents: false);
|
||||
|
||||
characters.Add(spawnedCharacter);
|
||||
characterItems.Add(spawnedCharacter, spawnedCharacter.Inventory.FindAllItems(recursive: true));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (State == 0 && itemToDestroy != null && itemToDestroy.Condition <= 0.0f)
|
||||
{
|
||||
State = 1;
|
||||
}
|
||||
}
|
||||
|
||||
public override void End()
|
||||
{
|
||||
completed = itemToDestroy == null || itemToDestroy.Condition <= 0.0f;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -11,11 +9,9 @@ namespace Barotrauma
|
||||
partial class BeaconMission : Mission
|
||||
{
|
||||
private bool swarmSpawned;
|
||||
private string monsterSpeciesName;
|
||||
private readonly string monsterSpeciesName;
|
||||
private Point monsterCountRange;
|
||||
private Level level;
|
||||
private Location[] locations;
|
||||
private string sonarLabel;
|
||||
private readonly string sonarLabel;
|
||||
|
||||
public BeaconMission(MissionPrefab prefab, Location[] locations) : base(prefab, locations)
|
||||
{
|
||||
@@ -34,8 +30,6 @@ namespace Barotrauma
|
||||
|
||||
monsterCountRange = new Point(min, max);
|
||||
|
||||
this.locations = locations;
|
||||
|
||||
sonarLabel = TextManager.Get("beaconstationsonarlabel");
|
||||
}
|
||||
|
||||
@@ -51,27 +45,58 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return level.BeaconStation.WorldPosition;
|
||||
if (level.BeaconStation == null)
|
||||
{
|
||||
yield break;
|
||||
}
|
||||
yield return level.BeaconStation.WorldPosition;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient) { return; }
|
||||
if (!swarmSpawned && level.CheckBeaconActive())
|
||||
{
|
||||
State = 1;
|
||||
|
||||
Vector2 spawnPos = level.BeaconStation.WorldPosition;
|
||||
spawnPos.Y += level.BeaconStation.GetDockedBorders().Height * 1.5f;
|
||||
|
||||
var availablePositions = Level.Loaded.PositionsOfInterest.FindAll(p =>
|
||||
p.PositionType == Level.PositionType.MainPath ||
|
||||
p.PositionType == Level.PositionType.SidePath);
|
||||
availablePositions.RemoveAll(p => Level.Loaded.ExtraWalls.Any(w => w.IsPointInside(p.Position.ToVector2())));
|
||||
availablePositions.RemoveAll(p => Submarine.FindContaining(p.Position.ToVector2()) != null);
|
||||
|
||||
if (availablePositions.Any())
|
||||
{
|
||||
Level.InterestingPosition? closestPos = null;
|
||||
float closestDist = float.PositiveInfinity;
|
||||
foreach (var pos in availablePositions)
|
||||
{
|
||||
float dist = Vector2.DistanceSquared(pos.Position.ToVector2(), level.BeaconStation.WorldPosition);
|
||||
if (dist < closestDist)
|
||||
{
|
||||
closestDist = dist;
|
||||
closestPos = pos;
|
||||
}
|
||||
}
|
||||
if (closestPos.HasValue)
|
||||
{
|
||||
spawnPos = closestPos.Value.Position.ToVector2();
|
||||
}
|
||||
}
|
||||
|
||||
int amount = Rand.Range(monsterCountRange.X, monsterCountRange.Y + 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
Entity.Spawner.AddToSpawnQueue(monsterSpeciesName, spawnPos);
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
//round ended before the coroutine finished
|
||||
if (GameMain.GameSession == null || Level.Loaded == null) { return; }
|
||||
Entity.Spawner.AddToSpawnQueue(monsterSpeciesName, spawnPos);
|
||||
}, Rand.Range(0f, amount));
|
||||
}
|
||||
swarmSpawned = true;
|
||||
}
|
||||
@@ -82,13 +107,15 @@ namespace Barotrauma
|
||||
completed = level.CheckBeaconActive();
|
||||
if (completed)
|
||||
{
|
||||
if (GameMain.GameSession.GameMode is CampaignMode)
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
int naturalFormationIndex = locations[0].Type.Identifier.Equals("None", StringComparison.OrdinalIgnoreCase) ? 0 : 1;
|
||||
var upgradeLocation = locations[naturalFormationIndex];
|
||||
upgradeLocation.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals("Explored", StringComparison.OrdinalIgnoreCase)));
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
if (level?.LevelData != null)
|
||||
{
|
||||
level.LevelData.IsBeaconActive = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,10 @@ namespace Barotrauma
|
||||
cargoSpawnPos.Position.X + Rand.Range(-20.0f, 20.0f, Rand.RandSync.Server),
|
||||
cargoRoom.Rect.Y - cargoRoom.Rect.Height + itemPrefab.Size.Y / 2);
|
||||
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine);
|
||||
var item = new Item(itemPrefab, position, cargoRoom.Submarine)
|
||||
{
|
||||
SpawnedInOutpost = true
|
||||
};
|
||||
item.FindHull();
|
||||
items.Add(item);
|
||||
|
||||
@@ -115,7 +118,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
items.Clear();
|
||||
parentInventoryIDs.Clear();
|
||||
@@ -135,6 +138,10 @@ namespace Barotrauma
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,11 +16,11 @@ namespace Barotrauma
|
||||
get { return false; }
|
||||
}
|
||||
|
||||
private Character.TeamType Winner
|
||||
private CharacterTeamType Winner
|
||||
{
|
||||
get
|
||||
{
|
||||
if (GameMain.GameSession?.WinningTeam == null) { return Character.TeamType.None; }
|
||||
if (GameMain.GameSession?.WinningTeam == null) { return CharacterTeamType.None; }
|
||||
return GameMain.GameSession.WinningTeam.Value;
|
||||
}
|
||||
}
|
||||
@@ -29,14 +29,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Winner == Character.TeamType.None || string.IsNullOrEmpty(base.SuccessMessage)) { return ""; }
|
||||
if (Winner == CharacterTeamType.None || string.IsNullOrEmpty(base.SuccessMessage)) { return ""; }
|
||||
|
||||
//disable success message for now if it hasn't been translated
|
||||
if (!TextManager.ContainsTag("MissionSuccess." + Prefab.TextIdentifier)) { return ""; }
|
||||
|
||||
var loser = Winner == Character.TeamType.Team1 ?
|
||||
Character.TeamType.Team2 :
|
||||
Character.TeamType.Team1;
|
||||
var loser = Winner == CharacterTeamType.Team1 ?
|
||||
CharacterTeamType.Team2 :
|
||||
CharacterTeamType.Team1;
|
||||
|
||||
return base.SuccessMessage
|
||||
.Replace("[loser]", GetTeamName(loser))
|
||||
@@ -44,11 +44,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override int TeamCount
|
||||
{
|
||||
get { return 2; }
|
||||
}
|
||||
|
||||
public CombatMission(MissionPrefab prefab, Location[] locations)
|
||||
: base(prefab, locations)
|
||||
{
|
||||
@@ -74,13 +69,13 @@ namespace Barotrauma
|
||||
};
|
||||
}
|
||||
|
||||
public static string GetTeamName(Character.TeamType teamID)
|
||||
public static string GetTeamName(CharacterTeamType teamID)
|
||||
{
|
||||
if (teamID == Character.TeamType.Team1)
|
||||
if (teamID == CharacterTeamType.Team1)
|
||||
{
|
||||
return teamNames.Length > 0 ? teamNames[0] : "Team 1";
|
||||
}
|
||||
else if (teamID == Character.TeamType.Team2)
|
||||
else if (teamID == CharacterTeamType.Team2)
|
||||
{
|
||||
return teamNames.Length > 1 ? teamNames[1] : "Team 2";
|
||||
}
|
||||
@@ -91,11 +86,11 @@ namespace Barotrauma
|
||||
public bool IsInWinningTeam(Character character)
|
||||
{
|
||||
return character != null &&
|
||||
Winner != Character.TeamType.None &&
|
||||
Winner != CharacterTeamType.None &&
|
||||
Winner == character.TeamID;
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (GameMain.NetworkMember == null)
|
||||
{
|
||||
@@ -104,7 +99,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
subs = new Submarine[] { Submarine.MainSubs[0], Submarine.MainSubs[1] };
|
||||
subs[0].TeamID = Character.TeamType.Team1; subs[1].TeamID = Character.TeamType.Team2;
|
||||
subs[0].TeamID = CharacterTeamType.Team1; subs[1].TeamID = CharacterTeamType.Team2;
|
||||
subs[0].NeutralizeBallast(); subs[1].NeutralizeBallast();
|
||||
subs[1].SetPosition(subs[1].FindSpawnPos(Level.Loaded.EndPosition));
|
||||
subs[1].FlipX();
|
||||
@@ -120,9 +115,9 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (GameMain.NetworkMember == null) return;
|
||||
if (GameMain.NetworkMember == null) { return; }
|
||||
|
||||
if (Winner != Character.TeamType.None)
|
||||
if (Winner != CharacterTeamType.None)
|
||||
{
|
||||
GiveReward();
|
||||
completed = true;
|
||||
|
||||
@@ -14,6 +14,8 @@ namespace Barotrauma
|
||||
private Dictionary<string, Item[]> RelevantLevelResources { get; } = new Dictionary<string, Item[]>();
|
||||
private List<Tuple<string, Vector2>> MissionClusterPositions { get; } = new List<Tuple<string, Vector2>>();
|
||||
|
||||
private readonly HashSet<Level.Cave> caves = new HashSet<Level.Cave>();
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
@@ -42,17 +44,72 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (SpawnedResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"SpawnedResources.Count > 0 ({SpawnedResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Spawned resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
SpawnedResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (RelevantLevelResources.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"RelevantLevelResources.Count > 0 ({RelevantLevelResources.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Relevant level resources list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
RelevantLevelResources.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (MissionClusterPositions.Any())
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"MissionClusterPositions.Count > 0 ({MissionClusterPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Mission cluster positions list was not empty at the start of a mineral mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
MissionClusterPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
caves.Clear();
|
||||
|
||||
if (IsClient) { return; }
|
||||
foreach (var kvp in ResourceClusters)
|
||||
{
|
||||
var prefab = ItemPrefab.Find(null, kvp.Key);
|
||||
if (prefab == null) { continue; }
|
||||
if (prefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"couldn't find an item prefab with the identifier " + kvp.Key);
|
||||
continue;
|
||||
}
|
||||
var spawnedResources = level.GenerateMissionResources(prefab, kvp.Value.First, out float rotation);
|
||||
if (spawnedResources.Count < kvp.Value.First)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in MineralMission - " +
|
||||
"spawned " + spawnedResources.Count + "/" + kvp.Value.First + " of " + prefab.Name);
|
||||
}
|
||||
if (spawnedResources.None()) { continue; }
|
||||
SpawnedResources.Add(kvp.Key, spawnedResources);
|
||||
kvp.Value.Second = rotation;
|
||||
|
||||
foreach (Level.Cave cave in Level.Loaded.Caves)
|
||||
{
|
||||
foreach (Item spawnedResource in spawnedResources)
|
||||
{
|
||||
if (cave.Area.Contains(spawnedResource.WorldPosition))
|
||||
{
|
||||
cave.DisplayOnSonar = true;
|
||||
caves.Add(cave);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
CalculateMissionClusterPositions();
|
||||
FindRelevantLevelResources();
|
||||
@@ -76,9 +133,29 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (!EnoughHaveBeenCollected()) { return; }
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (EnoughHaveBeenCollected())
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
}
|
||||
foreach (var kvp in SpawnedResources)
|
||||
{
|
||||
foreach (var i in kvp.Value)
|
||||
{
|
||||
if (i != null && !i.Removed && !HasBeenCollected(i))
|
||||
{
|
||||
i.Remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
SpawnedResources.Clear();
|
||||
RelevantLevelResources.Clear();
|
||||
MissionClusterPositions.Clear();
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
|
||||
private void FindRelevantLevelResources()
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -11,6 +9,9 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly MissionPrefab Prefab;
|
||||
protected bool completed, failed;
|
||||
|
||||
protected Level level;
|
||||
|
||||
protected int state;
|
||||
public int State
|
||||
{
|
||||
@@ -21,7 +22,7 @@ namespace Barotrauma
|
||||
{
|
||||
state = value;
|
||||
#if SERVER
|
||||
GameMain.Server?.UpdateMissionState(state);
|
||||
GameMain.Server?.UpdateMissionState(this, state);
|
||||
#endif
|
||||
ShowMessage(State);
|
||||
}
|
||||
@@ -85,11 +86,6 @@ namespace Barotrauma
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual int TeamCount
|
||||
{
|
||||
get { return 1; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get { return Enumerable.Empty<Vector2>(); }
|
||||
@@ -180,15 +176,23 @@ namespace Barotrauma
|
||||
return null;
|
||||
}
|
||||
|
||||
public virtual void Start(Level level) { }
|
||||
public void Start(Level level)
|
||||
{
|
||||
foreach (string categoryToShow in Prefab.UnhideEntitySubCategories)
|
||||
{
|
||||
foreach (MapEntity entityToShow in MapEntity.mapEntityList.Where(me => me.prefab.HasSubCategory(categoryToShow)))
|
||||
{
|
||||
entityToShow.HiddenInGame = false;
|
||||
}
|
||||
}
|
||||
this.level = level;
|
||||
StartMissionSpecific(level);
|
||||
}
|
||||
|
||||
protected virtual void StartMissionSpecific(Level level) { }
|
||||
|
||||
public virtual void Update(float deltaTime) { }
|
||||
|
||||
public virtual void AssignTeamIDs(List<Networking.Client> clients)
|
||||
{
|
||||
clients.ForEach(c => c.TeamID = Character.TeamType.Team1);
|
||||
}
|
||||
|
||||
protected void ShowMessage(int missionState)
|
||||
{
|
||||
ShowMessageProjSpecific(missionState);
|
||||
@@ -202,7 +206,10 @@ namespace Barotrauma
|
||||
public virtual void End()
|
||||
{
|
||||
completed = true;
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
}
|
||||
|
||||
@@ -234,6 +241,35 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected void ChangeLocationType(LocationTypeChange change)
|
||||
{
|
||||
if (change == null) { throw new ArgumentException(); }
|
||||
if (GameMain.GameSession.GameMode is CampaignMode && !IsClient)
|
||||
{
|
||||
int srcIndex = -1;
|
||||
for (int i = 0; i < Locations.Length; i++)
|
||||
{
|
||||
if (Locations[i].Type.Identifier.Equals(change.CurrentType, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
srcIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (srcIndex == -1) { return; }
|
||||
var location = Locations[srcIndex];
|
||||
|
||||
if (change.RequiredDurationRange.X > 0)
|
||||
{
|
||||
location.PendingLocationTypeChange = (change, Rand.Range(change.RequiredDurationRange.X, change.RequiredDurationRange.Y), Prefab);
|
||||
}
|
||||
else
|
||||
{
|
||||
location.ChangeType(LocationType.List.Find(lt => lt.Identifier.Equals(change.ChangeToType, StringComparison.OrdinalIgnoreCase)));
|
||||
location.LocationTypeChangeCooldown = change.CooldownAfterChange;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public virtual void AdjustLevelData(LevelData levelData) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,7 +18,9 @@ namespace Barotrauma
|
||||
Nest = 0x10,
|
||||
Mineral = 0x20,
|
||||
Combat = 0x40,
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | Combat
|
||||
AbandonedOutpost = 0x80,
|
||||
|
||||
All = Salvage | Monster | Cargo | Beacon | Nest | Mineral | AbandonedOutpost
|
||||
}
|
||||
|
||||
partial class MissionPrefab
|
||||
@@ -33,6 +35,7 @@ namespace Barotrauma
|
||||
{ MissionType.Beacon, typeof(BeaconMission) },
|
||||
{ MissionType.Nest, typeof(NestMission) },
|
||||
{ MissionType.Mineral, typeof(MineralMission) },
|
||||
{ MissionType.AbandonedOutpost, typeof(AbandonedOutpostMission) },
|
||||
};
|
||||
public static readonly Dictionary<MissionType, Type> PvPMissionClasses = new Dictionary<MissionType, Type>()
|
||||
{
|
||||
@@ -73,8 +76,26 @@ namespace Barotrauma
|
||||
public readonly List<string> Headers;
|
||||
public readonly List<string> Messages;
|
||||
|
||||
//the mission can only be received when travelling from Pair.First to Pair.Second
|
||||
public readonly List<Pair<string, string>> AllowedLocationTypes;
|
||||
public readonly bool AllowRetry;
|
||||
|
||||
public readonly bool IsSideObjective;
|
||||
|
||||
/// <summary>
|
||||
/// The mission can only be received when travelling from Pair.First to Pair.Second
|
||||
/// </summary>
|
||||
public readonly List<Pair<string, string>> AllowedConnectionTypes;
|
||||
|
||||
/// <summary>
|
||||
/// The mission can only be received in these location types
|
||||
/// </summary>
|
||||
public readonly List<string> AllowedLocationTypes = new List<string>();
|
||||
|
||||
/// <summary>
|
||||
/// Show entities belonging to these sub categories when the mission starts
|
||||
/// </summary>
|
||||
public readonly List<string> UnhideEntitySubCategories = new List<string>();
|
||||
|
||||
public LocationTypeChange LocationTypeChangeOnCompleted;
|
||||
|
||||
public readonly XElement ConfigElement;
|
||||
|
||||
@@ -130,7 +151,8 @@ namespace Barotrauma
|
||||
Name = TextManager.Get("MissionName." + TextIdentifier, true) ?? element.GetAttributeString("name", "");
|
||||
Description = TextManager.Get("MissionDescription." + TextIdentifier, true) ?? element.GetAttributeString("description", "");
|
||||
Reward = element.GetAttributeInt("reward", 1);
|
||||
|
||||
AllowRetry = element.GetAttributeBool("allowretry", false);
|
||||
IsSideObjective = element.GetAttributeBool("sideobjective", false);
|
||||
Commonness = element.GetAttributeInt("commonness", 1);
|
||||
|
||||
SuccessMessage = TextManager.Get("MissionSuccess." + TextIdentifier, true) ?? element.GetAttributeString("successmessage", "Mission completed successfully");
|
||||
@@ -152,9 +174,11 @@ namespace Barotrauma
|
||||
|
||||
AchievementIdentifier = element.GetAttributeString("achievementidentifier", "");
|
||||
|
||||
UnhideEntitySubCategories = element.GetAttributeStringArray("unhideentitysubcategories", new string[0]).ToList();
|
||||
|
||||
Headers = new List<string>();
|
||||
Messages = new List<string>();
|
||||
AllowedLocationTypes = new List<Pair<string, string>>();
|
||||
AllowedConnectionTypes = new List<Pair<string, string>>();
|
||||
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
@@ -183,9 +207,20 @@ namespace Barotrauma
|
||||
messageIndex++;
|
||||
break;
|
||||
case "locationtype":
|
||||
AllowedLocationTypes.Add(new Pair<string, string>(
|
||||
subElement.GetAttributeString("from", ""),
|
||||
subElement.GetAttributeString("to", "")));
|
||||
case "connectiontype":
|
||||
if (subElement.Attribute("identifier") != null)
|
||||
{
|
||||
AllowedLocationTypes.Add(subElement.GetAttributeString("identifier", ""));
|
||||
}
|
||||
else
|
||||
{
|
||||
AllowedConnectionTypes.Add(new Pair<string, string>(
|
||||
subElement.GetAttributeString("from", ""),
|
||||
subElement.GetAttributeString("to", "")));
|
||||
}
|
||||
break;
|
||||
case "locationtypechange":
|
||||
LocationTypeChangeOnCompleted = new LocationTypeChange(subElement.GetAttributeString("from", ""), subElement, requireChangeMessages: false, defaultProbability: 1.0f);
|
||||
break;
|
||||
case "reputation":
|
||||
case "reputationreward":
|
||||
@@ -257,19 +292,32 @@ namespace Barotrauma
|
||||
|
||||
public bool IsAllowed(Location from, Location to)
|
||||
{
|
||||
foreach (Pair<string, string> allowedLocationType in AllowedLocationTypes)
|
||||
if (from == to)
|
||||
{
|
||||
if (allowedLocationType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
return
|
||||
AllowedLocationTypes.Any(lt => lt.Equals("any", StringComparison.OrdinalIgnoreCase)) ||
|
||||
AllowedLocationTypes.Any(lt => lt.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
|
||||
foreach (Pair<string, string> allowedConnectionType in AllowedConnectionTypes)
|
||||
{
|
||||
if (allowedConnectionType.First.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedConnectionType.First.Equals(from.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
if (allowedLocationType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedLocationType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
if (allowedConnectionType.Second.Equals("any", StringComparison.OrdinalIgnoreCase) ||
|
||||
allowedConnectionType.Second.Equals(to.Type.Identifier, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Type == MissionType.Beacon)
|
||||
{
|
||||
var connection = from.Connections.Find(c => c.Locations.Contains(from) && c.Locations.Contains(to));
|
||||
if (connection?.LevelData == null || !connection.LevelData.HasBeaconStation || connection.LevelData.IsBeaconActive) { return false; }
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@ namespace Barotrauma
|
||||
|
||||
private readonly float maxSonarMarkerDistance = 10000.0f;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
@@ -52,6 +53,13 @@ namespace Barotrauma
|
||||
|
||||
maxSonarMarkerDistance = prefab.ConfigElement.GetAttributeFloat("maxsonarmarkerdistance", 10000.0f);
|
||||
|
||||
var spawnPosTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
if (string.IsNullOrWhiteSpace(spawnPosTypeStr) ||
|
||||
!Enum.TryParse(spawnPosTypeStr, true, out spawnPosType))
|
||||
{
|
||||
spawnPosType = Level.PositionType.MainPath | Level.PositionType.SidePath;
|
||||
}
|
||||
|
||||
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
|
||||
{
|
||||
speciesName = monsterElement.GetAttributeString("character", string.Empty);
|
||||
@@ -81,22 +89,32 @@ namespace Barotrauma
|
||||
TextManager.Get("character." + characterParams.SpeciesName));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
if (monsters.Count > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"monsters.Count > 0 ({monsters.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Monster list was not empty at the start of a monster mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
monsters.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (tempSonarPositions.Count > 0)
|
||||
{
|
||||
#if DEBUG
|
||||
throw new Exception($"tempSonarPositions.Count > 0 ({tempSonarPositions.Count})");
|
||||
#else
|
||||
DebugConsole.AddWarning("Sonar position list was not empty at the start of a monster mission. The mission instance may not have been ended correctly on previous rounds.");
|
||||
tempSonarPositions.Clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath | Level.PositionType.SidePath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
Level.Loaded.TryGetInterestingPosition(true, spawnPosType, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
foreach (var monster in monsterPrefabs)
|
||||
{
|
||||
int amount = Rand.Range(monster.Item2.X, monster.Item2.Y + 1);
|
||||
@@ -115,7 +133,7 @@ namespace Barotrauma
|
||||
foreach (var monster in monsters)
|
||||
{
|
||||
monster.Enabled = false;
|
||||
if (monster.Params.AI.EnforceAggressiveBehaviorForMissions)
|
||||
if (monster.Params.AI != null && monster.Params.AI.EnforceAggressiveBehaviorForMissions)
|
||||
{
|
||||
foreach (var targetParam in monster.Params.AI.Targets)
|
||||
{
|
||||
@@ -203,9 +221,17 @@ namespace Barotrauma
|
||||
tempSonarPositions.Clear();
|
||||
monsters.Clear();
|
||||
if (State < 1) { return; }
|
||||
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (level?.LevelData != null && Prefab.Tags.Any(t => t.Equals("huntinggrounds", StringComparison.OrdinalIgnoreCase)))
|
||||
{
|
||||
level.LevelData.HasHuntingGrounds = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsEliminated(Character enemy) =>
|
||||
|
||||
@@ -20,7 +20,9 @@ namespace Barotrauma
|
||||
|
||||
private readonly float itemSpawnRadius = 800.0f;
|
||||
private readonly float approachItemsRadius = 1000.0f;
|
||||
private readonly float nestObjectRadius = 1000.0f;
|
||||
private readonly float monsterSpawnRadius = 3000.0f;
|
||||
private readonly int nestObjectAmount = 10;
|
||||
|
||||
private readonly bool requireDelivery;
|
||||
|
||||
@@ -33,7 +35,14 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
yield return nestPosition;
|
||||
if (State > 0)
|
||||
{
|
||||
Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return nestPosition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +55,9 @@ namespace Barotrauma
|
||||
approachItemsRadius = prefab.ConfigElement.GetAttributeFloat("approachitemsradius", itemSpawnRadius * 2.0f);
|
||||
monsterSpawnRadius = prefab.ConfigElement.GetAttributeFloat("monsterspawnradius", approachItemsRadius * 2.0f);
|
||||
|
||||
nestObjectRadius = prefab.ConfigElement.GetAttributeFloat("nestobjectradius", itemSpawnRadius * 2.0f);
|
||||
nestObjectAmount = prefab.ConfigElement.GetAttributeInt("nestobjectamount", 10);
|
||||
|
||||
requireDelivery = prefab.ConfigElement.GetAttributeBool("requiredelivery", false);
|
||||
|
||||
string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
|
||||
@@ -55,7 +67,6 @@ namespace Barotrauma
|
||||
spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
|
||||
}
|
||||
|
||||
|
||||
foreach (var monsterElement in prefab.ConfigElement.GetChildElements("monster"))
|
||||
{
|
||||
string speciesName = monsterElement.GetAttributeString("character", string.Empty);
|
||||
@@ -79,8 +90,18 @@ namespace Barotrauma
|
||||
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
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
|
||||
@@ -90,6 +111,25 @@ namespace Barotrauma
|
||||
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())
|
||||
{
|
||||
@@ -171,6 +211,11 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnNestObjects(Level level, Level.Cave cave)
|
||||
{
|
||||
level.LevelObjectManager.PlaceNestObjects(level, cave, nestPosition, nestObjectRadius, nestObjectAmount);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
{
|
||||
if (IsClient)
|
||||
@@ -258,9 +303,17 @@ namespace Barotrauma
|
||||
|
||||
public override void End()
|
||||
{
|
||||
if (!AllItemsDestroyedOrRetrieved())
|
||||
if (AllItemsDestroyedOrRetrieved())
|
||||
{
|
||||
return;
|
||||
GiveReward();
|
||||
completed = true;
|
||||
if (completed)
|
||||
{
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (Item item in items)
|
||||
{
|
||||
@@ -270,8 +323,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
items.Clear();
|
||||
GiveReward();
|
||||
completed = true;
|
||||
failed = !completed && state > 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using FarseerPhysics;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
@@ -101,7 +102,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
protected override void StartMissionSpecific(Level level)
|
||||
{
|
||||
#if SERVER
|
||||
originalInventoryID = Entity.NullEntityID;
|
||||
@@ -168,10 +169,11 @@ namespace Barotrauma
|
||||
//try to find a container and place the item inside it
|
||||
if (!string.IsNullOrEmpty(containerTag) && item.ParentInventory == null)
|
||||
{
|
||||
List<ItemContainer> validContainers = new List<ItemContainer>();
|
||||
foreach (Item it in Item.ItemList)
|
||||
{
|
||||
if (!it.HasTag(containerTag)) { continue; }
|
||||
if (it.NonInteractable) { continue; }
|
||||
if (!it.IsPlayerTeamInteractable) { continue; }
|
||||
switch (spawnPositionType)
|
||||
{
|
||||
case Level.PositionType.Cave:
|
||||
@@ -185,15 +187,18 @@ namespace Barotrauma
|
||||
if (it.Submarine == null || it.Submarine.Info.Type != SubmarineType.Wreck) { continue; }
|
||||
break;
|
||||
}
|
||||
var itemContainer = it.GetComponent<Items.Components.ItemContainer>();
|
||||
if (itemContainer == null) { continue; }
|
||||
if (itemContainer.Combine(item, user: null))
|
||||
var itemContainer = it.GetComponent<ItemContainer>();
|
||||
if (itemContainer != null && itemContainer.Inventory.CanBePut(item)) { validContainers.Add(itemContainer); }
|
||||
}
|
||||
if (validContainers.Any())
|
||||
{
|
||||
var selectedContainer = validContainers.GetRandom();
|
||||
if (selectedContainer.Combine(item, user: null))
|
||||
{
|
||||
#if SERVER
|
||||
originalInventoryID = it.ID;
|
||||
originalItemContainerIndex = (byte)it.GetComponentIndex(itemContainer);
|
||||
originalInventoryID = selectedContainer.Item.ID;
|
||||
originalItemContainerIndex = (byte)selectedContainer.Item.GetComponentIndex(selectedContainer);
|
||||
#endif
|
||||
break;
|
||||
} // Placement successful
|
||||
}
|
||||
}
|
||||
@@ -248,6 +253,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (Prefab.LocationTypeChangeOnCompleted != null)
|
||||
{
|
||||
ChangeLocationType(Prefab.LocationTypeChangeOnCompleted);
|
||||
}
|
||||
|
||||
item?.Remove();
|
||||
item = null;
|
||||
GiveReward();
|
||||
|
||||
Reference in New Issue
Block a user