Merge pull request #2 from Regalis11/master

Updated to last original master commits
This commit is contained in:
Blue
2018-07-17 22:33:16 +02:00
committed by GitHub
76 changed files with 1394 additions and 733 deletions

View File

@@ -29,6 +29,8 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile />
<ReleaseVersion>0.7.0.1</ReleaseVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
@@ -231,9 +233,15 @@
<Compile Include="Source\Utils\ToolBox.cs" />
</ItemGroup>
<ItemGroup>
<Reference Include="GameAnalytics.Mono, Version=1.0.6710.29255, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference>
<Reference Condition="$(DefineConstants.Contains('WINDOWS'))" Include="MonoGame.Framework.WindowsDX">
<HintPath>..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.6.0.1625\lib\net40\MonoGame.Framework.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Condition="$(DefineConstants.Contains('WINDOWS'))" Include="PresentationCore" />
<Reference Condition="$(DefineConstants.Contains('WINDOWS'))" Include="SharpDX">
<SpecificVersion>False</SpecificVersion>
@@ -247,9 +255,12 @@
<HintPath>..\..\Libraries\NuGet\OpenTK.2.0.0\lib\net20\OpenTK.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data.SQLite, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Drawing" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.XML" />
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
<Reference Include="RestSharp">
<HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
@@ -324,6 +335,13 @@
<ItemGroup />
<Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets" Condition="Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.2")]
[assembly: AssemblyFileVersion("0.8.1.2")]
[assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.5")]

View File

@@ -4,6 +4,7 @@ using FarseerPhysics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
@@ -17,13 +18,17 @@ namespace Barotrauma
private set;
}
private string hitSoundTag;
private float damage, burnt, wetTimer;
private float dripParticleTimer;
public string HitSoundTag
public float Burnt
{
get { return hitSoundTag; }
get { return burnt; }
protected set { burnt = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public string HitSoundTag { get; private set; }
partial void InitProjSpecific(XElement element)
{
foreach (XElement subElement in element.Elements())
@@ -34,19 +39,97 @@ namespace Barotrauma
LightSource = new LightSource(subElement);
break;
case "sound":
hitSoundTag = subElement.GetAttributeString("tag", "");
if (string.IsNullOrWhiteSpace(hitSoundTag))
HitSoundTag = subElement.GetAttributeString("tag", "");
if (string.IsNullOrWhiteSpace(HitSoundTag))
{
//legacy support
hitSoundTag = subElement.GetAttributeString("file", "");
HitSoundTag = subElement.GetAttributeString("file", "");
}
break;
}
}
}
partial void UpdateProjSpecific()
partial void AddDamageProjSpecific(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound, List<DamageModifier> appliedDamageModifiers)
{
if (playSound)
{
string damageSoundType = (damageType == DamageType.Blunt) ? "LimbBlunt" : "LimbSlash";
foreach (DamageModifier damageModifier in appliedDamageModifiers)
{
if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound))
{
damageSoundType = damageModifier.DamageSound;
break;
}
}
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
}
if (character.UseBloodParticles)
{
float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
for (int i = 0; i < bloodParticleAmount; i++)
{
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
if (blood != null)
{
blood.Size *= bloodParticleSize;
}
}
if (bloodParticleAmount > 0 && character.CurrentHull != null)
{
character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
}
}
if (damageType == DamageType.Burn)
{
Burnt += amount * 10.0f;
}
damage += Math.Max(amount, bleedingAmount) / character.MaxHealth * 100.0f;
}
partial void UpdateProjSpecific(float deltaTime)
{
if (!body.Enabled) return;
if (!character.IsDead)
{
damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
Burnt -= deltaTime;
}
if (inWater)
{
wetTimer = 1.0f;
}
else
{
wetTimer -= deltaTime * 0.1f;
if (wetTimer > 0.0f)
{
dripParticleTimer += wetTimer * deltaTime * Mass * (wetTimer > 0.9f ? 50.0f : 5.0f);
if (dripParticleTimer > 1.0f)
{
float dropRadius = body.BodyShape == PhysicsBody.Shape.Rectangle ? Math.Min(body.width, body.height) : body.radius;
GameMain.ParticleManager.CreateParticle(
"waterdrop",
WorldPosition + Rand.Vector(Rand.Range(0.0f, ConvertUnits.ToDisplayUnits(dropRadius))),
ConvertUnits.ToDisplayUnits(body.LinearVelocity),
0, character.CurrentHull);
dripParticleTimer = 0.0f;
}
}
}
if (LightSource != null)
{
LightSource.ParentSub = body.Submarine;
@@ -73,10 +156,10 @@ namespace Barotrauma
body.Dir = Dir;
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb);
bool hideLimb = WearingItems.Any(w => w != null && w.HideLimb);
if (!hideLimb)
{
body.Draw(spriteBatch, sprite, color, null, scale);
body.Draw(spriteBatch, sprite, color, null, Scale);
}
else
{
@@ -89,7 +172,7 @@ namespace Barotrauma
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically;
}
foreach (WearableSprite wearable in wearingItems)
foreach (WearableSprite wearable in WearingItems)
{
SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
@@ -115,7 +198,7 @@ namespace Barotrauma
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color, origin,
-body.DrawRotation,
scale, spriteEffect, depth);
Scale, spriteEffect, depth);
}
if (damage > 0.0f && damagedSprite != null && !hideLimb)

View File

@@ -394,7 +394,7 @@ namespace Barotrauma
NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.White);
}));
commands.Add(new Command("followsub", "followsub: Toggle whether the ", (string[] args) =>
commands.Add(new Command("followsub", "followsub: Toggle whether the camera should follow the nearest submarine.", (string[] args) =>
{
Camera.FollowSub = !Camera.FollowSub;
NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White);

View File

@@ -6,10 +6,10 @@ namespace Barotrauma
{
public void ShowMessage(int index)
{
if (index >= headers.Count && index >= messages.Count) return;
if (index >= Headers.Count && index >= Messages.Count) return;
string header = index < headers.Count ? headers[index] : "";
string message = index < messages.Count ? messages[index] : "";
string header = index < Headers.Count ? Headers[index] : "";
string message = index < Messages.Count ? Messages[index] : "";
GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);

View File

@@ -198,7 +198,8 @@ namespace Barotrauma
private static bool QuitClicked(GUIButton button, object obj)
{
if (button.UserData as string == "save")
bool save = button.UserData as string == "save";
if (save)
{
SaveUtil.SaveGame(GameMain.GameSession.SavePath);
}
@@ -211,10 +212,18 @@ namespace Barotrauma
CoroutineManager.StopCoroutines("EndCinematic");
GameMain.GameSession = null;
if (GameMain.GameSession != null)
{
if (GameSettings.SendUserStatistics)
{
Mission mission = GameMain.GameSession.Mission;
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("QuitRound:" + (save ? "Save" : "NoSave"));
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("EndRound:" + (mission == null ? "NoMission" : (mission.Completed ? "MissionCompleted" : "MissionFailed")));
}
GameMain.GameSession = null;
}
GameMain.MainMenuScreen.Select();
//Game1.MainMenuScreen.SelectTab(null, (int)MainMenuScreen.Tabs.Main);
return true;
}

View File

@@ -9,6 +9,7 @@ using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using GameAnalyticsSDK.Net;
namespace Barotrauma
{
@@ -144,7 +145,7 @@ namespace Barotrauma
Timing.Accumulator = 0.0f;
fixedTime = new GameTime();
World = new World(new Vector2(0, -9.82f));
FarseerPhysics.Settings.AllowSleep = true;
FarseerPhysics.Settings.ContinuousPhysics = false;
@@ -232,6 +233,31 @@ namespace Barotrauma
loadingCoroutine = CoroutineManager.StartCoroutine(Load());
}
private void InitUserStats()
{
if (GameSettings.ShowUserStatisticsPrompt)
{
var userStatsPrompt = new GUIMessageBox(
"Do you want to help us make Barotrauma better?",
"Do you allow Barotrauma to send usage statistics and error reports to the developers? The data is anonymous, " +
"does not contain any personal information and is only used to help us diagnose issues and improve Barotrauma.",
new string[] { "Yes", "No" });
userStatsPrompt.Buttons[0].OnClicked += (btn, userdata) =>
{
GameSettings.SendUserStatistics = true;
GameAnalyticsManager.Init();
return true;
};
userStatsPrompt.Buttons[0].OnClicked += userStatsPrompt.Close;
userStatsPrompt.Buttons[1].OnClicked += (btn, userdata) => { GameSettings.SendUserStatistics = false; return true; };
userStatsPrompt.Buttons[1].OnClicked += userStatsPrompt.Close;
}
else if (GameSettings.SendUserStatistics)
{
GameAnalyticsManager.Init();
}
}
private IEnumerable<object> Load()
{
if (GameSettings.VerboseLogging)
@@ -241,6 +267,8 @@ namespace Barotrauma
GUI.GraphicsDevice = base.GraphicsDevice;
GUI.Init(Content);
InitUserStats();
GUIComponent.Init(Window);
DebugConsole.Init(Window);
DebugConsole.Log(SelectedPackage == null ? "No content package selected" : "Content package \"" + SelectedPackage.Name + "\" selected");
@@ -256,7 +284,7 @@ namespace Barotrauma
TitleScreen.LoadState = 2.0f;
yield return CoroutineStatus.Running;
Mission.Init();
MissionPrefab.Init();
MapEntityPrefab.Init();
LevelGenerationParams.LoadPresets();
TitleScreen.LoadState = 10.0f;
@@ -472,7 +500,7 @@ namespace Barotrauma
protected override void OnExiting(object sender, EventArgs args)
{
if (NetworkMember != null) NetworkMember.Disconnect();
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
base.OnExiting(sender, args);
}
}

View File

@@ -281,7 +281,7 @@ namespace Barotrauma
listBox.ClearChildren();
characters.Clear();
WayPoint[] waypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub, false);
WayPoint[] waypoints = WayPoint.SelectCrewSpawnPoints(characterInfos, Submarine.MainSub);
for (int i = 0; i < waypoints.Length; i++)
{

View File

@@ -238,6 +238,7 @@ namespace Barotrauma.Lights
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
{
if (!LosEnabled && !ObstructVision) return;
if (ViewTarget == null) return;
graphics.SetRenderTarget(losTexture);

View File

@@ -691,6 +691,7 @@ namespace Barotrauma.Networking
string shuttleHash = inc.ReadString();
string modeName = inc.ReadString();
int missionIndex = inc.ReadInt16();
bool respawnAllowed = inc.ReadBoolean();
bool loadSecondSub = inc.ReadBoolean();
@@ -750,7 +751,7 @@ namespace Barotrauma.Networking
if (campaign == null)
{
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, Mission.MissionTypes[missionTypeIndex]);
GameMain.GameSession = new GameSession(GameMain.NetLobbyScreen.SelectedSub, "", gameMode, missionIndex < 0 ? null : MissionPrefab.List[missionIndex]);
GameMain.GameSession.StartRound(levelSeed, loadSecondSub);
}
else
@@ -821,7 +822,7 @@ namespace Barotrauma.Networking
}
else
{
submarines.Add(new Submarine(Path.Combine(Submarine.SavePath, subName), subHash, false));
submarines.Add(new Submarine(Path.Combine(Submarine.SavePath, subName) + ".sub", subHash, false));
}
}
@@ -883,7 +884,7 @@ namespace Barotrauma.Networking
bool allowSpectating = inc.ReadBoolean();
YesNoMaybe traitorsEnabled = (YesNoMaybe)inc.ReadRangedInteger(0, 2);
int missionTypeIndex = inc.ReadRangedInteger(0, Mission.MissionTypes.Count - 1);
int missionTypeIndex = inc.ReadRangedInteger(0, MissionPrefab.MissionTypes.Count - 1);
int modeIndex = inc.ReadByte();
string levelSeed = inc.ReadString();
@@ -1440,6 +1441,8 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientPermissions.Ban);
msg.Write(kickedName);
msg.Write(reason);
msg.Write(range);
msg.Write(duration.HasValue ? duration.Value.TotalSeconds : 0.0); //0 = permaban
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
}

View File

@@ -3,6 +3,7 @@
using System;
using System.IO;
using System.Text;
using GameAnalyticsSDK.Net;
#if WINDOWS
using System.Windows.Forms;
@@ -135,7 +136,6 @@ namespace Barotrauma
sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")");
sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
sb.AppendLine("If you'd like to help fix the bug that caused the crash, please send this file to the developers on the Undertow Games forums.");
sb.AppendLine("\n");
#if DEBUG
sb.AppendLine("Game version " + GameMain.Version + " (debug build)");
@@ -192,14 +192,25 @@ namespace Barotrauma
{
sb.AppendLine("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
}
string crashReport = sb.ToString();
sw.WriteLine(sb.ToString());
sw.WriteLine(crashReport);
sw.Close();
if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs();
CrashMessageBox( "A crash report (\"crashreport.log\") was saved in the root folder of the game."+
" If you'd like to help fix this bug, please post the report on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/");
if (GameSettings.SendUserStatistics)
{
CrashMessageBox( "A crash report (\"crashreport.log\") was saved in the root folder of the game and sent to the developers.");
GameAnalytics.AddErrorEvent(EGAErrorSeverity.Error, crashReport);
GameAnalytics.OnStop();
}
else
{
CrashMessageBox("A crash report (\"crashreport.log\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
" if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/");
}
}
}
#endif

View File

@@ -87,6 +87,8 @@ namespace Barotrauma
campaignUI.StartRound = StartRound;
campaignUI.OnLocationSelected = SelectLocation;
campaignUI.UpdateCharacterLists();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("singleplayer");
}
public override void AddToGUIUpdateList()

View File

@@ -142,6 +142,8 @@ namespace Barotrauma
campaignSetupUI.UpdateSubList();
SelectTab(null, 0);
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("");
}
public bool SelectTab(GUIButton button, object obj)

View File

