Serialization v3 content PR (#3491)

* serv3 in shared pt 1

* beginning of deepclone api

* progress in implementing ideepclone & serv3 in content

* adds target

* its cant hurt you it cant hurt you

* more changes to content.server

* adds dataclasses

* almost there

* renamed & edited entry

* finishes refactoring content to use serv3

* gasmixture runtimes, next: reagentunit

* fucin hell that was an annoying one

* adds flags

* fixes some yaml errors

* removes comment

* fixes generic components for now

* removes todo
actually clones values my god paul
fixes bug involving resolving custom data classes from other proj
renames dataclass
fixes spritecomp
adds WithFormat.Constants support

* adds deepclone to ResistanceSet

* adds a bunch of deepclone implementations
adds a deepclone analyzer (TODO)
adds a deep clone fallback for classes & structs

* fixes a bunch of runtimes

* adds deepclone to entityuid

* adds generator to sln

* gets rid of warnings

* fixes

* argh

* componentdata refactors

* more deepclone impl

* heck me i reworked all of content deepclone

* renames custom dataclasstarget

* misc

* reworks prototypes

* deepclone nuke

* renamed customdataclass attribute

* fixes everything

* misc fixed

* the killcommit

* getting there

* changed yamlfieldattribute namespace

* adds back iselfserialize

* renames everything to data(field/definition)

* ouch

* Fix most errors on content

* Fix more errors in content

* Fix some components

* work on tests

* fixes some customdataclasses

* fuggin shit

* yes

* yeas

* Remove data classes

* Data field naming fixes

* arg

* Git resetti RobustToolbox

* Merge fixes

* General fixes

* Fix startup serialization errors

* Fix DamageContainerPrototype when supported classes or types are null

* Implement construction graph step type serializer

* Fix up construction serialization

* Fix up construction serialization part 2

* Fix null list in technology database component

* Fix body serialization

* Fix entity storage serialization

* Fix actions serialization

* Fix AI serialization

* Fix reaction serialization

* Fix body serialization

* Fix grid atmosphere serialization

* Rename IServ3Manager to ISerializationManager

* Convert every non generic serializer to the new format, general fixes

* Serialization and body system fix

* pushinheritance fix

* Update all prototypes to have a parent and have consistent id/parent properties

* Merge fixes

* smh my head

* cuddling slaps

* Content commit for engine PR

* stuff

* more fixes

* argh

* yes even you are fixed

* changelog fixes

* fixes seeds

* argh

* Test fixes

* Add writing for alert order prototype

* Fix alert order writing

* FIX

* its been alot ok

* Fix the rest of the visualizers

* Fix server alerts component tests

* Fix alert prototype tests not using the read value

* Fix alert prototype tests initializing serialization multiple times

* THIS IS AN AMERICAN CODEBASE GOD BLESS THE USA

* Add ImplicitDataDefinitionForInheritors to IMechanismBehavior
Fixes the behaviors not being found

* Fix NRE in strap component
Good night to the 1 buckle optimization

* Fix clothing component slot flags serialization tag

* Fix body component in all components test

* Merge fixes

* ffs

* Make construction graph prototype use serialization hooks

* human yaml linted

* a

* Do the thing for construction

* stuff

* a

* monke see yaml linter

* LINT HARDER

* Remove redundant todo

* yes

* Add skip hook argument to readers and copiers

* we gamin

* test/datafield fixes

* adds more verbose validation

* moves linter to action

* Improve construction graph step type serializer error message

* Fix ammo box component NRE

* gamin

* some updates to the linter

* yes

* removes that test

* misc fixes

* array fix
priority fix
misc fixes

* adds proper info the validation

* adds alwaysrelevant usa

* Make yaml linter take half as long to run (~50% less)

* Make yaml linter 5 times faster (~80% less execution time)

* based vera being based

* fixes mapsaving

* warning cleanup & moves surpressor

* removes old msbuild targets

* Revert "Make yaml linter 5 times faster (~80% less execution time)"

This reverts commit 3e6091359a26252c3e98828199553de668031c63.

* Add -nowarn to yaml linter run configuration

* Improve yaml linter message feedback

* Make dependencies an argument instead of a property on the serialization manager

* yamllinting slaps

* Clean up type serializers

* Move yaml linter code to its own method

* Fix yaml errors

* Change yaml linter action name and remove -nowarn

* yaml linter please shut

* Git resetti robust toolbox

Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
This commit is contained in:
Paul Ritter
2021-03-05 01:08:38 +01:00
committed by GitHub
parent 05d4d9692c
commit 5c50b1f6ed
545 changed files with 4547 additions and 6650 deletions

View File

@@ -14,7 +14,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
@@ -24,27 +24,30 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
public override string Name => "AmmoBox";
private BallisticCaliber _caliber;
public int Capacity => _capacity;
private int _capacity;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
[DataField("capacity")]
public int Capacity
{
get => _capacity;
set
{
_capacity = value;
_spawnedAmmo = new Stack<IEntity>(value);
}
}
private int _capacity = 30;
public int AmmoLeft => _spawnedAmmo.Count + _unspawnedCount;
private Stack<IEntity> _spawnedAmmo;
private Stack<IEntity> _spawnedAmmo = new();
private Container _ammoContainer;
private int _unspawnedCount;
[DataField("fillPrototype")]
private string _fillPrototype;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _capacity, "capacity", 30);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
_spawnedAmmo = new Stack<IEntity>(_capacity);
}
public override void Initialize()
{
base.Initialize();

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -19,13 +20,15 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
/// Generally used for bullets but can be used for other things like bananas
/// </summary>
[RegisterComponent]
public class AmmoComponent : Component, IExamine
public class AmmoComponent : Component, IExamine, ISerializationHooks
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
public override string Name => "Ammo";
public BallisticCaliber Caliber => _caliber;
private BallisticCaliber _caliber;
[field: DataField("caliber")]
public BallisticCaliber Caliber { get; } = BallisticCaliber.Unspecified;
public bool Spent
{
get
@@ -38,64 +41,60 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
return _spent;
}
}
private bool _spent;
/// <summary>
/// Used for anything without a case that fires itself
/// </summary>
[DataField("isProjectile")]
private bool _ammoIsProjectile;
/// <summary>
/// Used for something that is deleted when the projectile is retrieved
/// </summary>
public bool Caseless => _caseless;
private bool _caseless;
[field: DataField("caseless")]
public bool Caseless { get; }
// Rather than managing bullet / case state seemed easier to just have 2 toggles
// ammoIsProjectile being for a beanbag for example and caseless being for ClRifle rounds
/// <summary>
/// For shotguns where they might shoot multiple entities
/// </summary>
public int ProjectilesFired => _projectilesFired;
private int _projectilesFired;
[field: DataField("projectilesFired")]
public int ProjectilesFired { get; } = 1;
[DataField("projectile")]
private string _projectileId;
// How far apart each entity is if multiple are shot
public float EvenSpreadAngle => _evenSpreadAngle;
private float _evenSpreadAngle;
[field: DataField("ammoSpread")]
public float EvenSpreadAngle { get; } = default;
/// <summary>
/// How fast the shot entities travel
/// </summary>
public float Velocity => _velocity;
private float _velocity;
[field: DataField("ammoVelocity")]
public float Velocity { get; } = 20f;
private string _muzzleFlashSprite;
[DataField("muzzleFlash")]
private string _muzzleFlashSprite = "Objects/Weapons/Guns/Projectiles/bullet_muzzle.png";
public string SoundCollectionEject => _soundCollectionEject;
private string _soundCollectionEject;
[field: DataField("soundCollectionEject")]
public string SoundCollectionEject { get; } = "CasingEject";
public override void ExposeData(ObjectSerializer serializer)
void ISerializationHooks.AfterDeserialization()
{
base.ExposeData(serializer);
// For shotty of whatever as well
serializer.DataField(ref _projectileId, "projectile", null);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _projectilesFired, "projectilesFired", 1);
// Used for shotty to determine overall pellet spread
serializer.DataField(ref _evenSpreadAngle, "ammoSpread", 0);
serializer.DataField(ref _velocity, "ammoVelocity", 20.0f);
serializer.DataField(ref _ammoIsProjectile, "isProjectile", false);
serializer.DataField(ref _caseless, "caseless", false);
// Being both caseless and shooting yourself doesn't make sense
DebugTools.Assert(!(_ammoIsProjectile && _caseless));
serializer.DataField(ref _muzzleFlashSprite, "muzzleFlash", "Objects/Weapons/Guns/Projectiles/bullet_muzzle.png");
serializer.DataField(ref _soundCollectionEject, "soundCollectionEject", "CasingEject");
DebugTools.Assert(!(_ammoIsProjectile == true && Caseless == true));
if (_projectilesFired < 1)
if (ProjectilesFired < 1)
{
Logger.Error("Ammo can't have less than 1 projectile");
}
if (_evenSpreadAngle > 0 && _projectilesFired == 1)
if (EvenSpreadAngle > 0 && ProjectilesFired == 1)
{
Logger.Error("Can't have an even spread if only 1 projectile is fired");
throw new InvalidOperationException();

View File

@@ -0,0 +1,12 @@
using System;
using Robust.Shared.Log;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
public partial class AmmoComponentData : ISerializationHooks
{
}
}

