diff --git a/Content.Client/Silicons/Borgs/BorgSystem.cs b/Content.Client/Silicons/Borgs/BorgSystem.cs index 517027d0821..225cd204d7d 100644 --- a/Content.Client/Silicons/Borgs/BorgSystem.cs +++ b/Content.Client/Silicons/Borgs/BorgSystem.cs @@ -74,6 +74,13 @@ private void UpdateBorgAppearance(Entity(ent.Owner, MobStateVisuals.State, out var state, ent.Comp2)) { if (state != MobState.Alive) @@ -90,6 +97,9 @@ private void UpdateBorgAppearance(Entity ent, ref bool handled); + private void OnMMIAppearanceChanged(EntityUid uid, MMIComponent component, ref AppearanceChangeEvent args) { if (args.Sprite == null) diff --git a/Content.Client/_Lust/Borgs/BorgSystem.StandingVisuals.cs b/Content.Client/_Lust/Borgs/BorgSystem.StandingVisuals.cs new file mode 100644 index 00000000000..1f15b648f82 --- /dev/null +++ b/Content.Client/_Lust/Borgs/BorgSystem.StandingVisuals.cs @@ -0,0 +1,94 @@ +using Content.Shared._Lust.Borgs.Components; +using Content.Shared.Mobs; +using Content.Shared.Silicons.Borgs.Components; +using Robust.Client.GameObjects; +using Robust.Client.Graphics; + +// Lust edit start - визуал сидения боргов в отдельном файле +#pragma warning disable IDE0130 +namespace Content.Client.Silicons.Borgs; + +public sealed partial class BorgSystem +{ + partial void UpdateLustBorgStandingVisuals( + Entity ent, + ref bool handled) + { + if (!TryGetLayerState(ent, BorgVisualLayers.Body, out var bodyState)) + return; + + var alive = !_appearance.TryGetData(ent.Owner, MobStateVisuals.State, out var mobState, ent.Comp2) + || mobState == MobState.Alive; + var resting = alive + && _appearance.TryGetData(ent.Owner, BorgRestVisuals.Resting, out var restingVisual, ent.Comp2) + && restingVisual; + + if (TrySetDerivedLayerState(ent, BorgVisualLayers.Resting, bodyState, "rest")) + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Resting, resting); + else + HideLayer(ent, BorgVisualLayers.Resting); + + if (TrySetDerivedLayerState(ent, BorgVisualLayers.Wrecked, bodyState, "wreck")) + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Wrecked, !alive); + else + HideLayer(ent, BorgVisualLayers.Wrecked); + + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Body, alive && !resting); + + if (alive && !resting) + return; + + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.Light, false); + + if (_sprite.LayerExists((ent.Owner, ent.Comp3), BorgVisualLayers.LightStatus)) + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), BorgVisualLayers.LightStatus, false); + + handled = true; + } + + private bool TryGetLayerState( + Entity ent, + BorgVisualLayers layer, + out string state) + { + state = string.Empty; + + if (!_sprite.LayerExists((ent.Owner, ent.Comp3), layer)) + return false; + + var stateId = _sprite.LayerGetRsiState((ent.Owner, ent.Comp3), layer, RSI.StateId.Invalid); + if (!stateId.IsValid || stateId.Name == null) + return false; + + state = stateId.Name; + return true; + } + + private void HideLayer( + Entity ent, + BorgVisualLayers layer) + { + if (_sprite.LayerExists((ent.Owner, ent.Comp3), layer)) + _sprite.LayerSetVisible((ent.Owner, ent.Comp3), layer, false); + } + + private bool TrySetDerivedLayerState( + Entity ent, + BorgVisualLayers layer, + string bodyState, + string suffix) + { + if (!_sprite.LayerExists((ent.Owner, ent.Comp3), layer)) + return false; + + var state = $"{bodyState}_{suffix}"; + var rsi = _sprite.LayerGetEffectiveRsi((ent.Owner, ent.Comp3), layer, RSI.StateId.Invalid); + if (rsi == null || !rsi.TryGetState(state, out _)) + return false; + + _sprite.LayerSetRsiState((ent.Owner, ent.Comp3), layer, state); + return true; + } +} +#pragma warning restore IDE0130 +// Lust edit end diff --git a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs index d7b90a1ac2c..e58c610b74f 100644 --- a/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs +++ b/Content.Shared/Silicons/Borgs/Components/BorgChassisComponent.cs @@ -172,4 +172,16 @@ public enum BorgVisualLayers : byte /// Layer for the borg flashlight status. /// LightStatus, + + // Lust edit start - визуальные слои сидящего и сломанного борга + /// + /// Layer for the borg's voluntary resting state. + /// + Resting, + + /// + /// Layer for the borg's wrecked state. + /// + Wrecked, + // Lust edit end } diff --git a/Content.Shared/_Lust/Borgs/Components/BorgRestActionComponent.cs b/Content.Shared/_Lust/Borgs/Components/BorgRestActionComponent.cs new file mode 100644 index 00000000000..78dcd8baa89 --- /dev/null +++ b/Content.Shared/_Lust/Borgs/Components/BorgRestActionComponent.cs @@ -0,0 +1,39 @@ +using Content.Shared._Lust.Borgs; +using Content.Shared.Actions; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared._Lust.Borgs.Components; + +/// +/// Adds a borg action that toggles voluntary resting. +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +[Access(typeof(SharedBorgRestActionSystem))] +public sealed partial class BorgRestActionComponent : Component +{ + /// + /// Action prototype used to sit down or stand up. + /// + [DataField] + public EntProtoId ToggleAction = "ActionBorgToggleRest"; + + /// + /// Action entity currently granted to the borg. + /// + [AutoNetworkedField] + public EntityUid? ToggleActionEntity; +} + +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedBorgRestActionSystem))] +public sealed partial class BorgRestingComponent : Component; + +public sealed partial class BorgToggleRestActionEvent : InstantActionEvent; + +[Serializable, NetSerializable] +public enum BorgRestVisuals : byte +{ + Resting, +} diff --git a/Content.Shared/_Lust/Borgs/SharedBorgRestActionSystem.cs b/Content.Shared/_Lust/Borgs/SharedBorgRestActionSystem.cs new file mode 100644 index 00000000000..4e6fb702093 --- /dev/null +++ b/Content.Shared/_Lust/Borgs/SharedBorgRestActionSystem.cs @@ -0,0 +1,155 @@ +using Content.Shared._Lust.Borgs.Components; +using Content.Shared.Actions; +using Content.Shared.Mobs.Systems; +using Content.Shared.Rotation; +using Content.Shared.Silicons.Borgs.Components; +using Content.Shared.Stunnable; +using Robust.Shared.Player; + +namespace Content.Shared._Lust.Borgs; + +public sealed class SharedBorgRestActionSystem : EntitySystem +{ + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly SharedActionsSystem _actions = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedStunSystem _stun = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStartup); + SubscribeLocalEvent(OnShutdown); + SubscribeLocalEvent(OnToggleRest); + SubscribeLocalEvent(OnRestingStartup); + SubscribeLocalEvent(OnRestingRemove); + SubscribeLocalEvent(OnKnockedDownRemove); + } + + private void OnStartup(Entity ent, ref ComponentStartup args) + { + _actions.AddAction(ent, ref ent.Comp.ToggleActionEntity, ent.Comp.ToggleAction); + UpdateActionState(ent); + } + + private void OnShutdown(Entity ent, ref ComponentShutdown args) + { + _actions.RemoveAction(ent.Owner, ent.Comp.ToggleActionEntity); + } + + private void OnToggleRest(Entity ent, ref BorgToggleRestActionEvent args) + { + if (args.Handled) + return; + + if (TryToggleRest(ent.AsNullable())) + args.Handled = true; + } + + private void OnRestingStartup(Entity ent, ref ComponentStartup args) + { + if (TryComp(ent, out var borgRest)) + UpdateActionState((ent.Owner, borgRest)); + } + + private void OnRestingRemove(Entity ent, ref ComponentRemove args) + { + ClearRestingVisual(ent.Owner); + + if (TryComp(ent, out var borgRest)) + UpdateActionState((ent.Owner, borgRest), resting: false); + } + + private void OnKnockedDownRemove(Entity ent, ref ComponentRemove args) + { + RemCompDeferred(ent); + } + + private void UpdateActionState(Entity ent) + { + UpdateActionState(ent, HasComp(ent)); + } + + private void UpdateActionState(Entity ent, bool resting) + { + _actions.SetToggled(ent.Comp.ToggleActionEntity, resting); + } + + /// + /// Пытается переключить состояние отдыха борга. + /// + public bool TryToggleRest(Entity ent) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (!CanToggleRest(ent)) + return false; + + Entity borgRest = (ent.Owner, ent.Comp); + + if (HasComp(ent)) + { + DoStandUp(borgRest); + return true; + } + + return DoRest(borgRest); + } + + /// + /// Проверяет, может ли борг переключить состояние отдыха. + /// + public bool CanToggleRest(Entity ent, bool quiet = false) + { + if (!Resolve(ent, ref ent.Comp)) + return false; + + if (HasComp(ent)) + return true; + + if (!TryComp(ent, out var borg) || borg.BrainEntity == null) + return false; + + if (!HasComp(ent)) + return false; + + return _mobState.IsAlive(ent); + } + + private bool DoRest(Entity ent) + { + if (!_stun.TryKnockdown(ent.Owner, null, refresh: true, autoStand: false, drop: false, force: true)) + { + ClearRestingVisual(ent); + return false; + } + + EnsureComp(ent); + SetRestingVisual(ent, true); + return true; + } + + private void DoStandUp(Entity ent) + { + RemComp(ent); + RemComp(ent); + } + + private void SetRestingVisualRotation(EntityUid uid) + { + _appearance.SetData(uid, RotationVisuals.RotationState, RotationState.Vertical); + } + + private void SetRestingVisual(EntityUid uid, bool resting) + { + _appearance.SetData(uid, BorgRestVisuals.Resting, resting); + SetRestingVisualRotation(uid); + } + + private void ClearRestingVisual(EntityUid uid) + { + SetRestingVisual(uid, false); + } +} diff --git a/Resources/Locale/en-US/_prototypes/actions/borgs.ftl b/Resources/Locale/en-US/_prototypes/actions/borgs.ftl index be6b7a6eff8..ebbda1e8b10 100644 --- a/Resources/Locale/en-US/_prototypes/actions/borgs.ftl +++ b/Resources/Locale/en-US/_prototypes/actions/borgs.ftl @@ -2,3 +2,5 @@ ent-ActionViewLaws = View Laws .desc = View the laws that you must follow. ent-ActionSelectBorgType = Select Cyborg Type .desc = { "" } +ent-ActionBorgToggleRest = Sit down + .desc = Sit down or stand up. diff --git a/Resources/Locale/ru-RU/_prototypes/actions/borgs.ftl b/Resources/Locale/ru-RU/_prototypes/actions/borgs.ftl index f577d4aa7e6..80bbae6828a 100644 --- a/Resources/Locale/ru-RU/_prototypes/actions/borgs.ftl +++ b/Resources/Locale/ru-RU/_prototypes/actions/borgs.ftl @@ -2,3 +2,5 @@ ent-ActionViewLaws = Просмотреть законы .desc = Просмотреть законы, которым вы должны следовать. ent-ActionSelectBorgType = Select Cyborg Type .desc = { "" } +ent-ActionBorgToggleRest = Сесть + .desc = Сесть или встать. diff --git a/Resources/Prototypes/Actions/borgs.yml b/Resources/Prototypes/Actions/borgs.yml index 59c008352c2..df7dfe4153b 100644 --- a/Resources/Prototypes/Actions/borgs.yml +++ b/Resources/Prototypes/Actions/borgs.yml @@ -26,3 +26,20 @@ useDelay: 0.5 - type: InstantAction event: !type:BorgToggleSelectTypeEvent + +- type: entity + parent: BaseToggleAction + id: ActionBorgToggleRest + name: Sit down + description: Sit down or stand up. + components: + # Lust edit start - действие сидения для боргов + - type: Action + itemIconStyle: NoItem + icon: + sprite: _Lust/Mobs/Silicon/chassis.rsi + state: robot + useDelay: 0.5 + - type: InstantAction + event: !type:BorgToggleRestActionEvent + # Lust edit end diff --git a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml index fd66713d9cd..1b965e09a3a 100644 --- a/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml +++ b/Resources/Prototypes/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -15,11 +15,26 @@ shader: unshaded map: ["light","enum.BorgVisualLayers.LightStatus"] visible: false + # Lust edit start - слои общего визуала сидения и поломки боргов + - state: robot_rest + map: ["enum.BorgVisualLayers.Resting"] + visible: false + - state: robot_wreck + map: ["enum.BorgVisualLayers.Wrecked"] + visible: false + # Lust edit end - type: BorgChassis # Default borg can take no modules until selected type. maxModules: 0 hasMindState: robot_e noMindState: robot_e_r + # Lust edit - кнопка сидения для боргов с resting-спрайтами + - type: BorgRestAction + # Lust edit start - борги с кнопкой сидения используют общий механизм вставания + - type: Crawler + standTime: 1 + speedModifier: 0 + # Lust edit end - type: BorgTransponder sprite: sprite: Mobs/Silicon/chassis.rsi