v0.19.0.0 (unstable)
This commit is contained in:
@@ -511,9 +511,9 @@ namespace Barotrauma
|
||||
{
|
||||
newDir = Direction.Left;
|
||||
}
|
||||
if (Character.SelectedConstruction != null)
|
||||
if (Character.SelectedItem != null)
|
||||
{
|
||||
Character.SelectedConstruction.SecondaryUse(deltaTime, Character);
|
||||
Character.SelectedItem.SecondaryUse(deltaTime, Character);
|
||||
}
|
||||
}
|
||||
else if (AutoFaceMovement && Math.Abs(Character.AnimController.TargetMovement.X) > 0.1f && !Character.AnimController.InWater)
|
||||
@@ -2148,7 +2148,7 @@ namespace Barotrauma
|
||||
if (c.Removed) { continue; }
|
||||
if (c.TeamID != team) { continue; }
|
||||
if (c.IsIncapacitated) { continue; }
|
||||
if (c.SelectedConstruction == target.Item)
|
||||
if (c.SelectedItem == target.Item)
|
||||
{
|
||||
operatingCharacter = c;
|
||||
return true;
|
||||
@@ -2185,7 +2185,7 @@ namespace Barotrauma
|
||||
if (c.IsIncapacitated) { continue; }
|
||||
if (c.IsPlayer)
|
||||
{
|
||||
if (c.SelectedConstruction == target.Item)
|
||||
if (c.SelectedItem == target.Item)
|
||||
{
|
||||
// If the other character is player, don't try to operate
|
||||
other = c;
|
||||
|
||||
@@ -79,7 +79,8 @@ namespace Barotrauma
|
||||
{
|
||||
pathFinder = new PathFinder(WayPoint.WayPointList.FindAll(wp => wp.SpawnType == SpawnType.Path), true)
|
||||
{
|
||||
GetNodePenalty = GetNodePenalty
|
||||
GetNodePenalty = GetNodePenalty,
|
||||
GetSingleNodePenalty = GetSingleNodePenalty
|
||||
};
|
||||
|
||||
this.canOpenDoors = canOpenDoors;
|
||||
@@ -360,7 +361,7 @@ namespace Barotrauma
|
||||
Ladder nextLadder = GetNextLadder();
|
||||
var ladders = currentLadder ?? nextLadder;
|
||||
bool useLadders = canClimb && ladders != null && steering.LengthSquared() > 0.1f && (!isDiving || steering.Y > 1);
|
||||
if (useLadders && character.SelectedConstruction != ladders.Item)
|
||||
if (useLadders && character.SelectedSecondaryItem != ladders.Item)
|
||||
{
|
||||
if (character.CanInteractWith(ladders.Item))
|
||||
{
|
||||
@@ -372,7 +373,7 @@ namespace Barotrauma
|
||||
// Try to select the previous ladder, unless it's already selected, unless the previous ladder is not adjacent to the current ladder.
|
||||
// The intention of this code is to prevent the bots from dropping from the "double ladders".
|
||||
var previousLadders = currentPath.PrevNode?.Ladders;
|
||||
if (previousLadders != null && previousLadders != ladders && character.SelectedConstruction != previousLadders.Item &&
|
||||
if (previousLadders != null && previousLadders != ladders && character.SelectedSecondaryItem != previousLadders.Item &&
|
||||
character.CanInteractWith(previousLadders.Item) && Math.Abs(previousLadders.Item.WorldPosition.X - ladders.Item.WorldPosition.X) < 5)
|
||||
{
|
||||
previousLadders.Item.TryInteract(character, forceSelectKey: true);
|
||||
@@ -382,8 +383,7 @@ namespace Barotrauma
|
||||
var collider = character.AnimController.Collider;
|
||||
if (character.IsClimbing && !useLadders)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
character.StopClimbing();
|
||||
}
|
||||
if (character.IsClimbing && useLadders)
|
||||
{
|
||||
@@ -402,15 +402,14 @@ namespace Barotrauma
|
||||
// We need some margin, because if a hatch has closed, it's possible that the height from floor is slightly negative.
|
||||
bool isAboveFloor = heightFromFloor > -0.1f;
|
||||
// If the next waypoint is horizontally far, we don't want to keep holding the ladders
|
||||
if (isAboveFloor && (nextLadder == null || Math.Abs(currentPath.CurrentNode.WorldPosition.X - currentPath.NextNode.WorldPosition.X) > 50))
|
||||
if (isAboveFloor && !currentPath.IsAtEndNode && (nextLadder == null || Math.Abs(currentPath.CurrentNode.WorldPosition.X - currentPath.NextNode.WorldPosition.X) > 50))
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
character.StopClimbing();
|
||||
}
|
||||
else if (nextLadder != null && !nextLadderSameAsCurrent)
|
||||
{
|
||||
// Try to change the ladder (hatches between two submarines)
|
||||
if (character.SelectedConstruction != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
|
||||
if (character.SelectedSecondaryItem != nextLadder.Item && character.CanInteractWith(nextLadder.Item))
|
||||
{
|
||||
if (nextLadder.Item.TryInteract(character, forceSelectKey: true))
|
||||
{
|
||||
@@ -418,7 +417,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10)
|
||||
if (!currentPath.IsAtEndNode && (isAboveFloor || nextLadderSameAsCurrent || nextLadder == null && Math.Abs(diff.Y) < 10))
|
||||
{
|
||||
NextNode(!doorsChecked);
|
||||
}
|
||||
@@ -756,42 +755,8 @@ namespace Barotrauma
|
||||
private float? GetNodePenalty(PathNode node, PathNode nextNode)
|
||||
{
|
||||
if (character == null) { return 0.0f; }
|
||||
if (nextNode.Waypoint.isObstructed) { return null; }
|
||||
float penalty = 0.0f;
|
||||
if (nextNode.Waypoint.ConnectedGap != null && nextNode.Waypoint.ConnectedGap.Open < 0.9f)
|
||||
{
|
||||
var door = nextNode.Waypoint.ConnectedDoor;
|
||||
if (door == null)
|
||||
{
|
||||
penalty = 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CanAccessDoor(door, button =>
|
||||
{
|
||||
// Ignore buttons that are on the wrong side of the door
|
||||
if (door.IsHorizontal)
|
||||
{
|
||||
if (Math.Sign(button.Item.WorldPosition.Y - door.Item.WorldPosition.Y) != Math.Sign(character.WorldPosition.Y - door.Item.WorldPosition.Y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Sign(button.Item.WorldPosition.X - door.Item.WorldPosition.X) != Math.Sign(character.WorldPosition.X - door.Item.WorldPosition.X))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float? penalty = GetSingleNodePenalty(nextNode);
|
||||
if (penalty == null) { return null; }
|
||||
bool nextNodeAboveWaterLevel = nextNode.Waypoint.CurrentHull != null && nextNode.Waypoint.CurrentHull.Surface < nextNode.Waypoint.Position.Y;
|
||||
//non-humanoids can't climb up ladders
|
||||
if (!(character.AnimController is HumanoidAnimController))
|
||||
@@ -839,6 +804,47 @@ namespace Barotrauma
|
||||
return penalty;
|
||||
}
|
||||
|
||||
private float? GetSingleNodePenalty(PathNode node)
|
||||
{
|
||||
if (node.Waypoint.isObstructed) { return null; }
|
||||
if (node.IsBlocked()) { return null; }
|
||||
float penalty = 0.0f;
|
||||
if (node.Waypoint.ConnectedGap != null && node.Waypoint.ConnectedGap.Open < 0.9f)
|
||||
{
|
||||
var door = node.Waypoint.ConnectedDoor;
|
||||
if (door == null)
|
||||
{
|
||||
penalty = 100.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!CanAccessDoor(door, button =>
|
||||
{
|
||||
// Ignore buttons that are on the wrong side of the door
|
||||
if (door.IsHorizontal)
|
||||
{
|
||||
if (Math.Sign(button.Item.WorldPosition.Y - door.Item.WorldPosition.Y) != Math.Sign(character.WorldPosition.Y - door.Item.WorldPosition.Y))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Sign(button.Item.WorldPosition.X - door.Item.WorldPosition.X) != Math.Sign(character.WorldPosition.X - door.Item.WorldPosition.X))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return penalty;
|
||||
}
|
||||
|
||||
public static float smallRoomSize = 500;
|
||||
public void Wander(float deltaTime, float wallAvoidDistance = 150, bool stayStillInTightSpace = true)
|
||||
{
|
||||
|
||||
@@ -14,13 +14,11 @@ namespace Barotrauma
|
||||
public readonly LanguageIdentifier Language;
|
||||
|
||||
public readonly List<NPCConversation> Conversations;
|
||||
public readonly Dictionary<Identifier, NPCPersonalityTrait> PersonalityTraits;
|
||||
|
||||
public NPCConversationCollection(NPCConversationsFile file, ContentXElement element) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
Language = element.GetAttributeIdentifier("language", "English").ToLanguageIdentifier();
|
||||
Conversations = new List<NPCConversation>();
|
||||
PersonalityTraits = new Dictionary<Identifier, NPCPersonalityTrait>();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
Identifier elemName = new Identifier(subElement.Name.LocalName);
|
||||
@@ -28,11 +26,6 @@ namespace Barotrauma
|
||||
{
|
||||
Conversations.Add(new NPCConversation(subElement));
|
||||
}
|
||||
else if (elemName == "PersonalityTrait")
|
||||
{
|
||||
var personalityTrait = new NPCPersonalityTrait(subElement);
|
||||
PersonalityTraits.Add(personalityTrait.Name, personalityTrait);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -361,10 +354,13 @@ namespace Barotrauma
|
||||
|
||||
private static float GetConversationProbability(NPCConversation conversation)
|
||||
{
|
||||
int index = previousConversations.IndexOf(conversation);
|
||||
if (index < 0) return 10.0f;
|
||||
//prefer choosing conversations with more flags (= for more specific situations) when possible
|
||||
float baseProbability = MathF.Pow(conversation.Flags.Count + 1, 2);
|
||||
|
||||
return 1.0f - 1.0f / (index + 1);
|
||||
int index = previousConversations.IndexOf(conversation);
|
||||
if (index < 0) { return baseProbability * 10.0f; }
|
||||
|
||||
return baseProbability + 1.0f - 1.0f / (index + 1);
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
|
||||
+17
@@ -512,6 +512,23 @@ namespace Barotrauma
|
||||
foreach (var weapon in weaponList)
|
||||
{
|
||||
float priority = weapon.CombatPriority;
|
||||
if (weapon is RepairTool repairTool)
|
||||
{
|
||||
switch (repairTool.UsableIn)
|
||||
{
|
||||
case RepairTool.UseEnvironment.Air:
|
||||
if (character.InWater) { continue; }
|
||||
break;
|
||||
case RepairTool.UseEnvironment.Water:
|
||||
if (!character.InWater) { continue; }
|
||||
break;
|
||||
case RepairTool.UseEnvironment.None:
|
||||
continue;
|
||||
case RepairTool.UseEnvironment.Both:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (prioritizeMelee)
|
||||
{
|
||||
if (weapon is MeleeWeapon)
|
||||
|
||||
+3
-6
@@ -196,10 +196,7 @@ namespace Barotrauma
|
||||
character.AIController.SteeringManager.Reset();
|
||||
return;
|
||||
}
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
character.SelectedItem = null;
|
||||
if (Target is Entity e)
|
||||
{
|
||||
if (e.Removed)
|
||||
@@ -647,7 +644,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.IsClimbing)
|
||||
{
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder)
|
||||
if (SteeringManager == PathSteering && PathSteering.CurrentPath != null && !PathSteering.CurrentPath.Finished && PathSteering.IsCurrentNodeLadder && !PathSteering.CurrentPath.IsAtEndNode)
|
||||
{
|
||||
if (Target.WorldPosition.Y > character.WorldPosition.Y)
|
||||
{
|
||||
@@ -694,7 +691,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (Target is Item item)
|
||||
{
|
||||
if (!character.IsClimbing && character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
|
||||
if (character.CanInteractWith(item, out _, checkLinked: false)) { IsCompleted = true; }
|
||||
}
|
||||
else if (Target is Character targetCharacter)
|
||||
{
|
||||
|
||||
+3
-6
@@ -161,10 +161,7 @@ namespace Barotrauma
|
||||
character.DeselectCharacter();
|
||||
}
|
||||
|
||||
if (!character.IsClimbing)
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
character.SelectedItem = null;
|
||||
|
||||
CleanupItems(deltaTime);
|
||||
|
||||
@@ -310,7 +307,7 @@ namespace Barotrauma
|
||||
if (character.AnimController.GetHeightFromFloor() < 0.1f)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
character.SelectedSecondaryItem = null;
|
||||
}
|
||||
return;
|
||||
}
|
||||
@@ -375,7 +372,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
chairCheckTimer -= deltaTime;
|
||||
if (chairCheckTimer <= 0.0f && character.SelectedConstruction == null)
|
||||
if (chairCheckTimer <= 0.0f && character.SelectedSecondaryItem == null)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
|
||||
+1
-1
@@ -205,7 +205,7 @@ namespace Barotrauma
|
||||
if (!character.IsClimbing && character.CanInteractWith(target.Item, out _, checkLinked: false))
|
||||
{
|
||||
HumanAIController.FaceTarget(target.Item);
|
||||
if (character.SelectedConstruction != target.Item)
|
||||
if (character.SelectedItem != target.Item)
|
||||
{
|
||||
target.Item.TryInteract(character, forceSelectKey: true);
|
||||
}
|
||||
|
||||
+4
-6
@@ -26,7 +26,7 @@ namespace Barotrauma
|
||||
private bool IsRepairing() => IsRepairing(character, Item);
|
||||
private readonly bool isPriority;
|
||||
|
||||
public static bool IsRepairing(Character character, Item item) => character.SelectedConstruction == item && item.Repairables.Any(r => r.CurrentFixer == character);
|
||||
public static bool IsRepairing(Character character, Item item) => character.SelectedItem == item && item.Repairables.Any(r => r.CurrentFixer == character);
|
||||
|
||||
public AIObjectiveRepairItem(Character character, Item item, AIObjectiveManager objectiveManager, float priorityModifier = 1, bool isPriority = false)
|
||||
: base(character, objectiveManager, priorityModifier)
|
||||
@@ -165,7 +165,7 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (!character.IsClimbing && character.CanInteractWith(Item, out _, checkLinked: false))
|
||||
if (character.CanInteractWith(Item, out _, checkLinked: false))
|
||||
{
|
||||
waitTimer += deltaTime;
|
||||
if (waitTimer < WaitTimeBeforeRepair) { return; }
|
||||
@@ -184,12 +184,12 @@ namespace Barotrauma
|
||||
}
|
||||
if (!Abandon)
|
||||
{
|
||||
if (character.SelectedConstruction != Item)
|
||||
if (character.SelectedItem != Item)
|
||||
{
|
||||
if (Item.TryInteract(character, ignoreRequiredItems: true, forceSelectKey: true) ||
|
||||
Item.TryInteract(character, ignoreRequiredItems: true, forceUseKey: true))
|
||||
{
|
||||
character.SelectedConstruction = Item;
|
||||
character.SelectedItem = Item;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -232,8 +232,6 @@ namespace Barotrauma
|
||||
previousCondition = -1;
|
||||
var objective = new AIObjectiveGoTo(Item, character, objectiveManager)
|
||||
{
|
||||
// Don't stop in ladders, because we can't interact with other items while holding the ladders.
|
||||
endNodeFilter = node => node.Waypoint.Ladders == null,
|
||||
TargetName = Item.Name
|
||||
};
|
||||
if (repairTool != null)
|
||||
|
||||
+2
-2
@@ -67,7 +67,7 @@ namespace Barotrauma
|
||||
if (!ViableForRepair(item, character, HumanAIController)) { return false; };
|
||||
if (!Objectives.ContainsKey(item))
|
||||
{
|
||||
if (item != character.SelectedConstruction)
|
||||
if (item != character.SelectedItem)
|
||||
{
|
||||
if (NearlyFullCondition(item)) { return false; }
|
||||
}
|
||||
@@ -96,7 +96,7 @@ namespace Barotrauma
|
||||
|
||||
protected override float TargetEvaluation()
|
||||
{
|
||||
var selectedItem = character.SelectedConstruction;
|
||||
var selectedItem = character.SelectedItem;
|
||||
if (selectedItem != null && AIObjectiveRepairItem.IsRepairing(character, selectedItem) && selectedItem.ConditionPercentage < 100)
|
||||
{
|
||||
// Don't stop fixing until completely done
|
||||
|
||||
@@ -109,6 +109,8 @@ namespace Barotrauma
|
||||
{
|
||||
public delegate float? GetNodePenaltyHandler(PathNode node, PathNode prevNode);
|
||||
public GetNodePenaltyHandler GetNodePenalty;
|
||||
public delegate float? GetSingleNodePenaltyHandler(PathNode node);
|
||||
public GetSingleNodePenaltyHandler GetSingleNodePenalty;
|
||||
|
||||
private readonly List<PathNode> nodes;
|
||||
private readonly bool isCharacter;
|
||||
@@ -282,8 +284,6 @@ namespace Barotrauma
|
||||
}
|
||||
//avoid stopping at a doorway
|
||||
if (node.Waypoint.ConnectedDoor != null) { node.TempDistance *= 10.0f; }
|
||||
//avoid stopping at a ladder
|
||||
if (node.Waypoint.Ladders != null) { node.TempDistance *= 10.0f; }
|
||||
}
|
||||
//optimization: node extremely far (> 100m / 800 m) from the end position, don't try to use it as an end node
|
||||
if (node.TempDistance > (InsideSubmarine ? 100.0f * 100.0f : 800.0f * 800.0f))
|
||||
@@ -325,15 +325,24 @@ namespace Barotrauma
|
||||
#endif
|
||||
return new SteeringPath(true);
|
||||
}
|
||||
var path = FindPath(startNode, endNode, nodeFilter, errorMsgStr, minGapSize);
|
||||
return path;
|
||||
return FindPath(startNode, endNode, nodeFilter, errorMsgStr, minGapSize);
|
||||
|
||||
bool IsWaypointVisible(PathNode node, Vector2 rayStart, bool checkVisibility = true)
|
||||
bool IsValidStartNode(PathNode node) => IsValidNode(node, (isCharacter, start), startNodeFilter);
|
||||
|
||||
bool IsValidEndNode(PathNode node) => IsValidNode(node, (isCharacter && checkVisibility, end), endNodeFilter);
|
||||
|
||||
bool IsValidNode(PathNode node, (bool check, Vector2 start) visibilityCheck, Func<PathNode, bool> extraFilter)
|
||||
{
|
||||
//if searching for a path inside the sub, make sure the waypoint is visible
|
||||
if (checkVisibility && isCharacter)
|
||||
if (nodeFilter != null && !nodeFilter(node)) { return false; }
|
||||
if (extraFilter != null && !extraFilter(node)) { return false; }
|
||||
if (GetSingleNodePenalty != null && GetSingleNodePenalty(node) == null) { return false; }
|
||||
if (node.Waypoint.ConnectedGap != null)
|
||||
{
|
||||
var body = Submarine.PickBody(rayStart, node.TempPosition,
|
||||
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
|
||||
}
|
||||
if (visibilityCheck.check)
|
||||
{
|
||||
var body = Submarine.PickBody(visibilityCheck.start, node.TempPosition,
|
||||
collisionCategory: Physics.CollisionWall | Physics.CollisionLevel | Physics.CollisionStairs);
|
||||
if (body != null)
|
||||
{
|
||||
@@ -344,36 +353,6 @@ namespace Barotrauma
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsValidStartNode(PathNode node)
|
||||
{
|
||||
if (nodeFilter != null && !nodeFilter(node)) { return false; }
|
||||
if (startNodeFilter != null && !startNodeFilter(node)) { return false; }
|
||||
if (node.Waypoint.isObstructed) { return false; }
|
||||
// Always check the visibility for the start node
|
||||
if (!IsWaypointVisible(node, start)) { return false; }
|
||||
if (node.IsBlocked()) { return false; }
|
||||
if (node.Waypoint.ConnectedGap != null)
|
||||
{
|
||||
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IsValidEndNode(PathNode node)
|
||||
{
|
||||
if (nodeFilter != null && !nodeFilter(node)) { return false; }
|
||||
if (endNodeFilter != null && !endNodeFilter(node)) { return false; }
|
||||
if (node.Waypoint.isObstructed) { return false; }
|
||||
// Only check the visibility for the end node when allowed (fix leaks)
|
||||
if (!IsWaypointVisible(node, end, checkVisibility: checkVisibility)) { return false; }
|
||||
if (node.IsBlocked()) { return false; }
|
||||
if (node.Waypoint.ConnectedGap != null)
|
||||
{
|
||||
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { return false; }
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private SteeringPath FindPath(PathNode start, PathNode end, Func<PathNode, bool> filter = null, string errorMsgStr = "", float minGapSize = 0)
|
||||
@@ -402,15 +381,13 @@ namespace Barotrauma
|
||||
foreach (PathNode node in nodes)
|
||||
{
|
||||
if (node.state != 1 || node.F > dist) { continue; }
|
||||
if (isCharacter && node.Waypoint.isObstructed) { continue; }
|
||||
if (filter != null && !filter(node)) { continue; }
|
||||
if (node.IsBlocked()) { continue; }
|
||||
if (node.Waypoint.ConnectedGap != null)
|
||||
{
|
||||
if (!CanFitThroughGap(node.Waypoint.ConnectedGap, minGapSize)) { continue; }
|
||||
}
|
||||
}
|
||||
dist = node.F;
|
||||
currNode = node;
|
||||
currNode = node;
|
||||
}
|
||||
|
||||
if (currNode == null || currNode == end) { break; }
|
||||
@@ -515,7 +492,4 @@ namespace Barotrauma
|
||||
|
||||
private bool CanFitThroughGap(Gap gap, float minWidth) => gap.IsHorizontal ? gap.RectHeight > minWidth : gap.RectWidth > minWidth;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -115,6 +115,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsAtEndNode => currentIndex >= nodes.Count - 1;
|
||||
|
||||
public List<WayPoint> Nodes
|
||||
{
|
||||
get { return nodes; }
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (InWater || !CanWalk)
|
||||
{
|
||||
return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed);
|
||||
return TargetMovement.LengthSquared() > MathUtils.Pow2(SwimSlowParams.MovementSpeed + 0.0001f);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -134,9 +134,12 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public enum Animation { None, Climbing, UsingConstruction, Struggle, CPR };
|
||||
public enum Animation { None, Climbing, UsingItem, Struggle, CPR, UsingItemWhileClimbing };
|
||||
public Animation Anim;
|
||||
|
||||
public bool IsUsingItem => Anim == Animation.UsingItem || Anim == Animation.UsingItemWhileClimbing;
|
||||
public bool IsClimbing => Anim == Animation.Climbing || Anim == Animation.UsingItemWhileClimbing;
|
||||
|
||||
public Vector2 AimSourceWorldPos
|
||||
{
|
||||
get
|
||||
@@ -280,7 +283,7 @@ namespace Barotrauma
|
||||
public void UpdateUseItem(bool allowMovement, Vector2 handWorldPos)
|
||||
{
|
||||
useItemTimer = 0.5f;
|
||||
Anim = Animation.UsingConstruction;
|
||||
StartUsingItem();
|
||||
|
||||
if (!allowMovement)
|
||||
{
|
||||
@@ -359,8 +362,13 @@ namespace Barotrauma
|
||||
|
||||
Vector2 itemPos = aim ? aimPos : holdPos;
|
||||
|
||||
var controller = character.SelectedConstruction?.GetComponent<Controller>();
|
||||
var controller = character.SelectedItem?.GetComponent<Controller>();
|
||||
bool usingController = controller != null && !controller.AllowAiming;
|
||||
if (!usingController)
|
||||
{
|
||||
controller = character.SelectedSecondaryItem?.GetComponent<Controller>();
|
||||
usingController = controller != null && !controller.AllowAiming;
|
||||
}
|
||||
bool isClimbing = character.IsClimbing && Math.Abs(character.AnimController.TargetMovement.Y) > 0.01f;
|
||||
float itemAngle;
|
||||
Holdable holdable = item.GetComponent<Holdable>();
|
||||
@@ -722,5 +730,45 @@ namespace Barotrauma
|
||||
CalculateArmLengths();
|
||||
}
|
||||
}
|
||||
|
||||
private void StartAnimation(Animation animation)
|
||||
{
|
||||
if (animation == Animation.UsingItem)
|
||||
{
|
||||
Anim = IsClimbing ? Animation.UsingItemWhileClimbing : Animation.UsingItem;
|
||||
}
|
||||
else if (animation == Animation.Climbing)
|
||||
{
|
||||
Anim = IsUsingItem ? Animation.UsingItemWhileClimbing : Animation.Climbing;
|
||||
}
|
||||
else
|
||||
{
|
||||
Anim = animation;
|
||||
}
|
||||
}
|
||||
|
||||
private void StopAnimation(Animation animation)
|
||||
{
|
||||
if (animation == Animation.UsingItem)
|
||||
{
|
||||
Anim = IsClimbing ? Animation.Climbing : Animation.None;
|
||||
}
|
||||
else if (animation == Animation.Climbing)
|
||||
{
|
||||
Anim = IsUsingItem ? Animation.UsingItem : Animation.None;
|
||||
}
|
||||
else
|
||||
{
|
||||
Anim = Animation.None;
|
||||
}
|
||||
}
|
||||
|
||||
public void StartUsingItem() => StartAnimation(Animation.UsingItem);
|
||||
|
||||
public void StartClimbing() => StartAnimation(Animation.Climbing);
|
||||
|
||||
public void StopUsingItem() => StopAnimation(Animation.UsingItem);
|
||||
|
||||
public void StopClimbing() => StopAnimation(Animation.Climbing);
|
||||
}
|
||||
}
|
||||
|
||||
+52
-31
@@ -237,13 +237,14 @@ namespace Barotrauma
|
||||
|
||||
public override void UpdateAnim(float deltaTime)
|
||||
{
|
||||
if (Frozen) return;
|
||||
if (Frozen) { return; }
|
||||
if (MainLimb == null) { return; }
|
||||
|
||||
levitatingCollider = !IsHanging;
|
||||
ColliderIndex = Crouching && !swimming ? 1 : 0;
|
||||
if (character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false ||
|
||||
character.SelectedConstruction?.GetComponent<Ladder>() != null ||
|
||||
if ((character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) ||
|
||||
character.SelectedSecondaryItem?.GetComponent<Ladder>() != null ||
|
||||
(ForceSelectAnimationType != AnimationType.Crouch && ForceSelectAnimationType != AnimationType.NotDefined))
|
||||
{
|
||||
Crouching = false;
|
||||
@@ -354,12 +355,16 @@ namespace Barotrauma
|
||||
{
|
||||
ApplyTestPose();
|
||||
}
|
||||
else
|
||||
else if (Anim != Animation.UsingItem)
|
||||
{
|
||||
if (Anim != Animation.UsingConstruction)
|
||||
if (Anim != Animation.UsingItemWhileClimbing)
|
||||
{
|
||||
ResetPullJoints();
|
||||
}
|
||||
else
|
||||
{
|
||||
ResetPullJoints(l => l.IsLowerBody);
|
||||
}
|
||||
}
|
||||
|
||||
if (SimplePhysicsEnabled)
|
||||
@@ -377,49 +382,53 @@ namespace Barotrauma
|
||||
switch (Anim)
|
||||
{
|
||||
case Animation.Climbing:
|
||||
case Animation.UsingItemWhileClimbing:
|
||||
levitatingCollider = false;
|
||||
UpdateClimbing();
|
||||
UpdateUseItemTimer();
|
||||
break;
|
||||
case Animation.CPR:
|
||||
UpdateCPR(deltaTime);
|
||||
break;
|
||||
case Animation.UsingConstruction:
|
||||
case Animation.UsingItem:
|
||||
default:
|
||||
if (Anim == Animation.UsingConstruction)
|
||||
{
|
||||
useItemTimer -= deltaTime;
|
||||
if (useItemTimer <= 0.0f) Anim = Animation.None;
|
||||
}
|
||||
|
||||
UpdateUseItemTimer();
|
||||
swimmingStateLockTimer -= deltaTime;
|
||||
|
||||
if (forceStanding || character.AnimController.AnimationTestPose)
|
||||
{
|
||||
swimming = false;
|
||||
}
|
||||
else
|
||||
else if (swimming != inWater && swimmingStateLockTimer <= 0.0f)
|
||||
{
|
||||
//0.5 second delay for switching between swimming and walking
|
||||
//prevents rapid switches between swimming/walking if the water level is fluctuating around the minimum swimming depth
|
||||
if (swimming != inWater && swimmingStateLockTimer <= 0.0f)
|
||||
{
|
||||
swimming = inWater;
|
||||
swimmingStateLockTimer = 0.5f;
|
||||
}
|
||||
swimming = inWater;
|
||||
swimmingStateLockTimer = 0.5f;
|
||||
}
|
||||
|
||||
if (swimming)
|
||||
{
|
||||
UpdateSwimming();
|
||||
}
|
||||
else
|
||||
else if (character.SelectedItem == null || !(character.SelectedSecondaryItem?.GetComponent<Controller>() is { } controller) ||
|
||||
!controller.ControlCharacterPose || !controller.UserInCorrectPosition)
|
||||
{
|
||||
UpdateStanding();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
void UpdateUseItemTimer()
|
||||
{
|
||||
if (IsUsingItem)
|
||||
{
|
||||
useItemTimer -= deltaTime;
|
||||
if (useItemTimer <= 0.0f)
|
||||
{
|
||||
StopUsingItem();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (Timing.TotalTime > LockFlippingUntil && TargetDir != dir && !IsStuck)
|
||||
{
|
||||
Flip();
|
||||
@@ -841,7 +850,9 @@ namespace Barotrauma
|
||||
float targetSpeed = TargetMovement.Length();
|
||||
if (targetSpeed > 0.1f && !character.IsRemotelyControlled && !Aiming)
|
||||
{
|
||||
if (Anim != Animation.UsingConstruction && !(character.SelectedConstruction?.GetComponent<Controller>()?.ControlCharacterPose ?? false))
|
||||
if (!IsUsingItem &&
|
||||
!(character.SelectedItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false) &&
|
||||
!(character.SelectedSecondaryItem?.GetComponent<Controller>()?.ControlCharacterPose ?? false))
|
||||
{
|
||||
if (rotation > 20 && rotation < 170)
|
||||
{
|
||||
@@ -1041,12 +1052,17 @@ namespace Barotrauma
|
||||
|
||||
void UpdateClimbing()
|
||||
{
|
||||
var ladder = character.SelectedConstruction?.GetComponent<Ladder>();
|
||||
if (ladder == null || character.IsIncapacitated)
|
||||
var ladder = character.SelectedSecondaryItem?.GetComponent<Ladder>();
|
||||
if (character.IsIncapacitated)
|
||||
{
|
||||
Anim = Animation.None;
|
||||
return;
|
||||
}
|
||||
else if (ladder == null)
|
||||
{
|
||||
StopClimbing();
|
||||
return;
|
||||
}
|
||||
|
||||
onGround = false;
|
||||
IgnorePlatforms = true;
|
||||
@@ -1209,15 +1225,21 @@ namespace Barotrauma
|
||||
{
|
||||
RotateHead(head);
|
||||
}
|
||||
else if (Anim == Animation.UsingItemWhileClimbing && character.SelectedItem is { } selectedItem)
|
||||
{
|
||||
Vector2 diff = (selectedItem.WorldPosition - head.WorldPosition) * Dir;
|
||||
float targetRotation = MathHelper.WrapAngle(MathUtils.VectorToAngle(diff) - MathHelper.PiOver4 * Dir);
|
||||
head.body.SmoothRotate(targetRotation, force: WalkParams.HeadTorque);
|
||||
}
|
||||
else
|
||||
{
|
||||
float movementMultiplier = targetMovement.Y < 0 ? 0 : 1;
|
||||
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, WalkParams.HeadTorque);
|
||||
head.body.SmoothRotate(MathHelper.PiOver4 * movementMultiplier * Dir, force: WalkParams.HeadTorque);
|
||||
}
|
||||
|
||||
if (!ladder.Item.Prefab.Triggers.Any())
|
||||
if (ladder.Item.Prefab.Triggers.None())
|
||||
{
|
||||
character.SelectedConstruction = null;
|
||||
character.SelectedSecondaryItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1247,8 +1269,7 @@ namespace Barotrauma
|
||||
|
||||
if (!isClimbing)
|
||||
{
|
||||
Anim = Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
character.StopClimbing();
|
||||
IgnorePlatforms = false;
|
||||
}
|
||||
|
||||
@@ -1487,7 +1508,7 @@ namespace Barotrauma
|
||||
target.AnimController.ResetPullJoints();
|
||||
}
|
||||
|
||||
if (Anim == Animation.Climbing)
|
||||
if (IsClimbing)
|
||||
{
|
||||
//cannot drag up ladders if the character is conscious
|
||||
if (target.AllowInput && (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient))
|
||||
|
||||
@@ -922,12 +922,13 @@ namespace Barotrauma
|
||||
{
|
||||
limb.MoveToPos(pos, amount, pullFromCenter);
|
||||
}
|
||||
|
||||
public void ResetPullJoints()
|
||||
|
||||
public void ResetPullJoints(Func<Limb, bool> condition = null)
|
||||
{
|
||||
for (int i = 0; i < Limbs.Length; i++)
|
||||
{
|
||||
if (Limbs[i] == null) { continue; }
|
||||
if (condition != null && !condition(Limbs[i])) { continue; }
|
||||
Limbs[i].PullJointEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -813,43 +813,65 @@ namespace Barotrauma
|
||||
get { return AnimController?.Collider?.LinearVelocity.Length() ?? 0.0f; }
|
||||
}
|
||||
|
||||
private Item _selectedConstruction;
|
||||
public Item SelectedConstruction
|
||||
private Item _selectedItem;
|
||||
/// <summary>
|
||||
/// The primary selected item. It can be any device that character interacts with. This excludes items like ladders and chairs which are assigned to <see cref="SelectedSecondaryItem"/>.
|
||||
/// </summary>
|
||||
public Item SelectedItem
|
||||
{
|
||||
get => _selectedConstruction;
|
||||
get => _selectedItem;
|
||||
set
|
||||
{
|
||||
var prevSelectedConstruction = _selectedConstruction;
|
||||
_selectedConstruction = value;
|
||||
var prevSelectedItem = _selectedItem;
|
||||
_selectedItem = value;
|
||||
#if CLIENT
|
||||
HintManager.OnSetSelectedConstruction(this, prevSelectedConstruction, _selectedConstruction);
|
||||
HintManager.OnSetSelectedItem(this, prevSelectedItem, _selectedItem);
|
||||
if (Controlled == this)
|
||||
{
|
||||
if (_selectedConstruction == null)
|
||||
if (_selectedItem == null)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.ResetCrewList();
|
||||
}
|
||||
else if (_selectedConstruction.GetComponent<Ladder>() == null)
|
||||
else if (!_selectedItem.IsLadder)
|
||||
{
|
||||
GameMain.GameSession?.CrewManager?.AutoHideCrewList();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (prevSelectedConstruction == null && _selectedConstruction != null)
|
||||
if (prevSelectedItem != null && (_selectedItem == null || _selectedItem != prevSelectedItem) && itemSelectedTime > 0)
|
||||
{
|
||||
double selectedDuration = Timing.TotalTime - itemSelectedTime;
|
||||
if (itemSelectedDurations.ContainsKey(prevSelectedItem.Prefab))
|
||||
{
|
||||
itemSelectedDurations[prevSelectedItem.Prefab] += selectedDuration;
|
||||
}
|
||||
else
|
||||
{
|
||||
itemSelectedDurations.Add(prevSelectedItem.Prefab, selectedDuration);
|
||||
}
|
||||
itemSelectedTime = 0;
|
||||
}
|
||||
if (_selectedItem != null && (prevSelectedItem == null || prevSelectedItem != _selectedItem))
|
||||
{
|
||||
itemSelectedTime = Timing.TotalTime;
|
||||
}
|
||||
else if (prevSelectedConstruction != null && _selectedConstruction == null && itemSelectedTime > 0)
|
||||
{
|
||||
if (!itemSelectedDurations.ContainsKey(prevSelectedConstruction.Prefab))
|
||||
{
|
||||
itemSelectedDurations.Add(prevSelectedConstruction.Prefab, 0);
|
||||
}
|
||||
itemSelectedDurations[prevSelectedConstruction.Prefab] += Timing.TotalTime - itemSelectedTime;
|
||||
itemSelectedTime = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// The secondary selected item. It's an item other than a device (see <see cref="SelectedItem"/>), e.g. a ladder or a chair.
|
||||
/// </summary>
|
||||
public Item SelectedSecondaryItem { get; set; }
|
||||
/// <summary>
|
||||
/// Has the characters selected a primary or a secondary item?
|
||||
/// </summary>
|
||||
public bool HasSelectedAnyItem => SelectedItem != null || SelectedSecondaryItem != null;
|
||||
/// <summary>
|
||||
/// Is the item either the primary or the secondary selected item?
|
||||
/// </summary>
|
||||
/// <param name="item"></param>
|
||||
/// <returns></returns>
|
||||
public bool IsAnySelectedItem(Item item) => item == SelectedItem || item == SelectedSecondaryItem;
|
||||
public bool HasSelectedAnotherSecondaryItem(Item item) => SelectedSecondaryItem != null && SelectedSecondaryItem != item;
|
||||
|
||||
public Item FocusedItem
|
||||
{
|
||||
@@ -938,7 +960,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
return SelectedConstruction == null || SelectedConstruction.GetComponent<Ladder>() != null || (SelectedConstruction.GetComponent<Controller>()?.AllowAiming ?? false);
|
||||
return SelectedItem == null || (SelectedItem.GetComponent<Controller>()?.AllowAiming ?? false);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1481,8 +1503,20 @@ namespace Barotrauma
|
||||
|
||||
public void GiveJobItems(WayPoint spawnPoint = null)
|
||||
{
|
||||
if (info?.Job == null) { return; }
|
||||
info.Job.GiveJobItems(this, spawnPoint);
|
||||
if (info == null) { return; }
|
||||
if (info.HumanPrefabIds != default)
|
||||
{
|
||||
var humanPrefab = NPCSet.Get(info.HumanPrefabIds.NpcSetIdentifier, info.HumanPrefabIds.NpcIdentifier);
|
||||
if (humanPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to give job items for the character \"{Name}\" - could not find human prefab with the id \"{info.HumanPrefabIds.NpcIdentifier}\" from \"{info.HumanPrefabIds.NpcSetIdentifier}\".");
|
||||
}
|
||||
else if (humanPrefab.GiveItems(this, Submarine))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
info.Job?.GiveJobItems(this, spawnPoint);
|
||||
}
|
||||
|
||||
public void GiveIdCardTags(WayPoint spawnPoint, bool createNetworkEvent = false)
|
||||
@@ -1524,10 +1558,17 @@ namespace Barotrauma
|
||||
if (item?.GetComponent<Wearable>() is Wearable wearable &&
|
||||
!Inventory.IsInLimbSlot(item, InvSlotType.Any))
|
||||
{
|
||||
if (wearable.SkillModifiers.TryGetValue(skillIdentifier, out float skillValue))
|
||||
foreach (var allowedSlot in wearable.AllowedSlots)
|
||||
{
|
||||
skillLevel += skillValue;
|
||||
if (allowedSlot == InvSlotType.Any) { continue; }
|
||||
if (!Inventory.IsInLimbSlot(item, allowedSlot)) { continue; }
|
||||
if (wearable.SkillModifiers.TryGetValue(skillIdentifier, out float skillValue))
|
||||
{
|
||||
skillLevel += skillValue;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1541,7 +1582,7 @@ namespace Barotrauma
|
||||
public Vector2? OverrideMovement { get; set; }
|
||||
public bool ForceRun { get; set; }
|
||||
|
||||
public bool IsClimbing => AnimController.Anim == AnimController.Animation.Climbing;
|
||||
public bool IsClimbing => AnimController.IsClimbing;
|
||||
|
||||
public Vector2 GetTargetMovement()
|
||||
{
|
||||
@@ -1771,6 +1812,11 @@ namespace Barotrauma
|
||||
return speed;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping.
|
||||
/// </summary>
|
||||
private const float cursorFollowMargin = 40;
|
||||
|
||||
public void Control(float deltaTime, Camera cam)
|
||||
{
|
||||
ViewTarget = null;
|
||||
@@ -1803,10 +1849,10 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (!aiControlled &&
|
||||
AnimController.Anim != AnimController.Animation.UsingConstruction &&
|
||||
!AnimController.IsUsingItem &&
|
||||
AnimController.Anim != AnimController.Animation.CPR &&
|
||||
(GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient || Controlled == this) &&
|
||||
AnimController.OnGround && !AnimController.InWater)
|
||||
(AnimController.OnGround || IsClimbing) && !AnimController.InWater)
|
||||
{
|
||||
if (dontFollowCursor)
|
||||
{
|
||||
@@ -1814,13 +1860,11 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
// Values lower than this seem to cause constantious flipping when the mouse is near the player and the player is running, because the root collider moves after flipping.
|
||||
float followMargin = 40;
|
||||
if (CursorPosition.X < AnimController.Collider.Position.X - followMargin)
|
||||
if (CursorPosition.X < AnimController.Collider.Position.X - cursorFollowMargin)
|
||||
{
|
||||
AnimController.TargetDir = Direction.Left;
|
||||
}
|
||||
else if (CursorPosition.X > AnimController.Collider.Position.X + followMargin)
|
||||
else if (CursorPosition.X > AnimController.Collider.Position.X + cursorFollowMargin)
|
||||
{
|
||||
AnimController.TargetDir = Direction.Right;
|
||||
}
|
||||
@@ -1967,7 +2011,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedConstruction == null || !SelectedConstruction.Prefab.DisableItemUsageWhenSelected)
|
||||
bool CanUseItemsWhenSelected(Item item) => item == null || !item.Prefab.DisableItemUsageWhenSelected;
|
||||
if (CanUseItemsWhenSelected(SelectedItem) && CanUseItemsWhenSelected(SelectedSecondaryItem))
|
||||
{
|
||||
foreach (Item item in HeldItems)
|
||||
{
|
||||
@@ -1998,24 +2043,24 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
if (SelectedConstruction != null)
|
||||
if (SelectedItem != null)
|
||||
{
|
||||
if (IsKeyDown(InputType.Aim) || !SelectedConstruction.RequireAimToSecondaryUse)
|
||||
if (IsKeyDown(InputType.Aim) || !SelectedItem.RequireAimToSecondaryUse)
|
||||
{
|
||||
SelectedConstruction.SecondaryUse(deltaTime, this);
|
||||
SelectedItem.SecondaryUse(deltaTime, this);
|
||||
}
|
||||
if (IsKeyDown(InputType.Use) && SelectedConstruction != null && !SelectedConstruction.IsShootable)
|
||||
if (IsKeyDown(InputType.Use) && SelectedItem != null && !SelectedItem.IsShootable)
|
||||
{
|
||||
if (!SelectedConstruction.RequireAimToUse || IsKeyDown(InputType.Aim))
|
||||
if (!SelectedItem.RequireAimToUse || IsKeyDown(InputType.Aim))
|
||||
{
|
||||
SelectedConstruction.Use(deltaTime, this);
|
||||
SelectedItem.Use(deltaTime, this);
|
||||
}
|
||||
}
|
||||
if (IsKeyDown(InputType.Shoot) && SelectedConstruction != null && SelectedConstruction.IsShootable)
|
||||
if (IsKeyDown(InputType.Shoot) && SelectedItem != null && SelectedItem.IsShootable)
|
||||
{
|
||||
if (!SelectedConstruction.RequireAimToUse || IsKeyDown(InputType.Aim))
|
||||
if (!SelectedItem.RequireAimToUse || IsKeyDown(InputType.Aim))
|
||||
{
|
||||
SelectedConstruction.Use(deltaTime, this);
|
||||
SelectedItem.Use(deltaTime, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2351,15 +2396,15 @@ namespace Barotrauma
|
||||
|
||||
//wires are interactable if the character has selected an item the wire is connected to,
|
||||
//and it's disconnected from the other end
|
||||
if (wire.Connections[0]?.Item != null && SelectedConstruction == wire.Connections[0].Item)
|
||||
if (wire.Connections[0]?.Item != null && SelectedItem == wire.Connections[0].Item)
|
||||
{
|
||||
return wire.Connections[1] == null;
|
||||
}
|
||||
if (wire.Connections[1]?.Item != null && SelectedConstruction == wire.Connections[1].Item)
|
||||
if (wire.Connections[1]?.Item != null && SelectedItem == wire.Connections[1].Item)
|
||||
{
|
||||
return wire.Connections[0] == null;
|
||||
}
|
||||
if (SelectedConstruction?.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false)
|
||||
if (SelectedItem?.GetComponent<ConnectionPanel>()?.DisconnectedWires.Contains(wire) ?? false)
|
||||
{
|
||||
return wire.Connections[0] == null && wire.Connections[1] == null;
|
||||
}
|
||||
@@ -2385,7 +2430,7 @@ namespace Barotrauma
|
||||
Pickable pickableComponent = item.GetComponent<Pickable>();
|
||||
if (pickableComponent != null && pickableComponent.Picker != this && pickableComponent.Picker != null && !pickableComponent.Picker.IsDead) { return false; }
|
||||
|
||||
if (SelectedConstruction?.GetComponent<RemoteController>()?.TargetItem == item) { return true; }
|
||||
if (SelectedItem?.GetComponent<RemoteController>()?.TargetItem == item) { return true; }
|
||||
//optimization: don't use HeldItems because it allocates memory and this method is executed very frequently
|
||||
var heldItem1 = Inventory?.GetItemInLimbSlot(InvSlotType.RightHand);
|
||||
if (heldItem1?.GetComponent<RemoteController>()?.TargetItem == item) { return true; }
|
||||
@@ -2428,27 +2473,43 @@ namespace Barotrauma
|
||||
distanceToItem = Vector2.Distance(rectIntersectionPoint, playerDistanceCheckPosition);
|
||||
}
|
||||
|
||||
if (distanceToItem > item.InteractDistance && item.InteractDistance > 0.0f) { return false; }
|
||||
float interactDistance = item.InteractDistance;
|
||||
if ((SelectedSecondaryItem != null || item.IsSecondaryItem) && AnimController is HumanoidAnimController c)
|
||||
{
|
||||
// Use a distance slightly shorter than the arms length to keep the character in a comfortable pose
|
||||
float armLength = 0.75f * ConvertUnits.ToDisplayUnits(c.ArmLength);
|
||||
interactDistance = Math.Min(interactDistance, armLength);
|
||||
}
|
||||
if (distanceToItem > interactDistance && item.InteractDistance > 0.0f) { return false; }
|
||||
|
||||
Vector2 itemPosition = item.SimPosition;
|
||||
if (Submarine == null && item.Submarine != null)
|
||||
{
|
||||
//character is outside, item inside
|
||||
itemPosition += item.Submarine.SimPosition;
|
||||
}
|
||||
else if (Submarine != null && item.Submarine == null)
|
||||
{
|
||||
//character is inside, item outside
|
||||
itemPosition -= Submarine.SimPosition;
|
||||
}
|
||||
else if (Submarine != item.Submarine)
|
||||
{
|
||||
//character and the item are inside different subs
|
||||
itemPosition += item.Submarine.SimPosition;
|
||||
itemPosition -= Submarine.SimPosition;
|
||||
}
|
||||
|
||||
if (SelectedSecondaryItem != null && !item.IsSecondaryItem)
|
||||
{
|
||||
if (item.GetComponent<Controller>() is { } controller && controller.Direction != 0 && controller.Direction != AnimController.Direction) { return false; }
|
||||
float threshold = ConvertUnits.ToSimUnits(cursorFollowMargin);
|
||||
if (AnimController.Direction == Direction.Left && SimPosition.X + threshold < itemPosition.X) { return false; }
|
||||
if (AnimController.Direction == Direction.Right && SimPosition.X - threshold > itemPosition.X) { return false; }
|
||||
}
|
||||
|
||||
if (!item.Prefab.InteractThroughWalls && Screen.Selected != GameMain.SubEditorScreen && !insideTrigger)
|
||||
{
|
||||
Vector2 itemPosition = item.SimPosition;
|
||||
if (Submarine == null && item.Submarine != null)
|
||||
{
|
||||
//character is outside, item inside
|
||||
itemPosition += item.Submarine.SimPosition;
|
||||
}
|
||||
else if (Submarine != null && item.Submarine == null)
|
||||
{
|
||||
//character is inside, item outside
|
||||
itemPosition -= Submarine.SimPosition;
|
||||
}
|
||||
else if (Submarine != item.Submarine)
|
||||
{
|
||||
//character and the item are inside different subs
|
||||
itemPosition += item.Submarine.SimPosition;
|
||||
itemPosition -= Submarine.SimPosition;
|
||||
}
|
||||
var body = Submarine.CheckVisibility(SimPosition, itemPosition, ignoreLevel: true);
|
||||
if (body != null && body.UserData as Item != item && (body.UserData as ItemComponent)?.Item != item && Submarine.LastPickedFixture?.UserData as Item != item)
|
||||
{
|
||||
@@ -2521,7 +2582,7 @@ namespace Barotrauma
|
||||
|
||||
if (!CanInteract)
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
focusedItem = null;
|
||||
if (!AllowInput)
|
||||
{
|
||||
@@ -2570,8 +2631,8 @@ namespace Barotrauma
|
||||
AnimController.InWater :
|
||||
head.InWater;
|
||||
//climb ladders automatically when pressing up/down inside their trigger area
|
||||
Ladder currentLadder = SelectedConstruction?.GetComponent<Ladder>();
|
||||
if ((SelectedConstruction == null || currentLadder != null) &&
|
||||
Ladder currentLadder = SelectedSecondaryItem?.GetComponent<Ladder>();
|
||||
if ((SelectedSecondaryItem == null || currentLadder != null) &&
|
||||
!headInWater && Screen.Selected != GameMain.SubEditorScreen)
|
||||
{
|
||||
bool climbInput = IsKeyDown(InputType.Up) || IsKeyDown(InputType.Down);
|
||||
@@ -2613,7 +2674,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (nearbyLadder.Select(this))
|
||||
{
|
||||
SelectedConstruction = nearbyLadder.Item;
|
||||
SelectedSecondaryItem = nearbyLadder.Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2657,16 +2718,23 @@ namespace Barotrauma
|
||||
{
|
||||
FocusedCharacter.onCustomInteract(FocusedCharacter, this);
|
||||
}
|
||||
else if (IsKeyHit(InputType.Deselect) && SelectedConstruction != null && SelectedConstruction.GetComponent<Ladder>() == null)
|
||||
else if (IsKeyHit(InputType.Deselect) && SelectedItem != null)
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = null;
|
||||
#if CLIENT
|
||||
CharacterHealth.OpenHealthWindow = null;
|
||||
#endif
|
||||
}
|
||||
else if (IsKeyHit(InputType.Health) && SelectedConstruction != null && SelectedConstruction.GetComponent<Ladder>() == null)
|
||||
else if (IsKeyHit(InputType.Deselect) && SelectedSecondaryItem != null)
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
SelectedSecondaryItem = null;
|
||||
#if CLIENT
|
||||
CharacterHealth.OpenHealthWindow = null;
|
||||
#endif
|
||||
}
|
||||
else if (IsKeyHit(InputType.Health) && (SelectedItem != null || SelectedSecondaryItem != null))
|
||||
{
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
}
|
||||
else if (focusedItem != null)
|
||||
{
|
||||
@@ -2901,7 +2969,7 @@ namespace Barotrauma
|
||||
{
|
||||
Stun = Math.Max(5.0f, Stun);
|
||||
AnimController.ResetPullJoints();
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2958,7 +3026,7 @@ namespace Barotrauma
|
||||
humanAnimController.Crouching = false;
|
||||
}
|
||||
AnimController.ResetPullJoints();
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2974,9 +3042,13 @@ namespace Barotrauma
|
||||
DoInteractionUpdate(deltaTime, mouseSimPos);
|
||||
}
|
||||
|
||||
if (SelectedConstruction != null && !CanInteractWith(SelectedConstruction))
|
||||
if (SelectedItem != null && !CanInteractWith(SelectedItem))
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = null;
|
||||
}
|
||||
if (SelectedSecondaryItem != null && !CanInteractWith(SelectedSecondaryItem))
|
||||
{
|
||||
SelectedSecondaryItem = null;
|
||||
}
|
||||
|
||||
if (!IsDead) { LockHands = false; }
|
||||
@@ -3914,7 +3986,7 @@ namespace Barotrauma
|
||||
CharacterHealth.Stun = newStun;
|
||||
if (newStun > 0.0f)
|
||||
{
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
}
|
||||
HealthUpdateInterval = 0.0f;
|
||||
}
|
||||
@@ -3930,77 +4002,79 @@ namespace Barotrauma
|
||||
CharacterHealth.ReduceAfflictionOnAllLimbs("damage".ToIdentifier(), eatingRegen * deltaTime);
|
||||
}
|
||||
}
|
||||
if (!statusEffects.TryGetValue(actionType, out var statusEffectList)) { return; }
|
||||
foreach (StatusEffect statusEffect in statusEffectList)
|
||||
if (statusEffects.TryGetValue(actionType, out var statusEffectList))
|
||||
{
|
||||
if (statusEffect.type == ActionType.OnDamaged)
|
||||
foreach (StatusEffect statusEffect in statusEffectList)
|
||||
{
|
||||
if (!statusEffect.HasRequiredAfflictions(LastDamage)) { continue; }
|
||||
if (statusEffect.OnlyPlayerTriggered)
|
||||
if (statusEffect.type == ActionType.OnDamaged)
|
||||
{
|
||||
if (LastAttacker == null || !LastAttacker.IsPlayer)
|
||||
if (!statusEffect.HasRequiredAfflictions(LastDamage)) { continue; }
|
||||
if (statusEffect.OnlyPlayerTriggered)
|
||||
{
|
||||
continue;
|
||||
if (LastAttacker == null || !LastAttacker.IsPlayer)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
targets.Clear();
|
||||
targets.AddRange(statusEffect.GetNearbyTargets(WorldPosition, targets));
|
||||
statusEffect.Apply(actionType, deltaTime, this, targets);
|
||||
}
|
||||
else if (statusEffect.targetLimbs != null)
|
||||
{
|
||||
foreach (var limbType in statusEffect.targetLimbs)
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.NearbyItems) ||
|
||||
statusEffect.HasTargetType(StatusEffect.TargetType.NearbyCharacters))
|
||||
{
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
targets.Clear();
|
||||
targets.AddRange(statusEffect.GetNearbyTargets(WorldPosition, targets));
|
||||
statusEffect.Apply(actionType, deltaTime, this, targets);
|
||||
}
|
||||
else if (statusEffect.targetLimbs != null)
|
||||
{
|
||||
foreach (var limbType in statusEffect.targetLimbs)
|
||||
{
|
||||
// Target all matching limbs
|
||||
foreach (var limb in AnimController.Limbs)
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.AllLimbs))
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type == limbType)
|
||||
// Target all matching limbs
|
||||
foreach (var limb in AnimController.Limbs)
|
||||
{
|
||||
if (limb.IsSevered) { continue; }
|
||||
if (limb.type == limbType)
|
||||
{
|
||||
statusEffect.sourceBody = limb.body;
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
// Target just the first matching limb
|
||||
Limb limb = AnimController.GetLimb(limbType);
|
||||
if (limb != null)
|
||||
{
|
||||
statusEffect.sourceBody = limb.body;
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.LastLimb))
|
||||
{
|
||||
// Target just the last matching limb
|
||||
Limb limb = AnimController.Limbs.LastOrDefault(l => l.type == limbType && !l.IsSevered && !l.Hidden);
|
||||
if (limb != null)
|
||||
{
|
||||
statusEffect.sourceBody = limb.body;
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.Limb))
|
||||
{
|
||||
// Target just the first matching limb
|
||||
Limb limb = AnimController.GetLimb(limbType);
|
||||
if (limb != null)
|
||||
{
|
||||
statusEffect.sourceBody = limb.body;
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
else if (statusEffect.HasTargetType(StatusEffect.TargetType.LastLimb))
|
||||
{
|
||||
// Target just the last matching limb
|
||||
Limb limb = AnimController.Limbs.LastOrDefault(l => l.type == limbType && !l.IsSevered && !l.Hidden);
|
||||
if (limb != null)
|
||||
{
|
||||
statusEffect.sourceBody = limb.body;
|
||||
statusEffect.Apply(actionType, deltaTime, this, limb);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.This) || statusEffect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
{
|
||||
statusEffect.Apply(actionType, deltaTime, this, this);
|
||||
}
|
||||
}
|
||||
if (statusEffect.HasTargetType(StatusEffect.TargetType.This) || statusEffect.HasTargetType(StatusEffect.TargetType.Character))
|
||||
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
||||
{
|
||||
statusEffect.Apply(actionType, deltaTime, this, this);
|
||||
}
|
||||
}
|
||||
if (actionType != ActionType.OnDamaged && actionType != ActionType.OnSevered)
|
||||
{
|
||||
// OnDamaged is called only for the limb that is hit.
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
limb.ApplyStatusEffects(actionType, deltaTime);
|
||||
// OnDamaged is called only for the limb that is hit.
|
||||
foreach (Limb limb in AnimController.Limbs)
|
||||
{
|
||||
limb.ApplyStatusEffects(actionType, deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
//OnActive effects are handled by the afflictions themselves
|
||||
@@ -4075,10 +4149,12 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
#if SERVER
|
||||
if (GameMain.NetworkMember is { IsServer: true })
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new CharacterStatusEventData());
|
||||
GameMain.NetworkMember.CreateEntityEvent(this, new CharacterStatusEventData(forceAfflictionData: true));
|
||||
}
|
||||
#endif
|
||||
|
||||
isDead = true;
|
||||
|
||||
@@ -4153,7 +4229,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
SelectedConstruction = null;
|
||||
SelectedItem = SelectedSecondaryItem = null;
|
||||
SelectedCharacter = null;
|
||||
|
||||
AnimController.ResetPullJoints();
|
||||
@@ -4896,6 +4972,12 @@ namespace Barotrauma
|
||||
/// Compares just the species name and the group, ignores teams. There's a more complex version found in HumanAIController.cs
|
||||
/// </summary>
|
||||
public static bool IsFriendly(Character me, Character other) => other.SpeciesName == me.SpeciesName || other.Params.CompareGroup(me.Params.Group);
|
||||
|
||||
public void StopClimbing()
|
||||
{
|
||||
AnimController.StopClimbing();
|
||||
SelectedSecondaryItem = null;
|
||||
}
|
||||
}
|
||||
|
||||
class ActiveTeamChange
|
||||
|
||||
@@ -54,6 +54,15 @@ namespace Barotrauma
|
||||
public struct CharacterStatusEventData : IEventData
|
||||
{
|
||||
public EventType EventType => EventType.Status;
|
||||
|
||||
#if SERVER
|
||||
public bool ForceAfflictionData;
|
||||
|
||||
public CharacterStatusEventData(bool forceAfflictionData)
|
||||
{
|
||||
ForceAfflictionData = forceAfflictionData;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public struct TreatmentEventData : IEventData
|
||||
|
||||
@@ -261,6 +261,10 @@ namespace Barotrauma
|
||||
|
||||
public string Name;
|
||||
|
||||
public LocalizedString Title;
|
||||
|
||||
public (Identifier NpcSetIdentifier, Identifier NpcIdentifier) HumanPrefabIds;
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get
|
||||
@@ -649,15 +653,12 @@ namespace Barotrauma
|
||||
{
|
||||
Name = name;
|
||||
}
|
||||
else if (!npcIdentifier.IsEmpty && TextManager.Get("npctitle." + npcIdentifier) is { Loaded: true } npcTitle)
|
||||
{
|
||||
Name = npcTitle.Value;
|
||||
}
|
||||
else
|
||||
{
|
||||
Name = GetRandomName(randSync);
|
||||
}
|
||||
|
||||
|
||||
TryLoadNameAndTitle(npcIdentifier);
|
||||
SetPersonalityTrait();
|
||||
|
||||
Salary = CalculateSalary();
|
||||
@@ -727,7 +728,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
// Used for loading the data
|
||||
public CharacterInfo(XElement infoElement)
|
||||
public CharacterInfo(XElement infoElement, Identifier npcIdentifier = default)
|
||||
{
|
||||
ID = idCounter;
|
||||
idCounter++;
|
||||
@@ -774,6 +775,8 @@ namespace Barotrauma
|
||||
Head.FacialHairColor = infoElement.GetAttributeColor("facialhaircolor", Color.White);
|
||||
CheckColors();
|
||||
|
||||
TryLoadNameAndTitle(npcIdentifier);
|
||||
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
{
|
||||
var nameElement = CharacterConfigElement.GetChildElement("names");
|
||||
@@ -794,9 +797,21 @@ namespace Barotrauma
|
||||
ragdollFileName = infoElement.GetAttributeString("ragdoll", string.Empty);
|
||||
if (personalityName != Identifier.Empty)
|
||||
{
|
||||
PersonalityTrait = NPCPersonalityTrait.Get(GameSettings.CurrentConfig.Language, personalityName);
|
||||
if (NPCPersonalityTrait.Traits.TryGet(personalityName, out var trait) ||
|
||||
NPCPersonalityTrait.Traits.TryGet(personalityName.Replace(" ".ToIdentifier(), Identifier.Empty), out trait))
|
||||
{
|
||||
PersonalityTrait = trait;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Error in CharacterInfo \"{OriginalName}\": could not find a personality trait with the identifier \"{personalityName}\".");
|
||||
}
|
||||
}
|
||||
|
||||
HumanPrefabIds = (
|
||||
element.GetAttributeIdentifier("npcsetid", Identifier.Empty),
|
||||
element.GetAttributeIdentifier("npcid", Identifier.Empty));
|
||||
|
||||
MissionsCompletedSinceDeath = infoElement.GetAttributeInt("missionscompletedsincedeath", 0);
|
||||
|
||||
foreach (var subElement in infoElement.Elements())
|
||||
@@ -838,6 +853,19 @@ namespace Barotrauma
|
||||
LoadHeadAttachments();
|
||||
}
|
||||
|
||||
private void TryLoadNameAndTitle(Identifier npcIdentifier)
|
||||
{
|
||||
if (!npcIdentifier.IsEmpty)
|
||||
{
|
||||
Title = TextManager.Get("npctitle." + npcIdentifier);
|
||||
string nameTag = "charactername." + npcIdentifier;
|
||||
if (TextManager.ContainsTag(nameTag))
|
||||
{
|
||||
Name = TextManager.Get(nameTag).Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<ContentXElement> hairs;
|
||||
public IReadOnlyList<ContentXElement> Hairs => hairs;
|
||||
private List<ContentXElement> beards;
|
||||
@@ -1292,9 +1320,16 @@ namespace Barotrauma
|
||||
new XAttribute("facialhaircolor", XMLExtensions.ColorToString(Head.FacialHairColor)),
|
||||
new XAttribute("startitemsgiven", StartItemsGiven),
|
||||
new XAttribute("ragdoll", ragdollFileName),
|
||||
new XAttribute("personality", PersonalityTrait?.Name.Value ?? ""));
|
||||
new XAttribute("personality", PersonalityTrait?.Identifier ?? Identifier.Empty));
|
||||
// TODO: animations?
|
||||
|
||||
if (HumanPrefabIds != default)
|
||||
{
|
||||
charElement.Add(
|
||||
new XAttribute("npcsetid", HumanPrefabIds.NpcSetIdentifier),
|
||||
new XAttribute("npcid", HumanPrefabIds.NpcIdentifier));
|
||||
}
|
||||
|
||||
charElement.Add(new XAttribute("missionscompletedsincedeath", MissionsCompletedSinceDeath));
|
||||
|
||||
if (Character != null)
|
||||
@@ -1323,11 +1358,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
charElement.Add(savedStatElement);
|
||||
|
||||
parentElement.Add(charElement);
|
||||
parentElement?.Add(charElement);
|
||||
return charElement;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,26 +10,27 @@ namespace Barotrauma
|
||||
public readonly Direction Direction;
|
||||
|
||||
public readonly Character SelectedCharacter;
|
||||
public readonly Item SelectedItem;
|
||||
public readonly Item SelectedItem, SelectedSecondaryItem;
|
||||
|
||||
public readonly AnimController.Animation Animation;
|
||||
|
||||
public CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, float time, Direction dir, Character selectedCharacter, Item selectedItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
: this(pos, rotation, velocity, angularVelocity, 0, time, dir, selectedCharacter, selectedItem, animation)
|
||||
public CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, float time, Direction dir, Character selectedCharacter, Item selectedItem, Item selectedSecondaryItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
: this(pos, rotation, velocity, angularVelocity, 0, time, dir, selectedCharacter, selectedItem, selectedSecondaryItem, animation)
|
||||
{
|
||||
}
|
||||
|
||||
public CharacterStateInfo(Vector2 pos, float? rotation, UInt16 ID, Direction dir, Character selectedCharacter, Item selectedItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
: this(pos, rotation, Vector2.Zero, 0.0f, ID, 0.0f, dir, selectedCharacter, selectedItem, animation)
|
||||
public CharacterStateInfo(Vector2 pos, float? rotation, UInt16 ID, Direction dir, Character selectedCharacter, Item selectedItem, Item selectedSecondaryItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
: this(pos, rotation, Vector2.Zero, 0.0f, ID, 0.0f, dir, selectedCharacter, selectedItem, selectedSecondaryItem, animation)
|
||||
{
|
||||
}
|
||||
|
||||
protected CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, UInt16 ID, float time, Direction dir, Character selectedCharacter, Item selectedItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
protected CharacterStateInfo(Vector2 pos, float? rotation, Vector2 velocity, float? angularVelocity, UInt16 ID, float time, Direction dir, Character selectedCharacter, Item selectedItem, Item selectedSecondaryItem, AnimController.Animation animation = AnimController.Animation.None)
|
||||
: base(pos, rotation, velocity, angularVelocity, ID, time)
|
||||
{
|
||||
Direction = dir;
|
||||
SelectedCharacter = selectedCharacter;
|
||||
SelectedItem = selectedItem;
|
||||
SelectedSecondaryItem = selectedSecondaryItem;
|
||||
|
||||
Animation = animation;
|
||||
}
|
||||
|
||||
@@ -46,7 +46,7 @@ namespace Barotrauma
|
||||
[Serialize(0, IsPropertySaveable.No)]
|
||||
public int MaxMoney { get; private set; }
|
||||
|
||||
public CorpsePrefab(ContentXElement element, CorpsesFile file) : base(element, file) { }
|
||||
public CorpsePrefab(ContentXElement element, CorpsesFile file) : base(element, file, npcSetIdentifier: Identifier.Empty) { }
|
||||
|
||||
public static CorpsePrefab Random(Rand.RandSync sync = Rand.RandSync.Unsynced) => Prefabs.GetRandom(sync);
|
||||
}
|
||||
|
||||
@@ -55,13 +55,27 @@ namespace Barotrauma
|
||||
if (vitalityMultipliers != null)
|
||||
{
|
||||
float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f);
|
||||
vitalityMultipliers.ForEach(i => VitalityMultipliers.Add(i, multiplier));
|
||||
foreach (var vitalityMultiplier in vitalityMultipliers)
|
||||
{
|
||||
VitalityMultipliers.Add(vitalityMultiplier, multiplier);
|
||||
if (AfflictionPrefab.Prefabs.None(p => p.Identifier == vitalityMultiplier))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions with the identifier \"{vitalityMultiplier}\". Did you mean to define the afflictions by type instead?");
|
||||
}
|
||||
}
|
||||
}
|
||||
var vitalityTypeMultipliers = subElement.GetAttributeIdentifierArray("type", null) ?? subElement.GetAttributeIdentifierArray("types", null);
|
||||
if (vitalityTypeMultipliers != null)
|
||||
{
|
||||
float multiplier = subElement.GetAttributeFloat("multiplier", 1.0f);
|
||||
vitalityTypeMultipliers.ForEach(i => VitalityTypeMultipliers.Add(i, multiplier));
|
||||
foreach (var vitalityTypeMultiplier in vitalityTypeMultipliers)
|
||||
{
|
||||
VitalityTypeMultipliers.Add(vitalityTypeMultiplier, multiplier);
|
||||
if (AfflictionPrefab.Prefabs.None(p => p.AfflictionType == vitalityTypeMultiplier))
|
||||
{
|
||||
DebugConsole.AddWarning($"Potentially incorrectly defined vitality multiplier in \"{characterHealth.Character.Name}\". Could not find any afflictions of the type \"{vitalityTypeMultiplier}\". Did you mean to define the afflictions by identifier instead?");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (vitalityMultipliers == null && VitalityTypeMultipliers == null)
|
||||
{
|
||||
|
||||
@@ -10,7 +10,7 @@ namespace Barotrauma
|
||||
class HumanPrefab : PrefabWithUintIdentifier
|
||||
{
|
||||
[Serialize("any", IsPropertySaveable.No)]
|
||||
public string Job { get; protected set; }
|
||||
public Identifier Job { get; protected set; }
|
||||
|
||||
[Serialize(1f, IsPropertySaveable.No)]
|
||||
public float Commonness { get; protected set; }
|
||||
@@ -82,17 +82,19 @@ namespace Barotrauma
|
||||
public XElement Element { get; protected set; }
|
||||
|
||||
|
||||
public readonly Dictionary<XElement, float> ItemSets = new Dictionary<XElement, float>();
|
||||
public readonly Dictionary<XElement, float> CustomNPCSets = new Dictionary<XElement, float>();
|
||||
public readonly List<(XElement element, float commonness)> ItemSets = new List<(XElement element, float commonness)>();
|
||||
public readonly List<(XElement element, float commonness)> CustomCharacterInfos = new List<(XElement element, float commonness)>();
|
||||
|
||||
public HumanPrefab(ContentXElement element, ContentFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
private readonly Identifier npcSetIdentifier;
|
||||
|
||||
public HumanPrefab(ContentXElement element, ContentFile file, Identifier npcSetIdentifier) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
SerializableProperty.DeserializeProperties(this, element);
|
||||
Job = Job.ToLowerInvariant();
|
||||
Element = element;
|
||||
element.GetChildElements("itemset").ForEach(e => ItemSets.Add(e, e.GetAttributeFloat("commonness", 1)));
|
||||
element.GetChildElements("character").ForEach(e => CustomNPCSets.Add(e, e.GetAttributeFloat("commonness", 1)));
|
||||
element.GetChildElements("itemset").ForEach(e => ItemSets.Add((e, e.GetAttributeFloat("commonness", 1))));
|
||||
element.GetChildElements("character").ForEach(e => CustomCharacterInfos.Add((e, e.GetAttributeFloat("commonness", 1))));
|
||||
PreferredOutpostModuleTypes = element.GetAttributeIdentifierArray("preferredoutpostmoduletypes", Array.Empty<Identifier>());
|
||||
this.npcSetIdentifier = npcSetIdentifier;
|
||||
}
|
||||
|
||||
public IEnumerable<Identifier> GetModuleFlags()
|
||||
@@ -107,7 +109,7 @@ namespace Barotrauma
|
||||
|
||||
public JobPrefab GetJobPrefab(Rand.RandSync randSync = Rand.RandSync.Unsynced, Func<JobPrefab, bool> predicate = null)
|
||||
{
|
||||
return Job != null && Job != "any" ? JobPrefab.Get(Job) : JobPrefab.Random(randSync, predicate);
|
||||
return !Job.IsEmpty && Job != "any" ? JobPrefab.Get(Job) : JobPrefab.Random(randSync, predicate);
|
||||
}
|
||||
|
||||
public void InitializeCharacter(Character npc, ISpatialEntity positionToStayIn = null)
|
||||
@@ -146,23 +148,43 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public void GiveItems(Character character, Submarine submarine, Rand.RandSync randSync = Rand.RandSync.Unsynced, bool createNetworkEvents = true)
|
||||
public bool GiveItems(Character character, Submarine submarine, Rand.RandSync randSync = Rand.RandSync.Unsynced, bool createNetworkEvents = true)
|
||||
{
|
||||
if (ItemSets == null || !ItemSets.Any()) { return; }
|
||||
var spawnItems = ToolBox.SelectWeightedRandom(ItemSets.Keys.ToList(), ItemSets.Values.ToList(), randSync);
|
||||
if (ItemSets == null || !ItemSets.Any()) { return false; }
|
||||
var spawnItems = ToolBox.SelectWeightedRandom(ItemSets, it => it.commonness, randSync).element;
|
||||
if (spawnItems != null)
|
||||
{
|
||||
foreach (XElement itemElement in spawnItems.GetChildElements("item"))
|
||||
{
|
||||
InitializeItem(character, itemElement, submarine, this, createNetworkEvents: createNetworkEvents);
|
||||
int amount = itemElement.GetAttributeInt("amount", 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
InitializeItem(character, itemElement, submarine, this, createNetworkEvents: createNetworkEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public CharacterInfo GetCharacterInfo(Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
/// <summary>
|
||||
/// Creates a character info from the human prefab. If there are custom character infos defined, those are used, otherwise a randomized info is generated.
|
||||
/// </summary>
|
||||
/// <param name="randSync"></param>
|
||||
/// <returns></returns>
|
||||
public CharacterInfo CreateCharacterInfo(Rand.RandSync randSync = Rand.RandSync.Unsynced)
|
||||
{
|
||||
var characterElement = ToolBox.SelectWeightedRandom(CustomNPCSets.Keys.ToList(), CustomNPCSets.Values.ToList(), randSync);
|
||||
return characterElement != null ? new CharacterInfo(characterElement) : null;
|
||||
var characterElement = ToolBox.SelectWeightedRandom(CustomCharacterInfos, info => info.commonness, randSync).element;
|
||||
CharacterInfo characterInfo;
|
||||
if (characterElement == null)
|
||||
{
|
||||
characterInfo= new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: GetJobPrefab(randSync), npcIdentifier: Identifier);
|
||||
}
|
||||
else
|
||||
{
|
||||
characterInfo = new CharacterInfo(characterElement, Identifier);
|
||||
}
|
||||
characterInfo.HumanPrefabIds = (npcSetIdentifier, Identifier);
|
||||
return characterInfo;
|
||||
}
|
||||
|
||||
public static void InitializeItem(Character character, XElement itemElement, Submarine submarine, HumanPrefab humanPrefab, Item parentItem = null, bool createNetworkEvents = true)
|
||||
@@ -229,7 +251,11 @@ namespace Barotrauma
|
||||
parentItem?.Combine(item, user: null);
|
||||
foreach (XElement childItemElement in itemElement.Elements())
|
||||
{
|
||||
InitializeItem(character, childItemElement, submarine, humanPrefab, item, createNetworkEvents);
|
||||
int amount = childItemElement.GetAttributeInt("amount", 1);
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
InitializeItem(character, childItemElement, submarine, humanPrefab, item, createNetworkEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
|
||||
public int Variant;
|
||||
|
||||
public Skill PrimarySkill { get; }
|
||||
public Skill PrimarySkill { get; private set; }
|
||||
|
||||
public Job(JobPrefab jobPrefab) : this(jobPrefab, randSync: Rand.RandSync.Unsynced, variant: 0) { }
|
||||
|
||||
@@ -102,9 +102,14 @@ namespace Barotrauma
|
||||
public void OverrideSkills(Dictionary<Identifier, float> newSkills)
|
||||
{
|
||||
skills.Clear();
|
||||
foreach (var newSkill in newSkills)
|
||||
foreach (var newSkillInfo in newSkills)
|
||||
{
|
||||
skills.Add(newSkill.Key, new Skill(newSkill.Key, newSkill.Value));
|
||||
var newSkill = new Skill(newSkillInfo.Key, newSkillInfo.Value);
|
||||
if (PrimarySkill != null && newSkill.Identifier == PrimarySkill.Identifier)
|
||||
{
|
||||
PrimarySkill = newSkill;
|
||||
}
|
||||
skills.Add(newSkillInfo.Key, newSkill);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public static IReadOnlyDictionary<Identifier, float> ItemRepairPriorities => _itemRepairPriorities;
|
||||
|
||||
public static JobPrefab Get(string identifier)
|
||||
public static JobPrefab Get(Identifier identifier)
|
||||
{
|
||||
if (Prefabs.ContainsKey(identifier))
|
||||
{
|
||||
|
||||
@@ -303,6 +303,17 @@ namespace Barotrauma
|
||||
public Vector2 DebugTargetPos;
|
||||
public Vector2 DebugRefPos;
|
||||
|
||||
public bool IsLowerBody =>
|
||||
type == LimbType.LeftLeg ||
|
||||
type == LimbType.RightLeg ||
|
||||
type == LimbType.LeftFoot ||
|
||||
type == LimbType.RightFoot ||
|
||||
type == LimbType.Tail ||
|
||||
type == LimbType.Legs ||
|
||||
type == LimbType.RightThigh ||
|
||||
type == LimbType.LeftThigh ||
|
||||
type == LimbType.Waist;
|
||||
|
||||
public bool IsSevered
|
||||
{
|
||||
get { return isSevered; }
|
||||
|
||||
@@ -5,9 +5,11 @@ using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class NPCPersonalityTrait
|
||||
class NPCPersonalityTrait : PrefabWithUintIdentifier
|
||||
{
|
||||
public readonly Identifier Name;
|
||||
public readonly static PrefabCollection<NPCPersonalityTrait> Traits = new PrefabCollection<NPCPersonalityTrait>();
|
||||
|
||||
public readonly LocalizedString DisplayName;
|
||||
|
||||
public readonly List<string> AllowedDialogTags;
|
||||
|
||||
@@ -17,43 +19,29 @@ namespace Barotrauma
|
||||
get { return commonness; }
|
||||
}
|
||||
|
||||
public static IEnumerable<NPCPersonalityTrait> GetAll(LanguageIdentifier language)
|
||||
public NPCPersonalityTrait(XElement element, NPCPersonalityTraitsFile file)
|
||||
: base(file, element.GetAttributeIdentifier("identifier", element.GetAttributeIdentifier("name", Identifier.Empty)))
|
||||
{
|
||||
if (language != TextManager.DefaultLanguage && !NPCConversationCollection.Collections.ContainsKey(language))
|
||||
string name = element.GetAttributeString("name", null);
|
||||
if (name == null)
|
||||
{
|
||||
DebugConsole.AddWarning($"Could not find NPC personality traits for the language \"{language}\". Using \"{TextManager.DefaultLanguage}\" instead..");
|
||||
language = TextManager.DefaultLanguage;
|
||||
DisplayName = TextManager.Get("personalitytrait." + Identifier)
|
||||
.Fallback(Identifier.ToString());
|
||||
}
|
||||
return NPCConversationCollection.Collections[language]
|
||||
.SelectMany(cc => cc.PersonalityTraits.Values);
|
||||
}
|
||||
|
||||
public static NPCPersonalityTrait Get(LanguageIdentifier language, Identifier traitName)
|
||||
{
|
||||
if (language != TextManager.DefaultLanguage && !NPCConversationCollection.Collections.ContainsKey(language))
|
||||
else
|
||||
{
|
||||
DebugConsole.AddWarning($"Could not find NPC personality traits for the language \"{language}\". Using \"{TextManager.DefaultLanguage}\" instead..");
|
||||
language = TextManager.DefaultLanguage;
|
||||
DisplayName = name;
|
||||
}
|
||||
return NPCConversationCollection.Collections[language]
|
||||
.FirstOrDefault(cc => cc.PersonalityTraits.ContainsKey(traitName))
|
||||
.PersonalityTraits[traitName];
|
||||
}
|
||||
|
||||
public NPCPersonalityTrait(XElement element)
|
||||
{
|
||||
Name = element.GetAttributeIdentifier("name", "");
|
||||
AllowedDialogTags = new List<string>(element.GetAttributeStringArray("alloweddialogtags", Array.Empty<string>()));
|
||||
commonness = element.GetAttributeFloat("commonness", 1.0f);
|
||||
}
|
||||
|
||||
public static NPCPersonalityTrait GetRandom(string seed)
|
||||
{
|
||||
#warning TODO: implement NPCPersonality content type and revise this for determinism
|
||||
var rand = new MTRandom(ToolBox.StringToInt(seed));
|
||||
var list = GetAll(GameSettings.CurrentConfig.Language);
|
||||
return ToolBox.SelectWeightedRandom(list, t => t.commonness, rand);
|
||||
return ToolBox.SelectWeightedRandom(Traits.OrderBy(t => t.UintIdentifier), t => t.commonness, rand);
|
||||
}
|
||||
|
||||
public override void Dispose() { }
|
||||
}
|
||||
}
|
||||
|
||||
+6
-2
@@ -17,7 +17,7 @@ namespace Barotrauma.Abilities
|
||||
|
||||
protected override void ApplyEffect()
|
||||
{
|
||||
if (Character.SelectedConstruction == null || !Character.SelectedConstruction.HasTag(tag)) { return; }
|
||||
if (!SelectedItemHasTag(Character)) { return; }
|
||||
|
||||
Character closestCharacter = null;
|
||||
float closestDistance = squaredMaxDistance;
|
||||
@@ -31,13 +31,17 @@ namespace Barotrauma.Abilities
|
||||
}
|
||||
}
|
||||
|
||||
if (closestCharacter?.SelectedConstruction == null || !closestCharacter.SelectedConstruction.HasTag(tag)) { return; }
|
||||
if (!SelectedItemHasTag(closestCharacter)) { return; }
|
||||
|
||||
if (closestDistance < squaredMaxDistance)
|
||||
{
|
||||
ApplyEffectSpecific(Character);
|
||||
ApplyEffectSpecific(closestCharacter);
|
||||
}
|
||||
|
||||
bool SelectedItemHasTag(Character character) =>
|
||||
(character.SelectedItem != null && character.SelectedItem.HasTag(tag)) ||
|
||||
(character.SelectedSecondaryItem != null && character.SelectedSecondaryItem.HasTag(tag));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
namespace Barotrauma
|
||||
{
|
||||
sealed class NPCPersonalityTraitsFile : GenericPrefabFile<NPCPersonalityTrait>
|
||||
{
|
||||
public NPCPersonalityTraitsFile(ContentPackage contentPackage, ContentPath path) : base(contentPackage, path) { }
|
||||
|
||||
protected override bool MatchesSingular(Identifier identifier) => identifier == "personalitytrait";
|
||||
protected override bool MatchesPlural(Identifier identifier) => identifier == "personalitytraits";
|
||||
protected override PrefabCollection<NPCPersonalityTrait> Prefabs => NPCPersonalityTrait.Traits;
|
||||
protected override NPCPersonalityTrait CreatePrefab(ContentXElement element) => new NPCPersonalityTrait(element, this);
|
||||
}
|
||||
}
|
||||
@@ -84,7 +84,9 @@ namespace Barotrauma
|
||||
{
|
||||
static readonly List<CoroutineHandle> Coroutines = new List<CoroutineHandle>();
|
||||
|
||||
public static float UnscaledDeltaTime, DeltaTime;
|
||||
public static float DeltaTime { get; private set; }
|
||||
|
||||
public static bool Paused { get; private set; }
|
||||
|
||||
public static CoroutineHandle StartCoroutine(IEnumerable<CoroutineStatus> func, string name = "", bool useSeparateThread = false)
|
||||
{
|
||||
@@ -191,7 +193,7 @@ namespace Barotrauma
|
||||
if (current != null)
|
||||
{
|
||||
if (current.EndsCoroutine(handle) || handle.AbortRequested) { return true; }
|
||||
if (!current.CheckFinished(UnscaledDeltaTime)) { return false; }
|
||||
if (!current.CheckFinished(DeltaTime)) { return false; }
|
||||
}
|
||||
if (!handle.Coroutine.MoveNext()) { return true; }
|
||||
return false;
|
||||
@@ -204,7 +206,7 @@ namespace Barotrauma
|
||||
while (!handle.AbortRequested)
|
||||
{
|
||||
if (PerformCoroutineStep(handle)) { return; }
|
||||
Thread.Sleep((int)(UnscaledDeltaTime * 1000));
|
||||
Thread.Sleep((int)(DeltaTime * 1000));
|
||||
}
|
||||
}
|
||||
catch (ThreadAbortException)
|
||||
@@ -232,7 +234,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (handle.Thread.ThreadState.HasFlag(ThreadState.Stopped))
|
||||
{
|
||||
if (handle.Exception!=null || handle.Coroutine.Current == CoroutineStatus.Failure)
|
||||
if (handle.Exception != null || handle.Coroutine.Current == CoroutineStatus.Failure)
|
||||
{
|
||||
DebugConsole.ThrowError("Coroutine \"" + handle.Name + "\" has failed");
|
||||
}
|
||||
@@ -254,9 +256,9 @@ namespace Barotrauma
|
||||
#endif
|
||||
}
|
||||
// Updating just means stepping through all the coroutines
|
||||
public static void Update(float unscaledDeltaTime, float deltaTime)
|
||||
public static void Update(bool paused, float deltaTime)
|
||||
{
|
||||
UnscaledDeltaTime = unscaledDeltaTime;
|
||||
Paused = paused;
|
||||
DeltaTime = deltaTime;
|
||||
|
||||
List<CoroutineHandle> coroutineList;
|
||||
@@ -282,8 +284,8 @@ namespace Barotrauma
|
||||
{
|
||||
public readonly float TotalTime;
|
||||
|
||||
float timer;
|
||||
bool ignorePause;
|
||||
private float timer;
|
||||
private readonly bool ignorePause;
|
||||
|
||||
public WaitForSeconds(float time, bool ignorePause = true)
|
||||
{
|
||||
@@ -295,7 +297,7 @@ namespace Barotrauma
|
||||
public override bool CheckFinished(float deltaTime)
|
||||
{
|
||||
#if !SERVER
|
||||
if (ignorePause || !GUI.PauseMenuOpen)
|
||||
if (ignorePause || !CoroutineManager.Paused)
|
||||
{
|
||||
timer -= deltaTime;
|
||||
}
|
||||
|
||||
@@ -798,7 +798,55 @@ namespace Barotrauma
|
||||
eventPrefabs.Select(prefab => prefab.Identifier).Distinct().Select(id => id.Value).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
|
||||
commands.Add(new Command("unlockmission", "unlockmission [identifier/tag]: Unlocks a mission in a random adjacent level.", (string[] args) =>
|
||||
{
|
||||
if (!(GameMain.GameSession?.GameMode is CampaignMode campaign))
|
||||
{
|
||||
ThrowError("The unlockmission command is only usable in the campaign mode.");
|
||||
return;
|
||||
}
|
||||
if (args.Length == 0)
|
||||
{
|
||||
ThrowError("Please enter the identifier or a tag of the mission you want to unlock.");
|
||||
return;
|
||||
}
|
||||
var currentLocation = campaign.Map.CurrentLocation;
|
||||
if (MissionPrefab.Prefabs.Any(p => p.Identifier == args[0]))
|
||||
{
|
||||
currentLocation.UnlockMissionByIdentifier(args[0].ToIdentifier());
|
||||
}
|
||||
else
|
||||
{
|
||||
currentLocation.UnlockMissionByTag(args[0].ToIdentifier());
|
||||
}
|
||||
if (campaign is MultiPlayerCampaign mpCampaign)
|
||||
{
|
||||
mpCampaign.IncrementLastUpdateIdForFlag(MultiPlayerCampaign.NetFlags.MapAndMissions);
|
||||
}
|
||||
}, isCheat: true, getValidArgs: () =>
|
||||
{
|
||||
return new[]
|
||||
{
|
||||
MissionPrefab.Prefabs.Select(p => p.Identifier.ToString()).ToArray()
|
||||
};
|
||||
}));
|
||||
|
||||
commands.Add(new Command("setcampaignmetadata", "setcampaignmetadata [identifier] [value]: Sets the specified campaign metadata value.", (string[] args) =>
|
||||
{
|
||||
if (!(GameMain.GameSession?.GameMode is CampaignMode campaign))
|
||||
{
|
||||
ThrowError("The setcampaignmetadata command is only usable in the campaign mode.");
|
||||
return;
|
||||
}
|
||||
if (args.Length < 2)
|
||||
{
|
||||
ThrowError("Please specify an identifier and a value.");
|
||||
return;
|
||||
}
|
||||
SetDataAction.PerformOperation(campaign.CampaignMetadata, args[0].ToIdentifier(), args[1], SetDataAction.OperationType.Set);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("setskill", "setskill [all/identifier] [max/level] [character]: Set your skill level.", (string[] args) =>
|
||||
{
|
||||
if (args.Length < 2)
|
||||
@@ -2532,9 +2580,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
GameMain.DebugDraw = false;
|
||||
GameMain.LightManager.LightingEnabled = true;
|
||||
Character.DebugDrawInteract = false;
|
||||
#endif
|
||||
Hull.EditWater = false;
|
||||
Hull.EditFire = false;
|
||||
EnemyAIController.DisableEnemyAI = false;
|
||||
HumanAIController.DisableCrewAI = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,11 @@ namespace Barotrauma
|
||||
[Serialize(false, IsPropertySaveable.Yes)]
|
||||
public bool ContinueConversation { get; set; }
|
||||
|
||||
private Character speaker;
|
||||
public Character speaker
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private AIObjective prevIdleObjective, prevGotoObjective;
|
||||
|
||||
@@ -120,7 +124,7 @@ namespace Barotrauma
|
||||
#else
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.InGame && c.Character != null) { ServerWrite(speaker, c); }
|
||||
if (c.InGame && c.Character != null) { ServerWrite(speaker, c, interrupt); }
|
||||
}
|
||||
#endif
|
||||
ResetSpeaker();
|
||||
@@ -331,9 +335,11 @@ namespace Barotrauma
|
||||
if (!TargetTag.IsEmpty)
|
||||
{
|
||||
targets = ParentEvent.GetTargets(TargetTag).Where(e => IsValidTarget(e));
|
||||
if (!targets.Any() || IsBlockedByAnotherConversation(targets)) { return; }
|
||||
if (!targets.Any() || IsBlockedByAnotherConversation(targets, BlockOtherConversationsDuration)) { return; }
|
||||
}
|
||||
|
||||
if (targetCharacter != null && IsBlockedByAnotherConversation(targetCharacter.ToEnumerable(), 0.1f)) { return; }
|
||||
|
||||
if (speaker?.AIController is HumanAIController humanAI)
|
||||
{
|
||||
prevIdleObjective = humanAI.ObjectiveManager.GetObjective<AIObjectiveIdle>();
|
||||
|
||||
@@ -118,7 +118,7 @@ namespace Barotrauma
|
||||
ISpatialEntity spawnPos = GetSpawnPos();
|
||||
if (spawnPos != null)
|
||||
{
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), humanPrefab.GetCharacterInfo(), onSpawn: newCharacter =>
|
||||
Entity.Spawner.AddCharacterToSpawnQueue(CharacterPrefab.HumanSpeciesName, OffsetSpawnPos(spawnPos.WorldPosition, 100.0f), humanPrefab.CreateCharacterInfo(), onSpawn: newCharacter =>
|
||||
{
|
||||
if (newCharacter == null) { return; }
|
||||
newCharacter.HumanPrefab = humanPrefab;
|
||||
|
||||
@@ -157,8 +157,9 @@ namespace Barotrauma
|
||||
npcsOrItems.Add(item);
|
||||
}
|
||||
item.CampaignInteractionType = CampaignMode.InteractionType.Examine;
|
||||
if (player.SelectedConstruction == item ||
|
||||
player.Inventory != null && player.Inventory.Contains(item) ||
|
||||
if (player.SelectedItem == item ||
|
||||
player.SelectedSecondaryItem == item ||
|
||||
(player.Inventory != null && player.Inventory.Contains(item)) ||
|
||||
(player.FocusedItem == item && player.IsKeyHit(InputType.Use)))
|
||||
{
|
||||
Trigger(e1, e2);
|
||||
|
||||
@@ -71,6 +71,10 @@ namespace Barotrauma
|
||||
|
||||
private readonly List<Event> activeEvents = new List<Event>();
|
||||
|
||||
private readonly HashSet<Event> finishedEvents = new HashSet<Event>();
|
||||
private readonly HashSet<EventPrefab> nonRepeatableEvents = new HashSet<EventPrefab>();
|
||||
|
||||
|
||||
#if DEBUG && SERVER
|
||||
private DateTime nextIntensityLogTime;
|
||||
#endif
|
||||
@@ -169,54 +173,49 @@ namespace Barotrauma
|
||||
CreateEvents(additiveSet);
|
||||
}
|
||||
|
||||
if (level?.LevelData?.Type == LevelData.LevelType.Outpost)
|
||||
if (level?.LevelData != null)
|
||||
{
|
||||
//if the outpost is connected to a locked connection, create an event to unlock it
|
||||
if (level.StartLocation?.Connections.Any(c => c.Locked && level.StartLocation.MapPosition.X < c.OtherLocation(level.StartLocation).MapPosition.X) ?? false)
|
||||
if (level.LevelData.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
var unlockPathPrefabs = EventPrefab.Prefabs.Where(e => e.UnlockPathEvent);
|
||||
var unlockPathPrefabsForBiome = unlockPathPrefabs.Where(e =>
|
||||
e.BiomeIdentifier.IsEmpty ||
|
||||
e.BiomeIdentifier == level.LevelData.Biome.Identifier);
|
||||
//if the outpost is connected to a locked connection, create an event to unlock it
|
||||
if (level.StartLocation?.Connections.Any(c => c.Locked && level.StartLocation.MapPosition.X < c.OtherLocation(level.StartLocation).MapPosition.X) ?? false)
|
||||
{
|
||||
var unlockPathPrefabs = EventPrefab.Prefabs.Where(e => e.UnlockPathEvent);
|
||||
var unlockPathPrefabsForBiome = unlockPathPrefabs.Where(e =>
|
||||
e.BiomeIdentifier.IsEmpty ||
|
||||
e.BiomeIdentifier == level.LevelData.Biome.Identifier);
|
||||
|
||||
var unlockPathEventPrefab = unlockPathPrefabsForBiome.Any() ?
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabsForBiome, b => b.Commonness, rand) :
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabs, b => b.Commonness, rand);
|
||||
if (unlockPathEventPrefab != null)
|
||||
{
|
||||
var newEvent = unlockPathEventPrefab.CreateInstance();
|
||||
newEvent.Init();
|
||||
ActiveEvents.Add(newEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if no event that unlocks the path can be found, unlock it automatically
|
||||
level.StartLocation.Connections.ForEach(c => c.Locked = false);
|
||||
}
|
||||
}
|
||||
|
||||
level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e)));
|
||||
if (level.LevelData.EventHistory.Count > MaxEventHistory)
|
||||
{
|
||||
level.LevelData.EventHistory.RemoveRange(0, level.LevelData.EventHistory.Count - MaxEventHistory);
|
||||
}
|
||||
AddChildEvents(initialEventSet);
|
||||
void AddChildEvents(EventSet eventSet)
|
||||
{
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.EventPrefabs))
|
||||
var unlockPathEventPrefab = unlockPathPrefabsForBiome.Any() ?
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabsForBiome, b => b.Commonness, rand) :
|
||||
ToolBox.SelectWeightedRandom(unlockPathPrefabs, b => b.Commonness, rand);
|
||||
if (unlockPathEventPrefab != null)
|
||||
{
|
||||
if (!level.LevelData.NonRepeatableEvents.Contains(ep))
|
||||
{
|
||||
level.LevelData.NonRepeatableEvents.Add(ep);
|
||||
}
|
||||
var newEvent = unlockPathEventPrefab.CreateInstance();
|
||||
newEvent.Init();
|
||||
ActiveEvents.Add(newEvent);
|
||||
}
|
||||
else
|
||||
{
|
||||
//if no event that unlocks the path can be found, unlock it automatically
|
||||
level.StartLocation.Connections.ForEach(c => c.Locked = false);
|
||||
}
|
||||
}
|
||||
foreach (EventSet childSet in eventSet.ChildSets)
|
||||
|
||||
AddChildEvents(initialEventSet);
|
||||
void AddChildEvents(EventSet eventSet)
|
||||
{
|
||||
AddChildEvents(childSet);
|
||||
if (eventSet == null) { return; }
|
||||
if (eventSet.OncePerOutpost)
|
||||
{
|
||||
foreach (EventPrefab ep in eventSet.EventPrefabs.SelectMany(e => e.EventPrefabs))
|
||||
{
|
||||
nonRepeatableEvents.Add(ep);
|
||||
}
|
||||
}
|
||||
foreach (EventSet childSet in eventSet.ChildSets)
|
||||
{
|
||||
AddChildEvents(childSet);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -350,13 +349,33 @@ namespace Barotrauma
|
||||
selectedEvents.Clear();
|
||||
activeEvents.Clear();
|
||||
QueuedEvents.Clear();
|
||||
finishedEvents.Clear();
|
||||
nonRepeatableEvents.Clear();
|
||||
|
||||
preloadedSprites.ForEach(s => s.Remove());
|
||||
preloadedSprites.Clear();
|
||||
|
||||
|
||||
pathFinder = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Registers the exhaustible events in the level as exhausted, and adds the current events to the event history
|
||||
/// </summary>
|
||||
public void RegisterEventHistory()
|
||||
{
|
||||
level.LevelData.EventsExhausted = true;
|
||||
if (level?.LevelData != null && level.LevelData.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
level.LevelData.EventHistory.AddRange(selectedEvents.Values.SelectMany(v => v).Select(e => e.Prefab).Where(e => !level.LevelData.EventHistory.Contains(e)));
|
||||
if (level.LevelData.EventHistory.Count > MaxEventHistory)
|
||||
{
|
||||
level.LevelData.EventHistory.RemoveRange(0, level.LevelData.EventHistory.Count - MaxEventHistory);
|
||||
}
|
||||
level.LevelData.NonRepeatableEvents.AddRange(nonRepeatableEvents.Where(e => !level.LevelData.NonRepeatableEvents.Contains(e)));
|
||||
}
|
||||
}
|
||||
|
||||
public void SkipEventCooldown()
|
||||
{
|
||||
eventCoolDown = 0.0f;
|
||||
@@ -375,6 +394,8 @@ namespace Barotrauma
|
||||
selectedEvents.Remove(eventSet);
|
||||
if (level == null) { return; }
|
||||
if (level.LevelData.HasHuntingGrounds && eventSet.DisableInHuntingGrounds) { return; }
|
||||
if (eventSet.Exhaustible && level.LevelData.EventsExhausted) { return; }
|
||||
|
||||
DebugConsole.NewMessage($"Loading event set {eventSet.Identifier}", Color.LightBlue, debugOnly: true);
|
||||
|
||||
int applyCount = 1;
|
||||
@@ -427,7 +448,8 @@ namespace Barotrauma
|
||||
if (suitablePrefabSubsets.Any())
|
||||
{
|
||||
var unusedEvents = suitablePrefabSubsets.ToList();
|
||||
for (int j = 0; j < eventSet.EventCount; j++)
|
||||
int eventCount = eventSet.GetEventCount(level);
|
||||
for (int j = 0; j < eventCount; j++)
|
||||
{
|
||||
if (unusedEvents.All(e => e.EventPrefabs.All(p => CalculateCommonness(p, e.Commonness) <= 0.0f))) { break; }
|
||||
EventSet.SubEventPrefab subEventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, e => e.EventPrefabs.Max(p => CalculateCommonness(p, e.Commonness)), rand);
|
||||
@@ -706,7 +728,18 @@ namespace Barotrauma
|
||||
|
||||
foreach (Event ev in activeEvents)
|
||||
{
|
||||
if (!ev.IsFinished) { ev.Update(deltaTime); }
|
||||
if (!ev.IsFinished)
|
||||
{
|
||||
ev.Update(deltaTime);
|
||||
}
|
||||
else if (!finishedEvents.Contains(ev))
|
||||
{
|
||||
if (level?.LevelData != null && level.LevelData.Type == LevelData.LevelType.Outpost)
|
||||
{
|
||||
if (!level.LevelData.EventHistory.Contains(ev.Prefab)) { level.LevelData.EventHistory.Add(ev.Prefab); }
|
||||
}
|
||||
finishedEvents.Add(ev);
|
||||
}
|
||||
}
|
||||
|
||||
if (QueuedEvents.Count > 0)
|
||||
|
||||
@@ -92,7 +92,13 @@ namespace Barotrauma
|
||||
|
||||
public readonly bool ChooseRandom;
|
||||
|
||||
public readonly int EventCount = 1;
|
||||
private readonly int eventCount = 1;
|
||||
private readonly Dictionary<Identifier, int> overrideEventCount = new Dictionary<Identifier, int>();
|
||||
|
||||
/// <summary>
|
||||
/// 'Exhaustible' sets won't appear in the same level until after one world step (~10 min, see Map.ProgressWorld) has passed.
|
||||
/// </summary>
|
||||
public readonly bool Exhaustible;
|
||||
|
||||
public readonly float MinDistanceTraveled;
|
||||
public readonly float MinMissionTime;
|
||||
@@ -250,7 +256,8 @@ namespace Barotrauma
|
||||
MaxIntensity = Math.Max(element.GetAttributeFloat("maxintensity", 100.0f), MinIntensity);
|
||||
|
||||
ChooseRandom = element.GetAttributeBool("chooserandom", false);
|
||||
EventCount = element.GetAttributeInt("eventcount", 1);
|
||||
eventCount = element.GetAttributeInt("eventcount", 1);
|
||||
Exhaustible = element.GetAttributeBool("exhaustible", false);
|
||||
MinDistanceTraveled = element.GetAttributeFloat("mindistancetraveled", 0.0f);
|
||||
MinMissionTime = element.GetAttributeFloat("minmissiontime", 0.0f);
|
||||
|
||||
@@ -288,6 +295,13 @@ namespace Barotrauma
|
||||
case "eventset":
|
||||
childSets.Add(new EventSet(subElement, file, this));
|
||||
break;
|
||||
case "overrideeventcount":
|
||||
Identifier locationType = subElement.GetAttributeIdentifier("locationtype", "");
|
||||
if (!overrideEventCount.ContainsKey(locationType))
|
||||
{
|
||||
overrideEventCount.Add(locationType, subElement.GetAttributeInt("eventcount", eventCount));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
//an element with just an identifier = reference to an event prefab
|
||||
if (!subElement.HasElements && subElement.Attributes().First().Name.ToString().Equals("identifier", StringComparison.OrdinalIgnoreCase))
|
||||
@@ -332,6 +346,12 @@ namespace Barotrauma
|
||||
return OverrideCommonness.ContainsKey(key) ? OverrideCommonness[key] : DefaultCommonness;
|
||||
}
|
||||
|
||||
public int GetEventCount(Level level)
|
||||
{
|
||||
if (level?.StartLocation == null || !overrideEventCount.TryGetValue(level.StartLocation.Type.Identifier, out int count)) { return eventCount; }
|
||||
return count;
|
||||
}
|
||||
|
||||
public static List<string> GetDebugStatistics(int simulatedRoundCount = 100, Func<MonsterEvent, bool> filter = null, bool fullLog = false)
|
||||
{
|
||||
List<string> debugLines = new List<string>();
|
||||
@@ -358,7 +378,7 @@ namespace Barotrauma
|
||||
var unusedEvents = thisSet.EventPrefabs.ToList();
|
||||
if (unusedEvents.Any())
|
||||
{
|
||||
for (int i = 0; i < thisSet.EventCount; i++)
|
||||
for (int i = 0; i < thisSet.eventCount; i++)
|
||||
{
|
||||
var eventPrefab = ToolBox.SelectWeightedRandom(unusedEvents, unusedEvents.Select(e => e.Commonness).ToList(), Rand.RandSync.Unsynced);
|
||||
if (eventPrefab.EventPrefabs.Any(p => p != null))
|
||||
|
||||
@@ -95,37 +95,42 @@ namespace Barotrauma
|
||||
randSync = Rand.RandSync.Unsynced;
|
||||
}
|
||||
|
||||
//if any of the escortees have a job defined, try to use a spawnpoint designated for that job
|
||||
List<HumanPrefab> humanPrefabsToSpawn = new List<HumanPrefab>();
|
||||
foreach (XElement element in characterConfig.Elements())
|
||||
{
|
||||
int count = CalculateScalingEscortedCharacterCount(inMission: true);
|
||||
var humanPrefab = GetHumanPrefabFromElement(element);
|
||||
if (humanPrefab == null || string.IsNullOrEmpty(humanPrefab.Job) || humanPrefab.Job.Equals("any", StringComparison.OrdinalIgnoreCase)) { continue; }
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
humanPrefabsToSpawn.Add(humanPrefab);
|
||||
}
|
||||
}
|
||||
|
||||
var jobPrefab = humanPrefab.GetJobPrefab();
|
||||
//if any of the escortees have a job defined, try to use a spawnpoint designated for that job
|
||||
foreach (var humanPrefab in humanPrefabsToSpawn)
|
||||
{
|
||||
if (humanPrefab == null || humanPrefab.Job.IsEmpty || humanPrefab.Job == "any") { continue; }
|
||||
var jobPrefab = humanPrefab.GetJobPrefab(randSync);
|
||||
if (jobPrefab != null)
|
||||
{
|
||||
var jobSpecificSpawnPos = WayPoint.GetRandom(SpawnType.Human, jobPrefab, Submarine.MainSub);
|
||||
if (jobSpecificSpawnPos != null)
|
||||
if (jobSpecificSpawnPos != null)
|
||||
{
|
||||
explicitStayInHullPos = jobSpecificSpawnPos;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach (XElement element in characterConfig.Elements())
|
||||
foreach (var humanPrefab in humanPrefabsToSpawn)
|
||||
{
|
||||
int count = CalculateScalingEscortedCharacterCount(inMission: true);
|
||||
for (int i = 0; i < count; i++)
|
||||
Character spawnedCharacter = CreateHuman(humanPrefab, characters, characterItems, Submarine.MainSub, CharacterTeamType.FriendlyNPC, explicitStayInHullPos, humanPrefabRandSync: randSync);
|
||||
if (spawnedCharacter.AIController is HumanAIController humanAI)
|
||||
{
|
||||
Character spawnedCharacter = CreateHuman(GetHumanPrefabFromElement(element), characters, characterItems, Submarine.MainSub, CharacterTeamType.FriendlyNPC, explicitStayInHullPos, humanPrefabRandSync: randSync);
|
||||
if (spawnedCharacter.AIController is HumanAIController humanAI)
|
||||
{
|
||||
humanAI.InitMentalStateManager();
|
||||
}
|
||||
humanAI.InitMentalStateManager();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (terroristChance > 0f)
|
||||
{
|
||||
int terroristCount = (int)Math.Ceiling(terroristChance * Rand.Range(0.8f, 1.2f) * characters.Count);
|
||||
|
||||
@@ -511,7 +511,7 @@ namespace Barotrauma
|
||||
|
||||
protected Character CreateHuman(HumanPrefab humanPrefab, List<Character> characters, Dictionary<Character, List<Item>> characterItems, Submarine submarine, CharacterTeamType teamType, ISpatialEntity positionToStayIn = null, Rand.RandSync humanPrefabRandSync = Rand.RandSync.ServerAndClient, bool giveTags = true)
|
||||
{
|
||||
var characterInfo = humanPrefab.GetCharacterInfo(Rand.RandSync.ServerAndClient) ?? new CharacterInfo(CharacterPrefab.HumanSpeciesName, npcIdentifier: humanPrefab.Identifier, jobOrJobPrefab: humanPrefab.GetJobPrefab(humanPrefabRandSync), randSync: humanPrefabRandSync);
|
||||
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||
characterInfo.TeamID = teamType;
|
||||
|
||||
if (positionToStayIn == null)
|
||||
|
||||
@@ -2,21 +2,19 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
partial class MonsterMission : Mission
|
||||
{
|
||||
//string = filename, point = min,max
|
||||
private readonly HashSet<(CharacterPrefab character, Point amountRange)> monsterPrefabs = new HashSet<(CharacterPrefab character, Point amountRange)>();
|
||||
private readonly List<Character> monsters = new List<Character>();
|
||||
private readonly List<Vector2> sonarPositions = new List<Vector2>();
|
||||
|
||||
private readonly List<Vector2> tempSonarPositions = new List<Vector2>();
|
||||
|
||||
private readonly float maxSonarMarkerDistance = 10000.0f;
|
||||
|
||||
private readonly Level.PositionType spawnPosType;
|
||||
private Vector2? spawnPos = null;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
@@ -114,7 +112,16 @@ namespace Barotrauma
|
||||
|
||||
if (!IsClient)
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, spawnPosType, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
float minDistBetweenMonsterMissions = 10000;
|
||||
float mindDistFromSub = Level.Loaded.Size.X * 0.3f;
|
||||
var monsterMissions = GameMain.GameSession.Missions.Select(e => e as MonsterMission).Where(m => m != null && m != this && m.spawnPos.HasValue);
|
||||
if (!Level.Loaded.TryGetInterestingPosition(useSyncedRand: true, spawnPosType, mindDistFromSub, out Vector2 spawnPos,
|
||||
filter: p => monsterMissions.None(m => Vector2.DistanceSquared(p.Position.ToVector2(), m.spawnPos.Value) < minDistBetweenMonsterMissions * minDistBetweenMonsterMissions),
|
||||
suppressWarning: true))
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(useSyncedRand: true, spawnPosType, mindDistFromSub, out spawnPos);
|
||||
}
|
||||
this.spawnPos = spawnPos;
|
||||
foreach (var (character, amountRange) in monsterPrefabs)
|
||||
{
|
||||
int amount = Rand.Range(amountRange.X, amountRange.Y + 1);
|
||||
@@ -123,9 +130,8 @@ namespace Barotrauma
|
||||
monsters.Add(Character.Create(character.Identifier, spawnPos, ToolBox.RandomSeed(8), createNetworkEvent: false));
|
||||
}
|
||||
}
|
||||
|
||||
InitializeMonsters(monsters);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeMonsters(IEnumerable<Character> monsters)
|
||||
@@ -181,7 +187,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (monsters[i].Removed || monsters[i].IsDead) { continue; }
|
||||
Vector2 diff = tempSonarPositions[i] - monsters[i].Position;
|
||||
Vector2 diff = tempSonarPositions[i] - monsters[i].WorldPosition;
|
||||
|
||||
float maxDist = maxSonarMarkerDistance;
|
||||
Submarine refSub = Character.Controlled?.Submarine ?? Submarine.MainSub;
|
||||
@@ -191,12 +197,12 @@ namespace Barotrauma
|
||||
float subDist = Vector2.Distance(refPos, tempSonarPositions[i]) / maxDist;
|
||||
|
||||
maxDist = Math.Min(subDist * subDist * maxDist, maxDist);
|
||||
maxDist = Math.Min(Vector2.Distance(refPos, monsters[i].Position), maxDist);
|
||||
maxDist = Math.Min(Vector2.Distance(refPos, monsters[i].WorldPosition), maxDist);
|
||||
}
|
||||
|
||||
if (diff.LengthSquared() > maxDist * maxDist)
|
||||
{
|
||||
tempSonarPositions[i] = monsters[i].Position + Vector2.Normalize(diff) * maxDist;
|
||||
tempSonarPositions[i] = monsters[i].WorldPosition + Vector2.Normalize(diff) * maxDist;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,8 @@ namespace Barotrauma
|
||||
var file = CharacterPrefab.FindBySpeciesName(SpeciesName)?.ContentFile;
|
||||
if (file == null)
|
||||
{
|
||||
DebugConsole.ThrowError($"Failed to find config file for species \"{SpeciesName}\"");
|
||||
DebugConsole.ThrowError($"Failed to find config file for species \"{SpeciesName}\". Content package: \"{prefab.ConfigElement?.ContentPackage?.Name ?? "unknown"}\".");
|
||||
disallowed = true;
|
||||
yield break;
|
||||
}
|
||||
else
|
||||
@@ -535,10 +536,10 @@ namespace Barotrauma
|
||||
Character createdCharacter = Character.Create(SpeciesName, pos, seed, characterInfo: null, isRemotePlayer: false, hasAi: true, createNetworkEvent: true, throwErrorIfNotFound: false);
|
||||
if (createdCharacter == null)
|
||||
{
|
||||
DebugConsole.AddWarning($"Error in MonsterEvent: failed to spawn the character \"{SpeciesName}\". Content package: \"{prefab.ConfigElement?.ContentPackage?.Name ?? "unknown"}\".");
|
||||
disallowed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
var eventManager = GameMain.GameSession.EventManager;
|
||||
if (eventManager != null)
|
||||
{
|
||||
|
||||
@@ -342,6 +342,21 @@ namespace Barotrauma
|
||||
}
|
||||
private static Implementation? loadedImplementation;
|
||||
|
||||
private static void ValidateEventID(string eventID)
|
||||
{
|
||||
#if DEBUG
|
||||
string[] parts = eventID.Split(':');
|
||||
if (parts.Length > 5)
|
||||
{
|
||||
DebugConsole.ThrowError($"Invalid GameAnalytics event id \"{eventID}\". Only 5 id parts allowed separated by ':'");
|
||||
}
|
||||
if (parts.Any(p => p.Length > 32))
|
||||
{
|
||||
DebugConsole.ThrowError($"Invalid GameAnalytics event id \"{eventID}\". Each id part separated by ':' must be 32 characters or less.");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public static void AddErrorEvent(ErrorSeverity errorSeverity, string message)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
@@ -368,12 +383,14 @@ namespace Barotrauma
|
||||
public static void AddDesignEvent(string eventID)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
ValidateEventID(eventID);
|
||||
loadedImplementation?.AddDesignEvent(eventID);
|
||||
}
|
||||
|
||||
public static void AddDesignEvent(string eventID, double value)
|
||||
{
|
||||
if (!SendUserStatistics) { return; }
|
||||
ValidateEventID(eventID);
|
||||
loadedImplementation?.AddDesignEvent(eventID, value);
|
||||
}
|
||||
|
||||
@@ -450,11 +467,11 @@ namespace Barotrauma
|
||||
SetConsent(Consent.Error);
|
||||
return;
|
||||
}
|
||||
loadedImplementation?.SetEnabledInfoLog(true);
|
||||
loadedImplementation?.SetEnabledVerboseLog(true);
|
||||
#if DEBUG
|
||||
try
|
||||
{
|
||||
loadedImplementation?.SetEnabledInfoLog(true);
|
||||
loadedImplementation?.SetEnabledVerboseLog(true);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
||||
@@ -302,6 +302,10 @@ namespace Barotrauma
|
||||
var itemsInStoreCrate = GetBuyCrateItems(storeIdentifier, create: true);
|
||||
foreach (PurchasedItem item in itemsToPurchase)
|
||||
{
|
||||
// Exchange money
|
||||
int itemValue = item.Quantity * buyValues[item.ItemPrefab];
|
||||
if (!campaign.TryPurchase(client, itemValue)) { continue; }
|
||||
|
||||
// Add to the purchased items
|
||||
var purchasedItem = itemsPurchasedFromStore.Find(pi => pi.ItemPrefab == item.ItemPrefab);
|
||||
if (purchasedItem != null)
|
||||
@@ -313,9 +317,6 @@ namespace Barotrauma
|
||||
purchasedItem = new PurchasedItem(item.ItemPrefab, item.Quantity, client);
|
||||
itemsPurchasedFromStore.Add(purchasedItem);
|
||||
}
|
||||
// Exchange money
|
||||
int itemValue = item.Quantity * buyValues[item.ItemPrefab];
|
||||
campaign.TryPurchase(client, itemValue);
|
||||
if (GameMain.IsSingleplayer)
|
||||
{
|
||||
GameAnalyticsManager.AddMoneySpentEvent(itemValue, GameAnalyticsManager.MoneySink.Store, item.ItemPrefab.Identifier.Value);
|
||||
|
||||
@@ -96,20 +96,17 @@ namespace Barotrauma
|
||||
private object GetTypeOrDefault(Identifier identifier, Type type, object defaultValue)
|
||||
{
|
||||
object? value = GetValue(identifier);
|
||||
|
||||
if (value == null)
|
||||
if (value != null)
|
||||
{
|
||||
SetValue(identifier, defaultValue);
|
||||
if (value.GetType() == type)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Attempted to get value \"{identifier}\" as a {type} but the value is {value.GetType()}.");
|
||||
}
|
||||
}
|
||||
else if (value.GetType() == type)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError($"Attempted to get value \"{identifier}\" as a {type} but the value is {value.GetType()}.");
|
||||
}
|
||||
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ namespace Barotrauma
|
||||
public double TotalPlayTime;
|
||||
public int TotalPassedLevels;
|
||||
|
||||
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Repair, Upgrade, PurchaseSub, MedicalClinic, Cargo }
|
||||
public enum InteractionType { None, Talk, Examine, Map, Crew, Store, Upgrade, PurchaseSub, MedicalClinic, Cargo }
|
||||
|
||||
public static bool BlocksInteraction(InteractionType interactionType)
|
||||
{
|
||||
@@ -85,7 +85,9 @@ namespace Barotrauma
|
||||
|
||||
public bool CheatsEnabled;
|
||||
|
||||
public const int HullRepairCost = 500, ItemRepairCost = 500, ShuttleReplaceCost = 1000;
|
||||
public const float HullRepairCostPerDamage = 0.5f, ItemRepairCostPerRepairDuration = 1.0f;
|
||||
public const int ShuttleReplaceCost = 1000;
|
||||
public const int MaxHullRepairCost = 2000, MaxItemRepairCost = 2000;
|
||||
|
||||
protected bool wasDocked;
|
||||
|
||||
@@ -152,8 +154,9 @@ namespace Barotrauma
|
||||
{
|
||||
if (!(e.ChangedData.BalanceChanged is Some<int> { Value: var changed })) { return; }
|
||||
|
||||
bool isGain = changed > 0;
|
||||
if (changed != 0) { return; }
|
||||
|
||||
bool isGain = changed > 0;
|
||||
Color clr = isGain ? GUIStyle.Yellow : GUIStyle.Red;
|
||||
|
||||
switch (e.Owner)
|
||||
@@ -260,6 +263,39 @@ namespace Barotrauma
|
||||
wasDocked = Level.Loaded.StartOutpost != null && connectedSubs.Contains(Level.Loaded.StartOutpost);
|
||||
}
|
||||
|
||||
public int GetHullRepairCost()
|
||||
{
|
||||
float totalDamage = 0;
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine == null || wall.Submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (wall.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(wall.Submarine))
|
||||
{
|
||||
for (int i = 0; i < wall.SectionCount; i++)
|
||||
{
|
||||
totalDamage += wall.SectionDamage(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
return (int)Math.Min(totalDamage * HullRepairCostPerDamage, MaxHullRepairCost);
|
||||
}
|
||||
|
||||
public int GetItemRepairCost()
|
||||
{
|
||||
float totalRepairDuration = 0.0f;
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.Info.Type != SubmarineType.Player) { continue; }
|
||||
if (item.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(item.Submarine))
|
||||
{
|
||||
var repairable = item.GetComponent<Repairable>();
|
||||
if (repairable == null) { continue; }
|
||||
totalRepairDuration += repairable.FixDurationHighSkill * (1.0f - item.Condition / item.MaxCondition);
|
||||
}
|
||||
}
|
||||
return (int)Math.Min(totalRepairDuration * ItemRepairCostPerRepairDuration, MaxItemRepairCost);
|
||||
}
|
||||
|
||||
public void InitCampaignData()
|
||||
{
|
||||
Factions = new List<Faction>();
|
||||
|
||||
@@ -864,7 +864,7 @@ namespace Barotrauma
|
||||
double roundDuration = Timing.TotalTime - RoundStartTime;
|
||||
GameAnalyticsManager.AddProgressionEvent(
|
||||
success ? GameAnalyticsManager.ProgressionStatus.Complete : GameAnalyticsManager.ProgressionStatus.Fail,
|
||||
GameMode?.Name?.Value ?? "none",
|
||||
GameMode?.Preset.Identifier.Value ?? "none",
|
||||
roundDuration);
|
||||
string eventId = "EndRound:" + (GameMode?.Preset?.Identifier.Value ?? "none") + ":";
|
||||
LogEndRoundStats(eventId);
|
||||
|
||||
@@ -60,16 +60,6 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
public const bool UpgradeAlsoConnectedSubs = true;
|
||||
|
||||
/// <summary>
|
||||
/// Prevents the player from upgrading the submarine when we are switching to a new one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In singleplayer we check if CampaignMode.PendingSubmarineSwitch is not null indicating we are switching submarines
|
||||
/// but in multiplayer that value is not synced so we use this variable instead by setting it to false in <see cref="UpgradeManager.ClientRead"/>
|
||||
/// and then set it back to true when the round ends in <see cref="MultiPlayerCampaign.End"/>
|
||||
/// </remarks>
|
||||
public bool CanUpgrade = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is used by the client in multiplayer, acts like a secondary PendingUpgrades list
|
||||
/// but is not affected by server messages.
|
||||
@@ -713,9 +703,9 @@ namespace Barotrauma
|
||||
|
||||
public bool CanUpgradeSub()
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return CanUpgrade; }
|
||||
|
||||
return Campaign.PendingSubmarineSwitch == null;
|
||||
return
|
||||
Campaign.PendingSubmarineSwitch == null ||
|
||||
Campaign.PendingSubmarineSwitch.Name == Submarine.MainSub.Info.Name;
|
||||
}
|
||||
|
||||
public void Save(XElement? parent)
|
||||
|
||||
@@ -12,7 +12,7 @@ namespace Barotrauma
|
||||
Ragdoll, Health, Grab,
|
||||
SelectNextCharacter,
|
||||
SelectPreviousCharacter,
|
||||
Voice,
|
||||
Voice, RadioVoice, LocalVoice,
|
||||
Deselect,
|
||||
Shoot,
|
||||
Command,
|
||||
|
||||
@@ -691,12 +691,30 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
item.Drop(character);
|
||||
item.SetTransform(ConvertUnits.ToSimUnits(GetAttachPosition(character)), 0.0f, findNewHull: false);
|
||||
//the light source won't get properly updated if lighting is disabled (even though the light sprite is still drawn when lighting is disabled)
|
||||
//so let's ensure the light source is up-to-date
|
||||
RefreshLightSources(item);
|
||||
}
|
||||
AttachToWall();
|
||||
}
|
||||
return true;
|
||||
|
||||
static void RefreshLightSources(Item item)
|
||||
{
|
||||
item.body?.UpdateDrawPosition();
|
||||
foreach (var light in item.GetComponents<LightComponent>())
|
||||
{
|
||||
light.SetLightSourceTransform();
|
||||
}
|
||||
item.GetComponent<ItemContainer>()?.SetContainedItemPositions();
|
||||
foreach (var containedItem in item.ContainedItems)
|
||||
{
|
||||
RefreshLightSources(containedItem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
return true;
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Barotrauma.Items.Components
|
||||
{
|
||||
@@ -292,7 +290,6 @@ namespace Barotrauma.Items.Components
|
||||
item.body.PhysEnabled = false;
|
||||
}
|
||||
|
||||
|
||||
private bool OnCollision(Fixture f1, Fixture f2, Contact contact)
|
||||
{
|
||||
if (User == null || User.Removed)
|
||||
@@ -419,7 +416,18 @@ namespace Barotrauma.Items.Components
|
||||
else if (target.UserData is Item targetItem && targetItem.Prefab.DamagedByMeleeWeapons && targetItem.Condition > 0)
|
||||
{
|
||||
if (targetItem.Removed) { return; }
|
||||
Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
var attackResult = Attack.DoDamage(User, targetItem, item.WorldPosition, 1.0f);
|
||||
#if CLIENT
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
Character.Controlled?.UpdateHUDProgressBar(targetItem,
|
||||
targetItem.WorldPosition,
|
||||
targetItem.Condition / targetItem.MaxCondition,
|
||||
emptyColor: GUIStyle.HealthBarColorLow,
|
||||
fullColor: GUIStyle.HealthBarColorHigh,
|
||||
textTag: targetItem.Name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else if (target.UserData is Holdable holdable && holdable.CanPush)
|
||||
{
|
||||
|
||||
@@ -169,7 +169,7 @@ namespace Barotrauma.Items.Components
|
||||
//attempting to pick does not select the item, so if it is selected at this point, another ItemComponent
|
||||
//must have been selected and we should not keep deattaching (happens when for example interacting with
|
||||
//an electrical component while holding both a screwdriver and a wrench).
|
||||
if (picker.SelectedConstruction == item ||
|
||||
if (picker.IsAnySelectedItem(item)||
|
||||
picker.IsKeyDown(InputType.Aim) ||
|
||||
!picker.CanInteractWith(item) ||
|
||||
item.Removed || item.ParentInventory != null)
|
||||
@@ -187,7 +187,7 @@ namespace Barotrauma.Items.Components
|
||||
!string.IsNullOrWhiteSpace(PickingMsg) ? PickingMsg : this is Door ? "progressbar.opening" : "progressbar.deattaching");
|
||||
#endif
|
||||
|
||||
picker.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
|
||||
picker.AnimController.UpdateUseItem(!picker.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((pickTimer / 10.0f) % 0.1f));
|
||||
pickTimer += CoroutineManager.DeltaTime;
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
@@ -208,7 +208,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (picker != null)
|
||||
{
|
||||
picker.AnimController.Anim = AnimController.Animation.None;
|
||||
picker.AnimController.StopUsingItem();
|
||||
picker.PickingItem = null;
|
||||
}
|
||||
if (pickingCoroutine != null)
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Barotrauma.Items.Components
|
||||
};
|
||||
|
||||
private readonly HashSet<Identifier> fixableEntities;
|
||||
private readonly HashSet<Identifier> nonFixableEntities;
|
||||
private Vector2 pickedPosition;
|
||||
private float activeTimer;
|
||||
|
||||
@@ -135,6 +136,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
fixableEntities = new HashSet<Identifier>();
|
||||
nonFixableEntities = new HashSet<Identifier>();
|
||||
foreach (var subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -147,7 +149,16 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
fixableEntities.Add(subElement.GetAttributeIdentifier("identifier", ""));
|
||||
foreach (Identifier id in subElement.GetAttributeIdentifierArray("identifier", Array.Empty<Identifier>()))
|
||||
{
|
||||
fixableEntities.Add(id);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "nonfixable":
|
||||
foreach (Identifier id in subElement.GetAttributeIdentifierArray("identifier", Array.Empty<Identifier>()))
|
||||
{
|
||||
nonFixableEntities.Add(id);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -523,6 +534,7 @@ namespace Barotrauma.Items.Components
|
||||
if (sectionIndex < 0) { return false; }
|
||||
|
||||
if (!fixableEntities.Contains("structure") && !fixableEntities.Contains(targetStructure.Prefab.Identifier)) { return true; }
|
||||
if (nonFixableEntities.Contains(targetStructure.Prefab.Identifier) || nonFixableEntities.Any(t => targetStructure.Tags.Contains(t))) { return false; }
|
||||
|
||||
ApplyStatusEffectsOnTarget(user, deltaTime, ActionType.OnUse, structure: targetStructure);
|
||||
FixStructureProjSpecific(user, deltaTime, targetStructure, sectionIndex);
|
||||
|
||||
@@ -49,6 +49,8 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
public readonly NamedEvent<ItemContainer> OnContainedItemsChanged = new NamedEvent<ItemContainer>();
|
||||
|
||||
private bool alwaysContainedItemsSpawned;
|
||||
|
||||
public ItemInventory Inventory;
|
||||
@@ -347,6 +349,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//no need to Update() if this item has no statuseffects and no physics body
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
public override void Move(Vector2 amount, bool ignoreContacts = false)
|
||||
@@ -360,6 +363,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
//deactivate if the inventory is empty
|
||||
IsActive = activeContainedItems.Count > 0 || Inventory.AllItems.Any(it => it.body != null);
|
||||
OnContainedItemsChanged.Invoke(this);
|
||||
}
|
||||
|
||||
public bool CanBeContained(Item item)
|
||||
@@ -496,7 +500,7 @@ namespace Barotrauma.Items.Components
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (AutoInteractWithContained && character.SelectedConstruction == null)
|
||||
if (AutoInteractWithContained && character.SelectedItem == null)
|
||||
{
|
||||
foreach (Item contained in Inventory.AllItems)
|
||||
{
|
||||
@@ -510,7 +514,15 @@ namespace Barotrauma.Items.Components
|
||||
var abilityItem = new AbilityItemContainer(item);
|
||||
character.CheckTalents(AbilityEffectType.OnOpenItemContainer, abilityItem);
|
||||
|
||||
return base.Select(character);
|
||||
if (item.ParentInventory?.Owner == character)
|
||||
{
|
||||
//can't select ItemContainers in the character's inventory (the inventory is drawn by hovering the cursor over the inventory slot, not as a GUIFrame)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return base.Select(character);
|
||||
}
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
|
||||
@@ -19,8 +19,7 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (character == null || character.LockHands || character.Removed || !(character.AnimController is HumanoidAnimController)) return false;
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.Climbing;
|
||||
character.AnimController.StartClimbing();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ namespace Barotrauma.Items.Components
|
||||
private readonly List<LimbPos> limbPositions = new List<LimbPos>();
|
||||
|
||||
private Direction dir;
|
||||
public Direction Direction => dir;
|
||||
|
||||
//the position where the user walks to when using the controller
|
||||
//(relative to the position of the item)
|
||||
@@ -128,6 +129,13 @@ namespace Barotrauma.Items.Components
|
||||
set;
|
||||
}
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No, description: "If true, other items can be used simultaneously.")]
|
||||
public bool IsSecondaryItem
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Controller(Item item, ContentXElement element)
|
||||
: base(item, element)
|
||||
{
|
||||
@@ -150,7 +158,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (user == null
|
||||
|| user.Removed
|
||||
|| user.SelectedConstruction != item
|
||||
|| !user.IsAnySelectedItem(item)
|
||||
|| item.ParentInventory != null
|
||||
|| !user.CanInteractWith(item)
|
||||
|| (UsableIn == UseEnvironment.Water && !user.AnimController.InWater)
|
||||
@@ -165,7 +173,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
user.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
user.AnimController.StartUsingItem();
|
||||
|
||||
if (userPos != Vector2.Zero)
|
||||
{
|
||||
@@ -186,32 +194,34 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
diff.Y = 0.0f;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
|
||||
// Secondary items (like ladders or chairs) will control the character position over primary items
|
||||
// Only control the character position if the character doesn't have another secondary item already controlling it
|
||||
if (!user.HasSelectedAnotherSecondaryItem(Item))
|
||||
{
|
||||
if (Math.Abs(diff.X) > 20.0f)
|
||||
diff.Y = 0.0f;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient && user != Character.Controlled)
|
||||
{
|
||||
//wait for the character to walk to the correct position
|
||||
return;
|
||||
if (Math.Abs(diff.X) > 20.0f)
|
||||
{
|
||||
//wait for the character to walk to the correct position
|
||||
return;
|
||||
}
|
||||
else if (Math.Abs(diff.X) > 0.1f)
|
||||
{
|
||||
//aim to keep the collider at the correct position once close enough
|
||||
user.AnimController.Collider.LinearVelocity = new Vector2(
|
||||
diff.X * 0.1f,
|
||||
user.AnimController.Collider.LinearVelocity.Y);
|
||||
}
|
||||
}
|
||||
else if (Math.Abs(diff.X) > 0.1f)
|
||||
{
|
||||
//aim to keep the collider at the correct position once close enough
|
||||
user.AnimController.Collider.LinearVelocity = new Vector2(
|
||||
diff.X * 0.1f,
|
||||
user.AnimController.Collider.LinearVelocity.Y);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Math.Abs(diff.X) > 10.0f)
|
||||
else if (Math.Abs(diff.X) > 10.0f)
|
||||
{
|
||||
user.AnimController.TargetMovement = Vector2.Normalize(diff);
|
||||
user.AnimController.TargetDir = diff.X > 0.0f ? Direction.Right : Direction.Left;
|
||||
return;
|
||||
}
|
||||
user.AnimController.TargetMovement = Vector2.Zero;
|
||||
}
|
||||
user.AnimController.TargetMovement = Vector2.Zero;
|
||||
UserInCorrectPosition = true;
|
||||
}
|
||||
}
|
||||
@@ -220,9 +230,16 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (limbPositions.Count == 0) { return; }
|
||||
|
||||
user.AnimController.Anim = AnimController.Animation.UsingConstruction;
|
||||
user.AnimController.StartUsingItem();
|
||||
|
||||
user.AnimController.ResetPullJoints();
|
||||
if (user.SelectedItem != null)
|
||||
{
|
||||
user.AnimController.ResetPullJoints(l => l.IsLowerBody);
|
||||
}
|
||||
else
|
||||
{
|
||||
user.AnimController.ResetPullJoints();
|
||||
}
|
||||
|
||||
if (dir != 0) { user.AnimController.TargetDir = dir; }
|
||||
|
||||
@@ -230,7 +247,10 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Limb limb = user.AnimController.GetLimb(lb.LimbType);
|
||||
if (limb == null || !limb.body.Enabled) { continue; }
|
||||
|
||||
// Don't move lower body limbs if there's another selected secondary item that should control them
|
||||
if (limb.IsLowerBody && user.HasSelectedAnotherSecondaryItem(Item)) { continue; }
|
||||
// Don't move hands if there's a selected primary item that should control them
|
||||
if (!limb.IsLowerBody && Item == user.SelectedSecondaryItem && user.SelectedItem != null) { continue; }
|
||||
if (lb.AllowUsingLimb)
|
||||
{
|
||||
switch (lb.LimbType)
|
||||
@@ -247,12 +267,9 @@ namespace Barotrauma.Items.Components
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
limb.Disabled = true;
|
||||
|
||||
Vector2 worldPosition = new Vector2(item.WorldRect.X, item.WorldRect.Y) + lb.Position * item.Scale;
|
||||
Vector2 diff = worldPosition - limb.WorldPosition;
|
||||
|
||||
limb.PullJointEnabled = true;
|
||||
limb.PullJointWorldAnchorB = limb.SimPosition + ConvertUnits.ToSimUnits(diff);
|
||||
}
|
||||
@@ -266,9 +283,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (user == null || user.Removed ||
|
||||
user.SelectedConstruction != item || !user.CanInteractWith(item))
|
||||
if (user == null || user.Removed || !user.IsAnySelectedItem(item) || !user.CanInteractWith(item))
|
||||
{
|
||||
user = null;
|
||||
return false;
|
||||
@@ -290,46 +305,44 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
lastUsed = Timing.TotalTime;
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
|
||||
ApplyStatusEffects(ActionType.OnUse, 1.0f, activator);
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool SecondaryUse(float deltaTime, Character character = null)
|
||||
{
|
||||
if (this.user != character)
|
||||
if (user != character)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (this.user == null || character.Removed ||
|
||||
this.user.SelectedConstruction != item || !character.CanInteractWith(item))
|
||||
if (user == null || character.Removed || !user.IsAnySelectedItem(item) || !character.CanInteractWith(item))
|
||||
{
|
||||
user = null;
|
||||
return false;
|
||||
}
|
||||
if (character == null)
|
||||
{
|
||||
this.user = null;
|
||||
return false;
|
||||
}
|
||||
if (character == null) return false;
|
||||
|
||||
focusTarget = GetFocusTarget();
|
||||
|
||||
if (focusTarget == null)
|
||||
{
|
||||
Vector2 centerPos = new Vector2(item.WorldRect.Center.X, item.WorldRect.Center.Y);
|
||||
|
||||
Vector2 offset = character.CursorWorldPosition - centerPos;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(offset));
|
||||
return false;
|
||||
}
|
||||
|
||||
character.ViewTarget = focusTarget;
|
||||
|
||||
#if CLIENT
|
||||
if (character == Character.Controlled && cam != null)
|
||||
{
|
||||
Lights.LightManager.ViewTarget = focusTarget;
|
||||
cam.TargetPos = focusTarget.WorldPosition;
|
||||
|
||||
cam.OffsetAmount = MathHelper.Lerp(cam.OffsetAmount, (focusTarget as Item).Prefab.OffsetOnSelected * focusTarget.OffsetOnSelectedMultiplier, deltaTime * 10.0f);
|
||||
HideHUDs(true);
|
||||
}
|
||||
@@ -338,16 +351,12 @@ namespace Barotrauma.Items.Components
|
||||
if (!character.IsRemotePlayer || character.ViewTarget == focusTarget)
|
||||
{
|
||||
Vector2 centerPos = new Vector2(focusTarget.WorldRect.Center.X, focusTarget.WorldRect.Center.Y);
|
||||
|
||||
Turret turret = focusTarget.GetComponent<Turret>();
|
||||
if (turret != null)
|
||||
if (focusTarget.GetComponent<Turret>() is { } turret)
|
||||
{
|
||||
centerPos = new Vector2(focusTarget.WorldRect.X + turret.TransformedBarrelPos.X, focusTarget.WorldRect.Y - turret.TransformedBarrelPos.Y);
|
||||
}
|
||||
|
||||
Vector2 offset = character.CursorWorldPosition - centerPos;
|
||||
offset.Y = -offset.Y;
|
||||
|
||||
targetRotation = MathUtils.WrapAngleTwoPi(MathUtils.VectorToAngle(offset));
|
||||
}
|
||||
return true;
|
||||
@@ -425,9 +434,10 @@ namespace Barotrauma.Items.Components
|
||||
humanoidAnim.LockFlippingUntil = (float)Timing.TotalTime + 0.5f;
|
||||
}
|
||||
|
||||
if (character.SelectedConstruction == this.item) { character.SelectedConstruction = null; }
|
||||
if (character.SelectedItem == item) { character.SelectedItem = null; }
|
||||
if (character.SelectedSecondaryItem == item) { character.SelectedSecondaryItem = null; }
|
||||
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.AnimController.StopUsingItem();
|
||||
if (character == Character.Controlled)
|
||||
{
|
||||
HideHUDs(false);
|
||||
|
||||
@@ -3,6 +3,7 @@ using Barotrauma.Extensions;
|
||||
using Barotrauma.Networking;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
|
||||
@@ -62,11 +63,18 @@ namespace Barotrauma.Items.Components
|
||||
inputContainer = containers[0];
|
||||
outputContainer = containers[1];
|
||||
|
||||
#if CLIENT
|
||||
Identifier eventIdentifier = new Identifier(nameof(Deconstructor));
|
||||
inputContainer.OnContainedItemsChanged.RegisterOverwriteExisting(eventIdentifier, OnItemSlotsChanged);
|
||||
#endif
|
||||
|
||||
OnItemLoadedProjSpecific();
|
||||
}
|
||||
|
||||
partial void OnItemLoadedProjSpecific();
|
||||
|
||||
partial void OnItemSlotsChanged(ItemContainer container);
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
MoveInputQueue();
|
||||
@@ -281,6 +289,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
Entity.Spawner.AddItemToSpawnQueue(itemPrefab, outputContainer.Inventory, condition, onSpawned: (Item spawnedItem) =>
|
||||
{
|
||||
spawnedItem.SpawnedInCurrentOutpost = item.SpawnedInCurrentOutpost;
|
||||
spawnedItem.StolenDuringRound = targetItem.StolenDuringRound;
|
||||
spawnedItem.AllowStealing = targetItem.AllowStealing;
|
||||
for (int i = 0; i < outputContainer.Capacity; i++)
|
||||
|
||||
@@ -556,8 +556,20 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
const int MaxCraftingSkill = 100;
|
||||
|
||||
//having a higher-than-100 skill (e.g. due to talents) gives +1 quality
|
||||
quality += fabricatedItem.RequiredSkills.All(s => user.GetSkillLevel(s.Identifier) >= MaxCraftingSkill) ? 1 : 0;
|
||||
quality += FabricationDegreeOfSuccess(user, fabricatedItem.RequiredSkills) >= 0.5f ? 1 : 0;
|
||||
foreach (var skill in fabricatedItem.RequiredSkills)
|
||||
{
|
||||
//+1 quality if the character's skill level is >20% from the min requirement towards max skill
|
||||
//e.g. if the skill requirement is 10 -> 28
|
||||
//40 -> 52
|
||||
//90 -> 92
|
||||
float skillRequirement = MathHelper.Lerp(skill.Level, MaxCraftingSkill, 0.2f);
|
||||
if (user.GetSkillLevel(skill.Identifier) > skillRequirement)
|
||||
{
|
||||
quality += 1;
|
||||
}
|
||||
}
|
||||
return quality;
|
||||
}
|
||||
|
||||
|
||||
@@ -226,7 +226,7 @@ namespace Barotrauma.Items.Components
|
||||
// (= bots turn autotemp back on when leaving the reactor)
|
||||
if (LastAIUser != null)
|
||||
{
|
||||
if (LastAIUser.SelectedConstruction != item && LastAIUser.CanInteractWith(item))
|
||||
if (LastAIUser.SelectedItem != item && LastAIUser.CanInteractWith(item))
|
||||
{
|
||||
AutoTemp = true;
|
||||
if (GameMain.NetworkMember?.IsServer ?? false) { unsentChanges = true; }
|
||||
@@ -505,7 +505,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
fireTimer += MathHelper.Lerp(deltaTime * 2.0f, deltaTime, item.Condition / item.MaxCondition);
|
||||
#if SERVER
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedConstruction == item)
|
||||
if (fireTimer > Math.Min(5.0f, FireDelay / 2) && blameOnBroken?.Character?.SelectedItem == item)
|
||||
{
|
||||
GameMain.Server.KarmaManager.OnReactorOverHeating(item, blameOnBroken.Character, deltaTime);
|
||||
}
|
||||
@@ -705,7 +705,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (lastUser != null && lastUser != character && lastUser != LastAIUser)
|
||||
{
|
||||
if (lastUser.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
if (lastUser.SelectedItem == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogReactorTaken").Value, null, 0.0f, "reactortaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
|
||||
@@ -301,7 +301,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
float userSkill = 0.0f;
|
||||
if (user != null && controlledSub != null &&
|
||||
(user.SelectedConstruction == item || item.linkedTo.Contains(user.SelectedConstruction)))
|
||||
(user.SelectedItem == item || item.linkedTo.Contains(user.SelectedItem)))
|
||||
{
|
||||
userSkill = user.GetSkillLevel("helm") / 100.0f;
|
||||
}
|
||||
@@ -333,7 +333,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
showIceSpireWarning = false;
|
||||
if (user != null && user.Info != null &&
|
||||
user.SelectedConstruction == item &&
|
||||
user.SelectedItem == item &&
|
||||
controlledSub != null && controlledSub.Velocity.LengthSquared() > 0.01f)
|
||||
{
|
||||
IncreaseSkillLevel(user, deltaTime);
|
||||
@@ -389,7 +389,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
// if our tactical AI pilot has left, revert back to maintaining position
|
||||
if (navigateTactically && (user == null || user.SelectedConstruction != item))
|
||||
if (navigateTactically && (user == null || user.SelectedItem != item))
|
||||
{
|
||||
navigateTactically = false;
|
||||
AIRamTimer = 0f;
|
||||
@@ -722,7 +722,7 @@ namespace Barotrauma.Items.Components
|
||||
character.AIController.SteeringManager.Reset();
|
||||
if (objective.Override)
|
||||
{
|
||||
if (user != character && user != null && user.SelectedConstruction == item && character.IsOnPlayerTeam)
|
||||
if (user != character && user != null && user.SelectedItem == item && character.IsOnPlayerTeam)
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogSteeringTaken").Value, null, 0.0f, "steeringtaken".ToIdentifier(), 10.0f);
|
||||
}
|
||||
|
||||
@@ -117,9 +117,6 @@ namespace Barotrauma.Items.Components
|
||||
[Serialize(false, IsPropertySaveable.Yes, description: "If true, the recharge speed (and power consumption) of the device goes up exponentially as the recharge rate is increased.")]
|
||||
public bool ExponentialRechargeSpeed { get; set; }
|
||||
|
||||
[Editable(minValue: 0.0f, maxValue: 10.0f, decimals: 2), Serialize(0.5f, IsPropertySaveable.Yes)]
|
||||
public float RechargeAdjustSpeed { get; set; }
|
||||
|
||||
private float efficiency;
|
||||
[Editable(minValue: 0.0f, maxValue: 1.0f, decimals: 2), Serialize(0.95f, IsPropertySaveable.Yes, description: "The amount of power you can get out of a item relative to the amount of power that's put into it.")]
|
||||
public float Efficiency
|
||||
|
||||
@@ -851,7 +851,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
else if (target.Body.UserData is Limb limb)
|
||||
{
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User))
|
||||
if (!FriendlyFire && User != null && limb.character.IsFriendly(User) && HumanAIController.IsOnFriendlyTeam(limb.character, User))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -872,7 +872,18 @@ namespace Barotrauma.Items.Components
|
||||
if (targetItem.Removed) { return false; }
|
||||
if (Attack != null && targetItem.Prefab.DamagedByProjectiles && targetItem.Condition > 0)
|
||||
{
|
||||
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
|
||||
attackResult = Attack.DoDamage(User ?? Attacker, targetItem, item.WorldPosition, 1.0f);
|
||||
#if CLIENT
|
||||
if (attackResult.Damage > 0.0f)
|
||||
{
|
||||
Character.Controlled?.UpdateHUDProgressBar(targetItem,
|
||||
targetItem.WorldPosition,
|
||||
targetItem.Condition / targetItem.MaxCondition,
|
||||
emptyColor: GUIStyle.HealthBarColorLow,
|
||||
fullColor: GUIStyle.HealthBarColorHigh,
|
||||
textTag: targetItem.Name);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
else if (target.Body.UserData is IDamageable damageable)
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private int qualityLevel;
|
||||
|
||||
[Editable, Serialize(0, IsPropertySaveable.Yes)]
|
||||
[Editable(MinValueInt = 0, MaxValueInt = MaxQuality), Serialize(0, IsPropertySaveable.Yes)]
|
||||
public int QualityLevel
|
||||
{
|
||||
get { return qualityLevel; }
|
||||
|
||||
@@ -343,7 +343,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
CurrentFixer.CheckTalents(AbilityEffectType.OnStopTinkering);
|
||||
}
|
||||
CurrentFixer.AnimController.Anim = AnimController.Animation.None;
|
||||
CurrentFixer.AnimController.StopUsingItem();
|
||||
CurrentFixer = null;
|
||||
currentRepairItem = null;
|
||||
currentFixerAction = FixActions.None;
|
||||
@@ -430,7 +430,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
|
||||
if (CurrentFixer != null && (CurrentFixer.SelectedConstruction != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
|
||||
if (CurrentFixer != null && (CurrentFixer.SelectedItem != item || !CurrentFixer.CanInteractWith(item) || CurrentFixer.IsDead))
|
||||
{
|
||||
StopRepairing(CurrentFixer);
|
||||
return;
|
||||
@@ -502,7 +502,7 @@ namespace Barotrauma.Items.Components
|
||||
SteamAchievementManager.OnItemRepaired(item, CurrentFixer);
|
||||
CurrentFixer.CheckTalents(AbilityEffectType.OnRepairComplete);
|
||||
}
|
||||
if (CurrentFixer?.SelectedConstruction == item) { CurrentFixer.SelectedConstruction = null; }
|
||||
if (CurrentFixer?.SelectedItem == item) { CurrentFixer.SelectedItem = null; }
|
||||
deteriorationTimer = Rand.Range(MinDeteriorationDelay, MaxDeteriorationDelay);
|
||||
wasBroken = false;
|
||||
StopRepairing(CurrentFixer);
|
||||
|
||||
+12
-3
@@ -179,7 +179,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
UpdateProjSpecific(deltaTime);
|
||||
|
||||
if (user == null || user.SelectedConstruction != item)
|
||||
if (user == null || user.SelectedItem != item)
|
||||
{
|
||||
#if SERVER
|
||||
if (user != null) { item.CreateServerEvent(this); }
|
||||
@@ -196,7 +196,7 @@ namespace Barotrauma.Items.Components
|
||||
return;
|
||||
}
|
||||
|
||||
user.AnimController.UpdateUseItem(true, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
user.AnimController.UpdateUseItem(!user.IsClimbing, item.WorldPosition + new Vector2(0.0f, 100.0f) * (((float)Timing.TotalTime / 10.0f) % 0.1f));
|
||||
}
|
||||
|
||||
public override void UpdateBroken(float deltaTime, Camera cam)
|
||||
@@ -206,7 +206,7 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
partial void UpdateProjSpecific(float deltaTime);
|
||||
|
||||
public override bool Select(Character picker)
|
||||
public bool CanRewire()
|
||||
{
|
||||
//attaching wires to items with a body is not allowed
|
||||
//(signal items remove their bodies when attached to a wall)
|
||||
@@ -214,6 +214,15 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public override bool Select(Character picker)
|
||||
{
|
||||
if (!CanRewire())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
user = picker;
|
||||
#if SERVER
|
||||
|
||||
@@ -106,11 +106,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
case "set_text":
|
||||
case "signal_in":
|
||||
if (string.IsNullOrEmpty(signal.value)) { return; }
|
||||
if (signal.value.Length > MaxMessageLength)
|
||||
{
|
||||
signal.value = signal.value.Substring(0, MaxMessageLength);
|
||||
}
|
||||
|
||||
string inputSignal = signal.value.Replace("\\n", "\n");
|
||||
ShowOnDisplay(inputSignal, addToHistory: true, TextColor);
|
||||
break;
|
||||
|
||||
@@ -309,7 +309,7 @@ namespace Barotrauma.Items.Components
|
||||
if (nodes.Count == 0) { return; }
|
||||
|
||||
Character user = item.ParentInventory?.Owner as Character;
|
||||
editNodeDelay = (user?.SelectedConstruction == null) ? editNodeDelay - deltaTime : 0.5f;
|
||||
editNodeDelay = (user?.SelectedItem == null) ? editNodeDelay - deltaTime : 0.5f;
|
||||
|
||||
Submarine sub = item.Submarine;
|
||||
if (connections[0] != null && connections[0].Item.Submarine != null) { sub = connections[0].Item.Submarine; }
|
||||
@@ -369,7 +369,7 @@ namespace Barotrauma.Items.Components
|
||||
user.AnimController.Collider.ApplyForce(forceDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity * 0.5f);
|
||||
if (diff.LengthSquared() > 50.0f * 50.0f)
|
||||
{
|
||||
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
|
||||
user.AnimController.UpdateUseItem(!user.IsClimbing, user.WorldPosition + pullBackDir * Math.Min(150.0f, diff.Length()));
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
|
||||
@@ -428,7 +428,7 @@ namespace Barotrauma.Items.Components
|
||||
public override bool Use(float deltaTime, Character character = null)
|
||||
{
|
||||
if (character == null || character != Character.Controlled) { return false; }
|
||||
if (character.SelectedConstruction != null) { return false; }
|
||||
if (character.HasSelectedAnyItem) { return false; }
|
||||
#if CLIENT
|
||||
if (Screen.Selected == GameMain.SubEditorScreen && !PlayerInput.PrimaryMouseButtonClicked())
|
||||
{
|
||||
|
||||
@@ -525,7 +525,7 @@ namespace Barotrauma.Items.Components
|
||||
UpdateLightComponents();
|
||||
}
|
||||
|
||||
private void UpdateLightComponents()
|
||||
public void UpdateLightComponents()
|
||||
{
|
||||
if (lightComponents != null)
|
||||
{
|
||||
|
||||
@@ -303,6 +303,10 @@ namespace Barotrauma
|
||||
{
|
||||
light.SetLightSourceTransform();
|
||||
}
|
||||
foreach (var turret in GetComponents<Turret>())
|
||||
{
|
||||
turret.UpdateLightComponents();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
@@ -458,7 +462,7 @@ namespace Barotrauma
|
||||
|
||||
[Serialize(0.0f, IsPropertySaveable.No)]
|
||||
/// <summary>
|
||||
/// Can be used by status effects or conditionals to modify the sound range
|
||||
/// Can be used by status effects or conditionals to modify the sight range
|
||||
/// </summary>
|
||||
public new float SightRange
|
||||
{
|
||||
@@ -806,6 +810,10 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsLadder { get; }
|
||||
|
||||
public bool IsSecondaryItem { get; }
|
||||
|
||||
public Item(ItemPrefab itemPrefab, Vector2 position, Submarine submarine, ushort id = Entity.NullEntityID, bool callOnItemLoaded = true)
|
||||
: this(new Rectangle(
|
||||
(int)(position.X - itemPrefab.Sprite.size.X / 2 * itemPrefab.Scale),
|
||||
@@ -1013,6 +1021,9 @@ namespace Barotrauma
|
||||
|
||||
qualityComponent = GetComponent<Quality>();
|
||||
|
||||
IsLadder = GetComponent<Ladder>() != null;
|
||||
IsSecondaryItem = IsLadder || GetComponent<Controller>() is { IsSecondaryItem: true };
|
||||
|
||||
InitProjSpecific();
|
||||
|
||||
if (callOnItemLoaded)
|
||||
@@ -2491,16 +2502,30 @@ namespace Barotrauma
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
if (user.SelectedConstruction == this)
|
||||
if (user.SelectedItem == this)
|
||||
{
|
||||
if (user.IsKeyHit(InputType.Select) || forceSelectKey)
|
||||
{
|
||||
user.SelectedConstruction = null;
|
||||
user.SelectedItem = null;
|
||||
}
|
||||
}
|
||||
else if (user.SelectedSecondaryItem == this)
|
||||
{
|
||||
if (user.IsKeyHit(InputType.Select) || forceSelectKey)
|
||||
{
|
||||
user.SelectedSecondaryItem = null;
|
||||
}
|
||||
}
|
||||
else if (selected)
|
||||
{
|
||||
user.SelectedConstruction = this;
|
||||
if (IsSecondaryItem)
|
||||
{
|
||||
user.SelectedSecondaryItem = this;
|
||||
}
|
||||
else
|
||||
{
|
||||
user.SelectedItem = this;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3245,7 +3270,7 @@ namespace Barotrauma
|
||||
relativeOrigin = MathUtils.RotatePoint(relativeOrigin, -item.RotationRad);
|
||||
Vector2 origin = new Vector2(rect.X + rect.Width / 2, rect.Y - rect.Height / 2) + relativeOrigin;
|
||||
|
||||
item.rect.Location -= (origin - oldOrigin).ToPoint();
|
||||
item.rect.Location -= ((origin - oldOrigin) * scaleRelativeToPrefab).ToPoint();
|
||||
}
|
||||
|
||||
if (item.PurchasedNewSwap && !string.IsNullOrEmpty(appliedSwap.SwappableItem?.SpawnWithId))
|
||||
@@ -3433,7 +3458,8 @@ namespace Barotrauma
|
||||
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
if (character.SelectedConstruction == this) { character.SelectedConstruction = null; }
|
||||
if (character.SelectedItem == this) { character.SelectedItem = null; }
|
||||
if (character.SelectedSecondaryItem == this) { character.SelectedSecondaryItem = null; }
|
||||
}
|
||||
|
||||
Door door = GetComponent<Door>();
|
||||
|
||||
@@ -3128,14 +3128,14 @@ namespace Barotrauma
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null)
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Vector2 position, Func<InterestingPosition, bool> filter = null, bool suppressWarning = false)
|
||||
{
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, Vector2.Zero, minDistFromPoint: 0, filter);
|
||||
bool success = TryGetInterestingPosition(useSyncedRand, positionType, minDistFromSubs, out Point pos, Vector2.Zero, minDistFromPoint: 0, filter, suppressWarning);
|
||||
position = pos.ToVector2();
|
||||
return success;
|
||||
}
|
||||
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Vector2 awayPoint, float minDistFromPoint = 0f, Func<InterestingPosition, bool> filter = null)
|
||||
public bool TryGetInterestingPosition(bool useSyncedRand, PositionType positionType, float minDistFromSubs, out Point position, Vector2 awayPoint, float minDistFromPoint = 0f, Func<InterestingPosition, bool> filter = null, bool suppressWarning = false)
|
||||
{
|
||||
if (!PositionsOfInterest.Any())
|
||||
{
|
||||
@@ -3155,11 +3155,14 @@ namespace Barotrauma
|
||||
}
|
||||
if (!suitablePositions.Any())
|
||||
{
|
||||
string errorMsg = "Could not find a suitable position of interest. (PositionType: " + positionType + ", minDistFromSubs: " + minDistFromSubs + ")\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce("Level.TryGetInterestingPosition:PositionTypeNotFound", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
if (!suppressWarning)
|
||||
{
|
||||
string errorMsg = "Could not find a suitable position of interest. (PositionType: " + positionType + ", minDistFromSubs: " + minDistFromSubs + ")\n" + Environment.StackTrace.CleanupStackTrace();
|
||||
GameAnalyticsManager.AddErrorEventOnce("Level.TryGetInterestingPosition:PositionTypeNotFound", GameAnalyticsManager.ErrorSeverity.Error, errorMsg);
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
#endif
|
||||
}
|
||||
position = PositionsOfInterest[Rand.Int(PositionsOfInterest.Count, (useSyncedRand ? Rand.RandSync.ServerAndClient : Rand.RandSync.Unsynced))].Position;
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,8 @@ namespace Barotrauma
|
||||
public readonly List<EventPrefab> EventHistory = new List<EventPrefab>();
|
||||
public readonly List<EventPrefab> NonRepeatableEvents = new List<EventPrefab>();
|
||||
|
||||
public bool EventsExhausted { get; set; }
|
||||
|
||||
public float CrushDepth
|
||||
{
|
||||
get
|
||||
@@ -130,6 +132,8 @@ namespace Barotrauma
|
||||
|
||||
string[] nonRepeatablePrefabNames = element.GetAttributeStringArray("nonrepeatableevents", new string[] { });
|
||||
NonRepeatableEvents.AddRange(EventPrefab.Prefabs.Where(p => nonRepeatablePrefabNames.Any(n => p.Identifier == n)));
|
||||
|
||||
EventsExhausted = element.GetAttributeBool(nameof(EventsExhausted).ToLower(), false);
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +242,8 @@ namespace Barotrauma
|
||||
new XAttribute("difficulty", Difficulty.ToString("G", CultureInfo.InvariantCulture)),
|
||||
new XAttribute("size", XMLExtensions.PointToString(Size)),
|
||||
new XAttribute("generationparams", GenerationParams.Identifier),
|
||||
new XAttribute("initialdepth", InitialDepth));
|
||||
new XAttribute("initialdepth", InitialDepth),
|
||||
new XAttribute(nameof(EventsExhausted).ToLower(), EventsExhausted));
|
||||
|
||||
if (HasBeaconStation)
|
||||
{
|
||||
|
||||
@@ -944,15 +944,20 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
location.LevelData.EventsExhausted = false;
|
||||
if (location.Discovered)
|
||||
{
|
||||
if (furthestDiscoveredLocation == null ||
|
||||
if (furthestDiscoveredLocation == null ||
|
||||
location.MapPosition.X > furthestDiscoveredLocation.MapPosition.X)
|
||||
{
|
||||
furthestDiscoveredLocation = location;
|
||||
}
|
||||
}
|
||||
}
|
||||
foreach (LocationConnection connection in Connections)
|
||||
{
|
||||
connection.LevelData.EventsExhausted = false;
|
||||
}
|
||||
|
||||
foreach (Location location in Locations)
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
|
||||
public NPCSet(ContentXElement element, NPCSetsFile file) : base(file, element.GetAttributeIdentifier("identifier", ""))
|
||||
{
|
||||
Humans = element.Elements().Select(npcElement => new HumanPrefab(npcElement, file)).ToImmutableArray();
|
||||
Humans = element.Elements().Select(npcElement => new HumanPrefab(npcElement, file, Identifier)).ToImmutableArray();
|
||||
}
|
||||
|
||||
public static HumanPrefab? Get(Identifier setIdentifier, Identifier npcidentifier)
|
||||
|
||||
@@ -203,7 +203,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
newCollection.Add(new HumanPrefab(npcElement, file));
|
||||
newCollection.Add(new HumanPrefab(npcElement, file, npcSetIdentifier: from));
|
||||
}
|
||||
}
|
||||
humanPrefabCollections.Add(newCollection);
|
||||
|
||||
@@ -1426,7 +1426,7 @@ namespace Barotrauma
|
||||
|
||||
static bool ShouldRemoveLinkedEntity(MapEntity e, bool doorInUse, PlacedModule module)
|
||||
{
|
||||
if (e is Item it && it.GetComponent<Ladder>() != null)
|
||||
if (e is Item it && it.IsLadder)
|
||||
{
|
||||
if (module.UsedGapPositions.HasFlag(OutpostModuleInfo.GapPosition.Top) || module.UsedGapPositions.HasFlag(OutpostModuleInfo.GapPosition.Bottom))
|
||||
{
|
||||
@@ -1568,7 +1568,7 @@ namespace Barotrauma
|
||||
foreach (HumanPrefab humanPrefab in humanPrefabs)
|
||||
{
|
||||
if (humanPrefab is null) { continue; }
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: humanPrefab.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient);
|
||||
var characterInfo = humanPrefab.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||
if (location != null && location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
|
||||
{
|
||||
killedCharacters.Add(humanPrefab);
|
||||
@@ -1582,7 +1582,7 @@ namespace Barotrauma
|
||||
{
|
||||
for (int tries = 0; tries < 100; tries++)
|
||||
{
|
||||
var characterInfo = new CharacterInfo(CharacterPrefab.HumanSpeciesName, jobOrJobPrefab: killedCharacter.GetJobPrefab(Rand.RandSync.ServerAndClient), randSync: Rand.RandSync.ServerAndClient);
|
||||
var characterInfo = killedCharacter.CreateCharacterInfo(Rand.RandSync.ServerAndClient);
|
||||
if (!location.KilledCharacterIdentifiers.Contains(characterInfo.GetIdentifier()))
|
||||
{
|
||||
selectedCharacters.Add((killedCharacter, characterInfo));
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
class RoundEndCinematic
|
||||
{
|
||||
public bool Running
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
public Camera AssignedCamera;
|
||||
|
||||
private float duration;
|
||||
|
||||
private CoroutineHandle updateCoroutine;
|
||||
|
||||
public RoundEndCinematic(Submarine submarine, Camera cam, float duration = 10.0f)
|
||||
: this(new List<Submarine>() { submarine }, cam, duration)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public RoundEndCinematic(List<Submarine> submarines, Camera cam, float duration)
|
||||
{
|
||||
if (!submarines.Any(s => s != null)) return;
|
||||
|
||||
this.duration = duration;
|
||||
AssignedCamera = cam;
|
||||
|
||||
Running = true;
|
||||
updateCoroutine = CoroutineManager.StartCoroutine(Update(submarines, cam));
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
CoroutineManager.StopCoroutines(updateCoroutine);
|
||||
Running = false;
|
||||
#if CLIENT
|
||||
GUI.ScreenOverlayColor = Color.TransparentBlack;
|
||||
#endif
|
||||
}
|
||||
|
||||
private IEnumerable<CoroutineStatus> Update(List<Submarine> subs, Camera cam)
|
||||
{
|
||||
if (!subs.Any()) yield return CoroutineStatus.Success;
|
||||
|
||||
#if CLIENT
|
||||
Character.Controlled = null;
|
||||
GameMain.LightManager.LosEnabled = false;
|
||||
#endif
|
||||
cam.TargetPos = Vector2.Zero;
|
||||
|
||||
Level.Loaded.TopBarrier.Enabled = false;
|
||||
|
||||
foreach (Character character in Character.CharacterList)
|
||||
{
|
||||
character.AnimController.Frozen = true;
|
||||
foreach (Limb limb in character.AnimController.Limbs)
|
||||
{
|
||||
limb.body.PhysEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
cam.TargetPos = Vector2.Zero;
|
||||
float timer = 0.0f;
|
||||
float initialZoom = cam.Zoom;
|
||||
Vector2 initialCameraPos = cam.Position;
|
||||
|
||||
while (timer < duration)
|
||||
{
|
||||
if (Screen.Selected != GameMain.GameScreen)
|
||||
{
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
#if CLIENT
|
||||
GUI.ScreenOverlayColor = Color.TransparentBlack;
|
||||
#endif
|
||||
|
||||
Running = false;
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
|
||||
Vector2 minPos = new Vector2(
|
||||
subs.Min(s => s.WorldPosition.X - s.Borders.Width / 2),
|
||||
subs.Min(s => s.WorldPosition.Y - s.Borders.Height / 2));
|
||||
Vector2 maxPos = new Vector2(
|
||||
subs.Min(s => s.WorldPosition.X + s.Borders.Width / 2),
|
||||
subs.Min(s => s.WorldPosition.Y + s.Borders.Height / 2));
|
||||
Vector2 cameraPos = new Vector2(
|
||||
MathHelper.SmoothStep(minPos.X, maxPos.X, timer / duration),
|
||||
(minPos.Y + maxPos.Y) / 2.0f);
|
||||
cam.Translate(cameraPos - cam.Position);
|
||||
|
||||
foreach (Submarine sub in subs)
|
||||
{
|
||||
sub.PhysicsBody?.ResetDynamics();
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
cam.Zoom = MathHelper.SmoothStep(initialZoom, 0.5f, timer / duration);
|
||||
if (timer / duration > 0.9f)
|
||||
{
|
||||
GUI.ScreenOverlayColor = Color.Lerp(Color.TransparentBlack, Color.Black, ((timer / duration) - 0.9f) * 10.0f);
|
||||
}
|
||||
#endif
|
||||
timer += CoroutineManager.UnscaledDeltaTime;
|
||||
|
||||
yield return CoroutineStatus.Running;
|
||||
}
|
||||
|
||||
Running = false;
|
||||
|
||||
yield return new WaitForSeconds(0.1f);
|
||||
|
||||
#if CLIENT
|
||||
GUI.ScreenOverlayColor = Color.TransparentBlack;
|
||||
#endif
|
||||
|
||||
yield return CoroutineStatus.Success;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -986,14 +986,6 @@ namespace Barotrauma
|
||||
subBody.Body.ResetDynamics();
|
||||
subBody.Body.Enabled = false;
|
||||
|
||||
foreach (MapEntity e in MapEntity.mapEntityList)
|
||||
{
|
||||
if (e.Submarine == this)
|
||||
{
|
||||
Spawner.AddEntityToRemoveQueue(e);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (Character c in Character.CharacterList)
|
||||
{
|
||||
if (c.Submarine == this)
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
using Barotrauma.Extensions;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Networking;
|
||||
using FarseerPhysics;
|
||||
using FarseerPhysics.Collision;
|
||||
using FarseerPhysics.Common;
|
||||
using FarseerPhysics.Dynamics;
|
||||
using FarseerPhysics.Dynamics.Contacts;
|
||||
@@ -898,13 +896,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool holdingOntoSomething = false;
|
||||
if (c.SelectedConstruction != null)
|
||||
if (c.SelectedSecondaryItem != null)
|
||||
{
|
||||
holdingOntoSomething =
|
||||
c.SelectedConstruction.GetComponent<Ladder>() != null ||
|
||||
(c.SelectedConstruction.GetComponent<Controller>()?.LimbPositions.Any() ?? false);
|
||||
holdingOntoSomething = c.SelectedSecondaryItem.IsLadder ||
|
||||
(c.SelectedSecondaryItem.GetComponent<Controller>()?.LimbPositions.Any() ?? false);
|
||||
}
|
||||
if (!holdingOntoSomething && c.SelectedItem != null)
|
||||
{
|
||||
holdingOntoSomething = c.SelectedItem.GetComponent<Controller>()?.LimbPositions.Any() ?? false;
|
||||
}
|
||||
|
||||
if (!holdingOntoSomething)
|
||||
{
|
||||
c.AnimController.Collider.ApplyLinearImpulse(c.AnimController.Collider.Mass * impulse, 10.0f);
|
||||
|
||||
@@ -19,7 +19,7 @@ namespace Barotrauma
|
||||
|
||||
public static bool ShowWayPoints = true, ShowSpawnPoints = true;
|
||||
|
||||
public const float LadderWaypointInterval = 70.0f;
|
||||
public const float LadderWaypointInterval = 55.0f;
|
||||
|
||||
protected SpawnType spawnType;
|
||||
private string[] idCardTags;
|
||||
@@ -560,21 +560,22 @@ namespace Barotrauma
|
||||
stairPoints.ForEach(wp => wp.FindStairs());
|
||||
}
|
||||
|
||||
// Ladders
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var ladders = item.GetComponent<Ladder>();
|
||||
if (ladders == null) { continue; }
|
||||
|
||||
Vector2 bottomPoint = new Vector2(item.Rect.Center.X, item.Rect.Top - item.Rect.Height + 10);
|
||||
List<WayPoint> ladderPoints = new List<WayPoint>
|
||||
List<(WayPoint wp, bool connectHullPoints)> ladderPoints = new List<(WayPoint, bool)>
|
||||
{
|
||||
new WayPoint(bottomPoint, SpawnType.Path, submarine),
|
||||
(new WayPoint(bottomPoint, SpawnType.Path, submarine), true)
|
||||
};
|
||||
|
||||
List<Body> ignoredBodies = new List<Body>();
|
||||
// Lowest point is only meaningful for hanging ladders inside the sub, but it shouldn't matter in other cases either.
|
||||
// Start point is where the bots normally grasp the ladder when they stand on ground.
|
||||
WayPoint lowestPoint = ladderPoints[0];
|
||||
WayPoint lowestPoint = ladderPoints[0].wp;
|
||||
WayPoint prevPoint = lowestPoint;
|
||||
Vector2 prevPos = prevPoint.SimPosition;
|
||||
Body ground = Submarine.PickBody(lowestPoint.SimPosition, lowestPoint.SimPosition - Vector2.UnitY, ignoredBodies,
|
||||
@@ -589,7 +590,7 @@ namespace Barotrauma
|
||||
if (lowestPoint == null || Math.Abs(startPoint.Position.Y - startHeight) > 40 && Hull.FindHull(nextPos) != null)
|
||||
{
|
||||
startPoint = new WayPoint(nextPos, SpawnType.Path, submarine);
|
||||
ladderPoints.Add(startPoint);
|
||||
ladderPoints.Add((startPoint, true));
|
||||
if (lowestPoint != null)
|
||||
{
|
||||
startPoint.ConnectTo(lowestPoint);
|
||||
@@ -613,18 +614,13 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
//no door, check for walls
|
||||
//no door, check for platforms/walls
|
||||
pickedBody = Submarine.PickBody(
|
||||
ConvertUnits.ToSimUnits(new Vector2(startPoint.Position.X, y)), prevPos, ignoredBodies, null, false,
|
||||
(Fixture f) => f.Body.UserData is Structure);
|
||||
}
|
||||
|
||||
if (pickedBody == null)
|
||||
{
|
||||
prevPos = Submarine.LastPickedPosition;
|
||||
continue;
|
||||
}
|
||||
else
|
||||
if (pickedBody != null)
|
||||
{
|
||||
ignoredBodies.Add(pickedBody);
|
||||
}
|
||||
@@ -632,19 +628,29 @@ namespace Barotrauma
|
||||
if (pickedDoor != null)
|
||||
{
|
||||
WayPoint newPoint = new WayPoint(pickedDoor.Item.Position, SpawnType.Path, submarine);
|
||||
ladderPoints.Add(newPoint);
|
||||
ladderPoints.Add((newPoint, true));
|
||||
newPoint.ConnectedGap = pickedDoor.LinkedGap;
|
||||
// TODO: Prevent the waypoint below being too close to the door
|
||||
newPoint.ConnectTo(prevPoint);
|
||||
prevPoint = newPoint;
|
||||
prevPos = new Vector2(prevPos.X, ConvertUnits.ToSimUnits(pickedDoor.Item.Position.Y - pickedDoor.Item.Rect.Height));
|
||||
// Adjust y to prevent waypoints clamping up together
|
||||
y = Math.Max(pickedDoor.Item.Position.Y, y);
|
||||
}
|
||||
else
|
||||
{
|
||||
WayPoint newPoint = new WayPoint(ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * heightFromFloor, SpawnType.Path, submarine);
|
||||
ladderPoints.Add(newPoint);
|
||||
Vector2 pos = pickedBody == null ? new Vector2(startPoint.Position.X, y) :
|
||||
ConvertUnits.ToDisplayUnits(Submarine.LastPickedPosition) + Vector2.UnitY * heightFromFloor;
|
||||
WayPoint newPoint = new WayPoint(pos, SpawnType.Path, submarine);
|
||||
ladderPoints.Add((newPoint, pickedBody != null));
|
||||
newPoint.ConnectTo(prevPoint);
|
||||
prevPoint = newPoint;
|
||||
prevPos = ConvertUnits.ToSimUnits(newPoint.Position);
|
||||
if (pickedBody != null)
|
||||
{
|
||||
// Adjust y to prevent waypoints clamping up together
|
||||
y = Math.Max(newPoint.Position.Y, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -652,33 +658,30 @@ namespace Barotrauma
|
||||
if (prevPoint.rect.Y < item.Rect.Y - 40)
|
||||
{
|
||||
WayPoint wayPoint = new WayPoint(new Vector2(item.Rect.Center.X, item.Rect.Y - 1.0f), SpawnType.Path, submarine);
|
||||
ladderPoints.Add(wayPoint);
|
||||
ladderPoints.Add((wayPoint, true));
|
||||
wayPoint.ConnectTo(prevPoint);
|
||||
}
|
||||
|
||||
// Connect ladder waypoints to hull points at the right and left side
|
||||
foreach (WayPoint ladderPoint in ladderPoints)
|
||||
var ladderWaypoints = ladderPoints.Select(lp => lp.wp);
|
||||
foreach (var ladderPoint in ladderPoints)
|
||||
{
|
||||
ladderPoint.Ladders = ladders;
|
||||
bool isHatch = ladderPoint.ConnectedGap != null && !ladderPoint.ConnectedGap.IsRoomToRoom;
|
||||
var wp = ladderPoint.wp;
|
||||
wp.Ladders = ladders;
|
||||
if (!ladderPoint.connectHullPoints) { continue; }
|
||||
bool isHatch = wp.ConnectedGap != null && !wp.ConnectedGap.IsRoomToRoom;
|
||||
for (int dir = -1; dir <= 1; dir += 2)
|
||||
{
|
||||
WayPoint closest = null;
|
||||
if (isHatch)
|
||||
{
|
||||
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null, ignored: ladderPoints);
|
||||
}
|
||||
else
|
||||
{
|
||||
closest = ladderPoint.FindClosest(dir, horizontalSearch: true, new Vector2(150, 100), ladderPoint.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderPoints);
|
||||
}
|
||||
WayPoint closest = isHatch ?
|
||||
wp.FindClosest(dir, horizontalSearch: true, new Vector2(500, 1000), wp.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, filter: wp => wp.CurrentHull == null, ignored: ladderWaypoints) :
|
||||
wp.FindClosest(dir, horizontalSearch: true, new Vector2(150, 100), wp.ConnectedGap?.ConnectedDoor?.Body.FarseerBody, ignored: ladderWaypoints);
|
||||
if (closest == null) { continue; }
|
||||
ladderPoint.ConnectTo(closest);
|
||||
wp.ConnectTo(closest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Another pass: connect cap and bottom points with other ladders when they are vertically adjacent to another (double ladders)
|
||||
// Another ladder pass: connect cap and bottom points with other ladders when they are vertically adjacent to another (double ladders)
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
var ladders = item.GetComponent<Ladder>();
|
||||
@@ -1035,12 +1038,10 @@ namespace Barotrauma
|
||||
|
||||
w.tags = element.GetAttributeIdentifierArray("tags", Array.Empty<Identifier>()).ToHashSet();
|
||||
|
||||
string jobIdentifier = element.GetAttributeString("job", "").ToLowerInvariant();
|
||||
if (!string.IsNullOrWhiteSpace(jobIdentifier))
|
||||
Identifier jobIdentifier = element.GetAttributeIdentifier("job", Identifier.Empty);
|
||||
if (!jobIdentifier.IsEmpty)
|
||||
{
|
||||
w.AssignedJob =
|
||||
JobPrefab.Get(jobIdentifier) ??
|
||||
JobPrefab.Prefabs.Find(jp => jp.Name.Equals(jobIdentifier, StringComparison.OrdinalIgnoreCase));
|
||||
w.AssignedJob = JobPrefab.Get(jobIdentifier);
|
||||
}
|
||||
|
||||
w.linkedToID = new List<ushort>();
|
||||
|
||||
@@ -810,6 +810,13 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(120.0f, IsPropertySaveable.Yes)]
|
||||
public float DisallowKickVoteTime
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(300.0f, IsPropertySaveable.Yes)]
|
||||
public float KillDisconnectedTime
|
||||
{
|
||||
|
||||
@@ -87,10 +87,12 @@ namespace Barotrauma
|
||||
GameSettings.SaveCurrentConfig();
|
||||
GameMain.SoundManager.SetCategoryMuffle("default", false);
|
||||
GUI.ClearMessages();
|
||||
#if !DEBUG
|
||||
if (GameMain.GameSession?.GameMode is TestGameMode)
|
||||
{
|
||||
DebugConsole.DeactivateCheats();
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -168,9 +170,9 @@ namespace Barotrauma
|
||||
|
||||
if (Character.Controlled != null)
|
||||
{
|
||||
if (Character.Controlled.SelectedConstruction != null && Character.Controlled.CanInteractWith(Character.Controlled.SelectedConstruction))
|
||||
if (Character.Controlled.SelectedItem != null && Character.Controlled.CanInteractWith(Character.Controlled.SelectedItem))
|
||||
{
|
||||
Character.Controlled.SelectedConstruction.UpdateHUD(cam, Character.Controlled, (float)deltaTime);
|
||||
Character.Controlled.SelectedItem.UpdateHUD(cam, Character.Controlled, (float)deltaTime);
|
||||
}
|
||||
if (Character.Controlled.Inventory != null)
|
||||
{
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
{
|
||||
Selected.Deselect();
|
||||
#if CLIENT
|
||||
GameMain.ParticleManager.ClearParticles();
|
||||
GUIContextMenu.CurrentContextMenu = null;
|
||||
GUI.ClearCursorWait();
|
||||
//make sure any textbox in the previously selected screen doesn't stay selected
|
||||
|
||||
@@ -290,6 +290,8 @@ namespace Barotrauma
|
||||
{ InputType.CrewOrders, Keys.C },
|
||||
|
||||
{ InputType.Voice, Keys.V },
|
||||
{ InputType.RadioVoice, Keys.None },
|
||||
{ InputType.LocalVoice, Keys.None },
|
||||
{ InputType.ToggleChatMode, Keys.R },
|
||||
{ InputType.Command, MouseButton.MiddleMouse },
|
||||
{ InputType.PreviousFireMode, MouseButton.MouseWheelDown },
|
||||
@@ -333,16 +335,15 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
bool playerConfigContainsNewChatBinds = false;
|
||||
bool playerConfigContainsRestoredVoipBinds = false;
|
||||
foreach (XElement element in elements)
|
||||
{
|
||||
foreach (XAttribute attribute in element.Attributes())
|
||||
{
|
||||
if (Enum.TryParse(attribute.Name.LocalName, out InputType result))
|
||||
{
|
||||
if (!playerConfigContainsNewChatBinds)
|
||||
{
|
||||
playerConfigContainsNewChatBinds = result == InputType.ActiveChat;
|
||||
}
|
||||
playerConfigContainsNewChatBinds |= result == InputType.ActiveChat;
|
||||
playerConfigContainsRestoredVoipBinds |= result == InputType.RadioVoice;
|
||||
bindings[result] = element.GetAttributeKeyOrMouse(attribute.Name.LocalName, bindings[result]);
|
||||
}
|
||||
}
|
||||
@@ -351,14 +352,15 @@ namespace Barotrauma
|
||||
// Clear the old chat binds for configs saved before the introduction of the new chat binds
|
||||
if (!playerConfigContainsNewChatBinds)
|
||||
{
|
||||
if (bindings.ContainsKey(InputType.Chat))
|
||||
{
|
||||
bindings[InputType.Chat] = Keys.None;
|
||||
}
|
||||
if (bindings.ContainsKey(InputType.RadioChat))
|
||||
{
|
||||
bindings[InputType.RadioChat] = Keys.None;
|
||||
}
|
||||
bindings[InputType.Chat] = Keys.None;
|
||||
bindings[InputType.RadioChat] = Keys.None;
|
||||
}
|
||||
|
||||
// Clear old VOIP binds to make sure we have no overlapping binds
|
||||
if (!playerConfigContainsRestoredVoipBinds)
|
||||
{
|
||||
bindings[InputType.LocalVoice] = Keys.None;
|
||||
bindings[InputType.RadioVoice] = Keys.None;
|
||||
}
|
||||
|
||||
Bindings = bindings.ToImmutableDictionary();
|
||||
@@ -511,6 +513,7 @@ namespace Barotrauma
|
||||
if (hudScaleChanged)
|
||||
{
|
||||
HUDLayoutSettings.CreateAreas();
|
||||
GameMain.GameSession?.HUDScaleChanged();
|
||||
}
|
||||
|
||||
GameMain.SoundManager?.ApplySettings();
|
||||
|
||||
@@ -264,12 +264,36 @@ namespace Barotrauma
|
||||
public string Name => $"Character Spawn Info ({SpeciesName})";
|
||||
public Dictionary<Identifier, SerializableProperty> SerializableProperties { get; set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferBuffs { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferAfflictions { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferInventory { get; private set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public Identifier SpeciesName { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.No)]
|
||||
public int Count { get; private set; }
|
||||
|
||||
[Serialize(0, IsPropertySaveable.No)]
|
||||
public int Stun { get; private set; }
|
||||
|
||||
[Serialize("", IsPropertySaveable.No)]
|
||||
public Identifier AfflictionOnSpawn { get; private set; }
|
||||
|
||||
[Serialize(1, IsPropertySaveable.No)]
|
||||
public int AfflictionStrength { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool TransferControl { get; private set; }
|
||||
|
||||
[Serialize(false, IsPropertySaveable.No)]
|
||||
public bool RemovePreviousCharacter { get; private set; }
|
||||
|
||||
[Serialize(0f, IsPropertySaveable.No)]
|
||||
public float Spread { get; private set; }
|
||||
|
||||
@@ -1596,6 +1620,64 @@ namespace Barotrauma
|
||||
{
|
||||
SwarmBehavior.CreateSwarm(characters.Cast<AICharacter>());
|
||||
}
|
||||
if (!characterSpawnInfo.AfflictionOnSpawn.IsEmpty)
|
||||
{
|
||||
if (!AfflictionPrefab.Prefabs.TryGet(characterSpawnInfo.AfflictionOnSpawn, out AfflictionPrefab afflictionPrefab))
|
||||
{
|
||||
DebugConsole.NewMessage($"Could not apply an affliction to the spawned character(s). No affliction with the identifier \"{characterSpawnInfo.AfflictionOnSpawn}\" found.", Color.Red);
|
||||
return;
|
||||
}
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, afflictionPrefab.Instantiate(characterSpawnInfo.AfflictionStrength));
|
||||
}
|
||||
if (characterSpawnInfo.Stun > 0)
|
||||
{
|
||||
newCharacter.SetStun(characterSpawnInfo.Stun);
|
||||
}
|
||||
foreach (var target in targets)
|
||||
{
|
||||
if (!(target is Character character)) { continue; }
|
||||
if (characterSpawnInfo.TransferInventory && character.Inventory != null && newCharacter.Inventory != null)
|
||||
{
|
||||
if (character.Inventory.Capacity != newCharacter.Inventory.Capacity) { return; }
|
||||
for (int i = 0; i < character.Inventory.Capacity && i < newCharacter.Inventory.Capacity; i++)
|
||||
{
|
||||
character.Inventory.GetItemsAt(i).ForEachMod(item => newCharacter.Inventory.TryPutItem(item, i, allowSwapping: true, allowCombine: false, user: null));
|
||||
}
|
||||
}
|
||||
if (characterSpawnInfo.TransferBuffs || characterSpawnInfo.TransferAfflictions)
|
||||
{
|
||||
foreach (Affliction affliction in character.CharacterHealth.GetAllAfflictions())
|
||||
{
|
||||
if (!characterSpawnInfo.TransferAfflictions && characterSpawnInfo.TransferBuffs && affliction.Prefab.IsBuff)
|
||||
{
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, affliction.Prefab.Instantiate(affliction.Strength));
|
||||
}
|
||||
if (characterSpawnInfo.TransferAfflictions)
|
||||
{
|
||||
newCharacter.CharacterHealth.ApplyAffliction(newCharacter.AnimController.MainLimb, affliction.Prefab.Instantiate(affliction.Strength));
|
||||
}
|
||||
}
|
||||
}
|
||||
if (i == characterSpawnInfo.Count) // Only perform the below actions if this is the last character being spawned.
|
||||
{
|
||||
if (characterSpawnInfo.TransferControl)
|
||||
{
|
||||
#if CLIENT
|
||||
if (Character.Controlled == target)
|
||||
{
|
||||
Character.Controlled = newCharacter;
|
||||
}
|
||||
#elif SERVER
|
||||
foreach (Client c in GameMain.Server.ConnectedClients)
|
||||
{
|
||||
if (c.Character != target) { continue; }
|
||||
GameMain.Server.SetClientCharacter(c, newCharacter);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (characterSpawnInfo.RemovePreviousCharacter) { Entity.Spawner?.AddEntityToRemoveQueue(character); }
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -1897,7 +1979,7 @@ namespace Barotrauma
|
||||
{
|
||||
continue;
|
||||
}
|
||||
element.Parent.ApplyToProperty(target, property, n, CoroutineManager.UnscaledDeltaTime);
|
||||
element.Parent.ApplyToProperty(target, property, n, CoroutineManager.DeltaTime);
|
||||
}
|
||||
|
||||
foreach (Affliction affliction in element.Parent.Afflictions)
|
||||
|
||||
Reference in New Issue
Block a user