@@ -501,6 +501,8 @@ namespace Barotrauma
}
}
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("multiplayer");
if (GameModePreset.list.Count > 0 && modeList.Selected == null) modeList.Select(0);
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients);
@@ -559,14 +561,18 @@ namespace Barotrauma
var playYourself = new GUITickBox(new Rectangle(0, 0, 20, 20), TextManager.Get("PlayYourself"), Alignment.TopLeft, myPlayerFrame);
playYourself.Selected = GameMain.NetworkMember.CharacterInfo != null;
playYourself.OnSelected = TogglePlayYourself;
playYourself.UserData = "playyourself";
GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame);
toggleHead.UserData = -1;
toggleHead.OnClicked = ToggleHead;
toggleHead = new GUIButton(new Rectangle(60, 50, 15, 15), ">", "", myPlayerFrame);
toggleHead.UserData = 1;
toggleHead.OnClicked = ToggleHead;
playYourself.UserData = "playyourself";
GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame)
{
UserData = -1,
OnClicked = ToggleHead
};
toggleHead = new GUIButton(new Rectangle(60, 50, 15, 15), ">", "", myPlayerFrame)
{
UserData = 1,
OnClicked = ToggleHead
};
new GUITextBlock(new Rectangle(100, 30, 200, 30), TextManager.Get("Gender"), "", myPlayerFrame);
@@ -629,7 +635,10 @@ namespace Barotrauma
{
if (tickBox.Selected)
{
GameMain.NetworkMember.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, GameMain.NetworkMember.Name, Gender.None, null);
GameMain.NetworkMember.CharacterInfo =
new CharacterInfo(Character.HumanConfigFile, GameMain.NetworkMember.Name, GameMain.Config.CharacterGender, null);
GameMain.NetworkMember.CharacterInfo.HeadSpriteId = GameMain.Config.CharacterHeadIndex;
UpdatePlayerFrame(GameMain.NetworkMember.CharacterInfo);
}
else
@@ -688,9 +697,9 @@ namespace Barotrauma
public void SetMissionType(int missionTypeIndex)
{
if (missionTypeIndex < 0 || missionTypeIndex >= Mission.MissionTypes.Count) return;
if (missionTypeIndex < 0 || missionTypeIndex >= MissionPrefab.MissionTypes.Count) return;
missionTypeBlock.GetChild<GUITextBlock>().Text = Mission.MissionTypes[missionTypeIndex];
missionTypeBlock.GetChild<GUITextBlock>().Text = MissionPrefab.MissionTypes[missionTypeIndex];
missionTypeBlock.UserData = missionTypeIndex;
}
@@ -701,8 +710,8 @@ namespace Barotrauma
int missionTypeIndex = (int)missionTypeBlock.UserData;
missionTypeIndex += (int)userData;
if (missionTypeIndex < 0) missionTypeIndex = Mission.MissionTypes.Count - 1;
if (missionTypeIndex >= Mission.MissionTypes.Count) missionTypeIndex = 0;
if (missionTypeIndex < 0) missionTypeIndex = MissionPrefab.MissionTypes.Count - 1;
if (missionTypeIndex >= MissionPrefab.MissionTypes.Count) missionTypeIndex = 0;
SetMissionType(missionTypeIndex);
@@ -1247,14 +1256,12 @@ namespace Barotrauma
private bool ToggleHead(GUIButton button, object userData)
{
int dir = (int)userData;
if (GameMain.NetworkMember.CharacterInfo == null) return true;
int dir = (int)userData;
GameMain.NetworkMember.CharacterInfo.HeadSpriteId += dir;
GameMain.Config.CharacterHeadIndex = GameMain.NetworkMember.CharacterInfo.HeadSpriteId;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
return true;
}
@@ -1262,7 +1269,7 @@ namespace Barotrauma
{
Gender gender = (Gender)obj;
GameMain.NetworkMember.CharacterInfo.Gender = gender;
GameMain.Config.CharacterGender = GameMain.NetworkMember.CharacterInfo.Gender;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
return true;
}

View File

@@ -348,6 +348,8 @@ namespace Barotrauma
}
cam.UpdateTransform();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("editor");
}
public override void Deselect()

View File

@@ -62,6 +62,7 @@ namespace Barotrauma
if (File.Exists(file))
{
ToolBox.IsProperFilenameCase(file);
return TextureLoader.FromFile(file);
}
else

View File

@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GameAnalytics.Mono.SDK" version="1.1.12" targetFramework="net45" />
<package id="MonoGame.Framework.DesktopGL" version="3.6.0.1625" targetFramework="net45" />
<package id="MonoGame.Framework.WindowsDX" version="3.6.0.1625" targetFramework="net45" />
<package id="NLog" version="4.3.8" targetFramework="net45" />
<package id="NVorbis" version="0.8.5.0" targetFramework="net45" />
<package id="OpenTK" version="2.0.0" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" />

View File

@@ -28,6 +28,8 @@
<UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled>
<ReleaseVersion>0.7.0.1</ReleaseVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
@@ -69,12 +71,21 @@
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="GameAnalytics.Mono, Version=1.0.6710.29255, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\GameAnalytics.Mono.dll</HintPath>
</Reference>
<Reference Include="NLog, Version=4.0.0.0, Culture=neutral, PublicKeyToken=5120e14c03d0593c, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\NLog.4.3.8\lib\net45\NLog.dll</HintPath>
</Reference>
<Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.XML" />
<Reference Include="System.Data.SQLite, Version=1.0.102.0, Culture=neutral, PublicKeyToken=db937bc2d44ff139, processorArchitecture=MSIL">
<HintPath>..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\lib\net45\System.Data.SQLite.dll</HintPath>
</Reference>
<Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" />
</ItemGroup>
<ItemGroup>
@@ -142,6 +153,13 @@
<ItemGroup />
<Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets" Condition="Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets')" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\Libraries\NuGet\GameAnalytics.Mono.SDK.1.1.12\build\net45\GameAnalytics.Mono.SDK.targets'))" />
</Target>
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">

View File

@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.2")]
[assembly: AssemblyFileVersion("0.8.1.2")]
[assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.5")]

View File

@@ -14,7 +14,7 @@ namespace Barotrauma
{
lock (QueuedCommands)
{
while (QueuedCommands.Count>0)
while (QueuedCommands.Count > 0)
{
ExecuteCommand(QueuedCommands[0]);
QueuedCommands.RemoveAt(0);
@@ -127,7 +127,7 @@ namespace Barotrauma
{
return new string[][]
{
Mission.MissionTypes.ToArray()
MissionPrefab.MissionTypes.ToArray()
};
}));
@@ -190,6 +190,16 @@ namespace Barotrauma
NewMessage(ent.ToString(), Color.Lime);
}
}));
//"dummy commands" that only exist so that the server can give clients permissions to use them
commands.Add(new Command("control|controlcharacter", "control [character name]: Start controlling the specified character (client-only).", null));
commands.Add(new Command("los", "Toggle the line of sight effect on/off (client-only).", null));
commands.Add(new Command("lighting|lights", "Toggle lighting on/off (client-only).", null));
commands.Add(new Command("debugdraw", "Toggle the debug drawing mode on/off (client-only).", null));
commands.Add(new Command("togglehud|hud", "Toggle the character HUD (inventories, icons, buttons, etc) on/off (client-only).", null));
commands.Add(new Command("followsub", "Toggle whether the camera should follow the nearest submarine (client-only).", null));
commands.Add(new Command("toggleaitargets|aitargets", "Toggle the visibility of AI targets (= targets that enemies can detect and attack/escape from) (client-only).", null));
#if DEBUG
commands.Add(new Command("eventdata", "", (string[] args) =>
{

View File

@@ -1,5 +1,6 @@
using Barotrauma.Networking;
using FarseerPhysics.Dynamics;
using GameAnalyticsSDK.Net;
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
@@ -72,12 +73,17 @@ namespace Barotrauma
Config.Save("config.xml");
}
if (GameSettings.SendUserStatistics)
{
GameAnalyticsManager.Init();
}
GameScreen = new GameScreen();
}
public void Init()
{
Mission.Init();
MissionPrefab.Init();
MapEntityPrefab.Init();
LevelGenerationParams.LoadPresets();
@@ -118,6 +124,7 @@ namespace Barotrauma
public void CloseServer()
{
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
Server.Disconnect();
Server = null;
}
@@ -139,25 +146,27 @@ namespace Barotrauma
{
DebugConsole.NewMessage("WARNING: Stopwatch frequency under 1500 ticks per second. Expect significant syncing accuracy issues.", Color.Yellow);
}
Stopwatch stopwatch = Stopwatch.StartNew();
long prevTicks = stopwatch.ElapsedTicks;
while (ShouldRun)
{
long currTicks = stopwatch.ElapsedTicks;
Timing.Accumulator += (double)(currTicks - prevTicks) / frequency;
double elapsedTime = (currTicks - prevTicks) / frequency;
Timing.Accumulator += elapsedTime;
Timing.TotalTime += elapsedTime;
prevTicks = currTicks;
while (Timing.Accumulator>=Timing.Step)
while (Timing.Accumulator >= Timing.Step)
{
DebugConsole.Update();
if (Screen.Selected != null) Screen.Selected.Update((float)Timing.Step);
Server.Update((float)Timing.Step);
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step);
Timing.Accumulator -= Timing.Step;
}
int frameTime = (int)(((double)(stopwatch.ElapsedTicks - prevTicks) / frequency)*1000.0);
Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime)/2,0));
int frameTime = (int)(((double)(stopwatch.ElapsedTicks - prevTicks) / frequency) * 1000.0);
Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime) / 2, 0));
}
stopwatch.Stop();

View File

@@ -1,5 +1,6 @@
#region Using Statements
using GameAnalyticsSDK.Net;
using System;
using System.IO;
using System.Text;
@@ -46,7 +47,6 @@ namespace Barotrauma
sb.AppendLine("Barotrauma Dedicated Server crash report (generated on " + DateTime.Now + ")");
sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! ");
sb.AppendLine("If you'd like to help fix the bug that caused the crash, please send this file to the developers on the Undertow Games forums.");
sb.AppendLine("\n");
sb.AppendLine("Game version " + GameMain.Version);
sb.AppendLine("Selected content package: " + GameMain.SelectedPackage.Name);
@@ -75,11 +75,24 @@ namespace Barotrauma
sb.AppendLine(" "+DebugConsole.Messages[i].Time+" - "+DebugConsole.Messages[i].Text);
}
string crashReport = sb.ToString();
Console.ForegroundColor = ConsoleColor.Red;
Console.Write(sb.ToString());
Console.Write(crashReport);
sw.WriteLine(sb.ToString());
sw.Close();
sw.Close();
if (GameSettings.SendUserStatistics)
{
GameAnalytics.AddErrorEvent(EGAErrorSeverity.Error, crashReport);
GameAnalytics.OnStop();
Console.Write("A crash report (\"crashreport.log\") was saved in the root folder of the game and sent to the developers.");
}
else
{
Console.Write("A crash report(\"crashreport.log\") was saved in the root folder of the game. The error was not sent to the developers because user statistics have been disabled, but" +
" if you'd like to help fix this bug, you may post it on Barotrauma's GitHub issue tracker: https://github.com/Regalis11/Barotrauma/issues/");
}
}
}
}

View File

@@ -75,18 +75,18 @@ namespace Barotrauma
set
{
lastUpdateID++;
missionTypeIndex = Math.Max(0, Math.Min(Mission.MissionTypes.Count() - 1, value));
missionTypeIndex = Math.Max(0, Math.Min(MissionPrefab.MissionTypes.Count() - 1, value));
}
}
public string MissionTypeName
{
get { return Mission.MissionTypes[MissionTypeIndex]; }
get { return MissionPrefab.MissionTypes[MissionTypeIndex]; }
set
{
for (int i = 0; i < Mission.MissionTypes.Count(); i++)
for (int i = 0; i < MissionPrefab.MissionTypes.Count(); i++)
{
if (Mission.MissionTypes[i].ToLower() == value.ToLower())
if (MissionPrefab.MissionTypes[i].ToLower() == value.ToLower())
{
MissionTypeIndex = i;
break;

View File

@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="GameAnalytics.Mono.SDK" version="1.1.12" targetFramework="net45" />
<package id="NLog" version="4.3.8" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" />
</packages>

View File

@@ -232,6 +232,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\SearchLight\SearchLightBase.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\railgunetc2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
@@ -1471,6 +1474,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterNetworking.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\DamageModifier.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Events\Missions\MissionPrefab.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\GameAnalyticsManager.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Items\Components\Signal\AdderComponent.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\StatusEffects\DelayedEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\HuskInfection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Jobs\Job.cs" />

View File

@@ -170,7 +170,7 @@
<StatusEffect type="OnUse" target="This" Condition="-1.0"/>
<sound file="alienweapon.ogg" type="OnUse" range="800.0" loop="true"/>
<sound file="Content/Items/Artifacts/alienweapon.ogg" type="OnUse" range="800.0" loop="true"/>
<LightComponent LightColor="0.8,0.7,1.0,1.0" Flicker="0.5" range="500">
<sprite texture="Content/Items/Electricity/lightsprite.png" origin="0.5,0.5"/>
@@ -211,7 +211,7 @@
<Door canbepicked="false" canbeselected="true" horizontal="true">
<Sprite texture="Content/Map/ruins.png" sourcerect="0,842,260,54" depth="0.6" origin="0.0,0.5"/>
<sound file="aliendoor.ogg" type="OnUse" range="1000.0"/>
<sound file="Content/Items/Artifacts/aliendoor.ogg" type="OnUse" range="1000.0"/>
</Door>
<ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]">
@@ -233,7 +233,7 @@
<Door canbepicked="false" canbeselected="true">
<Sprite texture="Content/Map/ruins.png" sourcerect="842,192,54,259" depth="0.6" origin="0.5,0.0"/>
<sound file="aliendoor.ogg" type="OnUse" range="3000.0"/>
<sound file="Content/Items/Artifacts/aliendoor.ogg" type="OnUse" range="3000.0"/>
</Door>
<ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]">
@@ -288,4 +288,4 @@
<output name="power_out"/>
</ConnectionPanel>
</Item>
</Items>
</Items>

View File

@@ -18,7 +18,7 @@
<Controller direction ="None" canbepicked = "true" msg="Press [E]">
<RequiredItem name="ID Card" type="Picked" msg="UNAUTHORIZED ACCESS"/>
<sound file="beep.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Button/beep.ogg" type="OnUse" range="500.0"/>
</Controller>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -50,7 +50,7 @@
<Body width="12" height="16" density="20"/>
<Controller direction="None" canbepicked="true" msg="Press [E]">
<sound file="switch.ogg" type="OnUse" range="250.0"/>
<sound file="Content/Items/Button/switch.ogg" type="OnUse" range="250.0"/>
</Controller>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -63,4 +63,4 @@
<requireditem name="Wrench" type="Equipped"/>
</Holdable>
</Item>
</Items>
</Items>

View File

@@ -11,6 +11,18 @@
</ItemContainer>
</Item>
<Item
name="Railgun Shell Rack"
pickdistance ="120">
<Sprite texture ="Content/Items/Weapons/railgunetc2.png" depth="0.84" sourcerect="57,97,72,110"/>
<ItemContainer hideitems="false" drawinventory="true" capacity="3" slotsperrow="3" itempos="17,-49.5" iteminterval="19,0" itemrotation="90" canbeselected = "true">
<Containable name="Railgun Shell"/>
<Containable name="Nuclear Shell"/>
</ItemContainer>
</Item>
<Item
name="Medicine Cabinet"
linkable="true"

View File

@@ -17,9 +17,11 @@
<Pickable holdpos="30,-15" handle1="0,1" slots="RightHand,LeftHand,Any">
<StatusEffect type="OnFire" target="This" Condition="-100.0" disabledeltatime="true">
<Conditional Condition="gt 25"/>
<sound file="Content/Items/Reactor/explosion.ogg"/>
<Explosion range="250.0" structuredamage="10" damage="20" stun="5" force="3.0"/>
</StatusEffect>
<StatusEffect type="OnFire" target="This" Condition="-100.0" disabledeltatime="true"/>
</Pickable>
</Item>
@@ -188,7 +190,7 @@
<Propulsion force="80" usablein="water" particles="bubbles">
<RequiredItems name="Battery Cell,Fulgurium Battery Cell" type="Contained" msg="Battery Cell required"/>
<StatusEffect type="OnUse" target="Contained" Condition="-1.0"/>
<sound file="scooter.ogg" type="OnUse" range="500.0" loop="true"/>
<sound file="Content/Items/Diving/scooter.ogg" type="OnUse" range="500.0" loop="true"/>
<LightComponent LightColor="1.0,1.0,1.0,1.0" Flicker="0.2" range="800">
<LightTexture texture="Content/Lights/lightcone.png" origin="0.05, 0.5" size="2.0,1.0"/>
</LightComponent>

View File

@@ -11,7 +11,7 @@
<Sprite texture ="door.png" sourcerect="80,0,19,208" depth="0.05" origin="0.5,0.0"/>
<WeldedSprite texture ="door.png" sourcerect="99,0,32,188" depth="0.0" origin="0.5,0.5"/>
<BrokenSprite texture ="door.png" sourcerect="133,0,58,208" depth="0.051" origin="0.5,0.0" scale="true"/>
<sound file="door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Door/door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
@@ -45,7 +45,7 @@
<Sprite texture="door.png" sourcerect="56,0,19,208" depth="0.05" origin="0.5,0.0"/>
<WeldedSprite texture="door.png" sourcerect="9,0,32,188" depth="0.0" origin="0.5,0.5"/>
<BrokenSprite texture="door.png" sourcerect="192,0,40,208" depth="0.051" origin="0.5,0.0" scale="true"/>
<sound file="door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Door/door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak1.ogg" range="3000"/>
@@ -79,7 +79,7 @@
<Sprite texture="hatch.png" sourcerect="128,0,128,19" depth="0.05" origin="0.0,0.5"/>
<WeldedSprite texture="hatch.png" sourcerect="0,56,108,33" depth="0.0" origin="0.5,0.5"/>
<BrokenSprite texture="hatch.png" sourcerect="128,21,128,58" depth="0.051" origin="0.0,0.5" scale="true"/>
<sound file="door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Door/door.ogg" type="OnUse" range="500.0"/>
<sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak1.ogg" range="3000"/>
@@ -110,18 +110,24 @@
<DockingPort IsHorizontal="true" DistanceTolerance="128,64" DockedDistance="64">
<Sprite texture ="dockingport.png" sourcerect="127,0,112,144" depth="0.05" origin="0.5,0.5"/>
<sound file="dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<sound file="Content/Items/Door/dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="Content/Items/Door/dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
</StatusEffect>
</DockingPort>
<fixrequirement name="Electrical repairs">
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/>
<Wire/>
<fixrequirement name="Mechanical repairs">
<skill name="Construction" level="60"/>
<skill name="Construction" level="50"/>
<item name="Welding Tool"/>
</fixrequirement>
@@ -144,8 +150,8 @@
<DockingPort IsHorizontal="false" DistanceTolerance="64,128" DockedDistance="64">
<Sprite texture ="dockingport.png" sourcerect="127,144,48,112" depth="0.05" origin="0.5,0.5"/>
<sound file="dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<sound file="Content/Items/Door/dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="Content/Items/Door/dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
</StatusEffect>
@@ -161,7 +167,7 @@
<Wire/>
<fixrequirement name="Mechanical repairs">
<skill name="Construction" level="60"/>
<skill name="Construction" level="50"/>
<item name="Welding Tool"/>
</fixrequirement>
@@ -187,7 +193,7 @@
<Sprite texture ="duct.png" sourcerect="66,0,19,19" depth="0.05" origin="-0.4,0.5"/>
<WeldedSprite texture ="duct.png" sourcerect="33,0,33,33" depth="0.0" origin="0.5,0.5"/>
<BrokenSprite texture="duct.png" sourcerect="0,34,52,52" depth="0.051" origin="0.2,0.5" scale="true"/>
<sound file="duct.ogg" type="OnUse" range="300"/>
<sound file="Content/Items/Door/duct.ogg" type="OnUse" range="300"/>
<sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="500.0"/>
<StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/ductBreak.ogg" range="2000"/>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

@@ -146,6 +146,39 @@
</ConnectionPanel>
</Item>
<Item
name="Adder Component"
category="Electrical"
Tags="smallitem"
linkable="false"
cargocontainername="Metal Crate"
price="10"
description="Outputs the sum of the received signals.">
<Deconstruct time="10">
<Item name="Steel Bar"/>
<Item name="FPGA Circuit"/>
</Deconstruct>
<Sprite texture="signalcomp.png" depth="0.8" sourcerect="17,80,16,16"/>
<AdderComponent canbeselected="true"/>
<Body width="16" height="16" density="30"/>
<Holdable selectkey="Select" pickkey="Use" slots="Any,RightHand,LeftHand" msg="Detach [Wrench]" PickingTime="5.0"
aimpos="65,-10" handle1="0,0" attachable="true" aimable="true">
<requireditem name="Wrench" type="Equipped"/>
</Holdable>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="signal_in1"/>
<input name="signal_in2"/>
<output name="signal_out"/>
</ConnectionPanel>
</Item>
<Item
name="Or Component"
category="Electrical"

View File

@@ -20,7 +20,7 @@
<Engine minvoltage="0.5" powerconsumption="2000.0" maxforce="2000" canbeselected = "true">
<StatusEffect type="InWater" target="This" condition="-2.0"/>
<GuiFrame rect="0,0,350,160" alignment="Center" style="ItemUI"/>
<sound file="engine.ogg" type="OnActive" range="3000.0" volume="CurrentVolume" loop="true"/>
<sound file="Content/Items/Engine/engine.ogg" type="OnActive" range="3000.0" volume="CurrentVolume" loop="true"/>
</Engine>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -48,7 +48,7 @@
<Engine minvoltage="0.5" powerconsumption="500.0" maxforce="300" canbeselected = "true">
<GuiFrame rect="0,0,350,160" alignment="Center" style="ItemUI"/>
<sound file="engine.ogg" type="OnActive" range="3000.0" volume="CurrentVolume" loop="true"/>
<sound file="Content/Items/Engine/engine.ogg" type="OnActive" range="3000.0" volume="CurrentVolume" loop="true"/>
</Engine>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -78,7 +78,7 @@
</Steering>
<Radar canbeselected = "true" powerconsumption="100" displaybordersize="0.2">
<sound file="radarPing.ogg" type="OnUse" range="4000.0"/>
<sound file="Content/Items/Engine/radarPing.ogg" type="OnUse" range="4000.0"/>
<GuiFrame rect="0,0,0.5,0.5" alignment="Center" style="ItemUI" color="0.0,0.0,0.0,0.0"/>
<PingCircle texture="Content/Items/Engine/pingCircle.png" origin="0.5,0.5"/>
<ScreenOverlay texture="Content/Items/Engine/radarOverlay.png" origin="0.5,0.5"/>
@@ -121,4 +121,4 @@
<input name="power_in"/>
</ConnectionPanel>
</Item>
</Items>
</Items>

View File

@@ -13,7 +13,7 @@
<GuiFrame rect="0,0,600,400" alignment="Center" style="ItemUI"/>
<sound file="fabricator.ogg" type="OnActive" range="1000.0" loop="true"/>
<sound file="Content/Items/Fabricators/fabricator.ogg" type="OnActive" range="1000.0" loop="true"/>
<fabricableitem name="Harpoon Gun" requireditems="Steel Bar, Polycarbonate Bar, Aluminium" requiredtime="20">
<RequiredSkill name="Construction" level="30"/>
@@ -135,7 +135,7 @@
<GuiFrame rect="0,0,600,400" alignment="Center" style="ItemUI"/>
<sound file="fabricator.ogg" type="OnActive" range="1000.0" loop="true"/>
<sound file="Content/Items/Fabricators/fabricator.ogg" type="OnActive" range="1000.0" loop="true"/>
<fabricableitem name="Medical Syringe" requireditems="Steel Bar" requiredtime="10"/>
<fabricableitem name="Chloral Hydrate" requireditems="Chlorine, Ethanol" requiredtime="20">
@@ -216,7 +216,7 @@
<Sprite texture="Content/Items/machines.png" depth="0.8" sourcerect="64,128,64,128"/>
<Deconstructor canbeselected = "true" powerconsumption="500.0">
<sound file="deconstructor.ogg" type="OnActive" range="1000.0" loop="true"/>
<sound file="Content/Items/Fabricators/deconstructor.ogg" type="OnActive" range="1000.0" loop="true"/>
<GuiFrame rect="0,0,300,200" alignment="Center" style="ItemUI"/>
</Deconstructor>
@@ -229,4 +229,4 @@
<ItemContainer capacity="5" canbeselected="true" hideitems="true" hudpos="0.5, 0.4" slotsperrow="5"/>
<ItemContainer capacity="10" canbeselected="true" hideitems="true" hudpos="0.5, 0.8" slotsperrow="5"/>
</Item>
</Items>
</Items>

View File

@@ -7,8 +7,8 @@
<Sprite texture="oxygengenerator.png" depth="0.8" sourcerect="0,0,128,128"/>
<OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true">
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="0.5"/>
<sound file="oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="2.0"/>
<sound file="Content/Items/OxygenGenerator/oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/>
</OxygenGenerator>
<trigger/>
@@ -31,8 +31,8 @@
<Sprite texture="vent.png" depth="0.91" sourcerect="0,0,64,64"/>
<Vent>
<sound file="ventilation.ogg" type="OnActive" range="600.0" volume="OxygenFlow" volumemultiplier="0.001f" loop="true"/>
<sound file="Content/Items/OxygenGenerator/ventilation.ogg" type="OnActive" range="600.0" volume="OxygenFlow" volumemultiplier="0.001f" loop="true"/>
</Vent>
</Item>
</Items>
</Items>

View File

@@ -8,7 +8,7 @@
<Pump canbeselected = "true" maxflow="500" PowerConsumption="300.0" MinVoltage="0.3">
<GuiFrame rect="0,0,330,145" alignment="Center" style="ItemUI"/>
<sound file="pump.ogg" type="OnActive" range="800.0" volume="CurrFlow" volumemultiplier="0.01" loop="true"/>
<sound file="Content/Items/Pump/pump.ogg" type="OnActive" range="800.0" volume="CurrFlow" volumemultiplier="0.01" loop="true"/>
</Pump>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -30,7 +30,7 @@
<Pump canbeselected = "true" maxflow="100" PowerConsumption="60.0" MinVoltage="0.3">
<GuiFrame rect="0,0,330,145" alignment="Center" style="ItemUI"/>
<sound file="pump.ogg" type="OnActive" range="500.0" volume="CurrFlow" volumemultiplier="0.01" loop="true"/>
<sound file="Content/Items/Pump/pump.ogg" type="OnActive" range="500.0" volume="CurrFlow" volumemultiplier="0.01" loop="true"/>
</Pump>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -42,4 +42,4 @@
<input name="set_targetlevel"/>
</ConnectionPanel>
</Item>
</Items>
</Items>

View File

@@ -26,7 +26,7 @@
<RequiredSkill name="Construction" level="30"/>
<StatusEffect type="InWater" target="This" Temperature="-500.0"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Fuel Rod, Heat Absorber, Temperature Control Circuit" Condition="-0.1" />
<sound file="reactor.ogg" type="OnActive" range="2000.0" volume="FissionRate" volumemultiplier="0.02" loop="true"/>
<sound file="Content/Items/Reactor/reactor.ogg" type="OnActive" range="2000.0" volume="FissionRate" volumemultiplier="0.02" loop="true"/>
<StatusEffect type="OnBroken" target="This" FissionRate="0.0" disabledeltatime="true">
<Sound file="Content/Items/Reactor/explosion.ogg" range="5000"/>

View File

@@ -10,7 +10,7 @@
<Sprite texture ="tools.png" sourcerect="0,17,41,17" depth="0.55"/>
<!-- the item takes 10 seconds to break down in a deconstructor and turns into a polycarbonate bar and a steel bar -->
<!-- the item takes 10 seconds to break down in a deconstructor and turns into a polycarbonate bar, steel bar and aluminium -->
<Deconstruct time="10">
<Item name="Polycarbonate Bar"/>
<Item name="Steel Bar"/>
@@ -35,7 +35,7 @@
<ParticleEmitterHitItem particle="weldsmoke" particlespersecond="3" anglemin="-5" anglemax="5" velocitymin="10" velocitymax="100"/>
<ParticleEmitterHitCharacter particle="fleshsmoke" particlespersecond="3" anglemin="-5" anglemax="5" velocitymin="10" velocitymax="100"/>
<sound file="weldingTool.ogg" type="OnUse" range="500.0" loop="true"/>
<sound file="Content/Items/Tools/weldingTool.ogg" type="OnUse" range="500.0" loop="true"/>
<!-- when using, the contained welding fuel tank will detoriate (= lose fuel) -->
<StatusEffect type="OnUse" target="Contained" targetnames="Welding Fuel Tank" Condition="-1.0"/>
@@ -48,7 +48,7 @@
<!-- construction skill 20 required to use the item -->
<RequiredSkill name="Construction" level="20"/>
<!-- if the skill requirement isn't met the welding fuel will be used 3 times as fast -->
<StatusEffect type="OnFailure" target="Contained" targetnames="Welding Fuel Tank,Oxygen Tank" Condition="-3.0"/>
<LightComponent LightColor="1.0,0.9,0.7,1.0" Flicker="0.5">
@@ -98,7 +98,7 @@
<ParticleEmitterHitItem particle="plasmasmoke" particlespersecond="3" anglemin="-5" anglemax="5" velocitymin="10" velocitymax="100"/>
<ParticleEmitterHitCharacter particle="fleshsmoke" particlespersecond="3" anglemin="-5" anglemax="5" velocitymin="10" velocitymax="100"/>
<sound file="plasmaCutter.ogg" type="OnUse" range="500.0" loop="true"/>
<sound file="Content/Items/Tools/plasmaCutter.ogg" type="OnUse" range="500.0" loop="true"/>
<Fixable name="structure"/>
@@ -164,7 +164,7 @@
<RepairTool extinquishamount="60.0" range="500" barrelpos="21,25">
<ParticleEmitter particle="extinguisher" velocitymin="500.0" velocitymax="650.0" particlespersecond="50"/>
<StatusEffect type="OnUse" target="This" Condition="-2.0"/>
<sound file="extinguisher.ogg" type="OnUse" range="500.0" loop="true"/>
<sound file="Content/Items/Tools/extinguisher.ogg" type="OnUse" range="500.0" loop="true"/>
</RepairTool>
<Propulsion force="-20" usablein="water" particles="bubbles"/>
@@ -330,7 +330,7 @@
<StatusEffect type="OnBroken" target="This" IsOn="false"/>
<sound file="flare.ogg" type="OnActive" range="800.0" loop="true"/>
<sound file="Content/Items/Tools/flare.ogg" type="OnActive" range="800.0" loop="true"/>
</LightComponent>
</Item>

View File

@@ -35,8 +35,7 @@
category="Machine"
type="Controller"
linkable="true"
disableitemusagewhenselected="true"
>
disableitemusagewhenselected="true">
<Sprite texture ="railgunetc.png" depth="0.8" sourcerect="182,0,61,97"/>
@@ -55,11 +54,34 @@
</ConnectionPanel>
</Item>
<Item
name="Rear Railgun Controller"
category="Machine"
type="Controller"
linkable="true"
disableitemusagewhenselected="true" >
<Sprite texture ="railgunetc2.png" depth="0.8" sourcerect="188,0,57,96"/>
<Controller UserPos="35, -50.0" direction ="Left" canbeselected = "true">
<limbposition limb="Head" position="-5,-62"/>
<limbposition limb="Torso" position="-5,-108"/>
<limbposition limb="LeftHand" position="43,-85"/>
<limbposition limb="RightHand" position="43,-85"/>
</Controller>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
<requireditem name="Screwdriver,Wire" type="Equipped"/>
<input name="power_in"/>
<output name="position_out"/>
<output name="trigger_out"/>
</ConnectionPanel>
</Item>
<Item
name="Railgun Loader"
category="Machine"
linkable="true"
>
linkable="true">
<Sprite texture ="railgunetc.png" depth="0.8" sourcerect="0,0,177,128"/>
@@ -69,6 +91,48 @@
<Containable name="Ancient Weapon"/>
</ItemContainer>
</Item>
<Item
name="Railgun Single Loader"
category="Machine"
linkable="true">
<Sprite texture ="railgunetc2.png" depth="0.8" sourcerect="131,2,46,128"/>
<ItemContainer hideitems="false" drawinventory="true" capacity="1" slotsperrow="1" itempos="23,-76" iteminterval="0,0" itemrotation="90" canbeselected = "true">
<Containable name="Railgun Shell"/>
<Containable name="Nuclear Shell"/>
<Containable name="Ancient Weapon"/>
</ItemContainer>
</Item>
<Item
name="Forward Railgun Loader"
category="Machine"
linkable="true">
<Sprite texture ="railgunetc2.png" depth="0.8" sourcerect="1,2,128,46"/>
<ItemContainer hideitems="false" drawinventory="true" capacity="1" slotsperrow="1" itempos="49.5,-22.7" iteminterval="0,0" itemrotation="0" canbeselected = "true">
<Containable name="Railgun Shell"/>
<Containable name="Nuclear Shell"/>
<Containable name="Ancient Weapon"/>
</ItemContainer>
</Item>
<Item
name="Rear Railgun Loader"
category="Machine"
linkable="true">
<Sprite texture ="railgunetc2.png" depth="0.8" sourcerect="1,50,128,46"/>
<ItemContainer hideitems="false" drawinventory="true" capacity="1" slotsperrow="1" itempos="76,-22.7" iteminterval="0,0" itemrotation="180" canbeselected = "true">
<Containable name="Railgun Shell"/>
<Containable name="Nuclear Shell"/>
<Containable name="Ancient Weapon"/>
</ItemContainer>
</Item>
<Item
name="Railgun Shell"
@@ -86,7 +150,7 @@
<Body width="87" height="18" density="30"/>
<Holdable slots="RightHand+LeftHand" holdpos="0,-50" handle1="-10,0" handle2="10,0" aimable="false"/>
<Projectile characterusable="false" launchimpulse="80.0">
<Attack damage="100" bleedingdamage="10" structuredamage="200" damagetype="Blunt" severlimbsprobability="1.0"/>
@@ -94,7 +158,9 @@
<ParticleEmitter particle="bubbles" anglemin="0" anglemax="360" particleamount="10" velocitymin="0" velocitymax="50" scalemin="2" scalemax="5"/>
</StatusEffect>
<StatusEffect type="OnImpact" target="Contained" Condition="-100.0"/>
<StatusEffect type="OnImpact" target="Contained">
<Use/>
</StatusEffect>
</Projectile>
<ItemContainer hideitems="true" capacity="1" canbeselected = "true">

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

View File

@@ -43,8 +43,8 @@
holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/>
<RangedWeapon barrelpos="49,10" spread="1" unskilledspread="10">
<Sound file="harpoon1.ogg" type="OnUse"/>
<Sound file="harpoon2.ogg" type="OnUse"/>
<Sound file="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/>
<Sound file="Content/Items/Weapons/harpoon2.ogg" type="OnUse"/>
<StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" flash="false" sparks="false" camerashake="5.0"/>
</StatusEffect>
@@ -78,7 +78,7 @@
aimpos="90,10" handle1="-10,-7"/>
<RangedWeapon barrelpos="30,20" spread="0" unskilledspread="10">
<Sound file="syringegun.ogg" type="OnUse"/>
<Sound file="Content/Items/Weapons/syringegun.ogg" type="OnUse"/>
<RequiredItems name="Medical Syringe" type="Contained" msg="Please load a Syringe"/>
<RequiredSkill name="Weapons" level="15"/>
<RequiredSkill name="Medical" level="30"/>
@@ -128,7 +128,7 @@
aimpos="90,10" handle1="-11,-7"/>
<RangedWeapon barrelpos="14,6" spread="0" unskilledspread="2">
<Sound file="revolver.ogg" type="OnUse" range="3000"/>
<Sound file="Content/Items/Weapons/revolver.ogg" type="OnUse" range="3000"/>
<StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/>
</StatusEffect>
@@ -300,7 +300,7 @@
<Holdable slots="Any,RightHand,LeftHand" aimpos="100,0" handle1="-5,0"/>
<RangedWeapon reload="2">
<Sound file="honk.ogg" type="OnUse"/>
<Sound file="Content/Items/Weapons/honk.ogg" type="OnUse"/>
</RangedWeapon>
</Item>

View File

@@ -103,10 +103,10 @@
<Job name="Assistant" description="Assistants don't have any specific responsibilities or areas of expertise. This job is a good choice for newcomers who want to get a hang of working on board the submarine without taking up tasks that they aren't qualified for." allowalways="true" minkarma="0.0">
<Skills>
<Skill name="Weapons" level="20,30"/>
<Skill name="Construction" level="20,30"/>
<Skill name="Electrical Engineering" level="20,30"/>
<Skill name="Medical" level="10,20"/>
<Skill name="Weapons" level="20,32"/>
<Skill name="Construction" level="20,32"/>
<Skill name="Electrical Engineering" level="20,32"/>
<Skill name="Medical" level="10,22"/>
</Skills>
<Items>
<Item name="ID Card" equip="true"/>
@@ -117,4 +117,4 @@
</Item>
</Items>
</Job>
</Jobs>
</Jobs>

View File

@@ -78,6 +78,10 @@ namespace Barotrauma
public EnemyAIController(Character c, string file) : base(c)
{
targetMemories = new Dictionary<AITarget, AITargetMemory>();
outsideSteering = new SteeringManager(this);
insideSteering = new IndoorsSteeringManager(this, false);
steeringManager = outsideSteering;
state = AIState.None;
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return;
@@ -103,13 +107,6 @@ namespace Barotrauma
fleeHealthThreshold = aiElement.GetAttributeFloat("fleehealththreshold", 0.0f);
attachToWalls = aiElement.GetAttributeBool("attachtowalls", false);
outsideSteering = new SteeringManager(this);
insideSteering = new IndoorsSteeringManager(this, false);
steeringManager = outsideSteering;
state = AIState.None;
}
public override void SelectTarget(AITarget target)

View File

@@ -52,7 +52,7 @@ namespace Barotrauma
public float Range { get; private set; }
[Serialize(0.0f, false)]
public float DamageRange { get; private set; }
public float DamageRange { get; set; }
[Serialize(0.0f, false)]
public float Duration { get; private set; }

View File

@@ -1948,8 +1948,21 @@ namespace Barotrauma
AnimController.Frozen = false;
GameServer.Log(LogName+" has died (Cause of death: "+causeOfDeath+")", ServerLog.MessageType.Attack);
GameServer.Log(LogName + " has died (Cause of death: " + causeOfDeath + ")", ServerLog.MessageType.Attack);
if (GameSettings.SendUserStatistics)
{
string characterType = "Unknown";
if (this == controlled)
characterType = "Player";
else if (IsRemotePlayer)
characterType = "RemotePlayer";
else if (AIController is EnemyAIController)
characterType = "Enemy";
else if (AIController is HumanAIController)
characterType = "AICrew";
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Kill:" + characterType + ":" + SpeciesName + ":" + causeOfDeath);
}
if (OnDeath != null) OnDeath(this, causeOfDeath);
KillProjSpecific();

View File

@@ -51,12 +51,6 @@ namespace Barotrauma
//the physics body of the limb
public PhysicsBody body;
private readonly int refJointIndex;
private readonly float steerForce;
private readonly bool doesFlip;
protected readonly Vector2 stepOffset;
public Sprite sprite, damagedSprite;
@@ -69,8 +63,6 @@ namespace Barotrauma
public readonly bool ignoreCollisions;
private float damage, burnt;
private bool isSevered;
private float severedFadeOutTimer;
@@ -81,16 +73,9 @@ namespace Barotrauma
public const float SoundInterval = 0.4f;
public readonly Attack attack;
private List<DamageModifier> damageModifiers;
private Direction dir;
private List<WearableSprite> wearingItems;
private Vector2 animTargetPos;
private float scale;
private List<DamageModifier> damageModifiers;
public float AttackTimer;
@@ -100,17 +85,13 @@ namespace Barotrauma
set
{
isSevered = value;
if (isSevered)
{
damage = 100.0f;
}
#if CLIENT
if (isSevered) damage = 100.0f;
#endif
}
}
public bool DoesFlip
{
get { return doesFlip; }
}
public bool DoesFlip { get; private set; }
public Vector2 WorldPosition
{
@@ -132,21 +113,12 @@ namespace Barotrauma
get { return body.Rotation; }
}
public float Scale
{
get { return scale; }
}
public float Scale { get; private set; }
//where an animcontroller is trying to pull the limb, only used for debug visualization
public Vector2 AnimTargetPos
{
get { return animTargetPos; }
}
public Vector2 AnimTargetPos { get; private set; }
public float SteerForce
{
get { return steerForce; }
}
public float SteerForce { get; private set; }
public float Mass
{
@@ -166,38 +138,25 @@ namespace Barotrauma
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
}
public int RefJointIndex
{
get { return refJointIndex; }
}
public int RefJointIndex { get; private set; }
public Vector2 StepOffset
{
get { return stepOffset; }
}
public float Burnt
{
get { return burnt; }
protected set { burnt = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public List<WearableSprite> WearingItems
{
get { return wearingItems; }
}
public List<WearableSprite> WearingItems { get; private set; }
public Limb (Character character, XElement element, float scale = 1.0f)
{
this.character = character;
wearingItems = new List<WearableSprite>();
WearingItems = new List<WearableSprite>();
dir = Direction.Right;
DoesFlip = element.GetAttributeBool("flip", false);
doesFlip = element.GetAttributeBool("flip", false);
this.scale = scale;
Scale = scale;
body = new PhysicsBody(element, scale);
@@ -217,7 +176,7 @@ namespace Barotrauma
body.UserData = this;
refJointIndex = -1;
RefJointIndex = -1;
Vector2 pullJointPos = Vector2.Zero;
@@ -240,7 +199,7 @@ namespace Barotrauma
stepOffset = element.GetAttributeVector2("stepoffset", Vector2.Zero) * scale;
stepOffset = ConvertUnits.ToSimUnits(stepOffset);
refJointIndex = element.GetAttributeInt("refjoint", -1);
RefJointIndex = element.GetAttributeInt("refjoint", -1);
}
else
@@ -254,7 +213,7 @@ namespace Barotrauma
GameMain.World.AddJoint(pullJoint);
steerForce = element.GetAttributeFloat("steerforce", 0.0f);
SteerForce = element.GetAttributeFloat("steerforce", 0.0f);
if (element.Attribute("mouthpos") != null)
{
@@ -329,12 +288,12 @@ namespace Barotrauma
public void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false)
{
Vector2 pullPos = body.SimPosition;
if (pullJoint!=null && !pullFromCenter)
if (pullJoint != null && !pullFromCenter)
{
pullPos = pullJoint.WorldAnchorA;
}
animTargetPos = pos;
AnimTargetPos = pos;
body.MoveToPos(pos, force, pullPos);
}
@@ -352,7 +311,7 @@ namespace Barotrauma
}
}
foreach (WearableSprite wearable in wearingItems)
foreach (WearableSprite wearable in WearingItems)
{
foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
{
@@ -370,54 +329,13 @@ namespace Barotrauma
bleedingAmount *= damageModifier.BleedingMultiplier;
}
#if CLIENT
if (playSound)
{
string damageSoundType = (damageType == DamageType.Blunt) ? "LimbBlunt" : "LimbSlash";
foreach (DamageModifier damageModifier in appliedDamageModifiers)
{
if (!string.IsNullOrWhiteSpace(damageModifier.DamageSound))
{
damageSoundType = damageModifier.DamageSound;
break;
}
}
SoundPlayer.PlayDamageSound(damageSoundType, amount, position);
}
if (character.UseBloodParticles)
{
float bloodParticleAmount = bleedingAmount <= 0.0f ? 0 : (int)Math.Min(amount / 5, 10);
float bloodParticleSize = MathHelper.Clamp(amount / 50.0f, 0.1f, 1.0f);
for (int i = 0; i < bloodParticleAmount; i++)
{
var blood = GameMain.ParticleManager.CreateParticle(inWater ? "waterblood" : "blood", WorldPosition, Vector2.Zero, 0.0f, character.AnimController.CurrentHull);
if (blood != null)
{
blood.Size *= bloodParticleSize;
}
}
if (bloodParticleAmount > 0 && character.CurrentHull != null)
{
character.CurrentHull.AddDecal("blood", WorldPosition, MathHelper.Clamp(bloodParticleSize, 0.5f, 1.0f));
}
}
#endif
if (damageType == DamageType.Burn)
{
Burnt += amount * 10.0f;
}
damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
AddDamageProjSpecific(position, damageType, amount, bleedingAmount, playSound, appliedDamageModifiers);
return new AttackResult(amount, bleedingAmount, appliedDamageModifiers);
}
partial void AddDamageProjSpecific(Vector2 position, DamageType damageType, float amount, float bleedingAmount, bool playSound, List<DamageModifier> appliedDamageModifiers);
public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
{
if (armorSector == Vector2.Zero) return false;
@@ -435,10 +353,7 @@ namespace Barotrauma
public void Update(float deltaTime)
{
UpdateProjSpecific();
if (!character.IsDead) damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
if (burnt > 0.0f) Burnt -= deltaTime;
UpdateProjSpecific(deltaTime);
if (LinearVelocity.X > 500.0f)
{
@@ -463,17 +378,11 @@ namespace Barotrauma
if (character.IsDead) return;
damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
SoundTimer -= deltaTime;
}
partial void UpdateProjSpecific();
public void ActivateDamagedSprite()
{
damage = 100.0f;
}
partial void UpdateProjSpecific(float deltaTime);
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
{
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));

View File

@@ -88,6 +88,7 @@ namespace Barotrauma
public void Execute(string[] args)
{
if (onExecute == null) return;
onExecute(args);
}
@@ -100,6 +101,7 @@ namespace Barotrauma
{
if (onClientRequestExecute == null)
{
if (onExecute == null) return;
onExecute(args);
}
else
@@ -247,11 +249,10 @@ namespace Barotrauma
};
}));
commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory]: Spawn an item at the position of the cursor, in the inventory of the controlled character or at a random spawnpoint if the last parameter is omitted.",
commands.Add(new Command("spawnitem", "spawnitem [itemname] [cursor/inventory/random/[name]]: Spawn an item at the position of the cursor, in the inventory of the controlled character, in the inventory of the client with the given name, or at a random spawnpoint if the last parameter is omitted or \"random\".",
(string[] args) =>
{
string errorMsg;
SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out errorMsg);
SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Character.Controlled, out string errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg))
{
ThrowError(errorMsg);
@@ -260,20 +261,18 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
string errorMsg;
SpawnItem(args, cursorWorldPos, out errorMsg);
SpawnItem(args, cursorWorldPos, client.Character, out string errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg))
{
ThrowError(errorMsg);
GameMain.Server.SendConsoleMessage(errorMsg, client);
}
},
},
() =>
{
List<string> itemNames = new List<string>();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{
ItemPrefab itemPrefab = prefab as ItemPrefab;
if (itemPrefab != null) itemNames.Add(itemPrefab.Name);
if (prefab is ItemPrefab itemPrefab) itemNames.Add(itemPrefab.Name);
}
return new string[][]
@@ -283,6 +282,7 @@ namespace Barotrauma
};
}));
commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
{
HumanAIController.DisableCrewAI = true;
@@ -849,8 +849,7 @@ namespace Barotrauma
{
if (args.Length < 2) return;
int id;
int.TryParse(args[0], out id);
int.TryParse(args[0], out int id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
@@ -891,8 +890,7 @@ namespace Barotrauma
return;
}
int id;
int.TryParse(args[0], out id);
int.TryParse(args[0], out int id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
@@ -1019,11 +1017,10 @@ namespace Barotrauma
commands.Add(new Command("kickid", "kickid [id]: Kick the player with the specified client ID out of the server.", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
if (GameMain.NetworkMember == null || args.Length == 0) return;
int id = 0;
int.TryParse(args[0], out id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
int.TryParse(args[0], out int id);
var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
ThrowError("Client id \"" + id + "\" not found.");
@@ -1032,7 +1029,7 @@ namespace Barotrauma
ShowQuestionPrompt("Reason for kicking \"" + client.Name + "\"?", (reason) =>
{
GameMain.Server.KickPlayer(client.Name, reason);
GameMain.NetworkMember.KickPlayer(client.Name, reason);
});
}));
@@ -1073,17 +1070,16 @@ namespace Barotrauma
commands.Add(new Command("banid", "banid [id]: Kick and ban the player with the specified client ID from the server.", (string[] args) =>
{
if (GameMain.Server == null || args.Length == 0) return;
if (GameMain.NetworkMember == null || args.Length == 0) return;
int id = 0;
int.TryParse(args[0], out id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
int.TryParse(args[0], out int id);
var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
if (client == null)
{
ThrowError("Client id \"" + id + "\" not found.");
return;
}
ShowQuestionPrompt("Reason for banning \"" + client.Name + "\"?", (reason) =>
{
ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) =>
@@ -1091,8 +1087,7 @@ namespace Barotrauma
TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration))
{
TimeSpan parsedBanDuration;
if (!TryParseTimeSpan(duration, out parsedBanDuration))
if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
{
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return;
@@ -1100,7 +1095,7 @@ namespace Barotrauma
banDuration = parsedBanDuration;
}
GameMain.Server.BanPlayer(client.Name, reason, false, banDuration);
GameMain.NetworkMember.BanPlayer(client.Name, reason, false, banDuration);
});
});
}));
@@ -1117,8 +1112,7 @@ namespace Barotrauma
TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration))
{
TimeSpan parsedBanDuration;
if (!TryParseTimeSpan(duration, out parsedBanDuration))
if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
{
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return;
@@ -1139,23 +1133,72 @@ namespace Barotrauma
}
}
});
});
},
(string[] args) =>
{
#if CLIENT
if (GameMain.Client == null || args.Length == 0) return;
ShowQuestionPrompt("Reason for banning the ip \"" + args[0] + "\"?", (reason) =>
{
ShowQuestionPrompt("Enter the duration of the ban (leave empty to ban permanently, or use the format \"[days] d [hours] h\")", (duration) =>
{
TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration))
{
if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
{
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return;
}
banDuration = parsedBanDuration;
}
GameMain.Client.SendConsoleCommand(
"banip " +
args[0] + " " +
(banDuration.HasValue ? banDuration.Value.TotalSeconds.ToString() : "0") + " " +
reason);
});
});
#endif
},
(Client client, Vector2 cursorPos, string[] args) =>
{
if (args.Length < 1) return;
var clients = GameMain.Server.ConnectedClients.FindAll(c => c.Connection.RemoteEndPoint.Address.ToString() == args[0]);
TimeSpan? duration = null;
if (args.Length > 1)
{
if (double.TryParse(args[1], out double durationSeconds))
{
if (durationSeconds > 0) duration = TimeSpan.FromSeconds(durationSeconds);
}
else
{
GameMain.Server.SendConsoleMessage("\"" + args[1] + "\" is not a valid ban duration.", client);
return;
}
}
string reason = "";
if (args.Length > 2) reason = string.Join(" ", args.Skip(2));
if (clients.Count == 0)
{
GameMain.Server.BanList.BanPlayer("Unnamed", args[0], reason, duration);
}
else
{
foreach (Client cl in clients)
{
GameMain.Server.BanClient(cl, reason, false, duration);
}
}
}));
commands.Add(new Command("teleportcharacter|teleport", "teleport [character name]: Teleport the specified character to the position of the cursor. If the name parameter is omitted, the controlled character will be teleported.", (string[] args) =>
{
Character tpCharacter = null;
if (args.Length == 0)
{
tpCharacter = Character.Controlled;
}
else
{
tpCharacter = FindMatchingCharacter(args, false);
}
Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false);
if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam;
@@ -1167,17 +1210,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character tpCharacter = null;
if (args.Length == 0)
{
tpCharacter = client.Character;
}
else
{
tpCharacter = FindMatchingCharacter(args, false);
}
Character tpCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam;
@@ -1216,7 +1249,7 @@ namespace Barotrauma
Submarine.LockX = !Submarine.LockX;
}, null, null));
commands.Add(new Command("locky", "loxky: Lock vertical movement of the main submarine.", (string[] args) =>
commands.Add(new Command("locky", "locky: Lock vertical movement of the main submarine.", (string[] args) =>
{
Submarine.LockY = !Submarine.LockY;
}, null, null));
@@ -1234,18 +1267,29 @@ namespace Barotrauma
}
}));
commands.Add(new Command("findentityids", "findentityids [entityname]", (string[] args) =>
{
if (args.Length == 0) return;
args[0] = args[0].ToLowerInvariant();
foreach (MapEntity mapEntity in MapEntity.mapEntityList)
{
if (mapEntity.Name.ToLowerInvariant() == args[0])
{
ThrowError(mapEntity.ID + ": " + mapEntity.Name.ToString());
}
}
foreach (Character character in Character.CharacterList)
{
if (character.Name.ToLowerInvariant() == args[0] || character.SpeciesName.ToLowerInvariant() == args[0])
{
ThrowError(character.ID + ": " + character.Name.ToString());
}
}
}));
commands.Add(new Command("heal", "heal [character name]: Restore the specified character to full health. If the name parameter is omitted, the controlled character will be healed.", (string[] args) =>
{
Character healedCharacter = null;
if (args.Length == 0)
{
healedCharacter = Character.Controlled;
}
else
{
healedCharacter = FindMatchingCharacter(args);
}
Character healedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (healedCharacter != null)
{
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null);
@@ -1257,16 +1301,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character healedCharacter = null;
if (args.Length == 0)
{
healedCharacter = client.Character;
}
else
{
healedCharacter = FindMatchingCharacter(args);
}
Character healedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (healedCharacter != null)
{
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null);
@@ -1285,16 +1320,7 @@ namespace Barotrauma
commands.Add(new Command("revive", "revive [character name]: Bring the specified character back from the dead. If the name parameter is omitted, the controlled character will be revived.", (string[] args) =>
{
Character revivedCharacter = null;
if (args.Length == 0)
{
revivedCharacter = Character.Controlled;
}
else
{
revivedCharacter = FindMatchingCharacter(args);
}
Character revivedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
@@ -1313,16 +1339,7 @@ namespace Barotrauma
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character revivedCharacter = null;
if (args.Length == 0)
{
revivedCharacter = client.Character;
}
else
{
revivedCharacter = FindMatchingCharacter(args);
}
Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (revivedCharacter == null) return;
revivedCharacter.Revive(false);
@@ -1358,16 +1375,16 @@ namespace Barotrauma
commands.Add(new Command("ragdoll", "ragdoll [character name]: Force-ragdoll the specified character. If the name parameter is omitted, the controlled character will be ragdolled.", (string[] args) =>
{
Character ragdolledCharacter = null;
if (args.Length == 0)
Character ragdolledCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (ragdolledCharacter != null)
{
ragdolledCharacter = Character.Controlled;
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
}
else
{
ragdolledCharacter = FindMatchingCharacter(args);
}
},
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character ragdolledCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (ragdolledCharacter != null)
{
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
@@ -1477,20 +1494,14 @@ namespace Barotrauma
commands.Add(new Command("kill", "kill [character]: Immediately kills the specified character.", (string[] args) =>
{
Character killedCharacter = null;
if (args.Length == 0)
{
killedCharacter = Character.Controlled;
}
else
{
killedCharacter = FindMatchingCharacter(args);
}
if (killedCharacter != null)
{
killedCharacter.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
}
Character killedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
},
null,
(Client client, Vector2 cursorWorldPos, string[] args) =>
{
Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
}));
commands.Add(new Command("killmonsters", "killmonsters: Immediately kills all AI-controlled enemies in the level.", (string[] args) =>
@@ -2120,7 +2131,7 @@ namespace Barotrauma
}
}
private static void SpawnItem(string[] args, Vector2 cursorPos, out string errorMsg)
private static void SpawnItem(string[] args, Vector2 cursorPos, Character controlledCharacter, out string errorMsg)
{
errorMsg = "";
if (args.Length < 1) return;
@@ -2128,23 +2139,48 @@ namespace Barotrauma
Vector2? spawnPos = null;
Inventory spawnInventory = null;
int extraParams = 0;
switch (args.Last())
if (args.Length > 1)
{
case "cursor":
extraParams = 1;
spawnPos = cursorPos;
break;
case "inventory":
extraParams = 1;
spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory;
break;
default:
extraParams = 0;
break;
switch (args[1])
{
case "cursor":
spawnPos = cursorPos;
break;
case "inventory":
spawnInventory = controlledCharacter?.Inventory;
break;
default:
//Check if last arg matches the name of an in-game player
if (GameMain.Server != null)
{
var client = GameMain.Server.ConnectedClients.Find(c => c.Name.ToLower() == args.Last().ToLower());
if (client == null)
{
NewMessage("No player found with the name \"" + args.Last() + "\". Spawning item at random location. If the player you want to give the item to has a space in their name, try surrounding their name with quotes (\").", Color.Red);
break;
}
else if (client.Character == null)
{
errorMsg = "The player \"" + args.Last() + "\" is connected, but hasn't spawned yet.";
return;
}
else
{
//If the last arg matches the name of an in-game player, set the destination to their inventory.
spawnInventory = client.Character.Inventory;
break;
}
}
else
{
var matchingCharacter = FindMatchingCharacter(args.Skip(1).ToArray());
if (matchingCharacter?.Inventory != null) spawnInventory = matchingCharacter.Inventory;
}
break;
}
}
string itemName = string.Join(" ", args.Take(args.Length - extraParams)).ToLowerInvariant();
string itemName = args[0];
var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null)
@@ -2162,7 +2198,6 @@ namespace Barotrauma
if (spawnPos != null)
{
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
}
else if (spawnInventory != null)
{

View File

@@ -13,12 +13,12 @@ namespace Barotrauma
private int requiredDeliveryAmount;
public CargoMission(XElement element, Location[] locations)
: base(element, locations)
public CargoMission(MissionPrefab prefab, Location[] locations)
: base(prefab, locations)
{
itemConfig = element.Element("Items");
itemConfig = prefab.ConfigElement.Element("Items");
requiredDeliveryAmount = element.GetAttributeInt("requireddeliveryamount", 0);
requiredDeliveryAmount = prefab.ConfigElement.GetAttributeInt("requireddeliveryamount", 0);
}
private void InitItems()

View File

@@ -29,6 +29,8 @@ namespace Barotrauma
{
get
{
if (descriptions == null) return "";
if (GameMain.NetworkMember==null || GameMain.NetworkMember.Character==null)
{
//non-team-specific description
@@ -47,20 +49,20 @@ namespace Barotrauma
{
if (winner == -1) return "";
return successMessage
return base.SuccessMessage
.Replace("[loser]", teamNames[1 - winner])
.Replace("[winner]", teamNames[winner]);
}
}
public CombatMission(XElement element, Location[] locations)
: base(element, locations)
public CombatMission(MissionPrefab prefab, Location[] locations)
: base(prefab, locations)
{
descriptions = new string[]
{
element.GetAttributeString("descriptionneutral", ""),
element.GetAttributeString("description1", ""),
element.GetAttributeString("description2", "")
prefab.ConfigElement.GetAttributeString("descriptionneutral", ""),
prefab.ConfigElement.GetAttributeString("description1", ""),
prefab.ConfigElement.GetAttributeString("description2", "")
};
for (int i = 0; i < descriptions.Length; i++)
@@ -73,8 +75,8 @@ namespace Barotrauma
teamNames = new string[]
{
element.GetAttributeString("teamname1", "Team A"),
element.GetAttributeString("teamname2", "Team B")
prefab.ConfigElement.GetAttributeString("teamname1", "Team A"),
prefab.ConfigElement.GetAttributeString("teamname2", "Team B")
};
}

View File

@@ -1,45 +1,18 @@
using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma
{
partial class Mission
{
public static List<string> MissionTypes = new List<string>() { "Random" };
private string name;
private string description;
{
protected bool completed;
protected string successMessage;
protected string failureMessage;
private readonly MissionPrefab prefab;
protected string radarLabel;
protected List<string> headers;
protected List<string> messages;
private int reward;
public string Name
{
get { return name; }
}
public virtual string Description
{
get { return description; }
}
public int Reward
{
get { return reward; }
get { return prefab.Name; }
}
public bool Completed
@@ -48,109 +21,101 @@ namespace Barotrauma
set { completed = value; }
}
public int Reward
{
get { return prefab.Reward; }
}
public virtual bool AllowRespawn
{
get { return true; }
}
public virtual string RadarLabel
{
get { return radarLabel; }
}
public virtual Vector2 RadarPosition
{
get { return Vector2.Zero; }
}
virtual public string SuccessMessage
public string RadarLabel
{
get { return successMessage; }
get { return prefab.RadarLabel; }
}
public List<string> Headers
{
get; private set;
}
public List<string> Messages
{
get; private set;
}
public virtual string SuccessMessage
{
get;
protected set;
}
public string FailureMessage
{
get { return failureMessage; }
get;
protected set;
}
public static void Init()
public virtual string Description
{
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
foreach (string file in files)
{
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
{
string missionTypeName = element.Name.ToString();
missionTypeName = missionTypeName.Replace("Mission", "");
if (!MissionTypes.Contains(missionTypeName)) MissionTypes.Add(missionTypeName);
}
}
get;
protected set;
}
public Mission(XElement element, Location[] locations)
public MissionPrefab Prefab
{
name = element.GetAttributeString("name", "");
get { return prefab; }
}
public Mission(MissionPrefab prefab, Location[] locations)
{
System.Diagnostics.Debug.Assert(locations.Length == 2);
description = element.GetAttributeString("description", "");
this.prefab = prefab;
reward = element.GetAttributeInt("reward", 1);
successMessage = element.GetAttributeString("successmessage",
"Mission completed successfully");
failureMessage = element.GetAttributeString("failuremessage",
"Mission failed");
radarLabel = element.GetAttributeString("radarlabel", "");
messages = new List<string>();
headers = new List<string>();
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "message") continue;
headers.Add(subElement.GetAttributeString("header", ""));
messages.Add(subElement.GetAttributeString("text", ""));
}
Description = prefab.Description;
SuccessMessage = prefab.SuccessMessage;
FailureMessage = prefab.FailureMessage;
Headers = new List<string>(prefab.Headers);
Messages = new List<string>(prefab.Messages);
for (int n = 0; n < 2; n++)
{
description = description.Replace("[location" + (n + 1) + "]", locations[n].Name);
successMessage = successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
for (int m = 0; m < messages.Count; m++)
if (Description != null) Description = Description.Replace("[location" + (n + 1) + "]", locations[n].Name);
if (SuccessMessage != null) SuccessMessage = SuccessMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
if (FailureMessage != null) FailureMessage = FailureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
for (int m = 0; m < Messages.Count; m++)
{
messages[m] = messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name);
Messages[m] = Messages[m].Replace("[location" + (n + 1) + "]", locations[n].Name);
}
}
}
public static Mission LoadRandom(Location[] locations, string seed, string missionType = "", bool isSinglePlayer = false)
{
return LoadRandom(locations, new MTRandom(ToolBox.StringToInt(seed)), missionType, isSinglePlayer);
}
public static Mission LoadRandom(Location[] locations, MTRandom rand, string missionType = "", bool isSinglePlayer = false)
{
//todo: use something else than strings to define the mission type
missionType = missionType.ToLowerInvariant();
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
string configFile = files[rand.Next(files.Count)];
XDocument doc = XMLExtensions.TryLoadXml(configFile);
if (doc == null) return null;
int eventCount = doc.Root.Elements().Count();
//int[] commonness = new int[eventCount];
float[] eventProbability = new float[eventCount];
float probabilitySum = 0.0f;
List<XElement> matchingElements = new List<XElement>();
List<MissionPrefab> allowedMissions = new List<MissionPrefab>();
if (missionType == "random")
{
matchingElements = doc.Root.Elements().ToList();
allowedMissions.AddRange(MissionPrefab.List);
if (GameMain.Server != null)
{
allowedMissions.RemoveAll(mission => !GameMain.Server.AllowedRandomMissionTypes.Any(a => mission.TypeMatches(a)));
}
}
else if (missionType == "none")
{
@@ -158,68 +123,31 @@ namespace Barotrauma
}
else if (string.IsNullOrWhiteSpace(missionType))
{
matchingElements = doc.Root.Elements().ToList();
allowedMissions.AddRange(MissionPrefab.List);
}
else
{
matchingElements = doc.Root.Elements().ToList().FindAll(m => m.Name.ToString().ToLowerInvariant().Replace("mission", "") == missionType);
allowedMissions = MissionPrefab.List.FindAll(m => m.TypeMatches(missionType));
}
if (isSinglePlayer)
{
matchingElements.RemoveAll(m => m.GetAttributeBool("multiplayeronly", false));
allowedMissions.RemoveAll(m => m.MultiplayerOnly);
}
else
{
matchingElements.RemoveAll(m => m.GetAttributeBool("singleplayeronly", false));
allowedMissions.RemoveAll(m => m.SingleplayerOnly);
}
int i = 0;
foreach (XElement element in matchingElements)
{
eventProbability[i] = element.GetAttributeInt("commonness", 1);
probabilitySum += eventProbability[i];
i++;
}
float probabilitySum = allowedMissions.Sum(m => m.Commonness);
float randomNumber = (float)rand.NextDouble() * probabilitySum;
i = 0;
foreach (XElement element in matchingElements)
foreach (MissionPrefab missionPrefab in allowedMissions)
{
if (randomNumber <= eventProbability[i])
if (randomNumber <= missionPrefab.Commonness)
{
Type t;
string type = element.Name.ToString();
try
{
t = Type.GetType("Barotrauma." + type, true, true);
if (t == null)
{
DebugConsole.ThrowError("Error in " + configFile + "! Could not find a mission class of the type \"" + type + "\".");
continue;
}
}
catch
{
DebugConsole.ThrowError("Error in " + configFile + "! Could not find a mission class of the type \"" + type + "\".");
continue;
}
ConstructorInfo constructor = t.GetConstructor(new[] { typeof(XElement), typeof(Location[]) });
object instance = constructor.Invoke(new object[] { element, locations });
Mission mission = (Mission)instance;
return mission;
return missionPrefab.Instantiate(locations);
}
randomNumber -= eventProbability[i];
i++;
randomNumber -= missionPrefab.Commonness;
}
return null;
@@ -251,7 +179,7 @@ namespace Barotrauma
var mode = GameMain.GameSession.GameMode as CampaignMode;
if (mode == null) return;
mode.Money += reward;
mode.Money += Reward;
}
}
}

View File

@@ -0,0 +1,118 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma
{
class MissionPrefab
{
public static List<MissionPrefab> List = new List<MissionPrefab>();
public static List<string> MissionTypes = new List<string>() { "Random" };
private string name;
public string Name
{
get { return name; }
}
private Type missionType;
private ConstructorInfo constructor;
public virtual string Description { get; private set; }
public bool MultiplayerOnly { get; private set; }
public bool SingleplayerOnly { get; private set; }
public float Commonness { get; private set; }
public int Reward { get; private set; }
public string RadarLabel { get; private set; }
public List<string> Headers { get; private set; }
public List<string> Messages { get; private set; }
public string SuccessMessage { get; private set; }
public string FailureMessage { get; private set; }
public XElement ConfigElement { get; private set; }
public static void Init()
{
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions);
foreach (string file in files)
{
XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc?.Root == null) continue;
foreach (XElement element in doc.Root.Elements())
{
string missionTypeName = element.Name.ToString();
missionTypeName = missionTypeName.Replace("Mission", "");
List.Add(new MissionPrefab(element));
if (!MissionTypes.Contains(missionTypeName)) MissionTypes.Add(missionTypeName);
}
}
}
public MissionPrefab(XElement element)
{
ConfigElement = element;
name = element.GetAttributeString("name", "");
Description = element.GetAttributeString("description", "");
Commonness = element.GetAttributeFloat("commonness", 1.0f);
SingleplayerOnly = element.GetAttributeBool("singleplayeronly", false);
MultiplayerOnly = element.GetAttributeBool("multiplayeronly", false);
Reward = element.GetAttributeInt("reward", 1);
SuccessMessage = element.GetAttributeString("successmessage", "Mission completed successfully");
FailureMessage = element.GetAttributeString("failuremessage", "Mission failed");
RadarLabel = element.GetAttributeString("radarlabel", "");
Messages = new List<string>();
Headers = new List<string>();
foreach (XElement subElement in element.Elements())
{
if (subElement.Name.ToString().ToLowerInvariant() != "message") continue;
Headers.Add(subElement.GetAttributeString("header", ""));
Messages.Add(subElement.GetAttributeString("text", ""));
}
string type = element.Name.ToString();
try
{
missionType = Type.GetType("Barotrauma." + type, true, true);
if (missionType == null)
{
DebugConsole.ThrowError("Error in mission prefab " + Name + "! Could not find a mission class of the type \"" + type + "\".");
return;
}
}
catch
{
DebugConsole.ThrowError("Error in mission prefab " + Name + "! Could not find a mission class of the type \"" + type + "\".");
return;
}
constructor = missionType.GetConstructor(new[] { typeof(MissionPrefab), typeof(Location[]) });
}
public Mission Instantiate(Location[] locations)
{
return constructor?.Invoke(new object[] { this, locations }) as Mission;
}
public bool TypeMatches(string typeName)
{
//TODO: use enums instead of strings?
typeName = typeName.ToLowerInvariant();
return missionType.Name.ToString().Replace("Mission", "").ToLowerInvariant() == typeName;
}
}
}

View File

@@ -18,10 +18,10 @@ namespace Barotrauma
get { return monster != null && !monster.IsDead ? radarPosition : Vector2.Zero; }
}
public MonsterMission(XElement element, Location[] locations)
: base(element, locations)
public MonsterMission(MissionPrefab prefab, Location[] locations)
: base(prefab, locations)
{
monsterFile = element.GetAttributeString("monsterfile", "");
monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
}

View File

@@ -1,7 +1,6 @@
using FarseerPhysics;
using Microsoft.Xna.Framework;
using System;
using System.Xml.Linq;
namespace Barotrauma
{
@@ -23,10 +22,10 @@ namespace Barotrauma
}
}
public SalvageMission(XElement element, Location[] locations)
: base(element, locations)
public SalvageMission(MissionPrefab prefab, Location[] locations)
: base(prefab, locations)
{
string itemName = element.GetAttributeString("itemname", "");
string itemName = prefab.ConfigElement.GetAttributeString("itemname", "");
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null)
@@ -35,10 +34,10 @@ namespace Barotrauma
return;
}
string spawnPositionTypeStr = element.GetAttributeString("spawntype", "");
string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) ||
!Enum.TryParse<Level.PositionType>(spawnPositionTypeStr, true, out spawnPositionType))
!Enum.TryParse(spawnPositionTypeStr, true, out spawnPositionType))
{
spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
}

View File

@@ -0,0 +1,25 @@
using GameAnalyticsSDK.Net;
using System;
namespace Barotrauma
{
public static class GameAnalyticsManager
{
public static void Init()
{
#if DEBUB
GameAnalytics.SetEnabledInfoLog(true);
#endif
GameAnalytics.ConfigureBuild(GameMain.Version.ToString());
GameAnalytics.ConfigureAvailableCustomDimensions01("singleplayer", "multiplayer", "editor");
GameAnalytics.Initialize("a3a073c20982de7c15d21e840e149122", "9010ad9a671233b8d9610d76cec8c897d9ff3ba7");
string contentPackageName = GameMain.Config?.SelectedContentPackage?.Name;
if (!string.IsNullOrEmpty(contentPackageName))
{
GameAnalytics.AddDesignEvent("ContentPackage:" +
contentPackageName.Replace(":", "").Substring(0, Math.Min(32, contentPackageName.Length)));
}
}
}
}

View File

@@ -16,9 +16,22 @@
: base(preset, param)
{
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
MTRandom rand = new MTRandom(ToolBox.StringToInt(GameMain.NetLobbyScreen.LevelSeed));
mission = Mission.LoadRandom(locations, rand, param as string);
if (param is string)
{
mission = Mission.LoadRandom(locations, GameMain.NetLobbyScreen.LevelSeed, (string)param);
}
else if (param is MissionPrefab)
{
mission = ((MissionPrefab)param).Instantiate(locations);
}
else if (param is Mission)
{
mission = (Mission)param;
}
else
{
throw new System.ArgumentException("Unrecognized MissionMode parameter \"" + param + "\"");
}
}
}
}

