(1762f02b3) Merge branch 'dev' into human-ai
This commit is contained in:
@@ -122,6 +122,7 @@ namespace Barotrauma
|
||||
private readonly float memoryFadeTime = 0.5f;
|
||||
|
||||
public LatchOntoAI LatchOntoAI { get; private set; }
|
||||
public SwarmBehavior SwarmBehavior { get; private set; }
|
||||
|
||||
public bool AttackHumans
|
||||
{
|
||||
@@ -215,6 +216,10 @@ 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;
|
||||
@@ -364,12 +369,8 @@ namespace Barotrauma
|
||||
default:
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
|
||||
SwarmBehavior?.Update(deltaTime);
|
||||
steeringManager.Update(Character.AnimController.GetCurrentSpeed(run));
|
||||
}
|
||||
|
||||
@@ -790,6 +791,7 @@ namespace Barotrauma
|
||||
{
|
||||
UpdateLimbAttack(deltaTime, AttackingLimb, attackSimPos, distance);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private bool SteerThroughGap(Structure wall, WallSection section, Vector2 targetWorldPos, float deltaTime)
|
||||
@@ -1070,6 +1072,8 @@ namespace Barotrauma
|
||||
|
||||
private bool IsProperlyLatchedOnSub => LatchOntoAI != null && LatchOntoAI.IsAttachedToSub && SelectedAiTarget?.Entity == wallTarget?.Structure;
|
||||
|
||||
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
|
||||
|
||||
+4
-2
@@ -66,7 +66,8 @@ namespace Barotrauma
|
||||
if (!goToObjective.IsCompleted() && !goToObjective.CanBeCompleted)
|
||||
{
|
||||
abandon = true;
|
||||
character?.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f);
|
||||
// TODO: Add: "Can't repair [item]!"
|
||||
//character?.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f);
|
||||
}
|
||||
goToObjective = null;
|
||||
}
|
||||
@@ -116,7 +117,8 @@ namespace Barotrauma
|
||||
{
|
||||
// If the current condition is less than the previous condition, we can't complete the task, so let's abandon it. The item is probably deteriorating at a greater speed than we can repair it.
|
||||
abandon = true;
|
||||
character?.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f);
|
||||
// TODO: Add: "Can't repair [item]!"
|
||||
//character?.Speak(TextManager.Get("DialogCannotRepair").Replace("[itemname]", Item.Name), null, 0.0f, "cannotrepair", 10.0f);
|
||||
}
|
||||
}
|
||||
repairable.CurrentFixer = abandon && repairable.CurrentFixer == character ? null : character;
|
||||
|
||||
@@ -14,16 +14,11 @@ namespace Barotrauma
|
||||
private float maxDistFromCenter;
|
||||
private float cohesion;
|
||||
|
||||
public List<AICharacter> Members { get; private set; } = new List<AICharacter>();
|
||||
public HashSet<AICharacter> ActiveMembers { get; private set; } = new HashSet<AICharacter>();
|
||||
private List<AICharacter> members = new List<AICharacter>();
|
||||
|
||||
private EnemyAIController ai;
|
||||
private AIController ai;
|
||||
|
||||
public bool IsActive { get; set; }
|
||||
public bool IsEnoughMembers => ActiveMembers.Count > 1;
|
||||
|
||||
|
||||
public SwarmBehavior(XElement element, EnemyAIController ai)
|
||||
public SwarmBehavior(XElement element, AIController ai)
|
||||
{
|
||||
this.ai = ai;
|
||||
minDistFromClosest = ConvertUnits.ToSimUnits(element.GetAttributeFloat("mindistfromclosest", 10.0f));
|
||||
@@ -37,36 +32,21 @@ namespace Barotrauma
|
||||
{
|
||||
if (character.AIController is EnemyAIController enemyAI && enemyAI.SwarmBehavior != null)
|
||||
{
|
||||
enemyAI.SwarmBehavior.Members = swarm.ToList();
|
||||
enemyAI.SwarmBehavior.members = swarm.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Refresh()
|
||||
public void Update(float deltaTime)
|
||||
{
|
||||
Members.RemoveAll(m => m.IsDead || m.Removed);
|
||||
foreach (var member in Members)
|
||||
{
|
||||
if (!member.AIController.Enabled && member.IsRemotePlayer || Character.Controlled == member || !((EnemyAIController)member.AIController).SwarmBehavior.IsActive)
|
||||
{
|
||||
ActiveMembers.Remove(member);
|
||||
}
|
||||
else
|
||||
{
|
||||
ActiveMembers.Add(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
members.RemoveAll(m => m.IsDead || m.Removed);
|
||||
if (members.Count < 2) { return; }
|
||||
|
||||
public void UpdateSteering(float deltaTime)
|
||||
{
|
||||
if (!IsActive) { return; }
|
||||
if (!IsEnoughMembers) { return; }
|
||||
//calculate the "center of mass" of the swarm and the distance to the closest character in the swarm
|
||||
float closestDistSqr = float.MaxValue;
|
||||
Vector2 center = Vector2.Zero;
|
||||
AICharacter closest = null;
|
||||
foreach (AICharacter member in Members)
|
||||
foreach (AICharacter member in members)
|
||||
{
|
||||
center += member.SimPosition;
|
||||
if (member == ai.Character) { continue; }
|
||||
@@ -77,7 +57,7 @@ namespace Barotrauma
|
||||
closest = member;
|
||||
}
|
||||
}
|
||||
center /= Members.Count;
|
||||
center /= members.Count;
|
||||
|
||||
if (closest == null) { return; }
|
||||
|
||||
@@ -103,11 +83,11 @@ namespace Barotrauma
|
||||
if (cohesion > 0.0f)
|
||||
{
|
||||
Vector2 avgVel = Vector2.Zero;
|
||||
foreach (AICharacter member in Members)
|
||||
foreach (AICharacter member in members)
|
||||
{
|
||||
avgVel += member.AnimController.TargetMovement;
|
||||
}
|
||||
avgVel /= Members.Count;
|
||||
avgVel /= members.Count;
|
||||
ai.SteeringManager.SteeringManual(deltaTime, avgVel * cohesion);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -588,6 +588,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
float prevWalkPos = WalkPos;
|
||||
WalkPos -= MainLimb.LinearVelocity.X * (CurrentAnimationParams.CycleSpeed / RagdollParams.JointScale / 100.0f);
|
||||
|
||||
Vector2 transformedStepSize = Vector2.Zero;
|
||||
@@ -623,6 +624,11 @@ 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,
|
||||
@@ -631,13 +637,20 @@ 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,
|
||||
|
||||
@@ -1131,12 +1131,12 @@ namespace Barotrauma
|
||||
#if CLIENT
|
||||
if (Math.Abs(leftFootPos - prevLeftFootPos) > stepHeight && leftFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: leftFoot.WorldPosition);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", leftFoot.WorldPosition, hullGuess: currentHull);
|
||||
leftFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
if (Math.Abs(rightFootPos - prevRightFootPos) > stepHeight && rightFoot.LastImpactSoundTime < Timing.TotalTime - Limb.SoundInterval)
|
||||
{
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", volume: 0.5f, range: 500.0f, position: rightFoot.WorldPosition);
|
||||
SoundPlayer.PlaySound("footstep_armor_heavy", rightFoot.WorldPosition, hullGuess: currentHull);
|
||||
rightFoot.LastImpactSoundTime = (float)Timing.TotalTime;
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -283,6 +283,7 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionName + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -292,6 +293,7 @@ namespace Barotrauma
|
||||
if (afflictionPrefab == null)
|
||||
{
|
||||
DebugConsole.ThrowError("Error in Attack (" + parentDebugName + ") - Affliction prefab \"" + afflictionIdentifier + "\" not found.");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2172,7 +2172,8 @@ 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 (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return; }
|
||||
if (string.IsNullOrEmpty(message)) { return; }
|
||||
|
||||
//already sent a similar message a moment ago
|
||||
if (!string.IsNullOrEmpty(identifier) && minDurationBetweenSimilar > 0.0f &&
|
||||
@@ -2181,7 +2182,6 @@ namespace Barotrauma
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
aiChatMessageQueue.Add(new AIChatMessage(message, messageType, identifier, delay));
|
||||
}
|
||||
|
||||
@@ -2642,6 +2642,10 @@ namespace Barotrauma
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
#if CLIENT
|
||||
GameMain.GameSession?.CrewManager?.RemoveCharacter(this);
|
||||
#endif
|
||||
|
||||
@@ -266,6 +266,12 @@ 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;
|
||||
@@ -467,7 +473,13 @@ namespace Barotrauma
|
||||
|
||||
private void AddLimbAffliction(Limb limb, Affliction newAffliction)
|
||||
{
|
||||
if (!newAffliction.Prefab.LimbSpecific) return;
|
||||
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;
|
||||
}
|
||||
AddLimbAffliction(limbHealths[limb.HealthIndex], newAffliction);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
GameAnalyticsManager.AddErrorEventOnce("DebugConsole.SpawnItem:Error", GameAnalyticsSDK.Net.EGAErrorSeverity.Error, errorMsg + '\n' + e.Message + '\n' + e.StackTrace);
|
||||
}
|
||||
},
|
||||
() =>
|
||||
@@ -615,7 +615,7 @@ namespace Barotrauma
|
||||
NewMessage(Hull.EditWater ? "Water editing on" : "Water editing off", Color.White);
|
||||
}, isCheat: true));
|
||||
|
||||
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) =>
|
||||
commands.Add(new Command("fire|editfire", "fire/editfire: Allows putting up fires by left clicking.", (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 Vector2 SonarPosition
|
||||
public virtual IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get { return Vector2.Zero; }
|
||||
get { return Enumerable.Empty<Vector2>(); }
|
||||
}
|
||||
|
||||
public string SonarLabel
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using Microsoft.Xna.Framework;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Barotrauma
|
||||
{
|
||||
@@ -10,30 +12,36 @@ namespace Barotrauma
|
||||
|
||||
private int monsterCount;
|
||||
|
||||
private Vector2 sonarPosition;
|
||||
private readonly List<Character> monsters = new List<Character>();
|
||||
private readonly List<Vector2> sonarPositions = new List<Vector2>();
|
||||
|
||||
public override Vector2 SonarPosition
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get { return monster != null && !monster.IsDead ? sonarPosition : Vector2.Zero; }
|
||||
get
|
||||
{
|
||||
return sonarPositions;
|
||||
}
|
||||
}
|
||||
|
||||
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 = 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;
|
||||
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);
|
||||
}
|
||||
|
||||
public override void Update(float deltaTime)
|
||||
@@ -45,7 +53,15 @@ namespace Barotrauma
|
||||
var activeMonsters = monsters.Where(m => m != null && !m.Removed && !m.IsDead);
|
||||
if (activeMonsters.Any())
|
||||
{
|
||||
sonarPosition = monster.Position;
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -16,11 +16,18 @@ namespace Barotrauma
|
||||
|
||||
private int state;
|
||||
|
||||
public override Vector2 SonarPosition
|
||||
public override IEnumerable<Vector2> SonarPositions
|
||||
{
|
||||
get
|
||||
{
|
||||
return state > 0 ? Vector2.Zero : ConvertUnits.ToDisplayUnits(item.SimPosition);
|
||||
if (state > 0 )
|
||||
{
|
||||
Enumerable.Empty<Vector2>();
|
||||
}
|
||||
else
|
||||
{
|
||||
yield return 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 = false;
|
||||
#if CLIENT
|
||||
isClient = GameMain.Client != null;
|
||||
#endif
|
||||
bool isClient = GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient;
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -855,6 +855,9 @@ namespace Barotrauma
|
||||
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())
|
||||
@@ -977,14 +980,8 @@ namespace Barotrauma
|
||||
ToolBox.IsProperFilenameCase(file.Path);
|
||||
}
|
||||
}
|
||||
if (!SelectedContentPackages.Any())
|
||||
{
|
||||
var availablePackage = ContentPackage.List.FirstOrDefault(cp => cp.IsCompatible() && cp.CorePackage);
|
||||
if (availablePackage != null)
|
||||
{
|
||||
SelectedContentPackages.Add(availablePackage);
|
||||
}
|
||||
}
|
||||
|
||||
EnsureCoreContentPackageSelected();
|
||||
|
||||
//save to get rid of the invalid selected packages in the config file
|
||||
if (missingPackagePaths.Count > 0 || incompatiblePackages.Count > 0) { SaveNewPlayerConfig(); }
|
||||
@@ -1003,6 +1000,25 @@ 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
|
||||
@@ -1032,7 +1048,9 @@ namespace Barotrauma
|
||||
new XAttribute("aimassistamount", aimAssistAmount),
|
||||
new XAttribute("enablemouselook", EnableMouseLook),
|
||||
new XAttribute("chatopen", ChatOpen),
|
||||
new XAttribute("crewmenuopen", CrewMenuOpen));
|
||||
new XAttribute("crewmenuopen", CrewMenuOpen),
|
||||
new XAttribute("campaigndisclaimershown", CampaignDisclaimerShown),
|
||||
new XAttribute("editordisclaimershown", EditorDisclaimerShown));
|
||||
|
||||
if (!ShowUserStatisticsPrompt)
|
||||
{
|
||||
|
||||
@@ -122,10 +122,12 @@ 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...)
|
||||
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
|
||||
if (subItem.Condition > 0.0f)
|
||||
{
|
||||
subItem.GetComponent<ItemContainer>()?.Item.ApplyStatusEffects(ActionType.OnUse, deltaTime);
|
||||
}
|
||||
if (projectile != null) break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,8 +71,10 @@ namespace Barotrauma.Items.Components
|
||||
var targetItem = inputContainer.Inventory.Items.LastOrDefault(i => i != null);
|
||||
if (targetItem == null) { return; }
|
||||
|
||||
progressState = Math.Min(progressTimer / targetItem.Prefab.DeconstructTime, 1.0f);
|
||||
if (progressTimer > targetItem.Prefab.DeconstructTime)
|
||||
float deconstructTime = targetItem.Prefab.DeconstructItems.Any() ? targetItem.Prefab.DeconstructTime : 1.0f;
|
||||
|
||||
progressState = Math.Min(progressTimer / deconstructTime, 1.0f);
|
||||
if (progressTimer > deconstructTime)
|
||||
{
|
||||
foreach (DeconstructItem deconstructProduct in targetItem.Prefab.DeconstructItems)
|
||||
{
|
||||
@@ -100,10 +102,13 @@ namespace Barotrauma.Items.Components
|
||||
}
|
||||
}
|
||||
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
if (targetItem.Prefab.DeconstructItems.Any())
|
||||
{
|
||||
inputContainer.Inventory.RemoveItem(targetItem);
|
||||
Entity.Spawner.AddToRemoveQueue(targetItem);
|
||||
MoveInputQueue();
|
||||
PutItemsToLinkedContainer();
|
||||
}
|
||||
|
||||
if (inputContainer.Inventory.Items.Any(i => i != null))
|
||||
{
|
||||
|
||||
@@ -473,6 +473,8 @@ 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
|
||||
|
||||
@@ -328,6 +328,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;
|
||||
|
||||
@@ -201,7 +201,7 @@ namespace Barotrauma.Items.Components
|
||||
if (sparkSounds.Count > 0)
|
||||
{
|
||||
var sparkSound = sparkSounds[Rand.Int(sparkSounds.Count)];
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, sparkSound.Volume, sparkSound.Range, pt.item.WorldPosition, pt.item.CurrentHull);
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, pt.item.WorldPosition, sparkSound.Volume, sparkSound.Range, 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, powerOnSound.Volume, powerOnSound.Range, item.WorldPosition, item.CurrentHull);
|
||||
SoundPlayer.PlaySound(powerOnSound.Sound, item.WorldPosition, powerOnSound.Volume, powerOnSound.Range, item.CurrentHull);
|
||||
powerOnSoundPlayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, sparkSound.Volume, sparkSound.Range, item.WorldPosition, item.CurrentHull);
|
||||
SoundPlayer.PlaySound(sparkSound.Sound, item.WorldPosition, sparkSound.Volume, sparkSound.Range, item.CurrentHull);
|
||||
}
|
||||
#endif
|
||||
lightBrightness = 0.0f;
|
||||
|
||||
@@ -928,14 +928,9 @@ namespace Barotrauma
|
||||
|
||||
public void ApplyStatusEffects(ActionType type, float deltaTime, Character character = null, Limb limb = null, bool isNetworkEvent = false)
|
||||
{
|
||||
if (statusEffectLists == null) return;
|
||||
|
||||
if (!statusEffectLists.TryGetValue(type, out List<StatusEffect> statusEffects)) return;
|
||||
|
||||
bool broken = condition <= 0.0f;
|
||||
foreach (StatusEffect effect in statusEffects)
|
||||
if (!hasStatusEffectsOfType[(int)type]) { return; }
|
||||
foreach (StatusEffect effect in statusEffectLists[type])
|
||||
{
|
||||
if (broken && effect.type != ActionType.OnBroken) continue;
|
||||
ApplyStatusEffect(effect, type, deltaTime, character, limb, isNetworkEvent, false);
|
||||
}
|
||||
}
|
||||
@@ -1052,6 +1047,8 @@ namespace Barotrauma
|
||||
aiTarget.SoundRange -= deltaTime * 1000.0f;
|
||||
}
|
||||
|
||||
bool broken = condition <= 0.0f;
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsServer)
|
||||
{
|
||||
sendConditionUpdateTimer -= deltaTime;
|
||||
@@ -1127,6 +1124,10 @@ 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; }
|
||||
@@ -1206,7 +1207,7 @@ namespace Barotrauma
|
||||
|
||||
if (GameMain.NetworkMember != null && GameMain.NetworkMember.IsClient) { return true; }
|
||||
|
||||
if (ImpactTolerance > 0.0f && impact > ImpactTolerance)
|
||||
if (ImpactTolerance > 0.0f && condition > 0.0f && impact > ImpactTolerance)
|
||||
{
|
||||
ApplyStatusEffects(ActionType.OnImpact, 1.0f);
|
||||
#if SERVER
|
||||
|
||||
@@ -24,8 +24,6 @@ namespace Barotrauma
|
||||
|
||||
private bool removed;
|
||||
|
||||
private bool removed;
|
||||
|
||||
#if CLIENT
|
||||
private List<Decal> burnDecals = new List<Decal>();
|
||||
#endif
|
||||
|
||||
@@ -299,6 +299,25 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
|
||||
public string DisplayName
|
||||
{
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
private string roomName;
|
||||
[Editable, Serialize("", true, translationTextTag: "RoomName.")]
|
||||
public string RoomName
|
||||
{
|
||||
get { return roomName; }
|
||||
set
|
||||
{
|
||||
if (roomName == value) { return; }
|
||||
roomName = value;
|
||||
DisplayName = TextManager.Get(roomName, returnNull: true) ?? roomName;
|
||||
}
|
||||
}
|
||||
|
||||
public override Rectangle Rect
|
||||
{
|
||||
get
|
||||
@@ -633,11 +652,6 @@ 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)
|
||||
@@ -805,11 +819,6 @@ 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)
|
||||
|
||||
@@ -522,15 +522,9 @@ 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)]
|
||||
#else
|
||||
[Serialize(1f, false)]
|
||||
#endif
|
||||
public float Scale { get; set; } = 1;
|
||||
public virtual float Scale { get; set; } = 1;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,32 @@ 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
|
||||
@@ -169,9 +195,13 @@ 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;
|
||||
@@ -189,11 +219,11 @@ namespace Barotrauma
|
||||
|
||||
public float BodyWidth
|
||||
{
|
||||
get { return Prefab.BodyWidth > 0.0f ? Prefab.BodyWidth : rect.Width; }
|
||||
get { return Prefab.BodyWidth > 0.0f ? Prefab.BodyWidth * scale : rect.Width; }
|
||||
}
|
||||
public float BodyHeight
|
||||
{
|
||||
get { return Prefab.BodyHeight > 0.0f ? Prefab.BodyHeight : rect.Height; }
|
||||
get { return Prefab.BodyHeight > 0.0f ? Prefab.BodyHeight * scale : rect.Height; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -965,6 +995,7 @@ namespace Barotrauma
|
||||
|
||||
private void UpdateSections()
|
||||
{
|
||||
if (Bodies == null) return;
|
||||
foreach (Body b in Bodies)
|
||||
{
|
||||
GameMain.World.RemoveBody(b);
|
||||
@@ -1028,9 +1059,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);
|
||||
Vector2 bodyOffset = ConvertUnits.ToSimUnits(Prefab.BodyOffset) * scale;
|
||||
if (FlippedX) { bodyOffset.X = -bodyOffset.X; }
|
||||
if (FlippedY) { bodyOffset.Y = -bodyOffset.Y; }
|
||||
|
||||
@@ -1050,7 +1081,8 @@ 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
|
||||
@@ -1191,6 +1223,9 @@ 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),
|
||||
@@ -1203,15 +1238,6 @@ namespace Barotrauma
|
||||
if (FlippedX) element.Add(new XAttribute("flippedx", true));
|
||||
if (FlippedY) element.Add(new XAttribute("flippedy", true));
|
||||
|
||||
if (FlippedX) element.Add(new XAttribute("flippedx", true));
|
||||
if (FlippedY) element.Add(new XAttribute("flippedy", true));
|
||||
|
||||
if (FlippedX) element.Add(new XAttribute("flippedx", true));
|
||||
if (FlippedY) element.Add(new XAttribute("flippedy", true));
|
||||
|
||||
if (FlippedX) element.Add(new XAttribute("flippedx", true));
|
||||
if (FlippedY) element.Add(new XAttribute("flippedy", true));
|
||||
|
||||
for (int i = 0; i < Sections.Length; i++)
|
||||
{
|
||||
if (Sections[i].damage == 0.0f) continue;
|
||||
|
||||
@@ -1117,6 +1117,7 @@ namespace Barotrauma
|
||||
}
|
||||
}
|
||||
savedSubmarines.Add(new Submarine(filePath));
|
||||
savedSubmarines = savedSubmarines.OrderBy(s => s.filePath ?? "").ToList();
|
||||
}
|
||||
|
||||
public static void RefreshSavedSubs()
|
||||
|
||||
@@ -377,6 +377,12 @@ namespace Barotrauma.Networking
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool VoipEnabled {
|
||||
get;
|
||||
private set;
|
||||
}
|
||||
|
||||
[Serialize(true, true)]
|
||||
public bool EndRoundAtLevelEnd
|
||||
{
|
||||
|
||||
@@ -66,6 +66,16 @@ 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)
|
||||
{
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -336,9 +336,12 @@ namespace Barotrauma
|
||||
UnlockAchievement("survivereactormeltdown");
|
||||
}
|
||||
#endif
|
||||
|
||||
var charactersInSub = Character.CharacterList.FindAll(c => !c.IsDead &&
|
||||
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)));
|
||||
|
||||
if (charactersInSub.Count == 1)
|
||||
{
|
||||
//there must be some non-enemy casualties to get the last mant standing achievement
|
||||
@@ -346,7 +349,11 @@ namespace Barotrauma
|
||||
{
|
||||
UnlockAchievement(charactersInSub[0], "lastmanstanding");
|
||||
}
|
||||
else if (!Character.CharacterList.Any(c => !(c.AIController is EnemyAIController)))
|
||||
//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)))
|
||||
{
|
||||
UnlockAchievement(charactersInSub[0], "lonesailor");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user