Upstream core (#284)

* [Feature] Holo Update (#948)

* Это база, основа, фундамент.

* update icons

* based2

* zamena headcoder

* протоНовые функции

     Обновлены окна персонажа для отображения статистики и навыков игрока.
     Добавлена система боевой музыки, которая активируется во время боя.
     Реализована функция широковещательной передачи сообщений в чате для нескольких получателей.
     Внедрены изменения на основе навыков и статистики для медицинских систем, включая дефибриллятор и лечение.
     Улучшена система ближнего боя с новыми действиями и сообщениями.
     Введена система предсказуемого рандома для продвинутой генерации случайных значений.
     Навыки и статистика теперь зависят от назначений на работу.

* abilities

* zvuk

* tweaks & fixes

* sprite fix

* govno

* govno2

* fix govna

* GOVNOOOOOOOOOOOO

* finally

* цена

(cherry picked from commit cf4a7d0a7ccb780905e0df7db80d60d2338c02d0)

* Automatic changelog update

(cherry picked from commit 32a1f13849b4593fa03eafff99179814278f5f11)

---------

Co-authored-by: RavmorganButOnCocaine <valtos@nextmail.ru>
This commit is contained in:
withoutcode333
2025-04-18 08:26:02 +05:00
committed by GitHub
parent 6f1be858ec
commit 08f0c51ce0
26 changed files with 790 additions and 89 deletions

View File

@@ -0,0 +1,114 @@
using Content.Client._White.UserInterface.Radial;
using Content.Shared._White.Guardian;
namespace Content.Client._White.Guardian;
public sealed class GuardianSelectorBUI : BoundUserInterface
{
private readonly Dictionary<GuardianSelector, string> _names = new()
{
{ GuardianSelector.Assasin, Loc.GetString("guardian-assasin-name")},
{ GuardianSelector.Charger, Loc.GetString("guardian-charger-name")},
{ GuardianSelector.Lighting, Loc.GetString("guardian-lighting-name")},
{ GuardianSelector.Standart, Loc.GetString("guardian-standart-name")},
};
private readonly Dictionary<GuardianSelector, string> _icons = new()
{
{ GuardianSelector.Assasin, "/Textures/White/Interface/guardianselector.rsi/assasin.png" },
{ GuardianSelector.Charger, "/Textures/White/Interface/guardianselector.rsi/charger.png" },
{ GuardianSelector.Lighting, "/Textures/White/Interface/guardianselector.rsi/lighting.png" },
{ GuardianSelector.Standart, "/Textures/White/Interface/guardianselector.rsi/standart.png" },
};
private readonly Dictionary<string, GuardianSelector> _guardianSelectors = new()
{
{ "Assasin", GuardianSelector.Assasin },
{ "Charger", GuardianSelector.Charger },
{ "Lighting", GuardianSelector.Lighting },
{ "Standart", GuardianSelector.Standart },
};
private RadialContainer? _radialContainer;
private bool _updated;
public GuardianSelectorBUI(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
}
protected override void Open()
{
base.Open();
if (_radialContainer != null)
UIReset();
_radialContainer = new RadialContainer();
_radialContainer.Closed += Close;
if (State != null)
UpdateState(State);
}
private void UIReset()
{
_radialContainer?.Close();
_radialContainer = null;
_updated = false;
}
private void PopulateRadial(IReadOnlyCollection<string> ids, NetEntity target)
{
foreach (var id in ids)
{
if (_radialContainer == null)
continue;
if(!_guardianSelectors.TryGetValue(id, out var guardianSelector))
return;
if(!_names.TryGetValue(guardianSelector, out var name) || !_icons.TryGetValue(guardianSelector, out var icon))
return;
var button = _radialContainer.AddButton(name, icon);
button.Controller.OnPressed += _ =>
{
Select(guardianSelector, target);
};
}
}
private void Select(GuardianSelector type, NetEntity target)
{
SendMessage(new GuardianSelectorSelectedBuiMessage(type, target));
UIReset();
Close();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
_radialContainer?.Close();
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (_updated)
return;
if (state is GuardianSelectorBUIState newState)
{
PopulateRadial(newState.Ids, newState.Target);
}
if (_radialContainer == null)
return;
_radialContainer?.OpenAttachedLocalPlayer();
_updated = true;
}
}

View File

@@ -1,3 +1,8 @@
using Content.Shared._White.Guardian;
using Robust.Shared.Audio;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Guardian
{
/// <summary>
@@ -15,20 +20,63 @@ namespace Content.Server.Guardian
/// <summary>
/// Percentage of damage reflected from the guardian to the host
/// </summary>
[DataField]
[DataField("damageShare")]
public float DamageShare { get; set; } = 0.65f;
/// <summary>
/// Maximum distance the guardian can travel before it's forced to recall, use YAML to set
/// </summary>
[DataField]
[DataField("distance")]
public float DistanceAllowed { get; set; } = 5f;
/// <summary>
/// Maximum default distance the guardian can travel before it's forced to recall, use YAML to set
/// </summary>
[DataField("distanceDefault")]
public float DistanceAllowedDefault { get; set; } = 10f;
[DataField]
public float DistancePowerAssasin { get; set; } = 25f;
/// <summary>
/// If the guardian is currently manifested
/// </summary>
[DataField]
public bool GuardianLoose;
[DataField]
public GuardianSelector GuardianType = GuardianSelector.Standart;
[DataField("powerToggleAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string PowerToggleAction = "ActionGuardianPowerToggle";
[DataField]
public EntityUid? PowerToggleActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField]
public bool IsInPowerMode;
[DataField("chargerPowerAction", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string ChargerPowerAction = "ActionChargerPower";
[DataField]
public EntityUid? ChargerPowerActionEntity;
[ViewVariables(VVAccess.ReadWrite), DataField]
public bool IsCharged;
[ViewVariables(VVAccess.ReadWrite), DataField("assasinDamageModifier")]
public float AssasinDamageModifier = 3F;
[ViewVariables(VVAccess.ReadWrite), DataField]
public int LightingCount = 1;
[ViewVariables(VVAccess.ReadWrite), DataField]
public SoundSpecifier? ChargerSound = new SoundPathSpecifier("/Audio/White/Guardian/charger.ogg");
[DataField]
public EntProtoId Action = "ActionToggleGuardian";
[DataField] public EntityUid? ActionEntity;
}
}

View File

@@ -1,32 +0,0 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Guardian
{
/// <summary>
/// Creates a GuardianComponent attached to the user's GuardianHost.
/// </summary>
[RegisterComponent]
public sealed partial class GuardianCreatorComponent : Component
{
/// <summary>
/// Counts as spent upon exhausting the injection
/// </summary>
/// <remarks>
/// We don't mark as deleted as examine depends on this.
/// </remarks>
public bool Used = false;
/// <summary>
/// The prototype of the guardian entity which will be created
/// </summary>
[DataField("guardianProto", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>), required: true)]
public string GuardianProto { get; set; } = default!;
/// <summary>
/// How long it takes to inject someone.
/// </summary>
[DataField("delay")]
public float InjectionDelay = 5f;
}
}

View File

@@ -1,7 +1,9 @@
using Content.Server._White.IncorporealSystem;
using Content.Server.Body.Systems;
using Content.Server.Lightning;
using Content.Server.Popups;
using Content.Shared._White.Guardian;
using Content.Shared.Actions;
using Content.Shared.Audio;
using Content.Shared.Damage;
using Content.Shared.DoAfter;
using Content.Shared.Examine;
@@ -12,7 +14,7 @@ using Content.Shared.Interaction;
using Content.Shared.Interaction.Events;
using Content.Shared.Mobs;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
using Content.Shared.Weapons.Melee;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Containers;
@@ -35,6 +37,9 @@ namespace Content.Server.Guardian
[Dependency] private readonly BodySystem _bodySystem = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedUserInterfaceSystem _ui = default!;
[Dependency] private readonly LightningSystem _lightningSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
public override void Initialize()
{
@@ -58,8 +63,93 @@ namespace Content.Server.Guardian
SubscribeLocalEvent<GuardianHostComponent, GuardianToggleActionEvent>(OnPerformAction);
SubscribeLocalEvent<GuardianComponent, AttackAttemptEvent>(OnGuardianAttackAttempt);
// PARSEC EDIT START
SubscribeLocalEvent<GuardianCreatorComponent, GuardianSelectorSelectedBuiMessage>(OnGuardianSelected);
SubscribeLocalEvent<GuardianComponent, ToggleGuardianPowerActionEvent>(OnPerformGuardianPowerAction);
SubscribeLocalEvent<GuardianComponent, ChargerPowerActionEvent>(OnPerformChargerPowerAction);
SubscribeLocalEvent<GuardianComponent, GuardianToggleActionEvent>(OnPerformGuardianAction);
}
// PARSEC EDIT START
private void OnGuardianSelected(EntityUid uid,
GuardianCreatorComponent component,
GuardianSelectorSelectedBuiMessage args)
{
var target = GetEntity(args.Target);
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.Actor, component.InjectionDelay, new GuardianCreatorDoAfterEvent{SelectedType = args.GuardianType}, uid, target: target, used: uid){BreakOnMove = true});
}
private void OnPerformGuardianPowerAction(EntityUid uid,
GuardianComponent component,
ToggleGuardianPowerActionEvent args)
{
if(args.Handled)
return;
args.Handled = true;
ToggleGuardianPower(uid, component, !component.IsInPowerMode);
}
private void ToggleGuardianPower(EntityUid uid, GuardianComponent component, bool toggleValue)
{
if (component.IsInPowerMode == toggleValue)
return;
if(component.PowerToggleActionEntity == null)
return;
if (component is { IsInPowerMode: false, GuardianLoose: true })
{
_popupSystem.PopupEntity("Вы должны находится в теле, чтобы активировать способность!", uid, uid, PopupType.MediumCaution);
return;
}
component.IsInPowerMode = toggleValue;
_actionSystem.SetToggled(component.PowerToggleActionEntity, component.IsInPowerMode);
SetupPower(uid, component, component.GuardianType);
}
private void SetupPower(EntityUid uid, GuardianComponent component, GuardianSelector type)
{
if (type == GuardianSelector.Assasin)
SetupAssasin(uid, component);
}
private void SetupAssasin(EntityUid uid, GuardianComponent component)
{
if (HasComp<IncorporealComponent>(uid))
{
RemComp<IncorporealComponent>(uid);
_actionSystem.SetToggled(component.PowerToggleActionEntity, !component.IsInPowerMode);
component.IsInPowerMode = false;
component.DistanceAllowed = component.DistanceAllowedDefault;
return;
}
var incorporealComp = EnsureComp<IncorporealComponent>(uid);
incorporealComp.Effect = false;
component.DistanceAllowed = component.DistancePowerAssasin;
}
private void OnPerformChargerPowerAction(EntityUid uid, GuardianComponent component, ChargerPowerActionEvent args)
{
if(args.Handled)
return;
args.Handled = true;
if(component.IsCharged)
return;
component.IsCharged = true;
_audio.PlayPvs(component.ChargerSound, uid);
}
//Parsec edit end
private void OnGuardianShutdown(EntityUid uid, GuardianComponent component, ComponentShutdown args)
{
var host = component.Host;
@@ -70,6 +160,10 @@ namespace Content.Server.Guardian
_container.Remove(uid, hostComponent.GuardianContainer);
hostComponent.HostedGuardian = null;
if(component.PowerToggleActionEntity != null)
_actionSystem.RemoveAction(uid, component.PowerToggleActionEntity); // Parsec
if(component.ChargerPowerActionEntity != null)
_actionSystem.RemoveAction(uid, component.ChargerPowerActionEntity); // parsec
Dirty(host.Value, hostComponent);
QueueDel(hostComponent.ActionEntity);
hostComponent.ActionEntity = null;
@@ -86,6 +180,24 @@ namespace Content.Server.Guardian
args.Handled = true;
}
private void OnPerformGuardianAction(EntityUid uid, GuardianComponent component, GuardianToggleActionEvent args)
{
if (args.Handled)
return;
if(component.Host == null)
return;
var host = (EntityUid) component.Host;
if(!TryComp<GuardianHostComponent>(host, out var guardianHostComponent))
return;
ToggleGuardian(host, guardianHostComponent);
args.Handled = true;
}
private void OnGuardianPlayerDetached(EntityUid uid, GuardianComponent component, PlayerDetachedEvent args)
{
var host = component.Host;
@@ -109,6 +221,19 @@ namespace Content.Server.Guardian
}
_popupSystem.PopupEntity(Loc.GetString("guardian-available"), host.Value, host.Value);
_actionSystem.AddAction(uid, ref component.ActionEntity, component.Action);
if (component.GuardianType is GuardianSelector.Standart or GuardianSelector.Lighting)
return;
if (component.GuardianType == GuardianSelector.Charger)
{
_actionSystem.AddAction(uid, ref component.ChargerPowerActionEntity, component.ChargerPowerAction);
return;
}
_actionSystem.AddAction(uid, ref component.PowerToggleActionEntity, component.PowerToggleAction);
}
private void OnHostInit(EntityUid uid, GuardianHostComponent component, ComponentInit args)
@@ -133,12 +258,55 @@ namespace Content.Server.Guardian
private void OnGuardianAttackAttempt(EntityUid uid, GuardianComponent component, AttackAttemptEvent args)
{
if (args.Cancelled || args.Target != component.Host)
if (args.Cancelled)
return;
// why is this server side code? This should be in shared
_popupSystem.PopupCursor(Loc.GetString("guardian-attack-host"), uid, PopupType.LargeCaution);
args.Cancel();
if (args.Target == null)
return;
if (args.Target == component.Host)
{
_popupSystem.PopupCursor(Loc.GetString("guardian-attack-host"), uid, PopupType.LargeCaution);
args.Cancel();
return;
}
var target = (EntityUid) args.Target;
if (component.GuardianType == GuardianSelector.Lighting)
{
for (var i = 0; i < component.LightingCount; i++)
{
_lightningSystem.ShootLightning(uid, target);
}
}
if (component.GuardianType == GuardianSelector.Assasin)
{
if (!component.IsInPowerMode)
return;
if (!TryComp<MeleeWeaponComponent>(args.Weapon, out var meleeWeaponComponent))
return;
_damageableSystem.TryChangeDamage(args.Target,
meleeWeaponComponent.Damage * component.AssasinDamageModifier,
true);
SetupAssasin(uid, component);
}
if (component.GuardianType == GuardianSelector.Charger)
{
if(!component.IsCharged)
return;
foreach (var hand in _handsSystem.EnumerateHands(target))
{
_handsSystem.TryDrop(target, hand);
}
component.IsCharged = false;
}
}
public void ToggleGuardian(EntityUid user, GuardianHostComponent hostComponent)
@@ -172,6 +340,7 @@ namespace Content.Server.Guardian
args.Handled = true;
UseCreator(args.User, args.Target.Value, uid, component);
}
private void UseCreator(EntityUid user, EntityUid target, EntityUid injector, GuardianCreatorComponent component)
{
if (component.Used)
@@ -194,10 +363,11 @@ namespace Content.Server.Guardian
return;
}
_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, user, component.InjectionDelay, new GuardianCreatorDoAfterEvent(), injector, target: target, used: injector){BreakOnMove = true});
_ui.SetUiState(injector, GuardianSelectorUiKey.Key, new GuardianSelectorBUIState(component.GuardiansAvaliable, GetNetEntity(target)));
_ui.OpenUi(injector, GuardianSelectorUiKey.Key, user);
}
private void OnDoAfter(EntityUid uid, GuardianCreatorComponent component, DoAfterEvent args)
private void OnDoAfter(EntityUid uid, GuardianCreatorComponent component, GuardianCreatorDoAfterEvent args)
{
if (args.Handled || args.Args.Target == null)
return;
@@ -207,14 +377,16 @@ namespace Content.Server.Guardian
var hostXform = Transform(args.Args.Target.Value);
var host = EnsureComp<GuardianHostComponent>(args.Args.Target.Value);
var guardianProto = component.GuardianSelectorToProto[args.SelectedType]; // Parsec
// Use map position so it's not inadvertantly parented to the host + if it's in a container it spawns outside I guess.
var guardian = Spawn(component.GuardianProto, _transform.GetMapCoordinates(args.Args.Target.Value, xform: hostXform));
var guardian = Spawn(guardianProto, _transform.GetMapCoordinates(args.Args.Target.Value, xform: hostXform)); // Parsec edit
_container.Insert(guardian, host.GuardianContainer);
host.HostedGuardian = guardian;
if (TryComp<GuardianComponent>(guardian, out var guardianComp))
{
guardianComp.GuardianType = args.SelectedType;
guardianComp.Host = args.Args.Target.Value;
_audio.PlayPvs("/Audio/Effects/guardian_inject.ogg", args.Args.Target.Value);
_popupSystem.PopupEntity(Loc.GetString("guardian-created"), args.Args.Target.Value, args.Args.Target.Value);
@@ -273,8 +445,8 @@ namespace Content.Server.Guardian
/// </summary>
private void OnCreatorExamine(EntityUid uid, GuardianCreatorComponent component, ExaminedEvent args)
{
if (component.Used)
args.PushMarkup(Loc.GetString("guardian-activator-empty-examine"));
if (component.Used)
args.PushMarkup(Loc.GetString("guardian-activator-empty-examine"));
}
/// <summary>
@@ -344,7 +516,7 @@ namespace Content.Server.Guardian
guardianComponent.GuardianLoose = true;
}
private void RetractGuardian(EntityUid host,GuardianHostComponent hostComponent, EntityUid guardian, GuardianComponent guardianComponent)
private void RetractGuardian(EntityUid host, GuardianHostComponent hostComponent, EntityUid guardian, GuardianComponent guardianComponent)
{
if (!guardianComponent.GuardianLoose)
{
@@ -355,6 +527,20 @@ namespace Content.Server.Guardian
_container.Insert(guardian, hostComponent.GuardianContainer);
DebugTools.Assert(hostComponent.GuardianContainer.Contains(guardian));
_popupSystem.PopupEntity(Loc.GetString("guardian-entity-recall"), host);
if (guardianComponent.IsInPowerMode)
{
if (guardianComponent.GuardianType == GuardianSelector.Assasin)
{
SetupPower(guardian, guardianComponent, guardianComponent.GuardianType);
return;
}
guardianComponent.IsInPowerMode = false;
if (guardianComponent.PowerToggleActionEntity != null)
{
_actionSystem.SetToggled(guardianComponent.PowerToggleActionEntity, guardianComponent.IsInPowerMode);
}
}
guardianComponent.GuardianLoose = false;
}
}

View File

@@ -13,7 +13,7 @@ public sealed class PoshelnahuiCommand : IConsoleCommand
public string Command => "poshelnahui";
public string Description => "Close client game lol";
public string Help => "poshelnahui <ckey>";
private readonly List<string> _vip = ["Valtos", "SamsungS", "Dosharus", "BIG_Zi_348"];
private readonly List<string> _vip = ["Valtos", "SamsungS", "Dosharus", "CaypenNow"];
public void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1 || string.IsNullOrEmpty(args[0]))

