mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
* oops
* fixes serialization il
* copytest
* typo & misc fixes
* 139 moment
* boxing
* mesa dum
* stuff
* goodbye bad friend
* last commit before the big (4) rewrite
* adds datanodes
* kills yamlobjserializer in favor of the new system
* adds more serializers, actually implements them & removes most of the last of the old system
* changed yamlfieldattribute namespace
* adds back iselfserialize
* refactors consts&flags
* renames everything to data(field/definition)
* adds afterserialization
* help
* dataclassgen
* fuggen help me mannen
* Fix most errors on content
* Fix engine errors except map loader
* maploader & misc fix
* misc fixes
* thing
* help
* refactors datanodes
* help me mannen
* Separate ITypeSerializer into reader and writer
* Convert all type serializers
* priority
* adds alot
* il fixes
* adds robustgen
* argh
* adds array & enum serialization
* fixes dataclasses
* adds vec2i / misc fixes
* fixes inheritance
* a very notcursed todo
* fixes some custom dataclasses
* push dis
* Remove data classes
* boutta box
* yes
* Add angle and regex serializer tests
* Make TypeSerializerTest abstract
* sets up ioc etc
* remove pushinheritance
* fixes
* Merge fixes, fix yaml hot reloading
* General fixes2
* Make enum serialization ignore case
* Fix the tag not being copied in data nodes
* Fix not properly serializing flag enums
* Fix component serialization on startup
* Implement ValueDataNode ToString
* Serialization IL fixes, fix return and string equality
* Remove async from prototype manager
* Make serializing unsupported node as enum exception more descriptive
* Fix serv3 tryread casting to serializer instead of reader
* Add constructor for invalid node type exception
* Temporary fix for SERV3: Turn populate delegate into regular code
* Fix not copying the data of non primitive types
* Fix not using the data definition found in copying
* Make ISerializationHooks require explicit implementations
* Add test for serialization inheritance
* Improve IsOverridenIn method
* Fix error message when a data definition is null
* Add method to cast a read value in Serv3Manager
* Rename IServ3Manager to ISerializationManager
* Rename usages of serv3manager, add generic copy method
* Fix IL copy method lookup
* Rename old usages of serv3manager
* Add ITypeCopier
* resistance is futile
* we will conquer this codebase
* Add copy method to all serializers
* Make primitive mismatch error message more descriptive
* bing bong im going to freacking heck
* oopsie moment
* hello are you interested in my wares
* does generic serializers under new architecture
* Convert every non generic serializer to the new format, general fixes
* Update usgaes of generic serializers, cleanup
* does some pushinheritance logic
* finishes pushinheritance FRAMEWORK
* shed
* Add box2, color and component registry serializer tests
* Create more deserialized types and store prototypes with their deserialized results
* Fixes and serializer updates
* Add serialization manager extensions
* adds pushinheritance
* Update all prototypes to have a parent and have consistent id/parent properties
* Fix grammar component serialization
* Add generic serializer tests
* thonk
* Add array serializer test
* Replace logger warning calls with exceptions
* fixes
* Move redundant methods to serialization manager extensions, cleanup
* Add array serialization
* fixes context
* more fixes
* argh
* inheritance
* this should do it
* fixes
* adds copiers & fixes some stuff
* copiers use context v1
* finishing copy context
* more context fixes
* Test fixes
* funky maps
* Fix server user interface component serialization
* Fix value tuple serialization
* Add copying for value types and arrays. Fix copy internal for primitives, enums and strings
* fixes
* fixes more stuff
* yes
* Make abstract/interface skips debugs instead of warnings
* Fix typo
* Make some dictionaries readonly
* Add checks for the serialization manager initializing and already being initialized
* Add base type required and usage for MeansDataDefinition and ImplicitDataDefinitionForInheritorsAttribute
* copy by ref
* Fix exception wording
* Update data field required summary with the new forbidden docs
* Use extension in map loader
* wanna erp
* Change serializing to not use il temporarily
* Make writing work with nullable types
* pushing
* check
* cuddling slaps HARD
* Add serialization priority test
* important fix
* a serialization thing
* serializer moment
* Add validation for some type serializers
* adds context
* moar context
* fixes
* Do the thing for appearance
* yoo lmao
* push haha pp
* Temporarily make copy delegate regular c# code
* Create deserialized component registry to handle not inheriting conflicting references
* YAML LINTER BABY
* ayes
* Fix sprite component norot not being default true like in latest master
* Remove redundant todos
* Add summary doc to every ISerializationManager method
* icon fixes
* Add skip hook argument to readers and copiers
* Merge fixes
* Fix ordering of arguments in read and copy reflection call
* Fix user interface components deserialization
* pew pew
* i am going to HECK
* Add MustUseReturnValue to copy-over methods
* Make serialization log calls use the same sawmill
* gamin
* Fix doc errors in ISerializationManager.cs
* goodbye brave soldier
* fixes
* WIP merge fixes and entity serialization
* aaaaaaaaaaaaaaa
* aaaaaaaaaaaaaaa
* adds inheritancebehaviour
* test/datafield fixes
* forgot that one
* adds more verbose validation
* This fixes the YAML hot reloading
* Replace yield break with Enumerable.Empty
* adds copiers
* aaaaaaaaaaaaa
* array fix
priority fix
misc fixes
* fix(?)
* fix.
* funny map serialization (wip)
* funny map serialization (wip)
* Add TODO
* adds proper info the validation
* Make yaml linter 5 times faster (~80% less execution time)
* Improves the error message for missing fields in the linter
* Include component name in unknown component type error node
* adds alwaysrelevant usa
* fixes mapsaving
* moved surpressor to analyzers proj
* warning cleanup & moves surpressor
* removes old msbuild targets
* Revert "Make yaml linter 5 times faster (~80% less execution time)"
This reverts commit 2ee4cc2c26.
* Add serialization to RobustServerSimulation and mock reflection methods
Fixes container tests
* Fix nullability warnings
* Improve yaml linter message feedback
* oops moment
* Add IEquatable, IComparable, ToString and operators to DataPosition
Rename it to NodeMark
Make it a readonly struct
* Remove try catch from enum parsing
* Make dependency management in serialization less bad
* Make dependencies an argument instead of a property on the serialization manager
* Clean up type serializers
* Improve validation messages and resourc epath checking
* Fix sprite error message
* reached perfection
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: Vera Aguilera Puerto <zddm@outlook.es>
716 lines
24 KiB
C#
716 lines
24 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Graphics;
|
|
using Robust.Client.Input;
|
|
using Robust.Client.Player;
|
|
using Robust.Client.ResourceManagement;
|
|
using Robust.Shared.Enums;
|
|
using Robust.Shared.GameObjects;
|
|
using Robust.Shared.Input;
|
|
using Robust.Shared.Input.Binding;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Map;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Network;
|
|
using Robust.Shared.Network.Messages;
|
|
using Robust.Shared.Physics;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Reflection;
|
|
using Robust.Shared.Timing;
|
|
using Robust.Shared.Utility;
|
|
|
|
namespace Robust.Client.Placement
|
|
{
|
|
public partial class PlacementManager : IPlacementManager, IDisposable
|
|
{
|
|
[Dependency] public readonly IPhysicsManager PhysicsManager = default!;
|
|
[Dependency] private readonly IClientNetManager NetworkManager = default!;
|
|
[Dependency] public readonly IPlayerManager PlayerManager = default!;
|
|
[Dependency] public readonly IResourceCache ResourceCache = default!;
|
|
[Dependency] private readonly IReflectionManager ReflectionManager = default!;
|
|
[Dependency] public readonly IMapManager MapManager = default!;
|
|
[Dependency] private readonly IGameTiming _time = default!;
|
|
[Dependency] public readonly IEyeManager eyeManager = default!;
|
|
[Dependency] private readonly IInputManager _inputManager = default!;
|
|
[Dependency] private readonly IEntitySystemManager _entitySystemManager = default!;
|
|
[Dependency] public readonly IEntityManager EntityManager = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
|
[Dependency] private readonly IBaseClient _baseClient = default!;
|
|
[Dependency] private readonly IOverlayManager _overlayManager = default!;
|
|
[Dependency] public readonly IClyde _clyde = default!;
|
|
|
|
/// <summary>
|
|
/// How long before a pending tile change is dropped.
|
|
/// </summary>
|
|
private static readonly TimeSpan _pendingTileTimeout = TimeSpan.FromSeconds(2.0);
|
|
|
|
/// <summary>
|
|
/// Dictionary of all placement mode types
|
|
/// </summary>
|
|
private readonly Dictionary<string, Type> _modeDictionary = new();
|
|
private readonly List<Tuple<EntityCoordinates, TimeSpan>> _pendingTileChanges = new();
|
|
|
|
/// <summary>
|
|
/// Tells this system to try to handle placement of an entity during the next frame
|
|
/// </summary>
|
|
private bool _placenextframe;
|
|
|
|
/// <summary>
|
|
/// Allows various types of placement as singular, line, or grid placement where placement mode allows this type of placement
|
|
/// </summary>
|
|
public PlacementTypes PlacementType { get; set; }
|
|
|
|
/// <summary>
|
|
/// Holds the anchor that we can try to spawn in a line or a grid from
|
|
/// </summary>
|
|
public EntityCoordinates StartPoint { get; set; }
|
|
|
|
/// <summary>
|
|
/// Whether the placement manager is currently in a mode where it accepts actions
|
|
/// </summary>
|
|
public bool IsActive
|
|
{
|
|
get => _isActive;
|
|
private set
|
|
{
|
|
_isActive = value;
|
|
SwitchEditorContext(value);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Determines whether we are using the mode to delete an entity on click
|
|
/// </summary>
|
|
public bool Eraser { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Holds the selection rectangle for the eraser
|
|
/// </summary>
|
|
public Box2? EraserRect { get; set; }
|
|
|
|
/// <summary>
|
|
/// Drawing shader for drawing without being affected by lighting
|
|
/// </summary>
|
|
private ShaderInstance? _drawingShader { get; set; }
|
|
|
|
/// <summary>
|
|
/// The texture we use to show from our placement manager to represent the entity to place
|
|
/// </summary>
|
|
public List<IDirectionalTextureProvider>? CurrentTextures { get; set; }
|
|
|
|
/// <summary>
|
|
/// Which of the placement orientations we are trying to place with
|
|
/// </summary>
|
|
public PlacementMode? CurrentMode { get; set; }
|
|
|
|
public PlacementInformation? CurrentPermission { get; set; }
|
|
|
|
public PlacementHijack? Hijack { get; private set; }
|
|
|
|
private EntityPrototype? _currentPrototype;
|
|
|
|
/// <summary>
|
|
/// The prototype of the entity we are going to spawn on click
|
|
/// </summary>
|
|
public EntityPrototype? CurrentPrototype
|
|
{
|
|
get => _currentPrototype;
|
|
set
|
|
{
|
|
_currentPrototype = value;
|
|
|
|
if (value != null)
|
|
{
|
|
PlacementOffset = value.PlacementOffset;
|
|
}
|
|
|
|
_colliderAABB = new Box2(0f, 0f, 0f, 0f);
|
|
}
|
|
}
|
|
|
|
public Vector2i PlacementOffset { get; set; }
|
|
|
|
|
|
private Box2 _colliderAABB = new(0f, 0f, 0f, 0f);
|
|
|
|
/// <summary>
|
|
/// The box which certain placement modes collision checks will be done against
|
|
/// </summary>
|
|
public Box2 ColliderAABB
|
|
{
|
|
get => _colliderAABB;
|
|
set => _colliderAABB = value;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The directional to spawn the entity in
|
|
/// </summary>
|
|
public Direction Direction { get; set; } = Direction.South;
|
|
|
|
private PlacementOverlay _drawOverlay = default!;
|
|
private bool _isActive;
|
|
|
|
public void Initialize()
|
|
{
|
|
_drawingShader = _prototypeManager.Index<ShaderPrototype>("unshaded").Instance();
|
|
|
|
NetworkManager.RegisterNetMessage<MsgPlacement>(MsgPlacement.NAME, HandlePlacementMessage);
|
|
|
|
_modeDictionary.Clear();
|
|
foreach (var type in ReflectionManager.GetAllChildren<PlacementMode>())
|
|
{
|
|
_modeDictionary.Add(type.Name, type);
|
|
}
|
|
|
|
MapManager.TileChanged += HandleTileChanged;
|
|
|
|
_drawOverlay = new PlacementOverlay(this);
|
|
_overlayManager.AddOverlay(_drawOverlay);
|
|
|
|
// a bit ugly, oh well
|
|
_baseClient.PlayerJoinedServer += (sender, args) => SetupInput();
|
|
_baseClient.PlayerLeaveServer += (sender, args) => TearDownInput();
|
|
}
|
|
|
|
private void SetupInput()
|
|
{
|
|
CommandBinds.Builder
|
|
.Bind(EngineKeyFunctions.EditorLinePlace, InputCmdHandler.FromDelegate(
|
|
session =>
|
|
{
|
|
if (IsActive && !Eraser) ActivateLineMode();
|
|
}))
|
|
.Bind(EngineKeyFunctions.EditorGridPlace, InputCmdHandler.FromDelegate(
|
|
session =>
|
|
{
|
|
if (IsActive)
|
|
{
|
|
if (Eraser)
|
|
{
|
|
EraseRectMode();
|
|
}
|
|
else
|
|
{
|
|
ActivateGridMode();
|
|
}
|
|
}
|
|
}))
|
|
.Bind(EngineKeyFunctions.EditorPlaceObject, new PointerStateInputCmdHandler(
|
|
(session, coords, uid) =>
|
|
{
|
|
if (!IsActive)
|
|
return false;
|
|
|
|
if (EraserRect.HasValue)
|
|
{
|
|
HandleRectDeletion(StartPoint, EraserRect.Value);
|
|
EraserRect = null;
|
|
return true;
|
|
}
|
|
|
|
if (Eraser)
|
|
{
|
|
if (HandleDeletion(coords))
|
|
return true;
|
|
|
|
if (uid == EntityUid.Invalid)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
HandleDeletion(EntityManager.GetEntity(uid));
|
|
}
|
|
else
|
|
{
|
|
_placenextframe = true;
|
|
}
|
|
|
|
return true;
|
|
},
|
|
(session, coords, uid) =>
|
|
{
|
|
if (!IsActive || Eraser || !_placenextframe)
|
|
return false;
|
|
|
|
//Places objects for non-tile entities
|
|
if (!CurrentPermission!.IsTile)
|
|
HandlePlacement();
|
|
|
|
_placenextframe = false;
|
|
return true;
|
|
}))
|
|
.Bind(EngineKeyFunctions.EditorRotateObject, InputCmdHandler.FromDelegate(
|
|
session =>
|
|
{
|
|
if (IsActive && !Eraser) Rotate();
|
|
}))
|
|
.Bind(EngineKeyFunctions.EditorCancelPlace, InputCmdHandler.FromDelegate(
|
|
session =>
|
|
{
|
|
if (!IsActive)
|
|
return;
|
|
if (DeactivateSpecialPlacement())
|
|
return;
|
|
Clear();
|
|
}))
|
|
.Register<PlacementManager>();
|
|
|
|
var localPlayer = PlayerManager.LocalPlayer;
|
|
localPlayer!.EntityAttached += OnEntityAttached;
|
|
}
|
|
|
|
private void TearDownInput()
|
|
{
|
|
CommandBinds.Unregister<PlacementManager>();
|
|
|
|
if (PlayerManager.LocalPlayer != null)
|
|
{
|
|
PlayerManager.LocalPlayer.EntityAttached -= OnEntityAttached;
|
|
}
|
|
}
|
|
|
|
private void OnEntityAttached(EntityAttachedEventArgs eventArgs)
|
|
{
|
|
// player attached to a new entity, basically disable the editor
|
|
Clear();
|
|
}
|
|
|
|
private void SwitchEditorContext(bool enabled)
|
|
{
|
|
if (enabled)
|
|
{
|
|
_inputManager.Contexts.SetActiveContext("editor");
|
|
}
|
|
else
|
|
{
|
|
_entitySystemManager.GetEntitySystem<InputSystem>().SetEntityContextActive();
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
_drawOverlay?.Dispose();
|
|
}
|
|
|
|
private void HandlePlacementMessage(MsgPlacement msg)
|
|
{
|
|
switch (msg.PlaceType)
|
|
{
|
|
case PlacementManagerMessage.StartPlacement:
|
|
HandleStartPlacement(msg);
|
|
break;
|
|
case PlacementManagerMessage.CancelPlacement:
|
|
Clear();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void HandleTileChanged(object? sender, TileChangedEventArgs args)
|
|
{
|
|
var coords = MapManager.GetGrid(args.NewTile.GridIndex).GridTileToLocal(args.NewTile.GridIndices);
|
|
_pendingTileChanges.RemoveAll(c => c.Item1 == coords);
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public event EventHandler? PlacementChanged;
|
|
|
|
public void Clear()
|
|
{
|
|
PlacementChanged?.Invoke(this, EventArgs.Empty);
|
|
Hijack = null;
|
|
CurrentTextures = null;
|
|
CurrentPrototype = null;
|
|
CurrentPermission = null;
|
|
CurrentMode = null;
|
|
DeactivateSpecialPlacement();
|
|
_placenextframe = false;
|
|
IsActive = false;
|
|
Eraser = false;
|
|
EraserRect = null;
|
|
PlacementOffset = Vector2i.Zero;
|
|
}
|
|
|
|
public void Rotate()
|
|
{
|
|
if (Hijack != null && !Hijack.CanRotate)
|
|
return;
|
|
|
|
switch (Direction)
|
|
{
|
|
case Direction.North:
|
|
Direction = Direction.East;
|
|
break;
|
|
case Direction.East:
|
|
Direction = Direction.South;
|
|
break;
|
|
case Direction.South:
|
|
Direction = Direction.West;
|
|
break;
|
|
case Direction.West:
|
|
Direction = Direction.North;
|
|
break;
|
|
}
|
|
|
|
CurrentMode?.SetSprite();
|
|
}
|
|
|
|
public void HandlePlacement()
|
|
{
|
|
if (!IsActive || Eraser)
|
|
return;
|
|
|
|
switch (PlacementType)
|
|
{
|
|
case PlacementTypes.None:
|
|
RequestPlacement(CurrentMode!.MouseCoords);
|
|
break;
|
|
case PlacementTypes.Line:
|
|
foreach (var coordinate in CurrentMode!.LineCoordinates())
|
|
{
|
|
RequestPlacement(coordinate);
|
|
}
|
|
|
|
DeactivateSpecialPlacement();
|
|
break;
|
|
case PlacementTypes.Grid:
|
|
foreach (var coordinate in CurrentMode!.GridCoordinates())
|
|
{
|
|
RequestPlacement(coordinate);
|
|
}
|
|
|
|
DeactivateSpecialPlacement();
|
|
break;
|
|
}
|
|
}
|
|
|
|
public bool HandleDeletion(EntityCoordinates coordinates)
|
|
{
|
|
if (!IsActive || !Eraser) return false;
|
|
if (Hijack != null)
|
|
return Hijack.HijackDeletion(coordinates);
|
|
|
|
return false;
|
|
}
|
|
|
|
public void HandleDeletion(IEntity entity)
|
|
{
|
|
if (!IsActive || !Eraser) return;
|
|
if (Hijack != null && Hijack.HijackDeletion(entity)) return;
|
|
|
|
var msg = NetworkManager.CreateNetMessage<MsgPlacement>();
|
|
msg.PlaceType = PlacementManagerMessage.RequestEntRemove;
|
|
msg.EntityUid = entity.Uid;
|
|
NetworkManager.ClientSendMessage(msg);
|
|
}
|
|
|
|
public void HandleRectDeletion(EntityCoordinates start, Box2 rect)
|
|
{
|
|
var msg = NetworkManager.CreateNetMessage<MsgPlacement>();
|
|
msg.PlaceType = PlacementManagerMessage.RequestRectRemove;
|
|
msg.EntityCoordinates = new EntityCoordinates(StartPoint.EntityId, rect.BottomLeft);
|
|
msg.RectSize = rect.Size;
|
|
NetworkManager.ClientSendMessage(msg);
|
|
}
|
|
|
|
public void ToggleEraser()
|
|
{
|
|
if (!Eraser && !IsActive)
|
|
{
|
|
IsActive = true;
|
|
Eraser = true;
|
|
}
|
|
else Clear();
|
|
}
|
|
|
|
public void ToggleEraserHijacked(PlacementHijack hijack)
|
|
{
|
|
if (!Eraser && !IsActive)
|
|
{
|
|
IsActive = true;
|
|
Eraser = true;
|
|
Hijack = hijack;
|
|
}
|
|
else Clear();
|
|
}
|
|
|
|
public void BeginPlacing(PlacementInformation info, PlacementHijack? hijack = null)
|
|
{
|
|
BeginHijackedPlacing(info, hijack);
|
|
}
|
|
|
|
public void BeginHijackedPlacing(PlacementInformation info, PlacementHijack? hijack = null)
|
|
{
|
|
Clear();
|
|
|
|
CurrentPermission = info;
|
|
|
|
if (!_modeDictionary.Any(pair => pair.Key.Equals(CurrentPermission.PlacementOption)))
|
|
{
|
|
Clear();
|
|
return;
|
|
}
|
|
|
|
var modeType = _modeDictionary.First(pair => pair.Key.Equals(CurrentPermission.PlacementOption)).Value;
|
|
CurrentMode = (PlacementMode) Activator.CreateInstance(modeType, this)!;
|
|
|
|
if (hijack != null)
|
|
{
|
|
Hijack = hijack;
|
|
Hijack.StartHijack(this);
|
|
IsActive = true;
|
|
return;
|
|
}
|
|
|
|
if (info.IsTile)
|
|
PreparePlacementTile();
|
|
else
|
|
PreparePlacement(info.EntityType!);
|
|
}
|
|
|
|
private bool CurrentMousePosition(out ScreenCoordinates coordinates)
|
|
{
|
|
// Try to get current map.
|
|
var map = MapId.Nullspace;
|
|
var ent = PlayerManager.LocalPlayer!.ControlledEntity;
|
|
if (ent != null)
|
|
{
|
|
map = ent.Transform.MapID;
|
|
}
|
|
|
|
if (map == MapId.Nullspace || CurrentPermission == null || CurrentMode == null)
|
|
{
|
|
coordinates = new ScreenCoordinates(Vector2.Zero);
|
|
return false;
|
|
}
|
|
|
|
coordinates = new ScreenCoordinates(_inputManager.MouseScreenPosition);
|
|
return true;
|
|
}
|
|
|
|
private bool CurrentEraserMouseCoordinates(out EntityCoordinates coordinates)
|
|
{
|
|
var ent = PlayerManager.LocalPlayer?.ControlledEntity;
|
|
if (ent == null)
|
|
{
|
|
coordinates = new EntityCoordinates();
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
var map = ent.Transform.MapID;
|
|
if (map == MapId.Nullspace || !Eraser)
|
|
{
|
|
coordinates = new EntityCoordinates();
|
|
return false;
|
|
}
|
|
coordinates = EntityCoordinates.FromMap(ent.EntityManager, MapManager,
|
|
eyeManager.ScreenToMap(new ScreenCoordinates(_inputManager.MouseScreenPosition)));
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/// <inheritdoc />
|
|
public void FrameUpdate(FrameEventArgs e)
|
|
{
|
|
if (!CurrentMousePosition(out var mouseScreen))
|
|
{
|
|
if (EraserRect.HasValue)
|
|
{
|
|
if (!CurrentEraserMouseCoordinates(out EntityCoordinates end))
|
|
return;
|
|
float b, l, t, r;
|
|
if (StartPoint.X < end.X)
|
|
{
|
|
l = StartPoint.X;
|
|
r = end.X;
|
|
}
|
|
else
|
|
{
|
|
l = end.X;
|
|
r = StartPoint.X;
|
|
}
|
|
if (StartPoint.Y < end.Y)
|
|
{
|
|
b = StartPoint.Y;
|
|
t = end.Y;
|
|
}
|
|
else
|
|
{
|
|
b = end.Y;
|
|
t = StartPoint.Y;
|
|
}
|
|
EraserRect = new Box2(l, b, r, t);
|
|
}
|
|
return;
|
|
}
|
|
|
|
CurrentMode!.AlignPlacementMode(mouseScreen);
|
|
|
|
// purge old unapproved tile changes
|
|
_pendingTileChanges.RemoveAll(c => c.Item2 < _time.RealTime);
|
|
|
|
// continues tile placement but placement of entities only occurs on mouseUp
|
|
if (_placenextframe && CurrentPermission!.IsTile)
|
|
HandlePlacement();
|
|
}
|
|
|
|
private void ActivateLineMode()
|
|
{
|
|
if (!CurrentMode!.HasLineMode)
|
|
return;
|
|
|
|
if (!CurrentMousePosition(out var mouseScreen))
|
|
return;
|
|
|
|
CurrentMode.AlignPlacementMode(mouseScreen);
|
|
StartPoint = CurrentMode.MouseCoords;
|
|
PlacementType = PlacementTypes.Line;
|
|
}
|
|
|
|
private void ActivateGridMode()
|
|
{
|
|
if (!CurrentMode!.HasGridMode)
|
|
return;
|
|
|
|
if (!CurrentMousePosition(out var mouseScreen))
|
|
return;
|
|
|
|
CurrentMode.AlignPlacementMode(mouseScreen);
|
|
StartPoint = CurrentMode.MouseCoords;
|
|
PlacementType = PlacementTypes.Grid;
|
|
}
|
|
|
|
private void EraseRectMode()
|
|
{
|
|
if (!CurrentEraserMouseCoordinates(out EntityCoordinates coordinates))
|
|
return;
|
|
|
|
StartPoint = coordinates;
|
|
EraserRect = new Box2(coordinates.Position, Vector2.Zero);
|
|
}
|
|
|
|
private bool DeactivateSpecialPlacement()
|
|
{
|
|
if (PlacementType == PlacementTypes.None)
|
|
return false;
|
|
|
|
PlacementType = PlacementTypes.None;
|
|
return true;
|
|
}
|
|
|
|
private void Render(DrawingHandleWorld handle)
|
|
{
|
|
if (CurrentMode == null || !IsActive)
|
|
{
|
|
if (EraserRect.HasValue)
|
|
{
|
|
handle.UseShader(_drawingShader);
|
|
handle.DrawRect(EraserRect.Value, new Color(255, 0, 0, 50));
|
|
}
|
|
return;
|
|
}
|
|
|
|
CurrentMode.Render(handle);
|
|
|
|
if (CurrentPermission == null || CurrentPermission.Range <= 0 || !CurrentMode.RangeRequired
|
|
|| PlayerManager.LocalPlayer?.ControlledEntity == null)
|
|
return;
|
|
|
|
var worldPos = PlayerManager.LocalPlayer.ControlledEntity.Transform.WorldPosition;
|
|
|
|
handle.DrawCircle(worldPos, CurrentPermission.Range, new Color(1, 1, 1, 0.25f));
|
|
}
|
|
|
|
private void HandleStartPlacement(MsgPlacement msg)
|
|
{
|
|
CurrentPermission = new PlacementInformation
|
|
{
|
|
Range = msg.Range,
|
|
IsTile = msg.IsTile,
|
|
};
|
|
|
|
CurrentPermission.EntityType = msg.ObjType; // tile or ent type
|
|
CurrentPermission.PlacementOption = msg.AlignOption;
|
|
|
|
BeginPlacing(CurrentPermission);
|
|
}
|
|
|
|
private void PreparePlacement(string templateName)
|
|
{
|
|
var prototype = _prototypeManager.Index<EntityPrototype>(templateName);
|
|
|
|
CurrentTextures = SpriteComponent.GetPrototypeTextures(prototype, ResourceCache).ToList();
|
|
CurrentPrototype = prototype;
|
|
|
|
IsActive = true;
|
|
}
|
|
|
|
private void PreparePlacementTile()
|
|
{
|
|
CurrentTextures = new List<IDirectionalTextureProvider>
|
|
{ResourceCache
|
|
.GetResource<TextureResource>(new ResourcePath("/Textures/UserInterface/tilebuildoverlay.png")).Texture};
|
|
|
|
IsActive = true;
|
|
}
|
|
|
|
private void RequestPlacement(EntityCoordinates coordinates)
|
|
{
|
|
if (CurrentPermission == null) return;
|
|
if (!CurrentMode!.IsValidPosition(coordinates)) return;
|
|
if (Hijack != null && Hijack.HijackPlacementRequest(coordinates)) return;
|
|
|
|
if (CurrentPermission.IsTile)
|
|
{
|
|
var gridId = coordinates.GetGridId(EntityManager);
|
|
// If we have actually placed something on a valid grid...
|
|
if (gridId.IsValid())
|
|
{
|
|
var grid = MapManager.GetGrid(gridId);
|
|
|
|
// no point changing the tile to the same thing.
|
|
if (grid.GetTileRef(coordinates).Tile.TypeId == CurrentPermission.TileType)
|
|
return;
|
|
}
|
|
|
|
foreach (var tileChange in _pendingTileChanges)
|
|
{
|
|
// if change already pending, ignore it
|
|
if (tileChange.Item1 == coordinates)
|
|
return;
|
|
}
|
|
|
|
var tuple = new Tuple<EntityCoordinates, TimeSpan>(coordinates, _time.RealTime + _pendingTileTimeout);
|
|
_pendingTileChanges.Add(tuple);
|
|
}
|
|
|
|
var message = NetworkManager.CreateNetMessage<MsgPlacement>();
|
|
message.PlaceType = PlacementManagerMessage.RequestPlacement;
|
|
|
|
message.Align = CurrentMode.ModeName;
|
|
message.IsTile = CurrentPermission.IsTile;
|
|
|
|
if (CurrentPermission.IsTile)
|
|
message.TileType = CurrentPermission.TileType;
|
|
else
|
|
message.EntityTemplateName = CurrentPermission.EntityType;
|
|
|
|
// world x and y
|
|
message.EntityCoordinates = coordinates;
|
|
|
|
message.DirRcv = Direction;
|
|
|
|
NetworkManager.ClientSendMessage(message);
|
|
}
|
|
|
|
public enum PlacementTypes : byte
|
|
{
|
|
None = 0,
|
|
Line = 1,
|
|
Grid = 2
|
|
}
|
|
}
|
|
}
|