Compare commits
99 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
128728bfcb | ||
|
|
63b5d83728 | ||
|
|
bddd31b1b0 | ||
|
|
071ed3f1ed | ||
|
|
98bd1552b9 | ||
|
|
7554d51b0a | ||
|
|
3f89f3f0f7 | ||
|
|
b005d661f8 | ||
|
|
05d4b2793b | ||
|
|
cf29e4b7bd | ||
|
|
3dfce70fdc | ||
|
|
059832d324 | ||
|
|
1452502fbf | ||
|
|
1f22f8ab6a | ||
|
|
2eb30c9ba9 | ||
|
|
988b2b2bc5 | ||
|
|
5ee56a4cd3 | ||
|
|
37091d1254 | ||
|
|
68f4fd995e | ||
|
|
ba152fc61d | ||
|
|
074e6d8b62 | ||
|
|
283ed302db | ||
|
|
4b345c0ea6 | ||
|
|
fd4584519e | ||
|
|
de19cf652a | ||
|
|
7c840d71e2 | ||
|
|
0d0e6b2feb | ||
|
|
984617eed8 | ||
|
|
2d16205091 | ||
|
|
c41b867c7f | ||
|
|
7f196fc415 | ||
|
|
ec3e7968a6 | ||
|
|
82fbec02f0 | ||
|
|
9bd6200625 | ||
|
|
dac4f9cb3f | ||
|
|
8a11d2c649 | ||
|
|
2c99c8cb86 | ||
|
|
b6eb825c5c | ||
|
|
3601245d6f | ||
|
|
de46359742 | ||
|
|
de4583d14b | ||
|
|
60f975f0dc | ||
|
|
d38cc0ee82 | ||
|
|
36141f3b1a | ||
|
|
6f89d0672d | ||
|
|
7597cd9172 | ||
|
|
8c1fa84c6e | ||
|
|
7bc132bc0d | ||
|
|
ec4c864354 | ||
|
|
26e9c37be1 | ||
|
|
4c4687eb8e | ||
|
|
b8e01208af | ||
|
|
c7de994183 | ||
|
|
b0b220f085 | ||
|
|
5fd0e31920 | ||
|
|
bf10d34a8d | ||
|
|
1b44e84ef3 | ||
|
|
66ec5864d0 | ||
|
|
f40cffa746 | ||
|
|
4caa13f1c0 | ||
|
|
6864a457fe | ||
|
|
d345316db8 | ||
|
|
a2399f8842 | ||
|
|
92947a507c | ||
|
|
4f2d059de4 | ||
|
|
a464acf354 | ||
|
|
c85436118a | ||
|
|
fc1c29d14f | ||
|
|
17ea432604 | ||
|
|
3e0bcddd4d | ||
|
|
73ae408e20 | ||
|
|
66483bdd72 | ||
|
|
f6e265bccb | ||
|
|
60ff292f14 | ||
|
|
b2806dcb75 | ||
|
|
55b9e367b3 | ||
|
|
2d91344a5b | ||
|
|
eee05e1451 | ||
|
|
6d28ce7641 | ||
|
|
4e48f2d581 | ||
|
|
6d4e532ebe | ||
|
|
73c02f74f2 | ||
|
|
7add727db0 | ||
|
|
44cdd5ed55 | ||
|
|
7fda197391 | ||
|
|
8ca10e4f0b | ||
|
|
8a43a7022d | ||
|
|
e46e4a7993 | ||
|
|
3097106fe4 | ||
|
|
50cb6b4e30 | ||
|
|
e569eb276e | ||
|
|
17cd8c1d61 | ||
|
|
090fb0f417 | ||
|
|
e0941e2f45 | ||
|
|
b593220805 | ||
|
|
2b8d2d9c83 | ||
|
|
780fecff3b | ||
|
|
23381d936f | ||
|
|
a55ab49b6d |
2
.github/CODEOWNERS
vendored
@@ -1 +1 @@
|
||||
* @PJB3005
|
||||
* @PJB3005 @Silvertorch5
|
||||
|
||||
6
.gitignore
vendored
@@ -262,3 +262,9 @@ __pycache__/
|
||||
|
||||
# Visual Studio Code workspace settings.
|
||||
.vscode/
|
||||
|
||||
# Release package files go here:
|
||||
release/
|
||||
|
||||
# Apple please go.
|
||||
.DS_Store
|
||||
|
||||
3
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
||||
[submodule "engine"]
|
||||
path = engine
|
||||
url = https://github.com/space-wizards/space-station-14.git
|
||||
url = https://github.com/space-wizards/space-station-14.git
|
||||
branch = master
|
||||
@@ -16,12 +16,19 @@ https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild
|
||||
<PropertyGroup>
|
||||
<Python>python3</Python>
|
||||
<Python Condition="'$(OS)'=='Windows_NT' Or '$(OS)'=='Windows'">py -3</Python>
|
||||
<ProjectGuid>{C899FCA4-7037-4E49-ABC2-44DE72487110}</ProjectGuid>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<OutputType>Library</OutputType>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<StartupObject />
|
||||
</PropertyGroup>
|
||||
<Target Name="Build">
|
||||
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error"/>
|
||||
<Exec Command="$(Python) git_helper.py" CustomErrorRegularExpression="^Error" />
|
||||
</Target>
|
||||
<Target Name="Rebuild" DependsOnTargets="Build" />
|
||||
<Target Name="Clean">
|
||||
<Message Importance="low" Text="Ignoring 'Clean' target." />
|
||||
</Target>
|
||||
</Project>
|
||||
</Project>
|
||||
20
BuildFiles/Mac/Space Station 14.app/Contents/Info.plist
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>SS14</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>Space Station 14</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>SS14</string>
|
||||
<!--
|
||||
Just a note about this icon.
|
||||
MacOS seems REALLY iffy about this and even when the file is correct,
|
||||
it can take forever before it decides to actually update it and display it.
|
||||
TL;DR Apple is stupid.
|
||||
-->
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>ss14</string>
|
||||
</dict>
|
||||
</plist>
|
||||
9
BuildFiles/Mac/Space Station 14.app/Contents/MacOS/SS14
Executable file
@@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env -i bash
|
||||
|
||||
# cd to file containing script or something?
|
||||
BASEDIR=$(dirname "$0")
|
||||
echo "$BASEDIR"
|
||||
cd "$BASEDIR"
|
||||
|
||||
# TODO: unhardcode this, probably ship Mono with SS14.
|
||||
/Library/Frameworks/Mono.framework/Versions/Current/Commands/mono --arch=64 ./SS14.Client.exe
|
||||
BIN
BuildFiles/Mac/Space Station 14.app/Contents/Resources/ss14.icns
Normal file
@@ -11,32 +11,36 @@
|
||||
<AssemblyName>Content.Client</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ContentAssemblyTarget>..\bin\Client\Assemblies\</ContentAssemblyTarget>
|
||||
<ContentAssemblyTarget>..\bin\Client\Resources\Assemblies\</ContentAssemblyTarget>
|
||||
<!--
|
||||
This copies all dependencies,
|
||||
but on the plus side it's automatically located in the right place.
|
||||
-->
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<!--
|
||||
This copies all dependencies,
|
||||
but on the plus side it's automatically located in the right place.
|
||||
-->
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<!--
|
||||
This copies all dependencies,
|
||||
but on the plus side it's automatically located in the right place.
|
||||
-->
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -47,10 +51,20 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\OpenTK.3.0.0-pre\lib\net20\OpenTK.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="YamlDotNet, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\YamlDotNet.4.3.0\lib\net45\YamlDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EntryPoint.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\ClientHandsComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Doors\ClientDoorComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Items\IHandsComponent.cs" />
|
||||
<Compile Include="UserInterface\HandsGui.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj">
|
||||
@@ -84,4 +98,8 @@
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Client.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Shared.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
@@ -1,4 +1,8 @@
|
||||
using SS14.Shared.ContentPack;
|
||||
using Content.Client.GameObjects;
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using SS14.Shared.ContentPack;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
|
||||
namespace Content.Client
|
||||
{
|
||||
@@ -6,7 +10,36 @@ namespace Content.Client
|
||||
{
|
||||
public override void Init()
|
||||
{
|
||||
// TODO: Anything at all.
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
factory.RegisterIgnore("Inventory");
|
||||
factory.RegisterIgnore("Item");
|
||||
factory.RegisterIgnore("Interactable");
|
||||
factory.RegisterIgnore("Damageable");
|
||||
factory.RegisterIgnore("Destructible");
|
||||
factory.RegisterIgnore("Temperature");
|
||||
factory.RegisterIgnore("PowerTransfer");
|
||||
factory.RegisterIgnore("PowerNode");
|
||||
factory.RegisterIgnore("PowerProvider");
|
||||
factory.RegisterIgnore("PowerDevice");
|
||||
factory.RegisterIgnore("PowerStorage");
|
||||
factory.RegisterIgnore("PowerGenerator");
|
||||
|
||||
factory.RegisterIgnore("Wirecutter");
|
||||
factory.RegisterIgnore("Screwdriver");
|
||||
factory.RegisterIgnore("Multitool");
|
||||
factory.RegisterIgnore("Welder");
|
||||
factory.RegisterIgnore("Wrench");
|
||||
factory.RegisterIgnore("Crowbar");
|
||||
factory.RegisterIgnore("HitscanWeapon");
|
||||
factory.RegisterIgnore("ProjectileWeapon");
|
||||
factory.RegisterIgnore("Projectile");
|
||||
factory.RegisterIgnore("MeleeWeapon");
|
||||
|
||||
factory.Register<HandsComponent>();
|
||||
factory.RegisterReference<HandsComponent, IHandsComponent>();
|
||||
|
||||
factory.Register<ClientDoorComponent>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
using Content.Shared.GameObjects;
|
||||
using Lidgren.Network;
|
||||
using SS14.Client.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
public class ClientDoorComponent : SharedDoorComponent
|
||||
{
|
||||
public bool Opened { get; private set; }
|
||||
private SpriteComponent spriteComponent;
|
||||
|
||||
private string OpenSprite = "door_ewo";
|
||||
private string CloseSprite = "door_ew";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
spriteComponent = Owner.GetComponent<SpriteComponent>();
|
||||
}
|
||||
|
||||
private void Open()
|
||||
{
|
||||
Opened = true;
|
||||
spriteComponent.SetSpriteByKey(OpenSprite);
|
||||
}
|
||||
|
||||
private void Close()
|
||||
{
|
||||
Opened = false;
|
||||
spriteComponent.SetSpriteByKey(CloseSprite);
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState state)
|
||||
{
|
||||
var castState = (DoorComponentState)state;
|
||||
if (castState.Opened == Opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (castState.Opened)
|
||||
{
|
||||
Open();
|
||||
}
|
||||
else
|
||||
{
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
base.LoadParameters(mapping);
|
||||
|
||||
YamlNode node;
|
||||
if (mapping.TryGetNode("openstate", out node))
|
||||
{
|
||||
OpenSprite = node.AsString();
|
||||
}
|
||||
|
||||
if (mapping.TryGetNode("closestate", out node))
|
||||
{
|
||||
CloseSprite = node.AsString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using Content.Client.UserInterface;
|
||||
using Content.Shared.GameObjects;
|
||||
using SS14.Client.Interfaces.UserInterface;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Client.GameObjects
|
||||
{
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent
|
||||
{
|
||||
private readonly Dictionary<string, IEntity> hands = new Dictionary<string, IEntity>();
|
||||
public string ActiveIndex { get; private set; }
|
||||
|
||||
public IEntity GetEntity(string index)
|
||||
{
|
||||
if (hands.TryGetValue(index, out var entity))
|
||||
{
|
||||
return entity;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState state)
|
||||
{
|
||||
var cast = (HandsComponentState)state;
|
||||
hands.Clear();
|
||||
foreach (var hand in cast.Hands)
|
||||
{
|
||||
hands[hand.Key] = Owner.EntityManager.GetEntity(hand.Value);
|
||||
}
|
||||
|
||||
ActiveIndex = cast.ActiveIndex;
|
||||
|
||||
// Tell UI to update.
|
||||
var uiMgr = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
if (!uiMgr.TryGetSingleComponent<HandsGui>(out var component))
|
||||
{
|
||||
component = new HandsGui();
|
||||
uiMgr.AddComponent(component);
|
||||
}
|
||||
component.UpdateHandIcons();
|
||||
}
|
||||
|
||||
public void SendChangeHand(string index)
|
||||
{
|
||||
SendNetworkMessage(new ClientChangedHandMsg(index));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Client.Interfaces.GameObjects
|
||||
{
|
||||
// HYPER SIMPLE HANDS API CLIENT SIDE.
|
||||
// To allow for showing the HUD, mostly.
|
||||
public interface IHandsComponent
|
||||
{
|
||||
IEntity GetEntity(string index);
|
||||
string ActiveIndex { get; }
|
||||
|
||||
void SendChangeHand(string index);
|
||||
}
|
||||
}
|
||||
190
Content.Client/UserInterface/HandsGui.cs
Normal file
@@ -0,0 +1,190 @@
|
||||
using Content.Client.Interfaces.GameObjects;
|
||||
using SS14.Client.GameObjects;
|
||||
using SS14.Client.Graphics;
|
||||
using SS14.Client.Graphics.Input;
|
||||
using SS14.Client.Graphics.Sprites;
|
||||
using SS14.Client.Interfaces.Player;
|
||||
using SS14.Client.Interfaces.Resource;
|
||||
using SS14.Client.Interfaces.UserInterface;
|
||||
using SS14.Client.UserInterface.Controls;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Client.UserInterface
|
||||
{
|
||||
public class HandsGui : Control
|
||||
{
|
||||
private readonly Color _inactiveColor = new Color(90, 90, 90);
|
||||
|
||||
private readonly IPlayerManager _playerManager = IoCManager.Resolve<IPlayerManager>();
|
||||
private readonly IUserInterfaceManager _userInterfaceManager = IoCManager.Resolve<IUserInterfaceManager>();
|
||||
private readonly Sprite handSlot;
|
||||
private readonly int spacing = 1;
|
||||
|
||||
private UiHandInfo LeftHand;
|
||||
private UiHandInfo RightHand;
|
||||
private Box2i handL;
|
||||
private Box2i handR;
|
||||
|
||||
public HandsGui()
|
||||
{
|
||||
var _resMgr = IoCManager.Resolve<IResourceCache>();
|
||||
handSlot = _resMgr.GetSprite("hand");
|
||||
// OnCalcRect() calculates position so this needs to be ran
|
||||
// as it doesn't automatically get called by the UI manager.
|
||||
DoLayout();
|
||||
}
|
||||
|
||||
protected override void OnCalcRect()
|
||||
{
|
||||
// Individual size of the hand slot sprite.
|
||||
var slotBounds = handSlot.LocalBounds;
|
||||
var width = (int)((slotBounds.Width * 2) + spacing);
|
||||
var height = (int)slotBounds.Height;
|
||||
|
||||
// Force size because refactoring is HARD.
|
||||
Size = new Vector2i(width, height);
|
||||
ClientArea = Box2i.FromDimensions(0, 0, Width, Height);
|
||||
|
||||
// Hell force position too what could go wrong!
|
||||
Position = new Vector2i((int)(CluwneLib.Window.Viewport.Width - width) / 2, (int)CluwneLib.Window.Viewport.Height - height - 10);
|
||||
handL = Box2i.FromDimensions(Position.X, Position.Y, (int)slotBounds.Width, (int)slotBounds.Height);
|
||||
handR = Box2i.FromDimensions(Position.X + (int)slotBounds.Width + spacing, Position.Y, (int)slotBounds.Width, (int)slotBounds.Height);
|
||||
}
|
||||
|
||||
protected override void DrawContents()
|
||||
{
|
||||
if (_playerManager?.LocalPlayer == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEntity entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (entity == null || !entity.TryGetComponent<IHandsComponent>(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var leftActive = hands.ActiveIndex == "left";
|
||||
|
||||
handSlot.Color = Color.White;
|
||||
handSlot.SetTransformToRect(leftActive ? handL : handR);
|
||||
handSlot.Draw();
|
||||
|
||||
handSlot.Color = _inactiveColor;
|
||||
handSlot.SetTransformToRect(leftActive ? handR : handL);
|
||||
handSlot.Draw();
|
||||
|
||||
if (LeftHand.Entity != null && LeftHand.HeldSprite != null)
|
||||
{
|
||||
var bounds = LeftHand.HeldSprite.LocalBounds;
|
||||
LeftHand.HeldSprite.SetTransformToRect(
|
||||
Box2i.FromDimensions(handL.Left + (int)(handL.Width / 2f - bounds.Width / 2f),
|
||||
handL.Top + (int)(handL.Height / 2f - bounds.Height / 2f),
|
||||
(int)bounds.Width, (int)bounds.Height));
|
||||
LeftHand.HeldSprite.Draw();
|
||||
}
|
||||
|
||||
if (RightHand.Entity != null && RightHand.HeldSprite != null)
|
||||
{
|
||||
var bounds = RightHand.HeldSprite.LocalBounds;
|
||||
RightHand.HeldSprite.SetTransformToRect(
|
||||
Box2i.FromDimensions(handR.Left + (int)(handR.Width / 2f - bounds.Width / 2f),
|
||||
handR.Top + (int)(handR.Height / 2f - bounds.Height / 2f),
|
||||
(int)bounds.Width, (int)bounds.Height));
|
||||
RightHand.HeldSprite.Draw();
|
||||
}
|
||||
}
|
||||
|
||||
public void UpdateHandIcons()
|
||||
{
|
||||
if (_playerManager?.LocalPlayer.ControlledEntity == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
IEntity entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (!entity.TryGetComponent<IHandsComponent>(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var left = hands.GetEntity("left");
|
||||
var right = hands.GetEntity("right");
|
||||
|
||||
if (left != null)
|
||||
{
|
||||
if (left != LeftHand.Entity)
|
||||
{
|
||||
LeftHand.Entity = left;
|
||||
LeftHand.HeldSprite = GetIconSprite(left);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LeftHand.Entity = null;
|
||||
LeftHand.HeldSprite = null;
|
||||
}
|
||||
|
||||
if (right != null)
|
||||
{
|
||||
if (right != RightHand.Entity)
|
||||
{
|
||||
RightHand.Entity = right;
|
||||
RightHand.HeldSprite = GetIconSprite(right);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RightHand.Entity = null;
|
||||
RightHand.HeldSprite = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void SendSwitchHandTo(string index)
|
||||
{
|
||||
IEntity entity = _playerManager.LocalPlayer.ControlledEntity;
|
||||
if (!entity.TryGetComponent<IHandsComponent>(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
hands.SendChangeHand(index);
|
||||
}
|
||||
|
||||
public override bool MouseDown(MouseButtonEventArgs e)
|
||||
{
|
||||
if (e.Button != Mouse.Button.Right)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (handL.Contains(e.X, e.Y))
|
||||
{
|
||||
SendSwitchHandTo("left");
|
||||
return true;
|
||||
}
|
||||
if (handR.Contains(e.X, e.Y))
|
||||
{
|
||||
SendSwitchHandTo("right");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private static Sprite GetIconSprite(IEntity entity)
|
||||
{
|
||||
Sprite icon = null;
|
||||
if (entity.TryGetComponent<IconComponent>(out var component))
|
||||
{
|
||||
icon = component.Icon;
|
||||
}
|
||||
return icon ?? IoCManager.Resolve<IResourceCache>().DefaultSprite();
|
||||
}
|
||||
|
||||
private struct UiHandInfo
|
||||
{
|
||||
public IEntity Entity { get; set; }
|
||||
public Sprite HeldSprite { get; set; }
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Content.Client/app.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.86.0.518" newVersion="0.86.0.518" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
6
Content.Client/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="OpenTK" version="3.0.0-pre" targetFramework="net451" />
|
||||
<package id="System.ValueTuple" version="4.3.1" targetFramework="net451" />
|
||||
<package id="YamlDotNet" version="4.3.0" targetFramework="net451" />
|
||||
</packages>
|
||||
170
Content.Server/AI/AimShootLifeProcessor.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Server.AI;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||
using SS14.Shared.Interfaces.Physics;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server.AI
|
||||
{
|
||||
/// <summary>
|
||||
/// The object stays stationary. The object will periodically scan for *any* life forms in its radius, and engage them.
|
||||
/// The object will rotate itself to point at the locked entity, and if it has a weapon will shoot at the entity.
|
||||
/// </summary>
|
||||
[AiLogicProcessor("AimShootLife")]
|
||||
class AimShootLifeProcessor : AiLogicProcessor
|
||||
{
|
||||
private readonly ICollisionManager _physMan;
|
||||
private readonly IServerEntityManager _entMan;
|
||||
private readonly IGameTiming _timeMan;
|
||||
|
||||
private readonly List<IEntity> _workList = new List<IEntity>();
|
||||
|
||||
private const float MaxAngSpeed = (float) (Math.PI / 2); // how fast our turret can rotate
|
||||
private const float ScanPeriod = 1.0f; // tweak this for performance and gameplay experience
|
||||
private float _lastScan;
|
||||
|
||||
private IEntity _curTarget;
|
||||
|
||||
/// <summary>
|
||||
/// Creates an instance of this LogicProcessor.
|
||||
/// </summary>
|
||||
public AimShootLifeProcessor()
|
||||
{
|
||||
_physMan = IoCManager.Resolve<ICollisionManager>();
|
||||
_entMan = IoCManager.Resolve<IServerEntityManager>();
|
||||
_timeMan = IoCManager.Resolve<IGameTiming>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (SelfEntity == null)
|
||||
return;
|
||||
|
||||
DoScanning();
|
||||
DoTracking(frameTime);
|
||||
}
|
||||
|
||||
private void DoScanning()
|
||||
{
|
||||
var curTime = _timeMan.CurTime.TotalSeconds;
|
||||
if (curTime - _lastScan > ScanPeriod)
|
||||
{
|
||||
_lastScan = (float) curTime;
|
||||
_curTarget = FindBestTarget();
|
||||
}
|
||||
}
|
||||
|
||||
private void DoTracking(float frameTime)
|
||||
{
|
||||
// not valid entity to target.
|
||||
if (_curTarget == null || !_curTarget.IsValid())
|
||||
{
|
||||
_curTarget = null;
|
||||
return;
|
||||
}
|
||||
|
||||
// point me at the target
|
||||
var tarPos = _curTarget.GetComponent<ITransformComponent>().WorldPosition;
|
||||
var myPos = SelfEntity.GetComponent<ITransformComponent>().WorldPosition;
|
||||
|
||||
var curDir = SelfEntity.GetComponent<IServerTransformComponent>().LocalRotation.ToVec();
|
||||
var tarDir = (tarPos - myPos).Normalized;
|
||||
|
||||
var fwdAng = Vector2.Dot(curDir, tarDir);
|
||||
|
||||
Vector2 newDir;
|
||||
if (fwdAng < 0) // target behind turret, just rotate in a direction to get target in front
|
||||
{
|
||||
var curRight = new Vector2(-curDir.Y, curDir.X); // right handed coord system
|
||||
var rightAngle = Vector2.Dot(curDir, new Vector2(-tarDir.Y, tarDir.X)); // right handed coord system
|
||||
var rotateSign = -Math.Sign(rightAngle);
|
||||
newDir = curDir + curRight * rotateSign * MaxAngSpeed * frameTime;
|
||||
}
|
||||
else // target in front, adjust to aim at him
|
||||
{
|
||||
newDir = MoveTowards(curDir, tarDir, MaxAngSpeed, frameTime);
|
||||
}
|
||||
|
||||
SelfEntity.GetComponent<IServerTransformComponent>().LocalRotation = new Angle(newDir);
|
||||
|
||||
if (fwdAng > -0.9999)
|
||||
{
|
||||
// TODO: shoot gun, prob need aimbot because entity rotation lags behind moving target
|
||||
}
|
||||
}
|
||||
|
||||
private IEntity FindBestTarget()
|
||||
{
|
||||
// "best" target is the closest one with LOS
|
||||
|
||||
var ents = _entMan.GetEntitiesInRange(SelfEntity, VisionRadius);
|
||||
var myTransform = SelfEntity.GetComponent<IServerTransformComponent>();
|
||||
var maxRayLen = VisionRadius * 2.5f; // circle inscribed in square, square diagonal = 2*r*sqrt(2)
|
||||
|
||||
_workList.Clear();
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
// filter to "people" entities (entities with controllers)
|
||||
if (!entity.HasComponent<IMoverComponent>())
|
||||
continue;
|
||||
|
||||
// build the ray
|
||||
var dir = entity.GetComponent<TransformComponent>().WorldPosition - myTransform.WorldPosition;
|
||||
var ray = new Ray(myTransform.WorldPosition, dir.Normalized);
|
||||
|
||||
// cast the ray
|
||||
var result = _physMan.IntersectRay(ray, maxRayLen);
|
||||
|
||||
// add to visible list
|
||||
if (result.HitEntity == entity)
|
||||
_workList.Add(entity);
|
||||
}
|
||||
|
||||
// get closest entity in list
|
||||
var closestEnt = GetClosest(myTransform.WorldPosition, _workList);
|
||||
|
||||
// return closest
|
||||
return closestEnt;
|
||||
}
|
||||
|
||||
private static IEntity GetClosest(Vector2 origin, IEnumerable<IEntity> list)
|
||||
{
|
||||
IEntity closest = null;
|
||||
var minDistSqrd = float.PositiveInfinity;
|
||||
|
||||
foreach (var ent in list)
|
||||
{
|
||||
var pos = ent.GetComponent<ITransformComponent>().WorldPosition;
|
||||
var distSqrd = (pos - origin).LengthSquared;
|
||||
|
||||
if (distSqrd > minDistSqrd)
|
||||
continue;
|
||||
|
||||
closest = ent;
|
||||
minDistSqrd = distSqrd;
|
||||
}
|
||||
|
||||
return closest;
|
||||
}
|
||||
|
||||
private static Vector2 MoveTowards(Vector2 current, Vector2 target, float speed, float delta)
|
||||
{
|
||||
var maxDeltaDist = speed * delta;
|
||||
var a = target - current;
|
||||
var magnitude = a.Length;
|
||||
if (magnitude <= maxDeltaDist)
|
||||
{
|
||||
return target;
|
||||
}
|
||||
|
||||
return current + a / magnitude * maxDeltaDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,24 +11,32 @@
|
||||
<AssemblyName>Content.Server</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<ContentAssemblyTarget>..\bin\Server\Assemblies\</ContentAssemblyTarget>
|
||||
<ContentAssemblyTarget>..\bin\Server\Resources\Assemblies\</ContentAssemblyTarget>
|
||||
<OutputPath>..\bin\Content.Server\</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>..\bin\Content.Server\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>..\bin\Content.Server\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -39,10 +47,53 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\OpenTK.3.0.0-pre\lib\net20\OpenTK.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="YamlDotNet, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\YamlDotNet.4.3.0\lib\net45\YamlDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="AI\AimShootLifeProcessor.cs" />
|
||||
<Compile Include="EntryPoint.cs" />
|
||||
<Compile Include="GameObjects\Components\Doors\ServerDoorComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\BaseTool.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\CrowbarComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\MultitoolComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\ScrewdriverComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WelderComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WirecutterComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Interactable\Tools\WrenchComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerStorageComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerGeneratorComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerDevice.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\Powernet.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerNodeComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerProviderComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Power\PowerTransferComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Projectiles\ProjectileComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Weapon\Melee\MeleeWeaponComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Weapon\Ranged\Hitscan\HitscanWeaponComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Weapon\Ranged\Projectile\ProjectileWeapon.cs" />
|
||||
<Compile Include="GameObjects\Components\Weapon\Ranged\RangedWeapon.cs" />
|
||||
<Compile Include="GameObjects\EntitySystems\InteractionSystem.cs" />
|
||||
<Compile Include="GameObjects\EntitySystems\PowerSystem.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Items\IHandsComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Items\IInventoryComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Items\IItemComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\ServerHandsComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\InventoryComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\ItemComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\DamageableComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\DestructibleComponent.cs" />
|
||||
<Compile Include="GameObjects\Components\Damage\ResistanceSet.cs" />
|
||||
<Compile Include="GameObjects\Components\Temperature\TemperatureComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\IOnDamageBehavior.cs" />
|
||||
<Compile Include="Placement\SpawnHelpers.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Temperature\ITemperatureComponent.cs" />
|
||||
<Compile Include="Interfaces\GameObjects\Components\Damage\IDamageableComponent.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj">
|
||||
@@ -71,4 +122,9 @@
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Server.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
<ContentAssemblies Include="$(OutputPath)Content.Shared.pdb" Condition="'$(Configuration)' == 'Debug'" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
</Project>
|
||||
@@ -1,12 +1,182 @@
|
||||
using SS14.Shared.ContentPack;
|
||||
using Content.Server.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using Content.Server.GameObjects.Components.Interactable.Tools;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Server.Placement;
|
||||
using SS14.Server;
|
||||
using SS14.Server.Interfaces;
|
||||
using SS14.Server.Interfaces.Chat;
|
||||
using SS14.Server.Interfaces.Maps;
|
||||
using SS14.Server.Interfaces.Player;
|
||||
using SS14.Server.Player;
|
||||
using SS14.Shared.Console;
|
||||
using SS14.Shared.ContentPack;
|
||||
using SS14.Shared.Enums;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.Interfaces.Timers;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Timers;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.Maths;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan;
|
||||
using Content.Server.GameObjects.Components.Weapon.Ranged.Projectile;
|
||||
using Content.Server.GameObjects.Components.Projectiles;
|
||||
using Content.Server.GameObjects.Components.Weapon.Melee;
|
||||
|
||||
namespace Content.Server
|
||||
{
|
||||
public class EntryPoint : GameServer
|
||||
{
|
||||
private IBaseServer _server;
|
||||
private IPlayerManager _players;
|
||||
|
||||
private bool _countdownStarted;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Init()
|
||||
{
|
||||
// TODO: Anything at all.
|
||||
base.Init();
|
||||
|
||||
_server = IoCManager.Resolve<IBaseServer>();
|
||||
_players = IoCManager.Resolve<IPlayerManager>();
|
||||
|
||||
_server.RunLevelChanged += HandleRunLevelChanged;
|
||||
_players.PlayerStatusChanged += HandlePlayerStatusChanged;
|
||||
_players.PlayerPrototypeName = "HumanMob_Content";
|
||||
|
||||
var factory = IoCManager.Resolve<IComponentFactory>();
|
||||
|
||||
factory.Register<HandsComponent>();
|
||||
factory.RegisterReference<HandsComponent, IHandsComponent>();
|
||||
|
||||
factory.Register<InventoryComponent>();
|
||||
factory.RegisterReference<InventoryComponent, IInventoryComponent>();
|
||||
|
||||
factory.Register<ItemComponent>();
|
||||
factory.RegisterReference<ItemComponent, IItemComponent>();
|
||||
|
||||
factory.Register<DamageableComponent>();
|
||||
factory.Register<DestructibleComponent>();
|
||||
factory.Register<TemperatureComponent>();
|
||||
factory.Register<ServerDoorComponent>();
|
||||
|
||||
//Power Components
|
||||
factory.Register<PowerTransferComponent>();
|
||||
factory.Register<PowerProviderComponent>();
|
||||
factory.Register<PowerNodeComponent>();
|
||||
factory.Register<PowerStorageComponent>();
|
||||
factory.Register<PowerDeviceComponent>();
|
||||
factory.Register<PowerGeneratorComponent>();
|
||||
|
||||
//Tools
|
||||
factory.Register<MultitoolComponent>();
|
||||
factory.Register<WirecutterComponent>();
|
||||
factory.Register<WrenchComponent>();
|
||||
factory.Register<WelderComponent>();
|
||||
factory.Register<ScrewdriverComponent>();
|
||||
factory.Register<CrowbarComponent>();
|
||||
|
||||
factory.Register<HitscanWeaponComponent>();
|
||||
factory.Register<ProjectileWeaponComponent>();
|
||||
factory.Register<ProjectileComponent>();
|
||||
factory.Register<MeleeWeaponComponent>();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Dispose()
|
||||
{
|
||||
_server.RunLevelChanged -= HandleRunLevelChanged;
|
||||
_players.PlayerStatusChanged -= HandlePlayerStatusChanged;
|
||||
|
||||
base.Dispose();
|
||||
}
|
||||
|
||||
private static void HandleRunLevelChanged(object sender, RunLevelChangedEventArgs args)
|
||||
{
|
||||
switch (args.NewLevel)
|
||||
{
|
||||
case ServerRunLevel.PreGame:
|
||||
var timing = IoCManager.Resolve<IGameTiming>();
|
||||
|
||||
IoCManager.Resolve<IPlayerManager>().FallbackSpawnPoint = new LocalCoordinates(0, 0, GridId.DefaultGrid, new MapId(2));
|
||||
|
||||
var mapLoader = IoCManager.Resolve<IMapLoader>();
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
var startTime = timing.RealTime;
|
||||
{
|
||||
var newMap = mapMan.CreateMap(new MapId(2));
|
||||
|
||||
mapLoader.LoadBlueprint(newMap, new GridId(4), "Maps/Demo/DemoGrid.yaml");
|
||||
|
||||
var grid = newMap.GetGrid(new GridId(4));
|
||||
SpawnHelpers.SpawnLightTurret(grid, new Vector2(-15, 15));
|
||||
}
|
||||
var timeSpan = timing.RealTime - startTime;
|
||||
Logger.Info($"Loaded map in {timeSpan.TotalMilliseconds:N2}ms.");
|
||||
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Round loaded!");
|
||||
break;
|
||||
case ServerRunLevel.Game:
|
||||
IoCManager.Resolve<IPlayerManager>().SendJoinGameToAll();
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Round started!");
|
||||
break;
|
||||
case ServerRunLevel.PostGame:
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Round over!");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void HandlePlayerStatusChanged(object sender, SessionStatusEventArgs args)
|
||||
{
|
||||
switch (args.NewStatus)
|
||||
{
|
||||
case SessionStatus.Connected:
|
||||
{
|
||||
// timer time must be > tick length
|
||||
IoCManager.Resolve<ITimerManager>().AddTimer(new Timer(250, false, () =>
|
||||
{
|
||||
args.Session.JoinLobby();
|
||||
}));
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Player joined server!", args.Session.Index);
|
||||
}
|
||||
break;
|
||||
|
||||
case SessionStatus.InLobby:
|
||||
{
|
||||
// auto start game when first player joins
|
||||
if (_server.RunLevel == ServerRunLevel.PreGame && !_countdownStarted)
|
||||
{
|
||||
_countdownStarted = true;
|
||||
IoCManager.Resolve<ITimerManager>().AddTimer(new Timer(2000, false, () =>
|
||||
{
|
||||
_server.RunLevel = ServerRunLevel.Game;
|
||||
_countdownStarted = false;
|
||||
}));
|
||||
}
|
||||
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Player joined Lobby!", args.Session.Index);
|
||||
}
|
||||
break;
|
||||
|
||||
case SessionStatus.InGame:
|
||||
{
|
||||
//TODO: Check for existing mob and re-attach
|
||||
IoCManager.Resolve<IPlayerManager>().SpawnPlayerMob(args.Session);
|
||||
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Player joined Game!", args.Session.Index);
|
||||
}
|
||||
break;
|
||||
|
||||
case SessionStatus.Disconnected:
|
||||
{
|
||||
IoCManager.Resolve<IChatManager>().DispatchMessage(ChatChannel.Server, "Gamemode: Player left!", args.Session.Index);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,197 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using OpenTK;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
//TODO: add support for component add/remove
|
||||
|
||||
/// <summary>
|
||||
/// A component that handles receiving damage and healing,
|
||||
/// as well as informing other components of it.
|
||||
/// </summary>
|
||||
public class DamageableComponent : Component, IDamageableComponent
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Damageable";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint? NetID => ContentNetIDs.DAMAGEABLE;
|
||||
|
||||
/// <summary>
|
||||
/// The resistance set of this object.
|
||||
/// Affects receiving damage of various types.
|
||||
/// </summary>
|
||||
public ResistanceSet Resistances { get; private set; }
|
||||
|
||||
Dictionary<DamageType, int> CurrentDamage = new Dictionary<DamageType, int>();
|
||||
Dictionary<DamageType, List<int>> Thresholds = new Dictionary<DamageType, List<int>>();
|
||||
|
||||
public event EventHandler<DamageThresholdPassedEventArgs> DamageThresholdPassed;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("resistanceset", out YamlNode node))
|
||||
{
|
||||
Resistances = ResistanceSet.GetResistanceSet(node.AsString());
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
InitializeDamageType(DamageType.Total);
|
||||
if (Owner is IOnDamageBehavior damageBehavior)
|
||||
{
|
||||
AddThresholdsFrom(damageBehavior);
|
||||
}
|
||||
|
||||
RecalculateComponentThresholds();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TakeDamage(DamageType damageType, int amount)
|
||||
{
|
||||
if (damageType == DamageType.Total)
|
||||
{
|
||||
throw new ArgumentException("Cannot take damage for DamageType.Total");
|
||||
}
|
||||
InitializeDamageType(damageType);
|
||||
|
||||
int oldValue = CurrentDamage[damageType];
|
||||
int oldTotalValue = -1;
|
||||
|
||||
if (amount == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
amount = Resistances.CalculateDamage(damageType, amount);
|
||||
CurrentDamage[damageType] = Math.Max(0, CurrentDamage[damageType] + amount);
|
||||
UpdateForDamageType(damageType, oldValue);
|
||||
|
||||
if (Resistances.AppliesToTotal(damageType))
|
||||
{
|
||||
oldTotalValue = CurrentDamage[DamageType.Total];
|
||||
CurrentDamage[DamageType.Total] = Math.Max(0, CurrentDamage[DamageType.Total] + amount);
|
||||
UpdateForDamageType(DamageType.Total, oldTotalValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void TakeHealing(DamageType damageType, int amount)
|
||||
{
|
||||
if (damageType == DamageType.Total)
|
||||
{
|
||||
throw new ArgumentException("Cannot heal for DamageType.Total");
|
||||
}
|
||||
TakeDamage(damageType, -amount);
|
||||
}
|
||||
|
||||
void UpdateForDamageType(DamageType damageType, int oldValue)
|
||||
{
|
||||
int change = CurrentDamage[damageType] - oldValue;
|
||||
|
||||
if (change == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int changeSign = Math.Sign(change);
|
||||
|
||||
foreach (int value in Thresholds[damageType])
|
||||
{
|
||||
if (((value * changeSign) > (oldValue * changeSign)) && ((value * changeSign) <= (CurrentDamage[damageType] * changeSign)))
|
||||
{
|
||||
var args = new DamageThresholdPassedEventArgs(new DamageThreshold(damageType, value), (changeSign > 0));
|
||||
DamageThresholdPassed?.Invoke(this, args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RecalculateComponentThresholds()
|
||||
{
|
||||
foreach (IOnDamageBehavior onDamageBehaviorComponent in Owner.GetComponents<IOnDamageBehavior>())
|
||||
{
|
||||
AddThresholdsFrom(onDamageBehaviorComponent);
|
||||
}
|
||||
}
|
||||
|
||||
void AddThresholdsFrom(IOnDamageBehavior onDamageBehavior)
|
||||
{
|
||||
if (onDamageBehavior == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(onDamageBehavior));
|
||||
}
|
||||
|
||||
List<DamageThreshold> thresholds = onDamageBehavior.GetAllDamageThresholds();
|
||||
|
||||
foreach (DamageThreshold threshold in thresholds)
|
||||
{
|
||||
if (!Thresholds[threshold.DamageType].Contains(threshold.Value))
|
||||
{
|
||||
Thresholds[threshold.DamageType].Add(threshold.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void InitializeDamageType(DamageType damageType)
|
||||
{
|
||||
if (!CurrentDamage.ContainsKey(damageType))
|
||||
{
|
||||
CurrentDamage.Add(damageType, 0);
|
||||
Thresholds.Add(damageType, new List<int>());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public struct DamageThreshold
|
||||
{
|
||||
public DamageType DamageType { get; }
|
||||
public int Value { get; }
|
||||
|
||||
public DamageThreshold(DamageType damageType, int value)
|
||||
{
|
||||
DamageType = damageType;
|
||||
Value = value;
|
||||
}
|
||||
|
||||
public override bool Equals(Object obj)
|
||||
{
|
||||
return obj is DamageThreshold && this == (DamageThreshold)obj;
|
||||
}
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return DamageType.GetHashCode() ^ Value.GetHashCode();
|
||||
}
|
||||
public static bool operator ==(DamageThreshold x, DamageThreshold y)
|
||||
{
|
||||
return x.DamageType == y.DamageType && x.Value == y.Value;
|
||||
}
|
||||
public static bool operator !=(DamageThreshold x, DamageThreshold y)
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
}
|
||||
|
||||
public class DamageThresholdPassedEventArgs : EventArgs
|
||||
{
|
||||
public DamageThreshold DamageThreshold { get; }
|
||||
public bool Passed { get; }
|
||||
|
||||
public DamageThresholdPassedEventArgs(DamageThreshold threshold, bool passed)
|
||||
{
|
||||
DamageThreshold = threshold;
|
||||
Passed = passed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using Content.Server.Interfaces;
|
||||
using Content.Shared.GameObjects;
|
||||
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Deletes the entity once a certain damage threshold has been reached.
|
||||
/// </summary>
|
||||
public class DestructibleComponent : Component, IOnDamageBehavior
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Destructible";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint? NetID => ContentNetIDs.DESTRUCTIBLE;
|
||||
|
||||
/// <summary>
|
||||
/// Damage threshold calculated from the values
|
||||
/// given in the prototype declaration.
|
||||
/// </summary>
|
||||
public DamageThreshold Threshold { get; private set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
//TODO currently only supports one threshold pair; gotta figure out YAML better
|
||||
|
||||
YamlNode node;
|
||||
|
||||
DamageType damageType = DamageType.Total;
|
||||
int damageValue = 0;
|
||||
|
||||
if (mapping.TryGetNode("thresholdtype", out node))
|
||||
{
|
||||
damageType = node.AsEnum<DamageType>();
|
||||
}
|
||||
if (mapping.TryGetNode("thresholdvalue", out node))
|
||||
{
|
||||
damageValue = node.AsInt();
|
||||
}
|
||||
|
||||
Threshold = new DamageThreshold(damageType, damageValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (Owner.TryGetComponent<DamageableComponent>(out DamageableComponent damageable))
|
||||
{
|
||||
damageable.DamageThresholdPassed += OnDamageThresholdPassed;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<DamageThreshold> GetAllDamageThresholds()
|
||||
{
|
||||
return new List<DamageThreshold>() { Threshold };
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void OnDamageThresholdPassed(object obj, DamageThresholdPassedEventArgs e)
|
||||
{
|
||||
if (e.Passed && e.DamageThreshold == Threshold)
|
||||
{
|
||||
Owner.EntityManager.DeleteEntity(Owner);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
115
Content.Server/GameObjects/Components/Damage/ResistanceSet.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Damage types used in-game.
|
||||
/// Total should never be used directly - it's a derived value.
|
||||
/// </summary>
|
||||
public enum DamageType
|
||||
{
|
||||
Total,
|
||||
Brute,
|
||||
Heat,
|
||||
Cold,
|
||||
Acid,
|
||||
Toxic,
|
||||
Electric
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Resistance set used by damageable objects.
|
||||
/// For each damage type, has a coefficient, damage reduction and "included in total" value.
|
||||
/// </summary>
|
||||
public class ResistanceSet
|
||||
{
|
||||
Dictionary<DamageType, ResistanceSetSettings> _resistances = new Dictionary<DamageType, ResistanceSetSettings>();
|
||||
static Dictionary<string, ResistanceSet> _resistanceSets = new Dictionary<string, ResistanceSet>();
|
||||
|
||||
//TODO: make it load from YAML instead of hardcoded like this
|
||||
public ResistanceSet()
|
||||
{
|
||||
_resistances.Add(DamageType.Total, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Acid, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Brute, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Heat, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Cold, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Toxic, new ResistanceSetSettings(1f, 0, true));
|
||||
_resistances.Add(DamageType.Electric, new ResistanceSetSettings(1f, 0, true));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Loads a resistance set with the given name.
|
||||
/// </summary>
|
||||
/// <param name="setName">Name of the resistance set.</param>
|
||||
/// <returns>Resistance set by given name</returns>
|
||||
public static ResistanceSet GetResistanceSet(string setName)
|
||||
{
|
||||
ResistanceSet resistanceSet = null;
|
||||
|
||||
if (!_resistanceSets.TryGetValue(setName, out resistanceSet))
|
||||
{
|
||||
resistanceSet = Load(setName);
|
||||
}
|
||||
|
||||
return resistanceSet;
|
||||
}
|
||||
|
||||
static ResistanceSet Load(string setName)
|
||||
{
|
||||
//TODO: only creates a standard set RN, should be YAMLed
|
||||
|
||||
ResistanceSet resistanceSet = new ResistanceSet();
|
||||
|
||||
_resistanceSets.Add(setName, resistanceSet);
|
||||
|
||||
return resistanceSet;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adjusts input damage with the resistance set values.
|
||||
/// </summary>
|
||||
/// <param name="damageType">Type of the damage.</param>
|
||||
/// <param name="amount">Incoming amount of the damage.</param>
|
||||
/// <returns>Damage adjusted by the resistance set.</returns>
|
||||
public int CalculateDamage(DamageType damageType, int amount)
|
||||
{
|
||||
if (amount > 0) //if it's damage, reduction applies
|
||||
{
|
||||
amount -= _resistances[damageType].DamageReduction;
|
||||
|
||||
if (amount <= 0)
|
||||
return 0;
|
||||
}
|
||||
|
||||
amount = (int)Math.Floor(amount * _resistances[damageType].Coefficient);
|
||||
|
||||
return amount;
|
||||
}
|
||||
|
||||
public bool AppliesToTotal(DamageType damageType)
|
||||
{
|
||||
//Damage that goes straight to total (for whatever reason) never applies twice
|
||||
|
||||
return damageType == DamageType.Total ? false : _resistances[damageType].AppliesToTotal;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Settings for a specific damage type in a resistance set.
|
||||
/// </summary>
|
||||
struct ResistanceSetSettings
|
||||
{
|
||||
public float Coefficient { get; private set; }
|
||||
public int DamageReduction { get; private set; }
|
||||
public bool AppliesToTotal { get; private set; }
|
||||
|
||||
public ResistanceSetSettings(float coefficient, int damageReduction, bool appliesInTotal)
|
||||
{
|
||||
Coefficient = coefficient;
|
||||
DamageReduction = damageReduction;
|
||||
AppliesToTotal = appliesInTotal;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using System;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.IoC;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class ServerDoorComponent : SharedDoorComponent, IAttackHand
|
||||
{
|
||||
public bool Opened { get; private set; }
|
||||
|
||||
private float OpenTimeCounter;
|
||||
|
||||
private CollidableComponent collidableComponent;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
collidableComponent = Owner.GetComponent<CollidableComponent>();
|
||||
collidableComponent.OnBump += OnBump;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
collidableComponent.OnBump -= OnBump;
|
||||
collidableComponent = null;
|
||||
}
|
||||
|
||||
public bool Attackhand(IEntity user)
|
||||
{
|
||||
if (Opened)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
else
|
||||
{
|
||||
Open();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnBump(object sender, BumpEventArgs args)
|
||||
{
|
||||
Logger.Info("Bump!");
|
||||
if (Opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Open();
|
||||
}
|
||||
|
||||
public void Open()
|
||||
{
|
||||
Opened = true;
|
||||
collidableComponent.IsHardCollidable = false;
|
||||
}
|
||||
|
||||
public bool Close()
|
||||
{
|
||||
if (collidableComponent.TryCollision(Vector2.Zero))
|
||||
{
|
||||
// Do nothing, somebody's in the door.
|
||||
return false;
|
||||
}
|
||||
Opened = false;
|
||||
OpenTimeCounter = 0;
|
||||
collidableComponent.IsHardCollidable = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
return new DoorComponentState(Opened);
|
||||
}
|
||||
|
||||
private const float AUTO_CLOSE_DELAY = 5;
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
if (!Opened)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
OpenTimeCounter += frameTime;
|
||||
if (OpenTimeCounter > AUTO_CLOSE_DELAY)
|
||||
{
|
||||
if (!Close())
|
||||
{
|
||||
// Try again in 2 seconds if it's jammed or something.
|
||||
OpenTimeCounter -= 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
public abstract class ToolComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// For tool interactions that have a delay before action this will modify the rate, time to wait is divided by this value
|
||||
/// </summary>
|
||||
public float SpeedModifier { get; set; } = 1;
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("Speed", out YamlNode node))
|
||||
{
|
||||
SpeedModifier = node.AsFloat();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Status modifier which determines whether or not we can act as a tool at this time
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public virtual bool CanUse()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
public class CrowbarComponent : ToolComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Tool that can be used to crowbar things apart, such as deconstructing
|
||||
/// </summary>
|
||||
public override string Name => "Crowbar";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Tool used for interfacing/hacking into configurable computers
|
||||
/// </summary>
|
||||
public class MultitoolComponent : ToolComponent
|
||||
{
|
||||
public override string Name => "Multitool";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
public class ScrewdriverComponent : ToolComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Tool that interacts with technical components that need to be screwed in
|
||||
/// </summary>
|
||||
public override string Name => "Screwdriver";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using SS14.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Tool used to weld metal together, light things on fire, or melt into constituent parts
|
||||
/// </summary>
|
||||
class WelderComponent : ToolComponent, EntitySystems.IUse
|
||||
{
|
||||
public override string Name => "Welder";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum fuel capacity the welder can hold
|
||||
/// </summary>
|
||||
public float FuelCapacity { get; set; } = 50;
|
||||
|
||||
/// <summary>
|
||||
/// Fuel the welder has to do tasks
|
||||
/// </summary>
|
||||
public float Fuel { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Default Cost of using the welder fuel for an action
|
||||
/// </summary>
|
||||
public const float DefaultFuelCost = 5;
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which we expunge fuel from ourselves when activated
|
||||
/// </summary>
|
||||
public const float FuelLossRate = 0.2f;
|
||||
|
||||
/// <summary>
|
||||
/// Status of welder, whether it is ignited
|
||||
/// </summary>
|
||||
public bool Activated { get; private set; } = false;
|
||||
|
||||
//private string OnSprite { get; set; }
|
||||
//private string OffSprite { get; set; }
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
base.LoadParameters(mapping);
|
||||
|
||||
if (mapping.TryGetNode("Capacity", out YamlNode node))
|
||||
{
|
||||
FuelCapacity = node.AsFloat();
|
||||
}
|
||||
|
||||
//if (mapping.TryGetNode("On", out node))
|
||||
//{
|
||||
// OnSprite = node.AsString();
|
||||
//}
|
||||
|
||||
//if (mapping.TryGetNode("Off", out node))
|
||||
//{
|
||||
// OffSprite = node.AsString();
|
||||
//}
|
||||
|
||||
if (mapping.TryGetNode("Fuel", out node))
|
||||
{
|
||||
Fuel = node.AsFloat();
|
||||
}
|
||||
else
|
||||
{
|
||||
Fuel = FuelCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
Fuel = Math.Min(Fuel - FuelLossRate, 0);
|
||||
|
||||
if(Activated && Fuel == 0)
|
||||
{
|
||||
ToggleStatus();
|
||||
}
|
||||
}
|
||||
|
||||
public bool CanUse(float value)
|
||||
{
|
||||
return Fuel > value;
|
||||
}
|
||||
|
||||
public override bool CanUse()
|
||||
{
|
||||
return CanUse(DefaultFuelCost);
|
||||
}
|
||||
|
||||
public bool CanActivate()
|
||||
{
|
||||
return Fuel > 0;
|
||||
}
|
||||
|
||||
public bool UseEntity(IEntity user)
|
||||
{
|
||||
return ToggleStatus();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deactivates welding tool if active, activates welding tool if possible
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool ToggleStatus()
|
||||
{
|
||||
if(Activated)
|
||||
{
|
||||
Activated = false;
|
||||
|
||||
//TODO : Change sprite on deactivation
|
||||
return true;
|
||||
}
|
||||
else if(CanActivate())
|
||||
{
|
||||
Activated = true;
|
||||
|
||||
//TODO : Change sprite on activation
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Tool that can be used for some cutting interactions such as wires or hacking
|
||||
/// </summary>
|
||||
public class WirecutterComponent : ToolComponent
|
||||
{
|
||||
public override string Name => "Wirecutter";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Content.Server.GameObjects.Components.Interactable.Tools
|
||||
{
|
||||
/// <summary>
|
||||
/// Wrenches bolts, and interacts with things that have been bolted
|
||||
/// </summary>
|
||||
public class WrenchComponent : ToolComponent
|
||||
{
|
||||
public override string Name => "Wrench";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.GameObjects.Components.Container;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class InventoryComponent : Component, IInventoryComponent
|
||||
{
|
||||
public override string Name => "Inventory";
|
||||
|
||||
private Dictionary<string, InventorySlot> slots = new Dictionary<string, InventorySlot>();
|
||||
private TransformComponent transform;
|
||||
// TODO: Make this container unique per-slot.
|
||||
private IContainer container;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
transform = Owner.GetComponent<TransformComponent>();
|
||||
container = Container.Create("inventory", Owner);
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
foreach (var slot in slots.Keys)
|
||||
{
|
||||
RemoveSlot(slot);
|
||||
}
|
||||
transform = null;
|
||||
container = null;
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode<YamlSequenceNode>("slots", out var slotsNode))
|
||||
{
|
||||
foreach (var node in slotsNode)
|
||||
{
|
||||
AddSlot(node.AsString());
|
||||
}
|
||||
}
|
||||
base.LoadParameters(mapping);
|
||||
}
|
||||
|
||||
public IItemComponent Get(string slot)
|
||||
{
|
||||
return _GetSlot(slot).Item;
|
||||
}
|
||||
|
||||
public IInventorySlot GetSlot(string slot)
|
||||
{
|
||||
return slots[slot];
|
||||
}
|
||||
|
||||
// Private version that returns our concrete implementation.
|
||||
private InventorySlot _GetSlot(string slot)
|
||||
{
|
||||
return slots[slot];
|
||||
}
|
||||
|
||||
public bool Insert(string slot, IItemComponent item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item), "An item must be passed. To remove an item from a slot, use Drop()");
|
||||
}
|
||||
|
||||
var inventorySlot = _GetSlot(slot);
|
||||
if (!CanInsert(slot, item) || !container.Insert(item.Owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
inventorySlot.Item = item;
|
||||
item.EquippedToSlot(inventorySlot);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanInsert(string slot, IItemComponent item)
|
||||
{
|
||||
var inventorySlot = _GetSlot(slot);
|
||||
return inventorySlot.Item == null && container.CanInsert(item.Owner);
|
||||
}
|
||||
|
||||
public bool Drop(string slot)
|
||||
{
|
||||
if (!CanDrop(slot))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var inventorySlot = _GetSlot(slot);
|
||||
var item = inventorySlot.Item;
|
||||
if (!container.Remove(item.Owner))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
item.RemovedFromSlot();
|
||||
inventorySlot.Item = null;
|
||||
|
||||
// TODO: The item should be dropped to the container our owner is in, if any.
|
||||
var itemTransform = item.Owner.GetComponent<TransformComponent>();
|
||||
itemTransform.LocalPosition = transform.LocalPosition;
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool CanDrop(string slot)
|
||||
{
|
||||
var inventorySlot = _GetSlot(slot);
|
||||
var item = inventorySlot.Item;
|
||||
return item != null && container.CanRemove(item.Owner);
|
||||
}
|
||||
|
||||
public IInventorySlot AddSlot(string slot)
|
||||
{
|
||||
if (HasSlot(slot))
|
||||
{
|
||||
throw new InvalidOperationException($"Slot '{slot}' already exists.");
|
||||
}
|
||||
|
||||
return slots[slot] = new InventorySlot(slot, this);
|
||||
}
|
||||
|
||||
public void RemoveSlot(string slot)
|
||||
{
|
||||
if (!HasSlot(slot))
|
||||
{
|
||||
throw new InvalidOperationException($"Slow '{slot}' does not exist.");
|
||||
}
|
||||
|
||||
if (Get(slot) != null && !Drop(slot))
|
||||
{
|
||||
// TODO: Handle this potential failiure better.
|
||||
throw new InvalidOperationException("Unable to remove slot as the contained item could not be dropped");
|
||||
}
|
||||
|
||||
slots.Remove(slot);
|
||||
}
|
||||
|
||||
public bool HasSlot(string slot)
|
||||
{
|
||||
return slots.ContainsKey(slot);
|
||||
}
|
||||
|
||||
private class InventorySlot : IInventorySlot
|
||||
{
|
||||
public IItemComponent Item { get; set; }
|
||||
public string Name { get; }
|
||||
public IInventoryComponent Owner { get; }
|
||||
|
||||
public InventorySlot(string name, IInventoryComponent owner)
|
||||
{
|
||||
Name = name;
|
||||
Owner = owner;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
57
Content.Server/GameObjects/Components/Items/ItemComponent.cs
Normal file
@@ -0,0 +1,57 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using System;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class ItemComponent : Component, IItemComponent, EntitySystems.IAttackHand
|
||||
{
|
||||
public override string Name => "Item";
|
||||
|
||||
/// <inheritdoc />
|
||||
public IInventorySlot ContainingSlot { get; private set; }
|
||||
|
||||
public void RemovedFromSlot()
|
||||
{
|
||||
if (ContainingSlot == null)
|
||||
{
|
||||
throw new InvalidOperationException("Item is not in a slot.");
|
||||
}
|
||||
|
||||
ContainingSlot = null;
|
||||
|
||||
foreach (var component in Owner.GetComponents<ISpriteRenderableComponent>())
|
||||
{
|
||||
component.Visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
public void EquippedToSlot(IInventorySlot slot)
|
||||
{
|
||||
if (ContainingSlot != null)
|
||||
{
|
||||
throw new InvalidOperationException("Item is already in a slot.");
|
||||
}
|
||||
|
||||
ContainingSlot = slot;
|
||||
|
||||
foreach (var component in Owner.GetComponents<ISpriteRenderableComponent>())
|
||||
{
|
||||
component.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
public bool Attackhand(IEntity user)
|
||||
{
|
||||
if (ContainingSlot != null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var hands = user.GetComponent<IHandsComponent>();
|
||||
hands.PutInHand(this, hands.ActiveIndex, fallback: false);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,263 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Input;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
public class HandsComponent : SharedHandsComponent, IHandsComponent
|
||||
{
|
||||
private string activeIndex;
|
||||
|
||||
public string ActiveIndex
|
||||
{
|
||||
get => activeIndex;
|
||||
set
|
||||
{
|
||||
if (!hands.ContainsKey(value))
|
||||
{
|
||||
throw new ArgumentException($"No hand '{value}'");
|
||||
}
|
||||
|
||||
activeIndex = value;
|
||||
}
|
||||
}
|
||||
|
||||
private Dictionary<string, IInventorySlot> hands = new Dictionary<string, IInventorySlot>();
|
||||
private List<string> orderedHands = new List<string>();
|
||||
private IInventoryComponent inventory;
|
||||
private IServerTransformComponent transform;
|
||||
private YamlMappingNode tempParametersMapping;
|
||||
|
||||
// Mostly arbitrary.
|
||||
public const float PICKUP_RANGE = 2;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
inventory = Owner.GetComponent<IInventoryComponent>();
|
||||
transform = Owner.GetComponent<IServerTransformComponent>();
|
||||
if (tempParametersMapping != null)
|
||||
{
|
||||
foreach (var node in tempParametersMapping.GetNode<YamlSequenceNode>("hands"))
|
||||
{
|
||||
AddHand(node.AsString());
|
||||
}
|
||||
}
|
||||
|
||||
base.Initialize();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
inventory = null;
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
tempParametersMapping = mapping;
|
||||
base.LoadParameters(mapping);
|
||||
}
|
||||
|
||||
public IEnumerable<IItemComponent> GetAllHeldItems()
|
||||
{
|
||||
foreach (var slot in hands.Values)
|
||||
{
|
||||
if (slot.Item != null)
|
||||
{
|
||||
yield return slot.Item;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public IItemComponent GetHand(string index)
|
||||
{
|
||||
var slot = hands[index];
|
||||
return slot.Item;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates over the hand keys, returning the active hand first.
|
||||
/// </summary>
|
||||
private IEnumerable<string> ActivePriorityEnumerable()
|
||||
{
|
||||
yield return ActiveIndex;
|
||||
foreach (var hand in hands.Keys)
|
||||
{
|
||||
if (hand == ActiveIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return hand;
|
||||
}
|
||||
}
|
||||
|
||||
public bool PutInHand(IItemComponent item)
|
||||
{
|
||||
foreach (var hand in ActivePriorityEnumerable())
|
||||
{
|
||||
if (PutInHand(item, hand, fallback: false))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool PutInHand(IItemComponent item, string index, bool fallback = true)
|
||||
{
|
||||
if (!CanPutInHand(item, index))
|
||||
{
|
||||
return fallback && PutInHand(item);
|
||||
}
|
||||
|
||||
var slot = hands[index];
|
||||
return slot.Owner.Insert(slot.Name, item);
|
||||
}
|
||||
|
||||
public bool CanPutInHand(IItemComponent item)
|
||||
{
|
||||
foreach (var hand in ActivePriorityEnumerable())
|
||||
{
|
||||
if (CanPutInHand(item, hand))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool CanPutInHand(IItemComponent item, string index)
|
||||
{
|
||||
var slot = hands[index];
|
||||
return slot.Owner.CanInsert(slot.Name, item);
|
||||
}
|
||||
|
||||
public bool Drop(string index)
|
||||
{
|
||||
if (!CanDrop(index))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
var slot = hands[index];
|
||||
return slot.Owner.Drop(slot.Name);
|
||||
}
|
||||
|
||||
public bool CanDrop(string index)
|
||||
{
|
||||
var slot = hands[index];
|
||||
return slot.Item != null && slot.Owner.CanDrop(slot.Name);
|
||||
}
|
||||
|
||||
public void AddHand(string index)
|
||||
{
|
||||
if (HasHand(index))
|
||||
{
|
||||
throw new InvalidOperationException($"Hand '{index}' already exists.");
|
||||
}
|
||||
|
||||
var slot = inventory.AddSlot(HandSlotName(index));
|
||||
hands[index] = slot;
|
||||
orderedHands.Add(index);
|
||||
if (ActiveIndex == null)
|
||||
{
|
||||
ActiveIndex = index;
|
||||
}
|
||||
}
|
||||
|
||||
public void RemoveHand(string index)
|
||||
{
|
||||
if (!HasHand(index))
|
||||
{
|
||||
throw new InvalidOperationException($"Hand '{index}' does not exist.");
|
||||
}
|
||||
|
||||
inventory.RemoveSlot(HandSlotName(index));
|
||||
hands.Remove(index);
|
||||
orderedHands.Remove(index);
|
||||
|
||||
if (index == ActiveIndex)
|
||||
{
|
||||
if (orderedHands.Count == 0)
|
||||
{
|
||||
activeIndex = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
activeIndex = orderedHands[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasHand(string index)
|
||||
{
|
||||
return hands.ContainsKey(index);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the name of the slot passed to the inventory component.
|
||||
/// </summary>
|
||||
private string HandSlotName(string index) => $"_hand_{index}";
|
||||
|
||||
public override ComponentState GetComponentState()
|
||||
{
|
||||
var dict = new Dictionary<string, EntityUid>(hands.Count);
|
||||
foreach (var hand in hands)
|
||||
{
|
||||
if (hand.Value.Item != null)
|
||||
{
|
||||
dict[hand.Key] = hand.Value.Item.Owner.Uid;
|
||||
}
|
||||
}
|
||||
return new HandsComponentState(dict, ActiveIndex);
|
||||
}
|
||||
|
||||
private void SwapHands()
|
||||
{
|
||||
var index = orderedHands.FindIndex(x => x == ActiveIndex);
|
||||
index++;
|
||||
if (index >= orderedHands.Count)
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
ActiveIndex = orderedHands[index];
|
||||
}
|
||||
|
||||
public override void HandleMessage(object owner, ComponentMessage message)
|
||||
{
|
||||
base.HandleMessage(owner, message);
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClientChangedHandMsg msg:
|
||||
if (HasHand(msg.Index))
|
||||
ActiveIndex = msg.Index;
|
||||
break;
|
||||
|
||||
case BoundKeyChangedMsg msg:
|
||||
if(msg.State != BoundKeyState.Down)
|
||||
return;
|
||||
switch (msg.Function)
|
||||
{
|
||||
case BoundKeyFunctions.SwitchHands:
|
||||
SwapHands();
|
||||
break;
|
||||
case BoundKeyFunctions.Drop:
|
||||
Drop(ActiveIndex);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
293
Content.Server/GameObjects/Components/Power/PowerDevice.cs
Normal file
@@ -0,0 +1,293 @@
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Utility;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that requires power to function
|
||||
/// </summary>
|
||||
public class PowerDeviceComponent : Component
|
||||
{
|
||||
public override string Name => "PowerDevice";
|
||||
|
||||
/// <summary>
|
||||
/// The method of draw we will try to use to place our load set via component parameter, defaults to using power providers
|
||||
/// </summary>
|
||||
public virtual DrawTypes Drawtype { get; protected set; } = DrawTypes.Provider;
|
||||
|
||||
/// <summary>
|
||||
/// The power draw method we are currently connected to and using
|
||||
/// </summary>
|
||||
public DrawTypes Connected { get; protected set; } = DrawTypes.None;
|
||||
|
||||
public bool _powered = false;
|
||||
/// <summary>
|
||||
/// Status indicator variable for powered
|
||||
/// </summary>
|
||||
public virtual bool Powered
|
||||
{
|
||||
get => _powered;
|
||||
set => SetPowered(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority for powernet draw, lower will draw first, defined in powernet.cs
|
||||
/// </summary>
|
||||
public virtual Powernet.Priority Priority { get; protected set; } = Powernet.Priority.Medium;
|
||||
|
||||
|
||||
private float _load = 100; //arbitrary magic number to start
|
||||
/// <summary>
|
||||
/// Power load from this entity
|
||||
/// </summary>
|
||||
public float Load
|
||||
{
|
||||
get => _load;
|
||||
set { UpdateLoad(value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All the power providers that we are within range of
|
||||
/// </summary>
|
||||
public List<PowerProviderComponent> AvailableProviders = new List<PowerProviderComponent>();
|
||||
|
||||
|
||||
private PowerProviderComponent _provider;
|
||||
/// <summary>
|
||||
/// A power provider that will handle our load, if we are linked to any
|
||||
/// </summary>
|
||||
public PowerProviderComponent Provider
|
||||
{
|
||||
get => _provider;
|
||||
set {
|
||||
Connected = DrawTypes.Provider;
|
||||
if (_provider != null)
|
||||
{
|
||||
_provider.RemoveDevice(this);
|
||||
}
|
||||
|
||||
if(value != null)
|
||||
{
|
||||
_provider = value;
|
||||
_provider.AddDevice(this);
|
||||
}
|
||||
else
|
||||
{
|
||||
Connected = DrawTypes.None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
if (Drawtype == DrawTypes.Both || Drawtype == DrawTypes.Node)
|
||||
{
|
||||
if (!Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
Owner.AddComponent<PowerNodeComponent>();
|
||||
node = Owner.GetComponent<PowerNodeComponent>();
|
||||
}
|
||||
node.OnPowernetConnect += PowernetConnect;
|
||||
node.OnPowernetDisconnect += PowernetDisconnect;
|
||||
node.OnPowernetRegenerate += PowernetRegenerate;
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
if(node.Parent != null)
|
||||
{
|
||||
node.Parent.RemoveDevice(this);
|
||||
}
|
||||
|
||||
node.OnPowernetConnect -= PowernetConnect;
|
||||
node.OnPowernetDisconnect -= PowernetDisconnect;
|
||||
node.OnPowernetRegenerate -= PowernetRegenerate;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("Drawtype", out YamlNode node))
|
||||
{
|
||||
Drawtype = node.AsEnum<DrawTypes>();
|
||||
}
|
||||
if (mapping.TryGetNode("Load", out node))
|
||||
{
|
||||
Load = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("Priority", out node))
|
||||
{
|
||||
Priority = node.AsEnum<Powernet.Priority>();
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateLoad(float value)
|
||||
{
|
||||
var oldLoad = _load;
|
||||
_load = value;
|
||||
if(Connected == DrawTypes.Node)
|
||||
{
|
||||
var node = Owner.GetComponent<PowerNodeComponent>();
|
||||
node.Parent.UpdateDevice(this, oldLoad);
|
||||
}
|
||||
else if(Connected == DrawTypes.Provider)
|
||||
{
|
||||
Provider.UpdateDevice(this, oldLoad);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes behavior when receiving a command to become powered or depowered
|
||||
/// </summary>
|
||||
/// <param name="value"></param>
|
||||
public virtual void SetPowered(bool value)
|
||||
{
|
||||
//Let them set us to true
|
||||
if (value == true)
|
||||
{
|
||||
_powered = true;
|
||||
return;
|
||||
}
|
||||
|
||||
//A powernet has decided we will not be powered this tick, lets try to power ourselves
|
||||
if (value == false && Owner.TryGetComponent(out PowerStorageComponent storage))
|
||||
{
|
||||
if (storage.CanDeductCharge(Load))
|
||||
{
|
||||
storage.DeductCharge(Load);
|
||||
_powered = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//For some reason above we could not power ourselves, we depower
|
||||
_powered = false;
|
||||
return;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a new power provider as a possible connection to this device
|
||||
/// </summary>
|
||||
/// <param name="provider"></param>
|
||||
public void AddProvider(PowerProviderComponent provider)
|
||||
{
|
||||
AvailableProviders.Add(provider);
|
||||
|
||||
if(Connected != DrawTypes.Node)
|
||||
{
|
||||
ConnectToBestProvider();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find the nearest registered power provider and connect to it
|
||||
/// </summary>
|
||||
private void ConnectToBestProvider()
|
||||
{
|
||||
//Any values we can connect to or are we already connected to a node, cancel!
|
||||
if (!AvailableProviders.Any() || Connected == DrawTypes.Node)
|
||||
return;
|
||||
|
||||
//Get the starting value for our loop
|
||||
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
|
||||
var bestprovider = AvailableProviders[0];
|
||||
|
||||
//If we are already connected to a power provider we need to do a loop to find the nearest one, otherwise skip it and use first entry
|
||||
if (Connected == DrawTypes.Provider)
|
||||
{
|
||||
var bestdistance = (bestprovider.Owner.GetComponent<TransformComponent>().WorldPosition - position).LengthSquared;
|
||||
|
||||
foreach (var availprovider in AvailableProviders)
|
||||
{
|
||||
//Find distance to new provider
|
||||
var distance = (availprovider.Owner.GetComponent<TransformComponent>().WorldPosition - position).LengthSquared;
|
||||
|
||||
//If new provider distance is shorter it becomes new best possible provider
|
||||
if (distance < bestdistance)
|
||||
{
|
||||
bestdistance = distance;
|
||||
bestprovider = availprovider;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(Provider != bestprovider)
|
||||
Provider = bestprovider;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a power provider from being a possible connection to this device
|
||||
/// </summary>
|
||||
/// <param name="provider"></param>
|
||||
public void RemoveProvider(PowerProviderComponent provider)
|
||||
{
|
||||
if (!AvailableProviders.Contains(provider))
|
||||
return;
|
||||
|
||||
AvailableProviders.Remove(provider);
|
||||
|
||||
if (Connected != DrawTypes.Node)
|
||||
{
|
||||
ConnectToBestProvider();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become anchored to a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
//This sets connected = none so it must be first
|
||||
Provider = null;
|
||||
|
||||
eventarg.Powernet.AddDevice(this);
|
||||
Connected = DrawTypes.Node;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Powernet wire was remove so we need to regenerate the powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddDevice(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become unanchored from a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.RemoveDevice(this);
|
||||
Connected = DrawTypes.None;
|
||||
|
||||
ConnectToBestProvider();
|
||||
}
|
||||
}
|
||||
|
||||
public enum DrawTypes
|
||||
{
|
||||
None = 0,
|
||||
Node = 1,
|
||||
Provider = 2,
|
||||
Both = 3
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Utility;
|
||||
using System;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that creates power and supplies it to the powernet
|
||||
/// </summary>
|
||||
public class PowerGeneratorComponent : Component
|
||||
{
|
||||
public override string Name => "PowerGenerator";
|
||||
|
||||
/// <summary>
|
||||
/// Power supply from this entity
|
||||
/// </summary>
|
||||
private float _supply = 1000; //arbitrary initial magic number to start
|
||||
public float Supply
|
||||
{
|
||||
get => _supply;
|
||||
set { UpdateSupply(value); }
|
||||
}
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("Supply", out YamlNode node))
|
||||
{
|
||||
Supply = node.AsFloat();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
if (!Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
Owner.AddComponent<PowerNodeComponent>();
|
||||
node = Owner.GetComponent<PowerNodeComponent>();
|
||||
}
|
||||
node.OnPowernetConnect += PowernetConnect;
|
||||
node.OnPowernetDisconnect += PowernetDisconnect;
|
||||
node.OnPowernetRegenerate += PowernetRegenerate;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
node.Parent.RemoveGenerator(this);
|
||||
}
|
||||
|
||||
node.OnPowernetConnect -= PowernetConnect;
|
||||
node.OnPowernetDisconnect -= PowernetDisconnect;
|
||||
node.OnPowernetRegenerate -= PowernetRegenerate;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private void UpdateSupply(float value)
|
||||
{
|
||||
_supply = value;
|
||||
var node = Owner.GetComponent<PowerNodeComponent>();
|
||||
node.Parent.UpdateGenerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become anchored to a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddGenerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has had its powernet regenerated
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddGenerator(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become unanchored from a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.RemoveGenerator(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using System;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that connects to the powernet
|
||||
/// </summary>
|
||||
public class PowerNodeComponent : Component
|
||||
{
|
||||
public override string Name => "PowerNode";
|
||||
|
||||
/// <summary>
|
||||
/// The powernet this node is connected to
|
||||
/// </summary>
|
||||
public Powernet Parent;
|
||||
|
||||
/// <summary>
|
||||
/// An event handling when this node connects to a powernet
|
||||
/// </summary>
|
||||
public event EventHandler<PowernetEventArgs> OnPowernetConnect;
|
||||
|
||||
/// <summary>
|
||||
/// An event handling when this node disconnects from a powernet
|
||||
/// </summary>
|
||||
public event EventHandler<PowernetEventArgs> OnPowernetDisconnect;
|
||||
|
||||
/// <summary>
|
||||
/// An event that registers us to a regenerating powernet
|
||||
/// </summary>
|
||||
public event EventHandler<PowernetEventArgs> OnPowernetRegenerate;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
TryCreatePowernetConnection();
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
DisconnectFromPowernet();
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Find a nearby wire which will have a powernet and connect ourselves to its powernet
|
||||
/// </summary>
|
||||
public void TryCreatePowernetConnection()
|
||||
{
|
||||
var _emanager = IoCManager.Resolve<IServerEntityManager>();
|
||||
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
|
||||
var wires = _emanager.GetEntitiesIntersecting(Owner)
|
||||
.Where(x => x.HasComponent<PowerTransferComponent>())
|
||||
.OrderByDescending(x => (x.GetComponent<TransformComponent>().WorldPosition - position).Length);
|
||||
var choose = wires.FirstOrDefault();
|
||||
if(choose != null)
|
||||
ConnectToPowernet(choose.GetComponent<PowerTransferComponent>().Parent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers event telling power components that we connected to a powernet
|
||||
/// </summary>
|
||||
/// <param name="toconnect"></param>
|
||||
public void ConnectToPowernet(Powernet toconnect)
|
||||
{
|
||||
Parent = toconnect;
|
||||
Parent.Nodelist.Add(this);
|
||||
OnPowernetConnect?.Invoke(this, new PowernetEventArgs(Parent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers event telling power components that we haven't disconnected but have readded ourselves to a regenerated powernet
|
||||
/// </summary>
|
||||
/// <param name="toconnect"></param>
|
||||
public void RegeneratePowernet(Powernet toconnect)
|
||||
{
|
||||
//This removes the device from things that will be powernet disconnected when dirty powernet is killed
|
||||
Parent.Nodelist.Remove(this);
|
||||
|
||||
Parent = toconnect;
|
||||
Parent.Nodelist.Add(this);
|
||||
OnPowernetRegenerate?.Invoke(this, new PowernetEventArgs(Parent));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Triggers event telling power components we have exited any powernets
|
||||
/// </summary>
|
||||
public void DisconnectFromPowernet()
|
||||
{
|
||||
Parent.Nodelist.Remove(this);
|
||||
OnPowernetDisconnect?.Invoke(this, new PowernetEventArgs(Parent));
|
||||
Parent = null;
|
||||
}
|
||||
}
|
||||
|
||||
public class PowernetEventArgs : EventArgs
|
||||
{
|
||||
public PowernetEventArgs(Powernet powernet)
|
||||
{
|
||||
Powernet = powernet;
|
||||
}
|
||||
|
||||
public Powernet Powernet { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Utility;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Component that wirelessly connects and powers devices, connects to powernet via node and can be combined with internal storage component
|
||||
/// </summary>
|
||||
public class PowerProviderComponent : PowerDeviceComponent
|
||||
{
|
||||
public override string Name => "PowerProvider";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DrawTypes Drawtype { get; protected set; } = DrawTypes.Node;
|
||||
|
||||
/// <summary>
|
||||
/// Variable that determines the range that the power provider will try to supply power to
|
||||
/// </summary>
|
||||
public int PowerRange { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// List storing all the power devices that we are currently providing power to
|
||||
/// </summary>
|
||||
public SortedSet<PowerDeviceComponent> Deviceloadlist = new SortedSet<PowerDeviceComponent>(new Powernet.DevicePriorityCompare());
|
||||
|
||||
public List<PowerDeviceComponent> DepoweredDevices = new List<PowerDeviceComponent>();
|
||||
|
||||
public override Powernet.Priority Priority { get; protected set; } = Powernet.Priority.Provider;
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("Range", out YamlNode node))
|
||||
{
|
||||
PowerRange = node.AsInt();
|
||||
}
|
||||
if (mapping.TryGetNode("Priority", out node))
|
||||
{
|
||||
Priority = node.AsEnum<Powernet.Priority>();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void SetPowered(bool value)
|
||||
{
|
||||
//Let them set us true, we must now power all the devices that rely on us for energy
|
||||
if (value == true)
|
||||
{
|
||||
PowerAllDevices();
|
||||
return;
|
||||
}
|
||||
|
||||
//A powernet has decided we will not be powered this tick, lets try to power ourselves
|
||||
if (value == false && Owner.TryGetComponent(out PowerStorageComponent storage))
|
||||
{
|
||||
//Can the storage cover powering all our devices and us? If so power all
|
||||
if (storage.CanDeductCharge(Load))
|
||||
{
|
||||
storage.DeductCharge(Load);
|
||||
_powered = true;
|
||||
return;
|
||||
}
|
||||
//Does the storage even have any power to give us? If so power as much as we can
|
||||
else if (storage.RequestAllCharge() != 0)
|
||||
{
|
||||
var depowervalue = storage.RequestAllCharge() - Load;
|
||||
_powered = true;
|
||||
//See code in powernet for same functionality
|
||||
foreach (var device in Deviceloadlist)
|
||||
{
|
||||
device.Powered = false;
|
||||
DepoweredDevices.Add(device);
|
||||
depowervalue -= device.Load;
|
||||
if (depowervalue < 0)
|
||||
break;
|
||||
}
|
||||
return;
|
||||
}
|
||||
//Storage doesn't have anything, depower everything
|
||||
else if(storage.RequestAllCharge() == 0)
|
||||
{
|
||||
DepowerAllDevices();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//For some reason above we could not power ourselves, we depower ourselves and all devices
|
||||
DepowerAllDevices();
|
||||
return;
|
||||
}
|
||||
|
||||
private void PowerAllDevices()
|
||||
{
|
||||
_powered = true;
|
||||
foreach (var device in DepoweredDevices)
|
||||
{
|
||||
device.Powered = true;
|
||||
}
|
||||
DepoweredDevices.Clear();
|
||||
}
|
||||
|
||||
private void DepowerAllDevices()
|
||||
{
|
||||
_powered = false;
|
||||
foreach (var device in DepoweredDevices)
|
||||
{
|
||||
device.Powered = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddDevice(this);
|
||||
Connected = DrawTypes.Node;
|
||||
|
||||
//Find devices within range to take under our control
|
||||
var _emanager = IoCManager.Resolve<IServerEntityManager>();
|
||||
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
|
||||
var entities = _emanager.GetEntitiesInRange(Owner, PowerRange)
|
||||
.Where(x => x.HasComponent<PowerDeviceComponent>());
|
||||
|
||||
|
||||
foreach (var entity in entities)
|
||||
{
|
||||
var device = entity.GetComponent<PowerDeviceComponent>();
|
||||
|
||||
//Make sure the device can accept power providers to give it power
|
||||
if (device.Drawtype == DrawTypes.Provider || device.Drawtype == DrawTypes.Both)
|
||||
{
|
||||
device.AddProvider(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddDevice(this);
|
||||
}
|
||||
|
||||
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.RemoveDevice(this);
|
||||
Connected = DrawTypes.None;
|
||||
|
||||
//We don't want to make the devices under us think we're still a valid provider if we have no powernet to connect to
|
||||
foreach (var device in Deviceloadlist)
|
||||
{
|
||||
device.RemoveProvider(this);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a continuous load from a device connected to the powernet
|
||||
/// </summary>
|
||||
public void AddDevice(PowerDeviceComponent device)
|
||||
{
|
||||
Deviceloadlist.Add(device);
|
||||
Load += device.Load;
|
||||
if (!device.Powered)
|
||||
DepoweredDevices.Add(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update one of the loads from a deviceconnected to the powernet
|
||||
/// </summary>
|
||||
public void UpdateDevice(PowerDeviceComponent device, float oldLoad)
|
||||
{
|
||||
if (Deviceloadlist.Contains(device))
|
||||
{
|
||||
Load -= oldLoad;
|
||||
Load += device.Load;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a continuous load from a device connected to the powernet
|
||||
/// </summary>
|
||||
public void RemoveDevice(PowerDeviceComponent device)
|
||||
{
|
||||
if (Deviceloadlist.Contains(device))
|
||||
{
|
||||
Load -= device.Load;
|
||||
Deviceloadlist.Remove(device);
|
||||
if (DepoweredDevices.Contains(device))
|
||||
DepoweredDevices.Remove(device);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = device.Owner.Prototype.Name;
|
||||
Logger.Log(String.Format("We tried to remove a device twice from the same {0} somehow, prototype {1}", Name, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Utility;
|
||||
using System;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Feeds energy from the powernet and may have the ability to supply back into it
|
||||
/// </summary>
|
||||
public class PowerStorageComponent : Component
|
||||
{
|
||||
public override string Name => "PowerStorage";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum amount of energy the internal battery can store
|
||||
/// </summary>
|
||||
public float Capacity { get; private set; } = 10000; //arbitrary value replace
|
||||
|
||||
/// <summary>
|
||||
/// Energy the battery is currently storing
|
||||
/// </summary>
|
||||
public float Charge { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which energy will be taken to charge internal battery
|
||||
/// </summary>
|
||||
public float ChargeRate { get; private set; } = 1000;
|
||||
|
||||
/// <summary>
|
||||
/// Rate at which energy will be distributed to the powernet if needed
|
||||
/// </summary>
|
||||
public float DistributionRate { get; private set; } = 1000;
|
||||
|
||||
private bool _chargepowernet = false;
|
||||
|
||||
/// <summary>
|
||||
/// Do we distribute power into the powernet from our stores if the powernet requires it?
|
||||
/// </summary>
|
||||
public bool ChargePowernet
|
||||
{
|
||||
get => _chargepowernet;
|
||||
set
|
||||
{
|
||||
_chargepowernet = value;
|
||||
if (Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
if (node.Parent != null)
|
||||
node.Parent.UpdateStorageType(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
if (mapping.TryGetNode("Capacity", out YamlNode node))
|
||||
{
|
||||
Capacity = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("Charge", out node))
|
||||
{
|
||||
Charge = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("ChargeRate", out node))
|
||||
{
|
||||
ChargeRate = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("DistributionRate", out node))
|
||||
{
|
||||
DistributionRate = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("ChargePowernet", out node))
|
||||
{
|
||||
_chargepowernet = node.AsBool();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
|
||||
if (!Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
Owner.AddComponent<PowerNodeComponent>();
|
||||
node = Owner.GetComponent<PowerNodeComponent>();
|
||||
}
|
||||
node.OnPowernetConnect += PowernetConnect;
|
||||
node.OnPowernetDisconnect += PowernetDisconnect;
|
||||
node.OnPowernetRegenerate += PowernetRegenerate;
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
if (Owner.TryGetComponent(out PowerNodeComponent node))
|
||||
{
|
||||
if (node.Parent != null)
|
||||
{
|
||||
node.Parent.RemovePowerStorage(this);
|
||||
}
|
||||
|
||||
node.OnPowernetConnect -= PowernetConnect;
|
||||
node.OnPowernetDisconnect -= PowernetDisconnect;
|
||||
node.OnPowernetRegenerate -= PowernetRegenerate;
|
||||
}
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the storage can supply the amount of charge directly requested
|
||||
/// </summary>
|
||||
public bool CanDeductCharge(float todeduct)
|
||||
{
|
||||
if (Charge > todeduct)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deducts the requested charge from the energy storage
|
||||
/// </summary>
|
||||
public void DeductCharge(float todeduct)
|
||||
{
|
||||
Charge = Math.Min(0, Charge - todeduct);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns all possible charge available from the energy storage
|
||||
/// </summary>
|
||||
public float RequestAllCharge()
|
||||
{
|
||||
return Math.Min(ChargeRate, Capacity - Charge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the charge available from the energy storage
|
||||
/// </summary>
|
||||
public float RequestCharge()
|
||||
{
|
||||
return Math.Min(ChargeRate, Capacity - Charge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the charge available from the energy storage
|
||||
/// </summary>
|
||||
public float AvailableCharge()
|
||||
{
|
||||
return Math.Min(DistributionRate, Charge);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gives the storage one full tick of charging its energy storage
|
||||
/// </summary>
|
||||
public void ChargePowerTick()
|
||||
{
|
||||
Charge = Math.Max(Charge + ChargeRate, Capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Takes from the storage one full tick of energy
|
||||
/// </summary>
|
||||
public void RetrievePassiveStorage()
|
||||
{
|
||||
Charge = Math.Min(Charge - DistributionRate, 0);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become anchored to a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetConnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddPowerStorage(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has had its powernet regenerated
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetRegenerate(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.AddPowerStorage(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Node has become unanchored from a powernet
|
||||
/// </summary>
|
||||
/// <param name="sender"></param>
|
||||
/// <param name="eventarg"></param>
|
||||
private void PowernetDisconnect(object sender, PowernetEventArgs eventarg)
|
||||
{
|
||||
eventarg.Powernet.RemovePowerStorage(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using System.Linq;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using Content.Server.GameObjects.Components.Interactable.Tools;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Component to transfer power to nearby components, can create powernets and connect to nodes
|
||||
/// </summary>
|
||||
public class PowerTransferComponent : Component, IAttackby
|
||||
{
|
||||
public override string Name => "PowerTransfer";
|
||||
|
||||
/// <summary>
|
||||
/// The powernet this component is connected to
|
||||
/// </summary>
|
||||
public Powernet Parent;
|
||||
|
||||
public bool Regenerating { get; set; } = false;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if(Parent == null)
|
||||
{
|
||||
SpreadPowernet();
|
||||
}
|
||||
}
|
||||
|
||||
public override void OnRemove()
|
||||
{
|
||||
DisconnectFromPowernet();
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Searches for local powernets to connect to, otherwise creates its own, and spreads powernet to nearby entities
|
||||
/// </summary>
|
||||
public void SpreadPowernet()
|
||||
{
|
||||
var _emanager = IoCManager.Resolve<IServerEntityManager>();
|
||||
var position = Owner.GetComponent<TransformComponent>().WorldPosition;
|
||||
var wires = _emanager.GetEntitiesInRange(Owner, 0.1f) //arbitrarily low, just scrape things //wip
|
||||
.Where(x => x.HasComponent<PowerTransferComponent>());
|
||||
|
||||
//we have no parent so lets find a partner we can join his powernet
|
||||
if(Parent == null || Regenerating)
|
||||
{
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
var ptc = wire.GetComponent<PowerTransferComponent>();
|
||||
if (ptc.CanConnectTo())
|
||||
{
|
||||
ConnectToPowernet(ptc.Parent);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//we couldn't find a partner so none must have spread yet, lets make our own powernet to spread
|
||||
if (Parent == null || Regenerating)
|
||||
{
|
||||
var powernew = new Powernet();
|
||||
ConnectToPowernet(powernew);
|
||||
}
|
||||
}
|
||||
|
||||
//Find nodes intersecting us and if not already assigned to a powernet assign them to us
|
||||
var nodes = _emanager.GetEntitiesIntersecting(Owner)
|
||||
.Where(x => x.HasComponent<PowerNodeComponent>())
|
||||
.Select(x => x.GetComponent<PowerNodeComponent>());
|
||||
|
||||
foreach(var node in nodes)
|
||||
{
|
||||
if(node.Parent == null)
|
||||
{
|
||||
node.ConnectToPowernet(Parent);
|
||||
}
|
||||
else if(node.Parent.Dirty)
|
||||
{
|
||||
node.RegeneratePowernet(Parent);
|
||||
}
|
||||
}
|
||||
|
||||
//spread powernet to nearby wires which haven't got one yet, and tell them to spread as well
|
||||
foreach (var wire in wires)
|
||||
{
|
||||
var ptc = wire.GetComponent<PowerTransferComponent>();
|
||||
if (ptc.Parent == null || Regenerating)
|
||||
{
|
||||
ptc.ConnectToPowernet(Parent);
|
||||
SpreadPowernet();
|
||||
}
|
||||
else if(ptc.Parent != Parent && !ptc.Parent.Dirty)
|
||||
{
|
||||
Parent.MergePowernets(ptc.Parent);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when connecting to a new powernet, either on creation or on regeneration
|
||||
/// </summary>
|
||||
/// <param name="toconnect"></param>
|
||||
public void ConnectToPowernet(Powernet toconnect)
|
||||
{
|
||||
Parent = toconnect;
|
||||
Parent.Wirelist.Add(this);
|
||||
Regenerating = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when we are removed and telling the powernet that it is now dirty and must regenerate
|
||||
/// </summary>
|
||||
public void DisconnectFromPowernet()
|
||||
{
|
||||
Parent.Wirelist.Remove(this);
|
||||
Parent.Dirty = true;
|
||||
Parent = null;
|
||||
}
|
||||
|
||||
|
||||
public bool CanConnectTo()
|
||||
{
|
||||
return Parent != null && Parent.Dirty == false && !Regenerating;
|
||||
}
|
||||
|
||||
public bool Attackby(IEntity user, IEntity attackwith)
|
||||
{
|
||||
if(attackwith.TryGetComponent(out WirecutterComponent wirecutter))
|
||||
{
|
||||
Owner.Delete();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
402
Content.Server/GameObjects/Components/Power/Powernet.cs
Normal file
@@ -0,0 +1,402 @@
|
||||
using Content.Shared.GameObjects.EntitySystems;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Log;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Power
|
||||
{
|
||||
/// <summary>
|
||||
/// Master class for group of powertransfercomponents, takes in and distributes power via nodes
|
||||
/// </summary>
|
||||
public class Powernet
|
||||
{
|
||||
public Powernet()
|
||||
{
|
||||
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
|
||||
EntitySystemManager.GetEntitySystem<PowerSystem>().Powernets.Add(this);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The entities that make up the powernet's physical location and allow powernet connection
|
||||
/// </summary>
|
||||
public List<PowerTransferComponent> Wirelist { get; set; } = new List<PowerTransferComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// Entities that connect directly to the powernet through PTC above to add power or add power load
|
||||
/// </summary>
|
||||
public List<PowerNodeComponent> Nodelist { get; set; } = new List<PowerNodeComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// Subset of nodelist that adds a continuous power supply to the network
|
||||
/// </summary>
|
||||
public Dictionary<PowerGeneratorComponent, float> Generatorlist { get; set; } = new Dictionary<PowerGeneratorComponent, float>();
|
||||
|
||||
/// <summary>
|
||||
/// Subset of nodelist that draw power, stores information on current continuous powernet load
|
||||
/// </summary>
|
||||
public SortedSet<PowerDeviceComponent> Deviceloadlist { get; set; } = new SortedSet<PowerDeviceComponent>(new DevicePriorityCompare());
|
||||
|
||||
/// <summary>
|
||||
/// Comparer that keeps the device dictionary sorted by powernet priority
|
||||
/// </summary>
|
||||
public class DevicePriorityCompare : IComparer<PowerDeviceComponent>
|
||||
{
|
||||
public int Compare(PowerDeviceComponent x, PowerDeviceComponent y)
|
||||
{
|
||||
int compare = y.Priority.CompareTo(x.Priority);
|
||||
|
||||
//If the comparer returns 0 sortedset will believe it is a duplicate and return 0, so return 1 instead
|
||||
if (compare == 0 && !x.Equals(y))
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
return compare;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Priority that a device will receive power if powernet cannot supply every device
|
||||
/// </summary>
|
||||
public enum Priority
|
||||
{
|
||||
Necessary,
|
||||
High,
|
||||
Medium,
|
||||
Low,
|
||||
Provider,
|
||||
Unnecessary
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// All the devices that have been depowered by this powernet or depowered prior to being absorted into this powernet
|
||||
/// </summary>
|
||||
public List<PowerDeviceComponent> DepoweredDevices { get; set; } = new List<PowerDeviceComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of the energy storage components that will feed the powernet if necessary, and if there is enough power feed itself
|
||||
/// </summary>
|
||||
public List<PowerStorageComponent> PowerStorageSupplierlist { get; set; } = new List<PowerStorageComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// A list of energy storage components that will never feed the powernet, will try to draw energy to feed themselves if possible
|
||||
/// </summary>
|
||||
public List<PowerStorageComponent> PowerStorageConsumerlist { get; set; } = new List<PowerStorageComponent>();
|
||||
|
||||
/// <summary>
|
||||
/// Static counter of all continuous load placed from devices on this power network
|
||||
/// </summary>
|
||||
public float Load { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Static counter of all continiuous supply from generators on this power network
|
||||
/// </summary>
|
||||
public float Supply { get; private set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// Variable that causes powernet to be regenerated from its wires during the next update cycle
|
||||
/// </summary>
|
||||
public bool Dirty { get; set; } = false;
|
||||
|
||||
public void Update(float frametime)
|
||||
{
|
||||
float activesupply = Supply;
|
||||
float activeload = Load;
|
||||
|
||||
float storagedemand = 0;
|
||||
|
||||
foreach (var supply in PowerStorageConsumerlist)
|
||||
{
|
||||
storagedemand += supply.RequestCharge();
|
||||
}
|
||||
|
||||
float passivesupply = 0;
|
||||
float passivedemand = 0;
|
||||
|
||||
foreach (var supply in PowerStorageSupplierlist)
|
||||
{
|
||||
passivesupply += supply.AvailableCharge();
|
||||
passivedemand += supply.RequestCharge();
|
||||
}
|
||||
|
||||
|
||||
//If we have enough power to feed all load and storage demand, then feed everything
|
||||
if (activesupply > activeload + storagedemand + passivedemand)
|
||||
{
|
||||
PowerAllDevices();
|
||||
ChargeActiveStorage();
|
||||
ChargePassiveStorage();
|
||||
}
|
||||
//We don't have enough power for the storage powernet suppliers, ignore powering them
|
||||
else if (activesupply > activeload + storagedemand)
|
||||
{
|
||||
PowerAllDevices();
|
||||
ChargeActiveStorage();
|
||||
}
|
||||
//We require the storage powernet suppliers to power the remaining storage components and device load
|
||||
else if (activesupply + passivesupply > activeload + storagedemand)
|
||||
{
|
||||
PowerAllDevices();
|
||||
ChargeActiveStorage();
|
||||
RetrievePassiveStorage();
|
||||
}
|
||||
//We cant afford to fund the storage components, so lets try to power the basic load using our supply and storage supply
|
||||
else if (activesupply + passivesupply > activeload)
|
||||
{
|
||||
PowerAllDevices();
|
||||
RetrievePassiveStorage();
|
||||
}
|
||||
//We cant even cover the basic device load, start disabling devices in order of priority until the remaining load is lowered enough to be met
|
||||
else if (activesupply + passivesupply < activeload)
|
||||
{
|
||||
PowerAllDevices(); //This merely makes our inevitable betrayal all the sweeter
|
||||
RetrievePassiveStorage();
|
||||
|
||||
var depowervalue = activeload - (activesupply + passivesupply);
|
||||
|
||||
//Providers use same method to recreate functionality
|
||||
foreach(var device in Deviceloadlist)
|
||||
{
|
||||
device.Powered = false;
|
||||
DepoweredDevices.Add(device);
|
||||
depowervalue -= device.Load;
|
||||
if (depowervalue < 0)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void PowerAllDevices()
|
||||
{
|
||||
foreach(var device in DepoweredDevices)
|
||||
{
|
||||
device.Powered = true;
|
||||
}
|
||||
DepoweredDevices.Clear();
|
||||
}
|
||||
|
||||
private void ChargeActiveStorage()
|
||||
{
|
||||
foreach (var storage in PowerStorageConsumerlist)
|
||||
{
|
||||
storage.ChargePowerTick();
|
||||
}
|
||||
}
|
||||
|
||||
private void ChargePassiveStorage()
|
||||
{
|
||||
foreach (var storage in PowerStorageSupplierlist)
|
||||
{
|
||||
storage.ChargePowerTick();
|
||||
}
|
||||
}
|
||||
|
||||
private void RetrievePassiveStorage()
|
||||
{
|
||||
foreach (var storage in PowerStorageSupplierlist)
|
||||
{
|
||||
storage.ChargePowerTick();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Kills a powernet after it is marked dirty and its component have already been regenerated by the powernet system
|
||||
/// </summary>
|
||||
public void DirtyKill()
|
||||
{
|
||||
Wirelist.Clear();
|
||||
while(Nodelist.Count != 0)
|
||||
{
|
||||
Nodelist[0].DisconnectFromPowernet();
|
||||
}
|
||||
Generatorlist.Clear();
|
||||
Deviceloadlist.Clear();
|
||||
DepoweredDevices.Clear();
|
||||
PowerStorageSupplierlist.Clear();
|
||||
PowerStorageConsumerlist.Clear();
|
||||
|
||||
RemoveFromSystem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Combines two powernets when they connect via powertransfer components
|
||||
/// </summary>
|
||||
public void MergePowernets(Powernet toMerge)
|
||||
{
|
||||
//TODO: load balance reconciliation between powernets on merge tick here
|
||||
|
||||
foreach (var wire in toMerge.Wirelist)
|
||||
{
|
||||
wire.Parent = this;
|
||||
}
|
||||
Wirelist.AddRange(toMerge.Wirelist);
|
||||
toMerge.Wirelist.Clear();
|
||||
|
||||
foreach (var node in toMerge.Nodelist)
|
||||
{
|
||||
node.Parent = this;
|
||||
}
|
||||
Nodelist.AddRange(toMerge.Nodelist);
|
||||
toMerge.Nodelist.Clear();
|
||||
|
||||
foreach (var generator in toMerge.Generatorlist)
|
||||
{
|
||||
Generatorlist.Add(generator.Key, generator.Value);
|
||||
}
|
||||
toMerge.Generatorlist.Clear();
|
||||
|
||||
foreach (var device in toMerge.Deviceloadlist)
|
||||
{
|
||||
Deviceloadlist.Add(device);
|
||||
}
|
||||
toMerge.Deviceloadlist.Clear();
|
||||
|
||||
DepoweredDevices.AddRange(toMerge.DepoweredDevices);
|
||||
toMerge.DepoweredDevices.Clear();
|
||||
|
||||
PowerStorageSupplierlist.AddRange(toMerge.PowerStorageSupplierlist);
|
||||
toMerge.PowerStorageSupplierlist.Clear();
|
||||
|
||||
PowerStorageConsumerlist.AddRange(toMerge.PowerStorageConsumerlist);
|
||||
toMerge.PowerStorageConsumerlist.Clear();
|
||||
|
||||
toMerge.RemoveFromSystem();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes reference from the powernets list on the powernet system
|
||||
/// </summary>
|
||||
private void RemoveFromSystem()
|
||||
{
|
||||
var EntitySystemManager = IoCManager.Resolve<IEntitySystemManager>();
|
||||
EntitySystemManager.GetEntitySystem<PowerSystem>().Powernets.Remove(this);
|
||||
}
|
||||
|
||||
#region Registration
|
||||
|
||||
/// <summary>
|
||||
/// Register a continuous load from a device connected to the powernet
|
||||
/// </summary>
|
||||
public void AddDevice(PowerDeviceComponent device)
|
||||
{
|
||||
Deviceloadlist.Add(device);
|
||||
Load += device.Load;
|
||||
if (!device.Powered)
|
||||
DepoweredDevices.Add(device);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update one of the loads from a deviceconnected to the powernet
|
||||
/// </summary>
|
||||
public void UpdateDevice(PowerDeviceComponent device, float oldLoad)
|
||||
{
|
||||
if(Deviceloadlist.Contains(device))
|
||||
{
|
||||
Load -= oldLoad;
|
||||
Load += device.Load;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a continuous load from a device connected to the powernet
|
||||
/// </summary>
|
||||
public void RemoveDevice(PowerDeviceComponent device)
|
||||
{
|
||||
if(Deviceloadlist.Contains(device))
|
||||
{
|
||||
Load -= device.Load;
|
||||
Deviceloadlist.Remove(device);
|
||||
if (DepoweredDevices.Contains(device))
|
||||
DepoweredDevices.Remove(device);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = device.Owner.Prototype.Name;
|
||||
Logger.Log(String.Format("We tried to remove a device twice from the same powernet somehow, prototype {0}", name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a power supply from a generator connected to the powernet
|
||||
/// </summary>
|
||||
public void AddGenerator(PowerGeneratorComponent generator)
|
||||
{
|
||||
Generatorlist.Add(generator, generator.Supply);
|
||||
Supply += generator.Supply;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update the value supplied from a generator connected to the powernet
|
||||
/// </summary>
|
||||
public void UpdateGenerator(PowerGeneratorComponent generator)
|
||||
{
|
||||
if (Generatorlist.ContainsKey(generator))
|
||||
{
|
||||
Supply -= Generatorlist[generator];
|
||||
Generatorlist[generator] = generator.Supply;
|
||||
Supply += generator.Supply;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a power supply from a generator connected to the powernet
|
||||
/// </summary>
|
||||
public void RemoveGenerator(PowerGeneratorComponent generator)
|
||||
{
|
||||
if (Generatorlist.ContainsKey(generator))
|
||||
{
|
||||
Supply -= Generatorlist[generator];
|
||||
Generatorlist.Remove(generator);
|
||||
}
|
||||
else
|
||||
{
|
||||
var name = generator.Owner.Prototype.Name;
|
||||
Logger.Log(String.Format("We tried to remove a device twice from the same power somehow, prototype {1}", name));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register a power supply from a generator connected to the powernet
|
||||
/// </summary>
|
||||
public void AddPowerStorage(PowerStorageComponent storage)
|
||||
{
|
||||
if(storage.ChargePowernet)
|
||||
PowerStorageSupplierlist.Add(storage);
|
||||
else
|
||||
PowerStorageConsumerlist.Add(storage);
|
||||
}
|
||||
|
||||
//How do I even call this? TODO: fix
|
||||
public void UpdateStorageType(PowerStorageComponent storage)
|
||||
{
|
||||
//If our chargepowernet settings change we need to tell the powernet of this new setting and remove traces of our old setting
|
||||
if (PowerStorageSupplierlist.Contains(storage))
|
||||
PowerStorageSupplierlist.Remove(storage);
|
||||
if (PowerStorageConsumerlist.Contains(storage))
|
||||
PowerStorageConsumerlist.Remove(storage);
|
||||
|
||||
//Apply new setting
|
||||
if (storage.ChargePowernet)
|
||||
PowerStorageSupplierlist.Add(storage);
|
||||
else
|
||||
PowerStorageConsumerlist.Add(storage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove a power supply from a generator connected to the powernet
|
||||
/// </summary>
|
||||
public void RemovePowerStorage(PowerStorageComponent storage)
|
||||
{
|
||||
if (PowerStorageSupplierlist.Contains(storage))
|
||||
{
|
||||
PowerStorageSupplierlist.Remove(storage);
|
||||
}
|
||||
if (PowerStorageConsumerlist.Contains(storage))
|
||||
{
|
||||
PowerStorageSupplierlist.Remove(storage);
|
||||
}
|
||||
}
|
||||
#endregion Registration
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Physics;
|
||||
using SS14.Shared.Interfaces.GameObjects.Components;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using SS14.Shared.Utility;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Projectiles
|
||||
{
|
||||
public class ProjectileComponent : Component, ICollideSpecial, ICollideBehavior
|
||||
{
|
||||
public override string Name => "Projectile";
|
||||
|
||||
public bool IgnoreShooter = true;
|
||||
|
||||
private EntityUid Shooter = EntityUid.Invalid;
|
||||
|
||||
public Dictionary<DamageType, int> damages = new Dictionary<DamageType, int>();
|
||||
|
||||
/// <summary>
|
||||
/// Function that makes the collision of this object ignore a specific entity so we don't collide with ourselves
|
||||
/// </summary>
|
||||
/// <param name="shooter"></param>
|
||||
public void IgnoreEntity(IEntity shooter)
|
||||
{
|
||||
Shooter = shooter.Uid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Special collision override, can be used to give custom behaviors deciding when to collide
|
||||
/// </summary>
|
||||
/// <param name="collidedwith"></param>
|
||||
/// <returns></returns>
|
||||
bool ICollideSpecial.PreventCollide(ICollidable collidedwith)
|
||||
{
|
||||
if (IgnoreShooter && collidedwith.Owner.Uid == Shooter)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applys the damage when our projectile collides with its victim
|
||||
/// </summary>
|
||||
/// <param name="collidedwith"></param>
|
||||
void ICollideBehavior.CollideWith(List<IEntity> collidedwith)
|
||||
{
|
||||
foreach(var entity in collidedwith)
|
||||
{
|
||||
if(entity.TryGetComponent(out DamageableComponent damage))
|
||||
{
|
||||
damage.TakeDamage(DamageType.Brute, 10);
|
||||
}
|
||||
}
|
||||
|
||||
if (collidedwith.Count > 0)
|
||||
Owner.Delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using Content.Shared.Maths;
|
||||
using System;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.Utility;
|
||||
using YamlDotNet.RepresentationModel;
|
||||
using Content.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Handles changing temperature,
|
||||
/// informing others of the current temperature,
|
||||
/// and taking fire damage from high temperature.
|
||||
/// </summary>
|
||||
public class TemperatureComponent : Component, ITemperatureComponent
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string Name => "Temperature";
|
||||
|
||||
/// <inheritdoc />
|
||||
public override uint? NetID => ContentNetIDs.TEMPERATURE;
|
||||
|
||||
//TODO: should be programmatic instead of how it currently is
|
||||
public float CurrentTemperature { get; private set; } = PhysicalConstants.ZERO_CELCIUS;
|
||||
|
||||
float _fireDamageThreshold = 0;
|
||||
float _fireDamageCoefficient = 1;
|
||||
|
||||
float _secondsSinceLastDamageUpdate = 0;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void LoadParameters(YamlMappingNode mapping)
|
||||
{
|
||||
YamlNode node;
|
||||
|
||||
if (mapping.TryGetNode("firedamagethreshold", out node))
|
||||
{
|
||||
_fireDamageThreshold = node.AsFloat();
|
||||
}
|
||||
if (mapping.TryGetNode("firedamagecoefficient", out node))
|
||||
{
|
||||
_fireDamageCoefficient = node.AsFloat();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
int fireDamage = (int)Math.Floor(Math.Max(0, CurrentTemperature - _fireDamageThreshold) / _fireDamageCoefficient);
|
||||
|
||||
_secondsSinceLastDamageUpdate += frameTime;
|
||||
|
||||
Owner.TryGetComponent<DamageableComponent>(out DamageableComponent component);
|
||||
|
||||
while (_secondsSinceLastDamageUpdate >= 1)
|
||||
{
|
||||
component?.TakeDamage(DamageType.Heat, fireDamage);
|
||||
_secondsSinceLastDamageUpdate -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
using System;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.Serialization;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.GameObjects.EntitySystemMessages;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Melee
|
||||
{
|
||||
public class MeleeWeaponComponent : Component, IAfterAttack
|
||||
{
|
||||
public override string Name => "MeleeWeapon";
|
||||
|
||||
public int Damage = 1;
|
||||
public float Range = 1;
|
||||
public float ArcWidth = 90;
|
||||
|
||||
public override void ExposeData(EntitySerializer serializer)
|
||||
{
|
||||
base.ExposeData(serializer);
|
||||
|
||||
serializer.DataField(ref Damage, "damage", 5);
|
||||
serializer.DataField(ref Range, "damage", 1);
|
||||
serializer.DataField(ref ArcWidth, "damage", 90);
|
||||
|
||||
}
|
||||
|
||||
void IAfterAttack.Afterattack(IEntity user, LocalCoordinates clicklocation)
|
||||
{
|
||||
var location = user.GetComponent<TransformComponent>().LocalPosition;
|
||||
var angle = new Angle(clicklocation.ToWorld().Position - location.ToWorld().Position);
|
||||
var entities = IoCManager.Resolve<IServerEntityManager>().GetEntitiesInArc(user.GetComponent<TransformComponent>().LocalPosition, Range, angle, ArcWidth);
|
||||
|
||||
foreach(var entity in entities)
|
||||
{
|
||||
if (!entity.GetComponent<TransformComponent>().IsMapTransform || entity == user)
|
||||
continue;
|
||||
|
||||
if(entity.TryGetComponent(out DamageableComponent damagecomponent))
|
||||
{
|
||||
damagecomponent.TakeDamage(DamageType.Brute, Damage);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.GameObjects.EntitySystems;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.EntitySystemMessages;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Physics;
|
||||
using SS14.Shared.Interfaces.Timing;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
using SS14.Shared.Physics;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Hitscan
|
||||
{
|
||||
public class HitscanWeaponComponent : RangedWeaponComponent
|
||||
{
|
||||
public override string Name => "HitscanWeapon";
|
||||
|
||||
string spritename = "laser";
|
||||
|
||||
protected override void Fire(IEntity user, LocalCoordinates clicklocation)
|
||||
{
|
||||
var userposition = user.GetComponent<TransformComponent>().WorldPosition; //Remember world positions are ephemeral and can only be used instantaneously
|
||||
var angle = new Angle(clicklocation.Position - userposition);
|
||||
var theta = angle.Theta;
|
||||
|
||||
var ray = new Ray(userposition, angle.ToVec());
|
||||
var raycastresults = IoCManager.Resolve<ICollisionManager>().IntersectRay(ray, 20, Owner.GetComponent<TransformComponent>().GetMapTransform().Owner);
|
||||
|
||||
Hit(raycastresults);
|
||||
AfterEffects(user, raycastresults, theta);
|
||||
}
|
||||
|
||||
protected virtual void Hit(RayCastResults ray)
|
||||
{
|
||||
if(ray.HitEntity != null && ray.HitEntity.TryGetComponent(out DamageableComponent damage))
|
||||
{
|
||||
damage.TakeDamage(DamageType.Heat, 10);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void AfterEffects(IEntity user, RayCastResults ray, double theta)
|
||||
{
|
||||
var time = IoCManager.Resolve<IGameTiming>().CurTime;
|
||||
EffectSystemMessage message = new EffectSystemMessage
|
||||
{
|
||||
EffectSprite = spritename,
|
||||
Born = time,
|
||||
DeathTime = time + TimeSpan.FromSeconds(1),
|
||||
Size = new Vector2(ray.Distance, 1f),
|
||||
Coordinates = user.GetComponent<TransformComponent>().LocalPosition,
|
||||
//Rotated from east facing
|
||||
Rotation = (float)theta,
|
||||
ColorDelta = new Vector4(0, 0, 0, -1500f),
|
||||
Color = new Vector4(255,255,255,750)
|
||||
};
|
||||
IoCManager.Resolve<IEntitySystemManager>().GetEntitySystem<EffectSystem>().CreateParticle(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
using Content.Server.GameObjects.Components.Projectiles;
|
||||
using SS14.Server.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Projectile
|
||||
{
|
||||
public class ProjectileWeaponComponent : RangedWeaponComponent
|
||||
{
|
||||
public override string Name => "ProjectileWeapon";
|
||||
|
||||
private string _ProjectilePrototype = "ProjectileBullet";
|
||||
|
||||
private float _velocity = 20f;
|
||||
|
||||
protected override void Fire(IEntity user, LocalCoordinates clicklocation)
|
||||
{
|
||||
var userposition = user.GetComponent<TransformComponent>().LocalPosition; //Remember world positions are ephemeral and can only be used instantaneously
|
||||
var angle = new Angle(clicklocation.Position - userposition.Position);
|
||||
|
||||
var theta = angle.Theta;
|
||||
|
||||
|
||||
//Spawn the projectileprototype
|
||||
IEntity projectile = IoCManager.Resolve<IServerEntityManager>().ForceSpawnEntityAt(_ProjectilePrototype, userposition);
|
||||
|
||||
//Give it the velocity we fire from this weapon, and make sure it doesn't shoot our character
|
||||
projectile.GetComponent<ProjectileComponent>().IgnoreEntity(user);
|
||||
|
||||
//Give it the velocity this weapon gives to things it fires from itself
|
||||
projectile.GetComponent<PhysicsComponent>().LinearVelocity = angle.ToVec() * _velocity;
|
||||
|
||||
//Rotate the bullets sprite to the correct direction, from north facing I guess
|
||||
projectile.GetComponent<TransformComponent>().LocalRotation = angle.Theta;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using Content.Server.GameObjects.EntitySystems;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using SS14.Shared.Map;
|
||||
|
||||
namespace Content.Server.GameObjects.Components.Weapon.Ranged
|
||||
{
|
||||
public class RangedWeaponComponent : Component, IAfterAttack
|
||||
{
|
||||
public override string Name => "RangedWeapon";
|
||||
|
||||
void IAfterAttack.Afterattack(IEntity user, LocalCoordinates clicklocation)
|
||||
{
|
||||
if(UserCanFire(user) && WeaponCanFire())
|
||||
{
|
||||
Fire(user, clicklocation);
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual bool WeaponCanFire()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual bool UserCanFire(IEntity user)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
protected virtual void Fire(IEntity user, LocalCoordinates clicklocation)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
331
Content.Server/GameObjects/EntitySystems/InteractionSystem.cs
Normal file
@@ -0,0 +1,331 @@
|
||||
using Content.Server.Interfaces.GameObjects;
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.GameObjects;
|
||||
using SS14.Shared.GameObjects.System;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using SS14.Shared.Input;
|
||||
using SS14.Shared.Interfaces.Network;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Server.Interfaces.Player;
|
||||
using SS14.Shared.Log;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.GameObjects.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This interface gives components behavior when being clicked on or "attacked" by a user with an object in their hand
|
||||
/// </summary>
|
||||
public interface IAttackby
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when using one object on another
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="attackwith"></param>
|
||||
/// <returns></returns>
|
||||
bool Attackby(IEntity user, IEntity attackwith);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface gives components behavior when being clicked on or "attacked" by a user with an empty hand
|
||||
/// </summary>
|
||||
public interface IAttackHand
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when a player directly interacts with an empty hand
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
bool Attackhand(IEntity user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface gives components behavior when being clicked by objects outside the range of direct use
|
||||
/// </summary>
|
||||
public interface IRangedAttackby
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when we try to interact with an entity out of range
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="attackwith"></param>
|
||||
/// <param name="clicklocation"></param>
|
||||
/// <returns></returns>
|
||||
bool RangedAttackby(IEntity user, IEntity attackwith, LocalCoordinates clicklocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface gives components a behavior when clicking on another object and no interaction occurs
|
||||
/// Doesn't pass what you clicked on as an argument, but if it becomes necessary we can add it later
|
||||
/// </summary>
|
||||
public interface IAfterAttack
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when we interact with nothing, or when we interact with an entity out of range that has no behavior
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="clicklocation"></param>
|
||||
void Afterattack(IEntity user, LocalCoordinates clicklocation);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This interface gives components behavior when using the entity in your hands
|
||||
/// </summary>
|
||||
public interface IUse
|
||||
{
|
||||
/// <summary>
|
||||
/// Called when we activate an object we are holding to use it
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <returns></returns>
|
||||
bool UseEntity(IEntity user);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Governs interactions during clicking on entities
|
||||
/// </summary>
|
||||
public class InteractionSystem : EntitySystem
|
||||
{
|
||||
private const float INTERACTION_RANGE = 2;
|
||||
private const float INTERACTION_RANGE_SQUARED = INTERACTION_RANGE * INTERACTION_RANGE;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void RegisterMessageTypes()
|
||||
{
|
||||
base.RegisterMessageTypes();
|
||||
|
||||
RegisterMessageType<ClickEventMessage>();
|
||||
}
|
||||
|
||||
//Grab click events sent from the client input system
|
||||
public override void HandleNetMessage(INetChannel channel, EntitySystemMessage message)
|
||||
{
|
||||
base.HandleNetMessage(channel, message);
|
||||
|
||||
var playerMan = IoCManager.Resolve<IPlayerManager>();
|
||||
var session = playerMan.GetSessionByChannel(channel);
|
||||
var playerentity = session.AttachedEntity;
|
||||
|
||||
if (playerentity == null)
|
||||
return;
|
||||
|
||||
switch (message)
|
||||
{
|
||||
case ClickEventMessage msg:
|
||||
UserInteraction(msg, playerentity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UserInteraction(ClickEventMessage msg, IEntity player)
|
||||
{
|
||||
//Verify click type
|
||||
if (msg.Click != ClickType.Left)
|
||||
return;
|
||||
|
||||
//Get entity clicked upon from UID if valid UID, if not assume no entity clicked upon and null
|
||||
IEntity attacked = null;
|
||||
if (msg.Uid.IsValid())
|
||||
attacked = EntityManager.GetEntity(msg.Uid);
|
||||
|
||||
//Verify player has a transform component
|
||||
if (!player.TryGetComponent<IServerTransformComponent>(out var playerTransform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
//Verify player is on the same map as the entity he clicked on
|
||||
else if (msg.Coordinates.MapID != playerTransform.MapID)
|
||||
{
|
||||
Logger.Warning(string.Format("Player named {0} clicked on a map he isn't located on", player.Name));
|
||||
return;
|
||||
}
|
||||
|
||||
//Verify player has a hand, and find what object he is currently holding in his active hand
|
||||
if (!player.TryGetComponent<IHandsComponent>(out var hands))
|
||||
{
|
||||
return;
|
||||
}
|
||||
var item = hands.GetHand(hands.ActiveIndex)?.Owner;
|
||||
|
||||
|
||||
//TODO: Mob status code that allows or rejects interactions based on current mob status
|
||||
//Check if client should be able to see that object to click on it in the first place, prevent using locaters by firing a laser or something
|
||||
|
||||
//Off entity click handling
|
||||
if (attacked == null)
|
||||
{
|
||||
if(item != null)
|
||||
{
|
||||
//AFTERATTACK: Check if we clicked on an empty location, if so the only interaction we can do is afterattack
|
||||
InteractAfterattack(player, item, msg.Coordinates);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
}
|
||||
//USE: Check if we clicked on the item we are holding in our active hand to use it
|
||||
else if(attacked == item && item != null)
|
||||
{
|
||||
UseInteraction(player, item);
|
||||
return;
|
||||
}
|
||||
|
||||
//Check if ClickLocation is in object bounds here, if not lets log as warning and see why
|
||||
if(attacked != null && attacked.TryGetComponent(out BoundingBoxComponent boundingbox))
|
||||
{
|
||||
if (!boundingbox.WorldAABB.Contains(msg.Coordinates.Position))
|
||||
{
|
||||
Logger.Warning(string.Format("Player {0} clicked {1} outside of its bounding box component somehow", player.Name, attacked.Name));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//RANGEDATTACK/AFTERATTACK: Check distance between user and clicked item, if too large parse it in the ranged function
|
||||
//TODO: have range based upon the item being used? or base it upon some variables of the player himself?
|
||||
var distance = (playerTransform.WorldPosition - attacked.GetComponent<IServerTransformComponent>().WorldPosition).LengthSquared;
|
||||
if (distance > INTERACTION_RANGE_SQUARED)
|
||||
{
|
||||
if(item != null)
|
||||
{
|
||||
RangedInteraction(player, item, attacked, msg.Coordinates);
|
||||
return;
|
||||
}
|
||||
return; //Add some form of ranged attackhand here if you need it someday, or perhaps just ways to modify the range of attackhand
|
||||
}
|
||||
|
||||
//We are close to the nearby object and the object isn't contained in our active hand
|
||||
//ATTACKBY/AFTERATTACK: We will either use the item on the nearby object
|
||||
if (item != null)
|
||||
{
|
||||
Interaction(player, item, attacked, msg.Coordinates);
|
||||
}
|
||||
//ATTACKHAND: Since our hand is empty we will use attackhand
|
||||
else
|
||||
{
|
||||
Interaction(player, attacked);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// We didn't click on any entity, try doing an afterattack on the click location
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="weapon"></param>
|
||||
/// <param name="clicklocation"></param>
|
||||
private void InteractAfterattack(IEntity user, IEntity weapon, LocalCoordinates clicklocation)
|
||||
{
|
||||
List<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().ToList();
|
||||
|
||||
for (var i = 0; i < afterattacks.Count; i++)
|
||||
{
|
||||
afterattacks[i].Afterattack(user, clicklocation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses a weapon/object on an entity
|
||||
/// Finds interactable components with the Attackby interface and calls their function
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="weapon"></param>
|
||||
/// <param name="attacked"></param>
|
||||
public static void Interaction(IEntity user, IEntity weapon, IEntity attacked, LocalCoordinates clicklocation)
|
||||
{
|
||||
List<IAttackby> interactables = attacked.GetComponents<IAttackby>().ToList();
|
||||
|
||||
for(var i = 0; i < interactables.Count; i++)
|
||||
{
|
||||
if (interactables[i].Attackby(user, weapon)) //If an attackby returns a status completion we finish our attack
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Else check damage component to see if we damage if not attackby, and if so can we attack object
|
||||
|
||||
|
||||
//If we aren't directly attacking the nearby object, lets see if our item has an after attack we can do
|
||||
List<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().ToList();
|
||||
|
||||
for (var i = 0; i < afterattacks.Count; i++)
|
||||
{
|
||||
afterattacks[i].Afterattack(user, clicklocation);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Uses an empty hand on an entity
|
||||
/// Finds interactable components with the Attackhand interface and calls their function
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="attacked"></param>
|
||||
public static void Interaction(IEntity user, IEntity attacked)
|
||||
{
|
||||
List<IAttackHand> interactables = attacked.GetComponents<IAttackHand>().ToList();
|
||||
|
||||
for (var i = 0; i < interactables.Count; i++)
|
||||
{
|
||||
if (interactables[i].Attackhand(user)) //If an attackby returns a status completion we finish our attack
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//Else check damage component to see if we damage if not attackby, and if so can we attack object
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Activates/Uses an object in control/possession of a user
|
||||
/// If the item has the IUse interface on one of its components we use the object in our hand
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="attacked"></param>
|
||||
public static void UseInteraction(IEntity user, IEntity used)
|
||||
{
|
||||
List<IUse> usables = used.GetComponents<IUse>().ToList();
|
||||
|
||||
//Try to use item on any components which have the interface
|
||||
for (var i = 0; i < usables.Count; i++)
|
||||
{
|
||||
if (usables[i].UseEntity(user)) //If an attackby returns a status completion we finish our attack
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Will have two behaviors, either "uses" the weapon at range on the entity if it is capable of accepting that action
|
||||
/// Or it will use the weapon itself on the position clicked, regardless of what was there
|
||||
/// </summary>
|
||||
/// <param name="user"></param>
|
||||
/// <param name="weapon"></param>
|
||||
/// <param name="attacked"></param>
|
||||
public static void RangedInteraction(IEntity user, IEntity weapon, IEntity attacked, LocalCoordinates clicklocation)
|
||||
{
|
||||
List<IRangedAttackby> rangedusables = attacked.GetComponents<IRangedAttackby>().ToList();
|
||||
|
||||
//See if we have a ranged attack interaction
|
||||
for (var i = 0; i < rangedusables.Count; i++)
|
||||
{
|
||||
if (rangedusables[i].RangedAttackby(user, weapon, clicklocation)) //If an attackby returns a status completion we finish our attack
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(weapon != null)
|
||||
{
|
||||
List<IAfterAttack> afterattacks = weapon.GetComponents<IAfterAttack>().ToList();
|
||||
|
||||
//See if we have a ranged attack interaction
|
||||
for (var i = 0; i < afterattacks.Count; i++)
|
||||
{
|
||||
afterattacks[i].Afterattack(user, clicklocation);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
47
Content.Server/GameObjects/EntitySystems/PowerSystem.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
using Content.Server.GameObjects.Components.Power;
|
||||
using SS14.Shared.GameObjects.System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Content.Shared.GameObjects.EntitySystems
|
||||
{
|
||||
public class PowerSystem : EntitySystem
|
||||
{
|
||||
public List<Powernet> Powernets = new List<Powernet>();
|
||||
|
||||
public override void Update(float frametime)
|
||||
{
|
||||
for (int i = 0; i < Powernets.Count; i++)
|
||||
{
|
||||
var powernet = Powernets[i];
|
||||
if (powernet.Dirty)
|
||||
{
|
||||
//Tell all the wires of this net to be prepared to create/join new powernets
|
||||
foreach (var wire in powernet.Wirelist)
|
||||
{
|
||||
wire.Regenerating = true;
|
||||
}
|
||||
|
||||
foreach (var wire in powernet.Wirelist)
|
||||
{
|
||||
//Only a few wires should pass this if check since each will create and take all the others into its powernet
|
||||
if (wire.Regenerating)
|
||||
wire.SpreadPowernet();
|
||||
}
|
||||
|
||||
//At this point all wires will have found/joined new powernet, all capable nodes will have joined them as well and removed themselves from nodelist
|
||||
powernet.DirtyKill();
|
||||
i--;
|
||||
}
|
||||
}
|
||||
|
||||
foreach(var powernet in Powernets)
|
||||
{
|
||||
powernet.Update(frametime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
using Content.Server.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
{
|
||||
public interface IDamageableComponent : IComponent
|
||||
{
|
||||
event EventHandler<DamageThresholdPassedEventArgs> DamageThresholdPassed;
|
||||
ResistanceSet Resistances { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The function that handles receiving damage.
|
||||
/// Converts damage via the resistance set then applies it
|
||||
/// and informs components of thresholds passed as necessary.
|
||||
/// </summary>
|
||||
/// <param name="damageType">Type of damage being received.</param>
|
||||
/// <param name="amount">Amount of damage being received.</param>
|
||||
void TakeDamage(DamageType damageType, int amount);
|
||||
|
||||
/// <summary>
|
||||
/// Handles receiving healing.
|
||||
/// Converts healing via the resistance set then applies it
|
||||
/// and informs components of thresholds passed as necessary.
|
||||
/// </summary>
|
||||
/// <param name="damageType">Type of damage being received.</param>
|
||||
/// <param name="amount">Amount of damage being received.</param>
|
||||
void TakeHealing(DamageType damageType, int amount);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
{
|
||||
public interface IHandsComponent : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The hand index of the currently active hand.
|
||||
/// </summary>
|
||||
string ActiveIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Enumerates over every held item.
|
||||
/// </summary>
|
||||
IEnumerable<IItemComponent> GetAllHeldItems();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the item held by a hand.
|
||||
/// </summary>
|
||||
/// <param name="index">The index of the hand to get.</param>
|
||||
/// <returns>The item in the held, null if no item is held</returns>
|
||||
IItemComponent GetHand(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Puts an item into any empty hand, preferring the active hand.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to put in a hand.</param>
|
||||
/// <returns>True if the item was inserted, false otherwise.</returns>
|
||||
bool PutInHand(IItemComponent item);
|
||||
|
||||
/// <summary>
|
||||
/// Puts an item into a specific hand.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to put in the hand.</param>
|
||||
/// <param name="index">The index of the hand to put the item into.</param>
|
||||
/// <param name="fallback">
|
||||
/// If true and the provided hand is full, the method will fall back to <see cref="PutInHand(IItemComponent)" />
|
||||
/// </param>
|
||||
/// <returns>True if the item was inserted into a hand, false otherwise.</returns>
|
||||
bool PutInHand(IItemComponent item, string index, bool fallback=true);
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if an item can be put in any hand.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to check for.</param>
|
||||
/// <returns>True if the item can be inserted, false otherwise.</returns>
|
||||
bool CanPutInHand(IItemComponent item);
|
||||
|
||||
/// <summary>
|
||||
/// Checks to see if an item can be put in the specified hand.
|
||||
/// </summary>
|
||||
/// <param name="item">The item to check for.</param>
|
||||
/// <param name="index">The index for the hand to check for.</param>
|
||||
/// <returns>True if the item can be inserted, false otherwise.</returns>
|
||||
bool CanPutInHand(IItemComponent item, string index);
|
||||
|
||||
/// <summary>
|
||||
/// Drops an item on the ground, removing it from the hand.
|
||||
/// </summary>
|
||||
/// <param name="index">The hand to drop from.</param>
|
||||
/// <returns>True if an item was successfully dropped, false otherwise.</returns>
|
||||
bool Drop(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether the item in the specified hand can be dropped.
|
||||
/// </summary>
|
||||
/// <param name="index">The hand to check for.</param>
|
||||
/// <returns>
|
||||
/// True if the item can be dropped, false if the hand is empty or the item in the hand cannot be dropped.
|
||||
/// </returns>
|
||||
bool CanDrop(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new hand to this hands component.
|
||||
/// </summary>
|
||||
/// <param name="index">The name of the hand to add.</param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if a hand with specified name already exists.
|
||||
/// </exception>
|
||||
void AddHand(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a hand from this hands component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the hand contains an item, the item is dropped.
|
||||
/// </remarks>
|
||||
/// <param name="index">The name of the hand to remove.</param>
|
||||
void RemoveHand(string index);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a hand with the specified name exists.
|
||||
/// </summary>
|
||||
/// <param name="index">The hand name to check.</param>
|
||||
/// <returns>True if the hand exists, false otherwise.</returns>
|
||||
bool HasHand(string index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
using System;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
{
|
||||
public interface IInventoryComponent : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the item in the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">The slot to get the item for.</param>
|
||||
/// <returns>Null if the slot is empty, otherwise the item.</returns>
|
||||
IItemComponent Get(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the slot with specified name.
|
||||
/// This gets the slot, NOT the item contained therein.
|
||||
/// </summary>
|
||||
/// <param name="slot">The name of the slot to get.</param>
|
||||
IInventorySlot GetSlot(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Puts an item in a slot.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This will fail if there is already an item in the specified slot.
|
||||
/// </remarks>
|
||||
/// <param name="slot">The slot to put the item in.</param>
|
||||
/// <param name="item">The item to insert into the slot.</param>
|
||||
/// <returns>True if the item was successfully inserted, false otherwise.</returns>
|
||||
bool Insert(string slot, IItemComponent item);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an item can be put in the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">The slot to check for.</param>
|
||||
/// <param name="item">The item to check for.</param>
|
||||
/// <returns>True if the item can be inserted into the specified slot.</returns>
|
||||
bool CanInsert(string slot, IItemComponent item);
|
||||
|
||||
/// <summary>
|
||||
/// Drops the item in a slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">The slot to drop the item from.</param>
|
||||
/// <returns>True if an item was dropped, false otherwise.</returns>
|
||||
bool Drop(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether an item can be dropped from the specified slot.
|
||||
/// </summary>
|
||||
/// <param name="slot">The slot to check for.</param>
|
||||
/// <returns>
|
||||
/// True if there is an item in the slot and it can be dropped, false otherwise.
|
||||
/// </returns>
|
||||
bool CanDrop(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Adds a new slot to this inventory component.
|
||||
/// </summary>
|
||||
/// <param name="slot">The name of the slot to add.</param>
|
||||
/// <exception cref="InvalidOperationException">
|
||||
/// Thrown if the slot with specified name already exists.
|
||||
/// </exception>
|
||||
IInventorySlot AddSlot(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Removes a slot from this inventory component.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// If the slot contains an item, the item is dropped.
|
||||
/// </remarks>
|
||||
/// <param name="slot">The name of the slot to remove.</param>
|
||||
void RemoveSlot(string slot);
|
||||
|
||||
/// <summary>
|
||||
/// Checks whether a slot with the specified name exists.
|
||||
/// </summary>
|
||||
/// <param name="slot">The slot name to check.</param>
|
||||
/// <returns>True if the slot exists, false otherwise.</returns>
|
||||
bool HasSlot(string slot);
|
||||
}
|
||||
|
||||
public interface IInventorySlot
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the slot.
|
||||
/// </summary>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The item contained in the slot, can be null.
|
||||
/// </summary>
|
||||
IItemComponent Item { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The component owning us.
|
||||
/// </summary>
|
||||
IInventoryComponent Owner { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
{
|
||||
public interface IItemComponent : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// The inventory slot this item is stored in, if any.
|
||||
/// </summary>
|
||||
IInventorySlot ContainingSlot { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Called when the item is removed from its inventory slot.
|
||||
/// </summary>
|
||||
void RemovedFromSlot();
|
||||
|
||||
/// <summary>
|
||||
/// Called when the item is inserted into a new inventory slot.
|
||||
/// </summary>
|
||||
void EquippedToSlot(IInventorySlot slot);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using SS14.Shared.Interfaces.GameObjects;
|
||||
|
||||
namespace Content.Server.Interfaces.GameObjects
|
||||
{
|
||||
public interface ITemperatureComponent : IComponent
|
||||
{
|
||||
float CurrentTemperature { get; }
|
||||
}
|
||||
}
|
||||
27
Content.Server/Interfaces/GameObjects/IOnDamageBehavior.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Interfaces
|
||||
{
|
||||
/// <summary>
|
||||
/// Any component/entity that has behaviour linked to taking damage should implement this interface.
|
||||
/// TODO: Don't know how to work around this currently, but due to how events work
|
||||
/// you need to hook it up to the DamageableComponent via Initialize().
|
||||
/// See DestructibleComponent.Initialize() for an example.
|
||||
/// </summary>
|
||||
interface IOnDamageBehavior
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets a list of all DamageThresholds this component/entity are interested in.
|
||||
/// </summary>
|
||||
/// <returns>List of DamageThresholds to be added to DamageableComponent for watching.</returns>
|
||||
List<DamageThreshold> GetAllDamageThresholds();
|
||||
|
||||
/// <summary>
|
||||
/// Damage threshold passed event hookup.
|
||||
/// </summary>
|
||||
/// <param name="obj">Damageable component.</param>
|
||||
/// <param name="e">Damage threshold and whether it's passed in one way or another.</param>
|
||||
void OnDamageThresholdPassed(object obj, DamageThresholdPassedEventArgs e);
|
||||
}
|
||||
}
|
||||
30
Content.Server/Placement/SpawnHelpers.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using SS14.Server.Interfaces.GameObjects;
|
||||
using SS14.Shared.Interfaces.Map;
|
||||
using SS14.Shared.IoC;
|
||||
using SS14.Shared.Map;
|
||||
using SS14.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Placement
|
||||
{
|
||||
/// <summary>
|
||||
/// Helper function for spawning more complex multi-entity structures
|
||||
/// </summary>
|
||||
public static class SpawnHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Spawns a spotlight ground turret that will track any living entities in range.
|
||||
/// </summary>
|
||||
/// <param name="grid"></param>
|
||||
/// <param name="localPosition"></param>
|
||||
public static void SpawnLightTurret(IMapGrid grid, Vector2 localPosition)
|
||||
{
|
||||
var entMan = IoCManager.Resolve<IServerEntityManager>();
|
||||
var tBase = entMan.SpawnEntity("TurretBase");
|
||||
tBase.GetComponent<IServerTransformComponent>().LocalPosition = new LocalCoordinates(localPosition, grid);
|
||||
|
||||
var tTop = entMan.SpawnEntity("TurretTopLight");
|
||||
tTop.GetComponent<IServerTransformComponent>().LocalPosition = new LocalCoordinates(localPosition, grid);
|
||||
tTop.GetComponent<IServerTransformComponent>().AttachParent(tBase);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Content.Server/app.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.86.0.518" newVersion="0.86.0.518" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
6
Content.Server/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="OpenTK" version="3.0.0-pre" targetFramework="net451" />
|
||||
<package id="System.ValueTuple" version="4.3.1" targetFramework="net451" />
|
||||
<package id="YamlDotNet" version="4.3.0" targetFramework="net451" />
|
||||
</packages>
|
||||
@@ -11,23 +11,31 @@
|
||||
<AssemblyName>Content.Shared</AssemblyName>
|
||||
<TargetFrameworkVersion>v4.5.1</TargetFrameworkVersion>
|
||||
<FileAlignment>512</FileAlignment>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<OutputPath>bin\x86\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
|
||||
<OutputPath>bin\x86\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x86</PlatformTarget>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
|
||||
<DebugSymbols>true</DebugSymbols>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DebugType>full</DebugType>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<Optimize>true</Optimize>
|
||||
<PlatformTarget>x64</PlatformTarget>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
@@ -38,10 +46,21 @@
|
||||
<Reference Include="System.Data" />
|
||||
<Reference Include="System.Net.Http" />
|
||||
<Reference Include="System.Xml" />
|
||||
<Reference Include="OpenTK, Version=3.0.0.0, Culture=neutral, PublicKeyToken=bad199fe84eb3df4, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\OpenTK.3.0.0-pre\lib\net20\OpenTK.dll</HintPath>
|
||||
</Reference>
|
||||
<Reference Include="YamlDotNet, Version=4.3.0.0, Culture=neutral, processorArchitecture=MSIL">
|
||||
<HintPath>$(SolutionDir)packages\YamlDotNet.4.3.0\lib\net45\YamlDotNet.dll</HintPath>
|
||||
</Reference>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="EntryPoint.cs" />
|
||||
<Compile Include="GameObjects\Components\Doors\SharedDoorComponent.cs" />
|
||||
<Compile Include="GameObjects\ContentNetIDs.cs" />
|
||||
<Compile Include="GameObjects\PhysicalConstants.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="GameObjects\Components\Items\SharedHandsComponent.cs" />
|
||||
<Compile Include="Maths\PhysicalConstants.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\engine\Lidgren.Network\Lidgren.Network.csproj">
|
||||
@@ -64,8 +83,9 @@
|
||||
</ItemGroup>
|
||||
<ItemDefinitionGroup>
|
||||
<Resource>
|
||||
<Visible>False</Visible>
|
||||
<Prefix></Prefix>
|
||||
<Visible>False</Visible>
|
||||
<Prefix>
|
||||
</Prefix>
|
||||
</Resource>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
@@ -74,10 +94,14 @@
|
||||
<Prefix>Prototypes\Content\</Prefix>
|
||||
</Resource>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="app.config" />
|
||||
<None Include="packages.config" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Target Name="CopyResources">
|
||||
<Copy SourceFiles="@(Resource)" DestinationFiles="..\bin\Client\Resources\%(Prefix)%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
<Copy SourceFiles="@(Resource)" DestinationFiles="..\bin\Server\Resources\%(Prefix)%(RecursiveDir)%(Filename)%(Extension)" />
|
||||
</Target>
|
||||
<Target Name="AfterBuild" DependsOnTargets="CopyResources" />
|
||||
</Project>
|
||||
</Project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
using SS14.Shared.GameObjects;
|
||||
|
||||
namespace Content.Shared.GameObjects
|
||||
{
|
||||
public abstract class SharedDoorComponent : Component
|
||||
{
|
||||
public override string Name => "Door";
|
||||
public override uint? NetID => ContentNetIDs.DOOR;
|
||||
public override Type StateType => typeof(DoorComponentState);
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class DoorComponentState : ComponentState
|
||||
{
|
||||
public readonly bool Opened;
|
||||
|
||||
public DoorComponentState(bool opened) : base(ContentNetIDs.DOOR)
|
||||
{
|
||||
Opened = opened;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using SS14.Shared.GameObjects;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Content.Shared.GameObjects
|
||||
{
|
||||
public abstract class SharedHandsComponent : Component
|
||||
{
|
||||
public sealed override string Name => "Hands";
|
||||
public sealed override uint? NetID => ContentNetIDs.HANDS;
|
||||
public sealed override Type StateType => typeof(HandsComponentState);
|
||||
}
|
||||
|
||||
// The IDs of the items get synced over the network.
|
||||
[Serializable]
|
||||
public class HandsComponentState : ComponentState
|
||||
{
|
||||
public readonly Dictionary<string, EntityUid> Hands;
|
||||
public readonly string ActiveIndex;
|
||||
|
||||
public HandsComponentState(Dictionary<string, EntityUid> hands, string activeIndex) : base(ContentNetIDs.HANDS)
|
||||
{
|
||||
Hands = hands;
|
||||
ActiveIndex = activeIndex;
|
||||
}
|
||||
}
|
||||
}
|
||||
12
Content.Shared/GameObjects/ContentNetIDs.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
namespace Content.Shared.GameObjects
|
||||
{
|
||||
// Starting from 1000 to avoid crossover with engine.
|
||||
public static class ContentNetIDs
|
||||
{
|
||||
public const uint DAMAGEABLE = 1000;
|
||||
public const uint DESTRUCTIBLE = 1001;
|
||||
public const uint TEMPERATURE = 1002;
|
||||
public const uint HANDS = 1003;
|
||||
public const uint DOOR = 1004;
|
||||
}
|
||||
}
|
||||
10
Content.Shared/GameObjects/PhysicalConstants.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared.GameObjects
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains physical constants used in calculations.
|
||||
/// </summary>
|
||||
class PhysicalConstants
|
||||
{
|
||||
public const float ZERO_CELCIUS = 273.15f;
|
||||
}
|
||||
}
|
||||
10
Content.Shared/Maths/PhysicalConstants.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
namespace Content.Shared.Maths
|
||||
{
|
||||
/// <summary>
|
||||
/// Contains physical constants used in calculations.
|
||||
/// </summary>
|
||||
public static class PhysicalConstants
|
||||
{
|
||||
public const float ZERO_CELCIUS = 273.15f;
|
||||
}
|
||||
}
|
||||
11
Content.Shared/app.config
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<runtime>
|
||||
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
|
||||
<dependentAssembly>
|
||||
<assemblyIdentity name="ICSharpCode.SharpZipLib" publicKeyToken="1b03e6acf1164f73" culture="neutral" />
|
||||
<bindingRedirect oldVersion="0.0.0.0-0.86.0.518" newVersion="0.86.0.518" />
|
||||
</dependentAssembly>
|
||||
</assemblyBinding>
|
||||
</runtime>
|
||||
</configuration>
|
||||
6
Content.Shared/packages.config
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<packages>
|
||||
<package id="OpenTK" version="3.0.0-pre" targetFramework="net451" />
|
||||
<package id="System.ValueTuple" version="4.3.1" targetFramework="net451" />
|
||||
<package id="YamlDotNet" version="4.3.0" targetFramework="net451" />
|
||||
</packages>
|
||||
19
Jenkinsfile
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
pipeline {
|
||||
agent any
|
||||
|
||||
stages {
|
||||
stage('Setup') {
|
||||
steps {
|
||||
sh './RUN_THIS.py'
|
||||
sh 'TMP=~/.cache/NuGet/ nuget restore'
|
||||
}
|
||||
}
|
||||
stage('Build') {
|
||||
steps {
|
||||
sh './package_release_build.py'
|
||||
archiveArtifacts artifacts: 'release/*.zip'
|
||||
archiveArtifacts artifacts: 'engine/Resources/ResourcePack.zip'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python3
|
||||
#!/usr/bin/env python3
|
||||
|
||||
# Import future so people on py2 still get the clear error that they need to upgrade.
|
||||
from __future__ import print_function
|
||||
|
||||
23
Resources/Prototypes/1_Temperature.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
- type: entity
|
||||
id: thing_that_heats_up_on_its_own_and_dies
|
||||
name: Thing that heats up on its own and dies
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: Sprite
|
||||
sprites:
|
||||
- shoes
|
||||
|
||||
- type: Icon
|
||||
icon: shoes
|
||||
|
||||
- type: Damageable
|
||||
resistanceset: Standard
|
||||
|
||||
- type: Destructible
|
||||
thresholdtype: Total
|
||||
thresholdvalue: 100
|
||||
|
||||
- type: Temperature
|
||||
firedamagethreshold: 200
|
||||
firedamagecoefficient: 20
|
||||
23
Resources/Prototypes/Entities/Clothing.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: ShoesItem
|
||||
name: Shoes
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
sprite: player_shoes
|
||||
notWornSprite: shoes
|
||||
|
||||
- type: Icon
|
||||
icon: shoes
|
||||
|
||||
- type: entity
|
||||
parent: BaseItem
|
||||
id: JanitorUniformItem
|
||||
name: Janitor Jumpsuit
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
sprite: player_jumpsuit_gray
|
||||
notWornSprite: janitorsuit
|
||||
|
||||
- type: Icon
|
||||
icon: janitorsuit
|
||||
24
Resources/Prototypes/Entities/Door.yml
Normal file
@@ -0,0 +1,24 @@
|
||||
- type: entity
|
||||
id: DoorContent
|
||||
name: Actual door
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- door_ew
|
||||
- door_ewo
|
||||
|
||||
- type: Icon
|
||||
icon: door_ew
|
||||
|
||||
- type: BoundingBox
|
||||
sizeX: 1.9
|
||||
offsetY: 1.5
|
||||
- type: Collidable
|
||||
- type: Door
|
||||
|
||||
placement:
|
||||
snap:
|
||||
- Wall
|
||||
23
Resources/Prototypes/Entities/HitscanWeapons.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
- type: entity
|
||||
name: "LASER"
|
||||
parent: BaseItem
|
||||
id: LaserItem
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: gun
|
||||
sprite: gun
|
||||
- type: Icon
|
||||
icon: gun
|
||||
- type: HitscanWeapon
|
||||
|
||||
- type: entity
|
||||
name: GUN
|
||||
parent: BaseItem
|
||||
id: GUNITEM
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: projectileweapon
|
||||
sprite: projectileweapon
|
||||
- type: Icon
|
||||
icon: gun
|
||||
- type: ProjectileWeapon
|
||||
67
Resources/Prototypes/Entities/Items.yml
Normal file
@@ -0,0 +1,67 @@
|
||||
- type: entity
|
||||
name: "Item"
|
||||
id: BaseItem
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Item
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Physics
|
||||
mass: 5
|
||||
|
||||
- type: entity
|
||||
name: "Emergency Toolbox With Handle"
|
||||
parent: BaseItem
|
||||
id: RedToolboxItem
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: toolbox_r
|
||||
sprite: player_toolbox
|
||||
- type: Icon
|
||||
icon: toolbox_r
|
||||
|
||||
- type: entity
|
||||
name: "Mechanical Toolbox With Handle"
|
||||
parent: BaseItem
|
||||
id: BlueToolboxItem
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: toolbox_b
|
||||
sprite: player_toolbox
|
||||
- type: Icon
|
||||
icon: toolbox_b
|
||||
|
||||
- type: entity
|
||||
name: "Electrical Toolbox With Handle"
|
||||
parent: BaseItem
|
||||
id: YellowToolboxItem
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: toolbox_y
|
||||
sprite: player_toolbox
|
||||
- type: Icon
|
||||
icon: toolbox_y
|
||||
|
||||
- type: entity
|
||||
name: "Extra-Grip™ Mop"
|
||||
parent: BaseItem
|
||||
id: MopItem
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: mop
|
||||
sprite: player_mop
|
||||
- type: Icon
|
||||
sprite: mop
|
||||
|
||||
#handheld lights
|
||||
- type: entity
|
||||
name: "Lantern"
|
||||
parent: BaseItem
|
||||
id: FlashlightLantern
|
||||
components:
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: Flashlight
|
||||
sprite: player_Flashlight
|
||||
- type: Icon
|
||||
icon: Flashlight
|
||||
- type: PointLight
|
||||
10
Resources/Prototypes/Entities/Mobs.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
- type: entity
|
||||
name: Urist McHands
|
||||
id: HumanMob_Content
|
||||
parent: __engine_human
|
||||
components:
|
||||
- type: Hands
|
||||
hands:
|
||||
- left
|
||||
- right
|
||||
- type: Inventory
|
||||
126
Resources/Prototypes/Entities/Power.yml
Normal file
@@ -0,0 +1,126 @@
|
||||
- type: entity
|
||||
id: Wire
|
||||
name: Wire
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
color: Red
|
||||
sprites:
|
||||
- eightdirwire
|
||||
- type: Icon
|
||||
icon: eightdirwire
|
||||
- type: PowerTransfer
|
||||
|
||||
snap:
|
||||
- Wire
|
||||
|
||||
- type: entity
|
||||
parent: Wire
|
||||
id: BlueWire
|
||||
name: BlueWire
|
||||
components:
|
||||
- type: Sprite
|
||||
color: Blue
|
||||
|
||||
- type: entity
|
||||
id: Generator
|
||||
name: Generator
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- generator
|
||||
- type: Icon
|
||||
icon: generator
|
||||
- type: PowerGenerator
|
||||
|
||||
- type: entity
|
||||
id: WPPnobattery
|
||||
name: WPPnobattery
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- provider
|
||||
- type: Icon
|
||||
icon: provider
|
||||
- type: PowerProvider
|
||||
Range: 5
|
||||
Priority: Provider
|
||||
|
||||
- type: entity
|
||||
parent: WPPnobattery
|
||||
id: WPP
|
||||
name: WPP
|
||||
components:
|
||||
- type: PowerStorage
|
||||
Capacity: 1000
|
||||
Charge: 1000
|
||||
ChargeRate: 200
|
||||
ChargePowernet: false
|
||||
|
||||
|
||||
- type: entity
|
||||
id: SMES
|
||||
name: SMES
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- storage
|
||||
- type: Icon
|
||||
icon: storage
|
||||
- type: PowerStorage
|
||||
Capacity: 3000
|
||||
Charge: 1000
|
||||
ChargeRate: 200
|
||||
DistributionRate: 400
|
||||
ChargePowernet: true
|
||||
|
||||
- type: entity
|
||||
id: WiredMachine
|
||||
name: WiredMachine
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- wiredmachine
|
||||
- type: Icon
|
||||
icon: wiredmachine
|
||||
- type: PowerDevice
|
||||
Drawtype: Node
|
||||
Load: 100
|
||||
Priority: High
|
||||
|
||||
- type: entity
|
||||
id: WirelessMachine
|
||||
name: WirelessMachine
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- wirelessmachine
|
||||
- type: Icon
|
||||
icon: wirelessmachine
|
||||
- type: PowerDevice
|
||||
Drawtype: Both
|
||||
Load: 200
|
||||
Priority: Low
|
||||
17
Resources/Prototypes/Entities/Projectiles.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
- type: entity
|
||||
id: ProjectileBullet
|
||||
name: ProjectileBullet
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- projectilebullet
|
||||
- type: Icon
|
||||
icon: projectilebullet
|
||||
- type: BoundingBox
|
||||
- type: Physics
|
||||
edgeslide: false
|
||||
- type: Projectile
|
||||
- type: Collidable
|
||||
hard: false
|
||||
76
Resources/Prototypes/Entities/Tools.yml
Normal file
@@ -0,0 +1,76 @@
|
||||
- type: entity
|
||||
name: Wirecutter
|
||||
parent: BaseItem
|
||||
id: Wirecutter
|
||||
components:
|
||||
- type: Wirecutter
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: wirecutter
|
||||
sprite: wirecutter
|
||||
- type: Icon
|
||||
icon: wirecutter
|
||||
- type: MeleeWeapon
|
||||
|
||||
- type: entity
|
||||
name: Screwdriver
|
||||
parent: BaseItem
|
||||
id: Screwdriver
|
||||
components:
|
||||
- type: Screwdriver
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: screwdriver
|
||||
sprite: screwdriver
|
||||
- type: Icon
|
||||
icon: screwdriver
|
||||
- type: MeleeWeapon
|
||||
|
||||
- type: entity
|
||||
name: Welder
|
||||
parent: BaseItem
|
||||
id: Welder
|
||||
components:
|
||||
- type: Welder
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: welder
|
||||
sprite: welder
|
||||
- type: Icon
|
||||
icon: welder
|
||||
- type: MeleeWeapon
|
||||
|
||||
- type: entity
|
||||
name: Wrench
|
||||
parent: BaseItem
|
||||
id: Wrench
|
||||
components:
|
||||
- type: Wrench
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: wrench
|
||||
sprite: wrench
|
||||
- type: Icon
|
||||
icon: wrench
|
||||
- type: MeleeWeapon
|
||||
|
||||
- type: entity
|
||||
name: Crowbar
|
||||
parent: BaseItem
|
||||
id: Crowbar
|
||||
components:
|
||||
- type: Crowbar
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: crowbar
|
||||
sprite: crowbar
|
||||
- type: Icon
|
||||
icon: crowbar
|
||||
- type: MeleeWeapon
|
||||
|
||||
- type: entity
|
||||
name: Multitool
|
||||
parent: BaseItem
|
||||
id: Multitool
|
||||
components:
|
||||
- type: Multitool
|
||||
- type: WearableAnimatedSprite
|
||||
notWornSprite: multitool
|
||||
sprite: multitool
|
||||
- type: Icon
|
||||
icon: multitool
|
||||
46
Resources/Prototypes/Entities/Turret.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
- type: entity
|
||||
id: TurretBase
|
||||
name: Turret Base
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: FloorPlaceable
|
||||
sprites:
|
||||
- TurrBase
|
||||
|
||||
- type: entity
|
||||
id: TurretTopGun
|
||||
name: Turret (Gun)
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprites:
|
||||
- TurrTop
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
|
||||
- type: entity
|
||||
id: TurretTopLight
|
||||
name: Turret (Light)
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: BoundingBox
|
||||
- type: Sprite
|
||||
drawdepth: WallMountedItems
|
||||
sprites:
|
||||
- TurrLamp
|
||||
- type: AiController
|
||||
logic: AimShootLife
|
||||
vision: 6.0
|
||||
- type: PointLight
|
||||
radius: 512
|
||||
mask: flashlight_mask
|
||||
autoRot: true
|
||||
|
||||
13
Resources/Prototypes/Entities/prototype_test.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
- type: entity
|
||||
id: medkit_r
|
||||
name: Medkit
|
||||
components:
|
||||
- type: Transform
|
||||
- type: Clickable
|
||||
- type: Sprite
|
||||
sprites:
|
||||
- medkit_r
|
||||
|
||||
- type: Icon
|
||||
icon: medkit_r
|
||||
|
||||
304
Resources/SavedEntities.xml
Normal file
@@ -0,0 +1,304 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<SavedEntities>
|
||||
<SavedEntity X="-32" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-31" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-30" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-29" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-28" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-27" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-26" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-25" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-24" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-23" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-22" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-21" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-20" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-19" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-18" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-17" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-16" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-15" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-14" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-13" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-12" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-11" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-10" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-9" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-8" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-7" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-6" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-4" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-3" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-2" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-1" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="0" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="1" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="2" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="3" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="4" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="6" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="7" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="8" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="9" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="10" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="11" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="12" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="13" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="14" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="15" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="16" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="17" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="18" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="19" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="20" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="21" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="22" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="23" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="24" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="25" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="26" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="27" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="28" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="29" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="30" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="31" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-31" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-31" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-30" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-30" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-29" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-29" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-28" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-28" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-27" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-27" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-26" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-26" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-25" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-25" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-24" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-24" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-23" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-23" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-22" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-22" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-21" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-21" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-20" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-20" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-19" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-19" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-18" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-18" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-17" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-17" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-16" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-16" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-15" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-15" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-14" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-14" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-13" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-13" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-12" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-12" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-11" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-11" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-10" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-10" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-9" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-9" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-8" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-8" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-2" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-1" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="0" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="1" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="2" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-6" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-6" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-5" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-5" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="-4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="-4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="-3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="-3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="-2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="-2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="-1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="-1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="-1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="-1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="0" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="0" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="0" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="0" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="1" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="2" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="3" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="4" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="5" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="5" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="6" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="6" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-2" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-1" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="0" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="1" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="2" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="7" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="8" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="8" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="9" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="9" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="10" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="10" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="11" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="11" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="12" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="12" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="13" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="13" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="14" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="14" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="15" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="15" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="16" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="16" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="17" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="17" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="18" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="18" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="19" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="19" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="20" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="20" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="21" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="21" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="22" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="22" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="23" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="23" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="24" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="24" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="25" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="25" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="26" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="26" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="27" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="27" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="28" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="28" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="29" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="29" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="30" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="30" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="31" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="31" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-32" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-31" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-30" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-29" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-28" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-27" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-26" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-25" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-24" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-23" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-22" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-21" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-20" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-19" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-18" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-17" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-16" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-15" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-14" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-13" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-12" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-11" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-10" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-9" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-8" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-7" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-6" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-5" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-4" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-3" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-2" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="-1" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="0" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="1" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="2" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="3" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="4" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="5" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="6" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="7" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="8" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="9" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="10" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="11" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="12" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="13" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="14" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="15" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="16" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="17" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="18" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="19" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="20" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="21" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="22" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="23" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="24" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="25" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="26" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="27" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="28" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="29" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="30" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="31" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="32" Y="32" template="__engine_wall" direction="East" />
|
||||
<SavedEntity X="10.3125" Y="9.375" template="BlueToolboxItem" />
|
||||
<SavedEntity X="0" Y="0" template="__engine_wall_light" name="FuckingLight" />
|
||||
<SavedEntity X="13.49975" Y="9.267462" template="__engine_janitor_suit" name="Janitor_Suit" />
|
||||
<SavedEntity X="13.49975" Y="9.267462" template="__engine_shoes" />
|
||||
<SavedEntity X="78.9826" Y="73.99996" template="__engine_door" />
|
||||
<SavedEntity X="79.01385" Y="67.99996" template="__engine_door" />
|
||||
<SavedEntity X="4" Y="4" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="8" Y="8" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="12" Y="12" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="16" Y="16" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="20" Y="20" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="24" Y="24" template="__engine_wall_light" name="WallLight" />
|
||||
<SavedEntity X="18.84556" Y="4.915811" template="__engine_worktop" />
|
||||
<SavedEntity X="13.49975" Y="9.267462" template="__engine_mop" />
|
||||
<SavedEntity X="8.914371" Y="2.878233" template="__engine_extinguisher" name="Extinguisher" />
|
||||
<SavedEntity X="17.00254" Y="3.034483" template="__engine_fire_alarm" name="FireAlarm" />
|
||||
<SavedEntity X="21.36345" Y="7.079962" template="__engine_med_cabinet" name="MedCabinet" />
|
||||
</SavedEntities>
|
||||
BIN
Resources/textures/Buildings/TurrBase.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
Resources/textures/Buildings/TurrLamp.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
Resources/textures/Buildings/TurrTop.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
Resources/textures/Objects/crowbar.png
Normal file
|
After Width: | Height: | Size: 200 B |
BIN
Resources/textures/Objects/eightdirwire.png
Normal file
|
After Width: | Height: | Size: 389 B |
BIN
Resources/textures/Objects/generator.png
Normal file
|
After Width: | Height: | Size: 452 B |
BIN
Resources/textures/Objects/gun.png
Normal file
|
After Width: | Height: | Size: 282 B |
BIN
Resources/textures/Objects/laser.png
Normal file
|
After Width: | Height: | Size: 153 B |
BIN
Resources/textures/Objects/multitool.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
Resources/textures/Objects/projectilebullet.png
Normal file
|
After Width: | Height: | Size: 263 B |
BIN
Resources/textures/Objects/projectileweapon.png
Normal file
|
After Width: | Height: | Size: 308 B |
BIN
Resources/textures/Objects/provider.png
Normal file
|
After Width: | Height: | Size: 195 B |
BIN
Resources/textures/Objects/screwdriver.png
Normal file
|
After Width: | Height: | Size: 177 B |
BIN
Resources/textures/Objects/storage.png
Normal file
|
After Width: | Height: | Size: 869 B |
BIN
Resources/textures/Objects/welder.png
Normal file
|
After Width: | Height: | Size: 330 B |
BIN
Resources/textures/Objects/wirecutter.png
Normal file
|
After Width: | Height: | Size: 216 B |
BIN
Resources/textures/Objects/wiredmachine.png
Normal file
|
After Width: | Height: | Size: 742 B |
BIN
Resources/textures/Objects/wirelessmachine.png
Normal file
|
After Width: | Height: | Size: 324 B |
BIN
Resources/textures/Objects/wrench.png
Normal file
|
After Width: | Height: | Size: 230 B |
@@ -3,4 +3,9 @@
|
||||
<Target Name="CopyContentAssemblies">
|
||||
<Copy SourceFiles="@(ContentAssemblies)" DestinationFolder="$(ContentAssemblyTarget)" />
|
||||
</Target>
|
||||
<ItemDefinitionGroup>
|
||||
<ContentAssemblies>
|
||||
<Visible>False</Visible>
|
||||
</ContentAssemblies>
|
||||
</ItemDefinitionGroup>
|
||||
</Project>
|
||||
|
||||
6
Setup.bat
Normal file
@@ -0,0 +1,6 @@
|
||||
cd %CD%
|
||||
python RUN_THIS.py
|
||||
pause
|
||||
cd %CD%\engine\Resources
|
||||
python buildResourcePack.py
|
||||
pause
|
||||