Faction Test v1.0.1.0

This commit is contained in:
Regalis11
2023-02-16 15:01:28 +02:00
parent caa5a2f762
commit 2c5a7923b0
309 changed files with 7502 additions and 4335 deletions
@@ -1,5 +1,4 @@
using Barotrauma.Extensions;
using Barotrauma.Networking;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -8,7 +7,7 @@ using System.Linq;
namespace Barotrauma
{
class MissionAction : EventAction
partial class MissionAction : EventAction
{
[Serialize("", IsPropertySaveable.Yes)]
public Identifier MissionIdentifier { get; set; }
@@ -106,7 +105,8 @@ namespace Barotrauma
IconColor = unlockedMission.Prefab.IconColor
};
#else
NotifyMissionUnlock(unlockedMission, unlockLocation);
missionsUnlockedThisRound.Add(unlockedMission);
NotifyMissionUnlock(unlockedMission);
#endif
}
}
@@ -186,21 +186,5 @@ namespace Barotrauma
{
return $"{ToolBox.GetDebugSymbol(isFinished)} {nameof(MissionAction)} -> ({(MissionIdentifier.IsEmpty ? MissionTag : MissionIdentifier)})";
}
#if SERVER
private static void NotifyMissionUnlock(Mission mission, Location unlockLocation)
{
foreach (Client client in GameMain.Server.ConnectedClients)
{
IWriteMessage outmsg = new WriteOnlyMessage();
outmsg.WriteByte((byte)ServerPacketHeader.EVENTACTION);
outmsg.WriteByte((byte)EventManager.NetworkEventType.MISSION);
outmsg.WriteIdentifier(mission.Prefab.Identifier);
outmsg.WriteInt32(GameMain.GameSession?.Map?.Locations.IndexOf(unlockLocation) ?? -1);
outmsg.WriteString(mission.Name.Value);
GameMain.Server.ServerPeer.Send(outmsg, client.Connection, DeliveryMethod.Reliable);
}
}
#endif
}
}
@@ -69,8 +69,8 @@ namespace Barotrauma
{
DebugConsole.ThrowError($"Error in ModifyLocationAction ({ParentEvent.Prefab.Identifier}): could not find a location type with the identifier \"{Type}\".");
}
else
{
else if (!location.LocationTypeChangesBlocked)
{
location.ChangeType(campaign, locationType);
}
}
@@ -1,7 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Collections.Immutable;
namespace Barotrauma
{
@@ -11,17 +9,20 @@ namespace Barotrauma
public Identifier TargetTag { get; set; }
[Serialize("", IsPropertySaveable.Yes)]
public Identifier ItemIdentifier { get; set; }
public string ItemIdentifiers { get; set; }
[Serialize(1, IsPropertySaveable.Yes)]
public int Amount { get; set; }
public RemoveItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (ItemIdentifier.IsEmpty)
private readonly ImmutableHashSet<Identifier> itemIdentifierSplit;
public RemoveItemAction(ScriptedEvent parentEvent, ContentXElement element) : base(parentEvent, element)
{
if (string.IsNullOrEmpty(ItemIdentifiers))
{
ItemIdentifier = element.GetAttributeIdentifier("itemidentifiers", element.GetAttributeIdentifier("identifier", Identifier.Empty));
ItemIdentifiers = element.GetAttributeString("itemidentifier", element.GetAttributeString("identifier", string.Empty));
}
itemIdentifierSplit = ItemIdentifiers.Split(',').ToIdentifiers().ToImmutableHashSet();
}
private bool isFinished = false;
@@ -62,7 +63,7 @@ namespace Barotrauma
var item = inventory.FindItem(it =>
it != null &&
!removedItems.Contains(it) &&
(ItemIdentifier.IsEmpty || it.Prefab.Identifier == ItemIdentifier), recursive: true);
(itemIdentifierSplit.Count == 0 || itemIdentifierSplit.Contains(it.Prefab.Identifier)), recursive: true);
if (item == null) { break; }
Entity.Spawner.AddItemToRemoveQueue(item);
removedItems.Add(item);
@@ -70,7 +71,7 @@ namespace Barotrauma
}
else if (target is Item item)
{
if (ItemIdentifier.IsEmpty || item.Prefab.Identifier == ItemIdentifier)
if (itemIdentifierSplit.Count == 0 || itemIdentifierSplit.Contains(item.Prefab.Identifier))
{
Entity.Spawner.AddItemToRemoveQueue(item);
removedItems.Add(item);
@@ -93,7 +93,13 @@ namespace Barotrauma
{
ignoreSpawnPointType = element.GetAttribute("spawnpointtype") == null;
//backwards compatibility
TeamID = element.GetAttributeEnum("teamtag", element.GetAttributeEnum<CharacterTeamType>("team", TeamID));
TeamID = element.GetAttributeEnum("teamtag", element.GetAttributeEnum("team", TeamID));
if (element.GetAttribute("submarinetype") != null)
{
DebugConsole.ThrowError(
$"Error in even \"{(parentEvent.Prefab?.Identifier.ToString() ?? "unknown")}\". " +
$"The attribute \"submarinetype\" is not valid in {nameof(SpawnAction)}. Did you mean {nameof(SpawnLocation)}?");
}
}
public override bool IsFinished(ref string goTo)
@@ -320,30 +326,24 @@ namespace Barotrauma
public static WayPoint GetSpawnPos(SpawnLocationType spawnLocation, SpawnType? spawnPointType, IEnumerable<Identifier> moduleFlags = null, IEnumerable<Identifier> spawnpointTags = null, bool asFarAsPossibleFromAirlock = false, bool requireTaggedSpawnPoint = false)
{
bool requireHull = spawnLocation == SpawnLocationType.MainSub || spawnLocation == SpawnLocationType.Outpost;
List<WayPoint> potentialSpawnPoints = WayPoint.WayPointList.FindAll(wp => IsValidSubmarineType(spawnLocation, wp.Submarine) && (wp.CurrentHull != null || !requireHull));
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && !wp.isObstructed);
List<WayPoint> potentialSpawnPoints = WayPoint.WayPointList.FindAll(wp => IsValidSubmarineType(spawnLocation, wp.Submarine) && (wp.CurrentHull != null || !requireHull));
potentialSpawnPoints = potentialSpawnPoints.FindAll(wp => wp.ConnectedDoor == null && wp.Ladders == null && wp.IsTraversable);
if (moduleFlags != null && moduleFlags.Any())
{
List<WayPoint> spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull?.OutpostModuleTags.Any(moduleFlags.Contains) ?? false).ToList();
var spawnPoints = potentialSpawnPoints.Where(wp => wp.CurrentHull is Hull h && h.OutpostModuleTags.Any(moduleFlags.Contains));
if (spawnPoints.Any())
{
potentialSpawnPoints = spawnPoints;
potentialSpawnPoints = spawnPoints.ToList();
}
}
if (spawnpointTags != null && spawnpointTags.Any())
{
var spawnPoints = potentialSpawnPoints
.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && !wp.isObstructed));
var spawnPoints = potentialSpawnPoints.Where(wp => spawnpointTags.Any(tag => wp.Tags.Contains(tag) && wp.ConnectedDoor == null && wp.IsTraversable));
if (requireTaggedSpawnPoint || spawnPoints.Any())
{
potentialSpawnPoints = spawnPoints.ToList();
}
}
if (potentialSpawnPoints.None())
{
if (requireTaggedSpawnPoint && spawnpointTags != null && spawnpointTags.Any())
@@ -119,12 +119,12 @@ namespace Barotrauma
private void TagItemsByIdentifier(Identifier identifier)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.Prefab.Identifier == identifier);
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && IsValidItem(it) && it.Prefab.Identifier == identifier);
}
private void TagItemsByTag(Identifier tag)
{
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && SubmarineTypeMatches(it.Submarine) && it.HasTag(tag));
ParentEvent.AddTargetPredicate(Tag, e => e is Item it && IsValidItem(it) && it.HasTag(tag));
}
private void TagHullsByName(Identifier name)
@@ -137,6 +137,11 @@ namespace Barotrauma
ParentEvent.AddTargetPredicate(Tag, e => e is Submarine s && SubmarineTypeMatches(s) && (type.IsEmpty || type == s.Info?.Type.ToIdentifier()));
}
private bool IsValidItem(Item it)
{
return !it.HiddenInGame && SubmarineTypeMatches(it.Submarine);
}
private bool SubmarineTypeMatches(Submarine sub)
{
if (SubmarineType == SubType.Any) { return true; }
@@ -19,7 +19,9 @@ partial class UIHighlightAction : EventAction
TurbineOutputSlider,
DeconstructButton,
RechargeSpeedSlider,
CPRButton
CPRButton,
CloseButton,
MessageBoxCloseButton
}
[Serialize(ElementId.None, IsPropertySaveable.Yes)]
@@ -135,7 +135,9 @@ namespace Barotrauma
pendingEventSets.Clear();
selectedEvents.Clear();
activeEvents.Clear();
#if SERVER
MissionAction.ResetMissionsUnlockedThisRound();
#endif
pathFinder = new PathFinder(WayPoint.WayPointList, false);
totalPathLength = 0.0f;
if (level != null)
@@ -147,10 +147,7 @@ namespace Barotrauma
ISpatialEntity spawnPoint = SpawnAction.GetSpawnPos(
SpawnAction.SpawnLocationType.Outpost, SpawnType.Human | SpawnType.Enemy,
moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
if (spawnPoint == null)
{
spawnPoint = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
}
spawnPoint ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
Vector2 spawnPos = spawnPoint.WorldPosition;
if (spawnPoint is WayPoint wp && wp.CurrentHull != null && wp.CurrentHull.Rect.Width > 100)
{
@@ -189,7 +186,12 @@ namespace Barotrauma
if (element.Attribute("identifier") != null && element.Attribute("from") != null)
{
HumanPrefab humanPrefab = GetHumanPrefabFromElement(element);
HumanPrefab humanPrefab = GetHumanPrefabFromElement(element);
if (humanPrefab == null)
{
DebugConsole.ThrowError($"Couldn't spawn a human character for abandoned outpost mission: human prefab \"{element.GetAttributeString("identifier", string.Empty)}\" not found");
continue;
}
for (int i = 0; i < count; i++)
{
LoadHuman(humanPrefab, element, submarine);
@@ -201,7 +203,7 @@ namespace Barotrauma
var characterPrefab = CharacterPrefab.FindBySpeciesName(speciesName);
if (characterPrefab == null)
{
DebugConsole.ThrowError("Couldn't spawn a character for abandoned outpost mission: character prefab \"" + speciesName + "\" not found");
DebugConsole.ThrowError($"Couldn't spawn a character for abandoned outpost mission: character prefab \"{speciesName}\" not found");
continue;
}
for (int i = 0; i < count; i++)
@@ -223,10 +225,7 @@ namespace Barotrauma
moduleFlags ?? humanPrefab.GetModuleFlags(),
spawnPointTags ?? humanPrefab.GetSpawnPointTags(),
element.GetAttributeBool("asfaraspossible", false));
if (spawnPos == null)
{
spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
}
spawnPos ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
bool requiresRescue = element.GetAttributeBool("requirerescue", false);
var teamId = element.GetAttributeEnum("teamid", requiresRescue ? CharacterTeamType.FriendlyNPC : CharacterTeamType.None);
@@ -255,6 +254,13 @@ namespace Barotrauma
}
#endif
}
else if (TimesAttempted > 0 && spawnedCharacter.AIController is HumanAIController humanAi)
{
var order = OrderPrefab.Prefabs["fightintruders"]
.CreateInstance(OrderPrefab.OrderTargetType.Entity, orderGiver: spawnedCharacter)
.WithManualPriority(CharacterInfo.HighestManualOrderPriority);
spawnedCharacter.SetOrder(order, isNewOrder: true, speak: false);
}
if (element.GetAttributeBool("requirekill", false))
{
@@ -267,10 +273,7 @@ namespace Barotrauma
Identifier[] moduleFlags = element.GetAttributeIdentifierArray("moduleflags", null);
Identifier[] spawnPointTags = element.GetAttributeIdentifierArray("spawnpointtags", null);
ISpatialEntity spawnPos = SpawnAction.GetSpawnPos(SpawnAction.SpawnLocationType.Outpost, SpawnType.Enemy, moduleFlags, spawnPointTags, element.GetAttributeBool("asfaraspossible", false));
if (spawnPos == null)
{
spawnPos = submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
}
spawnPos ??= submarine.GetHulls(alsoFromConnectedSubs: false).GetRandomUnsynced();
Character spawnedCharacter = Character.Create(monsterPrefab.Identifier, spawnPos.WorldPosition, ToolBox.RandomSeed(8), createNetworkEvent: false);
characters.Add(spawnedCharacter);
if (element.GetAttributeBool("requirekill", false))
@@ -203,8 +203,11 @@ namespace Barotrauma
projectileTimer -= deltaTime;
if (projectileTimer <= 0.0f)
{
float dist = Vector2.Distance(Submarine.MainSub.WorldPosition, boss.WorldPosition);
float distanceFactor = Math.Min(dist / 10000.0f, 1.0f);
int projectileAmount = Rand.Range(3, 6);
float spread = MathHelper.ToRadians(Rand.Range(20.0f, 180.0f));
//more concentrated shots the further the sub is
float spread = MathHelper.ToRadians(Rand.Range(20.0f, 180.0f)) * Math.Max(1.0f - distanceFactor, 0.2f);
for (int i = 0; i < projectileAmount; i++)
{
int index = i;
@@ -218,13 +221,13 @@ namespace Barotrauma
}
it.body.SetTransform(it.SimPosition, angle);
it.UpdateTransform();
projectile.Use();
//faster launch velocity the further the sub is
projectile.Use(launchImpulseModifier: MathHelper.Lerp(0, 5, distanceFactor));
});
}
float dist = Vector2.Distance(Submarine.MainSub.WorldPosition, boss.WorldPosition);
//the closer the sub is, more likely it is to shoot frequently
float shortIntervalProbability = MathHelper.Lerp(0.9f, 0.05f, dist / 15000.0f);
float shortIntervalProbability = MathHelper.Lerp(0.9f, 0.05f, distanceFactor);
if (Rand.Range(0.0f, 1.0f) < shortIntervalProbability)
{
projectileTimer = Rand.Range(3.0f, 5.0f);
@@ -42,6 +42,8 @@ namespace Barotrauma
}
}
public int TimesAttempted { get; set; }
protected static bool IsClient => GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
private readonly CheckDataAction completeCheckDataAction;
@@ -49,6 +51,12 @@ namespace Barotrauma
public readonly ImmutableArray<LocalizedString> Headers;
public readonly ImmutableArray<LocalizedString> Messages;
/// <summary>
/// The reward that was actually given from completing the mission, taking any talent bonuses into account
/// (some of which may not be possible to determine in advance)
/// </summary>
private int? finalReward;
public virtual LocalizedString Name => Prefab.Name;
private readonly LocalizedString successMessage;
@@ -367,6 +375,8 @@ namespace Barotrauma
GiveReward();
}
TimesAttempted++;
EndMissionSpecific(completed);
}
@@ -374,6 +384,27 @@ namespace Barotrauma
protected virtual void EndMissionSpecific(bool completed) { }
/// <summary>
/// Get the final reward, taking talent bonuses into account if the mission has concluded and the talents modified the reward accordingly.
/// </summary>
public int GetFinalReward(Submarine sub)
{
return finalReward ?? GetReward(sub);
}
/// <summary>
/// Calculates the final reward after talent bonuses have been applied. Note that this triggers talent effects of the type OnGainMissionMoney,
/// and should only be called once when the mission is completed!
/// </summary>
private void CalculateFinalReward(Submarine sub)
{
int reward = GetReward(sub);
IEnumerable<Character> crewCharacters = GameSession.GetSessionCrewCharacters(CharacterType.Both);
var missionMoneyGainMultiplier = new AbilityMissionMoneyGainMultiplier(this, 1f);
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
crewCharacters.ForEach(c => missionMoneyGainMultiplier.Value += c.GetStatValue(StatTypes.MissionMoneyGainMultiplier));
finalReward = (int)(reward * missionMoneyGainMultiplier.Value);
}
private void GiveReward()
{
@@ -417,38 +448,35 @@ namespace Barotrauma
info?.GiveExperience((int)((experienceGain * experienceGainMultiplier.Value) * experienceGainMultiplierIndividual.Value));
}
// apply money gains afterwards to prevent them from affecting XP gains
var missionMoneyGainMultiplier = new AbilityMissionMoneyGainMultiplier(this, 1f);
crewCharacters.ForEach(c => c.CheckTalents(AbilityEffectType.OnGainMissionMoney, missionMoneyGainMultiplier));
crewCharacters.ForEach(c => missionMoneyGainMultiplier.Value += c.GetStatValue(StatTypes.MissionMoneyGainMultiplier));
int totalReward = (int)(reward * missionMoneyGainMultiplier.Value);
GameAnalyticsManager.AddMoneyGainedEvent(totalReward, GameAnalyticsManager.MoneySource.MissionReward, Prefab.Identifier.Value);
CalculateFinalReward(Submarine.MainSub);
#if SERVER
totalReward = DistributeRewardsToCrew(GameSession.GetSessionCrewCharacters(CharacterType.Player), totalReward);
finalReward = DistributeRewardsToCrew(GameSession.GetSessionCrewCharacters(CharacterType.Player), finalReward.Value);
#endif
bool isSingleplayerOrServer = GameMain.IsSingleplayer || GameMain.NetworkMember is { IsServer: true };
if (isSingleplayerOrServer && totalReward > 0)
if (isSingleplayerOrServer)
{
campaign.Bank.Give(totalReward);
}
foreach (Character character in crewCharacters)
{
character.Info.MissionsCompletedSinceDeath++;
}
foreach (KeyValuePair<Identifier, float> reputationReward in ReputationRewards)
{
if (reputationReward.Key == "location")
if (finalReward > 0)
{
OriginLocation.Reputation?.AddReputation(reputationReward.Value);
campaign.Bank.Give(finalReward.Value);
}
else
foreach (Character character in crewCharacters)
{
Faction faction = campaign.Factions.Find(faction1 => faction1.Prefab.Identifier == reputationReward.Key);
if (faction != null) { faction.Reputation.AddReputation(reputationReward.Value); }
character.Info.MissionsCompletedSinceDeath++;
}
foreach (KeyValuePair<Identifier, float> reputationReward in ReputationRewards)
{
if (reputationReward.Key == "location")
{
OriginLocation.Reputation?.AddReputation(reputationReward.Value);
}
else
{
Faction faction = campaign.Factions.Find(faction1 => faction1.Prefab.Identifier == reputationReward.Key);
float prevValue = faction.Reputation.Value;
faction?.Reputation.AddReputation(reputationReward.Value);
}
}
}
@@ -493,12 +521,9 @@ namespace Barotrauma
float rewardWeight = sum > 100 ? rewardDistribution / sum : rewardDistribution / 100f;
int rewardPercentage = (int)(rewardWeight * 100);
return reward switch
{
Some<int> { Value: var amount } => ((int)(amount * rewardWeight), rewardPercentage, sum),
None<int> _ => (0, rewardPercentage, sum),
_ => throw new ArgumentOutOfRangeException()
};
int amount = reward.TryUnwrap(out var a) ? a : 0;
return ((int)(amount * rewardWeight), rewardPercentage, sum);
}
protected void ChangeLocationType(LocationTypeChange change)
@@ -518,6 +543,8 @@ namespace Barotrauma
if (srcIndex == -1) { return; }
var location = Locations[srcIndex];
if (location.LocationTypeChangesBlocked) { return; }
if (change.RequiredDurationRange.X > 0)
{
location.PendingLocationTypeChange = (change, Rand.Range(change.RequiredDurationRange.X, change.RequiredDurationRange.Y), Prefab);
@@ -99,7 +99,13 @@ namespace Barotrauma
public readonly bool RequireWreck, RequireRuin;
/// <summary>
/// If enabled, locations this mission takes place in cannot change their type
/// </summary>
public readonly bool BlockLocationTypeChanges;
public readonly bool ShowProgressBar;
public readonly bool ShowProgressInNumbers;
public readonly int MaxProgressState;
public readonly LocalizedString ProgressBarLabel;
@@ -178,6 +184,7 @@ namespace Barotrauma
IsSideObjective = element.GetAttributeBool("sideobjective", false);
RequireWreck = element.GetAttributeBool("requirewreck", false);
RequireRuin = element.GetAttributeBool("requireruin", false);
BlockLocationTypeChanges = element.GetAttributeBool(nameof(BlockLocationTypeChanges), false);
Commonness = element.GetAttributeInt("commonness", 1);
AllowOtherMissionsInLevel = element.GetAttributeBool("allowothermissionsinlevel", true);
if (element.GetAttribute("difficulty") != null)
@@ -187,6 +194,7 @@ namespace Barotrauma
}
ShowProgressBar = element.GetAttributeBool(nameof(ShowProgressBar), false);
ShowProgressInNumbers = element.GetAttributeBool(nameof(ShowProgressInNumbers), false);
MaxProgressState = element.GetAttributeInt(nameof(MaxProgressState), 1);
string progressBarLabel = element.GetAttributeString(nameof(ProgressBarLabel), "");
ProgressBarLabel = TextManager.Get(progressBarLabel).Fallback(progressBarLabel);
@@ -234,6 +234,12 @@ namespace Barotrauma
if (!string.IsNullOrEmpty(target.ExistingItemTag))
{
var suitableItems = Item.ItemList.Where(it => it.HasTag(target.ExistingItemTag));
if (GameMain.GameSession?.Missions != null)
{
//don't choose an item that was already chosen as the target for another salvage mission
suitableItems = suitableItems.Where(it =>
GameMain.GameSession.Missions.None(m => m != this && m is SalvageMission salvageMission && salvageMission.targets.Any(t => t.Item == it)));
}
switch (target.SpawnPositionType)
{
case Level.PositionType.Cave: