Files
OldThink/Content.Client/IconSmoothing/IconSmoothComponent.cs

359 lines
12 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Log;
2021-06-09 22:19:39 +02:00
using Robust.Shared.Map;
using Robust.Shared.Maths;
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>
2021-03-05 01:08:38 +01:00
using Robust.Shared.Serialization.Manager.Attributes;
using static Robust.Client.GameObjects.SpriteComponent;
2018-08-09 20:22:54 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Client.IconSmoothing
2018-08-09 20:22:54 +02:00
{
// TODO: Potential improvements:
// Defer updating of these.
// Get told by somebody to use a loop.
/// <summary>
/// Makes sprites of other grid-aligned entities like us connect.
/// </summary>
/// <remarks>
/// The system is based on Baystation12's smoothwalling, and thus will work with those.
/// To use, set <c>base</c> equal to the prefix of the corner states in the sprite base RSI.
/// Any objects with the same <c>key</c> will connect.
/// </remarks>
2019-07-31 15:02:36 +02:00
[RegisterComponent]
[Virtual]
public class IconSmoothComponent : Component
2018-08-09 20:22:54 +02:00
{
[Dependency] private readonly IEntityManager _entMan = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
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>
2021-03-05 01:08:38 +01:00
[DataField("mode")]
private IconSmoothingMode _mode = IconSmoothingMode.Corners;
internal ISpriteComponent? Sprite { get; private set; }
2022-01-29 07:25:49 +13:00
public (GridId, Vector2i)? LastPosition;
/// <summary>
/// We will smooth with other objects with the same key.
/// </summary>
[DataField("key")]
public string? SmoothKey { get; }
2018-08-09 20:22:54 +02:00
/// <summary>
/// Prepended to the RSI state.
/// </summary>
[DataField("base")]
public string StateBase { get; } = string.Empty;
2018-08-09 20:22:54 +02:00
/// <summary>
/// Mode that controls how the icon should be selected.
2018-08-09 20:22:54 +02:00
/// </summary>
public IconSmoothingMode Mode => _mode;
2018-08-09 20:22:54 +02:00
/// <summary>
/// Used by <see cref="IconSmoothSystem"/> to reduce redundant updates.
/// </summary>
internal int UpdateGeneration { get; set; }
2018-08-09 20:22:54 +02:00
protected override void Initialize()
2018-08-09 20:22:54 +02:00
{
base.Initialize();
Sprite = _entMan.GetComponent<ISpriteComponent>(Owner);
2018-08-09 20:22:54 +02:00
}
/// <inheritdoc />
protected override void Startup()
2018-08-09 20:22:54 +02:00
{
base.Startup();
if (_entMan.GetComponent<TransformComponent>(Owner).Anchored)
{
// ensures lastposition initial value is populated on spawn. Just calling
// the hook here would cause a dirty event to fire needlessly
UpdateLastPosition();
2022-01-29 07:25:49 +13:00
EntitySystem.Get<IconSmoothSystem>().UpdateSmoothing(Owner, this);
}
if (Sprite != null && Mode == IconSmoothingMode.Corners)
2018-08-09 20:22:54 +02:00
{
var state0 = $"{StateBase}0";
Sprite.LayerMapSet(CornerLayers.SE, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.SE, DirectionOffset.None);
Sprite.LayerMapSet(CornerLayers.NE, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.NE, DirectionOffset.CounterClockwise);
Sprite.LayerMapSet(CornerLayers.NW, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.NW, DirectionOffset.Flip);
Sprite.LayerMapSet(CornerLayers.SW, Sprite.AddLayerState(state0));
Sprite.LayerSetDirOffset(CornerLayers.SW, DirectionOffset.Clockwise);
2018-08-09 20:22:54 +02:00
}
}
private void UpdateLastPosition()
{
2022-01-29 07:25:49 +13:00
var transform = _entMan.GetComponent<TransformComponent>(Owner);
if (_mapManager.TryGetGrid(transform.GridID, out var grid))
{
2022-01-29 07:25:49 +13:00
LastPosition = (transform.GridID, grid.TileIndicesFor(transform.Coordinates));
}
else
{
// When this is called during component startup, the transform can end up being with an invalid grid ID.
// In that case, use this.
2022-01-29 07:25:49 +13:00
LastPosition = (GridId.Invalid, new Vector2i(0, 0));
}
}
internal virtual void CalculateNewSprite()
{
2022-01-29 07:25:49 +13:00
var transform = _entMan.GetComponent<TransformComponent>(Owner);
if (!transform.Anchored)
{
CalculateNewSprite(null);
return;
}
if (!_mapManager.TryGetGrid(transform.GridID, out var grid))
{
2022-01-29 07:25:49 +13:00
Logger.Error($"Failed to calculate IconSmoothComponent sprite in {Owner} because grid {transform.GridID} was missing.");
return;
}
CalculateNewSprite(grid);
}
2022-01-29 07:25:49 +13:00
internal virtual void CalculateNewSprite(IMapGrid? grid)
{
switch (Mode)
{
case IconSmoothingMode.Corners:
CalculateNewSpriteCorners(grid);
break;
case IconSmoothingMode.CardinalFlags:
CalculateNewSpriteCardinal(grid);
break;
case IconSmoothingMode.NoSprite:
break;
default:
throw new ArgumentOutOfRangeException();
}
}
2022-01-29 07:25:49 +13:00
private void CalculateNewSpriteCardinal(IMapGrid? grid)
{
2022-01-29 07:25:49 +13:00
if (Sprite == null)
{
return;
}
var dirs = CardinalConnectDirs.None;
2022-01-29 07:25:49 +13:00
if (grid == null)
{
Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
return;
}
var position = _entMan.GetComponent<TransformComponent>(Owner).Coordinates;
if (MatchingEntity(grid.GetInDir(position, Direction.North)))
dirs |= CardinalConnectDirs.North;
if (MatchingEntity(grid.GetInDir(position, Direction.South)))
dirs |= CardinalConnectDirs.South;
if (MatchingEntity(grid.GetInDir(position, Direction.East)))
dirs |= CardinalConnectDirs.East;
if (MatchingEntity(grid.GetInDir(position, Direction.West)))
dirs |= CardinalConnectDirs.West;
Sprite.LayerSetState(0, $"{StateBase}{(int) dirs}");
}
2022-01-29 07:25:49 +13:00
private void CalculateNewSpriteCorners(IMapGrid? grid)
2020-04-25 12:10:28 +02:00
{
if (Sprite == null)
{
return;
}
var (cornerNE, cornerNW, cornerSW, cornerSE) = CalculateCornerFill(grid);
2020-04-25 12:10:28 +02:00
Sprite.LayerSetState(CornerLayers.NE, $"{StateBase}{(int) cornerNE}");
Sprite.LayerSetState(CornerLayers.SE, $"{StateBase}{(int) cornerSE}");
Sprite.LayerSetState(CornerLayers.SW, $"{StateBase}{(int) cornerSW}");
Sprite.LayerSetState(CornerLayers.NW, $"{StateBase}{(int) cornerNW}");
}
2022-01-29 07:25:49 +13:00
protected (CornerFill ne, CornerFill nw, CornerFill sw, CornerFill se) CalculateCornerFill(IMapGrid? grid)
{
2022-01-29 07:25:49 +13:00
if (grid == null)
{
return (CornerFill.None, CornerFill.None, CornerFill.None, CornerFill.None);
}
var position = _entMan.GetComponent<TransformComponent>(Owner).Coordinates;
var n = MatchingEntity(grid.GetInDir(position, Direction.North));
var ne = MatchingEntity(grid.GetInDir(position, Direction.NorthEast));
var e = MatchingEntity(grid.GetInDir(position, Direction.East));
var se = MatchingEntity(grid.GetInDir(position, Direction.SouthEast));
var s = MatchingEntity(grid.GetInDir(position, Direction.South));
var sw = MatchingEntity(grid.GetInDir(position, Direction.SouthWest));
var w = MatchingEntity(grid.GetInDir(position, Direction.West));
var nw = MatchingEntity(grid.GetInDir(position, Direction.NorthWest));
// ReSharper disable InconsistentNaming
var cornerNE = CornerFill.None;
var cornerSE = CornerFill.None;
var cornerSW = CornerFill.None;
var cornerNW = CornerFill.None;
// ReSharper restore InconsistentNaming
if (n)
{
cornerNE |= CornerFill.CounterClockwise;
cornerNW |= CornerFill.Clockwise;
}
if (ne)
{
cornerNE |= CornerFill.Diagonal;
}
if (e)
{
cornerNE |= CornerFill.Clockwise;
cornerSE |= CornerFill.CounterClockwise;
}
if (se)
{
cornerSE |= CornerFill.Diagonal;
}
if (s)
{
cornerSE |= CornerFill.Clockwise;
cornerSW |= CornerFill.CounterClockwise;
}
if (sw)
{
cornerSW |= CornerFill.Diagonal;
}
if (w)
{
cornerSW |= CornerFill.Clockwise;
cornerNW |= CornerFill.CounterClockwise;
}
if (nw)
{
cornerNW |= CornerFill.Diagonal;
}
2021-09-05 17:23:47 +10:00
// Local is fine as we already know it's parented to the grid (due to the way anchoring works).
switch (_entMan.GetComponent<TransformComponent>(Owner).LocalRotation.GetCardinalDir())
{
case Direction.North:
return (cornerSW, cornerSE, cornerNE, cornerNW);
case Direction.West:
return (cornerSE, cornerNE, cornerNW, cornerSW);
case Direction.South:
return (cornerNE, cornerNW, cornerSW, cornerSE);
default:
return (cornerNW, cornerSW, cornerSE, cornerNE);
}
}
/// <inheritdoc />
protected override void Shutdown()
2018-08-09 20:22:54 +02:00
{
base.Shutdown();
2022-01-29 07:25:49 +13:00
EntitySystem.Get<IconSmoothSystem>().UpdateSmoothing(Owner, this);
2018-08-09 20:22:54 +02:00
}
[System.Diagnostics.Contracts.Pure]
protected bool MatchingEntity(IEnumerable<EntityUid> candidates)
{
foreach (var entity in candidates)
{
if (!_entMan.TryGetComponent(entity, out IconSmoothComponent? other))
{
continue;
}
if (other.SmoothKey == SmoothKey)
{
return true;
}
}
return false;
}
[Flags]
private enum CardinalConnectDirs : byte
{
None = 0,
North = 1,
South = 2,
East = 4,
West = 8
}
[Flags]
public enum CornerFill : byte
{
// These values are pulled from Baystation12.
// I'm too lazy to convert the state names.
None = 0,
// The cardinal tile counter-clockwise of this corner is filled.
CounterClockwise = 1,
// The diagonal tile in the direction of this corner.
Diagonal = 2,
// The cardinal tile clockwise of this corner is filled.
Clockwise = 4,
}
2020-12-04 11:57:33 +01:00
public enum CornerLayers : byte
2018-08-09 20:22:54 +02:00
{
SE,
NE,
NW,
SW,
}
}
2018-08-09 20:22:54 +02:00
/// <summary>
/// Controls the mode with which icon smoothing is calculated.
/// </summary>
[PublicAPI]
2020-12-04 11:57:33 +01:00
public enum IconSmoothingMode : byte
{
/// <summary>
/// Each icon is made up of 4 corners, each of which can get a different state depending on
/// adjacent entities clockwise, counter-clockwise and diagonal with the corner.
/// </summary>
Corners,
/// <summary>
/// There are 16 icons, only one of which is used at once.
/// The icon selected is a bit field made up of the cardinal direction flags that have adjacent entities.
/// </summary>
CardinalFlags,
/// <summary>
/// Where this component contributes to our neighbors being calculated but we do not update our own sprite.
/// </summary>
NoSprite,
2018-08-09 20:22:54 +02:00
}
}