(7788ec72a) Test issuing orders automatically.
This commit is contained in:
@@ -14,11 +14,6 @@ namespace Barotrauma
|
||||
{
|
||||
public static bool DisableEnemyAI;
|
||||
|
||||
/// <summary>
|
||||
/// Enable the character to attack the outposts and the characters inside them. Disabled by default.
|
||||
/// </summary>
|
||||
public bool TargetOutposts;
|
||||
|
||||
class WallTarget
|
||||
{
|
||||
public Vector2 Position;
|
||||
@@ -122,7 +117,6 @@ namespace Barotrauma
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
|
||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||
public SwarmBehavior SwarmBehavior { get; private set; }
|
||||
|
||||
public bool AttackHumans
|
||||
{
|
||||
@@ -216,10 +210,6 @@ namespace Barotrauma
|
||||
case "latchonto":
|
||||
LatchOntoAI = new LatchOntoAI(subElement, this);
|
||||
break;
|
||||
case "swarm":
|
||||
case "swarmbehavior":
|
||||
SwarmBehavior = new SwarmBehavior(subElement, this);
|
||||
break;
|
||||
case "targetpriority":
|
||||
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
|
||||
break;
|
||||
@@ -320,9 +310,8 @@ namespace Barotrauma
|
||||
{
|
||||
State = AIState.Idle;
|
||||
}
|
||||
else if (Character.Health < fleeHealthThreshold && SwarmBehavior == null)
|
||||
else if (Character.Health < fleeHealthThreshold)
|
||||
{
|
||||
// Don't flee from damage if in a swarm.
|
||||
State = AIState.Escape;
|
||||
}
|
||||
else if (targetingPriority != null)
|
||||
@@ -331,6 +320,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
|
||||
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
|
||||
{
|
||||
State = AIState.Idle;
|
||||
@@ -369,14 +360,11 @@ namespace Barotrauma
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
LatchOntoAI?.Update(this, deltaTime);
|
||||
IsSteeringThroughGap = false;
|
||||
if (SwarmBehavior != null)
|
||||
{
|
||||
SwarmBehavior.IsActive = State == AIState.Idle && Character.CurrentHull == null;
|
||||
SwarmBehavior.Refresh();
|
||||
SwarmBehavior.UpdateSteering(deltaTime);
|
||||
}
|
||||
// Just some debug code that makes the characters to follow the mouse cursor
|
||||
//run = true;
|
||||
//Vector2 mousePos = ConvertUnits.ToSimUnits(Screen.Selected.Cam.ScreenToWorld(PlayerInput.MousePosition));
|
||||
//steeringManager.SteeringSeek(mousePos, Character.AnimController.GetCurrentSpeed(run));
|
||||
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||
}
|
||||
|
||||
@@ -499,7 +487,7 @@ namespace Barotrauma
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!IsLatchedOnSub)
|
||||
if (!IsProperlyLatchedOnSub)
|
||||
{
|
||||
UpdateWallTarget();
|
||||
}
|
||||
@@ -558,7 +546,7 @@ namespace Barotrauma
|
||||
{
|
||||
WallSection section = wallTarget.Structure.GetSection(wallTarget.SectionIndex);
|
||||
Vector2 targetPos = wallTarget.Structure.SectionPosition(wallTarget.SectionIndex, true);
|
||||
if (section?.gap != null && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
if (section?.gap != null && section.gap.IsRoomToRoom && SteerThroughGap(wallTarget.Structure, section, targetPos, deltaTime))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -799,21 +787,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSteeringThroughGap { get; private set; }
|
||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
||||
{
|
||||
IsSteeringThroughGap = true;
|
||||
SelectedAiTarget = wall.AiTarget;
|
||||
wallTarget = null;
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
Hull targetHull = section.gap?.FlowTargetHull;
|
||||
float distance = Vector2.Distance(Character.WorldPosition, targetWorldPos);
|
||||
float maxDistance = Math.Min(wall.Rect.Width, wall.Rect.Height);
|
||||
if (distance > maxDistance)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (targetHull != null)
|
||||
{
|
||||
if (wall.IsHorizontal)
|
||||
@@ -824,7 +800,16 @@ namespace Barotrauma
|
||||
{
|
||||
targetWorldPos.X = targetHull.WorldRect.Center.X;
|
||||
}
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
LatchOntoAI?.DeattachFromBody();
|
||||
Character.AnimController.ReleaseStuckLimbs();
|
||||
if (steeringManager is IndoorsSteeringManager)
|
||||
{
|
||||
steeringManager.SteeringManual(deltaTime, Vector2.Normalize(targetWorldPos - Character.WorldPosition));
|
||||
}
|
||||
else
|
||||
{
|
||||
steeringManager.SteeringSeek(ConvertUnits.ToSimUnits(targetWorldPos));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1056,43 +1041,19 @@ namespace Barotrauma
|
||||
#region Targeting
|
||||
private bool IsLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub;
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
//goes through all the AItargets, evaluates how preferable it is to attack the target,
|
||||
//whether the Character can see/hear the target and chooses the most preferable target within
|
||||
//sight/hearing range
|
||||
public AITarget UpdateTargets(Character character, out TargetingPriority priority)
|
||||
{
|
||||
if ((SelectedAiTarget != null || wallTarget != null) && IsLatchedOnSub)
|
||||
if (IsProperlyLatchedOnSub)
|
||||
{
|
||||
var wall = SelectedAiTarget.Entity as Structure;
|
||||
if (wall == null)
|
||||
{
|
||||
wall = wallTarget?.Structure;
|
||||
}
|
||||
// The target is not a wall or it's not the same as we are attached to -> release
|
||||
bool releaseTarget = wall == null || !wall.Bodies.Contains(LatchOntoAI.AttachJoints[0].BodyB);
|
||||
if (!releaseTarget)
|
||||
{
|
||||
for (int i = 0; i < wall.Sections.Length; i++)
|
||||
{
|
||||
if (CanPassThroughHole(wall, i))
|
||||
{
|
||||
releaseTarget = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (releaseTarget)
|
||||
{
|
||||
SelectedAiTarget = null;
|
||||
wallTarget = null;
|
||||
LatchOntoAI.DeattachFromBody();
|
||||
}
|
||||
else if (SelectedAiTarget?.Entity == wallTarget?.Structure)
|
||||
{
|
||||
// If attached to a valid target, just keep the target.
|
||||
// Priority not used in this case.
|
||||
priority = null;
|
||||
return SelectedAiTarget;
|
||||
}
|
||||
// If attached to a valid target, just keep the target.
|
||||
// Priority not used in this case.
|
||||
priority = null;
|
||||
return SelectedAiTarget;
|
||||
}
|
||||
AITarget newTarget = null;
|
||||
priority = null;
|
||||
@@ -1107,10 +1068,9 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
if (target.Type == AITarget.TargetType.HumanOnly) { continue; }
|
||||
if (!TargetOutposts)
|
||||
{
|
||||
if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; }
|
||||
}
|
||||
// Don't attack outposts.
|
||||
if (target.Entity.Submarine != null && target.Entity.Submarine.IsOutpost) { continue; }
|
||||
|
||||
Character targetCharacter = target.Entity as Character;
|
||||
//ignore the aitarget if it is the Character itself
|
||||
if (targetCharacter == character) continue;
|
||||
@@ -1175,7 +1135,7 @@ namespace Barotrauma
|
||||
else if (target.Entity != null)
|
||||
{
|
||||
//skip the target if it's a room and the character is already inside a sub
|
||||
if (character.CurrentHull != null && target.Entity is Hull) { continue; }
|
||||
if (character.CurrentHull != null && target.Entity is Hull) continue;
|
||||
|
||||
Door door = null;
|
||||
if (target.Entity is Item item)
|
||||
@@ -1204,37 +1164,23 @@ namespace Barotrauma
|
||||
// Ignore structures that doesn't have a body (not walls)
|
||||
continue;
|
||||
}
|
||||
if (s.IsPlatform)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (character.CurrentHull != null)
|
||||
{
|
||||
// Ignore walls when inside.
|
||||
continue;
|
||||
}
|
||||
valueModifier = 1;
|
||||
float wallMaxHealth = 400; // Anything more than this is ignored -> 200 = 1
|
||||
// Prefer weaker targets.
|
||||
valueModifier *= MathHelper.Lerp(1.5f, 0.5f, MathUtils.InverseLerp(0, 1, s.Health / wallMaxHealth));
|
||||
// Ignore walls when inside.
|
||||
valueModifier = character.CurrentHull == null ? 1 : 0;
|
||||
if (aggressiveBoarding)
|
||||
{
|
||||
var hulls = s.Submarine.GetHulls(false);
|
||||
for (int i = 0; i < s.Sections.Length; i++)
|
||||
{
|
||||
var section = s.Sections[i];
|
||||
if (section.gap != null)
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
if (CanPassThroughHole(s, i))
|
||||
{
|
||||
bool leadsInside = !section.gap.IsRoomToRoom && section.gap.FlowTargetHull != null && hulls.Any(h => h.Rect.Intersects(section.rect));
|
||||
valueModifier *= leadsInside ? 5 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// up to 100% priority increase for every gap in the wall
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
// Ignore walls that can be passed through
|
||||
valueModifier = 0;
|
||||
break;
|
||||
}
|
||||
else if (section.gap != null)
|
||||
{
|
||||
// up to 100% priority increase for every gap in the wall
|
||||
valueModifier *= 1 + section.gap.Open;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using Microsoft.Xna.Framework;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -54,6 +55,28 @@ namespace Barotrauma
|
||||
objectiveManager = new AIObjectiveManager(c);
|
||||
objectiveManager.AddObjective(new AIObjectiveFindSafety(c));
|
||||
objectiveManager.AddObjective(new AIObjectiveIdle(c));
|
||||
// TODO: do this only when the player hasn't issued an order for a while
|
||||
foreach (var automaticOrder in c.Info.Job.Prefab.AutomaticOrders)
|
||||
{
|
||||
var orderPrefab = Order.PrefabList.Find(o => o.AITag == automaticOrder.aiTag);
|
||||
// TODO: Similar code is used in CrewManager:815-> DRY
|
||||
var matchingItems = orderPrefab.ItemIdentifiers.Any() ?
|
||||
Item.ItemList.FindAll(it => orderPrefab.ItemIdentifiers.Contains(it.Prefab.Identifier) || it.HasTag(orderPrefab.ItemIdentifiers)) :
|
||||
Item.ItemList.FindAll(it => it.Components.Any(ic => ic.GetType() == orderPrefab.ItemComponentType));
|
||||
matchingItems.RemoveAll(it => it.Submarine != c.Submarine);
|
||||
var item = matchingItems.GetRandom();
|
||||
var order = new Order(orderPrefab, item ?? c.CurrentHull as Entity, item?.Components.FirstOrDefault(ic => ic.GetType() == orderPrefab.ItemComponentType));
|
||||
SetOrder(order, automaticOrder.option, c, true);
|
||||
|
||||
//#if CLIENT
|
||||
// GameMain.GameSession?.CrewManager?.SetCharacterOrder(c, order, automaticOrder.option, c);
|
||||
//#endif
|
||||
|
||||
// if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(order, order.FadeOutTime))
|
||||
// {
|
||||
// Character.Speak(order.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: true), ChatMessageType.Order);
|
||||
// }
|
||||
}
|
||||
|
||||
updateObjectiveTimer = Rand.Range(0.0f, UpdateObjectiveInterval);
|
||||
|
||||
@@ -267,7 +290,7 @@ namespace Barotrauma
|
||||
if (GameMain.GameSession?.CrewManager != null && GameMain.GameSession.CrewManager.AddOrder(newOrder, newOrder.FadeOutTime))
|
||||
{
|
||||
Character.Speak(
|
||||
newOrder.GetChatMessage("", Character.CurrentHull?.DisplayName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
newOrder.GetChatMessage("", Character.CurrentHull?.RoomName, givingOrderToSelf: false), ChatMessageType.Order);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -286,18 +309,18 @@ namespace Barotrauma
|
||||
|
||||
if (Character.PressureTimer > 50.0f && Character.CurrentHull != null)
|
||||
{
|
||||
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.DisplayName), null, 0, "pressure", 30.0f);
|
||||
Character.Speak(TextManager.Get("DialogPressure").Replace("[roomname]", Character.CurrentHull.RoomName), null, 0, "pressure", 30.0f);
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAttacked(Character attacker, AttackResult attackResult)
|
||||
{
|
||||
// Damage from falling etc.
|
||||
if (Character.LastDamageSource == null) { return; }
|
||||
float damage = attackResult.Damage;
|
||||
if (damage <= 0) { return; }
|
||||
if (attacker == null || attacker.IsDead || attacker.Removed)
|
||||
{
|
||||
// Ignore damage from falling etc that we shouldn't react to.
|
||||
if (Character.LastDamageSource == null) { return; }
|
||||
AddCombatObjective(AIObjectiveCombat.CombatMode.Retreat, Rand.Range(0.5f, 1f, Rand.RandSync.Unsynced));
|
||||
}
|
||||
else if (IsFriendly(attacker))
|
||||
|
||||
@@ -223,10 +223,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
bool isDiving = character.AnimController.InWater && character.AnimController.HeadInWater;
|
||||
|
||||
//only humanoids can climb ladders
|
||||
if (!isDiving && character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
if (character.AnimController is HumanoidAnimController && IsNextLadderSameAsCurrent)
|
||||
{
|
||||
if (character.SelectedConstruction != currentPath.CurrentNode.Ladders.Item &&
|
||||
currentPath.CurrentNode.Ladders.Item.IsInsideTrigger(character.WorldPosition))
|
||||
@@ -236,7 +234,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
var collider = character.AnimController.Collider;
|
||||
if (character.IsClimbing && !isDiving)
|
||||
if (character.IsClimbing)
|
||||
{
|
||||
Vector2 diff = currentPath.CurrentNode.SimPosition - pos;
|
||||
bool nextLadderSameAsCurrent = IsNextLadderSameAsCurrent;
|
||||
@@ -280,12 +278,6 @@ namespace Barotrauma
|
||||
}
|
||||
else if (character.AnimController.InWater)
|
||||
{
|
||||
// If the character is underwater, we don't need the ladders anymore
|
||||
if (character.IsClimbing && isDiving)
|
||||
{
|
||||
character.AnimController.Anim = AnimController.Animation.None;
|
||||
character.SelectedConstruction = null;
|
||||
}
|
||||
if (Vector2.DistanceSquared(pos, currentPath.CurrentNode.SimPosition) < MathUtils.Pow(collider.radius * 3, 2))
|
||||
{
|
||||
currentPath.SkipToNextNode();
|
||||
@@ -396,18 +388,6 @@ namespace Barotrauma
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!door.HasRequiredItems(character, false) && shouldBeOpen)
|
||||
{
|
||||
currentPath.Unreachable = true;
|
||||
return;
|
||||
}
|
||||
|
||||
door.Item.TryInteract(character, false, true, true);
|
||||
buttonPressCooldown = ButtonPressInterval;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -428,18 +408,20 @@ namespace Barotrauma
|
||||
//door closed and the character can't open doors -> node can't be traversed
|
||||
if (!canOpenDoors || character.LockHands) { return null; }
|
||||
|
||||
if (!canBreakDoors)
|
||||
{
|
||||
//door closed and the character can't open doors -> node can't be traversed
|
||||
if (!canOpenDoors || character.LockHands) return null;
|
||||
|
||||
var doorButtons = nextNode.Waypoint.ConnectedDoor.Item.GetConnectedComponents<Controller>();
|
||||
if (!doorButtons.Any())
|
||||
{
|
||||
if (!nextNode.Waypoint.ConnectedDoor.HasRequiredItems(character, false)) { return null; }
|
||||
}
|
||||
if (!doorButtons.Any()) return null;
|
||||
|
||||
foreach (Controller button in doorButtons)
|
||||
{
|
||||
if (Math.Sign(button.Item.Position.X - nextNode.Waypoint.Position.X) !=
|
||||
Math.Sign(node.Position.X - nextNode.Position.X)) { continue; }
|
||||
Math.Sign(node.Position.X - nextNode.Position.X)) continue;
|
||||
|
||||
if (!button.HasRequiredItems(character, false)) { return null; }
|
||||
if (!button.HasRequiredItems(character, false)) return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ namespace Barotrauma
|
||||
get { return attachJoints.Count > 0; }
|
||||
}
|
||||
|
||||
public bool IsAttachedToSub => IsAttached && (attachTargetBody?.UserData is Submarine || attachTargetBody?.UserData is Entity entity && entity.Submarine != null);
|
||||
public bool IsAttachedToSub => IsAttached && attachTargetBody?.UserData is Entity entity && (entity is Submarine sub || entity?.Submarine != null);
|
||||
|
||||
public LatchOntoAI(XElement element, EnemyAIController enemyAI)
|
||||
{
|
||||
@@ -190,7 +190,7 @@ namespace Barotrauma
|
||||
case AIController.AIState.Attack:
|
||||
if (enemyAI.AttackingLimb != null)
|
||||
{
|
||||
if (attachToSub && !enemyAI.IsSteeringThroughGap && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
if (attachToSub && wallAttachPos != Vector2.Zero && attachTargetBody != null)
|
||||
{
|
||||
// is not attached or is attached to something else
|
||||
if (!IsAttached || IsAttached && attachJoints[0].BodyB == attachTargetBody)
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace Barotrauma
|
||||
private AIObjectiveContainItem reloadWeaponObjective;
|
||||
private Hull retreatTarget;
|
||||
private AIObjectiveGoTo retreatObjective;
|
||||
private AIObjectiveFindSafety findSafety;
|
||||
|
||||
private float coolDownTimer;
|
||||
|
||||
@@ -61,9 +60,7 @@ namespace Barotrauma
|
||||
{
|
||||
Enemy = enemy;
|
||||
coolDownTimer = CoolDown;
|
||||
findSafety = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>();
|
||||
findSafety.Priority = 0;
|
||||
findSafety.unreachable.Clear();
|
||||
HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 0;
|
||||
Mode = mode;
|
||||
if (Enemy == null)
|
||||
{
|
||||
@@ -178,7 +175,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (retreatTarget == null || (retreatObjective != null && !retreatObjective.CanBeCompleted))
|
||||
{
|
||||
retreatTarget = findSafety.FindBestHull(new List<Hull>() { character.CurrentHull });
|
||||
retreatTarget = HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().FindBestHull(new List<Hull>() { character.CurrentHull });
|
||||
}
|
||||
if (retreatTarget != null)
|
||||
{
|
||||
@@ -280,6 +277,7 @@ namespace Barotrauma
|
||||
{
|
||||
abandon = true;
|
||||
SteeringManager.Reset();
|
||||
//HumanAIController.ObjectiveManager.GetObjective<AIObjectiveFindSafety>().Priority = 100;
|
||||
}
|
||||
|
||||
public override bool IsCompleted()
|
||||
|
||||
+10
-28
@@ -79,44 +79,26 @@ namespace Barotrauma
|
||||
foreach (FireSource fs in targetHull.FireSources)
|
||||
{
|
||||
bool inRange = fs.IsInDamageRange(character, MathHelper.Clamp(fs.DamageRange * 1.5f, extinguisher.Range * 0.5f, extinguisher.Range));
|
||||
bool move = !inRange;
|
||||
if (inRange || useExtinquisherTimer > 0.0f)
|
||||
if (targetHull == character.CurrentHull && (inRange || useExtinquisherTimer > 0.0f))
|
||||
{
|
||||
useExtinquisherTimer += deltaTime;
|
||||
if (useExtinquisherTimer > 2.0f)
|
||||
{
|
||||
useExtinquisherTimer = 0.0f;
|
||||
}
|
||||
if (useExtinquisherTimer > 2.0f) useExtinquisherTimer = 0.0f;
|
||||
|
||||
character.AIController.SteeringManager.Reset();
|
||||
character.CursorPosition = fs.Position;
|
||||
if (extinguisher.Item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
Limb sightLimb = null;
|
||||
if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.RightHand))
|
||||
extinguisher.Use(deltaTime, character);
|
||||
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
||||
}
|
||||
else if (character.Inventory.IsInLimbSlot(extinguisherItem, InvSlotType.LeftHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||
}
|
||||
if (!character.CanSeeTarget(fs, sightLimb))
|
||||
{
|
||||
move = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
move = false;
|
||||
extinguisher.Use(deltaTime, character);
|
||||
if (!targetHull.FireSources.Contains(fs))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
||||
}
|
||||
character.Speak(TextManager.Get("DialogPutOutFire").Replace("[roomname]", targetHull.Name), null, 0, "putoutfire", 10.0f);
|
||||
}
|
||||
return;
|
||||
}
|
||||
if (move)
|
||||
else
|
||||
{
|
||||
//go to the first firesource
|
||||
if (gotoObjective == null || !gotoObjective.CanBeCompleted || gotoObjective.IsCompleted())
|
||||
@@ -127,8 +109,8 @@ namespace Barotrauma
|
||||
{
|
||||
gotoObjective.TryComplete(deltaTime);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-17
@@ -18,7 +18,7 @@ namespace Barotrauma
|
||||
const float SearchHullInterval = 3.0f;
|
||||
const float clearUnreachableInterval = 30;
|
||||
|
||||
public readonly List<Hull> unreachable = new List<Hull>();
|
||||
private List<Hull> unreachable = new List<Hull>();
|
||||
|
||||
private float currenthullSafety;
|
||||
private float unreachableClearTimer;
|
||||
@@ -60,16 +60,11 @@ namespace Barotrauma
|
||||
else
|
||||
{
|
||||
divingGearObjective = null;
|
||||
// Reduce the timer so that we get a safe hull target faster.
|
||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
||||
// Reset the timer so that we get a safe hull target.
|
||||
searchHullTimer = 0;
|
||||
}
|
||||
}
|
||||
|
||||
if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
searchHullTimer = Math.Min(1, searchHullTimer);
|
||||
}
|
||||
|
||||
if (unreachableClearTimer > 0)
|
||||
{
|
||||
unreachableClearTimer -= deltaTime;
|
||||
@@ -84,7 +79,7 @@ namespace Barotrauma
|
||||
{
|
||||
searchHullTimer -= deltaTime;
|
||||
}
|
||||
else
|
||||
else if (currenthullSafety < HumanAIController.HULL_SAFETY_THRESHOLD)
|
||||
{
|
||||
var bestHull = FindBestHull();
|
||||
if (bestHull != null && bestHull != currentHull)
|
||||
@@ -193,17 +188,10 @@ namespace Barotrauma
|
||||
float dist = Math.Abs(character.WorldPosition.X - hull.WorldPosition.X) + Math.Abs(character.WorldPosition.Y - hull.WorldPosition.Y) * 2.0f;
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.9f, MathUtils.InverseLerp(0, 10000, dist));
|
||||
hullSafety *= distanceFactor;
|
||||
//skip the hull if the safety is already less than the best hull
|
||||
//(no need to do the expensive pathfinding if we already know we're not going to choose this hull)
|
||||
if (hullSafety < bestValue) { continue; }
|
||||
// Each unsafe node reduces the hull safety value.
|
||||
// Ignore current hull, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, hull.SimPosition);
|
||||
if (path.Unreachable)
|
||||
{
|
||||
unreachable.Add(hull);
|
||||
continue;
|
||||
}
|
||||
if (path.Unreachable) { continue; }
|
||||
int unsafeNodes = path.Nodes.Count(n => n.CurrentHull != character.CurrentHull && HumanAIController.UnsafeHulls.Contains(n.CurrentHull));
|
||||
hullSafety /= 1 + unsafeNodes;
|
||||
// If the target is not inside a friendly submarine, considerably reduce the hull safety.
|
||||
@@ -231,6 +219,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Huge preference for closer targets
|
||||
float distance = Vector2.DistanceSquared(character.WorldPosition, hull.WorldPosition);
|
||||
float distanceFactor = MathHelper.Lerp(1, 0.2f, MathUtils.InverseLerp(0, MathUtils.Pow(100000, 2), distance));
|
||||
|
||||
@@ -101,22 +101,10 @@ namespace Barotrauma
|
||||
HumanAIController.AnimController.Crouching = true;
|
||||
}
|
||||
|
||||
//float reach = HumanAIController.AnimController.ArmLength + ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
float reach = ConvertUnits.ToSimUnits(repairTool.Range);
|
||||
bool canReach = ConvertUnits.ToSimUnits(gapDiff.Length()) < reach;
|
||||
if (canReach)
|
||||
{
|
||||
Limb sightLimb = null;
|
||||
if (character.Inventory.IsInLimbSlot(repairTool.Item, InvSlotType.RightHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.RightHand);
|
||||
}
|
||||
else if (character.Inventory.IsInLimbSlot(repairTool.Item, InvSlotType.LeftHand))
|
||||
{
|
||||
sightLimb = character.AnimController.GetLimb(LimbType.LeftHand);
|
||||
}
|
||||
canReach = character.CanSeeTarget(leak, sightLimb);
|
||||
}
|
||||
else
|
||||
bool cannotReach = ConvertUnits.ToSimUnits(gapDiff.Length()) > reach;
|
||||
if (cannotReach)
|
||||
{
|
||||
if (gotoObjective != null)
|
||||
{
|
||||
@@ -142,6 +130,7 @@ namespace Barotrauma
|
||||
AddSubObjective(gotoObjective);
|
||||
}
|
||||
}
|
||||
canReach = character.CanSeeTarget(leak, sightLimb);
|
||||
}
|
||||
if (gotoObjective == null || gotoObjective.IsCompleted())
|
||||
{
|
||||
|
||||
@@ -26,9 +26,6 @@ namespace Barotrauma
|
||||
private float standStillTimer;
|
||||
private float walkDuration;
|
||||
|
||||
private readonly List<Hull> targetHulls = new List<Hull>(20);
|
||||
private readonly List<float> hullWeights = new List<float>(20);
|
||||
|
||||
public AIObjectiveIdle(Character character) : base(character, "")
|
||||
{
|
||||
standStillTimer = Rand.Range(-10.0f, 10.0f);
|
||||
@@ -109,7 +106,7 @@ namespace Barotrauma
|
||||
if (isCurrentHullOK)
|
||||
{
|
||||
// Check that there is no unsafe or forbidden hulls on the way to the target
|
||||
// Only do this when the current hull is ok, because otherwise would block all paths from the current hull to the target hull.
|
||||
// Only do this when the current hull is ok, because otherwise the would block all paths from the current hull to the target hull.
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, randomHull.SimPosition);
|
||||
if (path.Unreachable ||
|
||||
path.Nodes.Any(n => HumanAIController.UnsafeHulls.Contains(n.CurrentHull) || IsForbidden(n.CurrentHull)))
|
||||
@@ -131,8 +128,8 @@ namespace Barotrauma
|
||||
character.AIController.SelectTarget(currentTarget.AiTarget);
|
||||
string errorMsg = null;
|
||||
#if DEBUG
|
||||
bool isRoomNameFound = currentTarget.DisplayName != null;
|
||||
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.DisplayName : currentTarget.ToString()) + ")";
|
||||
bool isRoomNameFound = currentTarget.RoomName != null;
|
||||
errorMsg = "(Character " + character.Name + " idling, target " + (isRoomNameFound ? currentTarget.RoomName : currentTarget.ToString()) + ")";
|
||||
#endif
|
||||
var path = PathSteering.PathFinder.FindPath(character.SimPosition, currentTarget.SimPosition, errorMsg);
|
||||
PathSteering.SetPath(path);
|
||||
@@ -233,9 +230,13 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<Hull> targetHulls = new List<Hull>(20);
|
||||
private readonly List<float> hullWeights = new List<float>(20);
|
||||
|
||||
private void FindTargetHulls()
|
||||
{
|
||||
bool isCurrentHullOK = !HumanAIController.UnsafeHulls.Contains(character.CurrentHull) && !IsForbidden(character.CurrentHull);
|
||||
|
||||
targetHulls.Clear();
|
||||
hullWeights.Clear();
|
||||
foreach (var hull in Hull.hullList)
|
||||
@@ -265,6 +266,7 @@ namespace Barotrauma
|
||||
hullWeights.Add(weight);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private bool IsForbidden(Hull hull)
|
||||
|
||||
+5
-5
@@ -83,12 +83,12 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (character.CurrentHull == Item.CurrentHull && character.CanInteractWith(Item))
|
||||
if (character.CanInteractWith(Item))
|
||||
{
|
||||
if (repairTool == null)
|
||||
{
|
||||
FindRepairTool();
|
||||
}
|
||||
if (repairTool != null)
|
||||
{
|
||||
OperateRepairTool(deltaTime);
|
||||
|
||||
@@ -83,7 +83,7 @@ namespace Barotrauma
|
||||
if (character.SelectedCharacter == null)
|
||||
{
|
||||
character?.Speak(TextManager.Get("DialogFoundUnconsciousTarget")
|
||||
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.DisplayName),
|
||||
.Replace("[targetname]", targetCharacter.Name).Replace("[roomname]", character.CurrentHull.RoomName),
|
||||
null, 1.0f,
|
||||
"foundunconscioustarget" + targetCharacter.Name, 60.0f);
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
// TODO: Ensure that this works well enough. Consider using AIObjectiveLoop class.
|
||||
class AIObjectiveRescueAll : AIObjective
|
||||
{
|
||||
public override string DebugTag => "rescue all";
|
||||
|
||||
@@ -293,27 +293,6 @@ namespace Barotrauma
|
||||
return;
|
||||
}
|
||||
|
||||
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
|
||||
{
|
||||
//stop dragging if there's something between the pull limb and the target
|
||||
Vector2 sourceSimPos = mouthLimb.SimPosition;
|
||||
Vector2 targetSimPos = target.SimPosition;
|
||||
if (character.Submarine != null && character.SelectedCharacter.Submarine == null)
|
||||
{
|
||||
targetSimPos -= character.Submarine.SimPosition;
|
||||
}
|
||||
else if (character.Submarine == null && character.SelectedCharacter.Submarine != null)
|
||||
{
|
||||
sourceSimPos -= character.SelectedCharacter.Submarine.SimPosition;
|
||||
}
|
||||
var body = Submarine.CheckVisibility(sourceSimPos, targetSimPos, ignoreSubs: true);
|
||||
if (body != null)
|
||||
{
|
||||
character.DeselectCharacter();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Character targetCharacter = target;
|
||||
float eatSpeed = character.Mass / targetCharacter.Mass * 0.1f;
|
||||
eatTimer += deltaTime * eatSpeed;
|
||||
@@ -609,7 +588,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float prevWalkPos = WalkPos;
|
||||
WalkPos -= MainLimb.LinearVelocity.X * (CurrentAnimationParams.CycleSpeed / RagdollParams.JointScale / 100.0f);
|
||||
|
||||
Vector2 transformedStepSize = Vector2.Zero;
|
||||
@@ -645,11 +623,6 @@ namespace Barotrauma
|
||||
bool playFootstepSound = false;
|
||||
if (limb.type == LimbType.LeftFoot)
|
||||
{
|
||||
if (Math.Sign(Math.Sin(prevWalkPos)) > 0 && Math.Sign(transformedStepSize.Y) < 0)
|
||||
{
|
||||
playFootstepSound = true;
|
||||
}
|
||||
|
||||
limb.DebugRefPos = footPos + Vector2.UnitX * movement.X * 0.1f;
|
||||
limb.DebugTargetPos = footPos + new Vector2(
|
||||
transformedStepSize.X + movement.X * 0.1f,
|
||||
@@ -658,20 +631,13 @@ namespace Barotrauma
|
||||
}
|
||||
else if (limb.type == LimbType.RightFoot)
|
||||
{
|
||||
if (Math.Sign(Math.Sin(prevWalkPos)) < 0 && Math.Sign(transformedStepSize.Y) > 0)
|
||||
{
|
||||
playFootstepSound = true;
|
||||
}
|
||||
|
||||
limb.DebugRefPos = footPos + Vector2.UnitX * movement.X * 0.1f;
|
||||
limb.DebugTargetPos = footPos + new Vector2(
|
||||
-transformedStepSize.X + movement.X * 0.1f,
|
||||
(-transformedStepSize.Y > 0.0f) ? -transformedStepSize.Y : 0.0f);
|
||||
limb.MoveToPos(limb.DebugTargetPos, FootMoveForce);
|
||||
}
|
||||
#if CLIENT
|
||||
if (playFootstepSound) { PlayImpactSound(limb); }
|
||||
#endif
|
||||
|
||||
if (CurrentGroundedParams.FootAnglesInRadians.ContainsKey(limb.limbParams.ID))
|
||||
{
|
||||
SmoothRotateWithoutWrapping(limb,
|
||||
@@ -751,6 +717,22 @@ namespace Barotrauma
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
private void SmoothRotateWithoutWrapping(Limb limb, float angle, Limb referenceLimb, float torque)
|
||||
{
|
||||
//make sure the angle "has the same number of revolutions" as the reference limb
|
||||
//(e.g. we don't want to rotate the legs to 0 if the torso is at 360, because that'd blow up the hip joints)
|
||||
while (referenceLimb.Rotation - angle > MathHelper.TwoPi)
|
||||
{
|
||||
angle += MathHelper.TwoPi;
|
||||
}
|
||||
while (referenceLimb.Rotation - angle < -MathHelper.TwoPi)
|
||||
{
|
||||
angle -= MathHelper.TwoPi;
|
||||
}
|
||||
|
||||
limb?.body.SmoothRotate(angle, torque, wrapAngle: false);
|
||||
}
|
||||
|
||||
public override void Flip()
|
||||
{
|
||||
base.Flip();
|
||||
|
||||
@@ -534,12 +534,7 @@ namespace Barotrauma
|
||||
|
||||
Limb leftLeg = GetLimb(LimbType.LeftLeg);
|
||||
Limb rightLeg = GetLimb(LimbType.RightLeg);
|
||||
|
||||
float limpAmount =
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", leftFoot, true) +
|
||||
character.CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
limpAmount = MathHelper.Clamp(limpAmount / 100.0f, 0.0f, 1.0f);
|
||||
|
||||
|
||||
float walkCycleMultiplier = 1.0f;
|
||||
if (Stairs != null)
|
||||
{
|
||||
@@ -560,8 +555,7 @@ namespace Barotrauma
|
||||
//TODO: take into account that the feet aren't necessarily in CurrentHull
|
||||
//full slowdown (1.5f) when water is up to the torso
|
||||
surfaceY = ConvertUnits.ToSimUnits(currentHull.Surface);
|
||||
float bottomPos = Math.Max(colliderPos.Y, currentHull.Rect.Y - currentHull.Rect.Height);
|
||||
slowdownAmount = MathHelper.Clamp((surfaceY - bottomPos) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
|
||||
slowdownAmount = MathHelper.Clamp((surfaceY - colliderPos.Y) / TorsoPosition.Value, 0.0f, 1.0f) * 1.5f;
|
||||
}
|
||||
|
||||
float maxSpeed = Math.Max(TargetMovement.Length() - slowdownAmount, 1.0f);
|
||||
@@ -570,10 +564,11 @@ namespace Barotrauma
|
||||
|
||||
float walkPosX = (float)Math.Cos(WalkPos);
|
||||
float walkPosY = (float)Math.Sin(WalkPos);
|
||||
|
||||
|
||||
|
||||
Vector2 stepSize = StepSize.Value;
|
||||
stepSize.X *= walkPosX;
|
||||
stepSize.Y *= walkPosY;
|
||||
stepSize.Y *= walkPosY;
|
||||
|
||||
float footMid = colliderPos.X;
|
||||
if (limpAmount > 0.0f)
|
||||
@@ -594,7 +589,7 @@ namespace Barotrauma
|
||||
movement.Y = 0.0f;
|
||||
|
||||
if (torso == null) { return; }
|
||||
|
||||
|
||||
bool isNotRemote = true;
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) isNotRemote = !character.IsRemotePlayer;
|
||||
|
||||
@@ -698,7 +693,7 @@ namespace Barotrauma
|
||||
|
||||
//make the character limp if the feet are damaged
|
||||
float footAfflictionStrength = character.CharacterHealth.GetAfflictionStrength("damage", foot, true);
|
||||
footPos.X *= MathHelper.Lerp(1.0f, 0.75f, MathHelper.Clamp(footAfflictionStrength / 50.0f, 0.0f, 1.0f));
|
||||
footPos *= MathHelper.Lerp(1.0f, 0.5f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
|
||||
if (onSlope && Stairs == null)
|
||||
{
|
||||
@@ -788,19 +783,13 @@ namespace Barotrauma
|
||||
|
||||
//get the upper arm to point downwards
|
||||
var arm = GetLimb(armType);
|
||||
if (Math.Abs(arm.body.AngularVelocity) < 10.0f)
|
||||
{
|
||||
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.1f, 0.1f), arm.Mass * 10.0f);
|
||||
}
|
||||
arm.body.SmoothRotate(MathHelper.Clamp(-arm.body.AngularVelocity, -0.1f, 0.1f), arm.Mass * 10.0f);
|
||||
|
||||
//get the elbow to a neutral rotation
|
||||
if (Math.Abs(hand.body.AngularVelocity) < 10.0f)
|
||||
{
|
||||
LimbJoint elbow =
|
||||
LimbJoint elbow =
|
||||
GetJointBetweenLimbs(armType, hand.type) ??
|
||||
GetJointBetweenLimbs(armType, foreArmType);
|
||||
hand.body.ApplyTorque(MathHelper.Clamp(-elbow.JointAngle, -MathHelper.PiOver2, MathHelper.PiOver2) * hand.Mass * 10.0f);
|
||||
}
|
||||
hand.body.ApplyTorque(-elbow.JointAngle * hand.Mass * 10.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1138,12 +1127,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", leftFoot.WorldPosition, hullGuess: currentHull);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition);
|
||||
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", rightFoot.WorldPosition, hullGuess: currentHull);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition);
|
||||
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
#endif
|
||||
@@ -1194,8 +1183,7 @@ namespace Barotrauma
|
||||
isClimbing = false;
|
||||
}
|
||||
}
|
||||
else if ((character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right)) &&
|
||||
(!character.IsKeyDown(InputType.Up) && !character.IsKeyDown(InputType.Down)))
|
||||
else if (character.IsKeyDown(InputType.Left) || character.IsKeyDown(InputType.Right))
|
||||
{
|
||||
isClimbing = false;
|
||||
}
|
||||
|
||||
-1
@@ -84,7 +84,6 @@ namespace Barotrauma
|
||||
var folder = XMLExtensions.TryLoadXml(Character.GetConfigFile(speciesName))?.Root?.Element("ragdolls")?.GetAttributeString("folder", string.Empty);
|
||||
if (string.IsNullOrEmpty(folder) || folder.ToLowerInvariant() == "default")
|
||||
{
|
||||
//DebugConsole.NewMessage("[RagollParams] Using the default folder.");
|
||||
folder = GetDefaultFolder(speciesName);
|
||||
}
|
||||
return folder;
|
||||
|
||||
@@ -674,6 +674,8 @@ namespace Barotrauma
|
||||
velocity -= ((Submarine)f2.Body.UserData).Velocity;
|
||||
}
|
||||
|
||||
if (character.Submarine == null && f2.Body.UserData is Submarine) velocity -= ((Submarine)f2.Body.UserData).Velocity;
|
||||
|
||||
float impact = Vector2.Dot(velocity, -normal);
|
||||
if (f1.Body == Collider.FarseerBody || !Collider.Enabled)
|
||||
{
|
||||
@@ -692,11 +694,6 @@ namespace Barotrauma
|
||||
character.AddDamage(impactPos, new List<Affliction>() { AfflictionPrefab.InternalDamage.Instantiate((impact - ImpactTolerance) * 10.0f) }, 0.0f, true);
|
||||
strongestImpact = Math.Max(strongestImpact, impact - ImpactTolerance);
|
||||
character.ApplyStatusEffects(ActionType.OnImpact, 1.0f);
|
||||
//briefly disable impact damage
|
||||
//otherwise the character will take damage multiple times when for example falling,
|
||||
//because we use the velocity of the collider to determine the impact
|
||||
//(i.e. the character would take damage until the collider hits the floor and stops)
|
||||
character.DisableImpactDamageTimer = 0.25f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -704,7 +701,7 @@ namespace Barotrauma
|
||||
ImpactProjSpecific(impact, f1.Body);
|
||||
}
|
||||
|
||||
public void SeverLimbJoint(LimbJoint limbJoint, bool playSound = true)
|
||||
public void SeverLimbJoint(LimbJoint limbJoint)
|
||||
{
|
||||
if (!limbJoint.CanBeSevered || limbJoint.IsSevered)
|
||||
{
|
||||
@@ -733,7 +730,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint, bool playSound = true);
|
||||
partial void SeverLimbJointProjSpecific(LimbJoint limbJoint);
|
||||
|
||||
private void GetConnectedLimbs(List<Limb> connectedLimbs, List<LimbJoint> checkedJoints, Limb limb)
|
||||
{
|
||||
|
||||
@@ -80,13 +80,13 @@ namespace Barotrauma
|
||||
public HitDetection HitDetectionType { get; private set; }
|
||||
|
||||
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
|
||||
public AIBehaviorAfterAttack AfterAttack { get; set; }
|
||||
public AIBehaviorAfterAttack AfterAttack { get; private set; }
|
||||
|
||||
[Serialize(false, true), Editable(ToolTip = "Should the ai try to reverse when aiming with this attack?")]
|
||||
public bool Reverse { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target before the AI tries to attack.")]
|
||||
public float Range { get; set; }
|
||||
public float Range { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 2000.0f, ToolTip = "Min distance from the attack limb to the target to do damage. In distance based hit detection, the hit will be registered as soon as the target is within the damage range, unless the attack duration has expired.")]
|
||||
public float DamageRange { get; set; }
|
||||
@@ -95,19 +95,19 @@ namespace Barotrauma
|
||||
public float Duration { get; private set; }
|
||||
|
||||
[Serialize(5f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "How long the AI waits between the attacks.")]
|
||||
public float CoolDown { get; set; } = 5;
|
||||
public float CoolDown { get; private set; } = 5;
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 100.0f, DecimalCount = 2, ToolTip = "Used as the attack cooldown between different kind of attacks. Does not have effect, if set to 0.")]
|
||||
public float SecondaryCoolDown { get; set; } = 0;
|
||||
public float SecondaryCoolDown { get; private set; } = 0;
|
||||
|
||||
[Serialize(0f, true), Editable(MinValueFloat = 0, MaxValueFloat = 1, DecimalCount = 2, ToolTip = "Random factor applied to all cooldowns. Example: 0.1 -> adds a random value between -10% and 10% of the cooldown. Min 0 (default), Max 1 (could disable or double the cooldown in extreme cases).")]
|
||||
public float CoolDownRandomFactor { get; private set; } = 0;
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 10000.0f)]
|
||||
public float StructureDamage { get; set; }
|
||||
public float StructureDamage { get; private set; }
|
||||
|
||||
[Serialize(0.0f, true), Editable(MinValueFloat = 0.0f, MaxValueFloat = 1000.0f)]
|
||||
public float ItemDamage { get; set; }
|
||||
public float ItemDamage { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Legacy support. Use Afflictions.
|
||||
@@ -283,7 +283,6 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -293,7 +292,6 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -426,7 +426,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanSpeak;
|
||||
private bool canSpeak;
|
||||
|
||||
private bool speechImpedimentSet;
|
||||
|
||||
@@ -436,7 +436,7 @@ namespace Barotrauma
|
||||
{
|
||||
get
|
||||
{
|
||||
if (!CanSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
|
||||
if (!canSpeak || IsUnconscious || Stun > 0.0f || IsDead) return 100.0f;
|
||||
return speechImpediment;
|
||||
}
|
||||
set
|
||||
@@ -710,7 +710,7 @@ namespace Barotrauma
|
||||
displayName = TextManager.Get($"Character.{Path.GetFileName(Path.GetDirectoryName(file))}", true);
|
||||
|
||||
IsHumanoid = doc.Root.GetAttributeBool("humanoid", false);
|
||||
CanSpeak = doc.Root.GetAttributeBool("canspeak", false);
|
||||
canSpeak = doc.Root.GetAttributeBool("canspeak", false);
|
||||
needsAir = doc.Root.GetAttributeBool("needsair", false);
|
||||
Noise = doc.Root.GetAttributeFloat("noise", 100f);
|
||||
|
||||
@@ -853,7 +853,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (characterConfigFiles == null)
|
||||
{
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character, searchAllContentPackages: true);
|
||||
characterConfigFiles = GameMain.Instance.GetFilesOfType(ContentType.Character);
|
||||
}
|
||||
return characterConfigFiles;
|
||||
}
|
||||
@@ -1103,14 +1103,14 @@ namespace Barotrauma
|
||||
if (leftFoot != null)
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", leftFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
var rightFoot = AnimController.GetLimb(LimbType.RightFoot);
|
||||
if (rightFoot != null)
|
||||
{
|
||||
float footAfflictionStrength = CharacterHealth.GetAfflictionStrength("damage", rightFoot, true);
|
||||
speed *= MathHelper.Lerp(1.0f, 0.4f, MathHelper.Clamp(footAfflictionStrength / 80.0f, 0.0f, 1.0f));
|
||||
speed *= MathHelper.Lerp(1.0f, 0.25f, MathHelper.Clamp(footAfflictionStrength / 100.0f, 0.0f, 1.0f));
|
||||
}
|
||||
|
||||
return speed;
|
||||
@@ -1428,6 +1428,26 @@ namespace Barotrauma
|
||||
return (wall == null || !wall.CastShadow) && (door == null || door.IsOpen);
|
||||
}
|
||||
|
||||
public bool CanSeeCharacter(Character character, Vector2 sourceWorldPos)
|
||||
{
|
||||
Vector2 diff = ConvertUnits.ToSimUnits(character.WorldPosition - sourceWorldPos);
|
||||
|
||||
Body closestBody = null;
|
||||
if (character.Submarine == null)
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(sourceWorldPos, sourceWorldPos + diff);
|
||||
if (closestBody == null) return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
closestBody = Submarine.CheckVisibility(character.WorldPosition, character.WorldPosition - diff);
|
||||
if (closestBody == null) return true;
|
||||
}
|
||||
|
||||
Structure wall = closestBody.UserData as Structure;
|
||||
return wall == null || !wall.CastShadow;
|
||||
}
|
||||
|
||||
public bool HasEquippedItem(Item item)
|
||||
{
|
||||
for (int i = 0; i < Inventory.Capacity; i++)
|
||||
@@ -1568,8 +1588,9 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (item.InteractDistance == 0.0f && !item.Prefab.Triggers.Any()) { return false; }
|
||||
|
||||
|
||||
if (item.InteractDistance == 0.0f && !item.Prefab.Triggers.Any()) return false;
|
||||
|
||||
Pickable pickableComponent = item.GetComponent<Pickable>();
|
||||
if (pickableComponent != null && (pickableComponent.Picker != null && !pickableComponent.Picker.IsDead)) { return false; }
|
||||
@@ -2023,10 +2044,6 @@ namespace Barotrauma
|
||||
{
|
||||
IsRagdolled = IsForceRagdolled;
|
||||
}
|
||||
else if (IsRemotePlayer)
|
||||
{
|
||||
IsRagdolled = IsKeyDown(InputType.Ragdoll);
|
||||
}
|
||||
//Keep us ragdolled if we were forced or we're too speedy to unragdoll
|
||||
else if (allowRagdoll && (!IsRagdolled || AnimController.Collider.LinearVelocity.LengthSquared() < 1f))
|
||||
{
|
||||
@@ -2155,8 +2172,7 @@ namespace Barotrauma
|
||||
|
||||
public void Speak(string message, ChatMessageType? messageType = null, float delay = 0.0f, string identifier = "", float minDurationBetweenSimilar = 0.0f)
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(message)) { return; }
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) return;
|
||||
|
||||
//already sent a similar message a moment ago
|
||||
if (!string.IsNullOrEmpty(identifier) && minDurationBetweenSimilar > 0.0f &&
|
||||
@@ -2165,6 +2181,7 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
aiChatMessageQueue.Add(new AIChatMessage(message, messageType, identifier, delay));
|
||||
}
|
||||
|
||||
@@ -2581,9 +2598,11 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
CharacterList.Remove(this);
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
if (Controlled == this) { Controlled = null; }
|
||||
CharacterList.Remove(this);
|
||||
|
||||
if (Inventory != null)
|
||||
{
|
||||
|
||||
@@ -399,7 +399,7 @@ namespace Barotrauma
|
||||
{
|
||||
ID = idCounter;
|
||||
idCounter++;
|
||||
Name = element.GetAttributeString("name", "");
|
||||
Name = element.GetAttributeString("name", "unnamed");
|
||||
string genderStr = element.GetAttributeString("gender", "male").ToLowerInvariant();
|
||||
File = element.GetAttributeString("file", "");
|
||||
SourceElement = GetConfig(File).Root;
|
||||
@@ -423,29 +423,6 @@ namespace Barotrauma
|
||||
element.GetAttributeInt("beardindex", -1),
|
||||
element.GetAttributeInt("moustacheindex", -1),
|
||||
element.GetAttributeInt("faceattachmentindex", -1));
|
||||
|
||||
if (string.IsNullOrEmpty(Name))
|
||||
{
|
||||
if (SourceElement.Element("name") != null)
|
||||
{
|
||||
string firstNamePath = SourceElement.Element("name").GetAttributeString("firstname", "");
|
||||
if (firstNamePath != "")
|
||||
{
|
||||
firstNamePath = firstNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
Name = ToolBox.GetRandomLine(firstNamePath);
|
||||
}
|
||||
|
||||
string lastNamePath = SourceElement.Element("name").GetAttributeString("lastname", "");
|
||||
if (lastNamePath != "")
|
||||
{
|
||||
lastNamePath = lastNamePath.Replace("[GENDER]", (Head.gender == Gender.Female) ? "female" : "male");
|
||||
if (Name != "") Name += " ";
|
||||
Name += ToolBox.GetRandomLine(lastNamePath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
StartItemsGiven = element.GetAttributeBool("startitemsgiven", false);
|
||||
string personalityName = element.GetAttributeString("personality", "");
|
||||
ragdollFileName = element.GetAttributeString("ragdoll", string.Empty);
|
||||
@@ -729,8 +706,6 @@ namespace Barotrauma
|
||||
|
||||
partial void LoadAttachmentSprites();
|
||||
|
||||
// TODO: change the formula so that it's not linear and so that it takes into account the usefulness of the skill
|
||||
// -> give a weight to each skill, because some are much more valuable than others?
|
||||
private int CalculateSalary()
|
||||
{
|
||||
if (Name == null || Job == null) return 0;
|
||||
|
||||
@@ -231,8 +231,7 @@ namespace Barotrauma
|
||||
: afflictions.Concat(limbHealths.SelectMany(lh => lh.Afflictions.Where(limbHealthFilter)));
|
||||
}
|
||||
|
||||
private LimbHealth GetMatchingLimbHealth(Limb limb) => limbHealths[limb.HealthIndex];
|
||||
private LimbHealth GetMathingLimbHealth(Affliction affliction) => GetMatchingLimbHealth(Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb));
|
||||
private LimbHealth GetMathingLimbHealth(Affliction affliction) => limbHealths[Character.AnimController.GetLimb(affliction.Prefab.IndicatorLimb).HealthIndex];
|
||||
|
||||
/// <summary>
|
||||
/// Returns the limb afflictions and non-limbspecific afflictions that are set to be displayed on this limb.
|
||||
@@ -266,12 +265,6 @@ namespace Barotrauma
|
||||
|
||||
public Affliction GetAffliction(string afflictionType, Limb limb)
|
||||
{
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Limb health index out of bounds. Character\"" + Character.Name +
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
|
||||
return null;
|
||||
}
|
||||
foreach (Affliction affliction in limbHealths[limb.HealthIndex].Afflictions)
|
||||
{
|
||||
if (affliction.Prefab.AfflictionType == afflictionType) return affliction;
|
||||
@@ -473,13 +466,7 @@ namespace Barotrauma
|
||||
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction)
|
||||
{
|
||||
if (!newAffliction.Prefab.LimbSpecific || limb == null) return;
|
||||
if (limb.HealthIndex < 0 || limb.HealthIndex >= limbHealths.Count)
|
||||
{
|
||||
DebugConsole.ThrowError("Limb health index out of bounds. Character\"" + Character.Name +
|
||||
"\" only has health configured for" + limbHealths.Count + " limbs but the limb " + limb.type + " is targeting index " + limb.HealthIndex);
|
||||
return;
|
||||
}
|
||||
if (!newAffliction.Prefab.LimbSpecific) return;
|
||||
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction);
|
||||
}
|
||||
|
||||
@@ -624,12 +611,6 @@ namespace Barotrauma
|
||||
|
||||
partial void UpdateBleedingProjSpecific(AfflictionBleeding affliction, Limb targetLimb, float deltaTime);
|
||||
|
||||
public void SetVitality(float newVitality)
|
||||
{
|
||||
maxVitality = newVitality;
|
||||
CalculateVitality();
|
||||
}
|
||||
|
||||
public void CalculateVitality()
|
||||
{
|
||||
Vitality = MaxVitality;
|
||||
|
||||
@@ -1,17 +1,32 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
public class AutomaticOrder
|
||||
{
|
||||
public string aiTag;
|
||||
public string option;
|
||||
public float priority;
|
||||
|
||||
public AutomaticOrder(XElement element)
|
||||
{
|
||||
aiTag = element.GetAttributeString("aitag", null);
|
||||
option = element.GetAttributeString("option", null);
|
||||
priority = element.GetAttributeFloat("priority", 0);
|
||||
}
|
||||
}
|
||||
|
||||
partial class JobPrefab
|
||||
{
|
||||
public static List<JobPrefab> List;
|
||||
|
||||
public readonly XElement Items;
|
||||
public readonly List<string> ItemNames;
|
||||
|
||||
public List<SkillPrefab> Skills;
|
||||
public readonly XElement Items;
|
||||
public readonly List<string> ItemNames = new List<string>();
|
||||
public readonly List<SkillPrefab> Skills = new List<SkillPrefab>();
|
||||
public readonly List<AutomaticOrder> AutomaticOrders = new List<AutomaticOrder>();
|
||||
|
||||
[Serialize("1,1,1,1", false)]
|
||||
public Color UIColor
|
||||
@@ -111,10 +126,6 @@ namespace Barotrauma
|
||||
Name = TextManager.Get("JobName." + Identifier);
|
||||
Description = TextManager.Get("JobDescription." + Identifier);
|
||||
|
||||
ItemNames = new List<string>();
|
||||
|
||||
Skills = new List<SkillPrefab>();
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -155,7 +166,10 @@ namespace Barotrauma
|
||||
foreach (XElement skillElement in subElement.Elements())
|
||||
{
|
||||
Skills.Add(new SkillPrefab(skillElement));
|
||||
}
|
||||
}
|
||||
break;
|
||||
case "automaticorders":
|
||||
subElement.Elements().ForEach(order => AutomaticOrders.Add(new AutomaticOrder(order)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
partial class Limb : ISerializableEntity, ISpatialEntity
|
||||
partial class Limb : ISerializableEntity
|
||||
{
|
||||
// Note: not used
|
||||
private const float LimbDensity = 15;
|
||||
@@ -155,8 +155,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public Submarine Submarine => character.Submarine;
|
||||
|
||||
public Vector2 WorldPosition
|
||||
{
|
||||
get { return character.Submarine == null ? Position : Position + character.Submarine.Position; }
|
||||
|
||||
@@ -217,7 +217,6 @@ namespace Barotrauma
|
||||
{
|
||||
return corePackageRequiredFiles.All(fileType => Files.Any(file => file.Type == fileType));
|
||||
}
|
||||
|
||||
public bool ContainsRequiredCorePackageFiles(out List<ContentType> missingContentTypes)
|
||||
{
|
||||
missingContentTypes = new List<ContentType>();
|
||||
@@ -231,25 +230,6 @@ namespace Barotrauma
|
||||
return missingContentTypes.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make sure all the files defined in the content package are present
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool VerifyFiles(out List<string> errorMessages)
|
||||
{
|
||||
errorMessages = new List<string>();
|
||||
foreach (ContentFile file in Files)
|
||||
{
|
||||
if (!File.Exists(file.Path))
|
||||
{
|
||||
errorMessages.Add("File \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return errorMessages.Count == 0;
|
||||
}
|
||||
|
||||
public static ContentPackage CreatePackage(string name, string path, bool corePackage)
|
||||
{
|
||||
ContentPackage newPackage = new ContentPackage()
|
||||
@@ -418,13 +398,6 @@ namespace Barotrauma
|
||||
return path == "Mods";
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Are mods allowed to install a file into the specified path. If a content package XML includes files
|
||||
/// with a prohibited path, they are treated as references to external files. For example, a mod could include
|
||||
/// some vanilla files in the XML, in which case the game will simply use the vanilla files present in the game folder.
|
||||
/// </summary>
|
||||
/// <param name="path"></param>
|
||||
/// <returns></returns>
|
||||
public static bool IsModFilePathAllowed(string path)
|
||||
{
|
||||
while (true)
|
||||
@@ -449,6 +422,16 @@ namespace Barotrauma
|
||||
return contentPackages.SelectMany(f => f.Files).Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
return Files.Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public static IEnumerable<string> GetFilesOfType(IEnumerable<ContentPackage> contentPackages, ContentType type)
|
||||
{
|
||||
return contentPackages.SelectMany(f => f.Files).Where(f => f.Type == type).Select(f => f.Path);
|
||||
}
|
||||
|
||||
public IEnumerable<string> GetFilesOfType(ContentType type)
|
||||
{
|
||||
return Files.Where(f => f.Type == type).Select(f => f.Path);
|
||||
|
||||
@@ -68,7 +68,7 @@ namespace Barotrauma
|
||||
if (!CheatsEnabled && IsCheat)
|
||||
{
|
||||
NewMessage("You need to enable cheats using the command \"enablecheats\" before you can use the command \"" + names[0] + "\".", Color.Red);
|
||||
if (Steam.SteamManager.USE_STEAM)
|
||||
if (GameMain.Config.UseSteam)
|
||||
{
|
||||
NewMessage("Enabling cheats will disable Steam achievements during this play session.", Color.Red);
|
||||
}
|
||||
@@ -228,7 +228,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Failed to spawn an item. Arguments: \"" + string.Join(" ", args) + "\".";
|
||||
ThrowError(errorMsg, e);
|
||||
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnItem:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace);
|
||||
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnItem:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
}
|
||||
},
|
||||
() =>
|
||||
@@ -615,7 +615,7 @@ namespace Barotrauma
|
||||
NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White);
|
||||
}, isCheat: true));
|
||||
|
||||
commands.Add(new Command("fire|editfire", "fire/editfire: Allows putting up fires by left clicking.", (string[] args) =>
|
||||
commands.Add(new Command("water|editwater", "water/editwater: Toggle water editing. Allows adding water into rooms by holding the left mouse button and removing it by holding the right mouse button.", (string[] args) =>
|
||||
{
|
||||
Hull.EditFire = !Hull.EditFire;
|
||||
NewMessage(Hull.EditFire ? "Fire spawning on" : "Fire spawning off", Color.White);
|
||||
|
||||
@@ -55,9 +55,9 @@ namespace Barotrauma
|
||||
get { return true; }
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Vector2> SonarPositions
|
||||
public virtual Vector2 SonarPosition
|
||||
{
|
||||
get { return Enumerable.Empty<Vector2>(); }
|
||||
get { return Vector2.Zero; }
|
||||
}
|
||||
|
||||
public string SonarLabel
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -12,55 +10,30 @@ namespace Barotrauma
|
||||
|
||||
private int monsterCount;
|
||||
|
||||
private readonly List<Character> monsters = new List<Character>();
|
||||
private readonly List<Vector2> sonarPositions = new List<Vector2>();
|
||||
private Vector2 sonarPosition;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
public override Vector2 SonarPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
return sonarPositions;
|
||||
}
|
||||
get { return monster != null && !monster.IsDead ? sonarPosition : Vector2.Zero; }
|
||||
}
|
||||
|
||||
public MonsterMission(MissionPrefab prefab, Location[] locations)
|
||||
: base(prefab, locations)
|
||||
{
|
||||
monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
|
||||
monsterCount = prefab.ConfigElement.GetAttributeInt("monstercount", 1);
|
||||
}
|
||||
|
||||
public override void Start(Level level)
|
||||
{
|
||||
Level.Loaded.TryGetInterestingPosition(true, Level.PositionType.MainPath, Level.Loaded.Size.X * 0.3f, out Vector2 spawnPos);
|
||||
|
||||
//bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
//for (int i = 0; i < monsterCount; i++)
|
||||
//{
|
||||
// monsters.Add(Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false));
|
||||
//}
|
||||
//monsters.ForEach(m => m.Enabled = false);
|
||||
//SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
//sonarPositions.Add(spawnPos);
|
||||
|
||||
float offsetAmount = 500;
|
||||
for (int i = 0; i < monsterCount; i++)
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
var monster = Character.Create(monsterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true);
|
||||
monster.Enabled = false;
|
||||
monsters.Add(monster);
|
||||
if (monsters.Count == monsterCount)
|
||||
{
|
||||
//this will do nothing if the monsters have no swarm behavior defined,
|
||||
//otherwise it'll make the spawned characters act as a swarm
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
sonarPositions.Add(spawnPos);
|
||||
}
|
||||
}, Rand.Range(0f, monsterCount / 2, Rand.RandSync.Server));
|
||||
}
|
||||
bool isClient = false;
|
||||
#if CLIENT
|
||||
isClient = GameMain.Client != null;
|
||||
#endif
|
||||
monster = Character.Create(monsterFile, spawnPos, ToolBox.RandomSeed(8), null, isClient, true, false);
|
||||
monster.Enabled = false;
|
||||
sonarPosition = spawnPos;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -72,15 +45,7 @@ namespace Barotrauma
|
||||
var activeMonsters = monsters.Where(m => m != null && !m.Removed && !m.IsDead);
|
||||
if (activeMonsters.Any())
|
||||
{
|
||||
Vector2 centerOfMass = Vector2.Zero;
|
||||
foreach (var monster in activeMonsters)
|
||||
{
|
||||
//don't add another label if there's another monster roughly at the same spot
|
||||
if (sonarPositions.All(p => Vector2.DistanceSquared(p, monster.Position) > 1000.0f * 1000.0f))
|
||||
{
|
||||
sonarPositions.Add(monster.Position);
|
||||
}
|
||||
}
|
||||
sonarPosition = monster.Position;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,18 +16,11 @@ namespace Barotrauma
|
||||
|
||||
private int state;
|
||||
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
public override Vector2 SonarPosition
|
||||
{
|
||||
get
|
||||
{
|
||||
if (state > 0 )
|
||||
{
|
||||
Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return ConvertUnits.ToDisplayUnits(item.SimPosition);
|
||||
}
|
||||
return state > 0 ? Vector2.Zero : ConvertUnits.ToDisplayUnits(item.SimPosition);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -227,17 +227,17 @@ namespace Barotrauma
|
||||
monsters = new List<Character>();
|
||||
float offsetAmount = spawnPosType == Level.PositionType.MainPath ? 1000 : 100;
|
||||
for (int i = 0; i < amount; i++)
|
||||
{
|
||||
{
|
||||
CoroutineManager.InvokeAfter(() =>
|
||||
{
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
bool isClient = false;
|
||||
#if CLIENT
|
||||
isClient = GameMain.Client != null;
|
||||
#endif
|
||||
monsters.Add(Character.Create(characterFile, spawnPos + Rand.Vector(offsetAmount, Rand.RandSync.Server), i.ToString(), null, isClient, true, true));
|
||||
if (monsters.Count == amount)
|
||||
{
|
||||
spawnReady = true;
|
||||
//this will do nothing if the monsters have no swarm behavior defined,
|
||||
//otherwise it'll make the spawned characters act as a swarm
|
||||
SwarmBehavior.CreateSwarm(monsters.Cast<AICharacter>());
|
||||
}
|
||||
}, Rand.Range(0f, amount / 2, Rand.RandSync.Server));
|
||||
}
|
||||
|
||||
@@ -8,22 +8,6 @@ namespace Barotrauma
|
||||
{
|
||||
public static class StringFormatter
|
||||
{
|
||||
public static string Replace(this string s, string replacement, Func<char, bool> predicate)
|
||||
{
|
||||
var newString = new string[s.Length];
|
||||
for (int i = 0; i < s.Length; i++)
|
||||
{
|
||||
char letter = s[i];
|
||||
string newLetter = letter.ToString();
|
||||
if (predicate(letter))
|
||||
{
|
||||
newLetter = replacement;
|
||||
}
|
||||
newString[i] = newLetter;
|
||||
}
|
||||
return new string(newString.SelectMany(str => str.ToCharArray()).ToArray());
|
||||
}
|
||||
|
||||
public static string Remove(this string s, Func<char, bool> predicate)
|
||||
{
|
||||
return new string(s.ToCharArray().Where(c => !predicate(c)).ToArray());
|
||||
|
||||
@@ -108,7 +108,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
#if CLIENT
|
||||
new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.DisplayName));
|
||||
new GUIMessageBox("", TextManager.Get("CargoSpawnNotification").Replace("[roomname]", cargoRoom.RoomName));
|
||||
#endif
|
||||
|
||||
Dictionary<ItemContainer, int> availableContainers = new Dictionary<ItemContainer, int>();
|
||||
|
||||
@@ -12,8 +12,7 @@ namespace Barotrauma
|
||||
|
||||
public bool CheatsEnabled;
|
||||
|
||||
const int InitialMoney = 8700;
|
||||
public const int HullRepairCost = 500, ItemRepairCost = 500;
|
||||
const int InitialMoney = 4700;
|
||||
|
||||
protected bool watchmenSpawned;
|
||||
protected Character startWatchman, endWatchman;
|
||||
@@ -21,8 +20,6 @@ namespace Barotrauma
|
||||
//key = dialog flag, double = Timing.TotalTime when the line was last said
|
||||
private Dictionary<string, double> dialogLastSpoken = new Dictionary<string, double>();
|
||||
|
||||
public bool PurchasedHullRepairs, PurchasedItemRepairs;
|
||||
|
||||
protected Map map;
|
||||
public Map Map
|
||||
{
|
||||
@@ -73,37 +70,6 @@ namespace Barotrauma
|
||||
watchmenSpawned = false;
|
||||
startWatchman = null;
|
||||
endWatchman = null;
|
||||
|
||||
if (PurchasedHullRepairs)
|
||||
{
|
||||
foreach (Structure wall in Structure.WallList)
|
||||
{
|
||||
if (wall.Submarine == null || wall.Submarine.IsOutpost) { continue; }
|
||||
if (wall.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(wall.Submarine))
|
||||
{
|
||||
for (int i = 0; i < wall.SectionCount; i++)
|
||||
{
|
||||
wall.AddDamage(i, -100000.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
PurchasedHullRepairs = false;
|
||||
}
|
||||
if (PurchasedItemRepairs)
|
||||
{
|
||||
foreach (Item item in Item.ItemList)
|
||||
{
|
||||
if (item.Submarine == null || item.Submarine.IsOutpost) { continue; }
|
||||
if (item.Submarine == Submarine.MainSub || Submarine.MainSub.DockedTo.Contains(item.Submarine))
|
||||
{
|
||||
if (item.GetComponent<Items.Components.Repairable>() != null)
|
||||
{
|
||||
item.Condition = item.Health;
|
||||
}
|
||||
}
|
||||
}
|
||||
PurchasedItemRepairs = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Barotrauma
|
||||
if (CheatsEnabled)
|
||||
{
|
||||
DebugConsole.CheatsEnabled = true;
|
||||
if (Steam.SteamManager.USE_STEAM && !SteamAchievementManager.CheatsEnabled)
|
||||
if (GameMain.Config.UseSteam && !SteamAchievementManager.CheatsEnabled)
|
||||
{
|
||||
SteamAchievementManager.CheatsEnabled = true;
|
||||
#if CLIENT
|
||||
|
||||
@@ -43,7 +43,6 @@ namespace Barotrauma
|
||||
public bool SpecularityEnabled { get; set; }
|
||||
public bool ChromaticAberrationEnabled { get; set; }
|
||||
|
||||
public bool PauseOnFocusLost { get; set; } = true;
|
||||
public bool MuteOnFocusLost { get; set; }
|
||||
public bool UseDirectionalVoiceChat { get; set; }
|
||||
|
||||
@@ -78,17 +77,24 @@ namespace Barotrauma
|
||||
|
||||
#if DEBUG
|
||||
//steam functionality can be enabled/disabled in debug builds
|
||||
public bool UseSteam;
|
||||
public bool RequireSteamAuthentication
|
||||
{
|
||||
get { return requireSteamAuthentication && Steam.SteamManager.USE_STEAM; }
|
||||
get { return requireSteamAuthentication && UseSteam; }
|
||||
set { requireSteamAuthentication = value; }
|
||||
}
|
||||
public bool UseSteamMatchmaking
|
||||
{
|
||||
get { return useSteamMatchmaking && Steam.SteamManager.USE_STEAM; }
|
||||
get { return useSteamMatchmaking && UseSteam; }
|
||||
set { useSteamMatchmaking = value; }
|
||||
}
|
||||
|
||||
#else
|
||||
//steam functionality determined at compile time
|
||||
public bool UseSteam
|
||||
{
|
||||
get { return Steam.SteamManager.USE_STEAM; }
|
||||
}
|
||||
public bool RequireSteamAuthentication
|
||||
{
|
||||
get { return requireSteamAuthentication && Steam.SteamManager.USE_STEAM; }
|
||||
@@ -432,6 +438,9 @@ namespace Barotrauma
|
||||
VerboseLogging = doc.Root.GetAttributeBool("verboselogging", false);
|
||||
SaveDebugConsoleLogs = doc.Root.GetAttributeBool("savedebugconsolelogs", false);
|
||||
|
||||
#if DEBUG
|
||||
UseSteam = doc.Root.GetAttributeBool("usesteam", true);
|
||||
#endif
|
||||
QuickStartSubmarineName = doc.Root.GetAttributeString("quickstartsub", "");
|
||||
|
||||
if (doc == null)
|
||||
@@ -595,14 +604,13 @@ namespace Barotrauma
|
||||
}
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
bool packageOk = contentPackage.VerifyFiles(out List<string> errorMessages);
|
||||
if (!packageOk)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\":\n" + string.Join("\n", errorMessages));
|
||||
continue;
|
||||
}
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
@@ -841,16 +849,12 @@ namespace Barotrauma
|
||||
|
||||
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
|
||||
|
||||
PauseOnFocusLost = doc.Root.GetAttributeBool("pauseonfocuslost", PauseOnFocusLost);
|
||||
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount);
|
||||
EnableMouseLook = doc.Root.GetAttributeBool("enablemouselook", EnableMouseLook);
|
||||
|
||||
CrewMenuOpen = doc.Root.GetAttributeBool("crewmenuopen", CrewMenuOpen);
|
||||
ChatOpen = doc.Root.GetAttributeBool("chatopen", ChatOpen);
|
||||
|
||||
CampaignDisclaimerShown = doc.Root.GetAttributeBool("campaigndisclaimershown", false);
|
||||
EditorDisclaimerShown = doc.Root.GetAttributeBool("editordisclaimershown", false);
|
||||
|
||||
foreach (XElement subElement in doc.Root.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
@@ -963,19 +967,24 @@ namespace Barotrauma
|
||||
|
||||
foreach (ContentPackage contentPackage in SelectedContentPackages)
|
||||
{
|
||||
bool packageOk = contentPackage.VerifyFiles(out List<string> errorMessages);
|
||||
if (!packageOk)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\":\n" + string.Join("\n", errorMessages));
|
||||
continue;
|
||||
}
|
||||
foreach (ContentFile file in contentPackage.Files)
|
||||
{
|
||||
if (!System.IO.File.Exists(file.Path))
|
||||
{
|
||||
DebugConsole.ThrowError("Error in content package \"" + contentPackage.Name + "\" - file \"" + file.Path + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
|
||||
EnsureCoreContentPackageSelected();
|
||||
if (!SelectedContentPackages.Any())
|
||||
{
|
||||
var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage);
|
||||
if (availablePackage != null)
|
||||
{
|
||||
SelectedContentPackages.Add(availablePackage);
|
||||
}
|
||||
}
|
||||
|
||||
//save to get rid of the invalid selected packages in the config file
|
||||
if (missingPackagePaths.Count > 0 || incompatiblePackages.Count > 0) { SaveNewPlayerConfig(); }
|
||||
@@ -994,25 +1003,6 @@ namespace Barotrauma
|
||||
.Replace("[gameversion]", GameMain.Version.ToString()));
|
||||
}
|
||||
}
|
||||
|
||||
public void EnsureCoreContentPackageSelected()
|
||||
{
|
||||
if (SelectedContentPackages.Any(cp => cp.CorePackage)) { return; }
|
||||
|
||||
if (GameMain.VanillaContent != null)
|
||||
{
|
||||
SelectedContentPackages.Add(GameMain.VanillaContent);
|
||||
}
|
||||
else
|
||||
{
|
||||
var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage);
|
||||
if (availablePackage != null)
|
||||
{
|
||||
SelectedContentPackages.Add(availablePackage);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Save PlayerConfig
|
||||
@@ -1039,13 +1029,10 @@ namespace Barotrauma
|
||||
new XAttribute("quickstartsub", QuickStartSubmarineName),
|
||||
new XAttribute("requiresteamauthentication", requireSteamAuthentication),
|
||||
new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems),
|
||||
new XAttribute("pauseonfocuslost", PauseOnFocusLost),
|
||||
new XAttribute("aimassistamount", aimAssistAmount),
|
||||
new XAttribute("enablemouselook", EnableMouseLook),
|
||||
new XAttribute("chatopen", ChatOpen),
|
||||
new XAttribute("crewmenuopen", CrewMenuOpen),
|
||||
new XAttribute("campaigndisclaimershown", CampaignDisclaimerShown),
|
||||
new XAttribute("editordisclaimershown", EditorDisclaimerShown));
|
||||
new XAttribute("crewmenuopen", CrewMenuOpen));
|
||||
|
||||
if (!ShowUserStatisticsPrompt)
|
||||
{
|
||||
@@ -1148,9 +1135,9 @@ namespace Barotrauma
|
||||
{
|
||||
foreach (Tutorial tutorial in Tutorial.Tutorials)
|
||||
{
|
||||
if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Identifier))
|
||||
if (tutorial.Completed && !CompletedTutorialNames.Contains(tutorial.Name))
|
||||
{
|
||||
CompletedTutorialNames.Add(tutorial.Identifier);
|
||||
CompletedTutorialNames.Add(tutorial.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -176,9 +176,6 @@ namespace Barotrauma
|
||||
{
|
||||
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item)
|
||||
{
|
||||
#if CLIENT
|
||||
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
|
||||
#endif
|
||||
if (!Items[i].AllowedSlots.Contains(InvSlotType.Any) || !TryPutItem(Items[i], character, new List<InvSlotType> { InvSlotType.Any }, true))
|
||||
{
|
||||
free = false;
|
||||
@@ -198,9 +195,6 @@ namespace Barotrauma
|
||||
{
|
||||
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] == null)
|
||||
{
|
||||
#if CLIENT
|
||||
if (PersonalSlots.HasFlag(SlotTypes[i])) { hidePersonalSlots = false; }
|
||||
#endif
|
||||
bool removeFromOtherSlots = item.ParentInventory != this;
|
||||
if (placedInSlot == -1 && inWrongSlot)
|
||||
{
|
||||
@@ -254,9 +248,7 @@ namespace Barotrauma
|
||||
GameAnalyticsManager.AddErrorEventOnce("CharacterInventory.TryPutItem:IndexOutOfRange", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return false;
|
||||
}
|
||||
#if CLIENT
|
||||
if (PersonalSlots.HasFlag(SlotTypes[index])) { hidePersonalSlots = false; }
|
||||
#endif
|
||||
|
||||
//there's already an item in the slot
|
||||
if (Items[index] != null)
|
||||
{
|
||||
@@ -281,9 +273,7 @@ namespace Barotrauma
|
||||
foreach (InvSlotType allowedSlot in allowedSlots)
|
||||
{
|
||||
if (!allowedSlot.HasFlag(SlotTypes[index])) continue;
|
||||
#if CLIENT
|
||||
if (PersonalSlots.HasFlag(allowedSlot)) { hidePersonalSlots = false; }
|
||||
#endif
|
||||
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (allowedSlot.HasFlag(SlotTypes[i]) && Items[i] != null && Items[i] != item)
|
||||
|
||||
@@ -219,29 +219,31 @@ namespace Barotrauma.Items.Components
|
||||
private bool hasValidIdCard;
|
||||
public override bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
{
|
||||
if (item.Condition <= RepairThreshold) return true; //For repairing
|
||||
|
||||
var idCard = character.Inventory.FindItemByIdentifier("idcard");
|
||||
hasValidIdCard = requiredItems.Any(ri => ri.Value.Any(r => r.MatchesItem(idCard)));
|
||||
Msg = requiredItems.None() || hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
|
||||
Msg = hasValidIdCard ? "ItemMsgOpen" : "ItemMsgForceOpenCrowbar";
|
||||
ParseMsg();
|
||||
if (addMessage)
|
||||
{
|
||||
msg = msg ?? (HasIntegratedButtons ? accessDeniedTxt : cannotOpenText);
|
||||
msg = msg ?? (requiredItems.Any(ri => ri.Value.Any(r => r.Identifiers.Contains("idcard"))) ? accessDeniedTxt : cannotOpenText);
|
||||
}
|
||||
if (isBroken) { return true; }
|
||||
return base.HasRequiredItems(character, addMessage, msg);
|
||||
|
||||
//this is a bit pointless atm because if canBePicked is false it won't allow you to do Pick() anyway, however it's still good for future-proofing.
|
||||
return requiredItems.Any() ? base.HasRequiredItems(character, addMessage, msg) : canBePicked;
|
||||
}
|
||||
|
||||
public override bool Pick(Character picker)
|
||||
{
|
||||
if (item.Condition <= RepairThreshold) { return true; }
|
||||
if (requiredItems.None()) { return false; }
|
||||
if (HasRequiredItems(picker, false) && hasValidIdCard) { return false; }
|
||||
return base.Pick(picker);
|
||||
}
|
||||
|
||||
public override bool OnPicked(Character picker)
|
||||
{
|
||||
if (item.Condition <= RepairThreshold) { return true; }
|
||||
if (item.Condition <= RepairThreshold) return true; //repairs
|
||||
if (requiredItems.Any() && !hasValidIdCard)
|
||||
{
|
||||
ForceOpen(ActionType.OnPicked);
|
||||
@@ -259,24 +261,23 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (!isBroken)
|
||||
//can only be selected if the item is broken
|
||||
if (item.Condition <= RepairThreshold) return true; //repairs
|
||||
bool hasRequiredItems = HasRequiredItems(character, false);
|
||||
if (requiredItems.None() || hasRequiredItems && hasValidIdCard)
|
||||
{
|
||||
bool hasRequiredItems = HasRequiredItems(character, false);
|
||||
if (requiredItems.None() || hasRequiredItems && hasValidIdCard)
|
||||
{
|
||||
float originalPickingTime = PickingTime;
|
||||
PickingTime = 0;
|
||||
ForceOpen(ActionType.OnUse);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
else if (hasRequiredItems)
|
||||
{
|
||||
#if CLIENT
|
||||
GUI.AddMessage(accessDeniedTxt, Color.Red);
|
||||
#endif
|
||||
}
|
||||
float originalPickingTime = PickingTime;
|
||||
PickingTime = 0;
|
||||
ForceOpen(ActionType.OnUse);
|
||||
PickingTime = originalPickingTime;
|
||||
}
|
||||
return item.Condition <= RepairThreshold;
|
||||
else if (hasRequiredItems)
|
||||
{
|
||||
#if CLIENT
|
||||
GUI.AddMessage(accessDeniedTxt, Color.Red);
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
|
||||
@@ -122,12 +122,10 @@ namespace Barotrauma.Items.Components
|
||||
foreach (Item subItem in containedSubItems)
|
||||
{
|
||||
projectile = subItem.GetComponent<Projectile>();
|
||||
|
||||
//apply OnUse statuseffects to the container in case it has to react to it somehow
|
||||
//(play a sound, spawn more projectiles, reduce condition...)
|
||||
if (subItem.Condition > 0.0f)
|
||||
{
|
||||
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
|
||||
}
|
||||
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
|
||||
if (projectile != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -293,35 +293,10 @@ namespace Barotrauma.Items.Components
|
||||
//steer closer if almost in range
|
||||
if (dist > Range)
|
||||
{
|
||||
Vector2 standPos = new Vector2(Math.Sign(-fromItemToLeak.X), Math.Sign(-fromItemToLeak.Y)) / 2;
|
||||
if (!character.AnimController.InWater)
|
||||
{
|
||||
if (leak.IsHorizontal)
|
||||
{
|
||||
standPos.X *= 2;
|
||||
standPos.Y = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
standPos.X = 0;
|
||||
}
|
||||
}
|
||||
if (character.AIController.SteeringManager is IndoorsSteeringManager indoorSteering)
|
||||
{
|
||||
if (indoorSteering.CurrentPath != null && !indoorSteering.IsPathDirty && indoorSteering.CurrentPath.Unreachable)
|
||||
{
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character.AIController.SteeringManager.SteeringSeek(standPos);
|
||||
}
|
||||
Vector2 standPos = leak.IsHorizontal ? new Vector2(Math.Sign(-fromItemToLeak.X), 0.0f) : new Vector2(0.0f, Math.Sign(-fromItemToLeak.Y) * 0.5f);
|
||||
standPos = leak.WorldPosition + standPos * Range;
|
||||
Vector2 dir = Vector2.Normalize(standPos - character.WorldPosition);
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, dir / 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -330,29 +305,30 @@ namespace Barotrauma.Items.Components
|
||||
// Too close -> steer away
|
||||
character.AIController.SteeringManager.SteeringManual(deltaTime, Vector2.Normalize(character.SimPosition - leak.SimPosition) / 2);
|
||||
}
|
||||
else if (dist <= Range)
|
||||
{
|
||||
// In range
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
character.AIController.SteeringManager.Reset();
|
||||
}
|
||||
}
|
||||
|
||||
sinTime += deltaTime;
|
||||
character.CursorPosition = leak.Position + VectorExtensions.Forward(Item.body.TransformedRotation + (float)Math.Sin(sinTime), dist);
|
||||
if (item.RequireAimToUse)
|
||||
{
|
||||
character.SetInput(InputType.Aim, false, true);
|
||||
}
|
||||
|
||||
// Press the trigger only when the tool is approximately facing the target.
|
||||
var angle = VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak);
|
||||
if (angle < MathHelper.PiOver4)
|
||||
// If the character is climbing, ignore the check, because we cannot aim while climbing.
|
||||
if (VectorExtensions.Angle(VectorExtensions.Forward(item.body.TransformedRotation), fromItemToLeak) < MathHelper.PiOver4)
|
||||
{
|
||||
character.SetInput(InputType.Shoot, false, true);
|
||||
Use(deltaTime, character);
|
||||
}
|
||||
else
|
||||
{
|
||||
sinTime -= deltaTime * 2;
|
||||
}
|
||||
|
||||
bool leakFixed = (leak.Open <= 0.0f || leak.Removed) &&
|
||||
(leak.ConnectedWall == null || leak.ConnectedWall.Sections.Average(s => s.damage) < 1);
|
||||
@@ -362,11 +338,11 @@ namespace Barotrauma.Items.Components
|
||||
sinTime = 0;
|
||||
if (!leak.FlowTargetHull.ConnectedGaps.Any(g => !g.IsRoomToRoom && g.Open > 0.0f))
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leaksfixed", 10.0f);
|
||||
character.Speak(TextManager.Get("DialogLeaksFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leaksfixed", 10.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.DisplayName), null, 0.0f, "leakfixed", 10.0f);
|
||||
character.Speak(TextManager.Get("DialogLeakFixed").Replace("[roomname]", leak.FlowTargetHull.RoomName), null, 0.0f, "leakfixed", 10.0f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return name; }
|
||||
}
|
||||
|
||||
[Editable, Serialize("", true, translationTextTag: "ItemMsg")]
|
||||
[Editable, Serialize("", true)]
|
||||
public string Msg
|
||||
{
|
||||
get;
|
||||
@@ -580,8 +580,8 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
public virtual bool HasRequiredItems(Character character, bool addMessage, string msg = null)
|
||||
{
|
||||
if (!requiredItems.Any()) { return true; }
|
||||
if (character.Inventory == null) { return false; }
|
||||
if (!requiredItems.Any()) return true;
|
||||
if (character.Inventory == null) return false;
|
||||
bool hasRequiredItems = false;
|
||||
bool canContinue = true;
|
||||
if (requiredItems.ContainsKey(RelatedItem.RelationType.Equipped))
|
||||
@@ -615,15 +615,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
bool Predicate(Item it) => it != null && it.Condition > 0.0f && relatedItem.MatchesItem(it);
|
||||
bool shouldBreak = false;
|
||||
bool inEditor = false;
|
||||
#if CLIENT
|
||||
inEditor = Screen.Selected == GameMain.SubEditorScreen;
|
||||
#endif
|
||||
if (relatedItem.IgnoreInEditor && inEditor)
|
||||
{
|
||||
hasRequiredItems = true;
|
||||
}
|
||||
else if (relatedItem.IsOptional)
|
||||
if (relatedItem.IsOptional)
|
||||
{
|
||||
if (!hasRequiredItems)
|
||||
{
|
||||
@@ -792,7 +784,6 @@ namespace Barotrauma.Items.Components
|
||||
newRequiredItem.statusEffects = prevRequiredItem.statusEffects;
|
||||
newRequiredItem.Msg = prevRequiredItem.Msg;
|
||||
newRequiredItem.IsOptional = prevRequiredItem.IsOptional;
|
||||
newRequiredItem.IgnoreInEditor = prevRequiredItem.IgnoreInEditor;
|
||||
}
|
||||
|
||||
if (!requiredItems.ContainsKey(newRequiredItem.Type))
|
||||
@@ -810,7 +801,10 @@ namespace Barotrauma.Items.Components
|
||||
string msg = TextManager.Get(Msg, true);
|
||||
if (msg != null)
|
||||
{
|
||||
msg = TextManager.ParseInputTypes(msg);
|
||||
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
|
||||
{
|
||||
msg = msg.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
|
||||
}
|
||||
DisplayMsg = msg;
|
||||
}
|
||||
else
|
||||
|
||||
@@ -13,11 +13,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
public ItemContainer InputContainer
|
||||
{
|
||||
get { return inputContainer; }
|
||||
}
|
||||
|
||||
public ItemContainer OutputContainer
|
||||
{
|
||||
get { return outputContainer; }
|
||||
@@ -76,10 +71,8 @@ namespace Barotrauma.Items.Components
|
||||
var targetItem = inputContainer.Inventory.Items.LastOrDefault(i => i != null);
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime : 1.0f;
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
if (progressTimer > deconstructTime)
|
||||
progressState = Math.Min(progressTimer / targetItem.Prefab.DeconstructTime, 1.0f);
|
||||
if (progressTimer > targetItem.Prefab.DeconstructTime)
|
||||
{
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
{
|
||||
@@ -107,24 +100,10 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (outputContainer.Inventory.Items.All(i => i != null))
|
||||
{
|
||||
targetItem.Drop(dropper: null);
|
||||
}
|
||||
else
|
||||
{
|
||||
outputContainer.Inventory.TryPutItem(targetItem, user: null, createNetworkEvent: true);
|
||||
}
|
||||
}
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
|
||||
if (inputContainer.Inventory.Items.Any(i => i != null))
|
||||
{
|
||||
|
||||
@@ -23,16 +23,6 @@ namespace Barotrauma.Items.Components
|
||||
|
||||
private ItemContainer inputContainer, outputContainer;
|
||||
|
||||
public ItemContainer InputContainer
|
||||
{
|
||||
get { return inputContainer; }
|
||||
}
|
||||
|
||||
public ItemContainer OutputContainer
|
||||
{
|
||||
get { return outputContainer; }
|
||||
}
|
||||
|
||||
private float progressState;
|
||||
|
||||
public Fabricator(Item item, XElement element)
|
||||
@@ -108,23 +98,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
return (picker != null);
|
||||
}
|
||||
|
||||
public void RemoveFabricationRecipes(List<string> allowedIdentifiers)
|
||||
{
|
||||
for (int i = 0; i < fabricationRecipes.Count; i++)
|
||||
{
|
||||
if (!allowedIdentifiers.Contains(fabricationRecipes[i].TargetItem.Identifier))
|
||||
{
|
||||
fabricationRecipes.RemoveAt(i);
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
CreateRecipes();
|
||||
}
|
||||
|
||||
partial void CreateRecipes();
|
||||
|
||||
|
||||
private void StartFabricating(FabricationRecipe selectedItem, Character user)
|
||||
{
|
||||
if (selectedItem == null) return;
|
||||
|
||||
@@ -473,8 +473,6 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return false; }
|
||||
|
||||
IsActive = true;
|
||||
|
||||
float degreeOfSuccess = DegreeOfSuccess(character);
|
||||
|
||||
//characters with insufficient skill levels don't refuel the reactor
|
||||
|
||||
@@ -47,9 +47,8 @@ namespace Barotrauma.Items.Components
|
||||
//was the last ping sent with directional pinging
|
||||
private bool isLastPingDirectional;
|
||||
|
||||
private Sprite pingCircle, directionalPingCircle, screenOverlay, screenBackground;
|
||||
private Sprite sonarBlip;
|
||||
private Sprite lineSprite;
|
||||
private readonly Sprite pingCircle, directionalPingCircle, screenOverlay, screenBackground;
|
||||
private readonly Sprite sonarBlip;
|
||||
|
||||
private bool aiPingCheckPending;
|
||||
|
||||
@@ -86,7 +85,7 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
get { return zoom; }
|
||||
}
|
||||
|
||||
|
||||
public override bool IsActive
|
||||
{
|
||||
get
|
||||
@@ -112,7 +111,29 @@ namespace Barotrauma.Items.Components
|
||||
: base(item, element)
|
||||
{
|
||||
connectedTransducers = new List<ConnectedTransducer>();
|
||||
|
||||
|
||||
foreach (XElement subElement in element.Elements())
|
||||
{
|
||||
switch (subElement.Name.ToString().ToLowerInvariant())
|
||||
{
|
||||
case "pingcircle":
|
||||
pingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "directionalpingcircle":
|
||||
directionalPingCircle = new Sprite(subElement);
|
||||
break;
|
||||
case "screenoverlay":
|
||||
screenOverlay = new Sprite(subElement);
|
||||
break;
|
||||
case "screenbackground":
|
||||
screenBackground = new Sprite(subElement);
|
||||
break;
|
||||
case "blip":
|
||||
sonarBlip = new Sprite(subElement);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
IsActive = false;
|
||||
InitProjSpecific(element);
|
||||
}
|
||||
@@ -184,7 +205,6 @@ namespace Barotrauma.Items.Components
|
||||
directionalPingCircle?.Remove();
|
||||
screenOverlay?.Remove();
|
||||
screenBackground?.Remove();
|
||||
lineSprite?.Remove();
|
||||
}
|
||||
|
||||
public override bool AIOperate(float deltaTime, Character character, AIObjectiveOperateItem objective)
|
||||
@@ -239,24 +259,15 @@ namespace Barotrauma.Items.Components
|
||||
int clockDir = (int)Math.Round((angle / MathHelper.TwoPi) * 12);
|
||||
if (clockDir == 0) clockDir = 12;
|
||||
|
||||
return TextManager.Get("roomname.subdiroclock").Replace("[dir]", clockDir.ToString());
|
||||
return TextManager.Get("SubDirOClock").Replace("[dir]", clockDir.ToString());
|
||||
}
|
||||
|
||||
private Vector2 GetTransducerPos()
|
||||
private Vector2 GetTransducerCenter()
|
||||
{
|
||||
if (!UseTransducers || connectedTransducers.Count == 0)
|
||||
{
|
||||
//use the position of the sub if the item is static (no body) and inside a sub
|
||||
return item.Submarine != null && item.body == null ? item.Submarine.WorldPosition : item.WorldPosition;
|
||||
}
|
||||
|
||||
if (!UseTransducers || connectedTransducers.Count == 0) return Vector2.Zero;
|
||||
Vector2 transducerPosSum = Vector2.Zero;
|
||||
foreach (ConnectedTransducer transducer in connectedTransducers)
|
||||
{
|
||||
if (transducer.Transducer.Item.Submarine != null)
|
||||
{
|
||||
return transducer.Transducer.Item.Submarine.WorldPosition;
|
||||
}
|
||||
transducerPosSum += transducer.Transducer.Item.WorldPosition;
|
||||
}
|
||||
return transducerPosSum / connectedTransducers.Count;
|
||||
|
||||
@@ -172,6 +172,19 @@ namespace Barotrauma.Items.Components
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void OnItemLoaded()
|
||||
{
|
||||
sonar = item.GetComponent<Sonar>();
|
||||
}
|
||||
|
||||
public override bool Select(Character character)
|
||||
{
|
||||
if (!CanBeSelected) return false;
|
||||
|
||||
user = character;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
{
|
||||
networkUpdateTimer -= deltaTime;
|
||||
@@ -474,9 +487,7 @@ namespace Barotrauma.Items.Components
|
||||
if (!posToMaintain.HasValue)
|
||||
{
|
||||
unsentChanges = true;
|
||||
posToMaintain = controlledSub != null ?
|
||||
controlledSub.WorldPosition :
|
||||
item.Submarine == null ? item.WorldPosition : item.Submarine.WorldPosition;
|
||||
posToMaintain = controlledSub == null ? item.WorldPosition : controlledSub.WorldPosition;
|
||||
}
|
||||
|
||||
if (!AutoPilot || !MaintainPos) unsentChanges = true;
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma.Items.Components
|
||||
if (sparkSounds.Count > 0)
|
||||
{
|
||||
var sparkSound = sparkSounds[Rand.Int(sparkSounds.Count)];
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, pt.item.WorldPosition, sparkSound.Volume, sparkSound.Range, pt.item.CurrentHull);
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, sparkSound.Volume, sparkSound.Range, pt.item.WorldPosition, pt.item.CurrentHull);
|
||||
}
|
||||
|
||||
Vector2 baseVel = Rand.Vector(300.0f);
|
||||
|
||||
@@ -103,7 +103,7 @@ namespace Barotrauma.Items.Components
|
||||
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
|
||||
if (!powerOnSoundPlayed && powerOnSound != null)
|
||||
{
|
||||
SoundPlayer.PlaySound(powerOnSound.Sound, item.WorldPosition, powerOnSound.Volume, powerOnSound.Range, item.CurrentHull);
|
||||
SoundPlayer.PlaySound(powerOnSound.Sound, powerOnSound.Volume, powerOnSound.Range, item.WorldPosition, item.CurrentHull);
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -241,12 +241,5 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
character.AnimController.UpdateUseItem(false, item.WorldPosition + new Vector2(0.0f, 100.0f) * ((item.Condition / item.MaxCondition) % 0.1f));
|
||||
}
|
||||
|
||||
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0, float signalStrength = 1)
|
||||
{
|
||||
//do nothing
|
||||
//Repairables should always stay active, so we don't want to use the default behavior
|
||||
//where set_active/set_state signals can disable the component
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,19 +8,11 @@ namespace Barotrauma.Items.Components
|
||||
{
|
||||
partial class CustomInterface : ItemComponent, IClientSerializable, IServerSerializable
|
||||
{
|
||||
class CustomInterfaceElement : ISerializableEntity
|
||||
class CustomInterfaceElement
|
||||
{
|
||||
public bool ContinuousSignal;
|
||||
public bool State;
|
||||
public string Connection;
|
||||
[Serialize("", false, translationTextTag = "Label.")]
|
||||
public string Label { get; set; }
|
||||
[Serialize("1", false)]
|
||||
public string Signal { get; set; }
|
||||
|
||||
public string Name => "CustomInterfaceElement";
|
||||
|
||||
public Dictionary<string, SerializableProperty> SerializableProperties { get; set; }
|
||||
public string Label, Connection, Signal;
|
||||
|
||||
public List<StatusEffect> StatusEffects = new List<StatusEffect>();
|
||||
|
||||
@@ -41,7 +33,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
|
||||
private string[] labels;
|
||||
[Serialize("", true)]
|
||||
[Serialize("", true), Editable()]
|
||||
public string Labels
|
||||
{
|
||||
get { return string.Join(",", labels); }
|
||||
@@ -56,7 +48,7 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
private string[] signals;
|
||||
[Serialize("", true)]
|
||||
[Serialize("", true), Editable()]
|
||||
public string Signals
|
||||
{
|
||||
//use semicolon as a separator because comma may be needed in the signals (for color or vector values for example)
|
||||
@@ -125,7 +117,7 @@ namespace Barotrauma.Items.Components
|
||||
for (int i = 0; i < labels.Length; i++)
|
||||
{
|
||||
labels[i] = i < newLabels.Length ? newLabels[i] : customInterfaceElementList[i].Label;
|
||||
customInterfaceElementList[i].Label = TextManager.Get(labels[i], returnNull: true) ?? labels[i];
|
||||
customInterfaceElementList[i].Label = labels[i];
|
||||
}
|
||||
UpdateLabelsProjSpecific();
|
||||
}
|
||||
@@ -170,12 +162,5 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override XElement Save(XElement parentElement)
|
||||
{
|
||||
labels = customInterfaceElementList.Select(ci => ci.Label).ToArray();
|
||||
signals = customInterfaceElementList.Select(ci => ci.Signal).ToArray();
|
||||
return base.Save(parentElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ namespace Barotrauma.Items.Components
|
||||
if (voltage > 0.1f && sparkSounds.Count > 0)
|
||||
{
|
||||
var sparkSound = sparkSounds[Rand.Int(sparkSounds.Count)];
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, item.WorldPosition, sparkSound.Volume, sparkSound.Range, item.CurrentHull);
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, sparkSound.Volume, sparkSound.Range, item.WorldPosition, item.CurrentHull);
|
||||
}
|
||||
#endif
|
||||
lightBrightness = 0.0f;
|
||||
|
||||
@@ -5,7 +5,6 @@ using System.IO;
|
||||
using System.Linq;
|
||||
using System.Xml.Linq;
|
||||
using Barotrauma.Items.Components;
|
||||
using Barotrauma.Extensions;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -67,8 +66,6 @@ namespace Barotrauma
|
||||
|
||||
public LightComponent LightComponent { get; set; }
|
||||
|
||||
public int Variant { get; set; }
|
||||
|
||||
private Gender _gender;
|
||||
/// <summary>
|
||||
/// None = Any/Not Defined -> no effect.
|
||||
@@ -115,65 +112,30 @@ namespace Barotrauma
|
||||
/// <summary>
|
||||
/// Note: this constructor cannot initialize automatically, because the gender is unknown at this point. We only know it when the item is equipped.
|
||||
/// </summary>
|
||||
public WearableSprite(XElement subElement, Wearable wearable, int variant = 0)
|
||||
public WearableSprite(XElement subElement, Wearable wearable)
|
||||
{
|
||||
Type = WearableType.Item;
|
||||
WearableComponent = wearable;
|
||||
Variant = Math.Max(variant, 0);
|
||||
SpritePath = ParseSpritePath(subElement.GetAttributeString("texture", string.Empty));
|
||||
SourceElement = subElement;
|
||||
}
|
||||
|
||||
private string ParseSpritePath(string texturePath) => texturePath.Contains("/") ? texturePath : $"{Path.GetDirectoryName(WearableComponent.Item.Prefab.ConfigFile)}/{texturePath}";
|
||||
|
||||
public void RefreshPath()
|
||||
{
|
||||
if (Variant > 0)
|
||||
{
|
||||
// Restore the tag so that we can parse it again.
|
||||
ReplaceNumbersWith("[VARIANT]");
|
||||
}
|
||||
ParsePath(true);
|
||||
}
|
||||
|
||||
private void ReplaceNumbersWith(string replacement)
|
||||
{
|
||||
var fileName = Path.GetFileName(SpritePath);
|
||||
var path = Path.GetDirectoryName(SpritePath);
|
||||
fileName = fileName.Replace(replacement, c => char.IsNumber(c));
|
||||
SpritePath = Path.Combine(path, fileName);
|
||||
}
|
||||
|
||||
private void ParsePath(bool parseSpritePath)
|
||||
{
|
||||
if (_gender != Gender.None)
|
||||
{
|
||||
SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
|
||||
}
|
||||
SpritePath = SpritePath.Replace("[VARIANT]", Variant.ToString());
|
||||
if (!File.Exists(SpritePath))
|
||||
{
|
||||
// If the variant does not exist, parse the path so that it uses first variant.
|
||||
Variant = 1;
|
||||
ReplaceNumbersWith(Variant.ToString());
|
||||
}
|
||||
if (parseSpritePath)
|
||||
{
|
||||
Sprite.ParseTexturePath(file: SpritePath);
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsInitialized { get; private set; }
|
||||
public void Init(Gender gender = Gender.None)
|
||||
{
|
||||
if (IsInitialized) { return; }
|
||||
_gender = SpritePath.Contains("[GENDER]") ? gender : Gender.None;
|
||||
ParsePath(false);
|
||||
if (_gender != Gender.None)
|
||||
{
|
||||
SpritePath = SpritePath.Replace("[GENDER]", (_gender == Gender.Female) ? "female" : "male");
|
||||
}
|
||||
if (Sprite != null)
|
||||
{
|
||||
Sprite.Remove();
|
||||
}
|
||||
Sprite = new Sprite(SourceElement, file: SpritePath);
|
||||
Sprite = new Sprite(SourceElement, "", SpritePath);
|
||||
Limb = (LimbType)Enum.Parse(typeof(LimbType), SourceElement.GetAttributeString("limb", "Head"), true);
|
||||
HideLimb = SourceElement.GetAttributeBool("hidelimb", false);
|
||||
HideOtherWearables = SourceElement.GetAttributeBool("hideotherwearables", false);
|
||||
@@ -208,7 +170,7 @@ namespace Barotrauma.Items.Components
|
||||
get { return damageModifiers; }
|
||||
}
|
||||
|
||||
public Wearable(Item item, XElement element) : base(item, element)
|
||||
public Wearable (Item item, XElement element) : base(item, element)
|
||||
{
|
||||
this.item = item;
|
||||
|
||||
@@ -235,7 +197,7 @@ namespace Barotrauma.Items.Components
|
||||
limbType[i] = (LimbType)Enum.Parse(typeof(LimbType),
|
||||
subElement.GetAttributeString("limb", "Head"), true);
|
||||
|
||||
wearableSprites[i] = new WearableSprite(subElement, this, variant);
|
||||
wearableSprites[i] = new WearableSprite(subElement, this);
|
||||
|
||||
foreach (XElement lightElement in subElement.Elements())
|
||||
{
|
||||
|
||||
@@ -206,16 +206,6 @@ namespace Barotrauma
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool IsFull()
|
||||
{
|
||||
for (int i = 0; i < capacity; i++)
|
||||
{
|
||||
if (Items[i] == null) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected bool TrySwapping(int index, Item item, Character user, bool createNetworkEvent)
|
||||
{
|
||||
if (item?.ParentInventory == null || Items[index] == null) return false;
|
||||
|
||||
@@ -928,9 +928,14 @@ namespace Barotrauma
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
|
||||
{
|
||||
if (!hasStatusEffectsOfType[(int)type]) { return; }
|
||||
foreach (StatusEffect effect in statusEffectLists[type])
|
||||
if (statusEffectLists == null) return;
|
||||
|
||||
if (!statusEffectLists.TryGetValue(type, out List<StatusEffect> statusEffects)) return;
|
||||
|
||||
bool broken = condition <= 0.0f;
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
ApplyStatusEffect(effect, type, deltaTime, character, limb, isNetworkEvent, false);
|
||||
}
|
||||
}
|
||||
@@ -1047,8 +1052,6 @@ namespace Barotrauma
|
||||
aiTarget.SoundRange -= deltaTime * 1000.0f;
|
||||
}
|
||||
|
||||
bool broken = condition <= 0.0f;
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
sendConditionUpdateTimer -= deltaTime;
|
||||
@@ -1124,10 +1127,7 @@ namespace Barotrauma
|
||||
container = container.Container;
|
||||
}
|
||||
}
|
||||
if (!broken)
|
||||
{
|
||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||
}
|
||||
ApplyStatusEffects(!waterProof && inWater ? ActionType.InWater : ActionType.NotInWater, deltaTime);
|
||||
|
||||
if (body == null || !body.Enabled || !inWater || ParentInventory != null || Removed) { return; }
|
||||
|
||||
@@ -1149,10 +1149,6 @@ namespace Barotrauma
|
||||
{
|
||||
body.SetTransform(body.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
else if (Submarine != null && prevSub != null && Submarine != prevSub)
|
||||
{
|
||||
body.SetTransform(body.SimPosition + prevSub.SimPosition - Submarine.SimPosition, body.Rotation);
|
||||
}
|
||||
|
||||
Vector2 displayPos = ConvertUnits.ToDisplayUnits(body.SimPosition);
|
||||
rect.X = (int)(displayPos.X - rect.Width / 2.0f);
|
||||
@@ -1210,7 +1206,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return true; }
|
||||
|
||||
if (ImpactTolerance > 0.0f && condition > 0.0f && impact > ImpactTolerance)
|
||||
if (ImpactTolerance > 0.0f && impact > ImpactTolerance)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f);
|
||||
#if SERVER
|
||||
@@ -1269,7 +1265,7 @@ namespace Barotrauma
|
||||
|
||||
if (recursive)
|
||||
{
|
||||
HashSet<Connection> alreadySearched = new HashSet<Connection>();
|
||||
List<Item> alreadySearched = new List<Item>() { this };
|
||||
GetConnectedComponentsRecursive(alreadySearched, connectedComponents);
|
||||
|
||||
return connectedComponents;
|
||||
@@ -1291,7 +1287,7 @@ namespace Barotrauma
|
||||
return connectedComponents;
|
||||
}
|
||||
|
||||
private void GetConnectedComponentsRecursive<T>(HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
private void GetConnectedComponentsRecursive<T>(List<Item> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
{
|
||||
ConnectionPanel connectionPanel = GetComponent<ConnectionPanel>();
|
||||
if (connectionPanel == null) { return; }
|
||||
@@ -1315,35 +1311,28 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive<T>(alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public List<T> GetConnectedComponentsRecursive<T>(Connection c) where T : ItemComponent
|
||||
{
|
||||
List<T> connectedComponents = new List<T>();
|
||||
HashSet<Connection> alreadySearched = new HashSet<Connection>();
|
||||
List<T> connectedComponents = new List<T>();
|
||||
List<Item> alreadySearched = new List<Item>() { this };
|
||||
GetConnectedComponentsRecursive(c, alreadySearched, connectedComponents);
|
||||
|
||||
return connectedComponents;
|
||||
}
|
||||
|
||||
private static readonly Pair<string, string>[] connectionPairs = new Pair<string, string>[]
|
||||
{
|
||||
new Pair<string, string>("power_in", "power_out"),
|
||||
new Pair<string, string>("signal_in1", "signal_out1"),
|
||||
new Pair<string, string>("signal_in2", "signal_out2"),
|
||||
new Pair<string, string>("signal_in3", "signal_out3"),
|
||||
new Pair<string, string>("signal_in4", "signal_out4"),
|
||||
new Pair<string, string>("signal_in", "signal_out"),
|
||||
new Pair<string, string>("signal_in1", "signal_out"),
|
||||
new Pair<string, string>("signal_in2", "signal_out")
|
||||
};
|
||||
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, HashSet<Connection> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
private void GetConnectedComponentsRecursive<T>(Connection c, List<Item> alreadySearched, List<T> connectedComponents) where T : ItemComponent
|
||||
{
|
||||
alreadySearched.Add(c);
|
||||
alreadySearched.Add(this);
|
||||
|
||||
var recipients = c.Recipients;
|
||||
foreach (Connection recipient in recipients)
|
||||
{
|
||||
if (alreadySearched.Contains(recipient)) { continue; }
|
||||
if (alreadySearched.Contains(recipient.Item)) continue;
|
||||
|
||||
var component = recipient.Item.GetComponent<T>();
|
||||
if (component != null)
|
||||
@@ -1352,29 +1341,7 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
recipient.Item.GetConnectedComponentsRecursive(recipient, alreadySearched, connectedComponents);
|
||||
}
|
||||
|
||||
foreach (Pair<string, string> connectionPair in connectionPairs)
|
||||
{
|
||||
if (connectionPair.First == c.Name)
|
||||
{
|
||||
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == connectionPair.Second);
|
||||
if (pairedConnection != null)
|
||||
{
|
||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
else if (connectionPair.Second == c.Name)
|
||||
{
|
||||
var pairedConnection = c.Item.Connections.FirstOrDefault(c2 => c2.Name == connectionPair.First);
|
||||
if (pairedConnection != null)
|
||||
{
|
||||
if (alreadySearched.Contains(pairedConnection)) { continue; }
|
||||
GetConnectedComponentsRecursive(pairedConnection, alreadySearched, connectedComponents);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -138,6 +138,16 @@ namespace Barotrauma
|
||||
|
||||
private Dictionary<string, PriceInfo> prices;
|
||||
|
||||
//an area next to the construction
|
||||
//the construction can be Activated() by a Character inside the area
|
||||
public List<Rectangle> Triggers;
|
||||
|
||||
private List<XElement> fabricationRecipeElements = new List<XElement>();
|
||||
|
||||
private bool canSpriteFlipX, canSpriteFlipY;
|
||||
|
||||
private Dictionary<string, PriceInfo> prices;
|
||||
|
||||
/// <summary>
|
||||
/// Defines areas where the item can be interacted with. If RequireBodyInsideTrigger is set to true, the character
|
||||
/// has to be within the trigger to interact. If it's set to false, having the cursor within the trigger is enough.
|
||||
@@ -460,11 +470,11 @@ namespace Barotrauma
|
||||
DeconstructItems = new List<DeconstructItem>();
|
||||
FabricationRecipes = new List<FabricationRecipe>();
|
||||
DeconstructTime = 1.0f;
|
||||
|
||||
Tags = new HashSet<string>(element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true));
|
||||
|
||||
Tags = element.GetAttributeStringArray("tags", new string[0], convertToLowerInvariant: true).ToHashSet();
|
||||
if (Tags.None())
|
||||
{
|
||||
Tags = new HashSet<string>(element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true));
|
||||
Tags = element.GetAttributeStringArray("Tags", new string[0], convertToLowerInvariant: true).ToHashSet();
|
||||
}
|
||||
|
||||
if (element.Attribute("cargocontainername") != null)
|
||||
@@ -626,25 +636,10 @@ namespace Barotrauma
|
||||
|
||||
string treatmentIdentifier = subElement.GetAttributeString("identifier", "").ToLowerInvariant();
|
||||
|
||||
List<AfflictionPrefab> matchingAfflictions = AfflictionPrefab.List.FindAll(a => a.Identifier == treatmentIdentifier || a.AfflictionType == treatmentIdentifier);
|
||||
if (matchingAfflictions.Count == 0)
|
||||
var matchingAffliction = AfflictionPrefab.List.Find(a => a.Identifier == treatmentIdentifier);
|
||||
if (matchingAffliction != null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in item prefab \"" + Name + "\" - couldn't define as a treatment, no treatments with the identifier or type \"" + treatmentIdentifier + "\" were found.");
|
||||
continue;
|
||||
}
|
||||
|
||||
float suitability = subElement.GetAttributeFloat("suitability", 0.0f);
|
||||
foreach (AfflictionPrefab matchingAffliction in matchingAfflictions)
|
||||
{
|
||||
if (matchingAffliction.TreatmentSuitability.ContainsKey(identifier))
|
||||
{
|
||||
matchingAffliction.TreatmentSuitability[identifier] =
|
||||
Math.Max(matchingAffliction.TreatmentSuitability[identifier], suitability);
|
||||
}
|
||||
else
|
||||
{
|
||||
matchingAffliction.TreatmentSuitability.Add(identifier, suitability);
|
||||
}
|
||||
matchingAffliction.TreatmentSuitability.Add(identifier, subElement.GetAttributeFloat("suitability", 0.0f));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -17,8 +17,8 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
public bool IsOptional { get; set; }
|
||||
|
||||
public bool IgnoreInEditor { get; set; }
|
||||
|
||||
private string[] identifiers;
|
||||
|
||||
private string[] excludedIdentifiers;
|
||||
|
||||
@@ -35,20 +35,23 @@ namespace Barotrauma
|
||||
|
||||
public string JoinedIdentifiers
|
||||
{
|
||||
get { return string.Join(",", Identifiers); }
|
||||
get { return string.Join(",", identifiers); }
|
||||
set
|
||||
{
|
||||
if (value == null) return;
|
||||
|
||||
Identifiers = value.Split(',');
|
||||
for (int i = 0; i < Identifiers.Length; i++)
|
||||
identifiers = value.Split(',');
|
||||
for (int i = 0; i < identifiers.Length; i++)
|
||||
{
|
||||
Identifiers[i] = Identifiers[i].Trim().ToLowerInvariant();
|
||||
identifiers[i] = identifiers[i].Trim().ToLowerInvariant();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public string[] Identifiers { get; private set; }
|
||||
|
||||
public string[] Identifiers
|
||||
{
|
||||
get { return identifiers; }
|
||||
}
|
||||
|
||||
public string JoinedExcludedIdentifiers
|
||||
{
|
||||
@@ -69,7 +72,7 @@ namespace Barotrauma
|
||||
{
|
||||
if (item == null) return false;
|
||||
if (excludedIdentifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id))) return false;
|
||||
return Identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
|
||||
return identifiers.Any(id => item.Prefab.Identifier == id || item.HasTag(id));
|
||||
}
|
||||
|
||||
public RelatedItem(string[] identifiers, string[] excludedIdentifiers)
|
||||
@@ -78,7 +81,7 @@ namespace Barotrauma
|
||||
{
|
||||
identifiers[i] = identifiers[i].Trim().ToLowerInvariant();
|
||||
}
|
||||
this.Identifiers = identifiers;
|
||||
this.identifiers = identifiers;
|
||||
|
||||
for (int i = 0; i < excludedIdentifiers.Length; i++)
|
||||
{
|
||||
@@ -138,8 +141,7 @@ namespace Barotrauma
|
||||
element.Add(
|
||||
new XAttribute("identifiers", JoinedIdentifiers),
|
||||
new XAttribute("type", type.ToString()),
|
||||
new XAttribute("optional", IsOptional),
|
||||
new XAttribute("ignoreineditor", IgnoreInEditor));
|
||||
new XAttribute("optional", IsOptional));
|
||||
|
||||
if (excludedIdentifiers.Length > 0)
|
||||
{
|
||||
@@ -221,7 +223,6 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
ri.IsOptional = element.GetAttributeBool("optional", false);
|
||||
ri.IgnoreInEditor = element.GetAttributeBool("ignoreineditor", false);
|
||||
return ri;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ namespace Barotrauma
|
||||
flames = true;
|
||||
underwaterBubble = true;
|
||||
}
|
||||
|
||||
|
||||
public Explosion(XElement element, string parentDebugName)
|
||||
{
|
||||
attack = new Attack(element, parentDebugName + ", Explosion");
|
||||
@@ -62,16 +62,6 @@ namespace Barotrauma
|
||||
CameraShake = element.GetAttributeFloat("camerashake", attack.Range * 0.1f);
|
||||
}
|
||||
|
||||
public void DisableParticles()
|
||||
{
|
||||
sparks = false;
|
||||
shockwave = false;
|
||||
smoke = false;
|
||||
flash = false;
|
||||
flames = false;
|
||||
underwaterBubble = false;
|
||||
}
|
||||
|
||||
public List<Triplet<Explosion, Vector2, float>> GetRecentExplosions(float maxSecondsAgo)
|
||||
{
|
||||
return prevExplosions.FindAll(e => e.Third >= Timing.TotalTime - maxSecondsAgo);
|
||||
|
||||
@@ -15,17 +15,16 @@ namespace Barotrauma
|
||||
{
|
||||
const float OxygenConsumption = 50.0f;
|
||||
const float GrowSpeed = 5.0f;
|
||||
|
||||
protected Hull hull;
|
||||
|
||||
protected Vector2 position;
|
||||
protected Vector2 size;
|
||||
|
||||
private Hull hull;
|
||||
|
||||
private readonly Submarine submarine;
|
||||
public Submarine Submarine => submarine;
|
||||
|
||||
protected bool removed;
|
||||
|
||||
private bool removed;
|
||||
|
||||
#if CLIENT
|
||||
private List<Decal> burnDecals = new List<Decal>();
|
||||
#endif
|
||||
@@ -87,7 +86,7 @@ namespace Barotrauma
|
||||
position = worldPosition - new Vector2(-5.0f, 5.0f);
|
||||
if (hull.Submarine != null)
|
||||
{
|
||||
submarine = hull.Submarine;
|
||||
Submarine = hull.Submarine;
|
||||
position -= Submarine.Position;
|
||||
}
|
||||
|
||||
@@ -186,16 +185,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void ReduceOxygen(float deltaTime)
|
||||
{
|
||||
hull.Oxygen -= size.X * deltaTime * OxygenConsumption;
|
||||
}
|
||||
|
||||
protected virtual void AdjustXPos(float growModifier, float deltaTime)
|
||||
{
|
||||
position.X -= GrowSpeed * growModifier * 0.5f * deltaTime;
|
||||
}
|
||||
|
||||
partial void UpdateProjSpecific(float growModifier);
|
||||
|
||||
private void OnChangeHull(Vector2 pos, Hull particleHull)
|
||||
|
||||
@@ -63,6 +63,13 @@ namespace Barotrauma
|
||||
return "Hull";
|
||||
}
|
||||
}
|
||||
|
||||
[Editable, Serialize("", true)]
|
||||
public string RoomName
|
||||
{
|
||||
get;
|
||||
set;
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
@@ -417,6 +424,11 @@ namespace Barotrauma
|
||||
public void AddFireSource(FireSource fireSource)
|
||||
{
|
||||
FireSources.Add(fireSource);
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !IdFreed)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime, Camera cam)
|
||||
@@ -584,6 +596,11 @@ namespace Barotrauma
|
||||
public void RemoveFire(FireSource fire)
|
||||
{
|
||||
FireSources.Remove(fire);
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer && !Removed && !IdFreed)
|
||||
{
|
||||
GameMain.NetworkMember.CreateEntityEvent(this);
|
||||
}
|
||||
}
|
||||
|
||||
public IEnumerable<Hull> GetConnectedHulls(int? searchDepth)
|
||||
@@ -800,17 +817,17 @@ namespace Barotrauma
|
||||
}
|
||||
|
||||
if (roomItems.Contains("reactor"))
|
||||
return "RoomName.ReactorRoom";
|
||||
return TextManager.Get("ReactorRoom");
|
||||
else if (roomItems.Contains("engine"))
|
||||
return "RoomName.EngineRoom";
|
||||
return TextManager.Get("EngineRoom");
|
||||
else if (roomItems.Contains("steering") && roomItems.Contains("sonar"))
|
||||
return "RoomName.CommandRoom";
|
||||
return TextManager.Get("CommandRoom");
|
||||
else if (roomItems.Contains("ballast"))
|
||||
return "RoomName.Ballast";
|
||||
return TextManager.Get("Ballast");
|
||||
|
||||
if (ConnectedGaps.Any(g => !g.IsRoomToRoom && g.ConnectedDoor != null))
|
||||
{
|
||||
return "RoomName.Airlock";
|
||||
return TextManager.Get("Airlock");
|
||||
}
|
||||
|
||||
Rectangle subRect = Submarine.CalculateDimensions();
|
||||
@@ -830,7 +847,7 @@ namespace Barotrauma
|
||||
else
|
||||
roomPos |= Alignment.Right;
|
||||
|
||||
return "RoomName.Sub" + roomPos.ToString();
|
||||
return TextManager.Get("Sub" + roomPos.ToString());
|
||||
}
|
||||
|
||||
public static Hull Load(XElement element, Submarine submarine)
|
||||
|
||||
@@ -138,9 +138,6 @@ namespace Barotrauma
|
||||
public Submarine StartOutpost { get; private set; }
|
||||
public Submarine EndOutpost { get; private set; }
|
||||
|
||||
private Submarine preSelectedStartOutpost;
|
||||
private Submarine preSelectedEndOutpost;
|
||||
|
||||
public string Seed
|
||||
{
|
||||
get { return seed; }
|
||||
@@ -212,7 +209,7 @@ namespace Barotrauma
|
||||
/// </summary>
|
||||
/// <param name="difficulty">A scalar between 0-100</param>
|
||||
/// <param name="sizeFactor">A scalar between 0-1 (0 = the minimum width defined in the generation params is used, 1 = the max width is used)</param>
|
||||
public Level(string seed, float difficulty, float sizeFactor, LevelGenerationParams generationParams, Biome biome, Submarine startOutpost = null, Submarine endOutPost = null)
|
||||
public Level(string seed, float difficulty, float sizeFactor, LevelGenerationParams generationParams, Biome biome)
|
||||
: base(null)
|
||||
{
|
||||
|
||||
@@ -228,9 +225,6 @@ namespace Barotrauma
|
||||
(width / GridCellSize) * GridCellSize,
|
||||
(generationParams.Height / GridCellSize) * GridCellSize);
|
||||
|
||||
preSelectedStartOutpost = startOutpost;
|
||||
preSelectedEndOutpost = endOutPost;
|
||||
|
||||
//remove from entity dictionary
|
||||
base.Remove();
|
||||
}
|
||||
@@ -1516,24 +1510,14 @@ namespace Barotrauma
|
||||
continue;
|
||||
}
|
||||
|
||||
//only create a starting outpost in campaign and tutorial modes
|
||||
if (!IsModeStartOutpostCompatible() && ((i == 0) == !Mirrored))
|
||||
//only create a starting outpost in campaign mode
|
||||
if (GameMain.GameSession?.GameMode as CampaignMode == null && ((i == 0) == !Mirrored))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Submarine outpost = null;
|
||||
|
||||
if (i == 0 && preSelectedStartOutpost == null || i == 1 && preSelectedEndOutpost == null)
|
||||
{
|
||||
string outpostFile = outpostFiles.GetRandom(Rand.RandSync.Server);
|
||||
outpost = new Submarine(outpostFile, tryLoad: false);
|
||||
}
|
||||
else
|
||||
{
|
||||
outpost = (i == 0) ? preSelectedStartOutpost : preSelectedEndOutpost;
|
||||
}
|
||||
|
||||
|
||||
string outpostFile = outpostFiles.GetRandom(Rand.RandSync.Server);
|
||||
var outpost = new Submarine(outpostFile, tryLoad: false);
|
||||
outpost.Load(unloadPrevious: false);
|
||||
outpost.MakeOutpost();
|
||||
|
||||
@@ -1585,15 +1569,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
private bool IsModeStartOutpostCompatible()
|
||||
{
|
||||
#if CLIENT
|
||||
return GameMain.GameSession?.GameMode as CampaignMode != null || GameMain.GameSession?.GameMode as TutorialMode != null;
|
||||
#else
|
||||
return GameMain.GameSession?.GameMode as CampaignMode != null;
|
||||
#endif
|
||||
}
|
||||
|
||||
public override void Remove()
|
||||
{
|
||||
base.Remove();
|
||||
|
||||
@@ -27,13 +27,7 @@ namespace Barotrauma
|
||||
DisallowedAdjacentLocations = element.GetAttributeStringArray("disallowedadjacentlocations", new string[0]).ToList();
|
||||
RequiredAdjacentLocations = element.GetAttributeStringArray("requiredadjacentlocations", new string[0]).ToList();
|
||||
|
||||
string messageTag = element.GetAttributeString("messagetag", "LocationChange." + currentType + ".ChangeTo." + ChangeToType);
|
||||
|
||||
Messages = TextManager.GetAll(messageTag);
|
||||
if (Messages == null)
|
||||
{
|
||||
DebugConsole.ThrowError("No messages defined for the location type change " + currentType + " -> " + ChangeToType);
|
||||
}
|
||||
Messages = TextManager.GetAll("LocationChange." + currentType + ".ChangeTo." + ChangeToType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -522,9 +522,15 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// The value should always be copied from the prefab. Editing is enabled only for testing the scale in the sub editor (changes are not saved).
|
||||
|
||||
#if DEBUG
|
||||
[Serialize(1f, false), Editable(0.1f, 10f, DecimalCount = 3, ValueStep = 0.1f)]
|
||||
public virtual float Scale { get; set; } = 1;
|
||||
#else
|
||||
[Serialize(1f, false)]
|
||||
#endif
|
||||
public float Scale { get; set; } = 1;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,32 +159,6 @@ namespace Barotrauma
|
||||
private set;
|
||||
}
|
||||
|
||||
private float scale = 1.0f;
|
||||
public override float Scale
|
||||
{
|
||||
get { return scale; }
|
||||
set
|
||||
{
|
||||
if (scale == value) { return; }
|
||||
scale = MathHelper.Clamp(value, 0.1f, 10.0f);
|
||||
|
||||
float relativeScale = scale / prefab.Scale;
|
||||
|
||||
if (!ResizeHorizontal || !ResizeVertical)
|
||||
{
|
||||
int newWidth = ResizeHorizontal ? rect.Width : (int)(defaultRect.Width * relativeScale);
|
||||
int newHeight = ResizeVertical ? rect.Height : (int)(defaultRect.Height * relativeScale);
|
||||
Rect = new Rectangle(rect.X, rect.Y, newWidth, newHeight);
|
||||
if (Sections != null)
|
||||
{
|
||||
UpdateSections();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Rectangle defaultRect;
|
||||
|
||||
public override Rectangle Rect
|
||||
{
|
||||
get
|
||||
@@ -195,13 +169,9 @@ namespace Barotrauma
|
||||
{
|
||||
Rectangle oldRect = Rect;
|
||||
base.Rect = value;
|
||||
if (Prefab.Body)
|
||||
{
|
||||
CreateSections();
|
||||
}
|
||||
if (Prefab.Body) CreateSections();
|
||||
else
|
||||
{
|
||||
if (Sections == null) { return; }
|
||||
foreach (WallSection sec in Sections)
|
||||
{
|
||||
Rectangle secRect = sec.rect;
|
||||
@@ -219,11 +189,11 @@ namespace Barotrauma
|
||||
|
||||
public float BodyWidth
|
||||
{
|
||||
get { return Prefab.BodyWidth > 0.0f ? Prefab.BodyWidth * scale : rect.Width; }
|
||||
get { return Prefab.BodyWidth > 0.0f ? Prefab.BodyWidth : rect.Width; }
|
||||
}
|
||||
public float BodyHeight
|
||||
{
|
||||
get { return Prefab.BodyHeight > 0.0f ? Prefab.BodyHeight * scale : rect.Height; }
|
||||
get { return Prefab.BodyHeight > 0.0f ? Prefab.BodyHeight : rect.Height; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -344,8 +314,8 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
// Only add ai targets automatically to submarine/outpost walls
|
||||
if (aiTarget == null && HasBody && Tags.Contains("wall") && submarine != null)
|
||||
// Only add ai targets automatically to walls
|
||||
if (aiTarget == null && HasBody && Tags.Contains("wall"))
|
||||
{
|
||||
aiTarget = new AITarget(this);
|
||||
}
|
||||
@@ -635,6 +605,24 @@ namespace Barotrauma
|
||||
var character = ((Limb)f2.Body.UserData).character;
|
||||
if (character.DisableImpactDamageTimer > 0.0f || ((Limb)f2.Body.UserData).Mass < 100.0f) return true;
|
||||
}
|
||||
|
||||
if (!Prefab.Platform && Prefab.StairDirection == Direction.None)
|
||||
{
|
||||
Vector2 pos = ConvertUnits.ToDisplayUnits(f2.Body.Position);
|
||||
|
||||
int section = FindSectionIndex(pos);
|
||||
if (section > -1)
|
||||
{
|
||||
Vector2 normal = contact.Manifold.LocalNormal;
|
||||
|
||||
float impact = Vector2.Dot(f2.Body.LinearVelocity, -normal) * f2.Body.Mass * 0.1f;
|
||||
if (impact < 10.0f) return true;
|
||||
#if CLIENT
|
||||
SoundPlayer.PlayDamageSound("StructureBlunt", impact, SectionPosition(section, true), tags: Tags);
|
||||
#endif
|
||||
AddDamage(section, impact);
|
||||
}
|
||||
}
|
||||
|
||||
OnImpactProjSpecific(f1, f2, contact);
|
||||
|
||||
@@ -977,7 +965,6 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateSections()
|
||||
{
|
||||
if (Bodies == null) return;
|
||||
foreach (Body b in Bodies)
|
||||
{
|
||||
GameMain.World.RemoveBody(b);
|
||||
@@ -1041,9 +1028,9 @@ namespace Barotrauma
|
||||
if (BodyWidth > 0.0f) rect.Width = (int)BodyWidth;
|
||||
if (BodyHeight > 0.0f) rect.Height = Math.Max((int)Math.Round(BodyHeight * (rect.Height / (float)this.rect.Height)), 1);
|
||||
}
|
||||
if (FlippedX) { diffFromCenter = -diffFromCenter; }
|
||||
if (FlippedX) diffFromCenter = -diffFromCenter;
|
||||
|
||||
Vector2 bodyOffset = ConvertUnits.ToSimUnits(Prefab.BodyOffset) * scale;
|
||||
Vector2 bodyOffset = ConvertUnits.ToSimUnits(Prefab.BodyOffset);
|
||||
if (FlippedX) { bodyOffset.X = -bodyOffset.X; }
|
||||
if (FlippedY) { bodyOffset.Y = -bodyOffset.Y; }
|
||||
|
||||
@@ -1063,8 +1050,7 @@ namespace Barotrauma
|
||||
{
|
||||
newBody.Position = structureCenter + bodyOffset + new Vector2(
|
||||
(float)Math.Cos(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation),
|
||||
(float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation))
|
||||
* ConvertUnits.ToSimUnits(diffFromCenter);
|
||||
(float)Math.Sin(IsHorizontal ? -BodyRotation : MathHelper.PiOver2 - BodyRotation)) * ConvertUnits.ToSimUnits(diffFromCenter);
|
||||
newBody.Rotation = -BodyRotation;
|
||||
}
|
||||
else
|
||||
@@ -1205,9 +1191,6 @@ namespace Barotrauma
|
||||
{
|
||||
XElement element = new XElement("Structure");
|
||||
|
||||
int width = ResizeHorizontal ? rect.Width : defaultRect.Width;
|
||||
int height = ResizeVertical ? rect.Height : defaultRect.Height;
|
||||
|
||||
element.Add(
|
||||
new XAttribute("name", prefab.Name),
|
||||
new XAttribute("identifier", prefab.Identifier),
|
||||
|
||||
@@ -417,10 +417,7 @@ namespace Barotrauma
|
||||
if (me.Submarine != this) { continue; }
|
||||
if (me is Item item)
|
||||
{
|
||||
if (item.GetComponent<Repairable>() != null)
|
||||
{
|
||||
item.Indestructible = true;
|
||||
}
|
||||
item.Indestructible = true;
|
||||
foreach (ItemComponent ic in item.Components)
|
||||
{
|
||||
if (ic is ConnectionPanel connectionPanel)
|
||||
@@ -428,17 +425,11 @@ namespace Barotrauma
|
||||
//prevent rewiring
|
||||
connectionPanel.Locked = true;
|
||||
}
|
||||
else if (ic is Holdable holdable && holdable.Attached)
|
||||
else if (ic is Pickable pickable)
|
||||
{
|
||||
//prevent deattaching items from walls
|
||||
#if CLIENT
|
||||
if (GameMain.GameSession?.GameMode is TutorialMode)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
holdable.CanBePicked = false;
|
||||
holdable.CanBeSelected = false;
|
||||
//prevent picking up (or deattaching) items
|
||||
pickable.CanBePicked = false;
|
||||
pickable.CanBeSelected = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -542,6 +533,20 @@ namespace Barotrauma
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
maxX = Math.Min(maxX, ruin.Area.X - 100.0f);
|
||||
}
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
{
|
||||
//no walls found at either side, just use the initial spawnpos and hope for the best
|
||||
}
|
||||
else if (minX < 0)
|
||||
{
|
||||
//no wall found at the left side, spawn to the left from the right-side wall
|
||||
spawnPos.X = maxX - minWidth - 100.0f + subDockingPortOffset;
|
||||
}
|
||||
|
||||
if (minX < 0.0f && maxX > Level.Loaded.Size.X)
|
||||
@@ -1123,7 +1128,6 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
savedSubmarines.Add(new Submarine(filePath));
|
||||
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
|
||||
}
|
||||
|
||||
public static void RefreshSavedSubs()
|
||||
|
||||
@@ -468,7 +468,7 @@ namespace Barotrauma
|
||||
|
||||
var gaps = newHull?.ConnectedGaps ?? Gap.GapList.Where(g => g.Submarine == submarine);
|
||||
targetPos = character.WorldPosition;
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 500.0f);
|
||||
Gap adjacentGap = Gap.FindAdjacent(gaps, targetPos, 200.0f);
|
||||
if (adjacentGap == null) return true;
|
||||
|
||||
if (newHull != null)
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace;
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue1:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, worldPosition, condition));
|
||||
@@ -115,7 +115,7 @@ namespace Barotrauma
|
||||
{
|
||||
string errorMsg = "Attempted to add a null item to entity spawn queue.\n" + Environment.StackTrace;
|
||||
DebugConsole.ThrowError(errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue2:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
GameAnalyticsManager.AddErrorEventOnce("EntitySpawner.AddToSpawnQueue3:ItemPrefabNull", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return;
|
||||
}
|
||||
spawnQueue.Enqueue(new ItemSpawnInfo(itemPrefab, position, sub, condition));
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Barotrauma.Networking
|
||||
|
||||
public OrderChatMessage(Order order, string orderOption, Entity targetEntity, Character targetCharacter, Character sender)
|
||||
: this(order, orderOption,
|
||||
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.DisplayName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption),
|
||||
order.GetChatMessage(targetCharacter?.Name, sender?.CurrentHull?.RoomName, givingOrderToSelf: targetCharacter == sender, orderOption: orderOption),
|
||||
targetEntity, targetCharacter, sender)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -377,12 +377,6 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool VoipEnabled {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool EndRoundAtLevelEnd
|
||||
{
|
||||
@@ -401,7 +395,7 @@ namespace Barotrauma.Networking
|
||||
public bool AllowRagdollButton
|
||||
{
|
||||
get;
|
||||
set;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
|
||||
@@ -7,7 +7,15 @@ namespace Barotrauma.Steam
|
||||
{
|
||||
partial class SteamManager
|
||||
{
|
||||
#if DEBUG
|
||||
public static bool USE_STEAM
|
||||
{
|
||||
get { return GameMain.Config.UseSteam; }
|
||||
}
|
||||
#else
|
||||
//cannot enable/disable steam in release builds
|
||||
public const bool USE_STEAM = true;
|
||||
#endif
|
||||
|
||||
public const uint AppID = 602960;
|
||||
|
||||
@@ -58,16 +66,6 @@ namespace Barotrauma.Steam
|
||||
if (!USE_STEAM) return;
|
||||
instance = new SteamManager();
|
||||
}
|
||||
|
||||
public static void OverlayCustomURL(string url)
|
||||
{
|
||||
if (instance == null || !instance.isInitialized || instance.client == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
instance.client.Overlay.OpenUrl(url);
|
||||
}
|
||||
|
||||
public static bool UnlockAchievement(string achievementName)
|
||||
{
|
||||
|
||||
@@ -88,7 +88,7 @@ namespace Barotrauma
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj is KeyOrMouse keyOrMouse)
|
||||
if (obj is KeyOrMouse keyOrMouse )
|
||||
{
|
||||
if (MouseButton.HasValue)
|
||||
{
|
||||
@@ -162,6 +162,21 @@ namespace Barotrauma
|
||||
get { return binding; }
|
||||
}
|
||||
|
||||
public void SetState()
|
||||
{
|
||||
hit = binding.IsHit();
|
||||
if (hit) hitQueue = true;
|
||||
|
||||
held = binding.IsDown();
|
||||
if (held) heldQueue = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
public KeyOrMouse State
|
||||
{
|
||||
get { return binding; }
|
||||
}
|
||||
|
||||
public void SetState()
|
||||
{
|
||||
hit = binding.IsHit();
|
||||
|
||||
@@ -46,15 +46,7 @@ namespace Barotrauma
|
||||
|
||||
if (File.Exists(filePath))
|
||||
{
|
||||
try
|
||||
{
|
||||
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
doc = XDocument.Load(filePath, LoadOptions.SetBaseUri);
|
||||
if (doc.Root == null) return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -87,6 +87,7 @@ namespace Barotrauma
|
||||
|
||||
public string FullPath { get; private set; }
|
||||
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return FilePath + ": " + sourceRect;
|
||||
@@ -106,14 +107,27 @@ namespace Barotrauma
|
||||
{
|
||||
this.lazyLoad = lazyLoad;
|
||||
SourceElement = element;
|
||||
if (!ParseTexturePath(path, file)) { return; }
|
||||
if (file == "")
|
||||
{
|
||||
file = SourceElement.GetAttributeString("texture", "");
|
||||
}
|
||||
if (file == "")
|
||||
{
|
||||
DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!");
|
||||
return;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
LoadTexture(ref sourceVector, ref shouldReturn, preMultipliedAlpha);
|
||||
}
|
||||
FilePath = path + file;
|
||||
if (!string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
FullPath = Path.GetFullPath(FilePath);
|
||||
}
|
||||
|
||||
Name = SourceElement.GetAttributeString("name", null);
|
||||
Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero);
|
||||
var overrideElement = GetLocalizationOverrideElement();
|
||||
if (overrideElement != null && overrideElement.Attribute("sourcerect") != null)
|
||||
{
|
||||
sourceVector = overrideElement.GetAttributeVector4("sourcerect", Vector4.Zero);
|
||||
}
|
||||
preMultipliedAlpha = preMultiplyAlpha ?? SourceElement.GetAttributeBool("premultiplyalpha", true);
|
||||
bool shouldReturn = false;
|
||||
if (!lazyLoad)
|
||||
@@ -245,12 +259,8 @@ namespace Barotrauma
|
||||
}
|
||||
if (SourceElement != null)
|
||||
{
|
||||
sourceRect = SourceElement.GetAttributeRect("sourcerect", Rectangle.Empty);
|
||||
var overrideElement = GetLocalizationOverrideElement();
|
||||
if (overrideElement != null && overrideElement.Attribute("sourcerect") != null)
|
||||
{
|
||||
sourceRect = overrideElement.GetAttributeRect("sourcerect", Rectangle.Empty);
|
||||
}
|
||||
Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero);
|
||||
sourceRect = new Rectangle((int)sourceVector.X, (int)sourceVector.Y, (int)sourceVector.Z, (int)sourceVector.W);
|
||||
size = SourceElement.GetAttributeVector2("size", Vector2.One);
|
||||
size.X *= sourceRect.Width;
|
||||
size.Y *= sourceRect.Height;
|
||||
@@ -259,51 +269,6 @@ namespace Barotrauma
|
||||
ID = GetID(SourceElement);
|
||||
}
|
||||
}
|
||||
|
||||
public bool ParseTexturePath(string path = "", string file = "")
|
||||
{
|
||||
if (file == "")
|
||||
{
|
||||
file = SourceElement.GetAttributeString("texture", "");
|
||||
var overrideElement = GetLocalizationOverrideElement();
|
||||
if (overrideElement != null)
|
||||
{
|
||||
string overrideFile = overrideElement.GetAttributeString("texture", "");
|
||||
if (!string.IsNullOrEmpty(overrideFile)) { file = overrideFile; }
|
||||
}
|
||||
}
|
||||
if (file == "")
|
||||
{
|
||||
DebugConsole.ThrowError("Sprite " + SourceElement + " doesn't have a texture specified!");
|
||||
return false;
|
||||
}
|
||||
if (!string.IsNullOrEmpty(path))
|
||||
{
|
||||
if (!path.EndsWith("/")) path += "/";
|
||||
}
|
||||
FilePath = path + file;
|
||||
if (!string.IsNullOrEmpty(FilePath))
|
||||
{
|
||||
FullPath = Path.GetFullPath(FilePath);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private XElement GetLocalizationOverrideElement()
|
||||
{
|
||||
foreach (XElement subElement in SourceElement.Elements())
|
||||
{
|
||||
if (subElement.Name.ToString().ToLowerInvariant() == "override")
|
||||
{
|
||||
string language = subElement.GetAttributeString("language", "");
|
||||
if (TextManager.Language.ToLower() == language.ToLower())
|
||||
{
|
||||
return subElement;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -336,12 +336,9 @@ namespace Barotrauma
|
||||
UnlockAchievement("survivereactormeltdown");
|
||||
}
|
||||
#endif
|
||||
var charactersInSub = Character.CharacterList.FindAll(c =>
|
||||
!c.IsDead &&
|
||||
c.TeamID != Character.TeamType.FriendlyNPC &&
|
||||
!(c.AIController is EnemyAIController) &&
|
||||
(c.Submarine == gameSession.Submarine || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
|
||||
|
||||
var charactersInSub = Character.CharacterList.FindAll(c => !c.IsDead &&
|
||||
(c.Submarine == gameSession.Submarine || (Level.Loaded?.EndOutpost != null && c.Submarine == Level.Loaded.EndOutpost)));
|
||||
if (charactersInSub.Count == 1)
|
||||
{
|
||||
//there must be some non-enemy casualties to get the last mant standing achievement
|
||||
@@ -349,11 +346,7 @@ namespace Barotrauma
|
||||
{
|
||||
UnlockAchievement(charactersInSub[0], "lastmanstanding");
|
||||
}
|
||||
//lone sailor achievement if alone in the sub and there are no other characters with the same team ID
|
||||
else if (!Character.CharacterList.Any(c =>
|
||||
c != charactersInSub[0] &&
|
||||
c.TeamID == charactersInSub[0].TeamID &&
|
||||
!(c.AIController is EnemyAIController)))
|
||||
else if (!Character.CharacterList.Any(c => !(c.AIController is EnemyAIController)))
|
||||
{
|
||||
UnlockAchievement(charactersInSub[0], "lonesailor");
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -16,7 +15,7 @@ namespace Barotrauma
|
||||
private static string[] serverMessageCharacters = new string[] { "~", "[", "]", "=" };
|
||||
|
||||
public static string Language;
|
||||
|
||||
|
||||
private static HashSet<string> availableLanguages = new HashSet<string>();
|
||||
public static IEnumerable<string> AvailableLanguages
|
||||
{
|
||||
@@ -79,7 +78,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static string Get(string textTag, bool returnNull = false, string fallBackTag = null)
|
||||
public static string Get(string textTag, bool returnNull = false)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
@@ -94,16 +93,7 @@ namespace Barotrauma
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(textTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(fallBackTag))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
string text = textPack.Get(fallBackTag);
|
||||
if (text != null) { return text; }
|
||||
}
|
||||
if (text != null) return text;
|
||||
}
|
||||
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
@@ -128,14 +118,138 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public static string ParseInputTypes(string text)
|
||||
public static string GetFormatted(string textTag, bool returnNull = false, params object[] args)
|
||||
{
|
||||
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
|
||||
string text = Get(textTag, returnNull);
|
||||
|
||||
if (text == null || text.Length == 0)
|
||||
{
|
||||
text = text.Replace("[" + inputType.ToString().ToLowerInvariant() + "]", GameMain.Config.KeyBind(inputType).ToString());
|
||||
text = text.Replace("[InputType." + inputType.ToString() + "]", GameMain.Config.KeyBind(inputType).ToString());
|
||||
if (returnNull)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
DebugConsole.ThrowError("Text \"" + textTag + "\" not found.");
|
||||
return textTag;
|
||||
}
|
||||
}
|
||||
return text;
|
||||
|
||||
return string.Format(text, args);
|
||||
}
|
||||
|
||||
// Format: ServerMessage.Identifier1/ServerMessage.Indentifier2~[variable1]=value~[variable2]=value
|
||||
public static string GetServerMessage(string serverMessage)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
|
||||
string[] messages = serverMessage.Split('/');
|
||||
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
{
|
||||
if (!IsServerMessageWithVariables(messages[i])) // No variables, try to translate
|
||||
{
|
||||
if (messages[i].Contains(" ")) continue; // Spaces found, do not translate
|
||||
string msg = Get(messages[i], true);
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
{
|
||||
messages[i] = msg;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string[] messageWithVariables = messages[i].Split('~');
|
||||
string msg = Get(messageWithVariables[0], true);
|
||||
|
||||
if (msg != null) // If a translation was found, otherwise use the original
|
||||
{
|
||||
messages[i] = msg;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // No translation found, probably caused by player input -> skip variable handling
|
||||
}
|
||||
|
||||
// First index is always the message identifier -> start at 1
|
||||
for (int j = 1; j < messageWithVariables.Length; j++)
|
||||
{
|
||||
string[] variableAndValue = messageWithVariables[j].Split('=');
|
||||
messages[i] = messages[i].Replace(variableAndValue[0], variableAndValue[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string translatedServerMessage = string.Empty;
|
||||
for (int i = 0; i < messages.Length; i++)
|
||||
{
|
||||
translatedServerMessage += messages[i];
|
||||
}
|
||||
return translatedServerMessage;
|
||||
}
|
||||
|
||||
catch (IndexOutOfRangeException exception)
|
||||
{
|
||||
string errorMsg = "Failed to translate server message \"" + serverMessage + "\".";
|
||||
#if DEBUG
|
||||
DebugConsole.ThrowError(errorMsg, exception);
|
||||
#endif
|
||||
GameAnalyticsManager.AddErrorEventOnce("TextManager.GetServerMessage:" + serverMessage, GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg);
|
||||
return errorMsg;
|
||||
}
|
||||
}
|
||||
|
||||
public static bool IsServerMessageWithVariables(string message)
|
||||
{
|
||||
for (int i = 0; i < serverMessageCharacters.Length; i++)
|
||||
{
|
||||
if (!message.Contains(serverMessageCharacters[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static List<string> GetAll(string textTag)
|
||||
{
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
DebugConsole.ThrowError("No text packs available for the selected language (" + Language + ")! Switching to English...");
|
||||
Language = "English";
|
||||
if (!textPacks.ContainsKey(Language))
|
||||
{
|
||||
throw new Exception("No text packs available in English!");
|
||||
}
|
||||
}
|
||||
|
||||
List<string> allText;
|
||||
|
||||
foreach (TextPack textPack in textPacks[Language])
|
||||
{
|
||||
allText = textPack.GetAll(textTag);
|
||||
if (allText != null) return allText;
|
||||
}
|
||||
|
||||
//if text was not found and we're using a language other than English, see if we can find an English version
|
||||
//may happen, for example, if a user has selected another language and using mods that haven't been translated to that language
|
||||
if (Language != "English" && textPacks.ContainsKey("English"))
|
||||
{
|
||||
foreach (TextPack textPack in textPacks["English"])
|
||||
{
|
||||
allText = textPack.GetAll(textTag);
|
||||
if (allText != null) return allText;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public static string GetFormatted(string textTag, bool returnNull = false, params object[] args)
|
||||
|
||||
@@ -11,8 +11,8 @@ namespace Barotrauma
|
||||
public readonly string Language;
|
||||
|
||||
private Dictionary<string, List<string>> texts;
|
||||
|
||||
private readonly string filePath;
|
||||
|
||||
private string filePath;
|
||||
|
||||
public TextPack(string filePath)
|
||||
{
|
||||
@@ -37,7 +37,6 @@ namespace Barotrauma
|
||||
text = text.Replace("&", "&");
|
||||
text = text.Replace("<", "<");
|
||||
text = text.Replace(">", ">");
|
||||
text = text.Replace(""", "\"");
|
||||
infoList.Add(text);
|
||||
}
|
||||
}
|
||||
@@ -63,20 +62,6 @@ namespace Barotrauma
|
||||
return textList;
|
||||
}
|
||||
|
||||
public List<KeyValuePair<string, string>> GetAllTagTextPairs()
|
||||
{
|
||||
var pairs = new List<KeyValuePair<string, string>>();
|
||||
foreach (KeyValuePair<string, List<string>> kvp in texts)
|
||||
{
|
||||
foreach (string line in kvp.Value)
|
||||
{
|
||||
pairs.Add(new KeyValuePair<string, string>(kvp.Key, line));
|
||||
}
|
||||
}
|
||||
|
||||
return pairs;
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
public void CheckForDuplicates(int index)
|
||||
{
|
||||
|
||||
@@ -795,18 +795,6 @@ namespace Barotrauma
|
||||
return new Vector2((float)x, (float)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rotates a point in 2d space around the origin
|
||||
/// </summary>
|
||||
public static Vector2 RotatePoint(Vector2 point, float radians)
|
||||
{
|
||||
var sin = Math.Sin(radians);
|
||||
var cos = Math.Cos(radians);
|
||||
var x = (cos * point.X) - (sin * point.Y);
|
||||
var y = (sin * point.X) + (cos * point.Y);
|
||||
return new Vector2((float)x, (float)y);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the corners of an imaginary rectangle.
|
||||
/// Unlike the XNA rectangle, this can be rotated with the up parameter.
|
||||
|
||||
@@ -222,6 +222,12 @@ namespace Barotrauma
|
||||
if (fileName.Length == 0) fileName = "Save";
|
||||
}
|
||||
|
||||
if (fileName == "Save_Default")
|
||||
{
|
||||
fileName = TextManager.Get("SaveFile.DefaultName", true);
|
||||
if (fileName.Length == 0) fileName = "Save";
|
||||
}
|
||||
|
||||
if (!Directory.Exists(folder))
|
||||
{
|
||||
DebugConsole.ThrowError("Save folder \"" + folder + "\" not found. Created new folder");
|
||||
|
||||
Reference in New Issue
Block a user