Unstable 0.17.1.0
This commit is contained in:
@@ -388,7 +388,7 @@ namespace Barotrauma
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (targetingTag == null)
|
||||
if (targetingTag.IsNullOrEmpty())
|
||||
{
|
||||
if (targetItem.GetComponent<Sonar>() != null)
|
||||
{
|
||||
@@ -2100,15 +2100,11 @@ namespace Barotrauma
|
||||
if (!ActiveAttack.IsRunning)
|
||||
{
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new object[]
|
||||
{
|
||||
Networking.NetEntityEvent.Type.SetAttackTarget,
|
||||
GameMain.NetworkMember.CreateEntityEvent(Character, new Character.SetAttackTargetEventData(
|
||||
attackingLimb,
|
||||
(damageTarget as Entity)?.ID ?? Entity.NullEntityID,
|
||||
damageTarget is Character character && targetLimb != null ? Array.IndexOf(character.AnimController.Limbs, targetLimb) : 0,
|
||||
SimPosition.X,
|
||||
SimPosition.Y
|
||||
});
|
||||
damageTarget,
|
||||
targetLimb,
|
||||
SimPosition));
|
||||
#else
|
||||
Character.PlaySound(CharacterSound.SoundType.Attack, maxInterval: 3);
|
||||
#endif
|
||||
@@ -2696,7 +2692,7 @@ namespace Barotrauma
|
||||
float target = targetParams.Threshold;
|
||||
if (targetParams.ThresholdMin > 0 && targetParams.ThresholdMax > 0)
|
||||
{
|
||||
target = selectedTargetingParams == targetParams ? targetParams.ThresholdMax : targetParams.ThresholdMin;
|
||||
target = selectedTargetingParams == targetParams && State == AIState.FleeTo ? targetParams.ThresholdMax : targetParams.ThresholdMin;
|
||||
}
|
||||
if (Character.HealthPercentage > target)
|
||||
{
|
||||
|
||||
@@ -569,7 +569,8 @@ namespace Barotrauma
|
||||
(Character.Submarine.TeamID != Character.TeamID && !Character.IsEscorted) ||
|
||||
ObjectiveManager.CurrentOrders.Any(o => o.Objective.KeepDivingGearOnAlsoWhenInactive) ||
|
||||
ObjectiveManager.CurrentObjective.GetSubObjectivesRecursive(true).Any(o => o.KeepDivingGearOn) ||
|
||||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10;
|
||||
Character.CurrentHull.OxygenPercentage < HULL_LOW_OXYGEN_PERCENTAGE + 10 ||
|
||||
Character.CurrentHull.IsWetRoom;
|
||||
bool IsOrderedToWait() => Character.IsOnPlayerTeam && ObjectiveManager.CurrentOrder is AIObjectiveGoTo goTo && goTo.Target == Character;
|
||||
bool removeDivingSuit = !shouldKeepTheGearOn && !IsOrderedToWait();
|
||||
if (oxygenLow && Character.CurrentHull.Oxygen > 0 && (!isCurrentObjectiveFindSafety || Character.OxygenAvailable < 1))
|
||||
@@ -1265,15 +1266,15 @@ namespace Barotrauma
|
||||
{
|
||||
if (!IsFriendly(attacker))
|
||||
{
|
||||
if (Character.Submarine == null)
|
||||
if (c.Submarine == null)
|
||||
{
|
||||
// Outside
|
||||
return attacker.Submarine == null ? AIObjectiveCombat.CombatMode.Defensive : AIObjectiveCombat.CombatMode.Retreat;
|
||||
}
|
||||
if (!Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
if (!c.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
// Attacked from an unconnected submarine.
|
||||
return Character.SelectedConstruction?.GetComponent<Turret>() != null ? AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat;
|
||||
return c.SelectedConstruction?.GetComponent<Turret>() != null ? AIObjectiveCombat.CombatMode.None : AIObjectiveCombat.CombatMode.Retreat;
|
||||
}
|
||||
return c.AIController is HumanAIController humanAI &&
|
||||
(humanAI.ObjectiveManager.IsCurrentOrder<AIObjectiveFightIntruders>() || humanAI.ObjectiveManager.Objectives.Any(o => o is AIObjectiveFightIntruders))
|
||||
@@ -1285,18 +1286,22 @@ namespace Barotrauma
|
||||
{
|
||||
cumulativeDamage = 100;
|
||||
}
|
||||
if (GameMain.IsSingleplayer && attacker.IsPlayer && Character.TeamID == attacker.TeamID)
|
||||
if (attacker.IsPlayer && c.TeamID == attacker.TeamID)
|
||||
{
|
||||
// Bots in the player team never act aggressively in single player when attacked by the player
|
||||
return cumulativeDamage > minorDamageThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
|
||||
if (GameMain.IsSingleplayer || Character.TeamID != attacker.TeamID)
|
||||
{
|
||||
// Bots in the player team never act aggressively in single player when attacked by the player
|
||||
// In multiplayer, they react only to players attacking them or other crew members
|
||||
return Character == c && cumulativeDamage > minorDamageThreshold ? AIObjectiveCombat.CombatMode.Retreat : AIObjectiveCombat.CombatMode.None;
|
||||
}
|
||||
}
|
||||
if (Character.Submarine == null || !Character.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
if (c.Submarine == null || !c.Submarine.GetConnectedSubs().Contains(attacker.Submarine))
|
||||
{
|
||||
// Outside or attacked from an unconnected submarine -> don't react.
|
||||
return AIObjectiveCombat.CombatMode.None;
|
||||
}
|
||||
// If there are any enemies around, just ignore the friendly fire
|
||||
if (Character.CharacterList.Any(ch => ch.Submarine == Character.Submarine && !ch.Removed && !ch.IsIncapacitated && !IsFriendly(ch) && VisibleHulls.Contains(ch.CurrentHull)))
|
||||
if (Character.CharacterList.Any(ch => ch.Submarine == c.Submarine && !ch.Removed && !ch.IsIncapacitated && !IsFriendly(ch) && VisibleHulls.Contains(ch.CurrentHull)))
|
||||
{
|
||||
isAttackerFightingEnemy = true;
|
||||
return AIObjectiveCombat.CombatMode.None;
|
||||
@@ -1352,18 +1357,19 @@ namespace Barotrauma
|
||||
|
||||
Character FindInstigator()
|
||||
{
|
||||
if (Character.IsInstigator)
|
||||
if (attacker.IsInstigator)
|
||||
{
|
||||
return Character;
|
||||
return attacker;
|
||||
}
|
||||
else if (c.AIController is HumanAIController humanAi)
|
||||
if (c.IsInstigator)
|
||||
{
|
||||
return c;
|
||||
}
|
||||
if (c.AIController is HumanAIController humanAi)
|
||||
{
|
||||
return Character.CharacterList.FirstOrDefault(ch => ch.Submarine == c.Submarine && !ch.Removed && !ch.IsIncapacitated && ch.IsInstigator && humanAi.VisibleHulls.Contains(ch.CurrentHull));
|
||||
}
|
||||
else
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1497,7 +1503,7 @@ namespace Barotrauma
|
||||
if (hull == null ||
|
||||
hull.WaterPercentage > 90 ||
|
||||
hull.LethalPressure > 0 ||
|
||||
hull.ConnectedGaps.Any(gap => !gap.IsRoomToRoom && gap.Open > 0.5f))
|
||||
hull.ConnectedGaps.Any(gap => !gap.IsRoomToRoom && gap.Open > 0.9f))
|
||||
{
|
||||
needsSuit = !Character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
return true;
|
||||
|
||||
+1
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
if (!character.Submarine.IsConnectedTo(item.Submarine)) { return false; }
|
||||
}
|
||||
if (item.ConditionPercentage <= 0) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { return false; }
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
|
||||
if (IsReady(battery)) { return false; }
|
||||
return true;
|
||||
|
||||
+3
-1
@@ -83,7 +83,8 @@ namespace Barotrauma
|
||||
container.HasTag("allowcleanup") &&
|
||||
container.ParentInventory == null && container.OwnInventory != null && container.OwnInventory.AllItems.Any() &&
|
||||
container.GetComponent<ItemContainer>() != null &&
|
||||
IsItemInsideValidSubmarine(container, character);
|
||||
IsItemInsideValidSubmarine(container, character) &&
|
||||
!container.IsClaimedByBallastFlora;
|
||||
|
||||
public static bool IsValidTarget(Item item, Character character, bool checkInventory, bool allowUnloading = true)
|
||||
{
|
||||
@@ -100,6 +101,7 @@ namespace Barotrauma
|
||||
if (!IsValidContainer(item.Container, character, allowUnloading)) { return false; }
|
||||
}
|
||||
if (character != null && !IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||
if (item.HasBallastFloraInHull) { return false; }
|
||||
var pickable = item.GetComponent<Pickable>();
|
||||
if (pickable == null) { return false; }
|
||||
if (pickable is Holdable h && h.Attachable && h.Attached) { return false; }
|
||||
|
||||
+1
-1
@@ -777,7 +777,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (retreatTarget != null && character.CurrentHull != retreatTarget)
|
||||
{
|
||||
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager, false, true)
|
||||
TryAddSubObjective(ref retreatObjective, () => new AIObjectiveGoTo(retreatTarget, character, objectiveManager)
|
||||
{
|
||||
UsePathingOutside = false
|
||||
},
|
||||
|
||||
-1
@@ -162,7 +162,6 @@ namespace Barotrauma
|
||||
CloseEnough = reach,
|
||||
DialogueIdentifier = Leak.FlowTargetHull != null ? "dialogcannotreachleak".ToIdentifier() : Identifier.Empty,
|
||||
TargetName = Leak.FlowTargetHull?.DisplayName,
|
||||
CheckVisibility = false,
|
||||
requiredCondition = () => Leak.Submarine == character.Submarine,
|
||||
// The Go To objective can be abandoned if the leak is fixed (in which case we don't want to use the dialogue)
|
||||
SpeakCannotReachCondition = () => !CheckObjectiveSpecific()
|
||||
|
||||
+6
-13
@@ -1,7 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
@@ -11,6 +10,8 @@ namespace Barotrauma
|
||||
{
|
||||
public override Identifier Identifier { get; set; } = "go to".ToIdentifier();
|
||||
|
||||
public override bool KeepDivingGearOn => GetTargetHull() == null;
|
||||
|
||||
private AIObjectiveFindDivingGear findDivingGear;
|
||||
private readonly bool repeat;
|
||||
//how long until the path to the target is declared unreachable
|
||||
@@ -74,14 +75,6 @@ namespace Barotrauma
|
||||
_closeEnough = Math.Max(minDistance, value);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Currently we never check the visibility (to the end node), which is actually unintentional.
|
||||
// I don't think it has caused any issues so far, so let's keep defaulting to false for now, because the less we do raycasts the better.
|
||||
// However, if there are cases where the bots attempt to go through walls (select the end node that is behind an obstacle), we should set this true.
|
||||
|
||||
// NOTE: This seemes to have caused an issue now Regalis11/Barotrauma#8067: namely, the bot was trying to use a waypoint that was obstructed by a shuttle
|
||||
// because obstruction was only checked when checking visibility in PathFinder. Changed that so that obstructed nodes are no longer used.
|
||||
public bool CheckVisibility { get; set; }
|
||||
public bool IgnoreIfTargetDead { get; set; }
|
||||
public bool AllowGoingOutside { get; set; }
|
||||
|
||||
@@ -268,15 +261,15 @@ namespace Barotrauma
|
||||
{
|
||||
Character followTarget = Target as Character;
|
||||
bool needsDivingSuit = (!isInside || hasOutdoorNodes) && character.NeedsAir && !character.HasAbilityFlag(AbilityFlags.ImmuneToPressure);
|
||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit)) && character.NeedsAir;
|
||||
bool needsDivingGear = (needsDivingSuit || HumanAIController.NeedsDivingGear(targetHull, out needsDivingSuit));
|
||||
if (Mimic)
|
||||
{
|
||||
if (HumanAIController.HasDivingSuit(followTarget) && character.NeedsAir)
|
||||
if (HumanAIController.HasDivingSuit(followTarget))
|
||||
{
|
||||
needsDivingGear = true;
|
||||
needsDivingSuit = true;
|
||||
}
|
||||
else if (HumanAIController.HasDivingMask(followTarget) && character.NeedsAir)
|
||||
else if (HumanAIController.HasDivingMask(followTarget))
|
||||
{
|
||||
needsDivingGear = true;
|
||||
}
|
||||
@@ -505,7 +498,7 @@ namespace Barotrauma
|
||||
startNodeFilter: n => (n.Waypoint.CurrentHull == null) == (character.CurrentHull == null),
|
||||
endNodeFilter: endNodeFilter,
|
||||
nodeFilter: nodeFilter,
|
||||
checkVisiblity: CheckVisibility);
|
||||
checkVisiblity: Target is Item || Target is Character);
|
||||
}
|
||||
if (!isInside && (PathSteering.CurrentPath == null || PathSteering.IsPathDirty || PathSteering.CurrentPath.Unreachable))
|
||||
{
|
||||
|
||||
+1
@@ -60,6 +60,7 @@ namespace Barotrauma
|
||||
if (targetCondition.HasValue && container.Inventory.IsFull() && container.Inventory.AllItems.None(i => ItemMatchesTargetCondition(i, targetCondition.Value))) { return false; }
|
||||
if (!AIObjectiveCleanupItems.IsItemInsideValidSubmarine(item, character)) { return false; }
|
||||
if (item.GetRootInventoryOwner() is Character owner && owner != character) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { return false; }
|
||||
if (!item.HasAccess(character)) { return false; }
|
||||
// Ignore items that require power but don't have it
|
||||
if (item.GetComponent<Powered>() is Powered powered && powered.PowerConsumption > 0 && powered.Voltage < powered.MinVoltage) { return false; }
|
||||
|
||||
+22
-20
@@ -10,6 +10,16 @@ namespace Barotrauma
|
||||
{
|
||||
class AIObjectiveManager
|
||||
{
|
||||
public enum ObjectiveType
|
||||
{
|
||||
None = 0,
|
||||
Order = 1,
|
||||
Objective = 2,
|
||||
|
||||
MinValue = 0,
|
||||
MaxValue = 2
|
||||
}
|
||||
|
||||
public const float HighestOrderPriority = 70;
|
||||
public const float LowestOrderPriority = 60;
|
||||
public const float RunPriority = 50;
|
||||
@@ -184,28 +194,20 @@ namespace Barotrauma
|
||||
{
|
||||
var previousObjective = CurrentObjective;
|
||||
var firstObjective = Objectives.FirstOrDefault();
|
||||
|
||||
bool currentObjectiveIsOrder = CurrentOrder != null && firstObjective != null && CurrentOrder.Priority > firstObjective.Priority;
|
||||
if (currentObjectiveIsOrder)
|
||||
|
||||
CurrentObjective = currentObjectiveIsOrder ? CurrentOrder : firstObjective;
|
||||
|
||||
if (previousObjective == CurrentObjective) { return CurrentObjective; }
|
||||
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority(Math.Max(CurrentObjective.Priority - 10, 0));
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
CurrentObjective = CurrentOrder;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentObjective = firstObjective;
|
||||
}
|
||||
if (previousObjective != CurrentObjective)
|
||||
{
|
||||
previousObjective?.OnDeselected();
|
||||
CurrentObjective?.OnSelected();
|
||||
GetObjective<AIObjectiveIdle>().CalculatePriority(Math.Max(CurrentObjective.Priority - 10, 0));
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ObjectiveManagerState,
|
||||
currentObjectiveIsOrder ? "order" : "objective"
|
||||
});
|
||||
}
|
||||
GameMain.NetworkMember.CreateEntityEvent(character,
|
||||
new Character.ObjectiveManagerStateEventData(currentObjectiveIsOrder ? ObjectiveType.Order : ObjectiveType.Objective));
|
||||
}
|
||||
return CurrentObjective;
|
||||
}
|
||||
|
||||
+5
@@ -67,6 +67,11 @@ namespace Barotrauma
|
||||
Priority = 0;
|
||||
return Priority;
|
||||
}
|
||||
else if (targetItem.IsClaimedByBallastFlora)
|
||||
{
|
||||
Priority = 0;
|
||||
return Priority;
|
||||
}
|
||||
var reactor = component?.Item.GetComponent<Reactor>();
|
||||
if (reactor != null)
|
||||
{
|
||||
|
||||
+1
@@ -41,6 +41,7 @@ namespace Barotrauma
|
||||
if (!character.Submarine.IsConnectedTo(pump.Item.Submarine)) { return false; }
|
||||
}
|
||||
if (Character.CharacterList.Any(c => c.CurrentHull == pump.Item.CurrentHull && !HumanAIController.IsFriendly(c) && HumanAIController.IsActive(c))) { return false; }
|
||||
if (pump.Item.IsClaimedByBallastFlora) { return false; }
|
||||
if (IsReady(pump)) { return false; }
|
||||
return true;
|
||||
}
|
||||
|
||||
+5
-1
@@ -1,7 +1,6 @@
|
||||
using Barotrauma.Items.Components;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
@@ -12,6 +11,7 @@ namespace Barotrauma
|
||||
public override Identifier Identifier { get; set; } = "repair item".ToIdentifier();
|
||||
|
||||
public override bool AllowInAnySub => true;
|
||||
public override bool KeepDivingGearOn => Item?.CurrentHull == null;
|
||||
|
||||
public Item Item { get; private set; }
|
||||
|
||||
@@ -52,6 +52,10 @@ namespace Barotrauma
|
||||
Priority = 0;
|
||||
IsCompleted = true;
|
||||
}
|
||||
else if (Item.IsClaimedByBallastFlora)
|
||||
{
|
||||
Priority = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
float distanceFactor = 1;
|
||||
|
||||
+1
@@ -151,6 +151,7 @@ namespace Barotrauma
|
||||
if (!item.IsInteractable(character)) { return false; }
|
||||
if (item.IsFullCondition) { return false; }
|
||||
if (item.Submarine == null || character.Submarine == null) { return false; }
|
||||
if (item.IsClaimedByBallastFlora) { return false; }
|
||||
//player crew ignores items in outposts
|
||||
if (character.IsOnPlayerTeam && item.Submarine.Info.IsOutpost) { return false; }
|
||||
if (!character.Submarine.IsEntityFoundOnThisSub(item, includingConnectedSubs: true)) { return false; }
|
||||
|
||||
+12
-9
@@ -30,6 +30,7 @@ namespace Barotrauma
|
||||
private float findHullTimer;
|
||||
private bool ignoreOxygen;
|
||||
private readonly float findHullInterval = 1.0f;
|
||||
private bool performedCpr;
|
||||
|
||||
public AIObjectiveRescue(Character character, Character targetCharacter, AIObjectiveManager objectiveManager, float priorityModifier = 1)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -220,12 +221,12 @@ namespace Barotrauma
|
||||
DialogueIdentifier = "dialogcannotreachpatient".ToIdentifier(),
|
||||
TargetName = targetCharacter.DisplayName
|
||||
},
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
onCompleted: () => RemoveSubObjective(ref goToObjective),
|
||||
onAbandon: () =>
|
||||
{
|
||||
RemoveSubObjective(ref goToObjective);
|
||||
Abandon = true;
|
||||
});
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -401,6 +402,7 @@ namespace Barotrauma
|
||||
{
|
||||
character.SelectCharacter(targetCharacter);
|
||||
character.AnimController.Anim = AnimController.Animation.CPR;
|
||||
performedCpr = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -436,9 +438,10 @@ namespace Barotrauma
|
||||
{
|
||||
bool isCompleted = AIObjectiveRescueAll.GetVitalityFactor(targetCharacter) >= AIObjectiveRescueAll.GetVitalityThreshold(objectiveManager, character, targetCharacter);
|
||||
if (isCompleted && targetCharacter != character && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.GetWithVariable("DialogTargetHealed", "[targetname]", targetCharacter.Name).Value,
|
||||
null, 1.0f, $"targethealed{targetCharacter.Name}".ToIdentifier(), 60.0f);
|
||||
{
|
||||
string textTag = performedCpr ? "DialogTargetResuscitated" : "DialogTargetHealed";
|
||||
string message = TextManager.GetWithVariable(textTag, "[targetname]", targetCharacter.Name)?.Value;
|
||||
character.Speak(message, delay: 1.0f, identifier: $"targethealed{targetCharacter.Name}".ToIdentifier(), minDurationBetweenSimilar: 60.0f);
|
||||
}
|
||||
return isCompleted;
|
||||
}
|
||||
|
||||
@@ -392,7 +392,6 @@ namespace Barotrauma
|
||||
return option;
|
||||
}
|
||||
|
||||
|
||||
public ImmutableArray<Identifier> GetTargetItems(Identifier option = default)
|
||||
{
|
||||
if (option.IsEmpty || !OptionTargetItems.TryGetValue(option, out ImmutableArray<Identifier> optionTargetItems))
|
||||
@@ -418,6 +417,28 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public override void Dispose() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create an Order instance with a null target
|
||||
/// </summary>
|
||||
public Order CreateInstance(OrderTargetType targetType, Character orderGiver = null, bool isAutonomous = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
return targetType switch
|
||||
{
|
||||
OrderTargetType.Entity => new Order(this, targetEntity: null, targetItem: null, orderGiver, isAutonomous),
|
||||
OrderTargetType.Position => new Order(this, target: null, orderGiver),
|
||||
OrderTargetType.WallSection => new Order(this, wall: null, sectionIndex: null, orderGiver),
|
||||
_ => throw new NotImplementedException()
|
||||
};
|
||||
}
|
||||
catch (NotImplementedException e)
|
||||
{
|
||||
DebugConsole.ShowError($"Error creating a new Order instance: unexpected target type \"{targetType}\".\n{e.StackTrace.CleanupStackTrace()}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Order
|
||||
@@ -510,28 +531,45 @@ namespace Barotrauma
|
||||
public readonly bool UseController;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for order instances
|
||||
/// Constructor for orders with the target type OrderTargetType.Entity
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
|
||||
: this(prefab, Identifier.Empty, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.Entity
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, Identifier option, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
|
||||
: this(prefab, option, 0, OrderType.Current, null, targetEntity, targetItem, orderGiver, isAutonomous) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.Position
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, OrderTarget target, Character orderGiver = null)
|
||||
: this(prefab, prefab.Options.FirstOrDefault(), 0, OrderType.Current, null, target, orderGiver) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.Position
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, Identifier option, OrderTarget target, Character orderGiver = null)
|
||||
: this(prefab, option, 0, OrderType.Current, null, target, orderGiver) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.WallSection
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, Structure wall, int? sectionIndex, Character orderGiver = null)
|
||||
: this(prefab, Identifier.Empty, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
|
||||
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.WallSection
|
||||
/// </summary>
|
||||
public Order(OrderPrefab prefab, Identifier option, Structure wall, int? sectionIndex, Character orderGiver = null)
|
||||
: this(prefab, option, 0, OrderType.Current, null, wall, sectionIndex, orderGiver) { }
|
||||
|
||||
public Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.Entity
|
||||
/// </summary>
|
||||
private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Entity targetEntity, ItemComponent targetItem, Character orderGiver = null, bool isAutonomous = false)
|
||||
{
|
||||
Prefab = prefab;
|
||||
Option = option;
|
||||
@@ -561,14 +599,20 @@ namespace Barotrauma
|
||||
TargetType = OrderTargetType.Entity;
|
||||
}
|
||||
|
||||
public Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, OrderTarget target, Character orderGiver = null)
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.Position
|
||||
/// </summary>
|
||||
private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, OrderTarget target, Character orderGiver = null)
|
||||
: this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: null, targetItem: null, orderGiver)
|
||||
{
|
||||
TargetPosition = target;
|
||||
TargetType = OrderTargetType.Position;
|
||||
}
|
||||
|
||||
public Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Structure wall, int? sectionIndex, Character orderGiver = null)
|
||||
/// <summary>
|
||||
/// Constructor for orders with the target type OrderTargetType.WallSection
|
||||
/// </summary>
|
||||
private Order(OrderPrefab prefab, Identifier option, int manualPriority, OrderType orderType, AIObjective aiObjective, Structure wall, int? sectionIndex, Character orderGiver = null)
|
||||
: this(prefab, option, manualPriority, orderType, aiObjective, targetEntity: wall, null, orderGiver: orderGiver)
|
||||
{
|
||||
WallSectionIndex = sectionIndex;
|
||||
@@ -633,7 +677,7 @@ namespace Barotrauma
|
||||
|
||||
public Order WithTargetEntity(Entity entity)
|
||||
{
|
||||
return new Order(this, targetEntity: entity);
|
||||
return new Order(this, targetEntity: entity, targetType: OrderTargetType.Entity);
|
||||
}
|
||||
|
||||
public Order WithTargetSpatialEntity(ISpatialEntity spatialEntity)
|
||||
@@ -673,7 +717,7 @@ namespace Barotrauma
|
||||
|
||||
public Order WithTargetPosition(OrderTarget targetPosition)
|
||||
{
|
||||
return new Order(this, targetPosition: targetPosition);
|
||||
return new Order(this, targetPosition: targetPosition, targetType: OrderTargetType.Position);
|
||||
}
|
||||
|
||||
public Order Clone()
|
||||
|
||||
@@ -313,8 +313,7 @@ namespace Barotrauma
|
||||
ShipCommandLog("Dismissing " + shipIssueWorker + " for character " + shipIssueWorker.OrderedCharacter);
|
||||
#endif
|
||||
var order = new Order(OrderPrefab.Dismissal, null).WithManualPriority(3).WithOrderGiver(character);
|
||||
//character.Speak(orderPrefab.GetChatMessage(shipIssueWorker.OrderedCharacter.Name, "", givingOrderToSelf: false));
|
||||
shipIssueWorker.OrderedCharacter.SetOrder(order);
|
||||
shipIssueWorker.OrderedCharacter.SetOrder(order, isNewOrder: true);
|
||||
shipIssueWorker.RemoveOrder();
|
||||
break;
|
||||
}
|
||||
@@ -368,7 +367,7 @@ namespace Barotrauma
|
||||
ShipGlobalIssueFixLeaks shipGlobalIssueFixLeaks = new ShipGlobalIssueFixLeaks(this);
|
||||
for (int i = 0; i < crewSizeModifier; i++)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["fixleaks"], null);
|
||||
var order = OrderPrefab.Prefabs["fixleaks"].CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
ShipIssueWorkers.Add(new ShipIssueWorkerFixLeaks(this, order, shipGlobalIssueFixLeaks));
|
||||
}
|
||||
shipGlobalIssues.Add(shipGlobalIssueFixLeaks);
|
||||
@@ -376,7 +375,7 @@ namespace Barotrauma
|
||||
ShipGlobalIssueRepairSystems shipGlobalIssueRepairSystems = new ShipGlobalIssueRepairSystems(this);
|
||||
for (int i = 0; i < crewSizeModifier; i++)
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["repairsystems"], null);
|
||||
var order = OrderPrefab.Prefabs["repairsystems"].CreateInstance(OrderPrefab.OrderTargetType.Entity);
|
||||
ShipIssueWorkers.Add(new ShipIssueWorkerRepairSystems(this, order, shipGlobalIssueRepairSystems));
|
||||
}
|
||||
shipGlobalIssues.Add(shipGlobalIssueRepairSystems);
|
||||
|
||||
@@ -442,7 +442,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
public void ServerWrite(IWriteMessage msg, Client client, object[] extraData = null)
|
||||
public void ServerEventWrite(IWriteMessage msg, Client client, NetEntityEvent.IData extraData = null)
|
||||
{
|
||||
msg.Write(IsAlive);
|
||||
}
|
||||
|
||||
+17
-12
@@ -1017,24 +1017,29 @@ namespace Barotrauma
|
||||
{
|
||||
if (l.IsSevered) { continue; }
|
||||
|
||||
float rotation = l.body.Rotation;
|
||||
if (l.DoesFlip)
|
||||
{
|
||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||
{
|
||||
//horizontally oriented sprites can be mirrored by rotating 180 deg and inverting the angle
|
||||
rotation = -(l.body.Rotation + MathHelper.Pi);
|
||||
}
|
||||
else
|
||||
{
|
||||
//vertically oriented limbs can be mirrored by inverting the angle (neutral angle is straight upwards)
|
||||
rotation = -l.body.Rotation;
|
||||
}
|
||||
}
|
||||
|
||||
TrySetLimbPosition(l,
|
||||
centerOfMass,
|
||||
new Vector2(centerOfMass.X - (l.SimPosition.X - centerOfMass.X), l.SimPosition.Y),
|
||||
rotation,
|
||||
lerp);
|
||||
|
||||
l.body.PositionSmoothingFactor = 0.8f;
|
||||
|
||||
if (!l.DoesFlip) { continue; }
|
||||
if (RagdollParams.IsSpritesheetOrientationHorizontal)
|
||||
{
|
||||
//horizontally oriented sprites can be mirrored by rotating 180 deg and inverting the angle
|
||||
l.body.SetTransform(l.SimPosition, -(l.body.Rotation + MathHelper.Pi));
|
||||
}
|
||||
else
|
||||
{
|
||||
//vertically oriented limbs can be mirrored by inverting the angle (neutral angle is straight upwards)
|
||||
l.body.SetTransform(l.SimPosition, -l.body.Rotation);
|
||||
}
|
||||
|
||||
}
|
||||
if (character.SelectedCharacter != null && CanDrag(character.SelectedCharacter))
|
||||
{
|
||||
|
||||
+2
-4
@@ -1846,11 +1846,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
float angle = flipAngle ? -limb.body.Rotation : limb.body.Rotation;
|
||||
if (wrapAngle) angle = MathUtils.WrapAnglePi(angle);
|
||||
if (wrapAngle) { angle = MathUtils.WrapAnglePi(angle); }
|
||||
|
||||
TrySetLimbPosition(limb, Collider.SimPosition, position);
|
||||
|
||||
limb.body.SetTransform(limb.body.SimPosition, angle);
|
||||
TrySetLimbPosition(limb, Collider.SimPosition, position, angle);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -803,9 +803,9 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
SeverLimbJointProjSpecific(limbJoint, playSound: true);
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new object[] { NetEntityEvent.Type.Status });
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new Character.StatusEventData());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -1693,7 +1693,7 @@ namespace Barotrauma
|
||||
if (limb.IsSevered) { continue; }
|
||||
//check visibility from the new position of the collider to the new position of this limb
|
||||
Vector2 movePos = limb.SimPosition + limbMoveAmount;
|
||||
TrySetLimbPosition(limb, simPosition, movePos, lerp, ignorePlatforms);
|
||||
TrySetLimbPosition(limb, simPosition, movePos, limb.Rotation, lerp, ignorePlatforms);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1708,7 +1708,7 @@ namespace Barotrauma
|
||||
IsHanging = true;
|
||||
}
|
||||
|
||||
protected void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, bool lerp = false, bool ignorePlatforms = true)
|
||||
protected void TrySetLimbPosition(Limb limb, Vector2 original, Vector2 simPosition, float rotation, bool lerp = false, bool ignorePlatforms = true)
|
||||
{
|
||||
Vector2 movePos = simPosition;
|
||||
|
||||
@@ -1730,11 +1730,12 @@ namespace Barotrauma
|
||||
if (lerp)
|
||||
{
|
||||
limb.body.TargetPosition = movePos;
|
||||
limb.body.MoveToTargetPosition(true);
|
||||
limb.body.TargetRotation = rotation;
|
||||
limb.body.MoveToTargetPosition(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
limb.body.SetTransform(movePos, limb.Rotation);
|
||||
limb.body.SetTransform(movePos, rotation);
|
||||
limb.PullJointWorldAnchorB = limb.PullJointWorldAnchorA;
|
||||
limb.PullJointEnabled = false;
|
||||
}
|
||||
|
||||
@@ -487,6 +487,10 @@ namespace Barotrauma
|
||||
// TODO: do we want to apply the effect at the world position or the entity positions in each cases? -> go through also other cases where status effects are applied
|
||||
effect.Apply(effectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity, worldPosition);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Parent))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, attacker);
|
||||
}
|
||||
if (targetCharacter != null)
|
||||
{
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
@@ -551,6 +555,10 @@ namespace Barotrauma
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, sourceLimb ?? attacker as ISerializableEntity);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Parent))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, attacker, attacker);
|
||||
}
|
||||
if (effect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
effect.Apply(effectType, deltaTime, targetLimb.character, targetLimb.character);
|
||||
|
||||
@@ -25,7 +25,7 @@ namespace Barotrauma
|
||||
FriendlyNPC = 3
|
||||
}
|
||||
|
||||
partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerSerializable
|
||||
partial class Character : Entity, IDamageable, ISerializableEntity, IClientSerializable, IServerPositionSync
|
||||
{
|
||||
public readonly static List<Character> CharacterList = new List<Character>();
|
||||
|
||||
@@ -130,6 +130,22 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private Wallet wallet = new Wallet();
|
||||
|
||||
public Wallet Wallet
|
||||
{
|
||||
get
|
||||
{
|
||||
ThrowIfAccessingWalletsInSingleplayer();
|
||||
return wallet;
|
||||
}
|
||||
set
|
||||
{
|
||||
ThrowIfAccessingWalletsInSingleplayer();
|
||||
wallet = value;
|
||||
}
|
||||
}
|
||||
|
||||
public readonly HashSet<LatchOntoAI> Latchers = new HashSet<LatchOntoAI>();
|
||||
public readonly HashSet<Projectile> AttachedProjectiles = new HashSet<Projectile>();
|
||||
|
||||
@@ -137,6 +153,17 @@ namespace Barotrauma
|
||||
protected ActiveTeamChange currentTeamChange;
|
||||
const string OriginalTeamIdentifier = "original";
|
||||
|
||||
public static void ThrowIfAccessingWalletsInSingleplayer()
|
||||
{
|
||||
#if CLIENT && DEBUG
|
||||
if (Screen.Selected is TestScreen) { return; }
|
||||
#endif
|
||||
if (GameMain.NetworkMember is null || GameMain.IsSingleplayer)
|
||||
{
|
||||
throw new InvalidOperationException($"Tried to access crew wallets in singleplayer. Use {nameof(CampaignMode)}.{nameof(CampaignMode.Bank)} or {nameof(CampaignMode)}.{nameof(CampaignMode.GetWallet)} instead.");
|
||||
}
|
||||
}
|
||||
|
||||
public void SetOriginalTeam(CharacterTeamType newTeam)
|
||||
{
|
||||
TryRemoveTeamChange(OriginalTeamIdentifier);
|
||||
@@ -158,12 +185,11 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
// clear up any duties the character might have had from its old team (autonomous objectives are automatically recreated)
|
||||
var order = new Order(OrderPrefab.Dismissal, Identifier.Empty,
|
||||
manualPriority: 3, orderType: Order.OrderType.Current, aiObjective: null, target: null, orderGiver: this);
|
||||
SetOrder(order, speak: false);
|
||||
var order = OrderPrefab.Dismissal.CreateInstance(OrderPrefab.OrderTargetType.Entity, orderGiver: this).WithManualPriority(CharacterInfo.HighestManualOrderPriority);
|
||||
SetOrder(order, isNewOrder: true, speak: false);
|
||||
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.TeamChange });
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new TeamChangeEventData());
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -225,9 +251,8 @@ namespace Barotrauma
|
||||
|
||||
if (bestTeamChange.AggressiveBehavior) // this seemed like the least disruptive way to induce aggressive behavior
|
||||
{
|
||||
var order = new Order(OrderPrefab.Prefabs["fightintruders"], Identifier.Empty,
|
||||
manualPriority: 3, orderType: Order.OrderType.Current, aiObjective: null, target: null, orderGiver: this);
|
||||
SetOrder(order, speak: false);
|
||||
var order = OrderPrefab.Prefabs["fightintruders"].CreateInstance(OrderPrefab.OrderTargetType.Entity, orderGiver: this).WithManualPriority(CharacterInfo.HighestManualOrderPriority);
|
||||
SetOrder(order, isNewOrder: true, speak: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1023,7 +1048,7 @@ namespace Barotrauma
|
||||
#if SERVER
|
||||
if (GameMain.Server != null && Spawner != null && createNetworkEvent)
|
||||
{
|
||||
Spawner.CreateNetworkEvent(newCharacter, false);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(newCharacter));
|
||||
}
|
||||
#endif
|
||||
return newCharacter;
|
||||
@@ -1178,6 +1203,10 @@ namespace Barotrauma
|
||||
info = new CharacterInfo(nonHuskedSpeciesName);
|
||||
}
|
||||
}
|
||||
else if (Params.HasInfo && info == null)
|
||||
{
|
||||
info = new CharacterInfo(speciesName);
|
||||
}
|
||||
|
||||
if (IsHumanoid)
|
||||
{
|
||||
@@ -1418,9 +1447,9 @@ namespace Barotrauma
|
||||
{
|
||||
item.AddTag(s);
|
||||
}
|
||||
if (createNetworkEvent && (GameMain.NetworkMember?.IsServer ?? false))
|
||||
if (createNetworkEvent && GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new object[] { NetEntityEvent.Type.ChangeProperty, item.SerializableProperties[nameof(item.Tags).ToIdentifier()] });
|
||||
GameMain.NetworkMember.CreateEntityEvent(item, new Item.ChangePropertyEventData(item.SerializableProperties[nameof(item.Tags).ToIdentifier()]));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3199,7 +3228,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
/// <param name="force">Force an order to be set for the character, bypassing hearing checks</param>
|
||||
public void SetOrder(Order order, bool speak = true, bool force = false)
|
||||
public void SetOrder(Order order, bool isNewOrder, bool speak = true, bool force = false)
|
||||
{
|
||||
var orderGiver = order?.OrderGiver;
|
||||
//set the character order only if the character is close enough to hear the message
|
||||
@@ -3226,7 +3255,7 @@ namespace Barotrauma
|
||||
if (currentOrder.Identifier != order.Identifier) { continue; }
|
||||
if (currentOrder.TargetEntity != order.TargetEntity) { continue; }
|
||||
if (!currentOrder.AutoDismiss) { continue; }
|
||||
character.SetOrder(currentOrder.GetDismissal(), speak: speak, force: force);
|
||||
character.SetOrder(currentOrder.GetDismissal(), isNewOrder, speak: speak, force: force);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -3243,7 +3272,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (orderToReplace is { AutoDismiss: true })
|
||||
{
|
||||
SetOrder(orderToReplace.GetDismissal(), speak: speak, force: force);
|
||||
SetOrder(orderToReplace.GetDismissal(), isNewOrder, speak: speak, force: force);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -3251,10 +3280,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Prevent adding duplicate orders
|
||||
bool wasDuplicate = RemoveDuplicateOrders(order);
|
||||
RemoveDuplicateOrders(order);
|
||||
AddCurrentOrder(order);
|
||||
|
||||
if (orderGiver != null && order.Identifier != "dismissed" && !wasDuplicate)
|
||||
if (orderGiver != null && order.Identifier != "dismissed" && isNewOrder)
|
||||
{
|
||||
var abilityOrderedCharacter = new AbilityOrderedCharacter(this);
|
||||
orderGiver.CheckTalents(AbilityEffectType.OnGiveOrder, abilityOrderedCharacter);
|
||||
@@ -3976,14 +4005,14 @@ namespace Barotrauma
|
||||
HealthUpdateInterval = 0.0f;
|
||||
|
||||
//clients aren't allowed to kill characters unless they receive a network message
|
||||
if (!isNetworkMessage && GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
|
||||
if (!isNetworkMessage && GameMain.NetworkMember is { IsClient: true })
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.Status });
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new StatusEventData());
|
||||
}
|
||||
|
||||
isDead = true;
|
||||
@@ -4072,15 +4101,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (GameMain.GameSession != null)
|
||||
{
|
||||
if (GameMain.GameSession.Campaign != null && TeamID == CharacterTeamType.Team1 && !IsAssistant)
|
||||
{
|
||||
GameMain.GameSession.Campaign.CrewHasDied = true;
|
||||
}
|
||||
|
||||
GameMain.GameSession.KillCharacter(this);
|
||||
}
|
||||
GameMain.GameSession?.KillCharacter(this);
|
||||
}
|
||||
partial void KillProjSpecific(CauseOfDeathType causeOfDeath, Affliction causeOfDeathAffliction, bool log);
|
||||
|
||||
@@ -4245,7 +4266,7 @@ namespace Barotrauma
|
||||
if (!MathUtils.NearlyEqual(newItem.Condition, newItem.MaxCondition) &&
|
||||
GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(newItem, new object[] { NetEntityEvent.Type.Status });
|
||||
GameMain.NetworkMember.CreateEntityEvent(newItem, new StatusEventData());
|
||||
}
|
||||
#if SERVER
|
||||
newItem.GetComponent<Terminal>()?.SyncHistory();
|
||||
@@ -4334,7 +4355,7 @@ namespace Barotrauma
|
||||
hull?.Submarine ?? Submarine);
|
||||
extraDuffelBags.Add(newDuffelBag);
|
||||
#if SERVER
|
||||
Spawner.CreateNetworkEvent(newDuffelBag, false);
|
||||
Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(newDuffelBag));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -4547,7 +4568,7 @@ namespace Barotrauma
|
||||
info.UnlockedTalents.Add(talentPrefab.Identifier);
|
||||
if (characterTalents.Any(t => t.Prefab == talentPrefab)) { return false; }
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new object[] { NetEntityEvent.Type.UpdateTalents });
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new UpdateTalentsEventData());
|
||||
#endif
|
||||
CharacterTalent characterTalent = new CharacterTalent(talentPrefab, this);
|
||||
characterTalents.Add(characterTalent);
|
||||
@@ -4626,23 +4647,44 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public void GiveMoney(int amount)
|
||||
{
|
||||
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign)) { return; }
|
||||
if (!(GameMain.GameSession?.Campaign is { } campaign)) { return; }
|
||||
if (amount <= 0) { return; }
|
||||
|
||||
int prevAmount = campaign.Money;
|
||||
campaign.Money += amount;
|
||||
OnMoneyChanged(prevAmount, campaign.Money);
|
||||
Wallet wallet;
|
||||
#if SERVER
|
||||
if (!(campaign is MultiPlayerCampaign mpCampaign)) { throw new InvalidOperationException("Campaign on a server is not a multiplayer campaign"); }
|
||||
Client targetClient = null;
|
||||
|
||||
foreach (Client client in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (client.Character == this)
|
||||
{
|
||||
targetClient = client;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
wallet = targetClient is null ? mpCampaign.Bank : mpCampaign.GetWallet(targetClient);
|
||||
#else
|
||||
wallet = campaign.Wallet;
|
||||
#endif
|
||||
|
||||
int prevAmount = wallet.Balance;
|
||||
wallet.Give(amount);
|
||||
OnMoneyChanged(prevAmount, wallet.Balance);
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
public void SetMoney(int amount)
|
||||
{
|
||||
if (!(GameMain.GameSession?.Campaign is CampaignMode campaign)) { return; }
|
||||
if (amount == campaign.Money) { return; }
|
||||
if (!(GameMain.GameSession?.Campaign is { } campaign)) { return; }
|
||||
if (amount == campaign.Wallet.Balance) { return; }
|
||||
|
||||
int prevAmount = campaign.Money;
|
||||
campaign.Money = amount;
|
||||
OnMoneyChanged(prevAmount, campaign.Money);
|
||||
int prevAmount = campaign.Wallet.Balance;
|
||||
campaign.Wallet.Balance = amount;
|
||||
OnMoneyChanged(prevAmount, campaign.Wallet.Balance);
|
||||
}
|
||||
#endif
|
||||
|
||||
partial void OnMoneyChanged(int prevAmount, int newAmount);
|
||||
partial void OnTalentGiven(TalentPrefab talentPrefab);
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using Microsoft.Xna.Framework;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class Character
|
||||
{
|
||||
public enum EventType
|
||||
{
|
||||
InventoryState = 0,
|
||||
Control = 1,
|
||||
Status = 2,
|
||||
Treatment = 3,
|
||||
SetAttackTarget = 4,
|
||||
ExecuteAttack = 5,
|
||||
AssignCampaignInteraction = 6,
|
||||
ObjectiveManagerState = 7,
|
||||
TeamChange = 8,
|
||||
AddToCrew = 9,
|
||||
UpdateExperience = 10,
|
||||
UpdateTalents = 11,
|
||||
UpdateSkills = 12,
|
||||
UpdateMoney = 13,
|
||||
UpdatePermanentStats = 14,
|
||||
|
||||
MinValue = 0,
|
||||
MaxValue = 14
|
||||
}
|
||||
|
||||
private interface IEventData : NetEntityEvent.IData
|
||||
{
|
||||
public EventType EventType { get; }
|
||||
}
|
||||
|
||||
public struct InventoryStateEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.InventoryState;
|
||||
}
|
||||
|
||||
public struct ControlEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Control;
|
||||
public readonly Client Owner;
|
||||
|
||||
public ControlEventData(Client owner)
|
||||
{
|
||||
Owner = owner;
|
||||
}
|
||||
}
|
||||
|
||||
public struct StatusEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Status;
|
||||
}
|
||||
|
||||
public struct TreatmentEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Treatment;
|
||||
}
|
||||
|
||||
private interface IAttackEventData : IEventData
|
||||
{
|
||||
public Limb AttackLimb { get; }
|
||||
public IDamageable TargetEntity { get; }
|
||||
public Limb TargetLimb { get; }
|
||||
public Vector2 TargetSimPos { get; }
|
||||
}
|
||||
|
||||
public struct SetAttackTargetEventData : IAttackEventData
|
||||
{
|
||||
public EventType EventType => EventType.SetAttackTarget;
|
||||
public Limb AttackLimb { get; }
|
||||
public IDamageable TargetEntity { get; }
|
||||
public Limb TargetLimb { get; }
|
||||
public Vector2 TargetSimPos { get; }
|
||||
|
||||
public SetAttackTargetEventData(Limb attackLimb, IDamageable targetEntity, Limb targetLimb, Vector2 targetSimPos)
|
||||
{
|
||||
AttackLimb = attackLimb;
|
||||
TargetEntity = targetEntity;
|
||||
TargetLimb = targetLimb;
|
||||
TargetSimPos = targetSimPos;
|
||||
}
|
||||
}
|
||||
|
||||
public struct ExecuteAttackEventData : IAttackEventData
|
||||
{
|
||||
public EventType EventType => EventType.ExecuteAttack;
|
||||
public Limb AttackLimb { get; }
|
||||
public IDamageable TargetEntity { get; }
|
||||
public Limb TargetLimb { get; }
|
||||
public Vector2 TargetSimPos { get; }
|
||||
|
||||
public ExecuteAttackEventData(Limb attackLimb, IDamageable targetEntity, Limb targetLimb, Vector2 targetSimPos)
|
||||
{
|
||||
AttackLimb = attackLimb;
|
||||
TargetEntity = targetEntity;
|
||||
TargetLimb = targetLimb;
|
||||
TargetSimPos = targetSimPos;
|
||||
}
|
||||
}
|
||||
|
||||
public struct AssignCampaignInteractionEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.AssignCampaignInteraction;
|
||||
}
|
||||
|
||||
public struct ObjectiveManagerStateEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.ObjectiveManagerState;
|
||||
public readonly AIObjectiveManager.ObjectiveType ObjectiveType;
|
||||
|
||||
public ObjectiveManagerStateEventData(AIObjectiveManager.ObjectiveType objectiveType)
|
||||
{
|
||||
ObjectiveType = objectiveType;
|
||||
}
|
||||
}
|
||||
|
||||
private struct TeamChangeEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.TeamChange;
|
||||
}
|
||||
|
||||
public struct AddToCrewEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.AddToCrew;
|
||||
public readonly CharacterTeamType TeamType;
|
||||
public readonly ImmutableArray<Item> InventoryItems;
|
||||
|
||||
public AddToCrewEventData(CharacterTeamType teamType, IEnumerable<Item> inventoryItems)
|
||||
{
|
||||
TeamType = teamType;
|
||||
InventoryItems = inventoryItems.ToImmutableArray();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public struct UpdateExperienceEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.UpdateExperience;
|
||||
}
|
||||
|
||||
public struct UpdateTalentsEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.UpdateTalents;
|
||||
}
|
||||
|
||||
public struct UpdateSkillsEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.UpdateSkills;
|
||||
}
|
||||
|
||||
private struct UpdateMoneyEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.UpdateMoney;
|
||||
}
|
||||
|
||||
public struct UpdatePermanentStatsEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.UpdatePermanentStats;
|
||||
public readonly StatTypes StatType;
|
||||
|
||||
public UpdatePermanentStatsEventData(StatTypes statType)
|
||||
{
|
||||
StatType = statType;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -299,7 +299,11 @@ namespace Barotrauma
|
||||
{
|
||||
if (handleBuff)
|
||||
{
|
||||
Character.CharacterHealth.ApplyAffliction(Character.AnimController.GetLimb(LimbType.Head), AfflictionPrefab.List.FirstOrDefault(a => a.Identifier == "disguised").Instantiate(100f));
|
||||
var head = Character.AnimController.GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
Character.CharacterHealth.ApplyAffliction(head, AfflictionPrefab.List.FirstOrDefault(a => a.Identifier == "disguised").Instantiate(100f));
|
||||
}
|
||||
}
|
||||
|
||||
idCard ??= Character.Inventory?.GetItemInLimbSlot(InvSlotType.Card)?.GetComponent<IdCard>();
|
||||
@@ -319,7 +323,11 @@ namespace Barotrauma
|
||||
|
||||
if (handleBuff)
|
||||
{
|
||||
Character.CharacterHealth.ReduceAfflictionOnLimb(Character.AnimController.GetLimb(LimbType.Head), "disguised".ToIdentifier(), 100f);
|
||||
var head = Character.AnimController.GetLimb(LimbType.Head);
|
||||
if (head != null)
|
||||
{
|
||||
Character.CharacterHealth.ReduceAfflictionOnLimb(head, "disguised".ToIdentifier(), 100f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -572,15 +580,15 @@ namespace Barotrauma
|
||||
|
||||
private void CheckColors()
|
||||
{
|
||||
if (IsColorValid(Head.HairColor))
|
||||
if (!IsColorValid(Head.HairColor))
|
||||
{
|
||||
Head.HairColor = SelectRandomColor(HairColors, Rand.RandSync.Unsynced);
|
||||
}
|
||||
if (IsColorValid(Head.FacialHairColor))
|
||||
if (!IsColorValid(Head.FacialHairColor))
|
||||
{
|
||||
Head.FacialHairColor = SelectRandomColor(FacialHairColors, Rand.RandSync.Unsynced);
|
||||
}
|
||||
if (IsColorValid(Head.SkinColor))
|
||||
if (!IsColorValid(Head.SkinColor))
|
||||
{
|
||||
Head.SkinColor = SelectRandomColor(SkinColors, Rand.RandSync.Unsynced);
|
||||
}
|
||||
@@ -736,7 +744,7 @@ namespace Barotrauma
|
||||
|
||||
private int GetIdentifier(string name)
|
||||
{
|
||||
int id = ToolBox.StringToInt(name + string.Join("", Head.Preset.TagSet));
|
||||
int id = ToolBox.StringToInt(name + string.Join("", Head.Preset.TagSet.OrderBy(s => s)));
|
||||
id ^= Head.HairIndex << 12;
|
||||
id ^= Head.BeardIndex << 18;
|
||||
id ^= Head.MoustacheIndex << 24;
|
||||
@@ -822,12 +830,12 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (var limbElement in Ragdoll.MainElement.Elements())
|
||||
{
|
||||
if (!limbElement.GetAttributeString("type", "").Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
if (!limbElement.GetAttributeString("type", string.Empty).Equals("head", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
|
||||
ContentXElement spriteElement = limbElement.GetChildElement("sprite");
|
||||
if (spriteElement == null) { continue; }
|
||||
|
||||
string spritePath = spriteElement.Attribute("texture").Value;
|
||||
string spritePath = spriteElement.GetAttributeContentPath("texture")?.Value;
|
||||
if (string.IsNullOrEmpty(spritePath)) { continue; }
|
||||
|
||||
spritePath = ReplaceVars(spritePath);
|
||||
@@ -1298,7 +1306,7 @@ namespace Barotrauma
|
||||
var orders = LoadOrders(orderData);
|
||||
foreach (var order in orders)
|
||||
{
|
||||
character.SetOrder(order, speak: false, force: true);
|
||||
character.SetOrder(order, isNewOrder: true, speak: false, force: true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+62
-4
@@ -41,9 +41,11 @@ namespace Barotrauma
|
||||
if (previousValue > 0.0f && value <= 0.0f)
|
||||
{
|
||||
DeactivateHusk();
|
||||
highestStrength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
private float highestStrength;
|
||||
|
||||
public InfectionState State
|
||||
{
|
||||
@@ -75,6 +77,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (HuskPrefab == null) { return; }
|
||||
base.Update(characterHealth, targetLimb, deltaTime);
|
||||
highestStrength = Math.Max(_strength, highestStrength);
|
||||
character = characterHealth.Character;
|
||||
if (character == null) { return; }
|
||||
|
||||
@@ -98,7 +101,7 @@ namespace Barotrauma
|
||||
DeactivateHusk();
|
||||
if (Prefab is AfflictionPrefabHusk { CauseSpeechImpediment: true })
|
||||
{
|
||||
character.SpeechImpediment = 100;
|
||||
character.SpeechImpediment = 30;
|
||||
}
|
||||
State = InfectionState.Transition;
|
||||
}
|
||||
@@ -108,6 +111,10 @@ namespace Barotrauma
|
||||
{
|
||||
character.SetStun(Rand.Range(2f, 3f));
|
||||
}
|
||||
if (Prefab is AfflictionPrefabHusk { CauseSpeechImpediment: true })
|
||||
{
|
||||
character.SpeechImpediment = 100;
|
||||
}
|
||||
State = InfectionState.Active;
|
||||
ActivateHusk();
|
||||
}
|
||||
@@ -120,7 +127,57 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void UpdateMessages();
|
||||
private InfectionState? prevDisplayedMessage;
|
||||
private void UpdateMessages()
|
||||
{
|
||||
if (Prefab is AfflictionPrefabHusk { SendMessages: false }) { return; }
|
||||
if (prevDisplayedMessage.HasValue && prevDisplayedMessage.Value == State) { return; }
|
||||
if (highestStrength > Strength) { return; }
|
||||
|
||||
switch (State)
|
||||
{
|
||||
case InfectionState.Dormant:
|
||||
if (Strength < DormantThreshold * 0.5f)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
#if CLIENT
|
||||
GUI.AddMessage(TextManager.Get("HuskDormant"), GUIStyle.Red);
|
||||
#endif
|
||||
}
|
||||
else if (character.IsBot)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoghuskdormant").Value, delay: Rand.Range(0.5f, 5.0f), identifier: "huskdormant".ToIdentifier());
|
||||
}
|
||||
break;
|
||||
case InfectionState.Transition:
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
#if CLIENT
|
||||
GUI.AddMessage(TextManager.Get("HuskCantSpeak"), GUIStyle.Red);
|
||||
#endif
|
||||
}
|
||||
else if (character.IsBot)
|
||||
{
|
||||
character.Speak(TextManager.Get("dialoghuskcantspeak").Value, delay: Rand.Range(0.5f, 5.0f), identifier: "huskcantspeak".ToIdentifier());
|
||||
}
|
||||
break;
|
||||
case InfectionState.Active:
|
||||
#if CLIENT
|
||||
if (character == Character.Controlled && character.Params.UseHuskAppendage)
|
||||
{
|
||||
GUI.AddMessage(TextManager.GetWithVariable("HuskActivate", "[Attack]", GameSettings.CurrentConfig.KeyMap.KeyBindText(InputType.Attack)), GUIStyle.Red);
|
||||
}
|
||||
#endif
|
||||
break;
|
||||
case InfectionState.Final:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
prevDisplayedMessage = State;
|
||||
}
|
||||
|
||||
private void ApplyDamage(float deltaTime, bool applyForce)
|
||||
{
|
||||
@@ -209,7 +266,9 @@ namespace Barotrauma
|
||||
{
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
var client = GameMain.Server?.ConnectedClients.FirstOrDefault(c => c.Character == character);
|
||||
#endif
|
||||
character.Enabled = false;
|
||||
Entity.Spawner.AddEntityToRemoveQueue(character);
|
||||
UnsubscribeFromDeathEvent();
|
||||
@@ -246,7 +305,6 @@ namespace Barotrauma
|
||||
if (huskPrefab.ControlHusk)
|
||||
{
|
||||
#if SERVER
|
||||
var client = GameMain.Server?.ConnectedClients.FirstOrDefault(c => c.Character == character);
|
||||
if (client != null)
|
||||
{
|
||||
GameMain.Server.SetClientCharacter(client, husk);
|
||||
|
||||
@@ -657,6 +657,7 @@ namespace Barotrauma
|
||||
if (!DoesBleed && newAffliction is AfflictionBleeding) { return; }
|
||||
if (!Character.NeedsOxygen && newAffliction.Prefab == AfflictionPrefab.OxygenLow) { return; }
|
||||
if (Character.Params.Health.StunImmunity && newAffliction.Prefab.AfflictionType == "stun") { return; }
|
||||
if (Character.Params.Health.PoisonImmunity && newAffliction.Prefab.AfflictionType == "poison") { return; }
|
||||
if (newAffliction.Prefab is AfflictionPrefabHusk huskPrefab)
|
||||
{
|
||||
if (huskPrefab.TargetSpecies.None(s => s == Character.SpeciesName))
|
||||
@@ -731,48 +732,47 @@ namespace Barotrauma
|
||||
|
||||
StunTimer = Stun > 0 ? StunTimer + deltaTime : 0;
|
||||
|
||||
if (Character.GodMode) { return; }
|
||||
|
||||
afflictionsToRemove.Clear();
|
||||
afflictionsToUpdate.Clear();
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
if (!Character.GodMode)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
if (affliction.Strength <= 0.0f)
|
||||
afflictionsToRemove.Clear();
|
||||
afflictionsToUpdate.Clear();
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictions)
|
||||
{
|
||||
SteamAchievementManager.OnAfflictionRemoved(affliction, Character);
|
||||
if (!irremovableAfflictions.Contains(affliction)) { afflictionsToRemove.Add(affliction); }
|
||||
continue;
|
||||
var affliction = kvp.Key;
|
||||
if (affliction.Strength <= 0.0f)
|
||||
{
|
||||
SteamAchievementManager.OnAfflictionRemoved(affliction, Character);
|
||||
if (!irremovableAfflictions.Contains(affliction)) { afflictionsToRemove.Add(affliction); }
|
||||
continue;
|
||||
}
|
||||
afflictionsToUpdate.Add(kvp);
|
||||
}
|
||||
afflictionsToUpdate.Add(kvp);
|
||||
}
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictionsToUpdate)
|
||||
{
|
||||
var affliction = kvp.Key;
|
||||
Limb targetLimb = null;
|
||||
if (kvp.Value != null)
|
||||
foreach (KeyValuePair<Affliction, LimbHealth> kvp in afflictionsToUpdate)
|
||||
{
|
||||
int healthIndex = limbHealths.IndexOf(kvp.Value);
|
||||
targetLimb =
|
||||
Character.AnimController.Limbs.LastOrDefault(l => !l.IsSevered && !l.Hidden && l.HealthIndex == healthIndex) ??
|
||||
Character.AnimController.MainLimb;
|
||||
var affliction = kvp.Key;
|
||||
Limb targetLimb = null;
|
||||
if (kvp.Value != null)
|
||||
{
|
||||
int healthIndex = limbHealths.IndexOf(kvp.Value);
|
||||
targetLimb =
|
||||
Character.AnimController.Limbs.LastOrDefault(l => !l.IsSevered && !l.Hidden && l.HealthIndex == healthIndex) ??
|
||||
Character.AnimController.MainLimb;
|
||||
}
|
||||
affliction.Update(this, targetLimb, deltaTime);
|
||||
affliction.DamagePerSecondTimer += deltaTime;
|
||||
if (affliction is AfflictionBleeding bleeding)
|
||||
{
|
||||
UpdateBleedingProjSpecific(bleeding, targetLimb, deltaTime);
|
||||
}
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
affliction.Update(this, targetLimb, deltaTime);
|
||||
affliction.DamagePerSecondTimer += deltaTime;
|
||||
if (affliction is AfflictionBleeding bleeding)
|
||||
foreach (var affliction in afflictionsToRemove)
|
||||
{
|
||||
UpdateBleedingProjSpecific(bleeding, targetLimb, deltaTime);
|
||||
}
|
||||
Character.StackSpeedMultiplier(affliction.GetSpeedMultiplier());
|
||||
}
|
||||
|
||||
foreach (var affliction in afflictionsToRemove)
|
||||
{
|
||||
afflictions.Remove(affliction);
|
||||
afflictions.Remove(affliction);
|
||||
}
|
||||
}
|
||||
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.MovementSpeed));
|
||||
|
||||
if (Character.InWater)
|
||||
{
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.SwimmingSpeed));
|
||||
@@ -782,13 +782,16 @@ namespace Barotrauma
|
||||
Character.StackSpeedMultiplier(1f + Character.GetStatValue(StatTypes.WalkingSpeed));
|
||||
}
|
||||
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
if (!Character.GodMode)
|
||||
{
|
||||
Kill();
|
||||
UpdateLimbAfflictionOverlays();
|
||||
UpdateSkinTint();
|
||||
CalculateVitality();
|
||||
|
||||
if (Vitality <= MinVitality)
|
||||
{
|
||||
Kill();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Barotrauma
|
||||
GameMain.Server.EntityEventManager.Events.RemoveAll(ev => ev.Entity == item);
|
||||
}
|
||||
|
||||
Entity.Spawner.CreateNetworkEvent(item, false);
|
||||
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
|
||||
}
|
||||
#endif
|
||||
if (itemElement.GetAttributeBool("equip", false))
|
||||
|
||||
@@ -152,7 +152,7 @@ namespace Barotrauma
|
||||
GameMain.Server.EntityEventManager.Events.RemoveAll(ev => ev.Entity == item);
|
||||
}
|
||||
|
||||
Entity.Spawner.CreateNetworkEvent(item, false);
|
||||
Entity.Spawner.CreateNetworkEvent(new EntitySpawner.SpawnEntity(item));
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -1011,15 +1011,9 @@ namespace Barotrauma
|
||||
ExecuteAttack(damageTarget, targetLimb, out attackResult);
|
||||
}
|
||||
#if SERVER
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new object[]
|
||||
{
|
||||
NetEntityEvent.Type.ExecuteAttack,
|
||||
this,
|
||||
(damageTarget as Entity)?.ID ?? Entity.NullEntityID,
|
||||
damageTarget is Character && targetLimb != null ? Array.IndexOf(((Character)damageTarget).AnimController.Limbs, targetLimb) : 0,
|
||||
attackSimPos.X,
|
||||
attackSimPos.Y
|
||||
});
|
||||
GameMain.NetworkMember.CreateEntityEvent(character, new Character.ExecuteAttackEventData(
|
||||
attackLimb: this, targetEntity: damageTarget, targetLimb: targetLimb,
|
||||
targetSimPos: attackSimPos));
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1055,7 +1049,10 @@ namespace Barotrauma
|
||||
if (!attack.IsRunning)
|
||||
{
|
||||
// Set the main collider where the body lands after the attack
|
||||
character.AnimController.Collider.SetTransform(character.AnimController.MainLimb.body.SimPosition, rotation: character.AnimController.Collider.Rotation);
|
||||
if (Vector2.DistanceSquared(character.AnimController.Collider.SimPosition, character.AnimController.MainLimb.body.SimPosition) > 0.1f * 0.1f)
|
||||
{
|
||||
character.AnimController.Collider.SetTransform(character.AnimController.MainLimb.body.SimPosition, rotation: character.AnimController.Collider.Rotation);
|
||||
}
|
||||
}
|
||||
return wasHit;
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace Barotrauma
|
||||
Identifier variantOf = MainElement.VariantOf();
|
||||
if (!variantOf.IsEmpty)
|
||||
{
|
||||
VariantFile = doc;
|
||||
VariantFile = new XDocument(doc);
|
||||
#warning TODO: determine that CreateVariantXML is equipped to do this
|
||||
XElement newRoot = CreateVariantXml(MainElement, CharacterPrefab.FindBySpeciesName(variantOf).ConfigElement);
|
||||
var oldElement = MainElement;
|
||||
@@ -477,12 +477,15 @@ namespace Barotrauma
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
public float ConstantHealthRegeneration { get; private set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 10, DecimalCount = 2)]
|
||||
[Serialize(0f, IsPropertySaveable.Yes), Editable(MinValueFloat = 0, MaxValueFloat = 100, DecimalCount = 2)]
|
||||
public float HealthRegenerationWhenEating { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool StunImmunity { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes), Editable]
|
||||
public bool PoisonImmunity { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "Can afflictions affect the face/body tint of the character."), Editable]
|
||||
public bool ApplyAfflictionColors { get; private set; }
|
||||
|
||||
|
||||
@@ -956,7 +956,7 @@ namespace Barotrauma
|
||||
Deformations = new Dictionary<SpriteDeformationParams, XElement>();
|
||||
foreach (var deformationElement in element.GetChildElements("spritedeformation"))
|
||||
{
|
||||
string typeName = deformationElement.GetAttributeString("typename", null) ?? deformationElement.GetAttributeString("type", "");
|
||||
string typeName = deformationElement.GetAttributeString("type", null) ?? deformationElement.GetAttributeString("typename", string.Empty);
|
||||
SpriteDeformationParams deformation = null;
|
||||
switch (typeName.ToLowerInvariant())
|
||||
{
|
||||
@@ -982,7 +982,7 @@ namespace Barotrauma
|
||||
}
|
||||
if (deformation != null)
|
||||
{
|
||||
deformation.TypeName = typeName;
|
||||
deformation.Type = typeName;
|
||||
}
|
||||
Deformations.Add(deformation, deformationElement);
|
||||
}
|
||||
|
||||
+15
-7
@@ -1,20 +1,28 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Abilities
|
||||
namespace Barotrauma.Abilities
|
||||
{
|
||||
class AbilityConditionNoCrewDied : AbilityConditionDataless
|
||||
{
|
||||
public bool assistantsDontCount;
|
||||
|
||||
public AbilityConditionNoCrewDied(CharacterTalent characterTalent, ContentXElement conditionElement) : base(characterTalent, conditionElement)
|
||||
{
|
||||
assistantsDontCount = conditionElement.GetAttributeBool(nameof(assistantsDontCount), true);
|
||||
}
|
||||
|
||||
protected override bool MatchesConditionSpecific()
|
||||
{
|
||||
if (GameMain.GameSession?.Campaign is CampaignMode campaign)
|
||||
if (GameMain.GameSession == null) { return false; }
|
||||
|
||||
foreach (Character character in GameMain.GameSession.Casualties)
|
||||
{
|
||||
return !campaign.CrewHasDied;
|
||||
if (assistantsDontCount && character.Info?.Job?.Prefab.Identifier == "assistant")
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (character.CauseOfDeath != null && character.CauseOfDeath.Type != CauseOfDeathType.Disconnected)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user