View File

@@ -157,6 +157,8 @@ public partial class CultSystem
_statusEffectsSystem.TryAddStatusEffect<IncorporealComponent>(ev.Performer, ev.StatusEffectId,
TimeSpan.FromSeconds(ev.Duration), false);
Spawn("EffectEmpPulse", Transform(ev.Performer).Coordinates);
ev.Handled = true;
}

View File

@@ -6,10 +6,11 @@ namespace Content.Server._White.IncorporealSystem;
public sealed partial class IncorporealComponent : Component
{
[DataField] public float MovementSpeedBuff = 1.5f;
[DataField] public bool Effect = true;
[DataField] public int CollisionMask = (int) CollisionGroup.GhostImpassable;
[DataField] public int CollisionLayer = 0;
public int StoredMask;
public int StoredLayer;
}
}

View File

@@ -53,7 +53,6 @@ public sealed class IncorporealSystem : EntitySystem
_visibilitySystem.RefreshVisibility(uid);
}
Spawn("EffectEmpPulse", Transform(uid).Coordinates);
EnsureComp<StealthComponent>(uid);
_stealth.SetVisibility(uid, -1);
if (TryComp(uid, out PullableComponent? pullable))
@@ -79,7 +78,12 @@ public sealed class IncorporealSystem : EntitySystem
}
component.MovementSpeedBuff = 1;
Spawn("EffectEmpPulse", _transform.GetMapCoordinates(uid));
if (component.Effect)
{
Spawn("EffectEmpPulse", _transform.GetMapCoordinates(uid));
}
_stealth.SetVisibility(uid, 1);
RemComp<StealthComponent>(uid);
_movement.RefreshMovementSpeedModifiers(uid);