View File

@@ -55,13 +55,7 @@ namespace Barotrauma
public override void Start()
{
base.Start();
if (GameMain.Server != null)
{
CargoManager.CreateItems();
}
base.Start();
lastUpdateID++;
}

View File

@@ -108,10 +108,9 @@ namespace Barotrauma
codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
while (traitorCount-- >= 0)
while (traitorCount-- > 0)
{
if (traitorCandidates.Count <= 0)
break;
if (traitorCandidates.Count <= 0) break;
int traitorIndex = Rand.Int(traitorCandidates.Count);
Character traitorCharacter = traitorCandidates[traitorIndex];

View File

@@ -91,27 +91,35 @@ namespace Barotrauma
set { savePath = value; }
}
public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset = null, string missionType = "")
public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset, string missionType = "")
: this(submarine, savePath)
{
GameMode = gameModePreset.Instantiate(missionType);
}
public GameSession(Submarine submarine, string savePath, GameModePreset gameModePreset, MissionPrefab missionPrefab)
: this(submarine, savePath)
{
GameMode = gameModePreset.Instantiate(missionPrefab);
}
private GameSession(Submarine submarine, string savePath)
{
Submarine.MainSub = submarine;
this.submarine = submarine;
GameMain.GameSession = this;
EventManager = new EventManager(this);
this.savePath = savePath;
#if CLIENT
CrewManager = new CrewManager();
infoButton = new GUIButton(new Rectangle(10, 10, 100, 20), "Info", "", null);
infoButton.OnClicked = ToggleInfoFrame;
#endif
if (gameModePreset != null) GameMode = gameModePreset.Instantiate(missionType);
this.submarine = submarine;
}
public GameSession(Submarine selectedSub, string saveFile, XDocument doc)
: this(selectedSub, saveFile)
{
@@ -218,8 +226,22 @@ namespace Barotrauma
EventManager.StartRound(level);
if (GameMode != null) GameMode.MsgBox();
if (GameMode != null)
{
GameMode.MsgBox();
if (GameMode is MultiPlayerCampaign campaign && GameMain.Server != null)
{
campaign.CargoManager.CreateItems();
}
}
if (GameSettings.SendUserStatistics)
{
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Submarine:" + submarine.Name);
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent(GameAnalyticsSDK.Net.EGAProgressionStatus.Start,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
#if CLIENT
roundSummary = new RoundSummary(this);
@@ -231,6 +253,11 @@ namespace Barotrauma
public void EndRound(string endMessage)
{
if (Mission != null) Mission.End();
if (GameSettings.SendUserStatistics)
{
GameAnalyticsSDK.Net.GameAnalytics.AddProgressionEvent((Mission == null || Mission.Completed) ? GameAnalyticsSDK.Net.EGAProgressionStatus.Complete : GameAnalyticsSDK.Net.EGAProgressionStatus.Fail,
GameMode.Name, (Mission == null ? "None" : Mission.GetType().ToString()));
}
#if CLIENT
if (roundSummary != null)

View File

@@ -25,7 +25,7 @@ namespace Barotrauma
public bool EnableSplashScreen { get; set; }
//public bool FullScreenEnabled { get; set; }
private KeyOrMouse[] keyMapping;
private WindowMode windowMode;
@@ -51,8 +51,37 @@ namespace Barotrauma
}
}
private bool unsavedSettings;
private int characterHeadIndex;
public int CharacterHeadIndex
{
get { return characterHeadIndex; }
set
{
if (value == characterHeadIndex) return;
// Begin saving coroutine. Remove any existing save coroutines if one is running.
if (CoroutineManager.IsCoroutineRunning("saveCoroutine")) { CoroutineManager.StopCoroutines("saveCoroutine"); }
CoroutineManager.StartCoroutine(ApplyUnsavedChanges(), "saveCoroutine");
characterHeadIndex = value;
}
}
private Gender characterGender;
public Gender CharacterGender
{
get { return characterGender; }
set
{
if (value == characterGender) return;
// Begin saving coroutine. Remove any existing save coroutines if one is running.
if (CoroutineManager.IsCoroutineRunning("saveCoroutine")) { CoroutineManager.StopCoroutines("saveCoroutine"); }
CoroutineManager.StartCoroutine(ApplyUnsavedChanges(), "saveCoroutine");
characterGender = value;
}
}
private bool unsavedSettings;
public bool UnsavedSettings
{
get
@@ -125,6 +154,18 @@ namespace Barotrauma
public static bool VerboseLogging { get; set; }
public static bool SaveDebugConsoleLogs { get; set; }
private static bool sendUserStatistics;
public static bool SendUserStatistics
{
get { return sendUserStatistics; }
set
{
sendUserStatistics = value;
GameMain.Config.Save("config.xml");
}
}
public static bool ShowUserStatisticsPrompt { get; private set; }
public GameSettings(string filePath)
{
ContentPackage.LoadAll(ContentPackage.Folder);
@@ -143,6 +184,14 @@ namespace Barotrauma
VerboseLogging = doc.Root.GetAttributeBool("verboselogging", false);
SaveDebugConsoleLogs = doc.Root.GetAttributeBool("savedebugconsolelogs", false);
if (doc.Root.Attribute("senduserstatistics") == null)
{
ShowUserStatisticsPrompt = true;
}
else
{
sendUserStatistics = doc.Root.GetAttributeBool("senduserstatistics", true);
}
if (doc == null)
{
@@ -210,18 +259,15 @@ namespace Barotrauma
case "keymapping":
foreach (XAttribute attribute in subElement.Attributes())
{
InputType inputType;
if (Enum.TryParse(attribute.Name.ToString(), true, out inputType))
if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType))
{
int mouseButton;
if (int.TryParse(attribute.Value.ToString(), out mouseButton))
if (int.TryParse(attribute.Value.ToString(), out int mouseButton))
{
keyMapping[(int)inputType] = new KeyOrMouse(mouseButton);
}
else
{
Keys key;
if (Enum.TryParse(attribute.Value.ToString(), true, out key))
if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
{
keyMapping[(int)inputType] = new KeyOrMouse(key);
}
@@ -238,6 +284,9 @@ namespace Barotrauma
break;
case "player":
defaultPlayerName = subElement.GetAttributeString("name", "");
characterHeadIndex = subElement.GetAttributeInt("headindex", Rand.Int(10));
characterGender = subElement.GetAttributeString("gender", Rand.Range(0.0f, 1.0f) < 0.5f ? "male" : "female")
.ToLowerInvariant() == "male" ? Gender.Male : Gender.Female;
break;
}
}
@@ -259,10 +308,8 @@ namespace Barotrauma
{
case "contentpackage":
string path = subElement.GetAttributeString("path", "");
SelectedContentPackage = ContentPackage.list.Find(cp => cp.Path == path);
if (SelectedContentPackage == null) SelectedContentPackage = new ContentPackage(path);
break;
}
@@ -287,7 +334,8 @@ namespace Barotrauma
new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
new XAttribute("enablesplashscreen", EnableSplashScreen));
new XAttribute("enablesplashscreen", EnableSplashScreen),
new XAttribute("senduserstatistics", sendUserStatistics));
if (WasGameUpdated)
{
@@ -344,8 +392,10 @@ namespace Barotrauma
gameplay.Add(jobPreferences);
doc.Root.Add(gameplay);
var playerElement = new XElement("player");
playerElement.Add(new XAttribute("name", defaultPlayerName ?? ""));
var playerElement = new XElement("player",
new XAttribute("name", defaultPlayerName ?? ""),
new XAttribute("headindex", characterHeadIndex),
new XAttribute("gender", characterGender));
doc.Root.Add(playerElement);
doc.Save(filePath);

View File

@@ -179,7 +179,6 @@ namespace Barotrauma.Items.Components
return (picker != null);
}
public override bool Combine(Item item)
{
if (!containableItems.Any(x => x.MatchesItem(item))) return false;
@@ -195,6 +194,42 @@ namespace Barotrauma.Items.Components
return false;
}
public void SetContainedItemPositions()
{
Vector2 simPos = item.SimPosition;
Vector2 displayPos = item.Position;
foreach (Item contained in Inventory.Items)
{
if (contained == null) continue;
if (contained.body != null)
{
try
{
contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f);
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions", e);
#endif
}
}
contained.Rect =
new Rectangle(
(int)(displayPos.X - contained.Rect.Width / 2.0f),
(int)(displayPos.Y + contained.Rect.Height / 2.0f),
contained.Rect.Width, contained.Rect.Height);
contained.Submarine = item.Submarine;
contained.CurrentHull = item.CurrentHull;
contained.SetContainedItemPositions();
}
}
public override void OnMapLoaded()
{
if (itemIds == null) return;

View File

@@ -99,7 +99,9 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null);
if (hull1 == null) return;
//check the hull if the item is movable
if (item.body != null) GetHull();
if (hull1 == null) return;
float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : voltage;

View File

@@ -93,6 +93,26 @@ namespace Barotrauma.Items.Components
}
}
public override void OnItemLoaded()
{
if (attack != null && attack.DamageRange <= 0.0f && item.body != null)
{
switch (item.body.BodyShape)
{
case PhysicsBody.Shape.Circle:
attack.DamageRange = item.body.radius;
break;
case PhysicsBody.Shape.Capsule:
attack.DamageRange = item.body.height / 2 + item.body.radius;
break;
case PhysicsBody.Shape.Rectangle:
attack.DamageRange = new Vector2(item.body.width / 2.0f, item.body.height / 2.0f).Length();
break;
}
attack.DamageRange = ConvertUnits.ToDisplayUnits(attack.DamageRange);
}
}
public override bool Use(float deltaTime, Character character = null)
{
if (character != null && !characterUsable) return false;
@@ -196,7 +216,6 @@ namespace Barotrauma.Items.Components
if (OnProjectileCollision(fixture, normal))
{
hitSomething = true;
//Character.Controlled.AnimController.Teleport(point - Character.Controlled.SimPosition, Vector2.Zero);
break;
}
}
@@ -281,8 +300,7 @@ namespace Barotrauma.Items.Components
Character character = null;
if (attack != null)
{
var submarine = target.Body.UserData as Submarine;
if (submarine != null)
if (target.Body.UserData is Submarine submarine)
{
item.Move(-submarine.Position);
item.Submarine = submarine;
@@ -290,9 +308,8 @@ namespace Barotrauma.Items.Components
return true;
}
Limb limb = target.Body.UserData as Limb;
Structure structure;
if (limb != null)
if (target.Body.UserData is Limb limb)
{
attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f);
if (limb.character != null)
@@ -350,13 +367,12 @@ namespace Barotrauma.Items.Components
{
contained.SetTransform(item.SimPosition, contained.body.Rotation);
}
//contained.Condition = 0.0f; //Let the freaking .xml handle it jeez
}
}
if (RemoveOnHit)
{
Item.Spawner.AddToRemoveQueue(item);
Entity.Spawner.AddToRemoveQueue(item);
}
return true;

