99 Commits

Author SHA1 Message Date
clusterfack
128728bfcb Adds weapons (#48)
* Adds weapons

- Adds melee weapons
- Adds projectile weapons
- Adds hitscan weapons (like lasers)

* Adds a separate sprite for projectile weapons
2018-04-06 00:32:51 +02:00
DamianX
63b5d83728 Fixed crash when pressing BACK in lobby (#46)
* Fixed crash when pressing BACK in lobby

* that's true
2018-03-24 16:54:13 +01:00
DamianX
bddd31b1b0 Copied run{client,server}.bat to the content repo (#45) 2018-03-22 12:34:32 -05:00
Acruid
071ed3f1ed Turret Entities (#38)
* Spotlight Turret.

* Used new turret sprite, credit: @Lobstrex (Lob)#5692

* Moved AimShootLifeProcessor.cs to content.

* Update Submodule Try 2.
2018-03-15 20:41:31 +01:00
clusterfack
98bd1552b9 Fixes power components (#39)
Fixes the power components not having node set if they get a node added as a component in OnAdd()
2018-03-09 17:59:19 +01:00
clusterfack
7554d51b0a Full interaction system (#43)
* Full interaction system

Has support for ranged interactions, and afterattacks, no longer relies on the clickable comopnent and you can click on no component and it will still function

* yes
2018-03-09 17:59:03 +01:00
Acruid
3f89f3f0f7 InputSystem Messages (#37)
* Misc Cleanup.

* Removed BoundKeyChangeEventArgs.

* Nothing uses EntityEventArgs now.

* Rebase fixes.

* Updated Engine.
Fixed .gitmodules file.
2018-03-04 03:07:09 +01:00
Acruid
b005d661f8 Component Messaging Rework (#36)
* Remove DiscoBall.

* Changes `IEntity.AddComponent(IComponent)` to `IEntity.AddComponent<T>`.

* Pulled ComponentManager registration out of Component into Entity.

* Killed component message params.

* Updated engine submodule.
2018-02-24 11:48:23 -08:00
clusterfack
05d4b2793b Adds wirecutter, powernet wire interaction (#34)
So it begins, the first of many interactions. Wirecutters cut powernet wires.
2018-02-13 00:07:50 -06:00
clusterfack
cf29e4b7bd Update Submodule (#33) 2018-02-06 22:46:48 -06:00
Acruid
3dfce70fdc Map Blueprint Save/Loading (#32)
* Change entry point to new system.

* Map saving/loading works.

* Cleans up old code.

* Saving and Loading of blueprints works.

* Saving and Loading of blueprints works.

* Updated the nuget 'YamlDotNet' package to 4.3.0.

* Submodules are trash
2018-02-06 21:34:36 -06:00
clusterfack
059832d324 Adds Basic Tools (#27)
* Basic Tools

* Adds tool prototypes
2018-02-06 19:03:36 -06:00
clusterfack
1452502fbf Interaction Entity System (#26)
* Interaction Entity System

* ye

* Update submodule

* Requires engine update to function, but doesn't use shitcode

* Fix conflicts

* Fix conflicts but for real

* Update submodule
2018-02-05 12:57:26 -07:00
clusterfack
1f22f8ab6a Power System (Complete) (#25)
* Power Commit 1

* Commit 2

* Powernet Part2, All components essentially complete

* Commit 4

* Commit 5

* Commit 6

Creates prototypes

* Finishes powernet code alpha

Adds prototypes and logic for powernet updating and regeneration

* Adds onremove functionality to all components

Without these bits of logic nothing makes any sense!

* And this

* Fixes a lot of bugs

* Fix powernet thinking devices are duplicates

* Fix bug and add comments

* woop woop thats the sound of the police
2018-02-03 21:35:42 -07:00
clusterfack
2eb30c9ba9 Update submodule (#28)
* Update submodule

* Fix disco ball
2018-02-02 19:33:50 -06:00
Acruid
988b2b2bc5 EntityUid Type (#23)
* Wraps Entity Uids in their own type.

* Update submodule I think.
2018-01-20 23:46:31 +01:00
clusterfack
5ee56a4cd3 Updates submodule (#22)
* Updates submodule

Fix connect not entering server (with correct toml config) and compile error, updates submodule
Server still has an issue with toml file pointing towards sandbox instead of content and roundstart object initializations

* Updates more stuff

Needs my most recent PR on ss14 to function

* Submodule updated for real this time
2018-01-18 20:00:35 +01:00
BobdaBiscuit
37091d1254 Adds a basic lantern item prototype (#15) 2017-11-16 17:21:33 -07:00
Pieter-Jan Briers
68f4fd995e Update submodule, fix HandsGui.
HandsGui now works with the new system. The port was sloppy though so
somebody might want to come and refactor this so that it doesn't force
its own position and size.
2017-11-13 19:48:21 +01:00
Pieter-Jan Briers
ba152fc61d Make zip files compressed using DEFLATE 2017-11-02 21:34:47 +01:00
Pieter-Jan Briers
074e6d8b62 Fix code to work with engine update 2017-10-29 21:43:42 +01:00
Pieter-Jan Briers
283ed302db Update submodule 2017-10-29 20:24:51 +01:00
Pieter-Jan Briers
4b345c0ea6 Add --arch=64 to MacOS start script. 2017-10-26 17:58:48 +02:00
Pieter-Jan Briers
fd4584519e Fix naming on MacOS artifacts.
They said x86, which is wrong. They're called x64 now.
2017-10-25 20:11:40 +02:00
Pieter-Jan Briers
de19cf652a Enable MacOS builds! 2017-10-25 19:05:55 +02:00
Pieter-Jan Briers
7c840d71e2 Resource pack becomes Jenkins build artifact. 2017-10-25 18:12:53 +02:00
PJB3005
0d0e6b2feb Update submodule. Kinda test Jenkins auto-build. 2017-10-24 17:31:35 +02:00
Pieter-Jan Briers
984617eed8 Update submodule 2017-10-24 00:40:40 +02:00
Pieter-Jan Briers
2d16205091 Jenkins setup. (#13)
* Adding test Jenkinsfile

* Will it be this simple?

* Maybe if I don't typo?

* Nuget restore needed.

* Work around NuGet bug hopefully.

The first person to run NuGet gets to own the /tmp/NuGetScratch folder, so jenkins breaks because I already ran NuGet personally.

* I'm a genius
2017-10-24 00:11:08 +02:00
Serkket
c41b867c7f A QOL Thing (#11) 2017-10-22 23:57:08 +02:00
Pieter-Jan Briers
7f196fc415 Doors! (#12)
* Doors WiP

* Kinda seem to work now?

* Finished

* Oh yeah maybe enable that.

* It works except it doesn't

* Undo formatting changes

* BuildChecker too
2017-10-22 23:48:01 +02:00
Pieter-Jan Briers
ec3e7968a6 Update submodule 2017-10-22 23:47:41 +02:00
Pieter-Jan Briers
82fbec02f0 Update submodule 2017-10-22 20:49:55 +02:00
Pieter-Jan Briers
9bd6200625 Update submodule 2017-10-22 19:37:24 +02:00
Pieter-Jan Briers
dac4f9cb3f Update submodule 2017-10-22 18:01:08 +02:00
Pieter-Jan Briers
8a11d2c649 Update submodule 2017-10-21 23:00:50 +02:00
Pieter-Jan Briers
2c99c8cb86 Fix release builds 2017-10-21 22:33:39 +02:00
Pieter-Jan Briers
b6eb825c5c Fix clothing.yml prototypes. 2017-10-21 19:40:26 +02:00
Pieter-Jan Briers
3601245d6f Make platform control better 2017-10-21 15:32:22 +02:00
Silver
de46359742 Update Rejected Coders
memes
2017-10-20 13:38:48 -06:00
PJB3005
de4583d14b Fix compile from removal of SFML. 2017-10-20 20:55:59 +02:00
PJB3005
60f975f0dc Update submodule 2017-10-20 20:49:05 +02:00
PJB3005
d38cc0ee82 Some item stuff I had laying around. 2017-10-20 20:46:36 +02:00
PJB3005
36141f3b1a Update submodule 2017-10-09 18:49:00 +02:00
Pieter-Jan Briers
6f89d0672d Urist's damage and health thing. (#10)
* Add prototype for temperature testing entity.

* Add Damageable, Destructible, Temperature. Add a prototype based on those.

* Works and cleaned up.

* Nerf
2017-10-07 15:15:29 +02:00
Pieter-Jan Briers
7597cd9172 Interactable component system. (#9)
* InteractableComponent v1. Broken edition

* It works!
2017-10-06 21:05:21 +02:00
Silver
8c1fa84c6e Merge pull request #8 from space-wizards/17_09_24-inventories
Inventories!
2017-09-30 09:32:45 -06:00
PJB3005
7bc132bc0d Merge branch 'master' into 17_09_24-inventories 2017-09-30 16:57:23 +02:00
PJB3005
ec4c864354 update submodule 2017-09-30 16:57:06 +02:00
PJB3005
26e9c37be1 Functional GUI! 2017-09-30 16:56:19 +02:00
PJB3005
4c4687eb8e Merge branch 'master' into 17_09_24-inventories 2017-09-30 12:22:35 +02:00
PJB3005
b8e01208af Update submodule 2017-09-30 12:22:05 +02:00
PJB3005
c7de994183 Item pickup seems to work, crash due to netcode. 2017-09-30 12:17:45 +02:00
PJB3005
b0b220f085 Hand switching works! 2017-09-29 18:38:27 +02:00
PJB3005
5fd0e31920 Merge branch 'master' into 17_09_24-inventories 2017-09-29 18:30:32 +02:00
PJB3005
bf10d34a8d Update submodule 2017-09-29 18:30:20 +02:00
PJB3005
1b44e84ef3 Merge branch 'master' into 17_09_24-inventories 2017-09-26 23:28:20 +02:00
PJB3005
66ec5864d0 Update submodule 2017-09-26 23:24:02 +02:00
PJB3005
f40cffa746 Fix init ordering issue with hands. 2017-09-26 23:10:10 +02:00
PJB3005
4caa13f1c0 Merge branch 'master' into 17_09_24-inventories 2017-09-26 21:59:21 +02:00
PJB3005
6864a457fe Update submodule 2017-09-26 21:59:06 +02:00
PJB3005
d345316db8 Make NetID use 1000+ range 2017-09-26 21:58:45 +02:00
PJB3005
a2399f8842 Merge branch 'master' into 17_09_24-inventories 2017-09-26 21:29:00 +02:00
PJB3005
92947a507c Update submodule 2017-09-26 21:28:32 +02:00
PJB3005
4f2d059de4 Simple network replication for hands, untested. 2017-09-26 21:27:48 +02:00
PJB3005
a464acf354 Ignore item and inventory client side. 2017-09-26 19:59:31 +02:00
PJB3005
c85436118a Basic example prototype that manages not to crash. 2017-09-25 21:00:53 +02:00
PJB3005
fc1c29d14f Merge branch 'master' into 17_09_24-inventories 2017-09-25 20:55:24 +02:00
PJB3005
17ea432604 Update submodule 2017-09-25 20:54:16 +02:00
PJB3005
3e0bcddd4d Done I hope. Needs testing and type checker updates.
waiting on https://github.com/space-wizards/space-station-14/pull/422
2017-09-25 20:52:39 +02:00
PJB3005
73ae408e20 More inventory work. APIs implemented.
Still need to do prototype loading and test cases.
2017-09-24 23:19:47 +02:00
Pieter-Jan Briers
66483bdd72 Can* API functions by Yota's request. 2017-09-24 22:24:54 +02:00
PJB3005
f6e265bccb Inventories WiP 2017-09-24 21:09:26 +02:00
PJB3005
60ff292f14 Fix content assemblies not being loaded.
They were put in the wrong directory.
2017-09-24 16:41:15 +02:00
Pieter-Jan Briers
b2806dcb75 Update submodule 2017-09-23 22:12:28 +02:00
Pieter-Jan Briers
55b9e367b3 x64 build platform. 2017-09-23 00:35:08 +02:00
Pieter-Jan Briers
2d91344a5b Update submodule to 6e535df92627ba4f07685baa4ef6c6abb39e372f 2017-09-23 00:05:54 +02:00
clusterfack
eee05e1451 Adds basic wire object (#6)
* Adds basic wire object

* Tests out the entity parenting system
2017-09-14 23:46:34 -05:00
clusterfack
6d28ce7641 Update submodule (#7) 2017-09-14 23:45:49 -05:00
PJB3005
4e48f2d581 Update submodule again. 2017-08-23 11:09:01 +02:00
PJB3005
6d4e532ebe Update submodule. 2017-08-23 08:01:20 +02:00
PJB3005
73c02f74f2 Disco ball small edition
Example of inheritance in action.
2017-08-22 23:54:48 +02:00
PJB3005
7add727db0 Disco Ball prototype 2017-08-22 23:48:51 +02:00
PJB3005
44cdd5ed55 Update submodule 2017-08-22 23:19:36 +02:00
PJB3005
7fda197391 Remove SFML from shared and server. 2017-08-21 23:51:13 +02:00
PJB3005
8ca10e4f0b Update submodule. 2017-08-21 23:41:05 +02:00
PJB3005
8a43a7022d Remove x64 platform target. 2017-08-19 11:27:51 +02:00
PJB3005
e46e4a7993 Fix SFML references. 2017-08-19 11:24:49 +02:00
PJB3005
3097106fe4 Update submodule 2017-08-19 11:20:51 +02:00
Pieter-Jan Briers
50cb6b4e30 Update submodule 2017-08-17 13:11:48 +02:00
Pieter-Jan Briers
e569eb276e package_release_build mac support. 2017-08-14 15:05:39 +02:00
PJB3005
17cd8c1d61 Remove dead components from content prototype test. 2017-08-13 14:59:00 +02:00
PJB3005
090fb0f417 Give content YamlDotNet, OpenTK and SFML references. 2017-08-13 14:58:31 +02:00
PJB3005
e0941e2f45 Update submodule 2017-08-13 14:35:05 +02:00
PJB3005
b593220805 Update submodule to d8b3864 2017-08-11 10:23:34 +02:00
PJB3005
2b8d2d9c83 Prototype PoC 2017-08-09 13:54:13 +02:00
PJB3005
780fecff3b Clean up release folder too. 2017-08-09 13:43:14 +02:00
PJB3005
23381d936f Fix package build script deleting build output. 2017-08-09 13:18:44 +02:00
PJB3005
a55ab49b6d Add Linux builds to build packager. 2017-08-09 11:13:04 +02:00
105 changed files with 5676 additions and 71 deletions

2
.github/CODEOWNERS vendored
View File

@@ -1 +1 @@
* @PJB3005
* @PJB3005 @Silvertorch5

6
.gitignore vendored
View File

@@ -262,3 +262,9 @@ __pycache__/
# Visual Studio Code workspace settings.
.vscode/
# Release package files go here:
release/
# Apple please go.
.DS_Store

1
.gitmodules vendored
View File

@@ -1,3 +1,4 @@
[submodule "engine"]
path = engine
url = https://github.com/space-wizards/space-station-14.git
branch = master

View File

@@ -16,9 +16,16 @@ 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">

View 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>

View 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

View 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>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
</Project>

View File

@@ -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>();
}
}
}

View File

@@ -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();
}
}
}
}

View File

@@ -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));
}
}
}

View File

@@ -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);
}
}

View 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
View 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>

View 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>

View 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;
}
}
}

View File

@@ -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>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup />
</Project>

View File

@@ -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;
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}
}

View 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;
}
}
}
}

