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
@@ -29,6 +29,8 @@
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion> <TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<TargetFrameworkProfile /> <TargetFrameworkProfile />
<ReleaseVersion>0.7.0.1</ReleaseVersion> <ReleaseVersion>0.7.0.1</ReleaseVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
@@ -231,9 +233,15 @@
<Compile Include="Source\Utils\ToolBox.cs" /> <Compile Include="Source\Utils\ToolBox.cs" />
</ItemGroup> </ItemGroup>
<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"> <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> <HintPath>..\..\Libraries\NuGet\MonoGame.Framework.WindowsDX.3.6.0.1625\lib\net40\MonoGame.Framework.dll</HintPath>
</Reference> </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="PresentationCore" />
<Reference Condition="$(DefineConstants.Contains('WINDOWS'))" Include="SharpDX"> <Reference Condition="$(DefineConstants.Contains('WINDOWS'))" Include="SharpDX">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
@@ -247,9 +255,12 @@
<HintPath>..\..\Libraries\NuGet\OpenTK.2.0.0\lib\net20\OpenTK.dll</HintPath> <HintPath>..\..\Libraries\NuGet\OpenTK.2.0.0\lib\net20\OpenTK.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <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.Drawing" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.XML" /> <Reference Include="System.Xml" />
<Reference Include="System.Xml.Linq" /> <Reference Include="System.Xml.Linq" />
<Reference Include="RestSharp"> <Reference Include="RestSharp">
<HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> <HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
@@ -324,6 +335,13 @@
<ItemGroup /> <ItemGroup />
<Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" /> <Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.2")] [assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.2")] [assembly: AssemblyFileVersion("0.8.1.5")]
@@ -4,6 +4,7 @@ using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics; using Microsoft.Xna.Framework.Graphics;
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Xml.Linq; using System.Xml.Linq;
@@ -17,13 +18,17 @@ namespace Barotrauma
private set; 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) partial void InitProjSpecific(XElement element)
{ {
foreach (XElement subElement in element.Elements()) foreach (XElement subElement in element.Elements())
@@ -34,19 +39,97 @@ namespace Barotrauma
LightSource = new LightSource(subElement); LightSource = new LightSource(subElement);
break; break;
case "sound": case "sound":
hitSoundTag = subElement.GetAttributeString("tag", ""); HitSoundTag = subElement.GetAttributeString("tag", "");
if (string.IsNullOrWhiteSpace(hitSoundTag)) if (string.IsNullOrWhiteSpace(HitSoundTag))
{ {
//legacy support //legacy support
hitSoundTag = subElement.GetAttributeString("file", ""); HitSoundTag = subElement.GetAttributeString("file", "");
} }
break; 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) if (LightSource != null)
{ {
LightSource.ParentSub = body.Submarine; LightSource.ParentSub = body.Submarine;
@@ -73,10 +156,10 @@ namespace Barotrauma
body.Dir = Dir; body.Dir = Dir;
bool hideLimb = wearingItems.Any(w => w != null && w.HideLimb); bool hideLimb = WearingItems.Any(w => w != null && w.HideLimb);
if (!hideLimb) if (!hideLimb)
{ {
body.Draw(spriteBatch, sprite, color, null, scale); body.Draw(spriteBatch, sprite, color, null, Scale);
} }
else else
{ {
@@ -89,7 +172,7 @@ namespace Barotrauma
LightSource.LightSpriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipVertically; 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; SpriteEffects spriteEffect = (dir == Direction.Right) ? SpriteEffects.None : SpriteEffects.FlipHorizontally;
@@ -115,7 +198,7 @@ namespace Barotrauma
new Vector2(body.DrawPosition.X, -body.DrawPosition.Y), new Vector2(body.DrawPosition.X, -body.DrawPosition.Y),
color, origin, color, origin,
-body.DrawRotation, -body.DrawRotation,
scale, spriteEffect, depth); Scale, spriteEffect, depth);
} }
if (damage > 0.0f && damagedSprite != null && !hideLimb) if (damage > 0.0f && damagedSprite != null && !hideLimb)
@@ -394,7 +394,7 @@ namespace Barotrauma
NewMessage(GUI.DisableHUD ? "Disabled HUD" : "Enabled HUD", Color.White); 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; Camera.FollowSub = !Camera.FollowSub;
NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White); NewMessage(Camera.FollowSub ? "Set the camera to follow the closest submarine" : "Disabled submarine following.", Color.White);
@@ -6,10 +6,10 @@ namespace Barotrauma
{ {
public void ShowMessage(int index) 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 header = index < Headers.Count ? Headers[index] : "";
string message = index < messages.Count ? messages[index] : ""; string message = index < Messages.Count ? Messages[index] : "";
GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage); GameServer.Log(TextManager.Get("MissionInfo") + ": " + header + " - " + message, ServerLog.MessageType.ServerMessage);
+12 -3
View File
@@ -198,7 +198,8 @@ namespace Barotrauma
private static bool QuitClicked(GUIButton button, object obj) 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); SaveUtil.SaveGame(GameMain.GameSession.SavePath);
} }
@@ -211,10 +212,18 @@ namespace Barotrauma
CoroutineManager.StopCoroutines("EndCinematic"); 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(); GameMain.MainMenuScreen.Select();
//Game1.MainMenuScreen.SelectTab(null, (int)MainMenuScreen.Tabs.Main);
return true; return true;
} }
+31 -3
View File
@@ -9,6 +9,7 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Reflection; using System.Reflection;
using GameAnalyticsSDK.Net;
namespace Barotrauma namespace Barotrauma
{ {
@@ -144,7 +145,7 @@ namespace Barotrauma
Timing.Accumulator = 0.0f; Timing.Accumulator = 0.0f;
fixedTime = new GameTime(); fixedTime = new GameTime();
World = new World(new Vector2(0, -9.82f)); World = new World(new Vector2(0, -9.82f));
FarseerPhysics.Settings.AllowSleep = true; FarseerPhysics.Settings.AllowSleep = true;
FarseerPhysics.Settings.ContinuousPhysics = false; FarseerPhysics.Settings.ContinuousPhysics = false;
@@ -232,6 +233,31 @@ namespace Barotrauma
loadingCoroutine = CoroutineManager.StartCoroutine(Load()); 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() private IEnumerable<object> Load()
{ {
if (GameSettings.VerboseLogging) if (GameSettings.VerboseLogging)
@@ -241,6 +267,8 @@ namespace Barotrauma
GUI.GraphicsDevice = base.GraphicsDevice; GUI.GraphicsDevice = base.GraphicsDevice;
GUI.Init(Content); GUI.Init(Content);
InitUserStats();
GUIComponent.Init(Window); GUIComponent.Init(Window);
DebugConsole.Init(Window); DebugConsole.Init(Window);
DebugConsole.Log(SelectedPackage == null ? "No content package selected" : "Content package \"" + SelectedPackage.Name + "\" selected"); DebugConsole.Log(SelectedPackage == null ? "No content package selected" : "Content package \"" + SelectedPackage.Name + "\" selected");
@@ -256,7 +284,7 @@ namespace Barotrauma
TitleScreen.LoadState = 2.0f; TitleScreen.LoadState = 2.0f;
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
Mission.Init(); MissionPrefab.Init();
MapEntityPrefab.Init(); MapEntityPrefab.Init();
LevelGenerationParams.LoadPresets(); LevelGenerationParams.LoadPresets();
TitleScreen.LoadState = 10.0f; TitleScreen.LoadState = 10.0f;
@@ -472,7 +500,7 @@ namespace Barotrauma
protected override void OnExiting(object sender, EventArgs args) protected override void OnExiting(object sender, EventArgs args)
{ {
if (NetworkMember != null) NetworkMember.Disconnect(); if (NetworkMember != null) NetworkMember.Disconnect();
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
base.OnExiting(sender, args); base.OnExiting(sender, args);
} }
} }
@@ -281,7 +281,7 @@ namespace Barotrauma
listBox.ClearChildren(); listBox.ClearChildren();
characters.Clear(); 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++) for (int i = 0; i < waypoints.Length; i++)
{ {
@@ -238,6 +238,7 @@ namespace Barotrauma.Lights
public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition) public void UpdateObstructVision(GraphicsDevice graphics, SpriteBatch spriteBatch, Camera cam, Vector2 lookAtPosition)
{ {
if (!LosEnabled && !ObstructVision) return; if (!LosEnabled && !ObstructVision) return;
if (ViewTarget == null) return;
graphics.SetRenderTarget(losTexture); graphics.SetRenderTarget(losTexture);
@@ -691,6 +691,7 @@ namespace Barotrauma.Networking
string shuttleHash = inc.ReadString(); string shuttleHash = inc.ReadString();
string modeName = inc.ReadString(); string modeName = inc.ReadString();
int missionIndex = inc.ReadInt16();
bool respawnAllowed = inc.ReadBoolean(); bool respawnAllowed = inc.ReadBoolean();
bool loadSecondSub = inc.ReadBoolean(); bool loadSecondSub = inc.ReadBoolean();
@@ -750,7 +751,7 @@ namespace Barotrauma.Networking
if (campaign == null) 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); GameMain.GameSession.StartRound(levelSeed, loadSecondSub);
} }
else else
@@ -821,7 +822,7 @@ namespace Barotrauma.Networking
} }
else 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(); bool allowSpectating = inc.ReadBoolean();
YesNoMaybe traitorsEnabled = (YesNoMaybe)inc.ReadRangedInteger(0, 2); 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(); int modeIndex = inc.ReadByte();
string levelSeed = inc.ReadString(); string levelSeed = inc.ReadString();
@@ -1440,6 +1441,8 @@ namespace Barotrauma.Networking
msg.Write((byte)ClientPermissions.Ban); msg.Write((byte)ClientPermissions.Ban);
msg.Write(kickedName); msg.Write(kickedName);
msg.Write(reason); msg.Write(reason);
msg.Write(range);
msg.Write(duration.HasValue ? duration.Value.TotalSeconds : 0.0); //0 = permaban
client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered); client.SendMessage(msg, NetDeliveryMethod.ReliableUnordered);
} }
+15 -4
View File
@@ -3,6 +3,7 @@
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
using GameAnalyticsSDK.Net;
#if WINDOWS #if WINDOWS
using System.Windows.Forms; using System.Windows.Forms;
@@ -135,7 +136,6 @@ namespace Barotrauma
sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")"); sb.AppendLine("Barotrauma Client crash report (generated on " + DateTime.Now + ")");
sb.AppendLine("\n"); sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! "); 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("\n");
#if DEBUG #if DEBUG
sb.AppendLine("Game version " + GameMain.Version + " (debug build)"); sb.AppendLine("Game version " + GameMain.Version + " (debug build)");
@@ -192,14 +192,25 @@ namespace Barotrauma
{ {
sb.AppendLine("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text); sb.AppendLine("[" + DebugConsole.Messages[i].Time + "] " + DebugConsole.Messages[i].Text);
} }
string crashReport = sb.ToString();
sw.WriteLine(sb.ToString()); sw.WriteLine(crashReport);
sw.Close(); sw.Close();
if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs(); if (GameSettings.SaveDebugConsoleLogs) DebugConsole.SaveLogs();
CrashMessageBox( "A crash report (\"crashreport.log\") was saved in the root folder of the game."+ if (GameSettings.SendUserStatistics)
" 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/"); {
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 #endif
@@ -87,6 +87,8 @@ namespace Barotrauma
campaignUI.StartRound = StartRound; campaignUI.StartRound = StartRound;
campaignUI.OnLocationSelected = SelectLocation; campaignUI.OnLocationSelected = SelectLocation;
campaignUI.UpdateCharacterLists(); campaignUI.UpdateCharacterLists();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("singleplayer");
} }
public override void AddToGUIUpdateList() public override void AddToGUIUpdateList()
@@ -142,6 +142,8 @@ namespace Barotrauma
campaignSetupUI.UpdateSubList(); campaignSetupUI.UpdateSubList();
SelectTab(null, 0); SelectTab(null, 0);
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("");
} }
public bool SelectTab(GUIButton button, object obj) public bool SelectTab(GUIButton button, object obj)
@@ -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); if (GameModePreset.list.Count > 0 && modeList.Selected == null) modeList.Select(0);
GameMain.Server.Voting.ResetVotes(GameMain.Server.ConnectedClients); 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); var playYourself = new GUITickBox(new Rectangle(0, 0, 20, 20), TextManager.Get("PlayYourself"), Alignment.TopLeft, myPlayerFrame);
playYourself.Selected = GameMain.NetworkMember.CharacterInfo != null; playYourself.Selected = GameMain.NetworkMember.CharacterInfo != null;
playYourself.OnSelected = TogglePlayYourself; playYourself.OnSelected = TogglePlayYourself;
playYourself.UserData = "playyourself"; playYourself.UserData = "playyourself";
GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame); GUIButton toggleHead = new GUIButton(new Rectangle(0, 50, 15, 15), "<", "", myPlayerFrame)
toggleHead.UserData = -1; {
toggleHead.OnClicked = ToggleHead; UserData = -1,
toggleHead = new GUIButton(new Rectangle(60, 50, 15, 15), ">", "", myPlayerFrame); OnClicked = ToggleHead
toggleHead.UserData = 1; };
toggleHead.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); new GUITextBlock(new Rectangle(100, 30, 200, 30), TextManager.Get("Gender"), "", myPlayerFrame);
@@ -629,7 +635,10 @@ namespace Barotrauma
{ {
if (tickBox.Selected) 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); UpdatePlayerFrame(GameMain.NetworkMember.CharacterInfo);
} }
else else
@@ -688,9 +697,9 @@ namespace Barotrauma
public void SetMissionType(int missionTypeIndex) 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; missionTypeBlock.UserData = missionTypeIndex;
} }
@@ -701,8 +710,8 @@ namespace Barotrauma
int missionTypeIndex = (int)missionTypeBlock.UserData; int missionTypeIndex = (int)missionTypeBlock.UserData;
missionTypeIndex += (int)userData; missionTypeIndex += (int)userData;
if (missionTypeIndex < 0) missionTypeIndex = Mission.MissionTypes.Count - 1; if (missionTypeIndex < 0) missionTypeIndex = MissionPrefab.MissionTypes.Count - 1;
if (missionTypeIndex >= Mission.MissionTypes.Count) missionTypeIndex = 0; if (missionTypeIndex >= MissionPrefab.MissionTypes.Count) missionTypeIndex = 0;
SetMissionType(missionTypeIndex); SetMissionType(missionTypeIndex);
@@ -1247,14 +1256,12 @@ namespace Barotrauma
private bool ToggleHead(GUIButton button, object userData) private bool ToggleHead(GUIButton button, object userData)
{ {
int dir = (int)userData;
if (GameMain.NetworkMember.CharacterInfo == null) return true; if (GameMain.NetworkMember.CharacterInfo == null) return true;
int dir = (int)userData;
GameMain.NetworkMember.CharacterInfo.HeadSpriteId += dir; GameMain.NetworkMember.CharacterInfo.HeadSpriteId += dir;
GameMain.Config.CharacterHeadIndex = GameMain.NetworkMember.CharacterInfo.HeadSpriteId;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo); UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
return true; return true;
} }
@@ -1262,7 +1269,7 @@ namespace Barotrauma
{ {
Gender gender = (Gender)obj; Gender gender = (Gender)obj;
GameMain.NetworkMember.CharacterInfo.Gender = gender; GameMain.NetworkMember.CharacterInfo.Gender = gender;
GameMain.Config.CharacterGender = GameMain.NetworkMember.CharacterInfo.Gender;
UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo); UpdatePlayerHead(GameMain.NetworkMember.CharacterInfo);
return true; return true;
} }
@@ -348,6 +348,8 @@ namespace Barotrauma
} }
cam.UpdateTransform(); cam.UpdateTransform();
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.SetCustomDimension01("editor");
} }
public override void Deselect() public override void Deselect()
@@ -62,6 +62,7 @@ namespace Barotrauma
if (File.Exists(file)) if (File.Exists(file))
{ {
ToolBox.IsProperFilenameCase(file);
return TextureLoader.FromFile(file); return TextureLoader.FromFile(file);
} }
else else
@@ -1,7 +1,9 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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.DesktopGL" version="3.6.0.1625" targetFramework="net45" />
<package id="MonoGame.Framework.WindowsDX" 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="NVorbis" version="0.8.5.0" targetFramework="net45" />
<package id="OpenTK" version="2.0.0" targetFramework="net45" /> <package id="OpenTK" version="2.0.0" targetFramework="net45" />
<package id="RestSharp" version="105.2.3" targetFramework="net45" /> <package id="RestSharp" version="105.2.3" targetFramework="net45" />
@@ -28,6 +28,8 @@
<UseApplicationTrust>false</UseApplicationTrust> <UseApplicationTrust>false</UseApplicationTrust>
<BootstrapperEnabled>true</BootstrapperEnabled> <BootstrapperEnabled>true</BootstrapperEnabled>
<ReleaseVersion>0.7.0.1</ReleaseVersion> <ReleaseVersion>0.7.0.1</ReleaseVersion>
<NuGetPackageImportStamp>
</NuGetPackageImportStamp>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon> <ApplicationIcon>..\BarotraumaShared\Icon.ico</ApplicationIcon>
@@ -69,12 +71,21 @@
<PlatformTarget>x86</PlatformTarget> <PlatformTarget>x86</PlatformTarget>
</PropertyGroup> </PropertyGroup>
<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 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"> <Reference Include="RestSharp, Version=105.2.3.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion> <SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath> <HintPath>..\..\Libraries\NuGet\RestSharp.105.2.3\lib\net45\RestSharp.dll</HintPath>
</Reference> </Reference>
<Reference Include="System" /> <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" /> <Reference Include="System.Xml.Linq" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
@@ -142,6 +153,13 @@
<ItemGroup /> <ItemGroup />
<Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" /> <Import Project="..\BarotraumaShared\BarotraumaShared.projitems" Label="Shared" />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">
@@ -31,5 +31,5 @@ using System.Runtime.InteropServices;
// You can specify all the values or you can default the Build and Revision Numbers // You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below: // by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")] // [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("0.8.1.2")] [assembly: AssemblyVersion("0.8.1.5")]
[assembly: AssemblyFileVersion("0.8.1.2")] [assembly: AssemblyFileVersion("0.8.1.5")]
@@ -14,7 +14,7 @@ namespace Barotrauma
{ {
lock (QueuedCommands) lock (QueuedCommands)
{ {
while (QueuedCommands.Count>0) while (QueuedCommands.Count > 0)
{ {
ExecuteCommand(QueuedCommands[0]); ExecuteCommand(QueuedCommands[0]);
QueuedCommands.RemoveAt(0); QueuedCommands.RemoveAt(0);
@@ -127,7 +127,7 @@ namespace Barotrauma
{ {
return new string[][] return new string[][]
{ {
Mission.MissionTypes.ToArray() MissionPrefab.MissionTypes.ToArray()
}; };
})); }));
@@ -190,6 +190,16 @@ namespace Barotrauma
NewMessage(ent.ToString(), Color.Lime); 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 #if DEBUG
commands.Add(new Command("eventdata", "", (string[] args) => commands.Add(new Command("eventdata", "", (string[] args) =>
{ {
+16 -7
View File
@@ -1,5 +1,6 @@
using Barotrauma.Networking; using Barotrauma.Networking;
using FarseerPhysics.Dynamics; using FarseerPhysics.Dynamics;
using GameAnalyticsSDK.Net;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@@ -72,12 +73,17 @@ namespace Barotrauma
Config.Save("config.xml"); Config.Save("config.xml");
} }
if (GameSettings.SendUserStatistics)
{
GameAnalyticsManager.Init();
}
GameScreen = new GameScreen(); GameScreen = new GameScreen();
} }
public void Init() public void Init()
{ {
Mission.Init(); MissionPrefab.Init();
MapEntityPrefab.Init(); MapEntityPrefab.Init();
LevelGenerationParams.LoadPresets(); LevelGenerationParams.LoadPresets();
@@ -118,6 +124,7 @@ namespace Barotrauma
public void CloseServer() public void CloseServer()
{ {
if (GameSettings.SendUserStatistics) GameAnalytics.OnStop();
Server.Disconnect(); Server.Disconnect();
Server = null; 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); DebugConsole.NewMessage("WARNING: Stopwatch frequency under 1500 ticks per second. Expect significant syncing accuracy issues.", Color.Yellow);
} }
Stopwatch stopwatch = Stopwatch.StartNew(); Stopwatch stopwatch = Stopwatch.StartNew();
long prevTicks = stopwatch.ElapsedTicks; long prevTicks = stopwatch.ElapsedTicks;
while (ShouldRun) while (ShouldRun)
{ {
long currTicks = stopwatch.ElapsedTicks; long currTicks = stopwatch.ElapsedTicks;
Timing.Accumulator += (double)(currTicks - prevTicks) / frequency; double elapsedTime = (currTicks - prevTicks) / frequency;
Timing.Accumulator += elapsedTime;
Timing.TotalTime += elapsedTime;
prevTicks = currTicks; prevTicks = currTicks;
while (Timing.Accumulator>=Timing.Step) while (Timing.Accumulator >= Timing.Step)
{ {
DebugConsole.Update(); DebugConsole.Update();
if (Screen.Selected != null) Screen.Selected.Update((float)Timing.Step); if (Screen.Selected != null) Screen.Selected.Update((float)Timing.Step);
Server.Update((float)Timing.Step); Server.Update((float)Timing.Step);
CoroutineManager.Update((float)Timing.Step, (float)Timing.Step); CoroutineManager.Update((float)Timing.Step, (float)Timing.Step);
Timing.Accumulator -= Timing.Step; Timing.Accumulator -= Timing.Step;
} }
int frameTime = (int)(((double)(stopwatch.ElapsedTicks - prevTicks) / frequency)*1000.0); int frameTime = (int)(((double)(stopwatch.ElapsedTicks - prevTicks) / frequency) * 1000.0);
Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime)/2,0)); Thread.Sleep(Math.Max(((int)(Timing.Step * 1000.0) - frameTime) / 2, 0));
} }
stopwatch.Stop(); stopwatch.Stop();
+16 -3
View File
@@ -1,5 +1,6 @@
#region Using Statements #region Using Statements
using GameAnalyticsSDK.Net;
using System; using System;
using System.IO; using System.IO;
using System.Text; using System.Text;
@@ -46,7 +47,6 @@ namespace Barotrauma
sb.AppendLine("Barotrauma Dedicated Server crash report (generated on " + DateTime.Now + ")"); sb.AppendLine("Barotrauma Dedicated Server crash report (generated on " + DateTime.Now + ")");
sb.AppendLine("\n"); sb.AppendLine("\n");
sb.AppendLine("Barotrauma seems to have crashed. Sorry for the inconvenience! "); 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("\n");
sb.AppendLine("Game version " + GameMain.Version); sb.AppendLine("Game version " + GameMain.Version);
sb.AppendLine("Selected content package: " + GameMain.SelectedPackage.Name); sb.AppendLine("Selected content package: " + GameMain.SelectedPackage.Name);
@@ -75,11 +75,24 @@ namespace Barotrauma
sb.AppendLine(" "+DebugConsole.Messages[i].Time+" - "+DebugConsole.Messages[i].Text); sb.AppendLine(" "+DebugConsole.Messages[i].Time+" - "+DebugConsole.Messages[i].Text);
} }
string crashReport = sb.ToString();
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.Write(sb.ToString()); Console.Write(crashReport);
sw.WriteLine(sb.ToString()); 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/");
}
} }
} }
} }
@@ -75,18 +75,18 @@ namespace Barotrauma
set set
{ {
lastUpdateID++; 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 public string MissionTypeName
{ {
get { return Mission.MissionTypes[MissionTypeIndex]; } get { return MissionPrefab.MissionTypes[MissionTypeIndex]; }
set 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; MissionTypeIndex = i;
break; break;
@@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<packages> <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" /> <package id="RestSharp" version="105.2.3" targetFramework="net45" />
</packages> </packages>
@@ -232,6 +232,9 @@
<Content Include="$(MSBuildThisFileDirectory)Content\Items\SearchLight\SearchLightBase.png"> <Content Include="$(MSBuildThisFileDirectory)Content\Items\SearchLight\SearchLightBase.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Items\Weapons\railgunetc2.png">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
<Content Include="$(MSBuildThisFileDirectory)Content\Texts.xml"> <Content Include="$(MSBuildThisFileDirectory)Content\Texts.xml">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content> </Content>
@@ -1471,6 +1474,9 @@
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterInfo.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterNetworking.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\CharacterNetworking.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\DamageModifier.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\StatusEffects\DelayedEffect.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\HuskInfection.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\HuskInfection.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Jobs\Job.cs" /> <Compile Include="$(MSBuildThisFileDirectory)Source\Characters\Jobs\Job.cs" />
@@ -170,7 +170,7 @@
<StatusEffect type="OnUse" target="This" Condition="-1.0"/> <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"> <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"/> <sprite texture="Content/Items/Electricity/lightsprite.png" origin="0.5,0.5"/>
@@ -211,7 +211,7 @@
<Door canbepicked="false" canbeselected="true" horizontal="true"> <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"/> <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> </Door>
<ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]">
@@ -233,7 +233,7 @@
<Door canbepicked="false" canbeselected="true"> <Door canbepicked="false" canbeselected="true">
<Sprite texture="Content/Map/ruins.png" sourcerect="842,192,54,259" depth="0.6" origin="0.5,0.0"/> <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> </Door>
<ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" msg="Rewire [Screwdriver]">
@@ -288,4 +288,4 @@
<output name="power_out"/> <output name="power_out"/>
</ConnectionPanel> </ConnectionPanel>
</Item> </Item>
</Items> </Items>
@@ -18,7 +18,7 @@
<Controller direction ="None" canbepicked = "true" msg="Press [E]"> <Controller direction ="None" canbepicked = "true" msg="Press [E]">
<RequiredItem name="ID Card" type="Picked" msg="UNAUTHORIZED ACCESS"/> <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> </Controller>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -50,7 +50,7 @@
<Body width="12" height="16" density="20"/> <Body width="12" height="16" density="20"/>
<Controller direction="None" canbepicked="true" msg="Press [E]"> <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> </Controller>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -63,4 +63,4 @@
<requireditem name="Wrench" type="Equipped"/> <requireditem name="Wrench" type="Equipped"/>
</Holdable> </Holdable>
</Item> </Item>
</Items> </Items>
@@ -11,6 +11,18 @@
</ItemContainer> </ItemContainer>
</Item> </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 <Item
name="Medicine Cabinet" name="Medicine Cabinet"
linkable="true" linkable="true"
@@ -17,9 +17,11 @@
<Pickable holdpos="30,-15" handle1="0,1" slots="RightHand,LeftHand,Any"> <Pickable holdpos="30,-15" handle1="0,1" slots="RightHand,LeftHand,Any">
<StatusEffect type="OnFire" target="This" Condition="-100.0" disabledeltatime="true"> <StatusEffect type="OnFire" target="This" Condition="-100.0" disabledeltatime="true">
<Conditional Condition="gt 25"/>
<sound file="Content/Items/Reactor/explosion.ogg"/> <sound file="Content/Items/Reactor/explosion.ogg"/>
<Explosion range="250.0" structuredamage="10" damage="20" stun="5" force="3.0"/> <Explosion range="250.0" structuredamage="10" damage="20" stun="5" force="3.0"/>
</StatusEffect> </StatusEffect>
<StatusEffect type="OnFire" target="This" Condition="-100.0" disabledeltatime="true"/>
</Pickable> </Pickable>
</Item> </Item>
@@ -188,7 +190,7 @@
<Propulsion force="80" usablein="water" particles="bubbles"> <Propulsion force="80" usablein="water" particles="bubbles">
<RequiredItems name="Battery Cell,Fulgurium Battery Cell" type="Contained" msg="Battery Cell required"/> <RequiredItems name="Battery Cell,Fulgurium Battery Cell" type="Contained" msg="Battery Cell required"/>
<StatusEffect type="OnUse" target="Contained" Condition="-1.0"/> <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"> <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"/> <LightTexture texture="Content/Lights/lightcone.png" origin="0.05, 0.5" size="2.0,1.0"/>
</LightComponent> </LightComponent>
@@ -11,7 +11,7 @@
<Sprite texture ="door.png" sourcerect="80,0,19,208" depth="0.05" origin="0.5,0.0"/> <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"/> <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"/> <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"/> <sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/> <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"/> <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"/> <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"/> <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"/> <sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak1.ogg" range="3000"/> <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"/> <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"/> <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"/> <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"/> <sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="2000.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak1.ogg" range="3000"/> <sound file="Content/Items/Door/doorBreak1.ogg" range="3000"/>
@@ -110,18 +110,24 @@
<DockingPort IsHorizontal="true" DistanceTolerance="128,64" DockedDistance="64"> <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"/> <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="Content/Items/Door/dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/> <sound file="Content/Items/Door/dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/> <sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
</StatusEffect> </StatusEffect>
</DockingPort> </DockingPort>
<fixrequirement name="Electrical repairs">
<skill name="Electrical Engineering" level="40"/>
<item name="Wire"/>
<item name="Screwdriver"/>
</fixrequirement>
<PowerTransfer/> <PowerTransfer/>
<Wire/> <Wire/>
<fixrequirement name="Mechanical repairs"> <fixrequirement name="Mechanical repairs">
<skill name="Construction" level="60"/> <skill name="Construction" level="50"/>
<item name="Welding Tool"/> <item name="Welding Tool"/>
</fixrequirement> </fixrequirement>
@@ -144,8 +150,8 @@
<DockingPort IsHorizontal="false" DistanceTolerance="64,128" DockedDistance="64"> <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"/> <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="Content/Items/Door/dockingport1.ogg" type="OnUse" range="1000.0"/>
<sound file="dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/> <sound file="Content/Items/Door/dockingport2.ogg" type="OnSecondaryUse" range="1000.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/> <sound file="Content/Items/Door/doorBreak2.ogg" range="3000"/>
</StatusEffect> </StatusEffect>
@@ -161,7 +167,7 @@
<Wire/> <Wire/>
<fixrequirement name="Mechanical repairs"> <fixrequirement name="Mechanical repairs">
<skill name="Construction" level="60"/> <skill name="Construction" level="50"/>
<item name="Welding Tool"/> <item name="Welding Tool"/>
</fixrequirement> </fixrequirement>
@@ -187,7 +193,7 @@
<Sprite texture ="duct.png" sourcerect="66,0,19,19" depth="0.05" origin="-0.4,0.5"/> <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"/> <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"/> <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"/> <sound file="Content/Items/Tools/crowbar.ogg" type="OnPicked" range="500.0"/>
<StatusEffect type="OnBroken" target="This"> <StatusEffect type="OnBroken" target="This">
<sound file="Content/Items/Door/ductBreak.ogg" range="2000"/> <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

@@ -146,6 +146,39 @@
</ConnectionPanel> </ConnectionPanel>
</Item> </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 <Item
name="Or Component" name="Or Component"
category="Electrical" category="Electrical"
@@ -20,7 +20,7 @@
<Engine minvoltage="0.5" powerconsumption="2000.0" maxforce="2000" canbeselected = "true"> <Engine minvoltage="0.5" powerconsumption="2000.0" maxforce="2000" canbeselected = "true">
<StatusEffect type="InWater" target="This" condition="-2.0"/> <StatusEffect type="InWater" target="This" condition="-2.0"/>
<GuiFrame rect="0,0,350,160" alignment="Center" style="ItemUI"/> <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> </Engine>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -48,7 +48,7 @@
<Engine minvoltage="0.5" powerconsumption="500.0" maxforce="300" canbeselected = "true"> <Engine minvoltage="0.5" powerconsumption="500.0" maxforce="300" canbeselected = "true">
<GuiFrame rect="0,0,350,160" alignment="Center" style="ItemUI"/> <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> </Engine>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -78,7 +78,7 @@
</Steering> </Steering>
<Radar canbeselected = "true" powerconsumption="100" displaybordersize="0.2"> <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"/> <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"/> <PingCircle texture="Content/Items/Engine/pingCircle.png" origin="0.5,0.5"/>
<ScreenOverlay texture="Content/Items/Engine/radarOverlay.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"/> <input name="power_in"/>
</ConnectionPanel> </ConnectionPanel>
</Item> </Item>
</Items> </Items>
@@ -13,7 +13,7 @@
<GuiFrame rect="0,0,600,400" alignment="Center" style="ItemUI"/> <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"> <fabricableitem name="Harpoon Gun" requireditems="Steel Bar, Polycarbonate Bar, Aluminium" requiredtime="20">
<RequiredSkill name="Construction" level="30"/> <RequiredSkill name="Construction" level="30"/>
@@ -135,7 +135,7 @@
<GuiFrame rect="0,0,600,400" alignment="Center" style="ItemUI"/> <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="Medical Syringe" requireditems="Steel Bar" requiredtime="10"/>
<fabricableitem name="Chloral Hydrate" requireditems="Chlorine, Ethanol" requiredtime="20"> <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"/> <Sprite texture="Content/Items/machines.png" depth="0.8" sourcerect="64,128,64,128"/>
<Deconstructor canbeselected = "true" powerconsumption="500.0"> <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"/> <GuiFrame rect="0,0,300,200" alignment="Center" style="ItemUI"/>
</Deconstructor> </Deconstructor>
@@ -229,4 +229,4 @@
<ItemContainer capacity="5" canbeselected="true" hideitems="true" hudpos="0.5, 0.4" slotsperrow="5"/> <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"/> <ItemContainer capacity="10" canbeselected="true" hideitems="true" hudpos="0.5, 0.8" slotsperrow="5"/>
</Item> </Item>
</Items> </Items>
@@ -7,8 +7,8 @@
<Sprite texture="oxygengenerator.png" depth="0.8" sourcerect="0,0,128,128"/> <Sprite texture="oxygengenerator.png" depth="0.8" sourcerect="0,0,128,128"/>
<OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true"> <OxygenGenerator powerconsumption="1000.0" minvoltage="0.5" canbeselected = "true">
<StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="0.5"/> <StatusEffect type="OnActive" target="Contained" targetnames="Oxygen Tank" Condition="2.0"/>
<sound file="oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/> <sound file="Content/Items/OxygenGenerator/oxygengenerator.ogg" type="OnActive" range="1000.0" volume="CurrFlow" volumemultiplier="0.001f" loop="true"/>
</OxygenGenerator> </OxygenGenerator>
<trigger/> <trigger/>
@@ -31,8 +31,8 @@
<Sprite texture="vent.png" depth="0.91" sourcerect="0,0,64,64"/> <Sprite texture="vent.png" depth="0.91" sourcerect="0,0,64,64"/>
<Vent> <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> </Vent>
</Item> </Item>
</Items> </Items>
@@ -8,7 +8,7 @@
<Pump canbeselected = "true" maxflow="500" PowerConsumption="300.0" MinVoltage="0.3"> <Pump canbeselected = "true" maxflow="500" PowerConsumption="300.0" MinVoltage="0.3">
<GuiFrame rect="0,0,330,145" alignment="Center" style="ItemUI"/> <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> </Pump>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -30,7 +30,7 @@
<Pump canbeselected = "true" maxflow="100" PowerConsumption="60.0" MinVoltage="0.3"> <Pump canbeselected = "true" maxflow="100" PowerConsumption="60.0" MinVoltage="0.3">
<GuiFrame rect="0,0,330,145" alignment="Center" style="ItemUI"/> <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> </Pump>
<ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]"> <ConnectionPanel selectkey="Action" canbeselected = "true" msg="Rewire [Screwdriver]">
@@ -42,4 +42,4 @@
<input name="set_targetlevel"/> <input name="set_targetlevel"/>
</ConnectionPanel> </ConnectionPanel>
</Item> </Item>
</Items> </Items>
@@ -26,7 +26,7 @@
<RequiredSkill name="Construction" level="30"/> <RequiredSkill name="Construction" level="30"/>
<StatusEffect type="InWater" target="This" Temperature="-500.0"/> <StatusEffect type="InWater" target="This" Temperature="-500.0"/>
<StatusEffect type="OnActive" target="Contained" targetnames="Fuel Rod, Heat Absorber, Temperature Control Circuit" Condition="-0.1" /> <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"> <StatusEffect type="OnBroken" target="This" FissionRate="0.0" disabledeltatime="true">
<Sound file="Content/Items/Reactor/explosion.ogg" range="5000"/> <Sound file="Content/Items/Reactor/explosion.ogg" range="5000"/>
@@ -10,7 +10,7 @@
<Sprite texture ="tools.png" sourcerect="0,17,41,17" depth="0.55"/> <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"> <Deconstruct time="10">
<Item name="Polycarbonate Bar"/> <Item name="Polycarbonate Bar"/>
<Item name="Steel Bar"/> <Item name="Steel Bar"/>
@@ -35,7 +35,7 @@
<ParticleEmitterHitItem particle="weldsmoke" particlespersecond="3" anglemin="-5" anglemax="5" velocitymin="10" velocitymax="100"/> <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"/> <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) --> <!-- when using, the contained welding fuel tank will detoriate (= lose fuel) -->
<StatusEffect type="OnUse" target="Contained" targetnames="Welding Fuel Tank" Condition="-1.0"/> <StatusEffect type="OnUse" target="Contained" targetnames="Welding Fuel Tank" Condition="-1.0"/>
@@ -48,7 +48,7 @@
<!-- construction skill 20 required to use the item --> <!-- construction skill 20 required to use the item -->
<RequiredSkill name="Construction" level="20"/> <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"/> <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"> <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"/> <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"/> <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"/> <Fixable name="structure"/>
@@ -164,7 +164,7 @@
<RepairTool extinquishamount="60.0" range="500" barrelpos="21,25"> <RepairTool extinquishamount="60.0" range="500" barrelpos="21,25">
<ParticleEmitter particle="extinguisher" velocitymin="500.0" velocitymax="650.0" particlespersecond="50"/> <ParticleEmitter particle="extinguisher" velocitymin="500.0" velocitymax="650.0" particlespersecond="50"/>
<StatusEffect type="OnUse" target="This" Condition="-2.0"/> <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> </RepairTool>
<Propulsion force="-20" usablein="water" particles="bubbles"/> <Propulsion force="-20" usablein="water" particles="bubbles"/>
@@ -330,7 +330,7 @@
<StatusEffect type="OnBroken" target="This" IsOn="false"/> <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> </LightComponent>
</Item> </Item>
@@ -35,8 +35,7 @@
category="Machine" category="Machine"
type="Controller" type="Controller"
linkable="true" linkable="true"
disableitemusagewhenselected="true" disableitemusagewhenselected="true">
>
<Sprite texture ="railgunetc.png" depth="0.8" sourcerect="182,0,61,97"/> <Sprite texture ="railgunetc.png" depth="0.8" sourcerect="182,0,61,97"/>
@@ -55,11 +54,34 @@
</ConnectionPanel> </ConnectionPanel>
</Item> </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 <Item
name="Railgun Loader" name="Railgun Loader"
category="Machine" category="Machine"
linkable="true" linkable="true">
>
<Sprite texture ="railgunetc.png" depth="0.8" sourcerect="0,0,177,128"/> <Sprite texture ="railgunetc.png" depth="0.8" sourcerect="0,0,177,128"/>
@@ -69,6 +91,48 @@
<Containable name="Ancient Weapon"/> <Containable name="Ancient Weapon"/>
</ItemContainer> </ItemContainer>
</Item> </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 <Item
name="Railgun Shell" name="Railgun Shell"
@@ -86,7 +150,7 @@
<Body width="87" height="18" density="30"/> <Body width="87" height="18" density="30"/>
<Holdable slots="RightHand+LeftHand" holdpos="0,-50" handle1="-10,0" handle2="10,0" aimable="false"/> <Holdable slots="RightHand+LeftHand" holdpos="0,-50" handle1="-10,0" handle2="10,0" aimable="false"/>
<Projectile characterusable="false" launchimpulse="80.0"> <Projectile characterusable="false" launchimpulse="80.0">
<Attack damage="100" bleedingdamage="10" structuredamage="200" damagetype="Blunt" severlimbsprobability="1.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"/> <ParticleEmitter particle="bubbles" anglemin="0" anglemax="360" particleamount="10" velocitymin="0" velocitymax="50" scalemin="2" scalemax="5"/>
</StatusEffect> </StatusEffect>
<StatusEffect type="OnImpact" target="Contained" Condition="-100.0"/> <StatusEffect type="OnImpact" target="Contained">
<Use/>
</StatusEffect>
</Projectile> </Projectile>
<ItemContainer hideitems="true" capacity="1" canbeselected = "true"> <ItemContainer hideitems="true" capacity="1" canbeselected = "true">
Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

@@ -43,8 +43,8 @@
holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/> holdpos="35,-10" aimpos="35,-10" handle1="-15,-6" handle2="26,7"/>
<RangedWeapon barrelpos="49,10" spread="1" unskilledspread="10"> <RangedWeapon barrelpos="49,10" spread="1" unskilledspread="10">
<Sound file="harpoon1.ogg" type="OnUse"/> <Sound file="Content/Items/Weapons/harpoon1.ogg" type="OnUse"/>
<Sound file="harpoon2.ogg" type="OnUse"/> <Sound file="Content/Items/Weapons/harpoon2.ogg" type="OnUse"/>
<StatusEffect type="OnUse"> <StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" flash="false" sparks="false" camerashake="5.0"/> <Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" flash="false" sparks="false" camerashake="5.0"/>
</StatusEffect> </StatusEffect>
@@ -78,7 +78,7 @@
aimpos="90,10" handle1="-10,-7"/> aimpos="90,10" handle1="-10,-7"/>
<RangedWeapon barrelpos="30,20" spread="0" unskilledspread="10"> <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"/> <RequiredItems name="Medical Syringe" type="Contained" msg="Please load a Syringe"/>
<RequiredSkill name="Weapons" level="15"/> <RequiredSkill name="Weapons" level="15"/>
<RequiredSkill name="Medical" level="30"/> <RequiredSkill name="Medical" level="30"/>
@@ -128,7 +128,7 @@
aimpos="90,10" handle1="-11,-7"/> aimpos="90,10" handle1="-11,-7"/>
<RangedWeapon barrelpos="14,6" spread="0" unskilledspread="2"> <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"> <StatusEffect type="OnUse">
<Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/> <Explosion range="150.0" force="5" shockwave="false" smoke="false" flames="false" sparks="false" camerashake="5.0"/>
</StatusEffect> </StatusEffect>
@@ -300,7 +300,7 @@
<Holdable slots="Any,RightHand,LeftHand" aimpos="100,0" handle1="-5,0"/> <Holdable slots="Any,RightHand,LeftHand" aimpos="100,0" handle1="-5,0"/>
<RangedWeapon reload="2"> <RangedWeapon reload="2">
<Sound file="honk.ogg" type="OnUse"/> <Sound file="Content/Items/Weapons/honk.ogg" type="OnUse"/>
</RangedWeapon> </RangedWeapon>
</Item> </Item>
+5 -5
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"> <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> <Skills>
<Skill name="Weapons" level="20,30"/> <Skill name="Weapons" level="20,32"/>
<Skill name="Construction" level="20,30"/> <Skill name="Construction" level="20,32"/>
<Skill name="Electrical Engineering" level="20,30"/> <Skill name="Electrical Engineering" level="20,32"/>
<Skill name="Medical" level="10,20"/> <Skill name="Medical" level="10,22"/>
</Skills> </Skills>
<Items> <Items>
<Item name="ID Card" equip="true"/> <Item name="ID Card" equip="true"/>
@@ -117,4 +117,4 @@
</Item> </Item>
</Items> </Items>
</Job> </Job>
</Jobs> </Jobs>
@@ -78,6 +78,10 @@ namespace Barotrauma
public EnemyAIController(Character c, string file) : base(c) public EnemyAIController(Character c, string file) : base(c)
{ {
targetMemories = new Dictionary<AITarget, AITargetMemory>(); targetMemories = new Dictionary<AITarget, AITargetMemory>();
outsideSteering = new SteeringManager(this);
insideSteering = new IndoorsSteeringManager(this, false);
steeringManager = outsideSteering;
state = AIState.None;
XDocument doc = XMLExtensions.TryLoadXml(file); XDocument doc = XMLExtensions.TryLoadXml(file);
if (doc == null || doc.Root == null) return; if (doc == null || doc.Root == null) return;
@@ -103,13 +107,6 @@ namespace Barotrauma
fleeHealthThreshold = aiElement.GetAttributeFloat("fleehealththreshold", 0.0f); fleeHealthThreshold = aiElement.GetAttributeFloat("fleehealththreshold", 0.0f);
attachToWalls = aiElement.GetAttributeBool("attachtowalls", false); 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) public override void SelectTarget(AITarget target)
@@ -52,7 +52,7 @@ namespace Barotrauma
public float Range { get; private set; } public float Range { get; private set; }
[Serialize(0.0f, false)] [Serialize(0.0f, false)]
public float DamageRange { get; private set; } public float DamageRange { get; set; }
[Serialize(0.0f, false)] [Serialize(0.0f, false)]
public float Duration { get; private set; } public float Duration { get; private set; }
@@ -1948,8 +1948,21 @@ namespace Barotrauma
AnimController.Frozen = false; 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); if (OnDeath != null) OnDeath(this, causeOfDeath);
KillProjSpecific(); KillProjSpecific();
@@ -51,12 +51,6 @@ namespace Barotrauma
//the physics body of the limb //the physics body of the limb
public PhysicsBody body; public PhysicsBody body;
private readonly int refJointIndex;
private readonly float steerForce;
private readonly bool doesFlip;
protected readonly Vector2 stepOffset; protected readonly Vector2 stepOffset;
public Sprite sprite, damagedSprite; public Sprite sprite, damagedSprite;
@@ -69,8 +63,6 @@ namespace Barotrauma
public readonly bool ignoreCollisions; public readonly bool ignoreCollisions;
private float damage, burnt;
private bool isSevered; private bool isSevered;
private float severedFadeOutTimer; private float severedFadeOutTimer;
@@ -81,16 +73,9 @@ namespace Barotrauma
public const float SoundInterval = 0.4f; public const float SoundInterval = 0.4f;
public readonly Attack attack; public readonly Attack attack;
private List<DamageModifier> damageModifiers;
private Direction dir; private Direction dir;
private List<WearableSprite> wearingItems;
private Vector2 animTargetPos;
private float scale;
private List<DamageModifier> damageModifiers;
public float AttackTimer; public float AttackTimer;
@@ -100,17 +85,13 @@ namespace Barotrauma
set set
{ {
isSevered = value; isSevered = value;
if (isSevered) #if CLIENT
{ if (isSevered) damage = 100.0f;
damage = 100.0f; #endif
}
} }
} }
public bool DoesFlip public bool DoesFlip { get; private set; }
{
get { return doesFlip; }
}
public Vector2 WorldPosition public Vector2 WorldPosition
{ {
@@ -132,21 +113,12 @@ namespace Barotrauma
get { return body.Rotation; } get { return body.Rotation; }
} }
public float Scale public float Scale { get; private set; }
{
get { return scale; }
}
//where an animcontroller is trying to pull the limb, only used for debug visualization //where an animcontroller is trying to pull the limb, only used for debug visualization
public Vector2 AnimTargetPos public Vector2 AnimTargetPos { get; private set; }
{
get { return animTargetPos; }
}
public float SteerForce public float SteerForce { get; private set; }
{
get { return steerForce; }
}
public float Mass public float Mass
{ {
@@ -166,38 +138,25 @@ namespace Barotrauma
set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; } set { dir = (value==-1.0f) ? Direction.Left : Direction.Right; }
} }
public int RefJointIndex public int RefJointIndex { get; private set; }
{
get { return refJointIndex; }
}
public Vector2 StepOffset public Vector2 StepOffset
{ {
get { return stepOffset; } get { return stepOffset; }
} }
public float Burnt public List<WearableSprite> WearingItems { get; private set; }
{
get { return burnt; }
protected set { burnt = MathHelper.Clamp(value, 0.0f, 100.0f); }
}
public List<WearableSprite> WearingItems
{
get { return wearingItems; }
}
public Limb (Character character, XElement element, float scale = 1.0f) public Limb (Character character, XElement element, float scale = 1.0f)
{ {
this.character = character; this.character = character;
wearingItems = new List<WearableSprite>(); WearingItems = new List<WearableSprite>();
dir = Direction.Right; dir = Direction.Right;
DoesFlip = element.GetAttributeBool("flip", false);
doesFlip = element.GetAttributeBool("flip", false); Scale = scale;
this.scale = scale;
body = new PhysicsBody(element, scale); body = new PhysicsBody(element, scale);
@@ -217,7 +176,7 @@ namespace Barotrauma
body.UserData = this; body.UserData = this;
refJointIndex = -1; RefJointIndex = -1;
Vector2 pullJointPos = Vector2.Zero; Vector2 pullJointPos = Vector2.Zero;
@@ -240,7 +199,7 @@ namespace Barotrauma
stepOffset = element.GetAttributeVector2("stepoffset", Vector2.Zero) * scale; stepOffset = element.GetAttributeVector2("stepoffset", Vector2.Zero) * scale;
stepOffset = ConvertUnits.ToSimUnits(stepOffset); stepOffset = ConvertUnits.ToSimUnits(stepOffset);
refJointIndex = element.GetAttributeInt("refjoint", -1); RefJointIndex = element.GetAttributeInt("refjoint", -1);
} }
else else
@@ -254,7 +213,7 @@ namespace Barotrauma
GameMain.World.AddJoint(pullJoint); GameMain.World.AddJoint(pullJoint);
steerForce = element.GetAttributeFloat("steerforce", 0.0f); SteerForce = element.GetAttributeFloat("steerforce", 0.0f);
if (element.Attribute("mouthpos") != null) if (element.Attribute("mouthpos") != null)
{ {
@@ -329,12 +288,12 @@ namespace Barotrauma
public void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false) public void MoveToPos(Vector2 pos, float force, bool pullFromCenter=false)
{ {
Vector2 pullPos = body.SimPosition; Vector2 pullPos = body.SimPosition;
if (pullJoint!=null && !pullFromCenter) if (pullJoint != null && !pullFromCenter)
{ {
pullPos = pullJoint.WorldAnchorA; pullPos = pullJoint.WorldAnchorA;
} }
animTargetPos = pos; AnimTargetPos = pos;
body.MoveToPos(pos, force, pullPos); 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) foreach (DamageModifier damageModifier in wearable.WearableComponent.DamageModifiers)
{ {
@@ -370,54 +329,13 @@ namespace Barotrauma
bleedingAmount *= damageModifier.BleedingMultiplier; bleedingAmount *= damageModifier.BleedingMultiplier;
} }
#if CLIENT AddDamageProjSpecific(position, damageType, amount, bleedingAmount, playSound, 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));
}
}
#endif
if (damageType == DamageType.Burn)
{
Burnt += amount * 10.0f;
}
damage += Math.Max(amount,bleedingAmount) / character.MaxHealth * 100.0f;
return new AttackResult(amount, bleedingAmount, 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) public bool SectorHit(Vector2 armorSector, Vector2 simPosition)
{ {
if (armorSector == Vector2.Zero) return false; if (armorSector == Vector2.Zero) return false;
@@ -435,10 +353,7 @@ namespace Barotrauma
public void Update(float deltaTime) public void Update(float deltaTime)
{ {
UpdateProjSpecific(); UpdateProjSpecific(deltaTime);
if (!character.IsDead) damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
if (burnt > 0.0f) Burnt -= deltaTime;
if (LinearVelocity.X > 500.0f) if (LinearVelocity.X > 500.0f)
{ {
@@ -463,17 +378,11 @@ namespace Barotrauma
if (character.IsDead) return; if (character.IsDead) return;
damage = Math.Max(0.0f, damage - deltaTime * 0.1f);
SoundTimer -= deltaTime; SoundTimer -= deltaTime;
} }
partial void UpdateProjSpecific(); partial void UpdateProjSpecific(float deltaTime);
public void ActivateDamagedSprite()
{
damage = 100.0f;
}
public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget) public void UpdateAttack(float deltaTime, Vector2 attackPosition, IDamageable damageTarget)
{ {
float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition)); float dist = ConvertUnits.ToDisplayUnits(Vector2.Distance(SimPosition, attackPosition));
+165 -130
View File
@@ -88,6 +88,7 @@ namespace Barotrauma
public void Execute(string[] args) public void Execute(string[] args)
{ {
if (onExecute == null) return;
onExecute(args); onExecute(args);
} }
@@ -100,6 +101,7 @@ namespace Barotrauma
{ {
if (onClientRequestExecute == null) if (onClientRequestExecute == null)
{ {
if (onExecute == null) return;
onExecute(args); onExecute(args);
} }
else 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[] args) =>
{ {
string errorMsg; SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), Character.Controlled, out string errorMsg);
SpawnItem(args, GameMain.GameScreen.Cam.ScreenToWorld(PlayerInput.MousePosition), out errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg)) if (!string.IsNullOrWhiteSpace(errorMsg))
{ {
ThrowError(errorMsg); ThrowError(errorMsg);
@@ -260,20 +261,18 @@ namespace Barotrauma
null, null,
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
string errorMsg; SpawnItem(args, cursorWorldPos, client.Character, out string errorMsg);
SpawnItem(args, cursorWorldPos, out errorMsg);
if (!string.IsNullOrWhiteSpace(errorMsg)) if (!string.IsNullOrWhiteSpace(errorMsg))
{ {
ThrowError(errorMsg); GameMain.Server.SendConsoleMessage(errorMsg, client);
} }
}, },
() => () =>
{ {
List<string> itemNames = new List<string>(); List<string> itemNames = new List<string>();
foreach (MapEntityPrefab prefab in MapEntityPrefab.List) foreach (MapEntityPrefab prefab in MapEntityPrefab.List)
{ {
ItemPrefab itemPrefab = prefab as ItemPrefab; if (prefab is ItemPrefab itemPrefab) itemNames.Add(itemPrefab.Name);
if (itemPrefab != null) itemNames.Add(itemPrefab.Name);
} }
return new string[][] 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) => commands.Add(new Command("disablecrewai", "disablecrewai: Disable the AI of the NPCs in the crew.", (string[] args) =>
{ {
HumanAIController.DisableCrewAI = true; HumanAIController.DisableCrewAI = true;
@@ -849,8 +849,7 @@ namespace Barotrauma
{ {
if (args.Length < 2) return; if (args.Length < 2) return;
int id; int.TryParse(args[0], out int id);
int.TryParse(args[0], out id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null) if (client == null)
{ {
@@ -891,8 +890,7 @@ namespace Barotrauma
return; return;
} }
int id; int.TryParse(args[0], out int id);
int.TryParse(args[0], out id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id); var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null) 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) => 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 int id);
int.TryParse(args[0], out id); var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null) if (client == null)
{ {
ThrowError("Client id \"" + id + "\" not found."); ThrowError("Client id \"" + id + "\" not found.");
@@ -1032,7 +1029,7 @@ namespace Barotrauma
ShowQuestionPrompt("Reason for kicking \"" + client.Name + "\"?", (reason) => 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) => 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 int id);
int.TryParse(args[0], out id); var client = GameMain.NetworkMember.ConnectedClients.Find(c => c.ID == id);
var client = GameMain.Server.ConnectedClients.Find(c => c.ID == id);
if (client == null) if (client == null)
{ {
ThrowError("Client id \"" + id + "\" not found."); ThrowError("Client id \"" + id + "\" not found.");
return; return;
} }
ShowQuestionPrompt("Reason for banning \"" + client.Name + "\"?", (reason) => 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) => 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; TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration)) if (!string.IsNullOrWhiteSpace(duration))
{ {
TimeSpan parsedBanDuration; if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
if (!TryParseTimeSpan(duration, out parsedBanDuration))
{ {
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return; return;
@@ -1100,7 +1095,7 @@ namespace Barotrauma
banDuration = parsedBanDuration; 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; TimeSpan? banDuration = null;
if (!string.IsNullOrWhiteSpace(duration)) if (!string.IsNullOrWhiteSpace(duration))
{ {
TimeSpan parsedBanDuration; if (!TryParseTimeSpan(duration, out TimeSpan parsedBanDuration))
if (!TryParseTimeSpan(duration, out parsedBanDuration))
{ {
ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\"."); ThrowError("\"" + duration + "\" is not a valid ban duration. Use the format \"[days] d [hours] h\", \"[days] d\" or \"[hours] h\".");
return; 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) => 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; Character tpCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args, false);
if (args.Length == 0)
{
tpCharacter = Character.Controlled;
}
else
{
tpCharacter = FindMatchingCharacter(args, false);
}
if (tpCharacter == null) return; if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam; var cam = GameMain.GameScreen.Cam;
@@ -1167,17 +1210,7 @@ namespace Barotrauma
null, null,
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
Character tpCharacter = null; Character tpCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args, false);
if (args.Length == 0)
{
tpCharacter = client.Character;
}
else
{
tpCharacter = FindMatchingCharacter(args, false);
}
if (tpCharacter == null) return; if (tpCharacter == null) return;
var cam = GameMain.GameScreen.Cam; var cam = GameMain.GameScreen.Cam;
@@ -1216,7 +1249,7 @@ namespace Barotrauma
Submarine.LockX = !Submarine.LockX; Submarine.LockX = !Submarine.LockX;
}, null, null)); }, 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; Submarine.LockY = !Submarine.LockY;
}, null, null)); }, 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) => 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; Character healedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (args.Length == 0)
{
healedCharacter = Character.Controlled;
}
else
{
healedCharacter = FindMatchingCharacter(args);
}
if (healedCharacter != null) if (healedCharacter != null)
{ {
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null); healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, null);
@@ -1257,16 +1301,7 @@ namespace Barotrauma
null, null,
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
Character healedCharacter = null; Character healedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (args.Length == 0)
{
healedCharacter = client.Character;
}
else
{
healedCharacter = FindMatchingCharacter(args);
}
if (healedCharacter != null) if (healedCharacter != null)
{ {
healedCharacter.AddDamage(CauseOfDeath.Damage, -healedCharacter.MaxHealth, 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) => 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; Character revivedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (args.Length == 0)
{
revivedCharacter = Character.Controlled;
}
else
{
revivedCharacter = FindMatchingCharacter(args);
}
if (revivedCharacter == null) return; if (revivedCharacter == null) return;
revivedCharacter.Revive(false); revivedCharacter.Revive(false);
@@ -1313,16 +1339,7 @@ namespace Barotrauma
null, null,
(Client client, Vector2 cursorWorldPos, string[] args) => (Client client, Vector2 cursorWorldPos, string[] args) =>
{ {
Character revivedCharacter = null; Character revivedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (args.Length == 0)
{
revivedCharacter = client.Character;
}
else
{
revivedCharacter = FindMatchingCharacter(args);
}
if (revivedCharacter == null) return; if (revivedCharacter == null) return;
revivedCharacter.Revive(false); 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) => 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; Character ragdolledCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (args.Length == 0) if (ragdolledCharacter != null)
{ {
ragdolledCharacter = Character.Controlled; ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
} }
else },
{ null,
ragdolledCharacter = FindMatchingCharacter(args); (Client client, Vector2 cursorWorldPos, string[] args) =>
} {
Character ragdolledCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
if (ragdolledCharacter != null) if (ragdolledCharacter != null)
{ {
ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled; ragdolledCharacter.IsForceRagdolled = !ragdolledCharacter.IsForceRagdolled;
@@ -1477,20 +1494,14 @@ namespace Barotrauma
commands.Add(new Command("kill", "kill [character]: Immediately kills the specified character.", (string[] args) => commands.Add(new Command("kill", "kill [character]: Immediately kills the specified character.", (string[] args) =>
{ {
Character killedCharacter = null; Character killedCharacter = (args.Length == 0) ? Character.Controlled : FindMatchingCharacter(args);
if (args.Length == 0) killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
{ },
killedCharacter = Character.Controlled; null,
} (Client client, Vector2 cursorWorldPos, string[] args) =>
else {
{ Character killedCharacter = (args.Length == 0) ? client.Character : FindMatchingCharacter(args);
killedCharacter = FindMatchingCharacter(args); killedCharacter?.AddDamage(CauseOfDeath.Damage, killedCharacter.MaxHealth * 2, null);
}
if (killedCharacter != null)
{
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) => 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 = ""; errorMsg = "";
if (args.Length < 1) return; if (args.Length < 1) return;
@@ -2128,23 +2139,48 @@ namespace Barotrauma
Vector2? spawnPos = null; Vector2? spawnPos = null;
Inventory spawnInventory = null; Inventory spawnInventory = null;
int extraParams = 0; if (args.Length > 1)
switch (args.Last())
{ {
case "cursor": switch (args[1])
extraParams = 1; {
spawnPos = cursorPos; case "cursor":
break; spawnPos = cursorPos;
case "inventory": break;
extraParams = 1; case "inventory":
spawnInventory = Character.Controlled == null ? null : Character.Controlled.Inventory; spawnInventory = controlledCharacter?.Inventory;
break; break;
default: default:
extraParams = 0; //Check if last arg matches the name of an in-game player
break; 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; var itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null) if (itemPrefab == null)
@@ -2162,7 +2198,6 @@ namespace Barotrauma
if (spawnPos != null) if (spawnPos != null)
{ {
Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos); Entity.Spawner.AddToSpawnQueue(itemPrefab, (Vector2)spawnPos);
} }
else if (spawnInventory != null) else if (spawnInventory != null)
{ {
@@ -13,12 +13,12 @@ namespace Barotrauma
private int requiredDeliveryAmount; private int requiredDeliveryAmount;
public CargoMission(XElement element, Location[] locations) public CargoMission(MissionPrefab prefab, Location[] locations)
: base(element, 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() private void InitItems()
@@ -29,6 +29,8 @@ namespace Barotrauma
{ {
get get
{ {
if (descriptions == null) return "";
if (GameMain.NetworkMember==null || GameMain.NetworkMember.Character==null) if (GameMain.NetworkMember==null || GameMain.NetworkMember.Character==null)
{ {
//non-team-specific description //non-team-specific description
@@ -47,20 +49,20 @@ namespace Barotrauma
{ {
if (winner == -1) return ""; if (winner == -1) return "";
return successMessage return base.SuccessMessage
.Replace("[loser]", teamNames[1 - winner]) .Replace("[loser]", teamNames[1 - winner])
.Replace("[winner]", teamNames[winner]); .Replace("[winner]", teamNames[winner]);
} }
} }
public CombatMission(XElement element, Location[] locations) public CombatMission(MissionPrefab prefab, Location[] locations)
: base(element, locations) : base(prefab, locations)
{ {
descriptions = new string[] descriptions = new string[]
{ {
element.GetAttributeString("descriptionneutral", ""), prefab.ConfigElement.GetAttributeString("descriptionneutral", ""),
element.GetAttributeString("description1", ""), prefab.ConfigElement.GetAttributeString("description1", ""),
element.GetAttributeString("description2", "") prefab.ConfigElement.GetAttributeString("description2", "")
}; };
for (int i = 0; i < descriptions.Length; i++) for (int i = 0; i < descriptions.Length; i++)
@@ -73,8 +75,8 @@ namespace Barotrauma
teamNames = new string[] teamNames = new string[]
{ {
element.GetAttributeString("teamname1", "Team A"), prefab.ConfigElement.GetAttributeString("teamname1", "Team A"),
element.GetAttributeString("teamname2", "Team B") prefab.ConfigElement.GetAttributeString("teamname2", "Team B")
}; };
} }
@@ -1,45 +1,18 @@
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
partial class Mission partial class Mission
{ {
public static List<string> MissionTypes = new List<string>() { "Random" };
private string name;
private string description;
protected bool completed; protected bool completed;
protected string successMessage; private readonly MissionPrefab prefab;
protected string failureMessage;
protected string radarLabel;
protected List<string> headers;
protected List<string> messages;
private int reward;
public string Name public string Name
{ {
get { return name; } get { return prefab.Name; }
}
public virtual string Description
{
get { return description; }
}
public int Reward
{
get { return reward; }
} }
public bool Completed public bool Completed
@@ -48,109 +21,101 @@ namespace Barotrauma
set { completed = value; } set { completed = value; }
} }
public int Reward
{
get { return prefab.Reward; }
}
public virtual bool AllowRespawn public virtual bool AllowRespawn
{ {
get { return true; } get { return true; }
} }
public virtual string RadarLabel
{
get { return radarLabel; }
}
public virtual Vector2 RadarPosition public virtual Vector2 RadarPosition
{ {
get { return Vector2.Zero; } 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 public string FailureMessage
{ {
get { return failureMessage; } get;
protected set;
} }
public static void Init() public virtual string Description
{ {
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions); get;
foreach (string file in files) protected set;
{
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);
}
}
} }
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); Description = prefab.Description;
SuccessMessage = prefab.SuccessMessage;
successMessage = element.GetAttributeString("successmessage", FailureMessage = prefab.FailureMessage;
"Mission completed successfully"); Headers = new List<string>(prefab.Headers);
failureMessage = element.GetAttributeString("failuremessage", Messages = new List<string>(prefab.Messages);
"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", ""));
}
for (int n = 0; n < 2; n++) for (int n = 0; n < 2; n++)
{ {
description = description.Replace("[location" + (n + 1) + "]", locations[n].Name); if (Description != null) Description = Description.Replace("[location" + (n + 1) + "]", locations[n].Name);
if (SuccessMessage != null) SuccessMessage = SuccessMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
successMessage = successMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); if (FailureMessage != null) FailureMessage = FailureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name);
failureMessage = failureMessage.Replace("[location" + (n + 1) + "]", locations[n].Name); for (int m = 0; m < Messages.Count; m++)
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) 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(); missionType = missionType.ToLowerInvariant();
var files = GameMain.SelectedPackage.GetFilesOfType(ContentType.Missions); List<MissionPrefab> allowedMissions = new List<MissionPrefab>();
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>();
if (missionType == "random") 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") else if (missionType == "none")
{ {
@@ -158,68 +123,31 @@ namespace Barotrauma
} }
else if (string.IsNullOrWhiteSpace(missionType)) else if (string.IsNullOrWhiteSpace(missionType))
{ {
matchingElements = doc.Root.Elements().ToList(); allowedMissions.AddRange(MissionPrefab.List);
} }
else 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) if (isSinglePlayer)
{ {
matchingElements.RemoveAll(m => m.GetAttributeBool("multiplayeronly", false)); allowedMissions.RemoveAll(m => m.MultiplayerOnly);
} }
else else
{ {
matchingElements.RemoveAll(m => m.GetAttributeBool("singleplayeronly", false)); allowedMissions.RemoveAll(m => m.SingleplayerOnly);
} }
int i = 0; float probabilitySum = allowedMissions.Sum(m => m.Commonness);
foreach (XElement element in matchingElements)
{
eventProbability[i] = element.GetAttributeInt("commonness", 1);
probabilitySum += eventProbability[i];
i++;
}
float randomNumber = (float)rand.NextDouble() * probabilitySum; float randomNumber = (float)rand.NextDouble() * probabilitySum;
foreach (MissionPrefab missionPrefab in allowedMissions)
i = 0;
foreach (XElement element in matchingElements)
{ {
if (randomNumber <= eventProbability[i]) if (randomNumber <= missionPrefab.Commonness)
{ {
Type t; return missionPrefab.Instantiate(locations);
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;
} }
randomNumber -= missionPrefab.Commonness;
randomNumber -= eventProbability[i];
i++;
} }
return null; return null;
@@ -251,7 +179,7 @@ namespace Barotrauma
var mode = GameMain.GameSession.GameMode as CampaignMode; var mode = GameMain.GameSession.GameMode as CampaignMode;
if (mode == null) return; if (mode == null) return;
mode.Money += reward; mode.Money += Reward;
} }
} }
} }
@@ -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;
}
}
}
@@ -18,10 +18,10 @@ namespace Barotrauma
get { return monster != null && !monster.IsDead ? radarPosition : Vector2.Zero; } get { return monster != null && !monster.IsDead ? radarPosition : Vector2.Zero; }
} }
public MonsterMission(XElement element, Location[] locations) public MonsterMission(MissionPrefab prefab, Location[] locations)
: base(element, locations) : base(prefab, locations)
{ {
monsterFile = element.GetAttributeString("monsterfile", ""); monsterFile = prefab.ConfigElement.GetAttributeString("monsterfile", "");
} }
@@ -1,7 +1,6 @@
using FarseerPhysics; using FarseerPhysics;
using Microsoft.Xna.Framework; using Microsoft.Xna.Framework;
using System; using System;
using System.Xml.Linq;
namespace Barotrauma namespace Barotrauma
{ {
@@ -23,10 +22,10 @@ namespace Barotrauma
} }
} }
public SalvageMission(XElement element, Location[] locations) public SalvageMission(MissionPrefab prefab, Location[] locations)
: base(element, locations) : base(prefab, locations)
{ {
string itemName = element.GetAttributeString("itemname", ""); string itemName = prefab.ConfigElement.GetAttributeString("itemname", "");
itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab; itemPrefab = MapEntityPrefab.Find(itemName) as ItemPrefab;
if (itemPrefab == null) if (itemPrefab == null)
@@ -35,10 +34,10 @@ namespace Barotrauma
return; return;
} }
string spawnPositionTypeStr = element.GetAttributeString("spawntype", ""); string spawnPositionTypeStr = prefab.ConfigElement.GetAttributeString("spawntype", "");
if (string.IsNullOrWhiteSpace(spawnPositionTypeStr) || 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; spawnPositionType = Level.PositionType.Cave | Level.PositionType.Ruin;
} }
@@ -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)));
}
}
}
}
@@ -16,9 +16,22 @@
: base(preset, param) : base(preset, param)
{ {
Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation }; Location[] locations = { GameMain.GameSession.StartLocation, GameMain.GameSession.EndLocation };
if (param is string)
MTRandom rand = new MTRandom(ToolBox.StringToInt(GameMain.NetLobbyScreen.LevelSeed)); {
mission = Mission.LoadRandom(locations, rand, param as 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 + "\"");
}
} }
} }
} }
@@ -55,13 +55,7 @@ namespace Barotrauma
public override void Start() public override void Start()
{ {
base.Start(); base.Start();
if (GameMain.Server != null)
{
CargoManager.CreateItems();
}
lastUpdateID++; lastUpdateID++;
} }
@@ -108,10 +108,9 @@ namespace Barotrauma
codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt); codeWords = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt); codeResponse = ToolBox.GetRandomLine(wordsTxt) + ", " + ToolBox.GetRandomLine(wordsTxt);
while (traitorCount-- >= 0) while (traitorCount-- > 0)
{ {
if (traitorCandidates.Count <= 0) if (traitorCandidates.Count <= 0) break;
break;
int traitorIndex = Rand.Int(traitorCandidates.Count); int traitorIndex = Rand.Int(traitorCandidates.Count);
Character traitorCharacter = traitorCandidates[traitorIndex]; Character traitorCharacter = traitorCandidates[traitorIndex];
@@ -91,27 +91,35 @@ namespace Barotrauma
set { savePath = value; } 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; Submarine.MainSub = submarine;
this.submarine = submarine;
GameMain.GameSession = this; GameMain.GameSession = this;
EventManager = new EventManager(this); EventManager = new EventManager(this);
this.savePath = savePath; this.savePath = savePath;
#if CLIENT #if CLIENT
CrewManager = new CrewManager(); CrewManager = new CrewManager();
infoButton = new GUIButton(new Rectangle(10, 10, 100, 20), "Info", "", null); infoButton = new GUIButton(new Rectangle(10, 10, 100, 20), "Info", "", null);
infoButton.OnClicked = ToggleInfoFrame; infoButton.OnClicked = ToggleInfoFrame;
#endif #endif
if (gameModePreset != null) GameMode = gameModePreset.Instantiate(missionType);
this.submarine = submarine;
} }
public GameSession(Submarine selectedSub, string saveFile, XDocument doc) public GameSession(Submarine selectedSub, string saveFile, XDocument doc)
: this(selectedSub, saveFile) : this(selectedSub, saveFile)
{ {
@@ -218,8 +226,22 @@ namespace Barotrauma
EventManager.StartRound(level); 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 #if CLIENT
roundSummary = new RoundSummary(this); roundSummary = new RoundSummary(this);
@@ -231,6 +253,11 @@ namespace Barotrauma
public void EndRound(string endMessage) public void EndRound(string endMessage)
{ {
if (Mission != null) Mission.End(); 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 CLIENT
if (roundSummary != null) if (roundSummary != null)
@@ -25,7 +25,7 @@ namespace Barotrauma
public bool EnableSplashScreen { get; set; } public bool EnableSplashScreen { get; set; }
//public bool FullScreenEnabled { get; set; } //public bool FullScreenEnabled { get; set; }
private KeyOrMouse[] keyMapping; private KeyOrMouse[] keyMapping;
private WindowMode windowMode; 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 public bool UnsavedSettings
{ {
get get
@@ -125,6 +154,18 @@ namespace Barotrauma
public static bool VerboseLogging { get; set; } public static bool VerboseLogging { get; set; }
public static bool SaveDebugConsoleLogs { 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) public GameSettings(string filePath)
{ {
ContentPackage.LoadAll(ContentPackage.Folder); ContentPackage.LoadAll(ContentPackage.Folder);
@@ -143,6 +184,14 @@ namespace Barotrauma
VerboseLogging = doc.Root.GetAttributeBool("verboselogging", false); VerboseLogging = doc.Root.GetAttributeBool("verboselogging", false);
SaveDebugConsoleLogs = doc.Root.GetAttributeBool("savedebugconsolelogs", 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) if (doc == null)
{ {
@@ -210,18 +259,15 @@ namespace Barotrauma
case "keymapping": case "keymapping":
foreach (XAttribute attribute in subElement.Attributes()) foreach (XAttribute attribute in subElement.Attributes())
{ {
InputType inputType; if (Enum.TryParse(attribute.Name.ToString(), true, out InputType inputType))
if (Enum.TryParse(attribute.Name.ToString(), true, out inputType))
{ {
int mouseButton; if (int.TryParse(attribute.Value.ToString(), out int mouseButton))
if (int.TryParse(attribute.Value.ToString(), out mouseButton))
{ {
keyMapping[(int)inputType] = new KeyOrMouse(mouseButton); keyMapping[(int)inputType] = new KeyOrMouse(mouseButton);
} }
else else
{ {
Keys key; if (Enum.TryParse(attribute.Value.ToString(), true, out Keys key))
if (Enum.TryParse(attribute.Value.ToString(), true, out key))
{ {
keyMapping[(int)inputType] = new KeyOrMouse(key); keyMapping[(int)inputType] = new KeyOrMouse(key);
} }
@@ -238,6 +284,9 @@ namespace Barotrauma
break; break;
case "player": case "player":
defaultPlayerName = subElement.GetAttributeString("name", ""); 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; break;
} }
} }
@@ -259,10 +308,8 @@ namespace Barotrauma
{ {
case "contentpackage": case "contentpackage":
string path = subElement.GetAttributeString("path", ""); string path = subElement.GetAttributeString("path", "");
SelectedContentPackage = ContentPackage.list.Find(cp => cp.Path == path); SelectedContentPackage = ContentPackage.list.Find(cp => cp.Path == path);
if (SelectedContentPackage == null) SelectedContentPackage = new ContentPackage(path); if (SelectedContentPackage == null) SelectedContentPackage = new ContentPackage(path);
break; break;
} }
@@ -287,7 +334,8 @@ namespace Barotrauma
new XAttribute("soundvolume", soundVolume), new XAttribute("soundvolume", soundVolume),
new XAttribute("verboselogging", VerboseLogging), new XAttribute("verboselogging", VerboseLogging),
new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs), new XAttribute("savedebugconsolelogs", SaveDebugConsoleLogs),
new XAttribute("enablesplashscreen", EnableSplashScreen)); new XAttribute("enablesplashscreen", EnableSplashScreen),
new XAttribute("senduserstatistics", sendUserStatistics));
if (WasGameUpdated) if (WasGameUpdated)
{ {
@@ -344,8 +392,10 @@ namespace Barotrauma
gameplay.Add(jobPreferences); gameplay.Add(jobPreferences);
doc.Root.Add(gameplay); doc.Root.Add(gameplay);
var playerElement = new XElement("player"); var playerElement = new XElement("player",
playerElement.Add(new XAttribute("name", defaultPlayerName ?? "")); new XAttribute("name", defaultPlayerName ?? ""),
new XAttribute("headindex", characterHeadIndex),
new XAttribute("gender", characterGender));
doc.Root.Add(playerElement); doc.Root.Add(playerElement);
doc.Save(filePath); doc.Save(filePath);
@@ -179,7 +179,6 @@ namespace Barotrauma.Items.Components
return (picker != null); return (picker != null);
} }
public override bool Combine(Item item) public override bool Combine(Item item)
{ {
if (!containableItems.Any(x => x.MatchesItem(item))) return false; if (!containableItems.Any(x => x.MatchesItem(item))) return false;
@@ -195,6 +194,42 @@ namespace Barotrauma.Items.Components
return false; 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() public override void OnMapLoaded()
{ {
if (itemIds == null) return; if (itemIds == null) return;
@@ -99,7 +99,9 @@ namespace Barotrauma.Items.Components
ApplyStatusEffects(ActionType.OnActive, deltaTime, null); 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; float powerFactor = currPowerConsumption <= 0.0f ? 1.0f : voltage;
@@ -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) public override bool Use(float deltaTime, Character character = null)
{ {
if (character != null && !characterUsable) return false; if (character != null && !characterUsable) return false;
@@ -196,7 +216,6 @@ namespace Barotrauma.Items.Components
if (OnProjectileCollision(fixture, normal)) if (OnProjectileCollision(fixture, normal))
{ {
hitSomething = true; hitSomething = true;
//Character.Controlled.AnimController.Teleport(point - Character.Controlled.SimPosition, Vector2.Zero);
break; break;
} }
} }
@@ -281,8 +300,7 @@ namespace Barotrauma.Items.Components
Character character = null; Character character = null;
if (attack != null) if (attack != null)
{ {
var submarine = target.Body.UserData as Submarine; if (target.Body.UserData is Submarine submarine)
if (submarine != null)
{ {
item.Move(-submarine.Position); item.Move(-submarine.Position);
item.Submarine = submarine; item.Submarine = submarine;
@@ -290,9 +308,8 @@ namespace Barotrauma.Items.Components
return true; return true;
} }
Limb limb = target.Body.UserData as Limb;
Structure structure; Structure structure;
if (limb != null) if (target.Body.UserData is Limb limb)
{ {
attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f); attackResult = attack.DoDamageToLimb(User, limb, item.WorldPosition, 1.0f);
if (limb.character != null) if (limb.character != null)
@@ -350,13 +367,12 @@ namespace Barotrauma.Items.Components
{ {
contained.SetTransform(item.SimPosition, contained.body.Rotation); contained.SetTransform(item.SimPosition, contained.body.Rotation);
} }
//contained.Condition = 0.0f; //Let the freaking .xml handle it jeez
} }
} }
if (RemoveOnHit) if (RemoveOnHit)
{ {
Item.Spawner.AddToRemoveQueue(item); Entity.Spawner.AddToRemoveQueue(item);
} }
return true; return true;
@@ -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;
}
}
}
}
@@ -10,49 +10,51 @@ namespace Barotrauma.Items.Components
{ {
const int SignalQueueSize = 500; const int SignalQueueSize = 500;
//the output is sent if both inputs have received a signal within the timeframe private Queue<Pair<string, float>> signalQueue;
private TimeSpan delay;
private Queue<Tuple<string, DateTime>> signalQueue;
[InGameEditable, Serialize(1.0f, true)] [InGameEditable(MinValueFloat = 0.0f, MaxValueFloat = 60.0f), Serialize(1.0f, true)]
public float Delay public float Delay
{ {
get { return (float)delay.TotalSeconds; } get;
set set;
{
float seconds = MathHelper.Clamp(value, 0.0f, 60.0f);
delay = new TimeSpan(0,0,0,0, (int)(seconds*1000.0f));
}
} }
[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) public DelayComponent(Item item, XElement element)
: base (item, element) : base (item, element)
{ {
signalQueue = new Queue<Tuple<string, DateTime>>(); signalQueue = new Queue<Pair<string, float>>();
IsActive = true; IsActive = true;
} }
public override void Update(float deltaTime, Camera cam) 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(); var signalOut = signalQueue.Dequeue();
item.SendSignal(0, signalOut.First, "signal_out", null);
item.SendSignal(0, signalOut.Item1, "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) switch (connection.Name)
{ {
case "signal_in": case "signal_in":
if (signalQueue.Count >= SignalQueueSize) return; if (signalQueue.Count >= SignalQueueSize) return;
if (ResetWhenSignalReceived) signalQueue.Clear();
signalQueue.Enqueue(new Tuple<string, DateTime>(signal, DateTime.Now)); signalQueue.Enqueue(Pair<string, float>.Create(signal, Delay));
break; break;
} }
} }
@@ -49,14 +49,9 @@ namespace Barotrauma.Items.Components
public override void Update(float deltaTime, Camera cam) public override void Update(float deltaTime, Camera cam)
{ {
if (motionDetected) string signalOut = motionDetected ? output : falseOutput;
{
item.SendSignal(1, output, "state_out", null); if (!string.IsNullOrEmpty(signalOut)) item.SendSignal(1, signalOut, "state_out", null);
}
else if (!string.IsNullOrWhiteSpace(falseOutput))
{
item.SendSignal(1, falseOutput, "state_out", null);
}
updateTimer -= deltaTime; updateTimer -= deltaTime;
if (updateTimer > 0.0f) return; if (updateTimer > 0.0f) return;
@@ -315,9 +315,9 @@ namespace Barotrauma
public override string ToString() public override string ToString()
{ {
#if CLIENT #if CLIENT
return (GameMain.DebugDraw) ? Name + "(ID: " + ID + ")" : Name; return (GameMain.DebugDraw) ? Name + " (ID: " + ID + ")" : Name;
#elif SERVER #elif SERVER
return Name + "(ID: " + ID + ")"; return Name + " (ID: " + ID + ")";
#endif #endif
} }
@@ -602,7 +602,7 @@ namespace Barotrauma
foreach (Item item in ItemList) item.FindHull(); foreach (Item item in ItemList) item.FindHull();
} }
public virtual Hull FindHull() public Hull FindHull()
{ {
if (parentInventory != null && parentInventory.Owner != null) if (parentInventory != null && parentInventory.Owner != null)
{ {
@@ -648,39 +648,9 @@ namespace Barotrauma
public void SetContainedItemPositions() public void SetContainedItemPositions()
{ {
if (ownInventory == null) return; foreach (ItemComponent component in components)
Vector2 simPos = SimPosition;
Vector2 displayPos = Position;
foreach (Item contained in ownInventory.Items)
{ {
if (contained == null) continue; (component as ItemContainer)?.SetContainedItemPositions();
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();
} }
} }
@@ -804,7 +774,7 @@ namespace Barotrauma
private bool IsInWater() private bool IsInWater()
{ {
if (CurrentHull == null) return true; if (CurrentHull == null) return true;
float surfaceY = CurrentHull.Surface; float surfaceY = CurrentHull.Surface;
return CurrentHull.WaterVolume > 0.0f && Position.Y < surfaceY; return CurrentHull.WaterVolume > 0.0f && Position.Y < surfaceY;
@@ -80,7 +80,7 @@ namespace Barotrauma
if (distSqr > displayRangeSqr) continue; if (distSqr > displayRangeSqr) continue;
//ignore reactors (don't want to blow them up) //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; float distFactor = 1.0f - (float)Math.Sqrt(distSqr) / displayRange;
@@ -664,19 +664,20 @@ namespace Barotrauma
float damageAmount = 0.0f; float damageAmount = 0.0f;
for (int i = 0; i < SectionCount; i++) 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); damageAmount = attack.GetStructureDamage(deltaTime);
AddDamage(i, damageAmount, attacker); AddDamage(i, damageAmount, attacker);
#if CLIENT #if CLIENT
GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f); GameMain.ParticleManager.CreateParticle("dustcloud", SectionPosition(i), 0.0f, 0.0f);
#endif #endif
} }
} }
#if CLIENT #if CLIENT
if (playSound)// && !SectionBodyDisabled(i)) if (playSound)
{ {
string damageSoundType = (attack.DamageType == DamageType.Blunt) ? "StructureBlunt" : "StructureSlash"; string damageSoundType = (attack.DamageType == DamageType.Blunt) ? "StructureBlunt" : "StructureSlash";
SoundPlayer.PlayDamageSound(damageSoundType, damageAmount, worldPosition, tags: Tags); SoundPlayer.PlayDamageSound(damageSoundType, damageAmount, worldPosition, tags: Tags);
@@ -490,7 +490,7 @@ namespace Barotrauma
return wayPoints[Rand.Int(wayPoints.Count, (useSyncedRand ? Rand.RandSync.Server : Rand.RandSync.Unsynced))]; 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); List<WayPoint> subWayPoints = WayPointList.FindAll(wp => wp.Submarine == submarine);
@@ -524,29 +524,24 @@ namespace Barotrauma
assignedWayPoints[i] = wp; assignedWayPoints[i] = wp;
break; break;
} }
if (assignedWayPoints[i] != null) continue; 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 assignedWayPoints[i] = nonJobSpecificPoints[Rand.Int(nonJobSpecificPoints.Count, Rand.RandSync.Server)];
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)];
}
} }
if (assignedWayPoints[i] != null) continue; if (assignedWayPoints[i] != null) continue;
//everything else failed -> just give a random spawnpoint //everything else failed -> just give a random spawnpoint inside the sub
assignedWayPoints[i] = GetRandom(SpawnType.Human); 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 + "!"); DebugConsole.ThrowError("Couldn't find a waypoint for " + crew[i].Name + "!");
assignedWayPoints[i] = WayPointList[0]; assignedWayPoints[i] = WayPointList[0];
@@ -29,10 +29,20 @@ namespace Barotrauma.Networking
public byte TeamID = 0; 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 CharacterInfo CharacterInfo;
public NetConnection Connection { get; set; } 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; public UInt16 LastRecvGeneralUpdate = 0;
@@ -209,6 +209,8 @@ namespace Barotrauma.Networking
GameMain.NetLobbyScreen.RandomizeSettings(); GameMain.NetLobbyScreen.RandomizeSettings();
started = true; started = true;
if (GameSettings.SendUserStatistics) GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:Start");
yield return CoroutineStatus.Success; yield return CoroutineStatus.Success;
} }
@@ -778,11 +780,21 @@ namespace Barotrauma.Networking
case ClientPermissions.Ban: case ClientPermissions.Ban:
string bannedName = inc.ReadString().ToLowerInvariant(); string bannedName = inc.ReadString().ToLowerInvariant();
string banReason = inc.ReadString(); string banReason = inc.ReadString();
bool range = inc.ReadBoolean();
double durationSeconds = inc.ReadDouble();
var bannedClient = connectedClients.Find(cl => cl != sender && cl.Name.ToLowerInvariant() == bannedName); var bannedClient = connectedClients.Find(cl => cl != sender && cl.Name.ToLowerInvariant() == bannedName);
if (bannedClient != null) if (bannedClient != null)
{ {
Log("Client \"" + sender.Name + "\" banned \"" + bannedClient.Name + "\".", ServerLog.MessageType.ServerMessage); 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; break;
case ClientPermissions.EndRound: case ClientPermissions.EndRound:
@@ -993,7 +1005,7 @@ namespace Barotrauma.Networking
outmsg.WriteRangedInteger(0, 2, (int)TraitorsEnabled); 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((byte)GameMain.NetLobbyScreen.SelectedModeIndex);
outmsg.Write(GameMain.NetLobbyScreen.LevelSeed); outmsg.Write(GameMain.NetLobbyScreen.LevelSeed);
@@ -1213,7 +1225,7 @@ namespace Barotrauma.Networking
//don't instantiate a new gamesession if we're playing a campaign //don't instantiate a new gamesession if we're playing a campaign
if (campaign == null || GameMain.GameSession == null) 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 && if (GameMain.GameSession.GameMode.Mission != null &&
@@ -1341,11 +1353,12 @@ namespace Barotrauma.Networking
List<Character> characters = new List<Character>(); List<Character> characters = new List<Character>();
foreach (Client client in ConnectedClients) foreach (Client client in ConnectedClients)
{ {
if (client.Character != null) if (client.Character != null) characters.Add(client.Character);
characters.Add(client.Character);
} }
var max = (int)Math.Round(characters.Count * 0.2f, 1); if (Character != null) characters.Add(Character);
var traitorCount = Math.Max(1, TraitorsEnabled == YesNoMaybe.Maybe ? Rand.Int(max) + 1 : max);
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); TraitorManager = new TraitorManager(this, traitorCount);
if (TraitorManager.TraitorList.Count > 0) 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); SendStartMessage(roundStartSeed, Submarine.MainSub, GameMain.GameSession.GameMode.Preset, connectedClients);
yield return CoroutineStatus.Running; yield return CoroutineStatus.Running;
@@ -1402,6 +1417,8 @@ namespace Barotrauma.Networking
msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash); msg.Write(GameMain.NetLobbyScreen.SelectedShuttle.MD5Hash.Hash);
msg.Write(selectedMode.Name); 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; MultiPlayerCampaign campaign = GameMain.GameSession?.GameMode as MultiPlayerCampaign;
@@ -1495,6 +1512,7 @@ namespace Barotrauma.Networking
foreach (Client client in connectedClients) foreach (Client client in connectedClients)
{ {
client.Character = null; client.Character = null;
client.HasSpawned = false;
client.InGame = false; client.InGame = false;
} }
} }
@@ -1614,6 +1632,7 @@ namespace Barotrauma.Networking
} }
client.Character = null; client.Character = null;
client.HasSpawned = false;
client.InGame = false; client.InGame = false;
if (string.IsNullOrWhiteSpace(msg)) msg = client.Name + " has left the server"; if (string.IsNullOrWhiteSpace(msg)) msg = client.Name + " has left the server";
@@ -2063,9 +2082,17 @@ namespace Barotrauma.Networking
if (jobPrefab != null) jobPreferences.Add(jobPrefab); if (jobPrefab != null) jobPreferences.Add(jobPrefab);
} }
sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender); sender.CharacterInfo = new CharacterInfo(Character.HumanConfigFile, sender.Name, gender)
sender.CharacterInfo.HeadSpriteId = headSpriteId; {
sender.JobPreferences = jobPreferences; 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) 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 //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--) for (int i = unassigned.Count - 1; i >= 0; i--)
{ {
if (unassigned[i].JobPreferences.Count == 0) continue;
if (!unassigned[i].JobPreferences[0].AllowAlways) continue; if (!unassigned[i].JobPreferences[0].AllowAlways) continue;
unassigned[i].AssignedJob = unassigned[i].JobPreferences[0]; unassigned[i].AssignedJob = unassigned[i].JobPreferences[0];
unassigned.RemoveAt(i); unassigned.RemoveAt(i);
@@ -2138,47 +2166,49 @@ namespace Barotrauma.Networking
} }
} }
//find a suitable job for the rest of the players //attempt to give the clients a job they have in their job preferences
foreach (Client c in unassigned) 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 //the maximum number of players that can have this job hasn't been reached yet
// -> assign it to the client // -> 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]++; assignedClientCount[preferredJob]++;
unassigned.RemoveAt(i);
break; 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 //give random jobs to rest of the clients
if (remainingJobs.Count == 0) foreach (Client c in unassigned)
{ {
DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job..."); //find all jobs that are still available
int jobIndex = Rand.Range(0,JobPrefab.List.Count); var remainingJobs = JobPrefab.List.FindAll(jp => assignedClientCount[jp] < jp.MaxNumber && c.Karma >= jp.MinKarma);
int skips = 0;
while (c.Karma < JobPrefab.List[jobIndex].MinKarma) //all jobs taken, give a random job
{ if (remainingJobs.Count == 0)
jobIndex++; {
skips++; DebugConsole.ThrowError("Failed to assign a suitable job for \"" + c.Name + "\" (all jobs already have the maximum numbers of players). Assigning a random job...");
if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count; int jobIndex = Rand.Range(0, JobPrefab.List.Count);
if (skips >= JobPrefab.List.Count) break; int skips = 0;
} while (c.Karma < JobPrefab.List[jobIndex].MinKarma)
c.AssignedJob = JobPrefab.List[jobIndex]; {
assignedClientCount[c.AssignedJob]++; jobIndex++;
} skips++;
else //some jobs still left, choose one of them by random if (jobIndex >= JobPrefab.List.Count) jobIndex -= JobPrefab.List.Count;
{ if (skips >= JobPrefab.List.Count) break;
c.AssignedJob = remainingJobs[Rand.Range(0, remainingJobs.Count)];
assignedClientCount[c.AssignedJob]++;
}
} }
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("Shutting down the server...", ServerLog.MessageType.ServerMessage);
log.Save(); log.Save();
} }
if (GameSettings.SendUserStatistics)
{
GameAnalyticsSDK.Net.GameAnalytics.AddDesignEvent("GameServer:ShutDown");
}
server.Shutdown("The server has been shut down"); server.Shutdown("The server has been shut down");
} }
} }
@@ -254,6 +254,12 @@ namespace Barotrauma.Networking
set; set;
} }
public List<string> AllowedRandomMissionTypes
{
get;
set;
}
[Serialize(60f, true)] [Serialize(60f, true)]
public float AutoBanTime public float AutoBanTime
{ {
@@ -287,6 +293,8 @@ namespace Barotrauma.Networking
doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString()); doc.Root.SetAttributeValue("TraitorsEnabled", TraitorsEnabled.ToString());
doc.Root.SetAttributeValue("AllowedRandomMissionTypes", string.Join(",", AllowedRandomMissionTypes));
#if SERVER #if SERVER
doc.Root.SetAttributeValue("password", password); doc.Root.SetAttributeValue("password", password);
#endif #endif
@@ -334,18 +342,22 @@ namespace Barotrauma.Networking
#endif #endif
subSelectionMode = SelectionMode.Manual; 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; Voting.AllowSubVoting = subSelectionMode == SelectionMode.Vote;
modeSelectionMode = SelectionMode.Manual; 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; Voting.AllowModeVoting = modeSelectionMode == SelectionMode.Vote;
var traitorsEnabled = TraitorsEnabled; var traitorsEnabled = TraitorsEnabled;
Enum.TryParse<YesNoMaybe>(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled); Enum.TryParse(doc.Root.GetAttributeString("TraitorsEnabled", "No"), out traitorsEnabled);
TraitorsEnabled = traitorsEnabled; TraitorsEnabled = traitorsEnabled;
GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled); GameMain.NetLobbyScreen.SetTraitorsEnabled(traitorsEnabled);
AllowedRandomMissionTypes = doc.Root.GetAttributeStringArray(
"AllowedRandomMissionTypes",
MissionPrefab.MissionTypes.ToArray()).ToList();
if (GameMain.NetLobbyScreen != null if (GameMain.NetLobbyScreen != null
#if CLIENT #if CLIENT
&& GameMain.NetLobbyScreen.ServerMessage != null && GameMain.NetLobbyScreen.ServerMessage != null
@@ -204,8 +204,11 @@ namespace Barotrauma.Networking
respawnShuttle.Velocity = Vector2.Zero; respawnShuttle.Velocity = Vector2.Zero;
shuttleSteering.AutoPilot = false; if (shuttleSteering != null)
shuttleSteering.MaintainPos = false; {
shuttleSteering.AutoPilot = false;
shuttleSteering.MaintainPos = false;
}
} }
private void UpdateTransporting(float deltaTime) private void UpdateTransporting(float deltaTime)
@@ -264,7 +267,10 @@ namespace Barotrauma.Networking
respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier); respawnShuttle.PhysicsBody.FarseerBody.IgnoreCollisionWith(Level.Loaded.TopBarrier);
shuttleSteering.SetDestinationLevelStart(); if (shuttleSteering != null)
{
shuttleSteering.SetDestinationLevelStart();
}
foreach (Door door in shuttleDoors) foreach (Door door in shuttleDoors)
{ {
@@ -284,7 +290,7 @@ namespace Barotrauma.Networking
if (!CoroutineManager.IsCoroutineRunning("forcepos")) 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 && || (respawnShuttle.WorldPosition.Y + respawnShuttle.Borders.Y > Level.Loaded.StartPosition.Y - Level.ShaftHeight &&
Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f)) Math.Abs(Level.Loaded.StartPosition.X - respawnShuttle.WorldPosition.X) < 1000.0f))
{ {
@@ -323,7 +329,10 @@ namespace Barotrauma.Networking
ResetShuttle(); ResetShuttle();
shuttleSteering.TargetVelocity = Vector2.Zero; if (shuttleSteering != null)
{
shuttleSteering.TargetVelocity = Vector2.Zero;
}
GameServer.Log("Dispatching the respawn shuttle.", ServerLog.MessageType.Spawning); GameServer.Log("Dispatching the respawn shuttle.", ServerLog.MessageType.Spawning);
@@ -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(); var invalidChars = Path.GetInvalidFileNameChars();
foreach (char invalidChar in invalidChars) foreach (char invalidChar in invalidChars)
@@ -111,11 +111,11 @@ namespace Barotrauma
#endif #endif
break; break;
case VoteType.EndRound: case VoteType.EndRound:
if (sender.Character == null) return; if (!sender.HasSpawned) return;
sender.SetVote(voteType, inc.ReadBoolean()); sender.SetVote(voteType, inc.ReadBoolean());
GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.Character != null && c.GetVote<bool>(VoteType.EndRound)); GameMain.NetworkMember.EndVoteCount = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned && c.GetVote<bool>(VoteType.EndRound));
GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.Character != null); GameMain.NetworkMember.EndVoteMax = GameMain.Server.ConnectedClients.Count(c => c.HasSpawned);
break; break;
case VoteType.Kick: case VoteType.Kick:
+61
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 v0.8.1.2
--------------------------------------------------------------------------------------------------------- ---------------------------------------------------------------------------------------------------------