diff --git a/Content.Client/ADT/SlimeHair/SlimeHairBoundUserInterface.cs b/Content.Client/ADT/SlimeHair/SlimeHairBoundUserInterface.cs new file mode 100644 index 00000000000..b8c3a8fad37 --- /dev/null +++ b/Content.Client/ADT/SlimeHair/SlimeHairBoundUserInterface.cs @@ -0,0 +1,66 @@ +using Content.Shared.Humanoid.Markings; +using Content.Shared.ADT.SlimeHair; +using Robust.Client.GameObjects; +using Robust.Client.UserInterface; + +namespace Content.Client.ADT.SlimeHair; + +public sealed class SlimeHairBoundUserInterface : BoundUserInterface +{ + [ViewVariables] + private SlimeHairWindow? _window; + + public SlimeHairBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + + _window = this.CreateWindow(); + + _window.OnHairSelected += tuple => SelectHair(SlimeHairCategory.Hair, tuple.id, tuple.slot); + _window.OnHairColorChanged += args => ChangeColor(SlimeHairCategory.Hair, args.marking, args.slot); + _window.OnHairSlotAdded += delegate () { AddSlot(SlimeHairCategory.Hair); }; + _window.OnHairSlotRemoved += args => RemoveSlot(SlimeHairCategory.Hair, args); + + _window.OnFacialHairSelected += tuple => SelectHair(SlimeHairCategory.FacialHair, tuple.id, tuple.slot); + _window.OnFacialHairColorChanged += + args => ChangeColor(SlimeHairCategory.FacialHair, args.marking, args.slot); + _window.OnFacialHairSlotAdded += delegate () { AddSlot(SlimeHairCategory.FacialHair); }; + _window.OnFacialHairSlotRemoved += args => RemoveSlot(SlimeHairCategory.FacialHair, args); + } + + private void SelectHair(SlimeHairCategory category, string marking, int slot) + { + SendMessage(new SlimeHairSelectMessage(category, marking, slot)); + } + + private void ChangeColor(SlimeHairCategory category, Marking marking, int slot) + { + SendMessage(new SlimeHairChangeColorMessage(category, new(marking.MarkingColors), slot)); + } + + private void RemoveSlot(SlimeHairCategory category, int slot) + { + SendMessage(new SlimeHairRemoveSlotMessage(category, slot)); + } + + private void AddSlot(SlimeHairCategory category) + { + SendMessage(new SlimeHairAddSlotMessage(category)); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not SlimeHairUiState data || _window == null) + { + return; + } + + _window.UpdateState(data); + } +} diff --git a/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml b/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml new file mode 100644 index 00000000000..580d4509365 --- /dev/null +++ b/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml @@ -0,0 +1,9 @@ + + + + + + diff --git a/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml.cs b/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml.cs new file mode 100644 index 00000000000..4a31fa7639a --- /dev/null +++ b/Content.Client/ADT/SlimeHair/SlimeHairWindow.xaml.cs @@ -0,0 +1,48 @@ +using Content.Shared.Humanoid.Markings; +using Content.Shared.ADT.SlimeHair; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; + +namespace Content.Client.ADT.SlimeHair; + +[GenerateTypedNameReferences] +public sealed partial class SlimeHairWindow : DefaultWindow +{ + public Action<(int slot, string id)>? OnHairSelected; + public Action<(int slot, Marking marking)>? OnHairColorChanged; + public Action? OnHairSlotRemoved; + public Action? OnHairSlotAdded; + + public Action<(int slot, string id)>? OnFacialHairSelected; + public Action<(int slot, Marking marking)>? OnFacialHairColorChanged; + public Action? OnFacialHairSlotRemoved; + public Action? OnFacialHairSlotAdded; + + public SlimeHairWindow() + { + RobustXamlLoader.Load(this); + + HairPicker.OnMarkingSelect += args => OnHairSelected!(args); + HairPicker.OnColorChanged += args => OnHairColorChanged!(args); + HairPicker.OnSlotRemove += args => OnHairSlotRemoved!(args); + HairPicker.OnSlotAdd += delegate { OnHairSlotAdded!(); }; + + FacialHairPicker.OnMarkingSelect += args => OnFacialHairSelected!(args); + FacialHairPicker.OnColorChanged += args => OnFacialHairColorChanged!(args); + FacialHairPicker.OnSlotRemove += args => OnFacialHairSlotRemoved!(args); + FacialHairPicker.OnSlotAdd += delegate { OnFacialHairSlotAdded!(); }; + } + + public void UpdateState(SlimeHairUiState state) + { + HairPicker.UpdateData(state.Hair, state.Species, state.HairSlotTotal); + FacialHairPicker.UpdateData(state.FacialHair, state.Species, state.FacialHairSlotTotal); + + if (!HairPicker.Visible && !FacialHairPicker.Visible) + { + AddChild(new Label { Text = Loc.GetString("magic-mirror-component-activate-user-has-no-hair") }); + } + } +} diff --git a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs index 9cb89e94418..47f98ff728f 100644 --- a/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs +++ b/Content.Client/VendingMachines/UI/VendingMachineMenu.xaml.cs @@ -93,6 +93,7 @@ public void Populate(EntityUid entityUid, List inv }; var vendComp = _entityManager.GetComponent(entityUid); //ADT-Economy //ADT-Economy-End + if (inventory.Count == 0 && VendingContents.Visible) { SearchBar.Visible = false; @@ -123,6 +124,7 @@ public void Populate(EntityUid entityUid, List inv if (!_prototypeManager.TryIndex(entry.ID, out var prototype)) continue; + //ADT-Economy-Start var price = 0; if (!vendComp.AllForFree) @@ -142,12 +144,12 @@ public void Populate(EntityUid entityUid, List inv } var itemName = Identity.Name(dummy, _entityManager); - var itemText = $"{itemName} [{entry.Amount}]"; + var itemText = $" [{price}$] {itemName} [{entry.Amount}]"; //ADT-Economy if (itemText.Length > longestEntry.Length) longestEntry = itemText; - listData.Add(new VendorItemsListData(prototype.ID, itemText, i, price)); + listData.Add(new VendorItemsListData(prototype.ID, itemText, i)); } VendingContents.PopulateList(listData); @@ -163,4 +165,4 @@ private void SetSizeAfterUpdate(int longestEntryLength, int contentCount) } } -public record VendorItemsListData(EntProtoId ItemProtoID, string ItemText, int ItemIndex, int price) : ListData; +public record VendorItemsListData(EntProtoId ItemProtoID, string ItemText, int ItemIndex) : ListData; diff --git a/Content.Server/ADT/SlimeHair/SlimeHairComponent.cs b/Content.Server/ADT/SlimeHair/SlimeHairComponent.cs new file mode 100644 index 00000000000..65b602e7af0 --- /dev/null +++ b/Content.Server/ADT/SlimeHair/SlimeHairComponent.cs @@ -0,0 +1,61 @@ +using Content.Shared.DoAfter; +using Robust.Shared.Prototypes; +using Robust.Shared.Audio; + +namespace Content.Server.ADT.SlimeHair; + +/// +/// Allows humanoids to change their appearance mid-round. +/// +[RegisterComponent] +public sealed partial class SlimeHairComponent : Component +{ + [DataField] + public DoAfterId? DoAfter; + + /// + /// Magic mirror target, used for validating UI messages. + /// + [DataField] + public EntityUid? Target; + + /// + /// doafter time required to add a new slot + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan AddSlotTime = TimeSpan.FromSeconds(2); + + /// + /// doafter time required to remove a existing slot + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan RemoveSlotTime = TimeSpan.FromSeconds(2); + + /// + /// doafter time required to change slot + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan SelectSlotTime = TimeSpan.FromSeconds(2); + + /// + /// doafter time required to recolor slot + /// + [DataField, ViewVariables(VVAccess.ReadWrite)] + public TimeSpan ChangeSlotTime = TimeSpan.FromSeconds(1); + + /// + /// Sound emitted when slots are changed + /// + [DataField] + public SoundSpecifier ChangeHairSound = new SoundPathSpecifier("/Audio/ADT/slime-hair.ogg") + { + Params = AudioParams.Default.WithVolume(-1f), + }; + + [DataField("hairAction")] + public EntProtoId Action = "ActionSlimeHair"; + + [DataField, AutoNetworkedField] + public EntityUid? ActionEntity; + +} diff --git a/Content.Server/ADT/SlimeHair/SlimeHairSystem.Abilities.cs b/Content.Server/ADT/SlimeHair/SlimeHairSystem.Abilities.cs new file mode 100644 index 00000000000..c284a13220c --- /dev/null +++ b/Content.Server/ADT/SlimeHair/SlimeHairSystem.Abilities.cs @@ -0,0 +1,30 @@ +using Content.Shared.ADT.SlimeHair; +using Robust.Shared.Player; + +namespace Content.Server.ADT.SlimeHair; + +/// +/// Allows humanoids to change their appearance mid-round. +/// +public sealed partial class SlimeHairSystem +{ + private void InitializeSlimeAbilities() + { + SubscribeLocalEvent(SlimeHairAction); + } + + private void SlimeHairAction(EntityUid uid, SlimeHairComponent comp, SlimeHairActionEvent args) + { + if (args.Handled) + return; + + if (!TryComp(uid, out var actor)) + return; + + _uiSystem.TryOpenUi(uid, SlimeHairUiKey.Key, actor.Owner); + + UpdateInterface(uid, comp); + + args.Handled = true; + } +} diff --git a/Content.Server/ADT/SlimeHair/SlimeHairSystem.cs b/Content.Server/ADT/SlimeHair/SlimeHairSystem.cs new file mode 100644 index 00000000000..472d0c4e71a --- /dev/null +++ b/Content.Server/ADT/SlimeHair/SlimeHairSystem.cs @@ -0,0 +1,307 @@ +using System.Linq; +using Content.Server.DoAfter; +using Content.Server.Humanoid; +using Content.Shared.UserInterface; +using Content.Shared.DoAfter; +using Content.Shared.Humanoid; +using Content.Shared.Humanoid.Markings; +using Content.Shared.Interaction; +using Content.Shared.ADT.SlimeHair; +using Robust.Server.GameObjects; +using Robust.Shared.Audio.Systems; +using Content.Server.Actions; + +namespace Content.Server.ADT.SlimeHair; + +/// +/// Allows humanoids to change their appearance mid-round. +/// + +// TODO: Исправить проблему с генокрадом +public sealed partial class SlimeHairSystem : EntitySystem +{ + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; + [Dependency] private readonly MarkingManager _markings = default!; + [Dependency] private readonly HumanoidAppearanceSystem _humanoid = default!; + [Dependency] private readonly UserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly ActionsSystem _action = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnOpenUIAttempt); + + Subs.BuiEvents(SlimeHairUiKey.Key, subs => + { + subs.Event(OnUIClosed); + subs.Event(OnSlimeHairSelect); + subs.Event(OnTrySlimeHairChangeColor); + subs.Event(OnTrySlimeHairAddSlot); + subs.Event(OnTrySlimeHairRemoveSlot); + }); + + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(OnShutdown); + + SubscribeLocalEvent(OnSelectSlotDoAfter); + SubscribeLocalEvent(OnChangeColorDoAfter); + SubscribeLocalEvent(OnRemoveSlotDoAfter); + SubscribeLocalEvent(OnAddSlotDoAfter); + + InitializeSlimeAbilities(); + + } + + private void OnOpenUIAttempt(EntityUid uid, SlimeHairComponent mirror, ActivatableUIOpenAttemptEvent args) + { + if (!HasComp(uid)) + args.Cancel(); + } + + private void OnSlimeHairSelect(EntityUid uid, SlimeHairComponent component, SlimeHairSelectMessage message) + { + if (component.Target is not { } target) + return; + + _doAfterSystem.Cancel(component.DoAfter); + component.DoAfter = null; + + var doAfter = new SlimeHairSelectDoAfterEvent() + { + Category = message.Category, + Slot = message.Slot, + Marking = message.Marking, + }; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, component.Owner, component.SelectSlotTime, doAfter, uid, target: target, used: uid) + { + DistanceThreshold = SharedInteractionSystem.InteractionRange, + BreakOnDamage = true, + }, out var doAfterId); + + component.DoAfter = doAfterId; + } + + private void OnSelectSlotDoAfter(EntityUid uid, SlimeHairComponent component, SlimeHairSelectDoAfterEvent args) + { + if (args.Handled || args.Target == null || args.Cancelled) + return; + + if (component.Target != args.Target) + return; + + MarkingCategories category; + + switch (args.Category) + { + case SlimeHairCategory.Hair: + category = MarkingCategories.Hair; + break; + case SlimeHairCategory.FacialHair: + category = MarkingCategories.FacialHair; + break; + default: + return; + } + + _humanoid.SetMarkingId(uid, category, args.Slot, args.Marking); + + UpdateInterface(uid, component); + } + + private void OnTrySlimeHairChangeColor(EntityUid uid, SlimeHairComponent component, SlimeHairChangeColorMessage message) + { + if (component.Target is not { } target) + return; + + _doAfterSystem.Cancel(component.DoAfter); + component.DoAfter = null; + + var doAfter = new SlimeHairChangeColorDoAfterEvent() + { + Category = message.Category, + Slot = message.Slot, + Colors = message.Colors, + }; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, component.Owner, component.ChangeSlotTime, doAfter, uid, target: target, used: uid) + { + BreakOnDamage = true, + }, out var doAfterId); + + component.DoAfter = doAfterId; + } + private void OnChangeColorDoAfter(EntityUid uid, SlimeHairComponent component, SlimeHairChangeColorDoAfterEvent args) + { + if (args.Handled || args.Target == null || args.Cancelled) + return; + + if (component.Target != args.Target) + return; + + MarkingCategories category; + switch (args.Category) + { + case SlimeHairCategory.Hair: + category = MarkingCategories.Hair; + break; + case SlimeHairCategory.FacialHair: + category = MarkingCategories.FacialHair; + break; + default: + return; + } + + _humanoid.SetMarkingColor(uid, category, args.Slot, args.Colors); + + // using this makes the UI feel like total ass + // que + // UpdateInterface(uid, component.Target, message.Session); + } + + private void OnTrySlimeHairRemoveSlot(EntityUid uid, SlimeHairComponent component, SlimeHairRemoveSlotMessage message) + { + if (component.Target is not { } target) + return; + + _doAfterSystem.Cancel(component.DoAfter); + component.DoAfter = null; + + var doAfter = new SlimeHairRemoveSlotDoAfterEvent() + { + Category = message.Category, + Slot = message.Slot, + }; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, component.Owner, component.RemoveSlotTime, doAfter, uid, target: target, used: uid) + { + DistanceThreshold = SharedInteractionSystem.InteractionRange, + BreakOnDamage = true, + }, out var doAfterId); + + component.DoAfter = doAfterId; + } + + private void OnRemoveSlotDoAfter(EntityUid uid, SlimeHairComponent component, SlimeHairRemoveSlotDoAfterEvent args) + { + if (args.Handled || args.Target == null || args.Cancelled) + return; + + if (component.Target != args.Target) + return; + + MarkingCategories category; + + switch (args.Category) + { + case SlimeHairCategory.Hair: + category = MarkingCategories.Hair; + break; + case SlimeHairCategory.FacialHair: + category = MarkingCategories.FacialHair; + break; + default: + return; + } + + _humanoid.RemoveMarking(component.Target.Value, category, args.Slot); + + _audio.PlayPvs(component.ChangeHairSound, uid); + UpdateInterface(uid, component); + } + + private void OnTrySlimeHairAddSlot(EntityUid uid, SlimeHairComponent component, SlimeHairAddSlotMessage message) + { + if (component.Target == null) + return; + + if (message.Actor == null) + return; + + _doAfterSystem.Cancel(component.DoAfter); + component.DoAfter = null; + + var doAfter = new SlimeHairAddSlotDoAfterEvent() + { + Category = message.Category, + }; + + _doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, message.Actor, component.AddSlotTime, doAfter, uid, target: component.Target.Value, used: uid) + { + BreakOnDamage = true, + }, out var doAfterId); + + component.DoAfter = doAfterId; + _audio.PlayPvs(component.ChangeHairSound, uid); + } + private void OnAddSlotDoAfter(EntityUid uid, SlimeHairComponent component, SlimeHairAddSlotDoAfterEvent args) + { + if (args.Handled || args.Target == null || args.Cancelled || !TryComp(component.Target, out HumanoidAppearanceComponent? humanoid)) + return; + + MarkingCategories category; + + switch (args.Category) + { + case SlimeHairCategory.Hair: + category = MarkingCategories.Hair; + break; + case SlimeHairCategory.FacialHair: + category = MarkingCategories.FacialHair; + break; + default: + return; + } + + var marking = _markings.MarkingsByCategoryAndSpecies(category, humanoid.Species).Keys.FirstOrDefault(); + + if (string.IsNullOrEmpty(marking)) + return; + + _audio.PlayPvs(component.ChangeHairSound, uid); + _humanoid.AddMarking(uid, marking, Color.Black); + + UpdateInterface(uid, component); + + } + + private void UpdateInterface(EntityUid uid, SlimeHairComponent component) + { + if (!TryComp(uid, out var humanoid)) + return; + + var hair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.Hair, out var hairMarkings) + ? new List(hairMarkings) + : new(); + + var facialHair = humanoid.MarkingSet.TryGetCategory(MarkingCategories.FacialHair, out var facialHairMarkings) + ? new List(facialHairMarkings) + : new(); + + var state = new SlimeHairUiState( + humanoid.Species, + hair, + humanoid.MarkingSet.PointsLeft(MarkingCategories.Hair) + hair.Count, + facialHair, + humanoid.MarkingSet.PointsLeft(MarkingCategories.FacialHair) + facialHair.Count); + + component.Target = uid; + _uiSystem.SetUiState(uid, SlimeHairUiKey.Key, state); + } + + private void OnUIClosed(Entity ent, ref BoundUIClosedEvent args) + { + ent.Comp.Target = null; + } + + private void OnMapInit(EntityUid uid, SlimeHairComponent component, MapInitEvent args) + { + _action.AddAction(uid, ref component.ActionEntity, component.Action); + } + private void OnShutdown(EntityUid uid, SlimeHairComponent component, ComponentShutdown args) + { + _action.RemoveAction(uid, component.ActionEntity); + } + +} diff --git a/Content.Shared/ADT/SlimeHair/SharedSlimeHairSystem.cs b/Content.Shared/ADT/SlimeHair/SharedSlimeHairSystem.cs new file mode 100644 index 00000000000..cb06ae60152 --- /dev/null +++ b/Content.Shared/ADT/SlimeHair/SharedSlimeHairSystem.cs @@ -0,0 +1,147 @@ +using Content.Shared.DoAfter; +using Content.Shared.Humanoid.Markings; +using Robust.Shared.Serialization; +using Content.Shared.Actions; + +namespace Content.Shared.ADT.SlimeHair; + +[Serializable, NetSerializable] +public enum SlimeHairUiKey : byte +{ + Key +} + +[Serializable, NetSerializable] +public enum SlimeHairCategory : byte +{ + Hair, + FacialHair +} + +[Serializable, NetSerializable] +public sealed class SlimeHairSelectMessage : BoundUserInterfaceMessage +{ + public SlimeHairSelectMessage(SlimeHairCategory category, string marking, int slot) + { + Category = category; + Marking = marking; + Slot = slot; + } + + public SlimeHairCategory Category { get; } + public string Marking { get; } + public int Slot { get; } +} + +[Serializable, NetSerializable] +public sealed class SlimeHairChangeColorMessage : BoundUserInterfaceMessage +{ + public SlimeHairChangeColorMessage(SlimeHairCategory category, List colors, int slot) + { + Category = category; + Colors = colors; + Slot = slot; + } + + public SlimeHairCategory Category { get; } + public List Colors { get; } + public int Slot { get; } +} + +[Serializable, NetSerializable] +public sealed class SlimeHairRemoveSlotMessage : BoundUserInterfaceMessage +{ + public SlimeHairRemoveSlotMessage(SlimeHairCategory category, int slot) + { + Category = category; + Slot = slot; + } + + public SlimeHairCategory Category { get; } + public int Slot { get; } +} + +[Serializable, NetSerializable] +public sealed class SlimeHairSelectSlotMessage : BoundUserInterfaceMessage +{ + public SlimeHairSelectSlotMessage(SlimeHairCategory category, int slot) + { + Category = category; + Slot = slot; + } + + public SlimeHairCategory Category { get; } + public int Slot { get; } +} + +[Serializable, NetSerializable] +public sealed class SlimeHairAddSlotMessage : BoundUserInterfaceMessage +{ + public SlimeHairAddSlotMessage(SlimeHairCategory category) + { + Category = category; + } + + public SlimeHairCategory Category { get; } +} + +[Serializable, NetSerializable] +public sealed class SlimeHairUiState : BoundUserInterfaceState +{ + public SlimeHairUiState(string species, List hair, int hairSlotTotal, List facialHair, int facialHairSlotTotal) + { + Species = species; + Hair = hair; + HairSlotTotal = hairSlotTotal; + FacialHair = facialHair; + FacialHairSlotTotal = facialHairSlotTotal; + } + + public NetEntity Target; + + public string Species; + + public List Hair; + public int HairSlotTotal; + + public List FacialHair; + public int FacialHairSlotTotal; +} + +[Serializable, NetSerializable] +public sealed partial class SlimeHairRemoveSlotDoAfterEvent : DoAfterEvent +{ + public override DoAfterEvent Clone() => this; + public SlimeHairCategory Category; + public int Slot; +} + +[Serializable, NetSerializable] +public sealed partial class SlimeHairAddSlotDoAfterEvent : DoAfterEvent +{ + public override DoAfterEvent Clone() => this; + public SlimeHairCategory Category; +} + +[Serializable, NetSerializable] +public sealed partial class SlimeHairSelectDoAfterEvent : DoAfterEvent +{ + public SlimeHairCategory Category; + public int Slot; + public string Marking = string.Empty; + + public override DoAfterEvent Clone() => this; +} + +[Serializable, NetSerializable] +public sealed partial class SlimeHairChangeColorDoAfterEvent : DoAfterEvent +{ + public override DoAfterEvent Clone() => this; + public SlimeHairCategory Category; + public int Slot; + public List Colors = new List(); +} + +public sealed partial class SlimeHairActionEvent : InstantActionEvent +{ +} diff --git a/Resources/Audio/ADT/Ambience/Objects/minecraft_cat.ogg b/Resources/Audio/ADT/Ambience/Objects/minecraft_cat.ogg new file mode 100644 index 00000000000..340015e3f2d Binary files /dev/null and b/Resources/Audio/ADT/Ambience/Objects/minecraft_cat.ogg differ diff --git a/Resources/Audio/ADT/slime-hair.ogg b/Resources/Audio/ADT/slime-hair.ogg new file mode 100644 index 00000000000..b41cbf52bc3 Binary files /dev/null and b/Resources/Audio/ADT/slime-hair.ogg differ diff --git a/Resources/Locale/ru-RU/ADT/Interaction/interaction-popup-component.ftl b/Resources/Locale/ru-RU/ADT/Interaction/interaction-popup-component.ftl new file mode 100644 index 00000000000..b54a707585b --- /dev/null +++ b/Resources/Locale/ru-RU/ADT/Interaction/interaction-popup-component.ftl @@ -0,0 +1,2 @@ +petting-success-kerfus = Вы гладите { $target } по { POSS-ADJ($target) } гладкому кошкообразному монитору. +petting-failure-kerfus = Вы тянетесь погладить { $target }, но { $target } занимается кошачьими делами! diff --git a/Resources/Locale/ru-RU/ADT/Objects/Specific/Robotics/borg_parts.ftl b/Resources/Locale/ru-RU/ADT/Objects/Specific/Robotics/borg_parts.ftl index 7a467d6060d..bae1aa3ff3d 100644 --- a/Resources/Locale/ru-RU/ADT/Objects/Specific/Robotics/borg_parts.ftl +++ b/Resources/Locale/ru-RU/ADT/Objects/Specific/Robotics/borg_parts.ftl @@ -4,3 +4,5 @@ ent-ADTLeftLegBorgSecurity = левая нога киборга-офицера ent-ADTRightLegBorgSecurity = правая нога киборга-офицера ent-ADTHeadBorgSecurity = голова киборга-офицера ent-ADTTorsoBorgSecurity = туловище киборга-офицера +ent-ADTTorsoBorgKerfusNT = голова керфуса +ent-ADTHeadBorgKerfusNT = туловище керфуса diff --git a/Resources/Locale/ru-RU/ADT/actions/slime.ftl b/Resources/Locale/ru-RU/ADT/actions/slime.ftl new file mode 100644 index 00000000000..92b00f9e553 --- /dev/null +++ b/Resources/Locale/ru-RU/ADT/actions/slime.ftl @@ -0,0 +1,4 @@ +slime-hair-window-title = Сменить причёску + +action-slime-hair = Сменить причёску +action-slime-hair-desc = Никаких парикмахерских! Выберите свою причёску на сегодня! diff --git a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Mobs/Cyborgs/borg.ftl b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Mobs/Cyborgs/borg.ftl index d1c87079791..cf2c691a786 100644 --- a/Resources/Locale/ru-RU/ADT/prototypes/Entities/Mobs/Cyborgs/borg.ftl +++ b/Resources/Locale/ru-RU/ADT/prototypes/Entities/Mobs/Cyborgs/borg.ftl @@ -1,2 +1,4 @@ ent-ADTBorgChassisSec = киборг-офицер .desc = Прочный, сильный, быстрый, сме... справедливоносный. +ent-ADTBorgChassisKerfusNT = керфус + .desc = Великий разнорабочий станции. Ему найдется применение везде, где только можно. diff --git a/Resources/Prototypes/ADT/Actions/slime.yml b/Resources/Prototypes/ADT/Actions/slime.yml new file mode 100644 index 00000000000..a7bcc00e5af --- /dev/null +++ b/Resources/Prototypes/ADT/Actions/slime.yml @@ -0,0 +1,13 @@ +- type: entity + id: ActionSlimeHair + name: action-slime-hair + description: action-slime-hair-desc + noSpawn: true + components: + - type: InstantAction + icon: + sprite: ADT/Interface/Actions/actions_slime.rsi + state: hair + itemIconStyle: BigAction + event: !type:SlimeHairActionEvent + useDelay: 1 diff --git a/Resources/Prototypes/ADT/Entities/Mobs/Cyborgs/borg_chassis.yml b/Resources/Prototypes/ADT/Entities/Mobs/Cyborgs/borg_chassis.yml index 02bde298aa5..1518700ad5e 100644 --- a/Resources/Prototypes/ADT/Entities/Mobs/Cyborgs/borg_chassis.yml +++ b/Resources/Prototypes/ADT/Entities/Mobs/Cyborgs/borg_chassis.yml @@ -72,3 +72,70 @@ startingItem: PowerCellMedium - type: RandomMetadata nameSegments: [names_borg] + +- type: entity + id: ADTBorgChassisKerfusNT + parent: BaseBorgChassisNT + name: kerfus cyborg + components: + - type: Sprite + sprite: ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi + layers: + - state: kerfusNT + - state: kerfus_e_r + map: ["enum.BorgVisualLayers.Light"] + shader: unshaded + visible: false + - state: kerfusNT_l + shader: unshaded + map: ["light"] + visible: false + - type: BorgChassis + maxModules: 4 + moduleWhitelist: + tags: + - BorgModuleGeneric + - BorgModuleService + - BorgModuleJanitor + hasMindState: kerfusNT_e + noMindState: kerfus_e_r + - type: BorgTransponder + sprite: + sprite: ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi + state: kerfus + name: kerfus cyborg + - type: Construction + node: kerfusNT + - type: IntrinsicRadioTransmitter + channels: + - Binary + - Common + - Command + - Engineering + - Medical + - Science + - Security + - Service + - Supply + - type: ActiveRadio + channels: + - Binary + - Common + - Command + - Engineering + - Medical + - Science + - Security + - Service + - Supply + - type: AccessReader + access: [["Command"], ["Research"]] + - type: Inventory + templateId: borgTall + - type: InteractionPopup + interactSuccessString: petting-success-kerfus + interactFailureString: petting-failure-kerfus + interactSuccessSound: + path: /Audio/ADT/Ambience/Objects/minecraft_cat.ogg + - type: RandomMetadata + nameSegments: [names_borg] diff --git a/Resources/Prototypes/ADT/Entities/Objects/Specific/Robotics/borg_parts.yml b/Resources/Prototypes/ADT/Entities/Objects/Specific/Robotics/borg_parts.yml index c129d76790b..3f1080a6f4c 100644 --- a/Resources/Prototypes/ADT/Entities/Objects/Specific/Robotics/borg_parts.yml +++ b/Resources/Prototypes/ADT/Entities/Objects/Specific/Robotics/borg_parts.yml @@ -92,3 +92,34 @@ tags: - Trash - ADTBorgSecurityTorso + +- type: entity + id: ADTHeadBorgKerfusNT + parent: BaseBorgHead + name: kerfus cyborg head + components: + - type: Sprite + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: kerfusNT_head + - type: Icon + state: kerfusNT_head + - type: Tag + tags: + - Trash + - BorgHead + - ADTBorgKerfusNTHead + +- type: entity + id: ADTTorsoBorgKerfusNT + parent: BaseBorgTorso + name: kerfus cyborg torso + components: + - type: Icon + state: kerfusNT_torso + - type: Sprite + sprite: Objects/Specific/Robotics/cyborg_parts.rsi + state: kerfusNT_torso + - type: Tag + tags: + - Trash + - ADTBorgKerfusNTTorso diff --git a/Resources/Prototypes/ADT/Recipes/Lathes/robotics.yml b/Resources/Prototypes/ADT/Recipes/Lathes/robotics.yml index 97aee0c25c5..a3013a20c95 100644 --- a/Resources/Prototypes/ADT/Recipes/Lathes/robotics.yml +++ b/Resources/Prototypes/ADT/Recipes/Lathes/robotics.yml @@ -84,3 +84,21 @@ Steel: 500 Glass: 500 Plastic: 250 + +- type: latheRecipe + id: ADTHeadBorgKerfusNT + result: ADTHeadBorgKerfusNT + category: Robotics + completetime: 2 + materials: + Steel: 1500 + Glass: 1250 + +- type: latheRecipe + id: ADTTorsoBorgKerfusNT + result: ADTTorsoBorgKerfusNT + category: Robotics + completetime: 2 + materials: + Steel: 1750 + Glass: 1500 diff --git a/Resources/Prototypes/ADT/tags.yml b/Resources/Prototypes/ADT/tags.yml index a562fad0332..c14c04735be 100644 --- a/Resources/Prototypes/ADT/tags.yml +++ b/Resources/Prototypes/ADT/tags.yml @@ -63,3 +63,9 @@ - type: Tag id: ADTBorgSecurityTorso + +- type: Tag + id: ADTBorgKerfusNTTorso + +- type: Tag + id: ADTBorgKerfusNTHead diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 2bac6c700f3..ea42bf77bc8 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -34,6 +34,10 @@ type: HumanoidMarkingModifierBoundUserInterface enum.StrippingUiKey.Key: type: StrippableBoundUserInterface + # ADT-SlimeHair-Start + enum.SlimeHairUiKey.Key: + type: SlimeHairBoundUserInterface + # ADT-SlimeHair-End # to prevent bag open/honk spam - type: UseDelay delay: 0.5 @@ -125,6 +129,7 @@ understands: - GalacticCommon - Bubblish + - type: SlimeHair # ADT-SlimeHair - type: entity parent: MobHumanDummy diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml index fb57a08b25b..3fff3a1db1b 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/endoskeleton.yml @@ -192,6 +192,16 @@ tags: - ADTBorgSecurityHead #ADT secborg end + # ADT-Kerfus-Start + kerfusNT_torso+o: + whitelist: + tags: + - ADTBorgKerfusNTTorso + kerfusNT_head+o: + whitelist: + tags: + - ADTBorgKerfusNTHead + # ADT-Kerfus-End - type: ContainerContainer containers: part-container: !type:Container @@ -246,6 +256,11 @@ - ADTBorgSecurityRLeg - ADTBorgSecurityTorso #ADT secborg end + # ADT-Kerfus-Start + kerfusNT: + - ADTTorsoBorgKerfusNT + - ADTHeadBorgKerfusNT + # ADT-Kerfus-End - type: Construction graph: Cyborg node: start diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index d2934f996a5..a28d963b964 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -680,6 +680,10 @@ - ADTHeadBorgSecurity - ADTTorsoBorgSecurity #ADT secborg end + # ADT-Kerfus-Start + - ADTTorsoBorgKerfusNT + - ADTHeadBorgKerfusNT + # ADT-Kerfus-End dynamicRecipes: - ProximitySensor - BorgModuleLightReplacer diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml index 04b9c368380..f322a4ad324 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/machines/cyborg.yml @@ -179,6 +179,35 @@ - tool: Screwing doAfter: 0.5 + # ADT-Kerfus-Start + - to: kerfusNT + steps: + - assemblyId: kerfusNT + guideString: borg-construction-guide-string + + - material: Cable + amount: 1 + doAfter: 1 + store: part-container + + - component: Flash + name: flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - component: Flash + name: second flash + store: part-container + icon: + sprite: Objects/Weapons/Melee/flash.rsi + state: flash + + - tool: Screwing + doAfter: 1 + # ADT-Kerfus-End + #ADT secborg start - to: security steps: @@ -237,3 +266,8 @@ - node: syndicatesaboteur entity: BorgChassisSyndicateSaboteur + + # ADT-Kerfus-Start + - node: kerfusNT + entity: ADTBorgChassisKerfusNT + # ADT-Kerfus-End diff --git a/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/hair.png b/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/hair.png new file mode 100644 index 00000000000..4d7fc4b043c Binary files /dev/null and b/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/hair.png differ diff --git a/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/meta.json b/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/meta.json new file mode 100644 index 00000000000..c13eaf76f66 --- /dev/null +++ b/Resources/Textures/ADT/Interface/Actions/actions_slime.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Taken from https://github.com/tgstation/tgstation/blob/05ec94e46349c35e29ca91e5e97d0c88ae26ad44/icons/mob/species/human/human_face.dmi, resprited by Alekshhh, adapted for action by _kote", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "hair" + } + ] +} diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus.png new file mode 100644 index 00000000000..5606787e929 Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT.png new file mode 100644 index 00000000000..947504f98c4 Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_e.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_e.png new file mode 100644 index 00000000000..fe93ea025bc Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_e.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_l.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_l.png new file mode 100644 index 00000000000..d81e7735c02 Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfusNT_l.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e.png new file mode 100644 index 00000000000..436ab0f0718 Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e_r.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e_r.png new file mode 100644 index 00000000000..b4da08b064e Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_e_r.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_l.png b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_l.png new file mode 100644 index 00000000000..b78dc822b06 Binary files /dev/null and b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/kerfus_l.png differ diff --git a/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/meta.json b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/meta.json new file mode 100644 index 00000000000..67880bc70ca --- /dev/null +++ b/Resources/Textures/ADT/Mobs/Silicon/Borgs/cyborg_kerfus.rsi/meta.json @@ -0,0 +1,207 @@ +{ + "version": 1, + "license": "CC-BY-3.0", + "copyright": "White Dream", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "kerfus_e_r", + "directions": 4 + }, + { + "name": "kerfusNT", + "directions": 4 + }, + { + "name": "kerfusNT_e", + "directions": 4, + "delays": [ + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ] + ] + }, + { + "name": "kerfusNT_l", + "directions": 4, + "delays": [ + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ], + [ + 60, + 0.5, + 2, + 60, + 0.5, + 2, + 0.5, + 60, + 2, + 2, + 60, + 2 + ] + ] + }, + { + "name": "kerfus", + "directions": 4 + }, + { + "name": "kerfus_e", + "directions": 4, + "delays": [ + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ] + ] + }, + { + "name": "kerfus_l", + "directions": 4, + "delays": [ + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ], + [ + 60, + 2, + 60, + 2 + ] + ] + } + ] +} diff --git a/Resources/Textures/ADT/Structures/Machines/atm.rsi/atm.png b/Resources/Textures/ADT/Structures/Machines/atm.rsi/atm.png index 7b0bf33511b..9c533a116fe 100644 Binary files a/Resources/Textures/ADT/Structures/Machines/atm.rsi/atm.png and b/Resources/Textures/ADT/Structures/Machines/atm.rsi/atm.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head+o.png new file mode 100644 index 00000000000..694b84caa7e Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head.png new file mode 100644 index 00000000000..7d2d2d1c951 Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_head.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso+o.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso+o.png new file mode 100644 index 00000000000..b0e4c65613b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso+o.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso.png b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso.png new file mode 100644 index 00000000000..660c2be7a9b Binary files /dev/null and b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/kerfusNT_torso.png differ diff --git a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json index 16f24bd30c2..f65b87fa3d2 100644 --- a/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json +++ b/Resources/Textures/Objects/Specific/Robotics/cyborg_parts.rsi/meta.json @@ -214,6 +214,18 @@ { "name": "service_r_leg+o" }, + { + "name": "kerfusNT_head" + }, + { + "name": "kerfusNT_head+o" + }, + { + "name": "kerfusNT_torso" + }, + { + "name": "kerfusNT_torso+o" + }, { "name": "security_l_arm" },