(b451554e8) Merge remote-tracking branch 'origin/dev' into keymapping-refactor-test

This commit is contained in:
Joonas Rikkonen
2019-04-04 11:10:29 +03:00
parent b08a31a68f
commit cc122f2bbf
52 changed files with 601 additions and 786 deletions
@@ -530,7 +530,8 @@ namespace Barotrauma
}
}
if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater)
if (Math.Abs(Character.AnimController.movement.X) > 0.1f && !Character.AnimController.InWater &&
(GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer || Character.Controlled == Character))
{
Character.AnimController.TargetDir = Character.WorldPosition.X < attackWorldPos.X ? Direction.Right : Direction.Left;
}
@@ -696,7 +696,7 @@ namespace Barotrauma
return;
}
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength);
limb.body.ApplyForce(diff * (float)(Math.Sin(WalkPos) * Math.Sqrt(limb.Mass)) * 30.0f * animStrength, maxVelocity: 10.0f);
}
}
@@ -5,6 +5,7 @@ using System;
using System.Collections.Generic;
using System.Linq;
using Barotrauma.Extensions;
using Barotrauma.Networking;
namespace Barotrauma
{
@@ -1939,7 +1939,7 @@ namespace Barotrauma
//cannot be protected from pressure when below crush depth
protectedFromPressure = protectedFromPressure && WorldPosition.Y > CharacterHealth.CrushDepth;
//implode if not protected from pressure, and either outside or in a high-pressure hull
if (!protectedFromPressure &&
if (!protectedFromPressure &&
(AnimController.CurrentHull == null || AnimController.CurrentHull.LethalPressure >= 80.0f))
{
if (CharacterHealth.PressureKillDelay <= 0.0f)
@@ -1954,7 +1954,7 @@ namespace Barotrauma
if (PressureTimer >= 100.0f)
{
if (Controlled == this) cam.Zoom = 5.0f;
if (Controlled == this) { cam.Zoom = 5.0f; }
if (GameMain.NetworkMember == null || !GameMain.NetworkMember.IsClient)
{
Implode();
@@ -2235,7 +2235,7 @@ namespace Barotrauma
if (limbHit == null) return new AttackResult();
limbHit.body?.ApplyLinearImpulse(attack.TargetImpulseWorld + attack.TargetForceWorld * deltaTime);
limbHit.body?.ApplyLinearImpulse(attack.TargetImpulseWorld + attack.TargetForceWorld * deltaTime, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
#if SERVER
if (attacker is Character attackingCharacter && attackingCharacter.AIController == null)
{
@@ -2321,7 +2321,8 @@ namespace Barotrauma
{
Vector2 diff = dir;
if (diff == Vector2.Zero) diff = Rand.Vector(1.0f);
hitLimb.body.ApplyLinearImpulse(Vector2.Normalize(diff) * attackImpulse, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff));
hitLimb.body.ApplyLinearImpulse(Vector2.Normalize(diff) * attackImpulse, hitLimb.SimPosition + ConvertUnits.ToSimUnits(diff),
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
Vector2 simPos = hitLimb.SimPosition + ConvertUnits.ToSimUnits(dir);
AttackResult attackResult = hitLimb.AddDamage(simPos, afflictions, playSound);
@@ -103,6 +103,7 @@ namespace Barotrauma
}
public float CrushDepth { get; private set; }
public float PressureKillDelay { get; private set; } = 5.0f;
public float Vitality { get; private set; }
@@ -626,13 +626,17 @@ namespace Barotrauma
Limb limb = character.AnimController.Limbs[limbIndex];
Vector2 forcePos = limb.pullJoint == null ? limb.body.SimPosition : limb.pullJoint.WorldAnchorA;
limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
limb.body.ApplyLinearImpulse(limb.Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos,
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
}
else
{
Vector2 forcePos = pullJoint == null ? body.SimPosition : pullJoint.WorldAnchorA;
body.ApplyLinearImpulse(Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition), forcePos);
body.ApplyLinearImpulse(
Mass * attack.Force * Vector2.Normalize(attackSimPos - SimPosition),
forcePos,
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
}
return wasHit;
@@ -842,6 +842,7 @@ namespace Barotrauma
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount);
EnableMouseLook = doc.Root.GetAttributeBool("enablemouselook", EnableMouseLook);
foreach (XElement subElement in doc.Root.Elements())
{
@@ -910,7 +911,18 @@ namespace Barotrauma
foreach (XElement subElement in doc.Root.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath));
}
foreach (ContentPackage incompatiblePackage in incompatiblePackages)
{
DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
.Replace("[packagename]", incompatiblePackage.Name)
.Replace("[packageversion]", incompatiblePackage.GameVersion.ToString())
.Replace("[gameversion]", GameMain.Version.ToString()));
}
foreach (ContentPackage contentPackage in SelectedContentPackages)
{
foreach (ContentFile file in contentPackage.Files)
{
case "contentpackage":
string path = System.IO.Path.GetFullPath(subElement.GetAttributeString("path", ""));
@@ -1018,370 +1030,6 @@ namespace Barotrauma
new XAttribute("quickstartsub", QuickStartSubmarineName),
new XAttribute("requiresteamauthentication", requireSteamAuthentication),
new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems),
new XAttribute("aimassistamount", aimAssistAmount));
if (!ShowUserStatisticsPrompt)
{
doc.Root.Add(new XAttribute("senduserstatistics", sendUserStatistics));
}
XElement gMode = doc.Root.Element("graphicsmode");
if (gMode == null)
{
gMode = new XElement("graphicsmode");
doc.Root.Add(gMode);
}
if (GraphicsWidth == 0 || GraphicsHeight == 0)
{
gMode.ReplaceAttributes(new XAttribute("displaymode", windowMode));
}
else
{
gMode.ReplaceAttributes(
new XAttribute("width", GraphicsWidth),
new XAttribute("height", GraphicsHeight),
new XAttribute("vsync", VSyncEnabled),
new XAttribute("displaymode", windowMode));
}
XElement gSettings = doc.Root.Element("graphicssettings");
if (gSettings == null)
{
gSettings = new XElement("graphicssettings");
doc.Root.Add(gSettings);
}
gSettings.ReplaceAttributes(
new XAttribute("particlelimit", ParticleLimit),
new XAttribute("lightmapscale", LightMapScale),
new XAttribute("specularity", SpecularityEnabled),
new XAttribute("chromaticaberration", ChromaticAberrationEnabled),
new XAttribute("losmode", LosMode),
new XAttribute("hudscale", HUDScale),
new XAttribute("inventoryscale", InventoryScale));
foreach (ContentPackage contentPackage in SelectedContentPackages)
{
if (contentPackage.Path.Contains(vanillaContentPackagePath))
{
doc.Root.Add(new XElement("contentpackage", new XAttribute("path", contentPackage.Path)));
break;
}
}
var keyMappingElement = new XElement("keymapping");
doc.Root.Add(keyMappingElement);
for (int i = 0; i < keyMapping.Length; i++)
{
if (keyMapping[i].MouseButton == null)
{
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].Key));
}
else
{
keyMappingElement.Add(new XAttribute(((InputType)i).ToString(), keyMapping[i].MouseButton));
}
}
var gameplay = new XElement("gameplay");
var jobPreferences = new XElement("jobpreferences");
foreach (string jobName in JobPreferences)
{
jobPreferences.Add(new XElement("job", new XAttribute("identifier", jobName)));
}
gameplay.Add(jobPreferences);
doc.Root.Add(gameplay);
var playerElement = new XElement("player",
new XAttribute("name", defaultPlayerName ?? ""),
new XAttribute("headindex", CharacterHeadIndex),
new XAttribute("gender", CharacterGender),
new XAttribute("race", CharacterRace),
new XAttribute("hairindex", CharacterHairIndex),
new XAttribute("beardindex", CharacterBeardIndex),
new XAttribute("moustacheindex", CharacterMoustacheIndex),
new XAttribute("faceattachmentindex", CharacterFaceAttachmentIndex));
doc.Root.Add(playerElement);
XmlWriterSettings settings = new XmlWriterSettings
{
Indent = true,
OmitXmlDeclaration = true,
NewLineOnAttributes = true
};
try
{
using (var writer = XmlWriter.Create(savePath, settings))
{
doc.WriteTo(writer);
writer.Flush();
}
}
catch (Exception e)
{
DebugConsole.ThrowError("Saving game settings failed.", e);
GameAnalyticsManager.AddErrorEventOnce("GameSettings.Save:SaveFailed", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Saving game settings failed.\n" + e.Message + "\n" + e.StackTrace);
}
}
#endregion
#region Load PlayerConfig
// TODO: DRY
public void LoadPlayerConfig()
{
XDocument doc = XMLExtensions.LoadXml(playerSavePath);
if (doc == null || doc.Root == null)
{
ShowUserStatisticsPrompt = true;
SaveNewPlayerConfig();
return;
}
Language = doc.Root.GetAttributeString("language", Language);
AutoCheckUpdates = doc.Root.GetAttributeBool("autocheckupdates", AutoCheckUpdates);
sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", true);
XElement graphicsMode = doc.Root.Element("graphicsmode");
GraphicsWidth = graphicsMode.GetAttributeInt("width", GraphicsWidth);
GraphicsHeight = graphicsMode.GetAttributeInt("height", GraphicsHeight);
VSyncEnabled = graphicsMode.GetAttributeBool("vsync", VSyncEnabled);
XElement graphicsSettings = doc.Root.Element("graphicssettings");
ParticleLimit = graphicsSettings.GetAttributeInt("particlelimit", ParticleLimit);
LightMapScale = MathHelper.Clamp(graphicsSettings.GetAttributeFloat("lightmapscale", LightMapScale), 0.1f, 1.0f);
SpecularityEnabled = graphicsSettings.GetAttributeBool("specularity", SpecularityEnabled);
ChromaticAberrationEnabled = graphicsSettings.GetAttributeBool("chromaticaberration", ChromaticAberrationEnabled);
HUDScale = graphicsSettings.GetAttributeFloat("hudscale", HUDScale);
InventoryScale = graphicsSettings.GetAttributeFloat("inventoryscale", InventoryScale);
var losModeStr = graphicsSettings.GetAttributeString("losmode", "Transparent");
if (!Enum.TryParse(losModeStr, out losMode))
{
losMode = LosMode.Transparent;
}
#if CLIENT
if (GraphicsWidth == 0 || GraphicsHeight == 0)
{
GraphicsWidth = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Width;
GraphicsHeight = GraphicsAdapter.DefaultAdapter.CurrentDisplayMode.Height;
}
#endif
var windowModeStr = graphicsMode.GetAttributeString("displaymode", "Fullscreen");
if (!Enum.TryParse(windowModeStr, out windowMode))
{
windowMode = WindowMode.Fullscreen;
}
XElement audioSettings = doc.Root.Element("audio");
if (audioSettings != null)
{
SoundVolume = audioSettings.GetAttributeFloat("soundvolume", SoundVolume);
MusicVolume = audioSettings.GetAttributeFloat("musicvolume", MusicVolume);
VoiceChatVolume = audioSettings.GetAttributeFloat("voicechatvolume", VoiceChatVolume);
string voiceSettingStr = audioSettings.GetAttributeString("voicesetting", "Disabled");
VoiceCaptureDevice = audioSettings.GetAttributeString("voicecapturedevice", "");
NoiseGateThreshold = audioSettings.GetAttributeFloat("noisegatethreshold", -45);
var voiceSetting = VoiceMode.Disabled;
if (Enum.TryParse(voiceSettingStr, out voiceSetting))
{
VoiceSetting = voiceSetting;
}
}
useSteamMatchmaking = doc.Root.GetAttributeBool("usesteammatchmaking", useSteamMatchmaking);
requireSteamAuthentication = doc.Root.GetAttributeBool("requiresteamauthentication", requireSteamAuthentication);
EnableSplashScreen = doc.Root.GetAttributeBool("enablesplashscreen", EnableSplashScreen);
AimAssistAmount = doc.Root.GetAttributeFloat("aimassistamount", AimAssistAmount);
EnableMouseLook = doc.Root.GetAttributeBool("enablemouselook", EnableMouseLook);
foreach (XElement subElement in doc.Root.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "keymapping":
foreach (XAttribute attribute in subElement.Attributes())
{
if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType))
{
if (int.TryParse(attribute.Value.ToString(), out int mouseButton))
{
keyMapping[(int)inputType] = new KeyOrMouse(mouseButton);
}
else
{
if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
{
keyMapping[(int)inputType] = new KeyOrMouse(key);
}
}
}
}
break;
case "gameplay":
jobPreferences = new List<string>();
foreach (XElement ele in subElement.Element("jobpreferences").Elements("job"))
{
string jobIdentifier = ele.GetAttributeString("identifier", "");
if (string.IsNullOrEmpty(jobIdentifier)) continue;
jobPreferences.Add(jobIdentifier);
}
break;
case "player":
defaultPlayerName = subElement.GetAttributeString("name", defaultPlayerName);
CharacterHeadIndex = subElement.GetAttributeInt("headindex", CharacterHeadIndex);
if (Enum.TryParse(subElement.GetAttributeString("gender", "none"), true, out Gender g))
{
CharacterGender = g;
}
if (Enum.TryParse(subElement.GetAttributeString("race", "white"), true, out Race r))
{
CharacterRace = r;
}
else
{
CharacterRace = Race.White;
}
CharacterHairIndex = subElement.GetAttributeInt("hairindex", CharacterHairIndex);
CharacterBeardIndex = subElement.GetAttributeInt("beardindex", CharacterBeardIndex);
CharacterMoustacheIndex = subElement.GetAttributeInt("moustacheindex", CharacterMoustacheIndex);
CharacterFaceAttachmentIndex = subElement.GetAttributeInt("faceattachmentindex", CharacterFaceAttachmentIndex);
break;
case "tutorials":
foreach (XElement tutorialElement in subElement.Elements())
{
CompletedTutorialNames.Add(tutorialElement.GetAttributeString("name", ""));
}
break;
}
}
foreach (InputType inputType in Enum.GetValues(typeof(InputType)))
{
if (keyMapping[(int)inputType] == null)
{
DebugConsole.ThrowError("Key binding for the input type \"" + inputType + " not set!");
keyMapping[(int)inputType] = new KeyOrMouse(Keys.D1);
}
}
UnsavedSettings = false;
selectedContentPackagePaths = new HashSet<string>();
foreach (XElement subElement in doc.Root.Elements())
{
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "contentpackage":
string path = System.IO.Path.GetFullPath(subElement.GetAttributeString("path", ""));
selectedContentPackagePaths.Add(path);
break;
}
}
LoadContentPackages(selectedContentPackagePaths);
}
public void ReloadContentPackages()
{
LoadContentPackages(selectedContentPackagePaths);
}
private void LoadContentPackages(IEnumerable<string> contentPackagePaths)
{
var missingPackagePaths = new List<string>();
var incompatiblePackages = new List<ContentPackage>();
SelectedContentPackages.Clear();
foreach (string path in contentPackagePaths)
{
var matchingContentPackage = ContentPackage.List.Find(cp => System.IO.Path.GetFullPath(cp.Path) == path);
if (matchingContentPackage == null)
{
missingPackagePaths.Add(path);
}
else if (!matchingContentPackage.IsCompatible())
{
incompatiblePackages.Add(matchingContentPackage);
}
else
{
SelectedContentPackages.Add(matchingContentPackage);
}
}
TextManager.LoadTextPacks(SelectedContentPackages);
foreach (ContentPackage contentPackage in SelectedContentPackages)
{
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);
}
}
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(); }
//display error messages after all content packages have been loaded
//to make sure the package that contains text files has been loaded before we attempt to use TextManager
foreach (string missingPackagePath in missingPackagePaths)
{
DebugConsole.ThrowError(TextManager.Get("ContentPackageNotFound").Replace("[packagepath]", missingPackagePath));
}
foreach (ContentPackage incompatiblePackage in incompatiblePackages)
{
DebugConsole.ThrowError(TextManager.Get(incompatiblePackage.GameVersion <= new Version(0, 0, 0, 0) ? "IncompatibleContentPackageUnknownVersion" : "IncompatibleContentPackage")
.Replace("[packagename]", incompatiblePackage.Name)
.Replace("[packageversion]", incompatiblePackage.GameVersion.ToString())
.Replace("[gameversion]", GameMain.Version.ToString()));
}
}
#endregion
#region Save PlayerConfig
public void SaveNewPlayerConfig()
{
XDocument doc = new XDocument();
UnsavedSettings = false;
if (doc.Root == null)
{
doc.Add(new XElement("config"));
}
doc.Root.Add(
new XAttribute("language", TextManager.Language),
new XAttribute("masterserverurl", MasterServerUrl),
new XAttribute("autocheckupdates", AutoCheckUpdates),
new XAttribute("musicvolume", musicVolume),
new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
new XAttribute("enablesplashscreen", EnableSplashScreen),
new XAttribute("usesteammatchmaking", useSteamMatchmaking),
new XAttribute("quickstartsub", QuickStartSubmarineName),
new XAttribute("requiresteamauthentication", requireSteamAuthentication),
new XAttribute("autoupdateworkshopitems", AutoUpdateWorkshopItems),
new XAttribute("aimassistamount", aimAssistAmount),
new XAttribute("enablemouselook", EnableMouseLook));
@@ -168,11 +168,7 @@ namespace Barotrauma.Items.Components
return;
}
target.InitializeLinks();
#if CLIENT
PlaySound(ActionType.OnUse, item.WorldPosition);
#endif
target.InitializeLinks();
if (!item.linkedTo.Contains(target.item)) item.linkedTo.Add(target.item);
if (!target.item.linkedTo.Contains(item)) target.item.linkedTo.Add(item);
@@ -237,9 +233,9 @@ namespace Barotrauma.Items.Components
Math.Sign(DockingTarget.item.WorldPosition.X - item.WorldPosition.X) :
Math.Sign(DockingTarget.item.WorldPosition.Y - item.WorldPosition.Y);
DockingTarget.DockingDir = -DockingDir;
#if CLIENT
PlaySound(ActionType.OnSecondaryUse, item.WorldPosition);
#endif
ApplyStatusEffects(ActionType.OnUse, 1.0f);
Vector2 jointDiff = joint.WorldAnchorB - joint.WorldAnchorA;
if (item.Submarine.PhysicsBody.Mass < DockingTarget.item.Submarine.PhysicsBody.Mass ||
DockingTarget.item.Submarine.IsOutpost)
@@ -396,70 +396,25 @@ namespace Barotrauma.Items.Components
}
int dir = IsHorizontal ? Math.Sign(c.SimPosition.Y - item.SimPosition.Y) : Math.Sign(c.SimPosition.X - item.SimPosition.X);
List<PhysicsBody> bodies = c.AnimController.Limbs.Select(l => l.body).ToList();
bodies.Add(c.AnimController.Collider);
bool soundPlayed = false;
foreach (Limb limb in c.AnimController.Limbs)
{
float diff = 0.0f;
if (!MathUtils.IsValid(body.SimPosition))
{
DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:LimbPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
"Failed to push a character out of a doorway - position of the character \"" + c.Name + "\" is not valid (" + body.SimPosition + ")." +
" Removed: " + c.Removed +
" Remoteplayer: " + c.IsRemotePlayer);
continue;
}
if (IsHorizontal)
{
if (body.SimPosition.X < simPos.X || body.SimPosition.X > simPos.X + simSize.X) continue;
diff = body.SimPosition.Y - item.SimPosition.Y;
}
else
{
if (body.SimPosition.Y > simPos.Y || body.SimPosition.Y < simPos.Y - simSize.Y) continue;
diff = body.SimPosition.X - item.SimPosition.X;
}
if (Math.Sign(diff) != dir)
if (PushBodyOutOfDoorway(c, limb.body, dir, simPos, simSize) && !soundPlayed)
{
#if CLIENT
SoundPlayer.PlayDamageSound("LimbBlunt", 1.0f, limb.body);
#endif
if (IsHorizontal)
{
body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * simSize.Y * 2.0f), body.Rotation);
body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f));
}
else
{
body.SetTransform(new Vector2(item.SimPosition.X + dir * simSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
body.ApplyLinearImpulse(new Vector2(dir * 0.5f, isOpen ? 0.0f : -1.0f));
}
}
if (IsHorizontal)
{
if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > simSize.Y * 0.5f) continue;
body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 0.5f));
soundPlayed = true;
}
}
PushBodyOutOfDoorway(c, c.AnimController.Collider, dir, simPos, simSize);
}
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
private bool PushBodyOutOfDoorway(Character c, PhysicsBody body, int dir, Vector2 doorRectSimPos, Vector2 doorRectSimSize)
{
if (isStuck) return;
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
if (connection.Name == "toggle")
float diff = 0.0f;
if (!MathUtils.IsValid(body.SimPosition))
{
DebugConsole.ThrowError("Failed to push a limb out of a doorway - position of the body (character \"" + c.Name + "\") is not valid (" + body.SimPosition + ")");
GameAnalyticsManager.AddErrorEventOnce("PushCharactersAway:LimbPosInvalid", GameAnalyticsSDK.Net.EGAErrorSeverity.Error,
@@ -480,6 +435,51 @@ namespace Barotrauma.Items.Components
diff = body.SimPosition.X - item.SimPosition.X;
}
//if the limb is at a different side of the door than the character (collider),
//immediately teleport it to the correct side
if (Math.Sign(diff) != dir)
{
if (IsHorizontal)
{
body.SetTransform(new Vector2(body.SimPosition.X, item.SimPosition.Y + dir * doorRectSimSize.Y * 2.0f), body.Rotation);
}
else
{
body.SetTransform(new Vector2(item.SimPosition.X + dir * doorRectSimSize.X * 1.2f, body.SimPosition.Y), body.Rotation);
}
}
//apply an impulse to push the limb further from the door
if (IsHorizontal)
{
if (Math.Abs(body.SimPosition.Y - item.SimPosition.Y) > doorRectSimSize.Y * 0.5f) { return false; }
body.ApplyLinearImpulse(new Vector2(isOpen ? 0.0f : 1.0f, dir * 2.0f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
else
{
if (Math.Abs(body.SimPosition.X - item.SimPosition.X) > doorRectSimSize.X * 0.5f) { return false; }
body.ApplyLinearImpulse(new Vector2(dir * 2.0f, isOpen ? 0.0f : -1.0f), maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
c.SetStun(0.2f);
return true;
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f, float signalStrength = 1.0f)
{
if (isStuck) return;
bool wasOpen = PredictedState == null ? isOpen : PredictedState.Value;
if (connection.Name == "toggle")
{
SetState(!wasOpen, false, true);
}
else if (connection.Name == "set_state")
{
SetState(signal != "0", false, true);
}
#if SERVER
if (sender != null && wasOpen != isOpen)
{
@@ -1,4 +1,5 @@
using FarseerPhysics;
using Barotrauma.Networking;
using FarseerPhysics;
using FarseerPhysics.Collision;
using FarseerPhysics.Dynamics;
using Microsoft.Xna.Framework;
@@ -166,11 +167,12 @@ namespace Barotrauma.Items.Components
//set the rotation of the projectile again because dropping the projectile resets the rotation
projectile.Item.SetTransform(projectilePos,
rotation + ((item.body.Dir == 1.0f) ? projectile.LaunchRotationRadians : projectile.LaunchRotationRadians - MathHelper.Pi));
rotation + (projectile.Item.body.Dir * projectile.LaunchRotationRadians));
//recoil
item.body.ApplyLinearImpulse(
new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f);
new Vector2((float)Math.Cos(projectile.Item.body.Rotation), (float)Math.Sin(projectile.Item.body.Rotation)) * item.body.Mass * -50.0f,
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
item.RemoveContained(projectile.Item);
@@ -107,10 +107,10 @@ namespace Barotrauma.Items.Components
#endif
Character thrower = picker;
item.Drop(thrower, createNetworkEvent: GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f);
item.body.ApplyLinearImpulse(throwVector * throwForce * item.body.Mass * 3.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector*10.0f);
ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f);
ac.GetLimb(LimbType.Head).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
ac.GetLimb(LimbType.Torso).body.ApplyLinearImpulse(throwVector * 10.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
Limb rightHand = ac.GetLimb(LimbType.RightHand);
item.body.AngularVelocity = rightHand.body.AngularVelocity;
@@ -210,13 +210,8 @@ namespace Barotrauma.Items.Components
{
Entity.Spawner.AddToSpawnQueue(fabricatedItem.TargetItem, outputContainer.Inventory, fabricatedItem.TargetItem.Health * fabricatedItem.OutCondition);
}
bool isNotClient = true;
#if CLIENT
isNotClient = GameMain.Client == null;
#endif
if (isNotClient && user != null)
if ((GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer) && user != null && !user.Removed)
{
foreach (Skill skill in fabricatedItem.RequiredSkills)
{
@@ -377,23 +377,14 @@ namespace Barotrauma.Items.Components
target.Body.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass);
return true;
}
else if (target.Body.UserData is Limb limb)
{
//severed limbs don't deactivate the projectile (but may still slow it down enough to make it inactive)
if (limb.IsSevered)
{
target.Body.ApplyLinearImpulse(item.body.LinearVelocity * item.body.Mass);
return true;
}
limb.character.LastDamageSource = item;
attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f);
if (limb.character != null) character = limb.character;
}
else if (target.Body.UserData is Structure structure)
{
attackResult = attack.DoDamage(User, structure, item.WorldPosition, 1.0f);
}
limb.character.LastDamageSource = item;
if (attack != null) { attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); }
if (limb.character != null) { character = limb.character; }
}
else if (target.Body.UserData is Structure structure)
{
if (attack != null) { attackResult = attack.DoDamage(User, structure, item.WorldPosition, 1.0f); }
}
if (character != null) character.LastDamageSource = item;
@@ -22,6 +22,10 @@ namespace Barotrauma.Items.Components
private float blinkTimer;
private bool itemLoaded;
private float blinkTimer;
public PhysicsBody ParentBody;
[Editable(MinValueFloat = 0.0f, MaxValueFloat = 2048.0f), Serialize(100.0f, true)]
@@ -77,7 +81,7 @@ namespace Barotrauma.Items.Components
IsActive = value;
#if SERVER
if (GameMain.Server != null && GameMain.Server.GameStarted) { item.CreateServerEvent(this); }
if (GameMain.Server != null && itemLoaded) { item.CreateServerEvent(this); }
#endif
}
}
@@ -315,7 +315,7 @@ namespace Barotrauma.Items.Components
Vector2 diff = nodes[nodes.Count - 1] - newNodePos;
Vector2 pullBackDir = diff == Vector2.Zero ? Vector2.Zero : Vector2.Normalize(diff);
user.AnimController.Collider.ApplyForce(pullBackDir * user.Mass * 50.0f);
user.AnimController.Collider.ApplyForce(pullBackDir * user.Mass * 50.0f, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
user.AnimController.UpdateUseItem(true, user.WorldPosition + pullBackDir * 200.0f);
if (GameMain.NetworkMember == null || GameMain.NetworkMember.IsServer)
@@ -1145,24 +1145,6 @@ namespace Barotrauma
return;
}
float forceFactor = 1.0f;
if (CurrentHull != null)
{
return;
}
float forceFactor = 1.0f;
if (CurrentHull != null)
{
return;
}
float forceFactor = 1.0f;
if (CurrentHull != null)
{
return;
}
float forceFactor = 1.0f;
if (CurrentHull != null)
{
@@ -1442,17 +1424,7 @@ namespace Barotrauma
}
}
#if CLIENT
//if the cursor is on a UI component, disable interaction with the left mouse button
//to prevent accidentally selecting items when clicking UI elements
if (picker == Character.Controlled && GUI.MouseOn != null)
{
if (GameMain.Config.KeyBind(ic.PickKey).MouseButton == 0) pickHit = false;
if (GameMain.Config.KeyBind(ic.SelectKey).MouseButton == 0) selectHit = false;
}
#endif
}
}
if (!pickHit && !selectHit) continue;
if (!ic.HasRequiredSkills(picker, out Skill tempRequiredSkill)) hasRequiredSkills = false;
@@ -1653,6 +1625,8 @@ namespace Barotrauma
GameMain.NetworkMember != null && (GameMain.NetworkMember.IsServer || Character.Controlled == dropper))
{
parentInventory.CreateNetworkEvent();
//send frequent updates after the item has been dropped
PositionUpdateInterval = 0.0f;
}
}
@@ -505,7 +505,7 @@ namespace Barotrauma
{
iconFolder = Path.GetDirectoryName(filePath);
}
InventoryIcon = new Sprite(subElement, iconFolder);
InventoryIcon = new Sprite(subElement, iconFolder, lazyLoad: true);
break;
case "brokensprite":
string brokenSpriteFolder = "";
@@ -541,7 +541,7 @@ namespace Barotrauma
}
else
{
decorativeSprite = new DecorativeSprite(subElement, decorativeSpriteFolder);
decorativeSprite = new DecorativeSprite(subElement, decorativeSpriteFolder, lazyLoad: true);
DecorativeSprites.Add(decorativeSprite);
groupID = decorativeSprite.RandomGroupID;
}
@@ -558,7 +558,7 @@ namespace Barotrauma
{
containedSpriteFolder = Path.GetDirectoryName(filePath);
}
var containedSprite = new ContainedItemSprite(subElement, containedSpriteFolder);
var containedSprite = new ContainedItemSprite(subElement, containedSpriteFolder, lazyLoad: true);
if (containedSprite.Sprite != null)
{
ContainedSprites.Add(containedSprite);
@@ -568,7 +568,7 @@ namespace Barotrauma
public void Extinguish(float deltaTime, float amount, Vector2 position)
{
for (int i = FireSources.Count - 1; i >= 0; i-- )
for (int i = FireSources.Count - 1; i >= 0; i--)
{
FireSources[i].Extinguish(deltaTime, amount, position);
}
@@ -38,6 +38,9 @@ namespace Barotrauma
SerializableProperty.DeserializeProperties(this, configElement);
name = TextManager.Get("EntityName." + identifier, returnNull: true) ?? name;
Description = TextManager.Get("EntityDescription." + identifier, returnNull: true) ?? Description;
int minX = int.MaxValue, minY = int.MaxValue;
int maxX = int.MinValue, maxY = int.MinValue;
DisplayEntities = new List<Pair<MapEntityPrefab, Rectangle>>();
@@ -331,13 +331,13 @@ namespace Barotrauma
switch (subElement.Name.ToString().ToLowerInvariant())
{
case "sprite":
Sprite = new Sprite(subElement);
Sprite = new Sprite(subElement, lazyLoad: true);
break;
case "specularsprite":
SpecularSprite = new Sprite(subElement);
SpecularSprite = new Sprite(subElement, lazyLoad: true);
break;
case "deformablesprite":
DeformableSprite = new DeformableSprite(subElement);
DeformableSprite = new DeformableSprite(subElement, lazyLoad: true);
break;
case "overridecommonness":
string levelType = subElement.GetAttributeString("leveltype", "");
@@ -543,19 +543,19 @@ namespace Barotrauma
if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * currentForceFluctuation * distFactor, ForceVelocityLimit);
else
body.ApplyForce(Force * currentForceFluctuation * distFactor);
body.ApplyForce(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
break;
case TriggerForceMode.Acceleration:
if (ForceVelocityLimit < 1000.0f)
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, ForceVelocityLimit);
else
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor);
body.ApplyForce(Force * body.Mass * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
break;
case TriggerForceMode.Impulse:
if (ForceVelocityLimit < 1000.0f)
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, ForceVelocityLimit);
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: ForceVelocityLimit);
else
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor);
body.ApplyLinearImpulse(Force * currentForceFluctuation * distFactor, maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
break;
case TriggerForceMode.LimitVelocity:
float maxVel = ForceVelocityLimit * currentForceFluctuation * distFactor;
@@ -563,7 +563,8 @@ namespace Barotrauma
{
body.ApplyForce(
Vector2.Normalize(-body.LinearVelocity) *
Force.Length() * body.Mass * currentForceFluctuation * distFactor);
Force.Length() * body.Mass * currentForceFluctuation * distFactor,
maxVelocity: NetConfig.MaxPhysicsBodyVelocity);
}
break;
}
@@ -126,7 +126,7 @@ namespace Barotrauma
CanChangeTo.Add(new LocationTypeChange(Identifier, subElement));
break;
case "portrait":
var portrait = new Sprite(subElement);
var portrait = new Sprite(subElement, lazyLoad: true);
if (portrait != null)
{
portraits.Add(portrait);
@@ -276,9 +276,10 @@ namespace Barotrauma
}
StairDirection = Prefab.StairDirection;
SerializableProperties = SerializableProperty.GetProperties(this);
InitProjSpecific();
if (Prefab.Body)
{
Bodies = new List<Body>();
@@ -307,6 +308,8 @@ namespace Barotrauma
InsertToList();
}
partial void InitProjSpecific();
public override string ToString()
{
return Name;
@@ -605,7 +605,7 @@ namespace Barotrauma
{
if (!IsValidValue(impulse, "impulse", -1e10f, 1e10f)) return;
if (!IsValidValue(point, "point")) return;
if (!IsValidValue(impulse / body.Mass, "new velocity")) return;
if (!IsValidValue(impulse / body.Mass, "new velocity", -1000.0f, 1000.0f)) return;
body.ApplyLinearImpulse(impulse, point);
}
@@ -646,15 +646,20 @@ namespace Barotrauma
/// </summary>
public void ApplyForce(Vector2 force, float maxVelocity)
{
if (!IsValidValue(force, "force", -1e10f, 1e10f)) return;
if (!IsValidValue(maxVelocity, "max velocity")) return;
float currSpeed = body.LinearVelocity.Length();
Vector2 velocityAddition = force / Mass * (float)Timing.Step;
Vector2 newVelocity = body.LinearVelocity + velocityAddition;
newVelocity = newVelocity.ClampLength(Math.Max(currSpeed, maxVelocity));
float newSpeedSqr = newVelocity.LengthSquared();
if (newSpeedSqr > maxVelocity * maxVelocity)
{
newVelocity = newVelocity.ClampLength(maxVelocity);
}
body.ApplyForce((newVelocity - body.LinearVelocity) * Mass / (float)Timing.Step);
Vector2 clampedForce = (newVelocity - body.LinearVelocity) * Mass / (float)Timing.Step;
if (!IsValidValue(force, "clamped force", -1e10f, 1e10f)) return;
body.ApplyForce(force);
}
public void ApplyForce(Vector2 force, Vector2 point)
@@ -5,30 +5,25 @@ namespace Barotrauma
{
partial class DeformableSprite
{
private Sprite sprite;
public Vector2 Size
{
get { return sprite.size; }
get { return Sprite.size; }
}
public Vector2 Origin
{
get { return sprite.Origin; }
set { sprite.Origin = value; }
get { return Sprite.Origin; }
set { Sprite.Origin = value; }
}
public Sprite Sprite
public Sprite Sprite { get; private set; }
public DeformableSprite(XElement element, int? subdivisionsX = null, int? subdivisionsY = null, string filePath = "", bool lazyLoad = false)
{
get { return sprite; }
Sprite = new Sprite(element, file: filePath, lazyLoad: lazyLoad);
InitProjSpecific(element, subdivisionsX, subdivisionsY, lazyLoad);
}
public DeformableSprite(XElement element, int? subdivisionsX = null, int? subdivisionsY = null, string filePath = "")
{
sprite = new Sprite(element, file: filePath);
InitProjSpecific(element, subdivisionsX, subdivisionsY);
}
partial void InitProjSpecific(XElement element, int? subdivisionsX, int? subdivisionsY);
partial void InitProjSpecific(XElement element, int? subdivisionsX, int? subdivisionsY, bool lazyLoad = false);
}
}
@@ -103,8 +103,9 @@ namespace Barotrauma
partial void LoadTexture(ref Vector4 sourceVector, ref bool shouldReturn, bool premultiplyAlpha = true);
partial void CalculateSourceRect();
public Sprite(XElement element, string path = "", string file = "")
public Sprite(XElement element, string path = "", string file = "", bool? preMultiplyAlpha = null, bool lazyLoad = false)
{
this.lazyLoad = lazyLoad;
SourceElement = element;
if (file == "")
{
@@ -127,8 +128,12 @@ namespace Barotrauma
Name = SourceElement.GetAttributeString("name", null);
Vector4 sourceVector = SourceElement.GetAttributeVector4("sourcerect", Vector4.Zero);
preMultipliedAlpha = preMultiplyAlpha ?? SourceElement.GetAttributeBool("premultiplyalpha", true);
bool shouldReturn = false;
LoadTexture(ref sourceVector, ref shouldReturn, SourceElement.GetAttributeBool("premultiplyalpha", true));
if (!lazyLoad)
{
LoadTexture(ref sourceVector, ref shouldReturn, preMultipliedAlpha);
}
if (shouldReturn) return;
sourceRect = new Rectangle((int)sourceVector.X, (int)sourceVector.Y, (int)sourceVector.Z, (int)sourceVector.W);
size = SourceElement.GetAttributeVector2("size", Vector2.One);
@@ -31,6 +31,8 @@ namespace Barotrauma
public readonly HashSet<Character> ReactorMeltdown = new HashSet<Character>();
public readonly HashSet<Character> Casualties = new HashSet<Character>();
public bool SubWasDamaged;
}
private static RoundData roundData;
@@ -110,7 +112,47 @@ namespace Barotrauma
UnlockAchievement("subdeep", true, c => c != null && c.Submarine == sub && !c.IsDead && !c.IsUnconscious);
}
}
}
if (!roundData.SubWasDamaged)
{
roundData.SubWasDamaged = SubWallsDamaged(Submarine.MainSub);
}
}
if (GameMain.GameSession != null && Character.Controlled != null)
{
if (Character.Controlled.HasEquippedItem("clownmask") &&
Character.Controlled.HasEquippedItem("clowncostume"))
{
UnlockAchievement(Character.Controlled, "clowncostume");
}
if (Submarine.MainSub != null && Character.Controlled.Submarine == null)
{
float dist = 500 / Physics.DisplayToRealWorldRatio;
if (Vector2.DistanceSquared(Character.Controlled.WorldPosition, Submarine.MainSub.WorldPosition) >
dist * dist)
{
UnlockAchievement(Character.Controlled, "crewaway");
}
}
}
}
private static bool SubWallsDamaged(Submarine sub)
{
foreach (Structure structure in Structure.WallList)
{
if (structure.Submarine != sub || structure.HasBody) { continue; }
for (int i = 0; i < structure.SectionCount; i++)
{
if (structure.SectionIsLeaking(i))
{
return true;
}
}
}
return false;
}
public static void OnBiomeDiscovered(Biome biome)
@@ -274,14 +316,21 @@ namespace Barotrauma
//made it to the destination
if (gameSession.Submarine.AtEndPosition)
{
bool noDamageRun = !roundData.SubWasDamaged && !roundData.Casualties.Any(c => !(c.AIController is EnemyAIController));
#if SERVER
if (GameMain.Server != null)
{
//in MP all characters that were inside the sub during reactor meltdown and still alive at the end of the round get an achievement
UnlockAchievement("survivereactormeltdown", true, c => c != null && !c.IsDead && roundData.ReactorMeltdown.Contains(c));
if (noDamageRun)
{
UnlockAchievement("nodamagerun", true, c => c != null && !c.IsDead);
}
}
#endif
#if CLIENT
if (noDamageRun) { UnlockAchievement("nodamagerun"); }
if (roundData.ReactorMeltdown.Any()) //in SP getting to the destination after a meltdown is enough
{
UnlockAchievement("survivereactormeltdown");
@@ -302,6 +351,11 @@ namespace Barotrauma
UnlockAchievement(charactersInSub[0], "lonesailor");
}
}
foreach (Character character in charactersInSub)
{
if (character.Info.Job == null) { continue; }
UnlockAchievement(character, character.Info.Job.Prefab.Identifier + "round");
}
}
}