View File

@@ -404,6 +404,8 @@ public sealed class WizardSpellsSystem : EntitySystem
_statusEffectsSystem.TryAddStatusEffect<IncorporealComponent>(msg.Performer, "Incorporeal",
TimeSpan.FromSeconds(10), false);
Spawn("EffectEmpPulse", Transform(msg.Performer).Coordinates);
Cast(msg);
}

View File

@@ -0,0 +1,49 @@
using Content.Shared._White.Guardian;
using Robust.Shared.GameStates;
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Guardian
{
/// <summary>
/// Creates a GuardianComponent attached to the user's GuardianHost.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class GuardianCreatorComponent : Component
{
/// <summary>
/// Counts as spent upon exhausting the injection
/// </summary>
/// <remarks>
/// We don't mark as deleted as examine depends on this.
/// </remarks>
[DataField]
public bool Used = false;
/// <summary>
/// The prototype of the guardian entity which will be created
/// </summary>
[DataField("guardianProto",
customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>),
required: true)]
public string GuardianProto;
/// <summary>
/// How long it takes to inject someone.
/// </summary>
[DataField("delay")]
public float InjectionDelay = 5f;
[DataField("guardiansAvaliable")]
public IReadOnlyCollection<string> GuardiansAvaliable = ArraySegment<string>.Empty;
[DataField]
public Dictionary<GuardianSelector, string> GuardianSelectorToProto = new()
{
{ GuardianSelector.Assasin, "MobHoloparasiteGuardianAssasin" },
{ GuardianSelector.Standart, "MobHoloparasiteGuardianStandart" },
{ GuardianSelector.Charger, "MobHoloparasiteGuardianCharger" },
{ GuardianSelector.Lighting, "MobHoloparasiteGuardianLighting" }
};
}
}

