(d5ea3c7d5) Merge branch 'dev' of https://github.com/Regalis11/Barotrauma-development into dev

This commit is contained in:
Joonas Rikkonen
2019-03-29 17:24:17 +02:00
parent ca08b803dc
commit ddfd7274e9
22 changed files with 230 additions and 99 deletions
@@ -51,19 +51,19 @@ namespace Barotrauma
} }
GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black); GUI.DrawString(spriteBatch, pos - Vector2.UnitY * 80.0f, State.ToString(), stateColor, Color.Black);
if (latchOntoAI != null) if (LatchOntoAI != null)
{ {
foreach (Joint attachJoint in latchOntoAI.AttachJoints) foreach (Joint attachJoint in LatchOntoAI.AttachJoints)
{ {
GUI.DrawLine(spriteBatch, GUI.DrawLine(spriteBatch,
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)), ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorA.X, -attachJoint.WorldAnchorA.Y)),
ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Green, 0, 4); ConvertUnits.ToDisplayUnits(new Vector2(attachJoint.WorldAnchorB.X, -attachJoint.WorldAnchorB.Y)), Color.Green, 0, 4);
} }
if (latchOntoAI.WallAttachPos.HasValue) if (LatchOntoAI.WallAttachPos.HasValue)
{ {
GUI.DrawLine(spriteBatch, pos, GUI.DrawLine(spriteBatch, pos,
ConvertUnits.ToDisplayUnits(new Vector2(latchOntoAI.WallAttachPos.Value.X, -latchOntoAI.WallAttachPos.Value.Y)), Color.Green, 0, 3); ConvertUnits.ToDisplayUnits(new Vector2(LatchOntoAI.WallAttachPos.Value.X, -LatchOntoAI.WallAttachPos.Value.Y)), Color.Green, 0, 3);
} }
} }
@@ -224,6 +224,13 @@ namespace Barotrauma
} }
} }
if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
{
//emulate a Select input to get the character to deselect the item server-side
keys[(int)InputType.Select].Hit = true;
SelectedConstruction = null;
}
DoInteractionUpdate(deltaTime, mouseSimPos); DoInteractionUpdate(deltaTime, mouseSimPos);
} }
@@ -56,7 +56,7 @@ namespace Barotrauma
private static List<GUIMessage> messages = new List<GUIMessage>(); private static List<GUIMessage> messages = new List<GUIMessage>();
private static Sound[] sounds; private static Sound[] sounds;
private static bool pauseMenuOpen, settingsMenuOpen; private static bool pauseMenuOpen, settingsMenuOpen;
private static GUIFrame pauseMenu; public static GUIFrame PauseMenu { get; private set; }
private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon; private static Sprite arrow, lockIcon, checkmarkIcon, timerIcon;
public static KeyboardDispatcher KeyboardDispatcher { get; set; } public static KeyboardDispatcher KeyboardDispatcher { get; set; }
@@ -561,7 +561,7 @@ namespace Barotrauma
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
pauseMenu.AddToGUIUpdateList(); PauseMenu.AddToGUIUpdateList();
} }
if (settingsMenuOpen) if (settingsMenuOpen)
{ {
@@ -1420,9 +1420,9 @@ namespace Barotrauma
if (pauseMenuOpen) if (pauseMenuOpen)
{ {
pauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f); PauseMenu = new GUIFrame(new RectTransform(Vector2.One, Canvas), style: null, color: Color.Black * 0.5f);
var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), pauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) }); var pauseMenuInner = new GUIFrame(new RectTransform(new Vector2(0.13f, 0.3f), PauseMenu.RectTransform, Anchor.Center) { MinSize = new Point(200, 300) });
var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.85f), pauseMenuInner.RectTransform, Anchor.Center)) var buttonContainer = new GUILayoutGroup(new RectTransform(new Vector2(0.85f, 0.85f), pauseMenuInner.RectTransform, Anchor.Center))
{ {
@@ -200,6 +200,9 @@ namespace Barotrauma
{ {
public static bool CloseHUD(Rectangle rect) public static bool CloseHUD(Rectangle rect)
{ {
// Always close when hitting escape
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.Escape)) { return true; }
//don't close when the cursor is on a UI element //don't close when the cursor is on a UI element
if (GUI.MouseOn != null) return false; if (GUI.MouseOn != null) return false;
@@ -601,8 +601,9 @@ namespace Barotrauma
{ {
((GUIMessageBox)GUIMessageBox.VisibleBox).Close(); ((GUIMessageBox)GUIMessageBox.VisibleBox).Close();
} }
else // Otherwise toggle pausing. else if ((GUI.MouseOn == null || GUI.IsMouseOn(GUI.PauseMenu)) && Inventory.SelectedSlot == null && CharacterHealth.OpenHealthWindow == null)
{ {
// Otherwise toggle pausing, unless another window/interface is open.
GUI.TogglePauseMenu(); GUI.TogglePauseMenu();
} }
} }
@@ -7,7 +7,7 @@ namespace Barotrauma.Items.Components
{ {
public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime) public void ClientRead(ServerNetObject type, NetBuffer msg, float sendingTime)
{ {
DeattachTimer = msg.ReadSingle(); deattachTimer = msg.ReadSingle();
if (deattachTimer >= DeattachDuration) if (deattachTimer >= DeattachDuration)
{ {
holdable.DeattachFromWall(); holdable.DeattachFromWall();
@@ -1,6 +1,7 @@
using Barotrauma.Particles; using Barotrauma.Particles;
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
@@ -11,12 +12,21 @@ using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class RepairTool partial class RepairTool
#if DEBUG
: IDrawableComponent
#endif
{ {
public ParticleEmitter ParticleEmitter public ParticleEmitter ParticleEmitter
{ {
get; get;
private set; private set;
} }
#if DEBUG
public Vector2 DrawSize
{
get { return GameMain.DebugDraw ? Vector2.One * Range : Vector2.Zero; }
}
#endif
private List<ParticleEmitter> ParticleEmitterHitStructure = new List<ParticleEmitter>(); private List<ParticleEmitter> ParticleEmitterHitStructure = new List<ParticleEmitter>();
private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>(); private List<ParticleEmitter> ParticleEmitterHitCharacter = new List<ParticleEmitter>();
@@ -122,5 +132,17 @@ namespace Barotrauma.Items.Components
emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi); emitter.Second.Emit(deltaTime, particlePos, item.CurrentHull, particleAngle + MathHelper.Pi, -particleAngle + MathHelper.Pi);
} }
} }
#if DEBUG
public void Draw(SpriteBatch spriteBatch, bool editing)
{
if (GameMain.DebugDraw && IsActive)
{
GUI.DrawLine(spriteBatch,
new Vector2(debugRayStartPos.X, -debugRayStartPos.Y),
new Vector2(debugRayEndPos.X, -debugRayEndPos.Y),
Color.Yellow);
}
}
#endif
} }
} }
@@ -10,12 +10,6 @@ namespace Barotrauma
protected override void ControlInput(Camera cam) protected override void ControlInput(Camera cam)
{ {
if (draggingItem == null && HUD.CloseHUD(BackgroundFrame))
{
// TODO: fix so that works with the server side
Character.Controlled.SelectedConstruction = null;
return;
}
base.ControlInput(cam); base.ControlInput(cam);
if (BackgroundFrame.Contains(PlayerInput.MousePosition)) if (BackgroundFrame.Contains(PlayerInput.MousePosition))
{ {
@@ -504,12 +504,12 @@ namespace Barotrauma
{ {
foreach (MapEntity e in selectedList) foreach (MapEntity e in selectedList)
{ {
e.prefab.DrawPlacing(spriteBatch, e.prefab?.DrawPlacing(spriteBatch,
new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size)); new Rectangle(e.WorldRect.Location + new Point((int)moveAmount.X, (int)-moveAmount.Y), e.WorldRect.Size));
GUI.DrawRectangle(spriteBatch, GUI.DrawRectangle(spriteBatch,
new Vector2(e.WorldRect.X, -e.WorldRect.Y) + moveAmount, new Vector2(e.WorldRect.X, -e.WorldRect.Y) + moveAmount,
new Vector2(e.rect.Width, e.rect.Height), new Vector2(e.rect.Width, e.rect.Height),
Color.DarkRed, false, 0, (int)Math.Max(1.5f / GameScreen.Selected.Cam.Zoom, 1.0f)); Color.White, false, 0, (int)Math.Max(3.0f / GameScreen.Selected.Cam.Zoom, 2.0f));
} }
//stop dragging the "selection rectangle" //stop dragging the "selection rectangle"
@@ -44,7 +44,7 @@ namespace Barotrauma
drawRect.Location -= Submarine.MainSub.Position.ToPoint(); drawRect.Location -= Submarine.MainSub.Position.ToPoint();
} }
drawRect.Y = -drawRect.Y; drawRect.Y = -drawRect.Y;
GUI.DrawRectangle(spriteBatch, drawRect, Color.DarkBlue); GUI.DrawRectangle(spriteBatch, drawRect, Color.White);
} }
public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color) public void DrawListLine(SpriteBatch spriteBatch, Vector2 pos, Color color)
{ {
@@ -1239,7 +1239,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (vanilla != null && contentPackage == vanilla) if (vanilla != null && contentPackage == vanilla)
{ {
GUI.AddMessage($"Cannot edit the Vanilla content!", Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
@@ -1879,7 +1879,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
@@ -1894,7 +1894,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
return false; return false;
} }
#endif #endif
@@ -1966,7 +1966,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
box.Close(); box.Close();
return false; return false;
} }
@@ -2099,7 +2099,7 @@ namespace Barotrauma
#if !DEBUG #if !DEBUG
if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig)) if (VanillaCharacters != null && VanillaCharacters.Contains(currentCharacterConfig))
{ {
GUI.AddMessage(GetCharacterEditorTranslation("CantEditVanillaContent"), Color.Red, font: GUI.LargeFont); GUI.AddMessage(GetCharacterEditorTranslation("CannotEditVanillaCharacters"), Color.Red, font: GUI.LargeFont);
box.Close(); box.Close();
return false; return false;
} }
@@ -769,6 +769,20 @@ namespace Barotrauma
savePath = Path.Combine(Submarine.SavePath, savePath); savePath = Path.Combine(Submarine.SavePath, savePath);
} }
#if !DEBUG
var vanilla = GameMain.VanillaContent;
if (vanilla != null)
{
var vanillaSubs = vanilla.GetFilesOfType(ContentType.Submarine);
string pathToCompare = savePath.Replace(@"\", @"/").ToLowerInvariant();
if (vanillaSubs.Any(sub => sub.Replace(@"\", @"/").ToLowerInvariant() == pathToCompare))
{
GUI.AddMessage(TextManager.Get("CannotEditVanillaSubs"), Color.Red, font: GUI.LargeFont);
return false;
}
}
#endif
/*foreach (var contentPackage in GameMain.Config.SelectedContentPackages) /*foreach (var contentPackage in GameMain.Config.SelectedContentPackages)
{ {
Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name); Submarine.MainSub.RequiredContentPackages.Add(contentPackage.Name);
@@ -75,6 +75,7 @@
<Submarine file="Submarines/Bunyip.sub" /> <Submarine file="Submarines/Bunyip.sub" />
<Submarine file="Submarines/Humpback.sub" /> <Submarine file="Submarines/Humpback.sub" />
<Submarine file="Submarines/Dugong.sub" /> <Submarine file="Submarines/Dugong.sub" />
<Submarine file="Submarines/PAX.sub" />
<Submarine file="Submarines/Remora.sub" /> <Submarine file="Submarines/Remora.sub" />
<Submarine file="Submarines/RemoraDrone.sub" /> <Submarine file="Submarines/RemoraDrone.sub" />
<Text file="Content/Texts/EnglishVanilla.xml" /> <Text file="Content/Texts/EnglishVanilla.xml" />
@@ -70,12 +70,10 @@ namespace Barotrauma
private float raycastTimer; private float raycastTimer;
private bool IsCoolDownRunning => attackingLimb != null && attackingLimb.attack.CoolDownTimer > 0; private bool IsCoolDownRunning => AttackingLimb != null && AttackingLimb.attack.CoolDownTimer > 0;
private bool aggressiveBoarding; private bool aggressiveBoarding;
private LatchOntoAI latchOntoAI;
//a point in a wall which the Character is currently targeting //a point in a wall which the Character is currently targeting
private WallTarget wallTarget; private WallTarget wallTarget;
@@ -116,6 +114,8 @@ namespace Barotrauma
private readonly float priorityFearIncreasement = 2; private readonly float priorityFearIncreasement = 2;
private readonly float memoryFadeTime = 0.5f; private readonly float memoryFadeTime = 0.5f;
public LatchOntoAI LatchOntoAI { get; private set; }
public bool AttackHumans public bool AttackHumans
{ {
get get
@@ -134,11 +134,6 @@ namespace Barotrauma
} }
} }
public Limb AttackingLimb
{
get { return attackingLimb; }
}
public float CombatStrength public float CombatStrength
{ {
get { return combatStrength; } get { return combatStrength; }
@@ -149,7 +144,7 @@ namespace Barotrauma
get get
{ {
//can't enter a submarine when attached to something //can't enter a submarine when attached to something
return latchOntoAI == null || !latchOntoAI.IsAttached; return LatchOntoAI == null || !LatchOntoAI.IsAttached;
} }
} }
@@ -157,11 +152,13 @@ namespace Barotrauma
{ {
get get
{ {
//can't flip when attached to something //can't flip when attached to something or when reversing
return latchOntoAI == null || !latchOntoAI.IsAttached; return !Reverse && (LatchOntoAI == null || !LatchOntoAI.IsAttached);
} }
} }
public bool Reverse { get; private set; }
public EnemyAIController(Character c, string file, string seed) : base(c) public EnemyAIController(Character c, string file, string seed) : base(c)
{ {
targetMemories = new Dictionary<AITarget, AITargetMemory>(); targetMemories = new Dictionary<AITarget, AITargetMemory>();
@@ -209,7 +206,7 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant()) switch (subElement.Name.ToString().ToLowerInvariant())
{ {
case "latchonto": case "latchonto":
latchOntoAI = new LatchOntoAI(subElement, this); LatchOntoAI = new LatchOntoAI(subElement, this);
break; break;
case "targetpriority": case "targetpriority":
targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement)); targetingPriorities.Add(subElement.GetAttributeString("tag", "").ToLowerInvariant(), new TargetingPriority(subElement));
@@ -318,7 +315,7 @@ namespace Barotrauma
} }
} }
latchOntoAI?.Update(this, deltaTime); LatchOntoAI?.Update(this, deltaTime);
if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed)) if (SelectedAiTarget != null && (SelectedAiTarget.Entity == null || SelectedAiTarget.Entity.Removed))
{ {
@@ -574,7 +571,7 @@ namespace Barotrauma
if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height) if (Character.WorldPosition.Y < door.Item.WorldRect.Y && Character.WorldPosition.Y > door.Item.WorldRect.Y - door.Item.Rect.Height)
{ {
velocity.Y = 0; velocity.Y = 0;
latchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
steeringManager.SteeringManual(deltaTime, velocity); steeringManager.SteeringManual(deltaTime, velocity);
return; return;
@@ -585,7 +582,7 @@ namespace Barotrauma
if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right) if (Character.WorldPosition.X < door.Item.WorldRect.X && Character.WorldPosition.X > door.Item.WorldRect.Right)
{ {
velocity.X = 0; velocity.X = 0;
latchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
steeringManager.SteeringManual(deltaTime, velocity); steeringManager.SteeringManual(deltaTime, velocity);
return; return;
@@ -598,14 +595,14 @@ namespace Barotrauma
bool canAttack = true; bool canAttack = true;
if (IsCoolDownRunning) if (IsCoolDownRunning)
{ {
switch (attackingLimb.attack.AfterAttack) switch (AttackingLimb.attack.AfterAttack)
{ {
case AIBehaviorAfterAttack.Pursue: case AIBehaviorAfterAttack.Pursue:
case AIBehaviorAfterAttack.PursueIfCanAttack: case AIBehaviorAfterAttack.PursueIfCanAttack:
if (attackingLimb.attack.SecondaryCoolDown <= 0) if (AttackingLimb.attack.SecondaryCoolDown <= 0)
{ {
// No (valid) secondary cooldown defined. // No (valid) secondary cooldown defined.
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
{ {
canAttack = false; canAttack = false;
} }
@@ -617,33 +614,33 @@ namespace Barotrauma
} }
else else
{ {
if (attackingLimb.attack.SecondaryCoolDownTimer <= 0) if (AttackingLimb.attack.SecondaryCoolDownTimer <= 0)
{ {
// Don't allow attacking when the attack target has just changed. // Don't allow attacking when the attack target has just changed.
if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget) if (_previousAiTarget != null && SelectedAiTarget != _previousAiTarget)
{ {
canAttack = false; canAttack = false;
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack) if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.PursueIfCanAttack)
{ {
// Fall back if cannot attack. // Fall back if cannot attack.
UpdateFallBack(attackWorldPos, deltaTime); UpdateFallBack(attackWorldPos, deltaTime);
return; return;
} }
attackingLimb = null; AttackingLimb = null;
} }
else else
{ {
// If the secondary cooldown is defined and expired, check if we can switch the attack // If the secondary cooldown is defined and expired, check if we can switch the attack
var previousLimb = attackingLimb; var previousLimb = AttackingLimb;
var newLimb = GetAttackLimb(attackWorldPos, previousLimb); var newLimb = GetAttackLimb(attackWorldPos, previousLimb);
if (newLimb != null) if (newLimb != null)
{ {
attackingLimb = newLimb; AttackingLimb = newLimb;
} }
else else
{ {
// No new limb was found. // No new limb was found.
if (attackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue) if (AttackingLimb.attack.AfterAttack == AIBehaviorAfterAttack.Pursue)
{ {
canAttack = false; canAttack = false;
} }
@@ -670,20 +667,20 @@ namespace Barotrauma
} }
} }
if (attackingLimb == null || _previousAiTarget != SelectedAiTarget) if (AttackingLimb == null || _previousAiTarget != SelectedAiTarget)
{ {
attackingLimb = GetAttackLimb(attackWorldPos); AttackingLimb = GetAttackLimb(attackWorldPos);
} }
if (canAttack) if (canAttack)
{ {
canAttack = attackingLimb != null && attackingLimb.attack.CoolDownTimer <= 0; canAttack = AttackingLimb != null && AttackingLimb.attack.CoolDownTimer <= 0;
} }
float distance = 0; float distance = 0;
if (canAttack) if (canAttack)
{ {
// Check that we can reach the target // Check that we can reach the target
distance = Vector2.Distance(attackingLimb.WorldPosition, attackWorldPos); distance = Vector2.Distance(AttackingLimb.WorldPosition, attackWorldPos);
canAttack = distance < attackingLimb.attack.Range; canAttack = distance < AttackingLimb.attack.Range;
} }
// If the attacking limb is a hand or claw, for example, using it as the steering limb can end in the result where the character circles around the target. For example the Hammerhead steering with the claws when it should use the torso. // If the attacking limb is a hand or claw, for example, using it as the steering limb can end in the result where the character circles around the target. For example the Hammerhead steering with the claws when it should use the torso.
@@ -692,7 +689,7 @@ namespace Barotrauma
Limb steeringLimb; Limb steeringLimb;
var torso = Character.AnimController.GetLimb(LimbType.Torso); var torso = Character.AnimController.GetLimb(LimbType.Torso);
var head = Character.AnimController.GetLimb(LimbType.Head); var head = Character.AnimController.GetLimb(LimbType.Head);
if (attackingLimb == null) if (AttackingLimb == null)
{ {
steeringLimb = head ?? torso; steeringLimb = head ?? torso;
} }
@@ -700,7 +697,7 @@ namespace Barotrauma
{ {
if (head != null && torso != null) if (head != null && torso != null)
{ {
steeringLimb = Vector2.DistanceSquared(attackingLimb.SimPosition, head.SimPosition) < Vector2.DistanceSquared(attackingLimb.SimPosition, torso.SimPosition) ? head : torso; steeringLimb = Vector2.DistanceSquared(AttackingLimb.SimPosition, head.SimPosition) < Vector2.DistanceSquared(AttackingLimb.SimPosition, torso.SimPosition) ? head : torso;
} }
else else
{ {
@@ -751,7 +748,7 @@ namespace Barotrauma
if (canAttack) if (canAttack)
{ {
UpdateLimbAttack(deltaTime, attackingLimb, attackSimPos, distance); UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
} }
} }
@@ -768,7 +765,7 @@ namespace Barotrauma
{ {
targetWorldPos.X = targetHull.WorldRect.Center.X; targetWorldPos.X = targetHull.WorldRect.Center.X;
} }
latchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
if (steeringManager is IndoorsSteeringManager) if (steeringManager is IndoorsSteeringManager)
{ {
@@ -791,6 +788,7 @@ namespace Barotrauma
.Where(l => .Where(l =>
l != ignoredLimb && l != ignoredLimb &&
l.attack != null && l.attack != null &&
l.attack.CoolDownTimer <= 0 &&
!l.IsSevered && !l.IsSevered &&
!l.IsStuck && !l.IsStuck &&
l.attack.IsValidContext(currentContext) && l.attack.IsValidContext(currentContext) &&
@@ -859,7 +857,7 @@ namespace Barotrauma
attachTargetNormal = new Vector2(Math.Sign(WorldPosition.X - wall.WorldPosition.X), 0.0f); attachTargetNormal = new Vector2(Math.Sign(WorldPosition.X - wall.WorldPosition.X), 0.0f);
sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X; sectionPos.X += (wall.BodyWidth <= 0.0f ? wall.Rect.Width : wall.BodyWidth) / 2 * attachTargetNormal.X;
} }
latchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal); LatchOntoAI?.SetAttachTarget(wall.Submarine.PhysicsBody.FarseerBody, wall.Submarine, ConvertUnits.ToSimUnits(sectionPos), attachTargetNormal);
wallTarget = new WallTarget(sectionPos, wall, sectionIndex); wallTarget = new WallTarget(sectionPos, wall, sectionIndex);
} }
} }
@@ -877,7 +875,7 @@ namespace Barotrauma
} }
} }
latchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
if (attacker == null || attacker.AiTarget == null) return; if (attacker == null || attacker.AiTarget == null) return;
@@ -987,7 +985,7 @@ namespace Barotrauma
#region Targeting #region Targeting
private bool IsProperlyLatched => latchOntoAI != null && latchOntoAI.IsAttached && SelectedAiTarget?.Entity == wallTarget?.Structure; private bool IsProperlyLatched => LatchOntoAI != null && LatchOntoAI.IsAttached && SelectedAiTarget?.Entity == wallTarget?.Structure;
//goes through all the AItargets, evaluates how preferable it is to attack the target, //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 //whether the Character can see/hear the target and chooses the most preferable target within
@@ -1245,10 +1243,11 @@ namespace Barotrauma
protected override void OnStateChanged(AIState from, AIState to) protected override void OnStateChanged(AIState from, AIState to)
{ {
latchOntoAI?.DeattachFromBody(); LatchOntoAI?.DeattachFromBody();
Character.AnimController.ReleaseStuckLimbs(); Character.AnimController.ReleaseStuckLimbs();
escapePoint = Vector2.Zero; escapePoint = Vector2.Zero;
wallTarget = null; wallTarget = null;
AttackingLimb = null;
} }
private int GetMinimumPassableHoleCount() private int GetMinimumPassableHoleCount()
@@ -369,8 +369,8 @@ namespace Barotrauma
return; return;
} }
float movementAngle = MathUtils.VectorToAngle(movement) - MathHelper.PiOver2; Vector2 transformedMovement = reverse ? -movement : movement;
float movementAngle = MathUtils.VectorToAngle(transformedMovement) - MathHelper.PiOver2;
float mainLimbAngle = 0; float mainLimbAngle = 0;
if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue) if (MainLimb.type == LimbType.Torso && TorsoAngle.HasValue)
{ {
@@ -412,16 +412,19 @@ namespace Barotrauma
if (TailAngle.HasValue) if (TailAngle.HasValue)
{ {
Limb tail = GetLimb(LimbType.Tail); Limb tail = GetLimb(LimbType.Tail);
//tail?.body.SmoothRotate(movementAngle + TailAngle.Value * Dir, TailTorque);
if (tail != null) if (tail != null)
{ {
SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque); SmoothRotateWithoutWrapping(tail, movementAngle + TailAngle.Value * Dir, MainLimb, TailTorque);
} }
} }
} }
else else if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{ {
movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2; movementAngle = Dir > 0 ? -MathHelper.PiOver2 : MathHelper.PiOver2;
if (reverse)
{
movementAngle = MathUtils.WrapAngleTwoPi(movementAngle - MathHelper.Pi);
}
if (MainLimb.type == LimbType.Head && HeadAngle.HasValue) if (MainLimb.type == LimbType.Head && HeadAngle.HasValue)
{ {
Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque); Collider.SmoothRotate(HeadAngle.Value * Dir, CurrentSwimParams.SteerTorque);
@@ -451,7 +454,7 @@ namespace Barotrauma
var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude); var waveAmplitude = Math.Abs(CurrentSwimParams.WaveAmplitude);
if (waveLength > 0 && waveAmplitude > 0) if (waveLength > 0 && waveAmplitude > 0)
{ {
WalkPos -= movement.Length() / Math.Abs(waveLength); WalkPos -= transformedMovement.Length() / Math.Abs(waveLength);
WalkPos = MathUtils.WrapAngleTwoPi(WalkPos); WalkPos = MathUtils.WrapAngleTwoPi(WalkPos);
} }
@@ -81,6 +81,9 @@ namespace Barotrauma
[Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")] [Serialize(AIBehaviorAfterAttack.FallBack, true), Editable(ToolTip = "The preferred AI behavior after the attack.")]
public AIBehaviorAfterAttack AfterAttack { get; private 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.")] [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; private set; } public float Range { get; private set; }
@@ -1186,6 +1186,10 @@ namespace Barotrauma
if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F)) if (PlayerInput.KeyHit(Microsoft.Xna.Framework.Input.Keys.F))
{ {
AnimController.ReleaseStuckLimbs(); AnimController.ReleaseStuckLimbs();
if (AIController != null && AIController is EnemyAIController enemyAI)
{
enemyAI.LatchOntoAI?.DeattachFromBody();
}
} }
#endif #endif
@@ -1266,14 +1270,6 @@ namespace Barotrauma
} }
} }
#if CLIENT
if (SelectedConstruction != null && SelectedConstruction.ActiveHUDs.Any(ic => ic.GuiFrame != null && HUD.CloseHUD(ic.GuiFrame.Rect)))
{
//emulate a Select input to get the character to deselect the item server-side
keys[(int)InputType.Select].Hit = true;
SelectedConstruction = null;
}
#endif
if (SelectedConstruction != null) if (SelectedConstruction != null)
{ {
if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this); if (IsKeyDown(InputType.Use)) SelectedConstruction.Use(deltaTime, this);
@@ -2,12 +2,15 @@
using Lidgren.Network; using Lidgren.Network;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
namespace Barotrauma.Items.Components namespace Barotrauma.Items.Components
{ {
partial class LevelResource : ItemComponent, IServerSerializable partial class LevelResource : ItemComponent, IServerSerializable
{ {
private float lastSentDeattachTimer;
[Serialize(1.0f, false)] [Serialize(1.0f, false)]
public float DeattachDuration public float DeattachDuration
{ {
@@ -21,12 +24,29 @@ namespace Barotrauma.Items.Components
get { return deattachTimer; } get { return deattachTimer; }
set set
{ {
deattachTimer = Math.Max(0.0f, value);
//clients don't deattach the item until the server says so (handled in ClientRead) //clients don't deattach the item until the server says so (handled in ClientRead)
if ((GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient) && deattachTimer >= DeattachDuration) if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient)
{
return;
}
deattachTimer = Math.Max(0.0f, value);
#if SERVER
if (deattachTimer >= DeattachDuration)
{
if (holdable.Attached){ item.CreateServerEvent(this); }
holdable.DeattachFromWall();
}
else if (Math.Abs(lastSentDeattachTimer - deattachTimer) > 0.1f)
{
item.CreateServerEvent(this);
lastSentDeattachTimer = deattachTimer;
}
#else
if (deattachTimer >= DeattachDuration)
{ {
holdable.DeattachFromWall(); holdable.DeattachFromWall();
} }
#endif
} }
} }
@@ -67,7 +87,10 @@ namespace Barotrauma.Items.Components
return; return;
} }
holdable.Reattachable = false; holdable.Reattachable = false;
holdable.PickingTime = float.MaxValue; if (requiredItems.Any())
{
holdable.PickingTime = float.MaxValue;
}
var body = item.body ?? holdable.Body; var body = item.body ?? holdable.Body;
@@ -18,6 +18,8 @@ namespace Barotrauma.Items.Components
private Vector2 pickedPosition; private Vector2 pickedPosition;
private float activeTimer; private float activeTimer;
private Vector2 debugRayStartPos, debugRayEndPos;
[Serialize(0.0f, false)] [Serialize(0.0f, false)]
public float Range { get; set; } public float Range { get; set; }
@@ -247,6 +249,7 @@ namespace Barotrauma.Items.Components
var levelResource = targetItem.GetComponent<LevelResource>(); var levelResource = targetItem.GetComponent<LevelResource>();
if (levelResource != null && levelResource.IsActive && if (levelResource != null && levelResource.IsActive &&
levelResource.requiredItems.Any() &&
levelResource.HasRequiredItems(user, addMessage: false)) levelResource.HasRequiredItems(user, addMessage: false))
{ {
levelResource.DeattachTimer += deltaTime; levelResource.DeattachTimer += deltaTime;
@@ -49,7 +49,7 @@ namespace Barotrauma.Items.Components
private bool shutDown; private bool shutDown;
const float AIUpdateInterval = 1.0f; const float AIUpdateInterval = 0.2f;
private float aiUpdateTimer; private float aiUpdateTimer;
private Character lastUser; private Character lastUser;
@@ -209,7 +209,7 @@ namespace Barotrauma.Items.Components
allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess); allowedFissionRate = Vector2.Lerp(new Vector2(20, AvailableFuel), new Vector2(10, AvailableFuel), degreeOfSuccess);
allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10); allowedFissionRate.X = Math.Min(allowedFissionRate.X, allowedFissionRate.Y - 10);
float heatAmount = fissionRate * (AvailableFuel / 100.0f) * 2.0f; float heatAmount = GetGeneratedHeat(fissionRate);
float temperatureDiff = (heatAmount - turbineOutput) - Temperature; float temperatureDiff = (heatAmount - turbineOutput) - Temperature;
Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff)); Temperature += MathHelper.Clamp(Math.Sign(temperatureDiff) * 10.0f * deltaTime, -Math.Abs(temperatureDiff), Math.Abs(temperatureDiff));
//if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime; //if (item.InWater && AvailableFuel < 100.0f) Temperature -= 12.0f * deltaTime;
@@ -313,6 +313,48 @@ namespace Barotrauma.Items.Components
} }
} }
private float GetGeneratedHeat(float fissionRate)
{
return fissionRate * (prevAvailableFuel / 100.0f) * 2.0f;
}
/// <summary>
/// Do we need more fuel to generate enough power to match the current load.
/// </summary>
/// <param name="minimumOutputRatio">How low we allow the output/load ratio to go before loading more fuel.
/// 1.0 = always load more fuel when maximum output is too low, 0.5 = load more if max output is 50% of the load</param>
private bool NeedMoreFuel(float minimumOutputRatio)
{
if (prevAvailableFuel <= 0.0f && load > 0.0f)
{
return true;
}
//fission rate is clamped to the amount of available fuel
float maxFissionRate = Math.Min(prevAvailableFuel, 100.0f);
float maxTurbineOutput = 100.0f;
//calculate the maximum output if the fission rate is cranked as high as it goes and turbine output is at max
float theoreticalMaxHeat = GetGeneratedHeat(fissionRate: maxFissionRate);
float temperatureFactor = Math.Min(theoreticalMaxHeat / 50.0f, 1.0f);
float theoreticalMaxOutput = Math.Min(maxTurbineOutput / 100.0f, temperatureFactor) * MaxPowerOutput;
//maximum output not enough, we need more fuel
return theoreticalMaxOutput < load * minimumOutputRatio;
}
private bool TooMuchFuel()
{
var containedItems = item.ContainedItems;
if (containedItems != null && containedItems.Count() <= 1) { return false; }
//get the amount of heat we'd generate if the fission rate was at the low end of the optimal range
float minimumHeat = GetGeneratedHeat(optimalFissionRate.X);
//if we need a very high turbine output to keep the engine from overheating, there's too much fuel
return minimumHeat > Math.Min(correctTurbineOutput * 1.5f, 90);
}
private void UpdateFailures(float deltaTime) private void UpdateFailures(float deltaTime)
{ {
if (temperature > allowedTemperature.Y) if (temperature > allowedTemperature.Y)
@@ -367,6 +409,11 @@ namespace Barotrauma.Items.Components
targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f); targetFissionRate = Math.Min(targetFissionRate + speed * 2 * deltaTime, 100.0f);
} }
targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f); targetFissionRate = MathHelper.Clamp(targetFissionRate, 0.0f, 100.0f);
//don't push the target too far from the current fission rate
//otherwise we may "overshoot", cranking the target fission rate all the way up because it takes a while
//for the actual fission rate and temperature to follow
targetFissionRate = MathHelper.Clamp(targetFissionRate, FissionRate - 5, FissionRate + 5);
} }
public override void UpdateBroken(float deltaTime, Camera cam) public override void UpdateBroken(float deltaTime, Camera cam)
@@ -441,12 +488,18 @@ namespace Barotrauma.Items.Components
} }
} }
//we need more fuel if (aiUpdateTimer > 0.0f)
if (-currPowerConsumption < load * 0.5f && prevAvailableFuel <= 0.0f) {
aiUpdateTimer -= deltaTime;
return false;
}
//load more fuel if the current maximum output is only 50% of the current load
if (NeedMoreFuel(minimumOutputRatio: 0.5f))
{ {
var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>()) var containFuelObjective = new AIObjectiveContainItem(character, new string[] { "fuelrod", "reactorfuel" }, item.GetComponent<ItemContainer>())
{ {
MinContainedAmount = containedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1, MinContainedAmount = item.ContainedItems.Count(i => i != null && i.Prefab.Identifier == "fuelrod" || i.HasTag("reactorfuel")) + 1,
GetItemPriority = (Item fuelItem) => GetItemPriority = (Item fuelItem) =>
{ {
if (fuelItem.ParentInventory?.Owner is Item) if (fuelItem.ParentInventory?.Owner is Item)
@@ -461,6 +514,7 @@ namespace Barotrauma.Items.Components
character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f); character?.Speak(TextManager.Get("DialogReactorFuel"), null, 0.0f, "reactorfuel", 30.0f);
aiUpdateTimer = AIUpdateInterval;
return false; return false;
} }
else if (TooMuchFuel()) else if (TooMuchFuel())
@@ -479,12 +533,6 @@ namespace Barotrauma.Items.Components
} }
} }
if (aiUpdateTimer > 0.0f)
{
aiUpdateTimer -= deltaTime;
return false;
}
if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item) if (lastUser != character && lastUser != null && lastUser.SelectedConstruction == item)
{ {
character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f); character.Speak(TextManager.Get("DialogReactorTaken"), null, 0.0f, "reactortaken", 10.0f);
@@ -506,7 +554,7 @@ namespace Barotrauma.Items.Components
{ {
AutoTemp = false; AutoTemp = false;
unsentChanges = true; unsentChanges = true;
UpdateAutoTemp(2.0f + degreeOfSuccess * 5.0f, 1.0f); UpdateAutoTemp(MathHelper.Lerp(0.5f, 2.0f, degreeOfSuccess), 1.0f);
} }
#if CLIENT #if CLIENT
onOffSwitch.BarScroll = 0.0f; onOffSwitch.BarScroll = 0.0f;
@@ -678,17 +678,24 @@ namespace Barotrauma
Body closestBody = null; Body closestBody = null;
if (allowInsideFixture) if (allowInsideFixture)
{ {
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.UnitY * 0.001f, rayStart + Vector2.UnitY * 0.001f); var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) => GameMain.World.QueryAABB((fixture) =>
{ {
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return false; } if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
closestFraction = 0.0f; closestFraction = 0.0f;
closestNormal = Vector2.Normalize(rayEnd - rayStart); closestNormal = Vector2.Normalize(rayEnd - rayStart);
if (fixture.Body != null) closestBody = fixture.Body; if (fixture.Body != null) closestBody = fixture.Body;
return true; return false;
}, ref aabb); }, ref aabb);
if (closestFraction <= 0.0f) if (closestFraction <= 0.0f)
{ {
lastPickedPosition = rayStart;
lastPickedFraction = closestFraction;
lastPickedNormal = closestNormal;
return closestBody; return closestBody;
} }
} }
@@ -740,14 +747,21 @@ namespace Barotrauma
if (allowInsideFixture) if (allowInsideFixture)
{ {
var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.UnitY * 0.001f, rayStart + Vector2.UnitY * 0.001f); var aabb = new FarseerPhysics.Collision.AABB(rayStart - Vector2.One * 0.001f, rayStart + Vector2.One * 0.001f);
GameMain.World.QueryAABB((fixture) => GameMain.World.QueryAABB((fixture) =>
{ {
if (bodies.Contains(fixture.Body) || fixture.Body == null) { return false; } if (bodies.Contains(fixture.Body) || fixture.Body == null) { return true; }
if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return false; } if (!CheckFixtureCollision(fixture, ignoredBodies, collisionCategory, ignoreSensors, customPredicate)) { return true; }
fixture.Body.GetTransform(out FarseerPhysics.Common.Transform transform);
if (!fixture.Shape.TestPoint(ref transform, ref rayStart)) { return true; }
closestFraction = 0.0f; closestFraction = 0.0f;
lastPickedPosition = rayStart;
lastPickedFraction = 0.0f;
lastPickedNormal = Vector2.Normalize(rayEnd - rayStart);
bodies.Add(fixture.Body); bodies.Add(fixture.Body);
return true; return false;
}, ref aabb); }, ref aabb);
} }
@@ -615,7 +615,7 @@ namespace Barotrauma
ID = (ushort)int.Parse(element.Attribute("ID").Value) ID = (ushort)int.Parse(element.Attribute("ID").Value)
}; };
Enum.TryParse(element.GetAttributeString("spawn", "Path"), out w.spawnType); w.spawnType = spawnType;
string idCardDescString = element.GetAttributeString("idcarddesc", ""); string idCardDescString = element.GetAttributeString("idcarddesc", "");
if (!string.IsNullOrWhiteSpace(idCardDescString)) if (!string.IsNullOrWhiteSpace(idCardDescString))