View File

@@ -4,6 +4,8 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Serialization;
using System.Collections.Generic;
using System.Linq;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
@@ -12,13 +14,8 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
public override string Name => "ChemicalAmmo";
private float _fractionTransfered;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _fractionTransfered, "fractionTransfered", 1);
}
[DataField("fractionTransfered")]
private float _fractionTransfered = 1;
public override void HandleMessage(ComponentMessage message, IComponent component)
{

View File

@@ -12,7 +12,9 @@ using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
@@ -27,30 +29,25 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
public int ShotsLeft => _spawnedAmmo.Count + _unspawnedCount;
public int Capacity => _capacity;
private int _capacity;
[DataField("capacity")]
private int _capacity = 20;
public MagazineType MagazineType => _magazineType;
private MagazineType _magazineType;
[DataField("magazineType")]
private MagazineType _magazineType = MagazineType.Unspecified;
public BallisticCaliber Caliber => _caliber;
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
private AppearanceComponent _appearanceComponent;
// If there's anything already in the magazine
[DataField("fillPrototype")]
private string _fillPrototype;
// By default the magazine won't spawn the entity until needed so we need to keep track of how many left we can spawn
// Generally you probablt don't want to use this
private int _unspawnedCount;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _magazineType, "magazineType", MagazineType.Unspecified);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
serializer.DataField(ref _capacity, "capacity", 20);
}
void IMapInit.MapInit()
{
if (_fillPrototype != null)

View File

@@ -10,7 +10,9 @@ using Robust.Server.GameObjects;
using Robust.Shared.Containers;
using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
@@ -22,26 +24,19 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition
{
public override string Name => "SpeedLoader";
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
public int Capacity => _capacity;
private int _capacity;
[DataField("capacity")]
private int _capacity = 6;
private Container _ammoContainer;
private Stack<IEntity> _spawnedAmmo;
private Stack<IEntity> _spawnedAmmo = new();
private int _unspawnedCount;
public int AmmoLeft => _spawnedAmmo.Count + _unspawnedCount;
private string _fillPrototype;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _capacity, "capacity", 6);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
_spawnedAmmo = new Stack<IEntity>(_capacity);
}
[DataField("fillPrototype")]
private string _fillPrototype = default;
public override void Initialize()
{

View File

@@ -15,7 +15,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -42,16 +42,19 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
}
}
public override int Capacity => _capacity;
private int _capacity;
[DataField("capacity")]
private int _capacity = 6;
private ContainerSlot _chamberContainer;
private Stack<IEntity> _spawnedAmmo;
private Container _ammoContainer;
[ViewVariables]
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
[ViewVariables]
[DataField("fillPrototype")]
private string _fillPrototype;
[ViewVariables]
private int _unspawnedCount;
@@ -91,28 +94,19 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
}
}
private bool _boltOpen;
[DataField("autoCycle")]
private bool _autoCycle;
private AppearanceComponent _appearanceComponent;
// Sounds
private string _soundCycle;
private string _soundBoltOpen;
private string _soundBoltClosed;
private string _soundInsert;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _capacity, "capacity", 6);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
serializer.DataField(ref _autoCycle, "autoCycle", false);
serializer.DataField(ref _soundCycle, "soundCycle", "/Audio/Weapons/Guns/Cock/sf_rifle_cock.ogg");
serializer.DataField(ref _soundBoltOpen, "soundBoltOpen", "/Audio/Weapons/Guns/Bolt/rifle_bolt_open.ogg");
serializer.DataField(ref _soundBoltClosed, "soundBoltClosed", "/Audio/Weapons/Guns/Bolt/rifle_bolt_closed.ogg");
serializer.DataField(ref _soundInsert, "soundInsert", "/Audio/Weapons/Guns/MagIn/bullet_insert.ogg");
}
[DataField("soundCycle")]
private string _soundCycle = "/Audio/Weapons/Guns/Cock/sf_rifle_cock.ogg";
[DataField("soundBoltOpen")]
private string _soundBoltOpen = "/Audio/Weapons/Guns/Bolt/rifle_bolt_open.ogg";
[DataField("soundBoltClosed")]
private string _soundBoltClosed = "/Audio/Weapons/Guns/Bolt/rifle_bolt_closed.ogg";
[DataField("soundInsert")]
private string _soundInsert = "/Audio/Weapons/Guns/MagIn/bullet_insert.ogg";
void IMapInit.MapInit()
{

View File

@@ -3,7 +3,6 @@ using System.Threading.Tasks;
using Content.Server.GameObjects.Components.Weapon.Ranged.Ammunition;
using Content.Shared.GameObjects;
using Content.Shared.GameObjects.Components.Weapons.Ranged.Barrels;
using Content.Shared.GameObjects.EntitySystems;
using Content.Shared.Interfaces;
using Content.Shared.Interfaces.GameObjects.Components;
using Robust.Server.GameObjects;
@@ -14,6 +13,7 @@ using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -23,7 +23,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
/// Bolt-action rifles
/// </summary>
[RegisterComponent]
public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IMapInit, IExamine
public sealed class PumpBarrelComponent : ServerRangedBarrelComponent, IMapInit, ISerializationHooks
{
public override string Name => "PumpBarrel";
public override uint? NetID => ContentNetIDs.PUMP_BARREL;
@@ -37,43 +37,36 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
}
}
public override int Capacity => _capacity;
private int _capacity;
private const int DefaultCapacity = 6;
[field: DataField("capacity")]
public override int Capacity { get; } = DefaultCapacity;
// Even a point having a chamber? I guess it makes some of the below code cleaner
private ContainerSlot _chamberContainer;
private Stack<IEntity> _spawnedAmmo;
private Stack<IEntity> _spawnedAmmo = new (DefaultCapacity-1);
private Container _ammoContainer;
[ViewVariables]
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
[ViewVariables]
[DataField("fillPrototype")]
private string _fillPrototype;
[ViewVariables]
private int _unspawnedCount;
private bool _manualCycle;
[DataField("manualCycle")]
private bool _manualCycle = true;
private AppearanceComponent _appearanceComponent;
// Sounds
private string _soundCycle;
private string _soundInsert;
[DataField("soundCycle")]
private string _soundCycle = "/Audio/Weapons/Guns/Cock/sf_rifle_cock.ogg";
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _capacity, "capacity", 6);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
serializer.DataField(ref _manualCycle, "manualCycle", true);
serializer.DataField(ref _soundCycle, "soundCycle", "/Audio/Weapons/Guns/Cock/sf_rifle_cock.ogg");
serializer.DataField(ref _soundInsert, "soundInsert", "/Audio/Weapons/Guns/MagIn/bullet_insert.ogg");
_spawnedAmmo = new Stack<IEntity>(_capacity - 1);
}
[DataField("soundInsert")]
private string _soundInsert = "/Audio/Weapons/Guns/MagIn/bullet_insert.ogg";
void IMapInit.MapInit()
{
@@ -101,6 +94,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
SoundGunshot);
}
void ISerializationHooks.AfterDeserialization()
{
_spawnedAmmo = new Stack<IEntity>(Capacity - 1);
}
public override void Initialize()
{
base.Initialize();

View File

@@ -17,12 +17,13 @@ using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
{
[RegisterComponent]
public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent
public sealed class RevolverBarrelComponent : ServerRangedBarrelComponent, ISerializationHooks
{
[Dependency] private readonly IRobustRandom _random = default!;
@@ -30,42 +31,49 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
public override uint? NetID => ContentNetIDs.REVOLVER_BARREL;
[ViewVariables]
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
private Container _ammoContainer;
[ViewVariables]
private int _currentSlot = 0;
private int _currentSlot;
public override int Capacity => _ammoSlots.Length;
private IEntity[] _ammoSlots;
[DataField("capacity")]
private int _serializedCapacity = 6;
[DataField("ammoSlots", readOnly: true)]
private IEntity[] _ammoSlots = Array.Empty<IEntity>();
public override int ShotsLeft => _ammoContainer.ContainedEntities.Count;
private AppearanceComponent _appearanceComponent;
[ViewVariables]
[DataField("fillPrototype")]
private string _fillPrototype;
[ViewVariables]
private int _unspawnedCount;
// Sounds
private string _soundEject;
private string _soundInsert;
private string _soundSpin;
[DataField("soundEject")]
private string _soundEject = "/Audio/Weapons/Guns/MagOut/revolver_magout.ogg";
public override void ExposeData(ObjectSerializer serializer)
[DataField("soundInsert")]
private string _soundInsert = "/Audio/Weapons/Guns/MagIn/revolver_magin.ogg";
[DataField("soundSpin")]
private string _soundSpin = "/Audio/Weapons/Guns/Misc/revolver_spin.ogg";
void ISerializationHooks.BeforeSerialization()
{
base.ExposeData(serializer);
_serializedCapacity = _ammoSlots.Length;
}
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataReadWriteFunction(
"capacity",
6,
cap => _ammoSlots = new IEntity[cap],
() => _ammoSlots.Length);
serializer.DataField(ref _fillPrototype, "fillPrototype", null);
// Sounds
serializer.DataField(ref _soundEject, "soundEject", "/Audio/Weapons/Guns/MagOut/revolver_magout.ogg");
serializer.DataField(ref _soundInsert, "soundInsert", "/Audio/Weapons/Guns/MagIn/revolver_magin.ogg");
serializer.DataField(ref _soundSpin, "soundSpin", "/Audio/Weapons/Guns/Misc/revolver_spin.ogg");
void ISerializationHooks.AfterDeserialization()
{
_ammoSlots = new IEntity[_serializedCapacity];
}
public override ComponentState GetComponentState(ICommonSession player)
@@ -112,21 +120,21 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
idx++;
}
if (Owner.TryGetComponent(out AppearanceComponent appearanceComponent))
{
_appearanceComponent = appearanceComponent;
}
UpdateAppearance();
Dirty();
}
private void UpdateAppearance()
{
if (!Owner.TryGetComponent(out AppearanceComponent appearance))
{
return;
}
// Placeholder, at this stage it's just here for the RPG
_appearanceComponent?.SetData(MagazineBarrelVisuals.MagLoaded, ShotsLeft > 0);
_appearanceComponent?.SetData(AmmoVisuals.AmmoCount, ShotsLeft);
_appearanceComponent?.SetData(AmmoVisuals.AmmoMax, Capacity);
appearance.SetData(MagazineBarrelVisuals.MagLoaded, ShotsLeft > 0);
appearance.SetData(AmmoVisuals.AmmoCount, ShotsLeft);
appearance.SetData(AmmoVisuals.AmmoMax, Capacity);
}
public bool TryInsertBullet(IEntity user, IEntity entity)

View File

@@ -18,7 +18,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
@@ -30,17 +30,22 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
public override uint? NetID => ContentNetIDs.BATTERY_BARREL;
// The minimum change we need before we can fire
[ViewVariables] private float _lowerChargeLimit;
[ViewVariables] private int _baseFireCost;
[DataField("lowerChargeLimit")]
[ViewVariables] private float _lowerChargeLimit = 10;
[DataField("fireCost")]
[ViewVariables] private int _baseFireCost = 300;
// What gets fired
[DataField("ammoPrototype")]
[ViewVariables] private string _ammoPrototype;
[ViewVariables] public IEntity PowerCellEntity => _powerCellContainer.ContainedEntity;
public BatteryComponent PowerCell => _powerCellContainer.ContainedEntity?.GetComponent<BatteryComponent>();
private ContainerSlot _powerCellContainer;
private ContainerSlot _ammoContainer;
private string _powerCellPrototype;
[ViewVariables] private bool _powerCellRemovable;
[DataField("powerCellPrototype")]
private string _powerCellPrototype = default;
[DataField("powerCellRemovable")]
[ViewVariables] private bool _powerCellRemovable = default;
public override int ShotsLeft
{
@@ -75,21 +80,10 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
private AppearanceComponent _appearanceComponent;
// Sounds
private string _soundPowerCellInsert;
private string _soundPowerCellEject;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(ref _powerCellPrototype, "powerCellPrototype", null);
serializer.DataField(ref _powerCellRemovable, "powerCellRemovable", false);
serializer.DataField(ref _baseFireCost, "fireCost", 300);
serializer.DataField(ref _ammoPrototype, "ammoPrototype", null);
serializer.DataField(ref _lowerChargeLimit, "lowerChargeLimit", 10);
serializer.DataField(ref _soundPowerCellInsert, "soundPowerCellInsert", null);
serializer.DataField(ref _soundPowerCellEject, "soundPowerCellEject", null);
}
[DataField("soundPowerCellInsert")]
private string _soundPowerCellInsert = default;
[DataField("soundPowerCellEject")]
private string _soundPowerCellEject = default;
public override ComponentState GetComponentState(ICommonSession player)
{

View File

@@ -19,7 +19,7 @@ using Robust.Shared.GameObjects;
using Robust.Shared.Localization;
using Robust.Shared.Map;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Utility;
using Robust.Shared.ViewVariables;
@@ -37,9 +37,11 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
private ContainerSlot _magazineContainer;
[ViewVariables] public MagazineType MagazineTypes => _magazineTypes;
private MagazineType _magazineTypes;
[DataField("magazineTypes")]
private MagazineType _magazineTypes = default;
[ViewVariables] public BallisticCaliber Caliber => _caliber;
private BallisticCaliber _caliber;
[DataField("caliber")]
private BallisticCaliber _caliber = BallisticCaliber.Unspecified;
public override int ShotsLeft
{
@@ -77,6 +79,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
}
}
[DataField("magFillPrototype")]
private string _magFillPrototype;
public bool BoltOpen
@@ -115,40 +118,28 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
}
private bool _boltOpen = true;
[DataField("autoEjectMag")]
private bool _autoEjectMag;
// If the bolt needs to be open before we can insert / remove the mag (i.e. for LMGs)
public bool MagNeedsOpenBolt => _magNeedsOpenBolt;
private bool _magNeedsOpenBolt;
[DataField("magNeedsOpenBolt")]
private bool _magNeedsOpenBolt = default;
private AppearanceComponent _appearanceComponent;
// Sounds
private string _soundBoltOpen;
private string _soundBoltClosed;
private string _soundRack;
private string _soundMagInsert;
private string _soundMagEject;
private string _soundAutoEject;
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataReadWriteFunction(
"magazineTypes",
new List<MagazineType>(),
types => types.ForEach(mag => _magazineTypes |= mag), GetMagazineTypes);
serializer.DataField(ref _caliber, "caliber", BallisticCaliber.Unspecified);
serializer.DataField(ref _magFillPrototype, "magFillPrototype", null);
serializer.DataField(ref _autoEjectMag, "autoEjectMag", false);
serializer.DataField(ref _magNeedsOpenBolt, "magNeedsOpenBolt", false);
serializer.DataField(ref _soundBoltOpen, "soundBoltOpen", null);
serializer.DataField(ref _soundBoltClosed, "soundBoltClosed", null);
serializer.DataField(ref _soundRack, "soundRack", null);
serializer.DataField(ref _soundMagInsert, "soundMagInsert", null);
serializer.DataField(ref _soundMagEject, "soundMagEject", null);
serializer.DataField(ref _soundAutoEject, "soundAutoEject", "/Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg");
}
[DataField("soundBoltOpen")]
private string _soundBoltOpen = default;
[DataField("soundBoltClosed")]
private string _soundBoltClosed = default;
[DataField("soundRack")]
private string _soundRack = default;
[DataField("soundMagInsert")]
private string _soundMagInsert = default;
[DataField("soundMagEject")]
private string _soundMagEject = default;
[DataField("soundAutoEject")]
private string _soundAutoEject = "/Audio/Weapons/Guns/EmptyAlarm/smg_empty_alarm.ogg";
private List<MagazineType> GetMagazineTypes()
{
@@ -535,7 +526,6 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
[Flags]
public enum MagazineType
{
Unspecified = 0,
LPistol = 1 << 0, // Placeholder?
Pistol = 1 << 1,

View File

@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
@@ -24,6 +24,7 @@ using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
@@ -33,7 +34,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
/// All of the ranged weapon components inherit from this to share mechanics like shooting etc.
/// Only difference between them is how they retrieve a projectile to shoot (battery, magazine, etc.)
/// </summary>
public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IUse, IInteractUsing, IExamine
public abstract class ServerRangedBarrelComponent : SharedRangedBarrelComponent, IUse, IInteractUsing, IExamine, ISerializationHooks
{
// There's still some of py01 and PJB's work left over, especially in underlying shooting logic,
// it's just when I re-organised it changed me as the contributor
@@ -41,11 +42,17 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
[Dependency] private readonly IRobustRandom _robustRandom = default!;
public override FireRateSelector FireRateSelector => _fireRateSelector;
private FireRateSelector _fireRateSelector;
[DataField("currentSelector")]
private FireRateSelector _fireRateSelector = FireRateSelector.Safety;
public override FireRateSelector AllRateSelectors => _fireRateSelector;
[DataField("allSelectors")]
private FireRateSelector _allRateSelectors;
public override float FireRate => _fireRate;
private float _fireRate;
[field: DataField("fireRate")]
public override float FireRate { get; } = 2f;
// _lastFire is when we actually fired (so if we hold the button then recoil doesn't build up if we're not firing)
private TimeSpan _lastFire;
@@ -54,100 +61,76 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
public abstract IEntity TakeProjectile(EntityCoordinates spawnAt);
// Recoil / spray control
private Angle _minAngle;
private Angle _maxAngle;
[DataField("minAngle")]
private float _minAngleDegrees;
public Angle MinAngle { get; private set; }
[DataField("maxAngle")]
private float _maxAngleDegrees = 45;
public Angle MaxAngle { get; private set; }
private Angle _currentAngle = Angle.Zero;
[DataField("angleDecay")]
private float _angleDecayDegrees = 20;
/// <summary>
/// How slowly the angle's theta decays per second in radians
/// </summary>
private float _angleDecay;
public float AngleDecay { get; private set; }
[DataField("angleIncrease")]
private float? _angleIncreaseDegrees;
/// <summary>
/// How quickly the angle's theta builds for every shot fired in radians
/// </summary>
private float _angleIncrease;
// Multiplies the ammo spread to get the final spread of each pellet
private float _spreadRatio;
public float AngleIncrease { get; private set; }
public bool CanMuzzleFlash => _canMuzzleFlash;
private bool _canMuzzleFlash = true;
// Multiplies the ammo spread to get the final spread of each pellet
[DataField("ammoSpreadRatio")]
public float SpreadRatio { get; private set; }
[field: DataField("canMuzzleFlash")]
public bool CanMuzzleFlash { get; } = true;
// Sounds
public string SoundGunshot
[field: DataField("soundGunshot")]
public string SoundGunshot { get; set; }
[field: DataField("soundEmpty")]
public string SoundEmpty { get; } = "/Audio/Weapons/Guns/Empty/empty.ogg";
void ISerializationHooks.BeforeSerialization()
{
get => _soundGunshot;
set => _soundGunshot = value;
_minAngleDegrees = (float) (MinAngle.Degrees * 2);
_maxAngleDegrees = (float) (MaxAngle.Degrees * 2);
_angleIncreaseDegrees = MathF.Round(AngleIncrease / ((float) Math.PI / 180f), 2);
AngleDecay = MathF.Round(AngleDecay / ((float) Math.PI / 180f), 2);
}
private string _soundGunshot;
public string SoundEmpty => _soundEmpty;
private string _soundEmpty;
public override void ExposeData(ObjectSerializer serializer)
void ISerializationHooks.AfterDeserialization()
{
base.ExposeData(serializer);
serializer.DataField(ref _fireRateSelector, "currentSelector", FireRateSelector.Safety);
serializer.DataField(ref _fireRate, "fireRate", 2.0f);
// This hard-to-read area's dealing with recoil
// Use degrees in yaml as it's easier to read compared to "0.0125f"
serializer.DataReadWriteFunction(
"minAngle",
0,
angle => _minAngle = Angle.FromDegrees(angle / 2f),
() => _minAngle.Degrees * 2);
MinAngle = Angle.FromDegrees(_minAngleDegrees / 2f);
// Random doubles it as it's +/- so uhh we'll just half it here for readability
serializer.DataReadWriteFunction(
"maxAngle",
45,
angle => _maxAngle = Angle.FromDegrees(angle / 2f),
() => _maxAngle.Degrees * 2);
MaxAngle = Angle.FromDegrees(_maxAngleDegrees / 2f);
serializer.DataReadWriteFunction(
"angleIncrease",
40 / _fireRate,
angle => _angleIncrease = angle * (float) Math.PI / 180f,
() => MathF.Round(_angleIncrease / ((float) Math.PI / 180f), 2));
_angleIncreaseDegrees ??= 40 / FireRate;
AngleIncrease = _angleIncreaseDegrees.Value * (float) Math.PI / 180f;
serializer.DataReadWriteFunction(
"angleDecay",
20f,
angle => _angleDecay = angle * (float) Math.PI / 180f,
() => MathF.Round(_angleDecay / ((float) Math.PI / 180f), 2));
serializer.DataField(ref _spreadRatio, "ammoSpreadRatio", 1.0f);
serializer.DataReadWriteFunction(
"allSelectors",
new List<FireRateSelector>(),
selectors => selectors.ForEach(selector => _allRateSelectors |= selector),
() =>
{
var types = new List<FireRateSelector>();
foreach (FireRateSelector selector in Enum.GetValues(typeof(FireRateSelector)))
{
if ((_allRateSelectors & selector) != 0)
{
types.Add(selector);
}
}
return types;
});
AngleDecay = _angleDecayDegrees * (float) Math.PI / 180f;
// For simplicity we'll enforce it this way; ammo determines max spread
if (_spreadRatio > 1.0f)
if (SpreadRatio > 1.0f)
{
Logger.Error("SpreadRatio must be <= 1.0f for guns");
throw new InvalidOperationException();
}
serializer.DataField(ref _canMuzzleFlash, "canMuzzleFlash", true);
// Sounds
serializer.DataField(ref _soundGunshot, "soundGunshot", null);
serializer.DataField(ref _soundEmpty, "soundEmpty", "/Audio/Weapons/Guns/Empty/empty.ogg");
}
public override void OnAdd()
@@ -176,7 +159,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
{
var currentTime = _gameTiming.CurTime;
var timeSinceLastFire = (currentTime - _lastFire).TotalSeconds;
var newTheta = MathHelper.Clamp(_currentAngle.Theta + _angleIncrease - _angleDecay * timeSinceLastFire, _minAngle.Theta, _maxAngle.Theta);
var newTheta = MathHelper.Clamp(_currentAngle.Theta + AngleIncrease - AngleDecay * timeSinceLastFire, MinAngle.Theta, MaxAngle.Theta);
_currentAngle = new Angle(newTheta);
var random = (_robustRandom.NextDouble() - 0.5) * 2;
@@ -214,9 +197,9 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
{
if (ShotsLeft == 0)
{
if (_soundEmpty != null)
if (SoundEmpty != null)
{
SoundSystem.Play(Filter.Broadcast(), _soundEmpty, Owner.Transform.Coordinates);
SoundSystem.Play(Filter.Broadcast(), SoundEmpty, Owner.Transform.Coordinates);
}
return;
}
@@ -225,7 +208,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
var projectile = TakeProjectile(shooter.Transform.Coordinates);
if (projectile == null)
{
SoundSystem.Play(Filter.Broadcast(), _soundEmpty, Owner.Transform.Coordinates);
SoundSystem.Play(Filter.Broadcast(), SoundEmpty, Owner.Transform.Coordinates);
return;
}
@@ -266,7 +249,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
throw new InvalidOperationException();
}
SoundSystem.Play(Filter.Broadcast(), _soundGunshot, Owner.Transform.Coordinates);
SoundSystem.Play(Filter.Broadcast(), SoundGunshot, Owner.Transform.Coordinates);
_lastFire = _gameTiming.CurTime;
return;
@@ -352,7 +335,7 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged.Barrels
List<Angle> sprayAngleChange = null;
if (count > 1)
{
evenSpreadAngle *= _spreadRatio;
evenSpreadAngle *= SpreadRatio;
sprayAngleChange = Linspace(-evenSpreadAngle / 2, evenSpreadAngle / 2, count);
}

View File

@@ -18,8 +18,8 @@ using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Network;
using Robust.Shared.Players;
using Robust.Shared.Serialization;
using Robust.Shared.Timing;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.GameObjects.Components.Weapon.Ranged
@@ -33,9 +33,12 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged
private TimeSpan _lastFireTime;
[ViewVariables(VVAccess.ReadWrite)]
public bool ClumsyCheck { get; set; }
[DataField("clumsyCheck")]
public bool ClumsyCheck { get; set; } = true;
[ViewVariables(VVAccess.ReadWrite)]
public float ClumsyExplodeChance { get; set; }
[DataField("clumsyExplodeChance")]
public float ClumsyExplodeChance { get; set; } = 0.5f;
public Func<bool> WeaponCanFireHandler;
public Func<IEntity, bool> UserCanFireHandler;
@@ -70,14 +73,6 @@ namespace Content.Server.GameObjects.Components.Weapon.Ranged
return (UserCanFireHandler == null || UserCanFireHandler(user)) && ActionBlockerSystem.CanAttack(user);
}
public override void ExposeData(ObjectSerializer serializer)
{
base.ExposeData(serializer);
serializer.DataField(this, p => p.ClumsyCheck, "clumsyCheck", true);
serializer.DataField(this, p => p.ClumsyExplodeChance, "clumsyExplodeChance", 0.5f);
}
/// <inheritdoc />
public override void HandleNetworkMessage(ComponentMessage message, INetChannel channel, ICommonSession session = null)
{