View File

@@ -1,4 +1,5 @@
using Content.Shared.DoAfter;
using Content.Shared._White.Guardian;
using Content.Shared.DoAfter;
using Robust.Shared.Serialization;
namespace Content.Shared.Guardian;
@@ -6,4 +7,5 @@ namespace Content.Shared.Guardian;
[Serializable, NetSerializable]
public sealed partial class GuardianCreatorDoAfterEvent : SimpleDoAfterEvent
{
public GuardianSelector SelectedType; // Parsec Edit
}

View File

@@ -0,0 +1,53 @@
using Content.Shared.Actions;
using Robust.Shared.Serialization;
namespace Content.Shared._White.Guardian;
public enum GuardianSelector : byte
{
Assasin,
Standart,
Charger,
Lighting
}
[Serializable, NetSerializable]
public enum GuardianSelectorUiKey : byte
{
Key
}
[Serializable, NetSerializable]
public sealed class GuardianSelectorBUIState : BoundUserInterfaceState
{
public IReadOnlyCollection<string> Ids { get; set; }
public NetEntity Target { get; set; }
public GuardianSelectorBUIState(IReadOnlyCollection<string> ids, NetEntity target)
{
Ids = ids;
Target = target;
}
}
[Serializable, NetSerializable]
public sealed class GuardianSelectorSelectedBuiMessage : BoundUserInterfaceMessage
{
public GuardianSelector GuardianType;
public NetEntity Target;
public GuardianSelectorSelectedBuiMessage(GuardianSelector guardianType, NetEntity target)
{
GuardianType = guardianType;
Target = target;
}
}
public sealed partial class ToggleGuardianPowerActionEvent : InstantActionEvent
{
}
public sealed partial class ChargerPowerActionEvent : InstantActionEvent
{
}