View File

@@ -0,0 +1,64 @@
using System;
using System.Xml.Linq;
namespace Barotrauma.Items.Components
{
class AdderComponent : ItemComponent
{
//an array to keep track of how long ago a signal was received on both inputs
protected float[] timeSinceReceived;
protected float[] receivedSignal;
//the output is sent if both inputs have received a signal within the timeframe
protected float timeFrame;
[InGameEditable, Serialize(0.0f, true)]
public float TimeFrame
{
get { return timeFrame; }
set
{
timeFrame = Math.Max(0.0f, value);
}
}
public AdderComponent(Item item, XElement element)
: base(item, element)
{
timeSinceReceived = new float[] { Math.Max(timeFrame * 2.0f, 0.1f), Math.Max(timeFrame * 2.0f, 0.1f) };
receivedSignal = new float[2];
IsActive = true;
}
public override void Update(float deltaTime, Camera cam)
{
bool sendOutput = true;
for (int i = 0; i < timeSinceReceived.Length; i++)
{
if (timeSinceReceived[i] > timeFrame) sendOutput = false;
timeSinceReceived[i] += deltaTime;
}
if (sendOutput)
{
item.SendSignal(0, (receivedSignal[0] + receivedSignal[1]).ToString(), "signal_out", null);
}
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
{
switch (connection.Name)
{
case "signal_in1":
float.TryParse(signal, out receivedSignal[0]);
timeSinceReceived[0] = 0.0f;
break;
case "signal_in2":
float.TryParse(signal, out receivedSignal[1]);
timeSinceReceived[1] = 0.0f;
break;
}
}
}
}

View File

@@ -10,49 +10,51 @@ namespace Barotrauma.Items.Components
{
const int SignalQueueSize = 500;
//the output is sent if both inputs have received a signal within the timeframe
private TimeSpan delay;
private Queue<Tuple<string, DateTime>> signalQueue;
private Queue<Pair<string, float>> signalQueue;
[InGameEditable, Serialize(1.0f, true)]
[InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f), Serialize(1.0f, true)]
public float Delay
{
get { return (float)delay.TotalSeconds; }
set
{
float seconds = MathHelper.Clamp(value, 0.0f, 60.0f);
delay = new TimeSpan(0,0,0,0, (int)(seconds*1000.0f));
}
get;
set;
}
[InGameEditable(ToolTip = "Should the component discard previously received signals when a new one is received."), Serialize(false, true)]
public bool ResetWhenSignalReceived
{
get;
set;
}
public DelayComponent(Item item, XElement element)
: base (item, element)
{
signalQueue = new Queue<Tuple<string, DateTime>>();
signalQueue = new Queue<Pair<string, float>>();
IsActive = true;
}
public override void Update(float deltaTime, Camera cam)
{
while (signalQueue.Any() && signalQueue.Peek().Item2 + delay <= DateTime.Now)
foreach (var val in signalQueue)
{
val.Second -= deltaTime;
}
while (signalQueue.Count > 0 && signalQueue.Peek().Second <= 0.0f)
{
var signalOut = signalQueue.Dequeue();
item.SendSignal(0, signalOut.Item1, "signal_out", null);
item.SendSignal(0, signalOut.First, "signal_out", null);
}
}
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power=0.0f)
public override void ReceiveSignal(int stepsTaken, string signal, Connection connection, Item source, Character sender, float power = 0.0f)
{
switch (connection.Name)
{
case "signal_in":
if (signalQueue.Count >= SignalQueueSize) return;
signalQueue.Enqueue(new Tuple<string, DateTime>(signal, DateTime.Now));
if (ResetWhenSignalReceived) signalQueue.Clear();
signalQueue.Enqueue(Pair<string, float>.Create(signal, Delay));
break;
}
}