View File

@@ -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;
}
}
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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";
}
}

View File

@@ -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";
}
}

View File

@@ -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;
}
}
}
}

View 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;
}
}
}

View File

@@ -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;
}
}
}
}

View 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
}
}

View File

@@ -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);
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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));
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View 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
}
}

View File

@@ -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();
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}
}
}
}
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View 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);
}
}
}
}
}

View 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);
}
}
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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; }
}
}

View File

@@ -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);
}
}

View File

@@ -0,0 +1,9 @@
using SS14.Shared.Interfaces.GameObjects;
namespace Content.Server.Interfaces.GameObjects
{
public interface ITemperatureComponent : IComponent
{
float CurrentTemperature { get; }
}
}

View 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);
}
}

View 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
View 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>

View 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>

View File

@@ -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">
@@ -65,7 +84,8 @@
<ItemDefinitionGroup>
<Resource>
<Visible>False</Visible>
<Prefix></Prefix>
<Prefix>
</Prefix>
</Resource>
</ItemDefinitionGroup>
<ItemGroup>
@@ -74,6 +94,10 @@
<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)" />

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View 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;
}
}

View 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;
}
}

View 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
View 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>

View 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
View 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'
}
}
}
}

View File

@@ -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

View 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

View 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

View 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

View 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

View 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

View File

@@ -0,0 +1,10 @@
- type: entity
name: Urist McHands
id: HumanMob_Content
parent: __engine_human
components:
- type: Hands
hands:
- left
- right
- type: Inventory

View 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

View 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

View 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

View 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

View 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
View 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>

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 389 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 452 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 308 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 330 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

View File

@@ -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
View File

@@ -0,0 +1,6 @@
cd %CD%
python RUN_THIS.py
pause
cd %CD%\engine\Resources
python buildResourcePack.py
pause

Some files were not shown because too many files have changed in this diff Show More