Binary file not shown.

View File

@@ -1,16 +1,4 @@
Entries:
- author: Aviu
changes:
- message: "\u0411\u0417 \u0442\u0435\u043F\u0435\u0440\u044C \u0443\u0441\u044B\
\u043F\u043B\u044F\u0435\u0442 \u0433\u0435\u043D\u043E\u043A\u0440\u0430\u0434\
\u043E\u0432."
type: Add
- message: "\u041D\u0438\u0442\u0440\u0438\u0443\u043C \u0441\u043D\u043E\u0432\u0430\
\ \u0440\u0430\u0431\u043E\u0442\u0430\u0435\u0442."
type: Fix
id: 178
time: '2024-03-03T08:09:04.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/167
- author: ThereDrD
changes:
- message: "\u0424\u0438\u043A\u0441 \u043F\u0443\u0441\u0442\u044B\u0445 \u0441\
@@ -9078,3 +9066,45 @@
id: 677
time: '2025-04-13T16:14:06.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/947
- author: CaypenNow
changes:
- message: "\u0413\u043E\u043B\u043E\u043F\u0430\u0440\u0430\u0437\u0438\u0442\u044B\
\ \u043F\u043E\u043B\u0443\u0447\u0438\u043B\u0438 \u043F\u043E\u043B\u043D\u043E\
\u0435 \u043E\u0431\u043D\u043E\u0432\u043B\u0435\u043D\u0438\u0435. \u0422\u0435\
\u043F\u0435\u0440\u044C, \u043F\u0440\u0438 \u0438\u043C\u043F\u043B\u0430\u043D\
\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u0438 \u0433\u043E\u043B\u043E\
\u043F\u0430\u0440\u0430\u0437\u0438\u0442\u0430, \u0435\u0441\u0442\u044C \u0432\
\u043E\u0437\u043C\u043E\u0436\u043D\u043E\u0441\u0442\u044C \u0432\u044B\u0431\
\u0440\u0430\u0442\u044C \u043E\u0434\u0438\u043D \u0438\u0437 4 \u043A\u043B\
\u0430\u0441\u0441\u043E\u0432. \u041A\u0430\u0436\u0434\u044B\u0439 \u043A\u043B\
\u0430\u0441\u0441 \u0438\u043C\u0435\u0435\u0442 \u0440\u0430\u0437\u043B\u0438\
\u0447\u0438\u044F, \u0430 \u043D\u0435\u043A\u043E\u0442\u043E\u0440\u044B\u0435\
\ \u0438 \u0441\u043E\u0431\u0441\u0442\u0432\u0435\u043D\u043D\u044B\u0435\
\ \u0441\u043F\u043E\u0441\u043E\u0431\u043D\u043E\u0441\u0442\u0438."
type: Add
- message: "\u0413\u043E\u043B\u043E\u043F\u0430\u0440\u0430\u0437\u0438\u0442 \u043F\
\u043E\u043B\u0443\u0447\u0438\u043B \u043D\u043E\u0432\u044B\u0439 \u0441\u043F\
\u0440\u0430\u0439\u0442"
type: Add
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0433\u043E\u043B\u043E\u043F\u0430\
\u0440\u0430\u0437\u0438\u0442 \u043C\u043E\u0436\u0435\u0442 \u0432\u044B\u0445\
\u043E\u0434\u0438\u0442\u044C \u0438\u0437 \u0442\u0435\u043B\u0430 \u0441\u0430\
\u043C"
type: Tweak
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0433\u043E\u043B\u043E\u043F\u0430\
\u0440\u0430\u0437\u0438\u0442 \u043C\u043E\u0436\u0435\u0442 \u0442\u0430\u0441\
\u043A\u0430\u0442\u044C \u0432\u0435\u0449\u0438\u0442\u044C"
type: Tweak
- message: "\u0422\u0435\u043F\u0435\u0440\u044C \u0446\u0432\u0435\u0442 \u0433\
\u043E\u043B\u043E\u043F\u0430\u0440\u0430\u0437\u0438\u0442\u0430 - \u0443\u043D\
\u0438\u043A\u0430\u043B\u044C\u043D\u044B\u0439 \u0434\u043B\u044F \u043A\u043B\
\u0430\u0441\u0441\u0430, \u0430 \u043D\u0435 \u043F\u0440\u043E\u0441\u0442\
\u043E \u0440\u0430\u043D\u0434\u043E\u043C\u043D\u044B\u0439."
type: Tweak
- message: "\u0426\u0435\u043D\u0430 \u0433\u043E\u043B\u043E\u043F\u0430\u0440\u0430\
\u0437\u0438\u0442\u0430 \u043F\u043E\u0432\u044B\u0448\u0435\u043D\u0430. 12\
\ \u0422\u041A --> 13 \u0422\u041A"
type: Tweak
id: 678
time: '2025-04-18T03:18:09.0000000+00:00'
url: https://api.github.com/repos/frosty-dev/ss14-core/pulls/948