View File

@@ -49,14 +49,9 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam)
{
if (motionDetected)
{
item.SendSignal(1, output, "state_out", null);
}
else if (!string.IsNullOrWhiteSpace(falseOutput))
{
item.SendSignal(1, falseOutput, "state_out", null);
}
string signalOut = motionDetected ? output : falseOutput;
if (!string.IsNullOrEmpty(signalOut)) item.SendSignal(1, signalOut, "state_out", null);
updateTimer -= deltaTime;
if (updateTimer > 0.0f) return;

View File

@@ -315,9 +315,9 @@ namespace Barotrauma
public override string ToString()
{
#if CLIENT
return (GameMain.DebugDraw) ? Name + "(ID: " + ID + ")" : Name;
return (GameMain.DebugDraw) ? Name + " (ID: " + ID + ")" : Name;
#elif SERVER
return Name + "(ID: " + ID + ")";
return Name + " (ID: " + ID + ")";
#endif
}
@@ -602,7 +602,7 @@ namespace Barotrauma
foreach (Item item in ItemList) item.FindHull();
}
public virtual Hull FindHull()
public Hull FindHull()
{
if (parentInventory != null && parentInventory.Owner != null)
{
@@ -648,39 +648,9 @@ namespace Barotrauma
public void SetContainedItemPositions()
{
if (ownInventory == null) return;
Vector2 simPos = SimPosition;
Vector2 displayPos = Position;
foreach (Item contained in ownInventory.Items)
foreach (ItemComponent component in components)
{
if (contained == null) continue;
if (contained.body != null)
{
try
{
contained.body.FarseerBody.SetTransformIgnoreContacts(ref simPos, 0.0f);
}
catch (Exception e)
{
#if DEBUG
DebugConsole.ThrowError("SetTransformIgnoreContacts threw an exception in SetContainedItemPositions", e);
#endif
}
}
contained.Rect =
new Rectangle(
(int)(displayPos.X - contained.Rect.Width / 2.0f),
(int)(displayPos.Y + contained.Rect.Height / 2.0f),
contained.Rect.Width, contained.Rect.Height);
contained.Submarine = Submarine;
contained.CurrentHull = CurrentHull;
contained.SetContainedItemPositions();
(component as ItemContainer)?.SetContainedItemPositions();
}
}
@@ -804,7 +774,7 @@ namespace Barotrauma
private bool IsInWater()
{
if (CurrentHull == null) return true;
float surfaceY = CurrentHull.Surface;
return CurrentHull.WaterVolume > 0.0f && Position.Y < surfaceY;

View File

@@ -80,7 +80,7 @@ namespace Barotrauma
if (distSqr > displayRangeSqr) continue;
//ignore reactors (don't want to blow them up)
if (item.GetComponent<Reactor>() == null) continue;
if (item.GetComponent<Reactor>() != null) continue;
float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;

View File

@@ -664,19 +664,20 @@ namespace Barotrauma
float damageAmount = 0.0f;
for (int i = 0; i < SectionCount; i++)
{
if (Vector2.DistanceSquared(SectionPosition(i, true), worldPosition) <= attack.DamageRange * attack.DamageRange)
Rectangle sectionRect = sections[i].rect;
sectionRect.Y -= sections[i].rect.Height;
if (MathUtils.CircleIntersectsRectangle(transformedPos, attack.DamageRange, sectionRect))
{
damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker);
#if CLIENT
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
#endif
}
}
#if CLIENT
if (playSound)// && !SectionBodyDisabled(i))
if (playSound)
{
string damageSoundType = (attack.DamageType == DamageType.Blunt) ? "StructureBlunt" : "StructureSlash";
SoundPlayer.PlayDamageSound(damageSoundType, damageAmount, worldPosition, tags: Tags);

View File

@@ -490,7 +490,7 @@ namespace Barotrauma
return wayPoints[Rand.Int(wayPoints.Count, (useSyncedRand ? Rand.RandSync.Server : Rand.RandSync.Unsynced))];
}
public static WayPoint[] SelectCrewSpawnPoints(List<CharacterInfo> crew, Submarine submarine, bool tryAssignWayPoint = true)
public static WayPoint[] SelectCrewSpawnPoints(List<CharacterInfo> crew, Submarine submarine)
{
List<WayPoint> subWayPoints = WayPointList.FindAll(wp => wp.Submarine == submarine);
@@ -524,29 +524,24 @@ namespace Barotrauma
assignedWayPoints[i] = wp;
break;
}
if (assignedWayPoints[i] != null) continue;
if (tryAssignWayPoint)
//try to assign a spawnpoint that isn't meant for any specific job
var nonJobSpecificPoints = subWayPoints.FindAll(wp => wp.spawnType == SpawnType.Human && wp.assignedJob == null);
if (nonJobSpecificPoints.Any())
{
//try to assign a spawnpoint that isn't meant for any specific job
var nonJobSpecificPoints = subWayPoints.FindAll(wp => wp.spawnType == SpawnType.Human && wp.assignedJob == null);
if (nonJobSpecificPoints.Any())
{
assignedWayPoints[i] = nonJobSpecificPoints[Rand.Int(nonJobSpecificPoints.Count, Rand.RandSync.Server)];
}
assignedWayPoints[i] = nonJobSpecificPoints[Rand.Int(nonJobSpecificPoints.Count, Rand.RandSync.Server)];
}
if (assignedWayPoints[i] != null) continue;
//everything else failed -> just give a random spawnpoint
assignedWayPoints[i] = GetRandom(SpawnType.Human);
//everything else failed -> just give a random spawnpoint inside the sub
assignedWayPoints[i] = GetRandom(SpawnType.Human, null, submarine, true);
}
for (int i = 0; i < assignedWayPoints.Length; i++ )
for (int i = 0; i < assignedWayPoints.Length; i++)
{
if (assignedWayPoints[i]==null)
if (assignedWayPoints[i] == null)
{
DebugConsole.ThrowError("Couldn't find a waypoint for " + crew[i].Name + "!");
assignedWayPoints[i] = WayPointList[0];

View File

@@ -29,10 +29,20 @@ namespace Barotrauma.Networking
public byte TeamID = 0;
public Character Character;
private Character character;
public Character Character
{
get { return character; }
set
{
character = value;
if (character != null) HasSpawned = true;
}
}
public CharacterInfo CharacterInfo;
public NetConnection Connection { get; set; }
public bool InGame;
public bool InGame;
public bool HasSpawned; //has the client spawned as a character during the current round
public UInt16 LastRecvGeneralUpdate = 0;

View File

@@ -209,6 +209,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.RandomizeSettings();
started = true;
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:Start");
yield return CoroutineStatus.Success;
}
@@ -778,11 +780,21 @@ namespace Barotrauma.Networking
case ClientPermissions.Ban:
string bannedName = inc.ReadString().ToLowerInvariant();
string banReason = inc.ReadString();
bool range = inc.ReadBoolean();
double durationSeconds = inc.ReadDouble();
var bannedClient = connectedClients.Find(cl => cl != sender && cl.Name.ToLowerInvariant() == bannedName);
if (bannedClient != null)
{
Log("Client \"" + sender.Name + "\" banned \"" + bannedClient.Name + "\".", ServerLog.MessageType.ServerMessage);
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, false);
if (durationSeconds > 0)
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range, TimeSpan.FromSeconds(durationSeconds));
}
else
{
BanClient(bannedClient, string.IsNullOrEmpty(banReason) ? "Banned by " + sender.Name : banReason, range);
}
}
break;
case ClientPermissions.EndRound:
@@ -993,7 +1005,7 @@ namespace Barotrauma.Networking
outmsg.WriteRangedInteger(0, 2, (int)TraitorsEnabled);
outmsg.WriteRangedInteger(0, Mission.MissionTypes.Count - 1, (GameMain.NetLobbyScreen.MissionTypeIndex));
outmsg.WriteRangedInteger(0, MissionPrefab.MissionTypes.Count - 1, (GameMain.NetLobbyScreen.MissionTypeIndex));
outmsg.Write((byte)GameMain.NetLobbyScreen.SelectedModeIndex);
outmsg.Write(GameMain.NetLobbyScreen.LevelSeed);
@@ -1213,7 +1225,7 @@ namespace Barotrauma.Networking
//don't instantiate a new gamesession if we're playing a campaign
if (campaign == null || GameMain.GameSession == null)
{
GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, Mission.MissionTypes[GameMain.NetLobbyScreen.MissionTypeIndex]);
GameMain.GameSession = new GameSession(selectedSub, "", selectedMode, MissionPrefab.MissionTypes[GameMain.NetLobbyScreen.MissionTypeIndex]);
}
if (GameMain.GameSession.GameMode.Mission != null &&
@@ -1341,11 +1353,12 @@ namespace Barotrauma.Networking
List<Character> characters = new List<Character>();
foreach (Client client in ConnectedClients)
{
if (client.Character != null)
characters.Add(client.Character);
if (client.Character != null) characters.Add(client.Character);
}
var max = (int)Math.Round(characters.Count * 0.2f, 1);
var traitorCount = Math.Max(1, TraitorsEnabled == YesNoMaybe.Maybe ? Rand.Int(max) + 1 : max);
if (Character != null) characters.Add(Character);
int max = Math.Max(TraitorUseRatio ? (int)Math.Round(characters.Count * TraitorRatio, 1) : 1, 1);
int traitorCount = Rand.Int(max + 1);
TraitorManager = new TraitorManager(this, traitorCount);
if (TraitorManager.TraitorList.Count > 0)
@@ -1357,6 +1370,8 @@ namespace Barotrauma.Networking
}
}
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("Traitors:" + (TraitorManager == null ? "Disabled" : "Enabled"));
SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients);
yield return CoroutineStatus.Running;
@@ -1402,6 +1417,8 @@ namespace Barotrauma.Networking
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash);
msg.Write(selectedMode.Name);
msg.Write((short)(GameMain.GameSession.GameMode?.Mission == null ?
-1 : MissionPrefab.List.IndexOf(GameMain.GameSession.GameMode.Mission.Prefab)));
MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
@@ -1495,6 +1512,7 @@ namespace Barotrauma.Networking
foreach (Client client in connectedClients)
{
client.Character = null;
client.HasSpawned = false;
client.InGame = false;
}
}
@@ -1614,6 +1632,7 @@ namespace Barotrauma.Networking
}
client.Character = null;
client.HasSpawned = false;
client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) msg = client.Name + " has left the server";
@@ -2063,9 +2082,17 @@ namespace Barotrauma.Networking
if (jobPrefab != null) jobPreferences.Add(jobPrefab);
}
sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender);
sender.CharacterInfo.HeadSpriteId = headSpriteId;
sender.JobPreferences = jobPreferences;
sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender)
{
HeadSpriteId = headSpriteId
};
//if the client didn't provide job preferences, we'll use the preferences that are randomly assigned in the Client constructor
Debug.Assert(sender.JobPreferences.Count > 0);
if (jobPreferences.Count > 0)
{
sender.JobPreferences = jobPreferences;
}
}
public void AssignJobs(List<Client> unassigned, bool assignHost)
@@ -2110,6 +2137,7 @@ namespace Barotrauma.Networking
//if any of the players has chosen a job that is Always Allowed, give them that job
for (int i = unassigned.Count - 1; i >= 0; i--)
{
if (unassigned[i].JobPreferences.Count == 0) continue;
if (!unassigned[i].JobPreferences[0].AllowAlways) continue;
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
unassigned.RemoveAt(i);
@@ -2138,47 +2166,49 @@ namespace Barotrauma.Networking
}
}
//find a suitable job for the rest of the players
foreach (Client c in unassigned)
//attempt to give the clients a job they have in their job preferences
for (int i = unassigned.Count - 1; i >= 0; i--)
{
foreach (JobPrefab preferredJob in c.JobPreferences)
foreach (JobPrefab preferredJob in unassigned[i].JobPreferences)
{
//the maximum number of players that can have this job hasn't been reached yet
// -> assign it to the client
if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && c.Karma >= preferredJob.MinKarma)
if (assignedClientCount[preferredJob] < preferredJob.MaxNumber && unassigned[i].Karma >= preferredJob.MinKarma)
{
c.AssignedJob = preferredJob;
unassigned[i].AssignedJob = preferredJob;
assignedClientCount[preferredJob]++;
unassigned.RemoveAt(i);
break;
}
//none of the jobs the client prefers are available anymore
else if (preferredJob == c.JobPreferences.Last())
{
//find all jobs that are still available
var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[preferredJob] < jp.MaxNumber && c.Karma >= jp.MinKarma);
}
}
//all jobs taken, give a random job
if (remainingJobs.Count == 0)
{
DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
int jobIndex = Rand.Range(0,JobPrefab.List.Count);
int skips = 0;
while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
{
jobIndex++;
skips++;
if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
if (skips >= JobPrefab.List.Count) break;
}
c.AssignedJob = JobPrefab.List[jobIndex];
assignedClientCount[c.AssignedJob]++;
}
else //some jobs still left, choose one of them by random
{
c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
assignedClientCount[c.AssignedJob]++;
}
//give random jobs to rest of the clients
foreach (Client c in unassigned)
{
//find all jobs that are still available
var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
//all jobs taken, give a random job
if (remainingJobs.Count == 0)
{
DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
int jobIndex = Rand.Range(0, JobPrefab.List.Count);
int skips = 0;
while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
{
jobIndex++;
skips++;
if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
if (skips >= JobPrefab.List.Count) break;
}
c.AssignedJob = JobPrefab.List[jobIndex];
assignedClientCount[c.AssignedJob]++;
}
else //some jobs still left, choose one of them by random
{
c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
assignedClientCount[c.AssignedJob]++;
}
}
}
@@ -2236,7 +2266,11 @@ namespace Barotrauma.Networking
Log("Shutting down the server...", ServerLog.MessageType.ServerMessage);
log.Save();
}
if (GameSettings.SendUserStatistics)
{
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:ShutDown");
}
server.Shutdown("The server has been shut down");
}
}

View File

@@ -254,6 +254,12 @@ namespace Barotrauma.Networking
set;
}
public List<string> AllowedRandomMissionTypes
{
get;
set;
}
[Serialize(60f, true)]
public float AutoBanTime
{
@@ -287,6 +293,8 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString());
doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes));
#if SERVER
doc.Root.SetAttributeValue("password", password);
#endif
@@ -334,18 +342,22 @@ namespace Barotrauma.Networking
#endif
subSelectionMode = SelectionMode.Manual;
Enum.TryParse<SelectionMode>(doc.Root.GetAttributeString("SubSelection", "Manual"), out subSelectionMode);
Enum.TryParse(doc.Root.GetAttributeString("SubSelection", "Manual"), out subSelectionMode);
Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
modeSelectionMode = SelectionMode.Manual;
Enum.TryParse<SelectionMode>(doc.Root.GetAttributeString("ModeSelection", "Manual"), out modeSelectionMode);
Enum.TryParse(doc.Root.GetAttributeString("ModeSelection", "Manual"), out modeSelectionMode);
Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
var traitorsEnabled = TraitorsEnabled;
Enum.TryParse<YesNoMaybe>(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
Enum.TryParse(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
TraitorsEnabled = traitorsEnabled;
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
AllowedRandomMissionTypes = doc.Root.GetAttributeStringArray(
"AllowedRandomMissionTypes",
MissionPrefab.MissionTypes.ToArray()).ToList();
if (GameMain.NetLobbyScreen != null
#if CLIENT
&& GameMain.NetLobbyScreen.ServerMessage != null

View File

@@ -204,8 +204,11 @@ namespace Barotrauma.Networking
respawnShuttle.Velocity = Vector2.Zero;
shuttleSteering.AutoPilot = false;
shuttleSteering.MaintainPos = false;
if (shuttleSteering != null)
{
shuttleSteering.AutoPilot = false;
shuttleSteering.MaintainPos = false;
}
}
private void UpdateTransporting(float deltaTime)
@@ -264,7 +267,10 @@ namespace Barotrauma.Networking
respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
shuttleSteering.SetDestinationLevelStart();
if (shuttleSteering != null)
{
shuttleSteering.SetDestinationLevelStart();
}
foreach (Door door in shuttleDoors)
{
@@ -284,7 +290,7 @@ namespace Barotrauma.Networking
if (!CoroutineManager.IsCoroutineRunning("forcepos"))
{
if (shuttleSteering.SteeringPath != null && shuttleSteering.SteeringPath.Finished
if ((shuttleSteering?.SteeringPath != null && shuttleSteering.SteeringPath.Finished)
|| (respawnShuttle.WorldPosition.Y + respawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f))
{
@@ -323,7 +329,10 @@ namespace Barotrauma.Networking
ResetShuttle();
shuttleSteering.TargetVelocity = Vector2.Zero;
if (shuttleSteering != null)
{
shuttleSteering.TargetVelocity = Vector2.Zero;
}
GameServer.Log("Dispatching the respawn shuttle.", ServerLog.MessageType.Spawning);

View File

@@ -139,7 +139,7 @@ namespace Barotrauma.Networking
}
}
string fileName = serverName + "_" + DateTime.Now.ToShortDateString() + "_" + DateTime.Now.ToShortTimeString() + ".txt";
string fileName = serverName + "_" + DateTime.Now.ToString("yyyy-MM-dd_HH:mm") + ".txt";
var invalidChars = Path.GetInvalidFileNameChars();
foreach (char invalidChar in invalidChars)

View File

@@ -111,11 +111,11 @@ namespace Barotrauma
#endif
break;
case VoteType.EndRound:
if (sender.Character == null) return;
if (!sender.HasSpawned) return;
sender.SetVote(voteType, inc.ReadBoolean());
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.Character != null && c.GetVote<bool>(VoteType.EndRound));
GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.Character != null);
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));
GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
break;
case VoteType.Kick:

View File

@@ -1,3 +1,64 @@
---------------------------------------------------------------------------------------------------------
v0.8.1.5
---------------------------------------------------------------------------------------------------------
- Added the option to automatically send crash reports and anonymous usage statistics to the developers.
The usage statistics include information such as the selected game mode, selected submarine, causes of
death and mission outcomes. When the game is started for the first time, a message box prompts you to
select whether you want to send the information or not.
- Fixed a bug that caused clients to get desynced when purchasing items in the multiplayer campaign.
- Added a signal component that adds the received signals together.
- Devices outside the submarine can be rewired in-game (not just in the sub editor).
- Fixed a crash caused by vision obstruction logic.
- Fixed clients being unable to give non-permanent or range bans.
- Clients are allowed to vote to end the round if they have spawned at some point during the round,
even if the character they controlled doesn't exist anymore.
- Dedicated servers can give clients the permission to use console commands that aren't available in
for dedicated server (e.g. los, lights, control)
- Banip, banid & kickid commands can be used by clients now (if they have the permission to do so).
- Spawnitem [item] inventory, ragdoll and kill commands target the character of the client using
the command instead of the host's character.
- Spawnitem can be used to spawn items in the inventory of a specific character.
- Fixed explosions with an EMP value only damaging reactors (when they should only ignore reactors).
- Fire can only explode oxygen tanks that are >25% full (otherwise the condition of the tank just drops
to 0). Prevents infinite explosions when an oxygen generator is on fire with oxygen tanks inside.
- Fixed projectiles with a damage range of 0 not applying their structuredamage value to structures.
- Items with a physics body can be used as pumps, so now it's possible to make portable items that remove
water from inside the sub.
- Fixed traitor ratio setting being ignored and the minimum number of traitors being 2.
- Fixed crashes caused by custom characters with no AI configuration.
- Character head and gender settings are saved.
---------------------------------------------------------------------------------------------------------
v0.8.1.4
---------------------------------------------------------------------------------------------------------
- Fixed clients getting assigned random jobs regardless of job preferences.
---------------------------------------------------------------------------------------------------------
v0.8.1.3
---------------------------------------------------------------------------------------------------------
- Fixed server-side crashes during job assignment if a client hasn't sent any job preferences.
- Fixed crashing if the selected respawn shuttle doesn't have a navigation terminal or any other item
with a Steering component.
- Fixed InWater status effects triggering when an item is fabricated, causing issues such as
water-sensitive items to breaking/exploding immediately after being fabricated.
- Fixed motion sensors sending out signals even if the output is set to nothing.
- Fixed crashing when a round starts if the sub has been saved while a fabricator was running.
- Fixed explosives not detonating inside railgun shells.
- Fixed characters spawning inside the respawn shuttle if no suitable spawnpoint is found inside the
main submarine.
- Fixed a timing bug in the dedicated server which prevented clients from ever timing out, AI characters
from fixing leaks and reactor usage from being logged.
- Changed the format of the server log filenames to make them more easily sortable.
- Increased assistant's skill levels.
- Added oxygenite tanks.
- Added a rearward facing variant of railgun controller.
- Added railgun loader variants with 1 shell capacity.
- Added railgun shell rack.
- Decreased the skill level requirements for fixing docking ports.
---------------------------------------------------------------------------------------------------------
v0.8.1.2
---------------------------------------------------------------------------------------------------------