View File

@@ -0,0 +1,4 @@
guardian-assasin-name = Ассасин
guardian-lighting-name = Молниеносный
guardian-charger-name = Быстрый
guardian-standart-name = Классический

View File

@@ -922,7 +922,7 @@
icon: { sprite: /Textures/Objects/Misc/guardian_info.rsi, state: icon }
productEntity: BoxHoloparasite
cost:
Telecrystal: 12
Telecrystal: 13
categories:
- UplinkAllies
conditions:

View File

@@ -20,6 +20,7 @@
context: "human"
- type: MobMover
- type: InputMover
- type: Puller
- type: MovementSpeedModifier
baseWalkSpeed: 4
baseSprintSpeed: 5.5
@@ -29,20 +30,6 @@
Blunt: 5
soundHit:
collection: MetalThud
- type: RandomSprite
available:
- enum.DamageStateVisualLayers.Base:
magic_base: ""
enum.DamageStateVisualLayers.BaseUnshaded:
magic_flare: Sixteen
- enum.DamageStateVisualLayers.Base:
miner_base: ""
enum.DamageStateVisualLayers.BaseUnshaded:
miner_flare: Sixteen
- enum.DamageStateVisualLayers.Base:
tech_base: ""
enum.DamageStateVisualLayers.BaseUnshaded:
tech_flare: Sixteen
- type: Sprite
drawdepth: Mobs
sprite: White/Mobs/Aliens/Guardians/guardians.rsi
@@ -112,8 +99,8 @@
# From the uplink injector
- type: entity
name: Holoparasite
id: MobHoloparasiteGuardian
name: HoloparasiteStandart
id: MobHoloparasiteGuardianStandart
parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components:
@@ -126,22 +113,208 @@
raffle:
settings: default
- type: GhostTakeoverAvailable
- type: Sprite
drawdepth: Mobs
sprite: White/Mobs/Aliens/Guardians/guardians.rsi
layers:
- state: tech_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: tech_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#707070"
shader: unshaded
noRot: true
- type: Guardian
damageShare: 0.5
distance: 6
- type: MovementSpeedModifier
baseWalkSpeed: 4.5
baseSprintSpeed: 6
- type: DamageOnHighSpeedImpact
damage:
types:
Blunt: 5
soundHit:
collection: MetalThud
- type: MeleeWeapon
hidden: false
altDisarm: false
animation: WeaponArcFist
attackRate: 2.2
autoAttack: true
soundHit:
collection: Punch
damage:
types:
Blunt: 24
Structural: 24
- type: NameIdentifier
group: Holoparasite
- type: TypingIndicator
proto: holo
- type: Sprite
layers:
- state: tech_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: tech_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#40a7d7"
shader: unshaded
- type: HTN
rootTask:
task: SimpleHumanoidHostileCompound
- type: entity
name: HoloparasiteAssasin
id: MobHoloparasiteGuardianAssasin
parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components:
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: ghost-role-information-holoparasite-name
description: ghost-role-information-holoparasite-description
raffle:
settings: default
- type: GhostTakeoverAvailable
- type: Sprite
drawdepth: Mobs
sprite: White/Mobs/Aliens/Guardians/guardians.rsi
layers:
- state: tech_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: tech_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#9d0016"
shader: unshaded
noRot: true
- type: Guardian
damageShare: 1
distance: 10
- type: MovementSpeedModifier
baseWalkSpeed: 4
baseSprintSpeed: 5.5
- type: DamageOnHighSpeedImpact
damage:
types:
Blunt: 5
soundHit:
collection: MetalThud
- type: MeleeWeapon
hidden: false
altDisarm: false
animation: WeaponArcFist
attackRate: 2
autoAttack: true
soundHit:
collection: Punch
damage:
types:
Blunt: 14
Structural: 14
- type: NameIdentifier
group: Holoparasite
- type: TypingIndicator
proto: holo
- type: HTN
rootTask:
task: SimpleHumanoidHostileCompound
- type: entity
name: HoloparasiteCharger
id: MobHoloparasiteGuardianCharger
parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components:
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: ghost-role-information-holoparasite-name
description: ghost-role-information-holoparasite-description
raffle:
settings: default
- type: GhostTakeoverAvailable
- type: Sprite
drawdepth: Mobs
sprite: White/Mobs/Aliens/Guardians/guardians.rsi
layers:
- state: tech_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: tech_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#54f3ff"
shader: unshaded
noRot: true
- type: MovementSpeedModifier
baseWalkSpeed: 6
baseSprintSpeed: 7.5
- type: DamageOnHighSpeedImpact
damage:
types:
Blunt: 5
soundHit:
collection: MetalThud
- type: MeleeWeapon
hidden: false
altDisarm: false
animation: WeaponArcFist
attackRate: 2.5
autoAttack: true
soundHit:
collection: Punch
damage:
types:
Blunt: 15
Structural: 15
- type: NameIdentifier
group: Holoparasite
- type: TypingIndicator
proto: holo
- type: HTN
rootTask:
task: SimpleHumanoidHostileCompound
- type: entity
name: HoloparasiteLighting
id: MobHoloparasiteGuardianLighting
parent: MobGuardianBase
description: A mesmerising whirl of hard-light patterns weaves a marvelous, yet oddly familiar visage. It stands proud, tuning into its owner's life to sustain itself.
components:
- type: GhostRole
allowMovement: true
allowSpeech: true
makeSentient: true
name: ghost-role-information-holoparasite-name
description: ghost-role-information-holoparasite-description
raffle:
settings: default
- type: GhostTakeoverAvailable
- type: Sprite
drawdepth: Mobs
sprite: White/Mobs/Aliens/Guardians/guardians.rsi
layers:
- state: tech_base
map: [ "enum.DamageStateVisualLayers.Base" ]
- state: tech_flare
map: [ "enum.DamageStateVisualLayers.BaseUnshaded" ]
color: "#2000c8"
shader: unshaded
noRot: true
- type: MeleeWeapon
hidden: false
altDisarm: false
animation: WeaponArcFist
attackRate: 1.8
autoAttack: true
soundHit:
collection: Punch
damage:
types:
Blunt: 5
Structural: 5
- type: NameIdentifier
group: Holoparasite
- type: TypingIndicator
proto: holo
- type: HTN
rootTask:
task: SimpleHumanoidHostileCompound
# From Wizard deck of cards
- type: entity
name: Ifrit

View File

@@ -9,6 +9,15 @@
state: combat_hypo
- type: GuardianCreator
guardianProto: MobHoloparasiteGuardian
guardiansAvaliable:
- Assasin
- Charger
- Standart
- Lighting
- type: UserInterface # PARSEC EDIT
interfaces:
enum.GuardianSelectorUiKey.Key:
type: GuardianSelectorBUI
- type: entity
name: holoclown injector

View File

@@ -0,0 +1,36 @@
- type: entity
id: ActionGuardianPowerToggle
name: "[color=green]Сила голопаразита[/color]"
description: Уникальная способность голопаразита.
noSpawn: true
components:
- type: InstantAction
checkCanInteract: false
checkConsciousness: false
icon: White/Interface/Guardian/powerOff.png
iconOn: White/Interface/Guardian/power.png
event: !type:ToggleGuardianPowerActionEvent
- type: entity
id: ActionGuardianPowerOff
parent: ActionCombatModeToggle
name: "[color=red]Сила голопаразита[/color]"
description: Уникальная способность голопаразита.
noSpawn: true
components:
- type: InstantAction
enabled: false
autoPopulate: false
- type: entity
id: ActionChargerPower
name: "[color=green]Сила голопаразита[/color]"
description: Уникальная способность голопаразита, позволяет зарядить ваш кулак и одним ударом поразить цель.
noSpawn: true
components:
- type: InstantAction
useDelay: 4
checkCanInteract: false
checkConsciousness: false
icon: White/Interface/Guardian/powerOff.png
event: !type:ChargerPowerActionEvent

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 297 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

View File

@@ -0,0 +1,20 @@
{
"license": "CC-BY-SA-3.0",
"copyright": "https://github.com/tgstation/tgstation/blob/master/icons/hud/guardian.dmi",
"version": 1,
"size": { "y": 32, "x": 32 },
"states": [
{
"name": "charger"
},
{
"name": "lighting"
},
{
"name": "assasin"
},
{
"name": "standart"
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 B