From 60b9bff44b2275ab6a340f4e74acb9864ef54379 Mon Sep 17 00:00:00 2001 From: Kill_Me_I_Noobs <118206719+Vonsant@users.noreply.github.com> Date: Sat, 21 Dec 2024 01:19:17 +0300 Subject: [PATCH 1/7] Cringe fridge try 2(#177) * SmartCringe * JustCringe --- .../SmartFridgeBoundUserInterface.cs | 55 +++++ .../Medical/SmartFridge/SmartFridgeSystem.cs | 24 +++ .../SmartFridge/UI/SmartFridgeMenu.xaml | 39 ++++ .../SmartFridge/UI/SmartFridgeMenu.xaml.cs | 86 ++++++++ .../Medical/SmartFridge/SmartFridgeSystem.cs | 203 ++++++++++++++++++ .../SmartFridge/SharedSmartFridgeSystem.cs | 58 +++++ .../SmartFridge/SmartFridgeComponent.cs | 96 +++++++++ .../ru-RU/_corvaxnext/reagents/biological.ftl | 2 + .../_corvaxnext/smartfridge/smartfridge.ftl | 2 + .../Structures/Machines/smartfridge.yml | 63 ++++-- 10 files changed, 610 insertions(+), 18 deletions(-) create mode 100644 Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeBoundUserInterface.cs create mode 100644 Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs create mode 100644 Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml create mode 100644 Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml.cs create mode 100644 Content.Server/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs create mode 100644 Content.Shared/_CorvaxNext/Medical/SmartFridge/SharedSmartFridgeSystem.cs create mode 100644 Content.Shared/_CorvaxNext/Medical/SmartFridge/SmartFridgeComponent.cs create mode 100644 Resources/Locale/ru-RU/_corvaxnext/reagents/biological.ftl create mode 100644 Resources/Locale/ru-RU/_corvaxnext/smartfridge/smartfridge.ftl diff --git a/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeBoundUserInterface.cs b/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeBoundUserInterface.cs new file mode 100644 index 00000000000..fa75dec559f --- /dev/null +++ b/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeBoundUserInterface.cs @@ -0,0 +1,55 @@ +using Content.Client.UserInterface.Controls; +using Robust.Client.UserInterface; +using Robust.Shared.Input; +using System.Linq; +using Content.Shared._CorvaxNext.Medical.SmartFridge; +using SmartFridgeMenu = Content.Client._CorvaxNext.Medical.SmartFridge.UI.SmartFridgeMenu; + +namespace Content.Client._CorvaxNext.Medical.SmartFridge; + +public sealed class SmartFridgeBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + [ViewVariables] + private SmartFridgeMenu? _menu; + + [ViewVariables] + private List _cachedInventory = []; + + protected override void Open() + { + base.Open(); + + _menu = this.CreateWindow(); + _menu.OpenCenteredLeft(); + _menu.Title = EntMan.GetComponent(Owner).EntityName; + _menu.OnItemSelected += OnItemSelected; + Refresh(); + } + + public void Refresh() + { + var system = EntMan.System(); + _cachedInventory = system.GetInventoryClient(Owner); + + _menu?.Populate(_cachedInventory); + } + + private void OnItemSelected(GUIBoundKeyEventArgs args, ListData data) + { + if (args.Function != EngineKeyFunctions.UIClick) + return; + + if (data is not VendorItemsListData { ItemIndex: var itemIndex }) + return; + + if (_cachedInventory.Count == 0) + return; + + var selectedItem = _cachedInventory.ElementAtOrDefault(itemIndex); + + if (selectedItem == null) + return; + + SendMessage(new SmartFridgeEjectMessage(selectedItem.StorageSlotId)); + } +} diff --git a/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs b/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs new file mode 100644 index 00000000000..d5296c1b763 --- /dev/null +++ b/Content.Client/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared._CorvaxNext.Medical.SmartFridge; +using SmartFridgeComponent = Content.Shared._CorvaxNext.Medical.SmartFridge.SmartFridgeComponent; + +namespace Content.Client._CorvaxNext.Medical.SmartFridge; + +public sealed class SmartFridgeSystem : SharedSmartFridgeSystem +{ + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnVendingAfterState); + } + + private void OnVendingAfterState(EntityUid uid, SmartFridgeComponent component, ref AfterAutoHandleStateEvent args) + { + if (_uiSystem.TryGetOpenUi(uid, SmartFridgeUiKey.Key, out var bui)) + { + bui.Refresh(); + } + } +} diff --git a/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml b/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml new file mode 100644 index 00000000000..0fd1f95f2f6 --- /dev/null +++ b/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml.cs b/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml.cs new file mode 100644 index 00000000000..fd0963c7a0b --- /dev/null +++ b/Content.Client/_CorvaxNext/Medical/SmartFridge/UI/SmartFridgeMenu.xaml.cs @@ -0,0 +1,86 @@ +using System.Numerics; +using Content.Client.UserInterface.Controls; +using Content.Client.VendingMachines.UI; +using Content.Shared._CorvaxNext.Medical.SmartFridge; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.Controls; +using Robust.Client.UserInterface.XAML; +using Robust.Client.UserInterface; +using Robust.Shared.Prototypes; + +namespace Content.Client._CorvaxNext.Medical.SmartFridge.UI; + +[GenerateTypedNameReferences] +public sealed partial class SmartFridgeMenu : FancyWindow +{ + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + + private readonly Dictionary _dummies = []; + + public event Action? OnItemSelected; + + public SmartFridgeMenu() + { + RobustXamlLoader.Load(this); + IoCManager.InjectDependencies(this); + + VendingContents.SearchBar = SearchBar; + VendingContents.DataFilterCondition += DataFilterCondition; + VendingContents.GenerateItem += GenerateButton; + VendingContents.ItemKeyBindDown += (args, data) => OnItemSelected?.Invoke(args, data); + } + + private static bool DataFilterCondition(string filter, ListData data) + { + if (data is not VendorItemsListData { ItemText: var text }) + return false; + + return string.IsNullOrEmpty(filter) || text.Contains(filter, StringComparison.CurrentCultureIgnoreCase); + } + + private void GenerateButton(ListData data, ListContainerButton button) + { + if (data is not VendorItemsListData { ItemProtoID: var protoId, ItemText: var text }) + return; + + button.AddChild(new VendingMachineItem(protoId, text)); + button.ToolTip = text; + } + + public void Populate(List inventory) + { + if (inventory.Count == 0) + { + SearchBar.Visible = false; + VendingContents.Visible = false; + OutOfStockLabel.Visible = true; + return; + } + + SearchBar.Visible = true; + VendingContents.Visible = true; + OutOfStockLabel.Visible = false; + + var listData = new List(); + + for (var i = 0; i < inventory.Count; i++) + { + var entry = inventory[i]; + + if (!_prototypeManager.TryIndex(entry.Id, out var prototype)) + continue; + + if (!_dummies.TryGetValue(entry.Id, out var dummy)) + { + dummy = _entityManager.Spawn(entry.Id); + _dummies.Add(entry.Id, dummy); + } + + var itemText = $"{entry.ItemName} [{entry.Quantity}]"; + listData.Add(new VendorItemsListData(prototype.ID, itemText, i)); + } + + VendingContents.PopulateList(listData); + } +} diff --git a/Content.Server/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs b/Content.Server/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs new file mode 100644 index 00000000000..4d772d9c449 --- /dev/null +++ b/Content.Server/_CorvaxNext/Medical/SmartFridge/SmartFridgeSystem.cs @@ -0,0 +1,203 @@ +using Content.Server.Interaction; +using Content.Server.Power.EntitySystems; +using Content.Shared._CorvaxNext.Medical.SmartFridge; +using Content.Shared.Construction.EntitySystems; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Interaction; +using Content.Shared.Tag; +using Robust.Server.Audio; +using Robust.Shared.Containers; +using Robust.Shared.Prototypes; +using Content.Server.Labels; +using Content.Shared.Chemistry.Components.SolutionManager; +using Content.Server.Chemistry.Containers.EntitySystems; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Reagent; +using Content.Shared.Verbs; + +namespace Content.Server._CorvaxNext.Medical.SmartFridge; + +public sealed class SmartFridgeSystem : SharedSmartFridgeSystem +{ + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + [Dependency] private readonly AnchorableSystem _anchorable = default!; + [Dependency] private readonly IEntityManager _entityManager = default!; + [Dependency] private readonly TagSystem _tags = default!; + [Dependency] private readonly AudioSystem _audio = default!; + + [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly LabelSystem _label = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + + public override void Initialize() + { + base.Initialize(); + + Subs.BuiEvents(SmartFridgeUiKey.Key, + subs => + { + subs.Event(OnSmartFridgeEjectMessage); + }); + + SubscribeLocalEvent(MapInit, before: [typeof(ItemSlotsSystem)]); + SubscribeLocalEvent(OnItemEjectEvent); + SubscribeLocalEvent(OnInteractEvent); + } + + private void OnInteractEvent(EntityUid entity, SmartFridgeComponent component, ref InteractUsingEvent ev) + { + if (_tags.HasTag(ev.Used, "Wrench")) + { + _anchorable.TryToggleAnchor(entity, ev.User, ev.Used); + ev.Handled = true; + } + + if (!_anchorable.IsPowered(entity, _entityManager)) + { + ev.Handled = true; + return; + } + + if (component.StorageWhitelist != null) + { + if (!_tags.HasAnyTag(ev.Used, component.StorageWhitelist.Tags!.ToArray())) + { + ev.Handled = true; + return; + } + } + + if (!_itemSlotsSystem.TryInsertEmpty(ev.Target, ev.Used, ev.User, true)) + return; + + if (_solutionContainerSystem.TryGetDrainableSolution(ev.Used, out _, out var sol)) + { + ReagentId? reagentId = sol.GetPrimaryReagentId(); + if (reagentId is not null && _prototypeManager.TryIndex(reagentId.Value.Prototype, out var reagent)) + { + var reagentQuantity = sol.GetReagentQuantity(reagentId.Value); + var totalQuantity = sol.Volume; + + if (reagentQuantity == totalQuantity) + _label.Label(ev.Used, reagent.LocalizedName); + else + { + _label.Label(ev.Used, Loc.GetString("reagent-dispenser-component-impure-auto-label", + ("reagent", reagent.LocalizedName), + ("purity", 100.0f * reagentQuantity / totalQuantity))); + } + } + } + + component.Inventory = GetInventory(entity); + Dirty(entity, component); + + ev.Handled = true; + } + + private void OnItemEjectEvent(EntityUid entity, SmartFridgeComponent component, ref ItemSlotEjectAttemptEvent ev) + { + if (component.SlotToEjectFrom == ev.Slot) + { + Dirty(entity, component); + return; + } + + ev.Cancelled = !component.Ejecting; + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + while (query.MoveNext(out var uid, out var comp)) + { + if (!comp.Ejecting) + continue; + + comp.EjectAccumulator += frameTime; + if (!(comp.EjectAccumulator >= comp.EjectDelay)) + continue; + + comp.EjectAccumulator = 0f; + comp.Ejecting = false; + + EjectItem(uid, comp); + } + } + + private void MapInit(EntityUid uid, SmartFridgeComponent component, MapInitEvent _) + { + SetupSmartFridge(uid, component); + } + + private void OnSmartFridgeEjectMessage(EntityUid uid, SmartFridgeComponent component, SmartFridgeEjectMessage args) + { + if (!this.IsPowered(uid, EntityManager)) + return; + + if (args.Actor is not { Valid: true } entity || Deleted(entity)) + return; + + VendFromSlot(uid, args.Id); + Dirty(uid, component); + } + + private void VendFromSlot(EntityUid uid, string itemSlotToEject, SmartFridgeComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + if (!this.IsPowered(uid, EntityManager)) + { + return; + } + + var item = _itemSlotsSystem.GetItemOrNull(uid, itemSlotToEject); + + if (item == null) + return; + + if (!_itemSlotsSystem.TryGetSlot(uid, itemSlotToEject, out var itemSlot) && itemSlot == null) + return; + + component.Ejecting = true; + component.SlotToEjectFrom = itemSlot; + + _audio.PlayPvs(component.SoundVend, uid); + } + + private void EjectItem(EntityUid uid, SmartFridgeComponent component) + { + if (component.SlotToEjectFrom == null || + !_itemSlotsSystem.TryEject(uid, component.SlotToEjectFrom, null, out _)) + return; + + component.Inventory = GetInventory(uid); + component.SlotToEjectFrom = null; + + Dirty(uid, component); + } + + private void SetupSmartFridge(EntityUid uid, SmartFridgeComponent component) + { + for (var i = 0; i < component.NumSlots; i++) + { + var storageSlotId = SmartFridgeComponent.BaseStorageSlotId + i; + ItemSlot storageComponent = new() + { + Whitelist = component.StorageWhitelist, + Swap = false, + EjectOnBreak = true, + }; + + component.StorageSlotIds.Add(storageSlotId); + component.StorageSlots.Add(storageComponent); + component.StorageSlots[i].Name = "Storage Slot " + (i+1); + _itemSlotsSystem.AddItemSlot(uid, component.StorageSlotIds[i], component.StorageSlots[i]); + } + + _itemSlotsSystem.AddItemSlot(uid, "itemSlot", component.FridgeSlots); + } +} diff --git a/Content.Shared/_CorvaxNext/Medical/SmartFridge/SharedSmartFridgeSystem.cs b/Content.Shared/_CorvaxNext/Medical/SmartFridge/SharedSmartFridgeSystem.cs new file mode 100644 index 00000000000..4e3c1d3b944 --- /dev/null +++ b/Content.Shared/_CorvaxNext/Medical/SmartFridge/SharedSmartFridgeSystem.cs @@ -0,0 +1,58 @@ +using System.Linq; +using Content.Shared.Containers.ItemSlots; +using Content.Shared.Labels.Components; + +namespace Content.Shared._CorvaxNext.Medical.SmartFridge; + +public abstract class SharedSmartFridgeSystem : EntitySystem +{ + [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; + + public List GetInventoryClient(EntityUid uid, + SmartFridgeComponent? smartFridgeComponent = null) + { + if (!Resolve(uid, ref smartFridgeComponent)) + return []; + + var inventory = new List(); + + inventory.AddRange(smartFridgeComponent.Inventory); + + return inventory; + } + + protected List GetInventory(EntityUid uid, SmartFridgeComponent? smartFridgeComponent = null) + { + if (!Resolve(uid, ref smartFridgeComponent)) + return []; + + var repeatedItems = new Dictionary(); + for (var i = 0; i < smartFridgeComponent.NumSlots; i++) + { + var storageSlotId = SmartFridgeComponent.BaseStorageSlotId + i; + + var storedItem = _itemSlotsSystem.GetItemOrNull(uid, storageSlotId); + + if (storedItem == null) + continue; + + string itemLabel; + if (TryComp(storedItem, out var label) && !string.IsNullOrEmpty(label.CurrentLabel)) + itemLabel = label.CurrentLabel; + else + itemLabel = Name(storedItem.Value); + + if (repeatedItems.TryGetValue(itemLabel, out var item)) + { + item.Quantity += 1; + continue; + } + + var meta = MetaData(storedItem.Value); + + repeatedItems.Add(itemLabel, new SmartFridgeInventoryItem(meta.EntityPrototype!, storageSlotId, itemLabel, 1)); + } + + return repeatedItems.Values.ToList(); + } +} diff --git a/Content.Shared/_CorvaxNext/Medical/SmartFridge/SmartFridgeComponent.cs b/Content.Shared/_CorvaxNext/Medical/SmartFridge/SmartFridgeComponent.cs new file mode 100644 index 00000000000..f9440e3e25e --- /dev/null +++ b/Content.Shared/_CorvaxNext/Medical/SmartFridge/SmartFridgeComponent.cs @@ -0,0 +1,96 @@ +using Content.Shared.Containers.ItemSlots; +using Content.Shared.FixedPoint; +using Content.Shared.Whitelist; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; +using Component = Robust.Shared.GameObjects.Component; + +namespace Content.Shared._CorvaxNext.Medical.SmartFridge; + +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState(true)] +public sealed partial class SmartFridgeComponent : Component +{ + /// + /// max slots in the SmartFridge, means they can store n items + /// + [DataField("numStorageSlots")] + public int NumSlots = 100; + + [DataField, AutoNetworkedField] + public ItemSlot FridgeSlots = new(); + + [DataField] + public List StorageSlotIds = []; + + [DataField] + public List StorageSlots = []; + + /// + /// latest available inventory + /// + [DataField, AutoNetworkedField] + public List Inventory = []; + + /// + /// Prefix for automatically-generated slot name for storage, up to NumSlots. + /// + public static readonly string BaseStorageSlotId = "SmartFridge-storageSlot"; + + /// + /// what types of things can people store in here + /// pill bottles, bottles, and food + /// + [DataField] + public EntityWhitelist? StorageWhitelist; + + /// + /// How long should the SmartFridge take to dispense something. In Seconds. + /// + [DataField] + public float EjectDelay = 1.2f; + + /// + /// If the SmartFridge is currently vending anything. + /// + [DataField] + public bool Ejecting; + + [DataField] + public float EjectAccumulator; + public ItemSlot? SlotToEjectFrom; + + [DataField] + // Grabbed from: https://github.com/tgstation/tgstation/blob/d34047a5ae911735e35cd44a210953c9563caa22/sound/machines/machine_vend.ogg + public SoundSpecifier SoundVend = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg") + { + Params = new AudioParams + { + Volume = -4f, + Variation = 0.15f, + }, + }; +} + +[Serializable, NetSerializable] +public sealed class SmartFridgeInventoryItem(EntProtoId id, string storageSlotId, string itemName, FixedPoint2 quantity) +{ + public EntProtoId Id = id; + public string StorageSlotId = storageSlotId; + public string ItemName = itemName; + public FixedPoint2 Quantity = quantity; +} + +[Serializable, NetSerializable] +public enum SmartFridgeUiKey +{ + Key +} + +// doing it here cuz idgaf +[Serializable, NetSerializable] +public sealed class SmartFridgeEjectMessage(string id) : BoundUserInterfaceMessage +{ + public readonly string Id = id; +} diff --git a/Resources/Locale/ru-RU/_corvaxnext/reagents/biological.ftl b/Resources/Locale/ru-RU/_corvaxnext/reagents/biological.ftl new file mode 100644 index 00000000000..87c05a76ab2 --- /dev/null +++ b/Resources/Locale/ru-RU/_corvaxnext/reagents/biological.ftl @@ -0,0 +1,2 @@ +reagent-name-resomi-blood = Фиолетовая кровь +reagent-desc-resomi-blood = Густая жидкость с резким аммиачным запахом diff --git a/Resources/Locale/ru-RU/_corvaxnext/smartfridge/smartfridge.ftl b/Resources/Locale/ru-RU/_corvaxnext/smartfridge/smartfridge.ftl new file mode 100644 index 00000000000..e6406bd2e36 --- /dev/null +++ b/Resources/Locale/ru-RU/_corvaxnext/smartfridge/smartfridge.ftl @@ -0,0 +1,2 @@ +smart-fridge-title = умный холодильник +smart-fridge-search = умный фильтр diff --git a/Resources/Prototypes/Entities/Structures/Machines/smartfridge.yml b/Resources/Prototypes/Entities/Structures/Machines/smartfridge.yml index 5a8eb69b41e..20aaf657dda 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/smartfridge.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/smartfridge.yml @@ -4,6 +4,24 @@ name: SmartFridge description: A refrigerated storage unit for keeping items cold and fresh. components: + # Corvax-Next-SmartFridge-Start + - type: SmartFridge + storageWhitelist: + tags: + - PillCanister + - Meat + - Cooked + - ChemDispensable + - Bottle + - Syringe + - type: ActivatableUI + key: enum.SmartFridgeUiKey.Key + - type: ActivatableUIRequiresPower + - type: UserInterface + interfaces: + enum.SmartFridgeUiKey.Key: + type: SmartFridgeBoundUserInterface + # Corvax-Next-SmartFridge-End - type: StationAiWhitelist - type: Advertise pack: SmartFridgeAds @@ -18,6 +36,13 @@ - state: smartfridge_door map: ["enum.StorageVisualLayers.Door"] shader: unshaded + # Corvax-Next-SmartFridge-Start + - type: ApcPowerReceiver + powerLoad: 100 + - type: ExtensionCableReceiver + - type: LightningTarget + priority: 1 + # Corvax-Next-SmartFridge-End - type: EntityStorageVisuals stateBaseClosed: smartfridge stateDoorOpen: smartfridge_open @@ -26,28 +51,30 @@ radius: 1.5 energy: 1.6 color: "#9dc5c9" - - type: EntityStorage - isCollidableWhenOpen: true - closeSound: - path: /Audio/Machines/windoor_open.ogg - params: - volume: -3 - openSound: - path: /Audio/Machines/windoor_open.ogg - params: - volume: -3 - - type: ContainerContainer - containers: - entity_storage: !type:Container - - type: UseDelay - delay: 1 - - type: AntiRottingContainer - - type: ResistLocker + # Corvax-Next-SmartFridge-Start + # - type: EntityStorage + # isCollidableWhenOpen: true + # closeSound: + # path: /Audio/Machines/windoor_open.ogg + # params: + # volume: -3 + # openSound: + # path: /Audio/Machines/windoor_open.ogg + # params: + # volume: -3 + # - type: ContainerContainer + # containers: + # entity_storage: !type:Container + # - type: UseDelay + # delay: 1 + # - type: AntiRottingContainer + # - type: ResistLocker + # Corvax-Next-SmartFridge-End - type: Physics bodyType: Static - type: Transform noRot: true - anchored: True + anchored: true - type: Fixtures fixtures: fix1: From 9b480136173e49384e0ee0d2705a922b02d97d70 Mon Sep 17 00:00:00 2001 From: PuroSlavKing <103608145+PuroSlavKing@users.noreply.github.com> Date: Fri, 13 Dec 2024 02:59:50 +0300 Subject: [PATCH 2/7] [Port] Flags (#156) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Флаги разных государств --- .../entities/structures/wallmounts/flags.ftl | 32 ++++ .../Markers/Spawners/Random/posters.yml | 28 +++- .../Entities/Structures/Wallmounts/flags.yml | 157 ++++++++++++++++++ .../Structures/Wallmounts/flags.rsi/agurk.png | Bin 0 -> 707 bytes .../Structures/Wallmounts/flags.rsi/coder.png | Bin 0 -> 626 bytes .../Wallmounts/flags.rsi/goldring.png | Bin 0 -> 639 bytes .../Structures/Wallmounts/flags.rsi/hca.png | Bin 0 -> 570 bytes .../Structures/Wallmounts/flags.rsi/inteq.png | Bin 0 -> 915 bytes .../Wallmounts/flags.rsi/lizard.png | Bin 0 -> 1355 bytes .../Structures/Wallmounts/flags.rsi/meta.json | 59 +++++++ .../Wallmounts/flags.rsi/mothic.png | Bin 0 -> 638 bytes .../Structures/Wallmounts/flags.rsi/nri.png | Bin 0 -> 705 bytes .../Structures/Wallmounts/flags.rsi/nt.png | Bin 0 -> 690 bytes .../Structures/Wallmounts/flags.rsi/shigu.png | Bin 0 -> 735 bytes .../Wallmounts/flags.rsi/solfed.png | Bin 0 -> 712 bytes .../Wallmounts/flags.rsi/solfed_alt.png | Bin 0 -> 672 bytes .../Wallmounts/flags.rsi/soviet.png | Bin 0 -> 868 bytes .../Wallmounts/flags.rsi/syndicate.png | Bin 0 -> 692 bytes .../Wallmounts/flags.rsi/tizira.png | Bin 0 -> 742 bytes .../Structures/Wallmounts/flags.rsi/vulp.png | Bin 0 -> 924 bytes Resources/migration.yml | 4 + 21 files changed, 278 insertions(+), 2 deletions(-) create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/wallmounts/flags.ftl create mode 100644 Resources/Prototypes/_CorvaxNext/Entities/Structures/Wallmounts/flags.yml create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/agurk.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/coder.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/goldring.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/hca.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/inteq.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/lizard.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/meta.json create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/mothic.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/nri.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/nt.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/shigu.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/solfed.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/solfed_alt.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/soviet.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/syndicate.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/tizira.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/vulp.png diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/wallmounts/flags.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/wallmounts/flags.ftl new file mode 100644 index 00000000000..f5aa44cadc8 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/wallmounts/flags.ftl @@ -0,0 +1,32 @@ +ent-FlagAgurk = флаг Королевства Агуркррал + .desc = Флаг королевства Агуркррал. +ent-FlagCoder = флаг кодеров + .desc = Флаг клуба задротов. Великолепно. +ent-FlagHCA = флаг Ассоциации Человеческого содружества + .desc = Официальный флаг политической партии «Человеческое Содружество», который гордо развевается над душой каждого человека. Если у вас есть этот флаг, то вы, скорее всего, принадлежите к этой ассоциации. +ent-FlagInteQ = флаг ЧВК Интек + .desc = Коричнево-оранжевый флаг со щитом в центре. Флаг пахнет кровью. +ent-FlagLizard = флаг ящеров + .desc = Сила в ящерах, ребят. +ent-FlagMothic = флаг Великого Кочевого Флота + .desc = Флаг Великого Флота молей. Классический военно-морской флаг, пришедший на смену старому национальному флагу, который можно увидеть в его кантоне. +ent-FlagNanoTrasen = флаг НТ + .desc = Официальный корпоративный флаг НТ. В основном используется в церемониальных целях, или для обозначения земель на новой границе. +ent-FlagShigu = флаг империи Сигу + .desc = Флаг империи Шигу. Империя Шигу когда-то была всего лишь объединением северных племён, которые боялись и почитали дикую природу. Однако с приходом генерала Рейна Шигу стала крупнейшей империей, которая захватила огромное количество земель, но вернула часть из них, когда её сын взошёл на престол. +ent-FlagSolfed = флаг Солнечной Федерации + .desc = Флаг Солнечной Федерации. Это символ человечества, где бы он не был и как бы он не использовался, надейтесь что это так. +ent-FlagSolfedAlt = { ent-FlagSolfed } + .desc = { ent-FlagSolfed.desc } +ent-FlagSyndicate = флаг Синдиката + .desc = Флаг Сотранского Синдиката. Ранее использовавшийся сотранцами как способ заявить о несогласии с Нанотрасеном, теперь он стал межгалактическим символом того же, но с более извращённой целью, поскольку к восстанию присоединились новые заинтересованные группы ради собственной выгоды. +ent-FlagTizira = флаг Республики Северная Тизира + .desc = Флаг великой Республики Северной Тизиры. В зависимости от того, кого вы спросите вы получите ответ, что он символизирует силу, или то, что вы лишь - муравей в улье. +ent-FlagGoldRing = флаг Золотого Кольца + .desc = Флаг Золотого Кольца. Золотое Кольцо — это мегаполис в южной части Тала, который разросся до размеров полноценного государства. Расположенное вокруг солёного озера КолВилус, Золотое Кольцо стало оазисом в бескрайней пустыне, где процветают торговля и культура. +ent-FlagNRI = флаг Новой Российской Империи + .desc = Флаг Новой Российской Империи. Жёлтый, чёрный и белый цвета символизируют её суверенитет, духовность и чистоту. +ent-FlagSovietUSSP = флаг Союза Советских Социалистических Планет + .desc = Флаг некогда великого Государства. От флага ощущается сильный запах... железа. Или крови? Из чего сделана эта красная краска? +ent-FlagVulp = флаг Вульп + .desc = Флаг воинов с планеты Далти'Шар. Гордый народ со своим чувством долга и чести, что через кровавую историю проложил путь к миру и космосу. diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml index cab5b9a1cc7..dcc0b89dd6e 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/posters.yml @@ -93,7 +93,14 @@ - PosterContrabandInterdyne - PosterContrabandWaffleCorp - PosterContrabandMissingSpacepen - chance: 1 + chance: 0.99 # Сorvax-Next-EDIT + # Сorvax-Next-START + rarePrototypes: + - FlagCoder + - FlagSyndicate + - FlagInteQ + rareChance: 0.01 + # Сorvax-Next-END - type: entity parent: MarkerBase @@ -170,4 +177,21 @@ - PosterLegitSafetyMothHardhat - PosterLegitSafetyMothSSD - PosterLegitOppenhopper - chance: 1 + chance: 0.99 # Сorvax-Next-EDIT + # Сorvax-Next-START + rarePrototypes: + - FlagAgurk + - FlagHCA + - FlagLizard + - FlagMothic + - FlagNanoTrasen + - FlagShigu + - FlagSolfed + - FlagSolfedAlt + - FlagTizira + - FlagGoldRing + - FlagNRI + - FlagSovietUSSP + - FlagVulp + rareChance: 0.01 + # Сorvax-Next-END diff --git a/Resources/Prototypes/_CorvaxNext/Entities/Structures/Wallmounts/flags.yml b/Resources/Prototypes/_CorvaxNext/Entities/Structures/Wallmounts/flags.yml new file mode 100644 index 00000000000..79f2451d9e8 --- /dev/null +++ b/Resources/Prototypes/_CorvaxNext/Entities/Structures/Wallmounts/flags.yml @@ -0,0 +1,157 @@ +- type: entity + parent: BaseFlag + id: FlagAgurk + name: Kingdom of Agurkrral flag + description: The flag of the Kingdom of Agurkrral. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: agurk + +- type: entity + parent: BaseFlag + id: FlagCoder + name: coder flag + description: The flag of the nerd club. Great. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: coder + +- type: entity + parent: BaseFlag + id: FlagHCA + name: Human Commonwealth Association flag + description: The official flag of the political party Human Commonwealth, which proudly flies over the soul of every human. If you have this flag, then you most likely belong to this association. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: hca + +- type: entity + parent: BaseFlag + id: FlagInteQ + name: PMC InteQ flag + description: Brown-Orange flag with a shield in the center. The flag smells of blood. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: inteq + +- type: entity + parent: BaseFlag + id: FlagLizard + name: lizard flag + description: Lizard power, guys. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: lizard + +- type: entity + parent: BaseFlag + id: FlagMothic + name: Grand Nomad Fleet flag + description: The flag of the Mothic Grand Nomad Fleet. A classic naval ensign, its use has superceded the old national flag which can be seen in its canton. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: mothic + +- type: entity + parent: BaseFlag + id: FlagNanoTrasen + name: Nanotrasen flag + description: The official corporate flag of Nanotrasen. Mostly flown as a ceremonial piece, or to mark land on a new frontier. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: nt + +- type: entity + parent: BaseFlag + id: FlagShigu + name: Shigu Empire flag + description: This is the flag of the Shigu Empire. The Shigu Empire was once just an association of northern tribes that feared and revered wildlife. However, with the arrival of General Rain, Shigu became the largest empire, which seized a huge amount of land, but returned some of it when her son ascended to the throne. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: shigu + +- type: entity + parent: BaseFlag + id: FlagSolfed + name: Sol Federation flag + description: The flag of Sol Federation. Its a symbol of humanity no matter where they go, or how much they wish it wasnt. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: solfed + +- type: entity + parent: FlagSolfed + id: FlagSolfedAlt + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: solfed_alt + +- type: entity + parent: BaseFlag + id: FlagSyndicate + name: Syndicate flag + description: The flag of the Sothran Syndicate. Previously used by the Sothran people as a way of declaring opposition against the Nanotrasen, now it became an intergalactic symbol of the same, yet way more skewed purpose, as more groups of interest have joined the rebellions side for their own gain. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: syndicate + +- type: entity + parent: BaseFlag + id: FlagTizira + name: Republic of Northern Moghes flag + description: The flag of the Great Republic of Northern Moghes. Depending on who you ask, it represents strength or being an ant in the hive. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: tizira + +- type: entity + parent: BaseFlag + id: FlagGoldRing + name: Gold Ring flag + description: This is the flag of the Gold Ring. The Golden Ring is a megalopolis in the southern part of Tal that has grown to the size of a full-fledged state. Being located around the ColVilous salt Lake, the Golden Ring has become an oasis in the boundless desert where trade and culture flourish. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: goldring + +- type: entity + parent: BaseFlag + id: FlagNRI + name: Novaya Rossiyskaya Imperiya flag + description: The flag of the Novaya Rossiyskaya Imperiya. The yellow, black and white colours represent its sovereignity, spirituality and pureness. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: nri + +- type: entity + parent: BaseFlag + id: FlagSovietUSSP + name: флаг Союза Советских Социалистических Планет + description: Флаг некогда великого Государства. От флага ощущается сильный запах... железа. Или крови? Из чего сделана эта красная краска? + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: soviet + +- type: entity + parent: BaseFlag + id: FlagVulp + name: Vulpkanins flag + description: Flag of humanoid dog-like organisms from the Vazzend system. Now they live on the Kellun and Dalhstadt, although their original homeland was known as Altam. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Wallmounts/flags.rsi + state: vulp diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/agurk.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/agurk.png new file mode 100644 index 0000000000000000000000000000000000000000..324224de170dcb33063408286cfe9d7a2756855b GIT binary patch literal 707 zcmV;!0zCbRP)DGH@{0}~T8Hi}3@Q!%~ozIU$YzIX5Z5hbDd zHut{w?z!LZoO9os>7swE4nPOs`~VD(KdAHk{maD*XJKaZFe)%|Z#4EwHE+Nrd(&#|)u91>4~7s- zp%=3&{yby&#Q~mu_N?h#fkAXBQCxmq}24Yj0xfMWyU<8cXAOs5M8Z0WA9oE?l pTLN$au^8+$bbkI7cL4rdfWHw=X5GFTSsnlY002ovPDHLkV1h9zIb#3- literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/coder.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/coder.png new file mode 100644 index 0000000000000000000000000000000000000000..71dc056efa011d9b1ed89a182bf0bbab963bbbb5 GIT binary patch literal 626 zcmV-&0*(ENP)Q=4RVQWPYw{eaDb)E z1yUv#NV&z{1$Ws32@w$~MJOye_H1G1^RfN0vk9W%RlxJiJikBo3>FdLkPrv0eiU#N z0H6Z^o_9@B_5c7cFOSx~i@NTR03gd&g#amg$n)RmbcS2x+uNUS6T^7{z_vYnd^`cT zghfQ~{SgV_sEhMFK_TKD>`a!utuJg9&$94k9noQc=3F*U5v zNgd^GjiSjG@B1Sx77tr|7>+B2g>aOsy0e^L9f7WlVX{pJ^PF54q4WGZ_d~&TcfSYP zIK$uq`mwQVrkevw!-$pX%A4-IuHgVE+nX8^Luk>{%*!GlRN?UIUCY+sivkyM6j&QnAb7LEG+#15!+Z+< zG*(s?A-$cog@1wsixE4~Q5r4`lClcjo1YnE+k}1o3Zqy^c;%4yhcOac1YJKsOHBt~5#}f8moanvgEd`ID@lLd`Oq~EX zu+{9Yx4+%7_34L;&A5A`(_${s8RPEw^4NV21z>aT0&m1-738cZ(^eo>@6$0TtS0~x z0U~UD81={%{uSV?_+ALL=c*KaTMNud`PMlHj-oGsCgnf_#K3~N%;{e85CGkJjJ=M7 z{1toMaAH*D$RjRw@CbD4hdpnZM}miRGUO5_`c}=7XRq&t2f;FRLj#l!H)&%%OG}IM z&im8zE7d=mw7s=Tv7J++xo*9NXk8vq`RbNZvk70`%Jl;s9iRIFSZs)m7&!qa@|3T- z@Ag=@D|F{`-FfWS=I}6ffW@1cPzE3ocVZZE9@89q)M|AzfHnjOQ){sH*##AIyS{fR zj?t|*DF7f_kE%HlCv=h)inNm7a}2d<)Skb$*Xy0u%Yp!-K|=o%asVLEI-DOHvzeA- z-l-hLlod>92kcuJxv69=1cFpUK)QM-4x{Lzgpppx4)Et8D?rtph&9MXMAe9g@L9_X z7ytlS>15yOL?)98fb?o73&$jn45qSjBY*~h0EA=!(~{Nkv&nEEfPO%u)ENCLo&fw; ZfFE~OC<*w7%A_P)`6pHRCt{2mcL2^K@i4QjZa`2vA3{EC4%?>Vw!6shHFLc9quK9 z*d`Ivriu6fB8b&B7WQJJPvBp^!|#~eo!z^rAr=`3m)z~l_uHA-n>5UvZ2_kt1HdQKy~fnd}94>n%DQ|(!COZ zCJq}62BxvRHZ{Q8$BW|!jl-k14&V!*i9bHwn~n7?b9pr~2m2k>pMDxhCBXH~q;R4b z4WhJGtL5DQ{SXK7_IBIOnp(#&ilQPIu34590ul(Bkn7-AWnKVDdw2U#f}kKkkO5f& zDGom^UL}B5v+;QBK%{A!-7bw z>~xfWG()JXuw(}z@XqfU?XZNq&SbEY)nG1gtjjWWPT@Y}#M0hom?0Ok(x1r$V2Tw-cdVE_OC07*qo IM6N<$f^lU4(*OVf literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/inteq.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/inteq.png new file mode 100644 index 0000000000000000000000000000000000000000..9e186d013e7296ce24ec2de85ea533f4eaa1accc GIT binary patch literal 915 zcmV;E18n?>P)?wFL8D%Cu%C>1?Ly2Kt$o+g>iymE%@$>9LMTvIgl{lEWQ}G4&B>*4etl zI+Grt@yRq3x^ndtzyJ8*C&!2ql1r2XR!;@M5{{@^DuC?fc8#m_R_-7e^xcNCc0vN{nu;6reRapCKj@OcazsO&UH~|J;ENam_x6%6fr_P{BdC0AILX^F zH&^EdLl2OLgclq>BPpj5Q3!JUDpfUK70Y)UitW`KE(xci76Xj?ECUEFwVM>a0*)M_ z?4h=yR|Oanp(3b26jbJ$HbCc;WN>@!;VaVNpa*9(s7o!ref);_w75wVlf!%-z{<7~%k~+7Eh%dcQ)Nfa zvem7IOW@v{^?-B1;O44pBw$F29}XDo@%+j*WiOuKIXnX#b7BH6c>s=!Y$_YJZ_>Pb zlWGd!V__(P8;_WTDxb)Tgs&ExbbmU;Rl@*|cjK8MDwL}%wKShp4RCx1teX1rX#}7G z#@BbpT9>mOM|`NPnZ$_!0GnMP-`h}nXks+b{_yBDspRiTpqHbUkQ}9Q8$P%;;3{|! p;GqZ{MCd5(_4cB@6%n002ovPDHLkV1hnKs4M^g literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/lizard.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/lizard.png new file mode 100644 index 0000000000000000000000000000000000000000..a3185bcfc476ebc77e3770e6b20d2a57de136325 GIT binary patch literal 1355 zcmV-R1+@B!P)-K7 z6gp?g970?6AS?5s?#nM<^TX&q48YPg8Zak z!gX|%E9!{wUx?NE%ko;I2fI4KPmw2L*+*-UHnAdF?Ps}scD7^JqhY<7Ua)ukjO6O=SfkgkHs zu`FUD!_vr0q>smdzl?D+ ziqLVu#>f#1Vj@FsvlmObh#1vqk2fGDGEgHSNETA9vgs#AHK>sg6B*q%s$nS?$!+!` zCNhK~?{n=y?~Lrs+yw&@lynvI%f3;p-S>^xVoopupWSoG5GVi^-nGXY5OY(jW9`1r zlE@m4AOGCQ(4zsA6a<`*%(CdRZw7=fw2k(7gDLpK{q6Ave4%Zo{#e@}KcBq;$*dk* znJPe-Rs(sr75jyxv3`p#`-p8-2p*~iAeBoY<^(d?oN1_n;y(YJ9Q^NutdLFOH4 zrf=guq`DCOCp!Vy9ot}lzq#oxly6if{Sv}L39ZpH6F~6l$>@8hpTMY}z?VL*i#mkY z;y|hkQL(-Wxm3n$agZ7tHG-tZM)6u4$fYtB>zk13LU=6>0G4f9&KKdM1YYygH_&g= zG69HD4U$>rFPLvsbRg<89Gxx~a;c1#(NjF!a?0o{w6#nMxm3pKav6Q`a8%zNQ4Jb^ zWL9ra2S``h5ThENQ>847tb!6N)J(0H%jQJOq;T1sc#bu*iY-AMVit237vqwr*3l(YJ`B!N28c z`ocdz{{y4IX9ZBcSD{=4hlOk5??`XSl3r|Q`-(HfyE=>+4tL&Wm$cRxw@X^faOZ7f z_VKO`wy!utda)hl)-Kn(Rn zjAAQ$awQe(n;7be7~*Zg^Q2e15bk6^#t?tLWeu0ZJ^HfD0Ql#I3YJu(EKyPFeJNF+Z-qnHE;$WyJf?O)&5bO-~MDSW1#Jf7!x&N@9%`ug{Y$LGJPucEj zlm#kRo4zt>0SJY!VnGlH?C|9a*+?(8L%E3Zx=L`5eipbj_~hK52ws7`hd!c6lz5Py zz^mTmy^c@G-^t@V|>yRL#FQQslHfxF!E`#AC82ej5#BDx)hf9XLd3M!1}^j((7;OtJC zYQS_9Ur|Q{)Dh!uuceIiD$O)NbUPq7Zam+tnMaW*ak2LV-iO3NKU?&>KRFeDnwgg1 zQTFi1#@pFf@K_x@u`Xot8NPU$(d;;eAzlhAL1F@mf@m-%Y7{rJ`cHC}v$H$vdPBkd&CYIS=Ks!RcM_*xRtum7 z@OJ>Z5*dfbkM?2gC(IIw5!b`E&to0{XmDwPD!C%j89Cnx!p7}Mi#n}-s$S&X-+U#w zD{j-s@`@T~=PPRA$-%9{^;9lV-`I@rd0-R5T$RG0iK)i2qrDVG z5zI|+#IdS_+*qDqgHpTVPE zHS~aP$RMB&`VHn0=DF{-q6?Q#D9zr@vhggCR8!U9NtJnU3 zZbl#x9t}JRfppnRK?vQXizHn(3XE6}vPh?GD51l&Br7-zd#ZUx->>ice8+XbXd$1= zj5FW&KEL;QXVmlbkF^3=0h}Fxcs%}#&ppqJ{S7n8Ljjb_Wok4UG#m~okw^p!UDsU- zNIIQX#;H__27`gRXN;iN>jeuknG9c28^`0ZQPfUJ?+)^tQ0J%#!I;lQ00;>H01NfS z`njHxF1xr?*OvGY7K=ry*XyAL=NW~WhwTvoN~Kb8tX8Wcbrd0XeH?QE`u#rTa=E2J zu(>_HPym?Q5S>nky4`N%*hiz$Oy@ib{ehkt0nBT)no6MAY*L|6Ajfec0VI=2WA;dc zll0%9Z~!Qu&l^bq0#Nv#h$1$hzBTIl&h|yRzk6%~AZj+{QRqqVF)9Q<2VXv|2cHi= zc=Y!9pZi7v?RHyr%|{yopo@%S@69>-`1CtvH#cZ~E3U2|bdQX627nzsONV&!c!RE8 z`F1J*I+dk(Or4~-Vtr%!h%vE5v^zN`uGF%8C n6o@qiTZYcgzv30Ze+%#%1X4v&nw{=B00000NkvXXu0mjf$73>7 literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/nt.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/nt.png new file mode 100644 index 0000000000000000000000000000000000000000..56eb7a50e6c9c88c2d282e0b5643d5c37bcce4e3 GIT binary patch literal 690 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 Y1NW+K!s5DvzyJUM07*qoM6N<$f~dP!asU7T literal 0 HcmV?d00001 diff --git a/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/vulp.png b/Resources/Textures/_CorvaxNext/Structures/Wallmounts/flags.rsi/vulp.png new file mode 100644 index 0000000000000000000000000000000000000000..7d383975d890f08df35e393baf9004e2212bb22d GIT binary patch literal 924 zcmV;N17rM&P)#Er!5_5V#167PIIJ3OA%nv3ekgo?xY*~ayG#v%=908XzqG?;hm>q3zBQu}nYSH6>zo4&A%aKDPfwP-V> zCMp_WksyJ7MzqycsbMd=UlxyQ7ayI-R!tW74nU3b?zR{Sh$0 zPEC8AtP{cvW<6cZ&(E74PcRJnFrd(}a4~zt(Ll?dqM(7MSh>hi(K2ZZgA8C`c%VPf zKArm@ePJj^!8?W+Q2u~{_l1I_UXLMVHe@&h*XJfg{HvX9XGe#$Q5HkF30unq;^E*} z!Vfn_C?7JwdT9_~88~(+$L)`2W4`#?LWk+}+9?~)#I%i3Z0+w0hr9F9NJV7;FzdNu zQ3B$2=9ppEQfE`K?jLVFG?!mJ5A3oN~xV{sbdiNy_?19(z_;G&|Nb2+m zsgd@0{224KDBBHBM_?zSI~uuBODft`hyes ypD++u!B7;AB6uVNe=>BOI641{PYwLH27Uoxl=!ZCL&Bs00000 Date: Wed, 11 Dec 2024 02:52:28 +0300 Subject: [PATCH 3/7] =?UTF-8?q?=D0=AD=D0=BB=D0=B5=D0=BA=D1=82=D1=80=D0=B8?= =?UTF-8?q?=D1=87=D0=B5=D1=81=D0=BA=D0=B8=D0=B9=20=D1=81=D1=82=D1=83=D0=BB?= =?UTF-8?q?.=20(#148)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * ExeCute * fix? * nope * fix: formatting - remove useless comments - if statement in OnSignalRecieved() should be handled in switch-case - Logger class shouldn't be used in logging, there is sawmills for this - Remove unnecessary methods * some more shit * wrong author --------- Co-authored-by: JerryImMouse --- .../ExecutionChair/ExecutionChairComponent.cs | 85 +++++++++++ .../ExecutionChair/ExecutionChairSystem.cs | 138 ++++++++++++++++++ .../executionchair/executionchair.ftl | 2 + .../structures/machines/execution_chair.ftl | 2 + .../Structures/Machines/execution_chair.yml | 48 ++++++ .../execution_chair.rsi/execution-chair.png | Bin 0 -> 2980 bytes .../Furniture/execution_chair.rsi/meta.json | 15 ++ 7 files changed, 290 insertions(+) create mode 100644 Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairComponent.cs create mode 100644 Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairSystem.cs create mode 100644 Resources/Locale/ru-RU/_CorvaxNext/executionchair/executionchair.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/machines/execution_chair.ftl create mode 100644 Resources/Prototypes/_CorvaxNext/Entities/Structures/Machines/execution_chair.yml create mode 100644 Resources/Textures/_CorvaxNext/Structures/Furniture/execution_chair.rsi/execution-chair.png create mode 100644 Resources/Textures/_CorvaxNext/Structures/Furniture/execution_chair.rsi/meta.json diff --git a/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairComponent.cs b/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairComponent.cs new file mode 100644 index 00000000000..7e9895ee6c6 --- /dev/null +++ b/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairComponent.cs @@ -0,0 +1,85 @@ +using Content.Shared.DeviceLinking; +using Robust.Shared.Audio; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Content.Server._CorvaxNext.ExecutionChair; + +namespace Content.Server._CorvaxNext.ExecutionChair; + +/// +/// This component represents the state and configuration of an Execution Chair entity. +/// It holds data fields that determine how the chair behaves when it delivers electric shocks +/// to entities buckled into it. It also provides fields for connecting to and receiving signals +/// from the device linking system. +/// +[RegisterComponent, Access(typeof(ExecutionChairSystem))] +public sealed partial class ExecutionChairComponent : Component +{ + /// + /// The next scheduled time at which this chair can deliver damage to strapped entities. + /// This is used to control the rate of repeated electrocution ticks. + /// + [ViewVariables] + public TimeSpan NextDamageTick = TimeSpan.Zero; + + /// + /// Indicates whether the chair is currently enabled. If true, and all conditions (powered, anchored, etc.) + /// are met, the chair will deliver electrical damage to any buckled entities at regular intervals. + /// + [DataField, AutoNetworkedField] + public bool Enabled = false; + + /// + /// Determines whether the chair should play a sound when entities are shocked. If set to true, + /// a sound from will be played each time damage is dealt. + /// + [DataField] + public bool PlaySoundOnShock = true; + + /// + /// Specifies which sound collection is played when entities are shocked. By default, uses a collection of + /// "sparks" sounds. This allows multiple random sparks audio clips to be played. + /// + [DataField] + public SoundSpecifier ShockNoises = new SoundCollectionSpecifier("sparks"); + + /// + /// Controls how loud the shock sound is. This value is applied to the base volume of the chosen sound + /// when played. + /// + [DataField] + public float ShockVolume = 20; + + /// + /// The amount of damage delivered to a buckled entity each damage tick while the chair is active. + /// + [DataField] + public int DamagePerTick = 25; + + /// + /// The duration in seconds for which the electrocution effect is applied each time damage is dealt. + /// For example, if set to 4, it electrocutes an entity for 4 seconds. + /// + [DataField] + public int DamageTime = 4; + + /// + /// The name of the device link port used to toggle the chair's state. Receiving a signal on this port + /// switches the enabled state from on to off or from off to on. + /// + [DataField] + public string TogglePort = "Toggle"; + + /// + /// The name of the device link port used to force the chair's state to enabled (on). + /// Receiving a signal here ensures the chair is active. + /// + [DataField] + public string OnPort = "On"; + + /// + /// The name of the device link port used to force the chair's state to disabled (off). + /// Receiving a signal here ensures the chair is inactive. + /// + [DataField] + public string OffPort = "Off"; +} diff --git a/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairSystem.cs b/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairSystem.cs new file mode 100644 index 00000000000..2e54af008fc --- /dev/null +++ b/Content.Server/_CorvaxNext/ExecutionChair/ExecutionChairSystem.cs @@ -0,0 +1,138 @@ +using Content.Server.DeviceLinking.Events; +using Content.Server.DeviceLinking.Systems; +using Content.Server.Electrocution; +using Content.Server.Power.EntitySystems; +using Content.Shared.Buckle.Components; +using Content.Shared.Popups; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Robust.Shared.Random; +using Robust.Shared.Timing; + +namespace Content.Server._CorvaxNext.ExecutionChair +{ + public sealed partial class ExecutionChairSystem : EntitySystem + { + [Dependency] private readonly IGameTiming _gameTimer = default!; + [Dependency] private readonly IRobustRandom _randomGen = default!; + [Dependency] private readonly DeviceLinkSystem _deviceSystem = default!; + [Dependency] private readonly ElectrocutionSystem _shockSystem = default!; + [Dependency] private readonly SharedAudioSystem _soundSystem = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + + private ISawmill _sawmill = default!; + + private const float VolumeVariationMin = 0.8f; + private const float VolumeVariationMax = 1.2f; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnChairSpawned); + SubscribeLocalEvent(OnSignalReceived); + + _sawmill = Logger.GetSawmill("execution_chair"); + } + + private void OnChairSpawned(EntityUid uid, ExecutionChairComponent component, ref MapInitEvent args) + { + _deviceSystem.EnsureSinkPorts(uid, component.TogglePort, component.OnPort, component.OffPort); + } + + private void OnSignalReceived(EntityUid uid, ExecutionChairComponent component, ref SignalReceivedEvent args) + { + // default case for switch below + bool DefaultCase(EntityUid uid, string port, ExecutionChairComponent component) + { + _sawmill.Debug($"Receieved unexpected port signal: {port} on chair {ToPrettyString(uid)}"); + return component.Enabled; + } + + var newState = args.Port switch + { + var p when p == component.TogglePort => !component.Enabled, + var p when p == component.OnPort => true, + var p when p == component.OffPort => false, + _ => DefaultCase(uid, args.Port, component) + }; + + UpdateChairState(uid, newState, component); + } + + private void UpdateChairState(EntityUid uid, bool activated, ExecutionChairComponent? component = null) + { + if (!Resolve(uid, ref component)) + return; + + component.Enabled = activated; + Dirty(uid, component); + var message = activated + ? Loc.GetString("execution-chair-turn-on") + : Loc.GetString("execution-chair-chair-turn-off"); + + _popup.PopupEntity(message, uid, PopupType.Medium); + } + + public override void Update(float frameTime) + { + base.Update(frameTime); + + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out var chair)) + { + if (!ValidateChairOperation(uid, chair)) + continue; + + if (!TryComp(uid, out var restraint) || restraint.BuckledEntities.Count == 0) + continue; + + ApplyShockEffect(uid, chair, restraint); + } + } + + /// + /// Ensures that the chair is in a valid state to operate: + /// - The chair is anchored in the world (not picked up or moved). + /// - The chair is powered. + /// - The chair is currently enabled/turned on. + /// - The current game time has passed beyond the next scheduled damage tick. + /// + private bool ValidateChairOperation(EntityUid uid, ExecutionChairComponent chair) + { + var transformComponent = Transform(uid); + return transformComponent.Anchored && + this.IsPowered(uid, EntityManager) && + chair.Enabled && + _gameTimer.CurTime >= chair.NextDamageTick; + } + + private void ApplyShockEffect(EntityUid uid, ExecutionChairComponent chair, StrapComponent restraint) + { + var shockDuration = TimeSpan.FromSeconds(chair.DamageTime); + + foreach (var target in restraint.BuckledEntities) + { + var volumeModifier = _randomGen.NextFloat(VolumeVariationMin, VolumeVariationMax); + + var shockSuccess = _shockSystem.TryDoElectrocution( + target, + uid, + chair.DamagePerTick, + shockDuration, + true, + volumeModifier, + ignoreInsulation: true + ); + + if (shockSuccess && chair.PlaySoundOnShock && chair.ShockNoises != null) + { + var audioParams = AudioParams.Default.WithVolume(chair.ShockVolume); + _soundSystem.PlayPvs(chair.ShockNoises, target, audioParams); + } + } + + chair.NextDamageTick = _gameTimer.CurTime + TimeSpan.FromSeconds(1); + } + } +} diff --git a/Resources/Locale/ru-RU/_CorvaxNext/executionchair/executionchair.ftl b/Resources/Locale/ru-RU/_CorvaxNext/executionchair/executionchair.ftl new file mode 100644 index 00000000000..6106190885d --- /dev/null +++ b/Resources/Locale/ru-RU/_CorvaxNext/executionchair/executionchair.ftl @@ -0,0 +1,2 @@ +execution-chair-turn-on = Воздух словно искрится... +execution-chair-chair-turn-off = Атмосфера разряжается. diff --git a/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/machines/execution_chair.ftl b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/machines/execution_chair.ftl new file mode 100644 index 00000000000..b86f49cd469 --- /dev/null +++ b/Resources/Locale/ru-RU/ss14-ru/prototypes/_corvaxnext/entities/structures/machines/execution_chair.ftl @@ -0,0 +1,2 @@ +ent-ExecutionChair = электрический стул + .desc = Выглядит комфортно. diff --git a/Resources/Prototypes/_CorvaxNext/Entities/Structures/Machines/execution_chair.yml b/Resources/Prototypes/_CorvaxNext/Entities/Structures/Machines/execution_chair.yml new file mode 100644 index 00000000000..16dcbc5cd8a --- /dev/null +++ b/Resources/Prototypes/_CorvaxNext/Entities/Structures/Machines/execution_chair.yml @@ -0,0 +1,48 @@ +- type: entity + id: ExecutionChair + parent: BaseStructureDynamic + name: execution chair + description: Looks comfy. + components: + - type: Sprite + sprite: _CorvaxNext/Structures/Furniture/execution_chair.rsi + state: execution-chair + noRot: true + - type: Rotatable + - type: InteractionOutline + - type: Strap + position: Stand + buckleOffset: "0,-0.05" + - type: Fixtures + fixtures: + fix1: + shape: + !type:PhysShapeCircle + radius: 0.2 + density: 100 + mask: + - TableMask + - type: ExecutionChair + - type: ApcPowerReceiver + powerLoad: 1500 + - type: ExtensionCableReceiver + - type: Transform + anchored: true + - type: Damageable + damageModifierSet: Metallic + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:DoActsBehavior + acts: ["Destruction"] + - !type:PlaySoundBehavior + sound: + collection: MetalBreak + - !type:SpawnEntitiesBehavior + spawn: + SheetSteel: + min: 5 + max: 5 diff --git a/Resources/Textures/_CorvaxNext/Structures/Furniture/execution_chair.rsi/execution-chair.png b/Resources/Textures/_CorvaxNext/Structures/Furniture/execution_chair.rsi/execution-chair.png new file mode 100644 index 0000000000000000000000000000000000000000..52d5e3c785e56ce7d96a0b79b3ee00ef4a2fdbe4 GIT binary patch literal 2980 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 aP5*xq;8G1FgDo!r0000 Date: Tue, 10 Dec 2024 16:41:51 +0300 Subject: [PATCH 4/7] =?UTF-8?q?[Port]=20=D0=A1=D0=BB=D1=83=D1=87=D0=B0?= =?UTF-8?q?=D0=B9=D0=BD=D1=8B=D0=B5=20=D0=B2=D1=8B=D1=81=D1=82=D1=80=D0=B5?= =?UTF-8?q?=D0=BB=D1=8B=20=D0=BF=D1=80=D0=B8=20=D0=BF=D0=B0=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D0=B8=20=D0=BE=D1=80=D1=83=D0=B6=D0=B8=D1=8F=20(#1?= =?UTF-8?q?47)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * FireOnDrop * whoopsy --- .../Ranged/Systems/FireOnDropSystem.cs | 27 +++++++++++++++++++ .../Weapons/Ranged/Components/GunComponent.cs | 6 +++++ .../Objects/Weapons/Guns/Basic/base_pka.yml | 1 + .../Weapons/Guns/Battery/battery_guns.yml | 1 + .../Objects/Weapons/Guns/Pistols/pistols.yml | 1 + .../Weapons/Guns/Revolvers/revolvers.yml | 1 + .../Objects/Weapons/Guns/Rifles/rifles.yml | 1 + .../Objects/Weapons/Guns/SMGs/smgs.yml | 1 + .../Weapons/Guns/Shotguns/shotguns.yml | 1 + .../Objects/Weapons/Guns/Snipers/snipers.yml | 1 + 10 files changed, 41 insertions(+) create mode 100644 Content.Server/_CorvaxNext/Weapons/Ranged/Systems/FireOnDropSystem.cs diff --git a/Content.Server/_CorvaxNext/Weapons/Ranged/Systems/FireOnDropSystem.cs b/Content.Server/_CorvaxNext/Weapons/Ranged/Systems/FireOnDropSystem.cs new file mode 100644 index 00000000000..a6112ad49cf --- /dev/null +++ b/Content.Server/_CorvaxNext/Weapons/Ranged/Systems/FireOnDropSystem.cs @@ -0,0 +1,27 @@ +using Content.Shared.Throwing; +using Content.Shared.Weapons.Ranged.Components; +using Content.Shared.Weapons.Ranged.Systems; +using Robust.Shared.Random; + +namespace Content.Server.Weapons.Ranged.Systems; + +public sealed class FireOnDropSystem : EntitySystem +{ + [Dependency] private readonly SharedGunSystem _gun = default!; + [Dependency] private readonly IRobustRandom _random = default!; + + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(HandleLand); + } + + + private void HandleLand(EntityUid uid, GunComponent component, ref ThrowDoHitEvent args) + { + if (_random.Prob(component.FireOnDropChance)) + _gun.AttemptShoot(uid, uid, component, Transform(uid).Coordinates.Offset(Transform(uid).LocalRotation.ToVec())); + } +} diff --git a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs index 98b1d2fe2a4..bcc56c28ab1 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunComponent.cs @@ -263,6 +263,12 @@ public sealed partial class GunComponent : Component /// [DataField] public Vector2 DefaultDirection = new Vector2(0, -1); + + /// + /// Corvax-Next. The percentage chance of a given gun to accidentally discharge if violently thrown into a wall or person + /// + [DataField] + public float FireOnDropChance = 0.1f; } [Flags] diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml index a661323c7c3..03dc3a2dc0b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Basic/base_pka.yml @@ -22,6 +22,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/kinetic_accel.ogg + fireOnDropChance: 1 # Corvax-Next-FireOnDrop - type: AmmoCounter - type: Appearance - type: GenericVisualizer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml index 55499a0a979..7dbb328e42c 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml @@ -20,6 +20,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/laser.ogg + fireOnDropChance: 0.15 # Corvax-Next-FireOnDrop - type: Battery maxCharge: 1000 startingCharge: 1000 diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml index 167e21e15f2..7cf0f06ccde 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml @@ -33,6 +33,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/pistol.ogg + fireOnDropChance: 0.3 # Corvax-Next-FireOnDrop - type: ChamberMagazineAmmoProvider soundRack: path: /Audio/Weapons/Guns/Cock/pistol_cock.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml index a34d0a4fe0a..e0aa5057f45 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Revolvers/revolvers.yml @@ -29,6 +29,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/revolver.ogg + fireOnDropChance: 0.6 # Corvax-Next-FireOnDrop - type: UseDelay delay: 0.66 - type: ContainerContainer diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml index 44b8944592e..d51e95088e8 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml @@ -22,6 +22,7 @@ - FullAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/batrifle.ogg + fireOnDropChance: 0.5 # Corvax-Next-FireOnDrop - type: ChamberMagazineAmmoProvider soundRack: path: /Audio/Weapons/Guns/Cock/sf_rifle_cock.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml index bccb514c63f..cca78cb4ffe 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml @@ -29,6 +29,7 @@ soundGunshot: path: /Audio/Weapons/Guns/Gunshots/smg.ogg defaultDirection: 1, 0 + fireOnDropChance: 0.3 # Corvax-Next-FireOnDrop - type: ChamberMagazineAmmoProvider soundRack: path: /Audio/Weapons/Guns/Cock/smg_cock.ogg diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml index 02f6b5c03de..5887669fd0e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Shotguns/shotguns.yml @@ -28,6 +28,7 @@ path: /Audio/Weapons/Guns/Gunshots/shotgun.ogg soundEmpty: path: /Audio/Weapons/Guns/Empty/empty.ogg + fireOnDropChance: 0.3 # Corvax-Next-FireOnDrop - type: BallisticAmmoProvider whitelist: tags: diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index 9cb0b630b7e..7e2586f485e 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -25,6 +25,7 @@ - SemiAuto soundGunshot: path: /Audio/Weapons/Guns/Gunshots/sniper.ogg + fireOnDropChance: 0.9 # Corvax-Next-FireOnDrop - type: BallisticAmmoProvider capacity: 10 proto: CartridgeLightRifle From d7cc749a400a9b230abea4ead2aef321e0f4023c Mon Sep 17 00:00:00 2001 From: Kill_Me_I_Noobs <118206719+Vonsant@users.noreply.github.com> Date: Fri, 6 Dec 2024 17:43:52 +0300 Subject: [PATCH 5/7] Port TapeRecorder (#129) --- .../TapeRecorder/TapeRecorderSystem.cs | 24 + .../Ui/TapeRecorderBoundUserInterface.cs | 64 +++ .../TapeRecorder/Ui/TapeRecorderWindow.xaml | 23 + .../Ui/TapeRecorderWindow.xaml.cs | 133 ++++++ .../TapeRecorder/TapeRecorderSystem.cs | 132 ++++++ .../Components/ActiveTapeRecorderComponent.cs | 9 + .../Components/TapeCassetteComponent.cs | 72 +++ .../Components/TapeRecorderComponent.cs | 124 +++++ .../TapeRecorder/Events/TapeRecorderEvents.cs | 50 +++ .../TapeRecorder/SharedTapeRecorderSystem.cs | 422 ++++++++++++++++++ .../TapeCassetteRecordedMessage.cs | 51 +++ .../Audio/Items/Taperecorder/attributions.yml | 17 + .../Items/Taperecorder/taperecorder_play.ogg | Bin 0 -> 11670 bytes .../Taperecorder/taperecorder_rewind.ogg | Bin 0 -> 13159 bytes .../Items/Taperecorder/taperecorder_stop.ogg | Bin 0 -> 7395 bytes .../en-US/taperecorder/taperecorder.ftl | 26 ++ .../catalog/fills/boxes/security.ftl | 2 + .../objects/devices/tape_recorder.ftl | 4 + .../entities/objects/misc/paper.ftl | 1 + .../ru-RU/taperecorder/taperecorder.ftl | 26 ++ .../Catalog/Fills/Boxes/security.yml | 23 + .../Catalog/Fills/Lockers/security.yml | 1 + .../Objects/Devices/tape_recorder.yml | 88 ++++ .../Entities/Objects/Misc/paper.yml | 6 + .../Entities/Structures/Machines/lathe.yml | 3 + Resources/Prototypes/Recipes/Lathes/misc.yml | 18 + .../Roles/Jobs/Wildcards/reporter.yml | 8 +- .../Devices/cassette_tapes.rsi/meta.json | 17 + .../cassette_tapes.rsi/tape_greyscale.png | Bin 0 -> 423 bytes .../cassette_tapes.rsi/tape_ribbonoverlay.png | Bin 0 -> 449 bytes .../Devices/tape_recorder.rsi/inhand-left.png | Bin 0 -> 491 bytes .../tape_recorder.rsi/inhand-right.png | Bin 0 -> 487 bytes .../Devices/tape_recorder.rsi/meta.json | 58 +++ .../tape_recorder.rsi/taperecorder_empty.png | Bin 0 -> 581 bytes .../tape_recorder.rsi/taperecorder_idle.png | Bin 0 -> 597 bytes .../taperecorder_playing.png | Bin 0 -> 955 bytes .../taperecorder_recording.png | Bin 0 -> 934 bytes .../taperecorder_rewinding.png | Bin 0 -> 887 bytes .../Objects/Storage/boxes.rsi/meta.json | 3 + .../Objects/Storage/boxes.rsi/recorder.png | Bin 0 -> 189 bytes 40 files changed, 1402 insertions(+), 3 deletions(-) create mode 100644 Content.Client/TapeRecorder/TapeRecorderSystem.cs create mode 100644 Content.Client/TapeRecorder/Ui/TapeRecorderBoundUserInterface.cs create mode 100644 Content.Client/TapeRecorder/Ui/TapeRecorderWindow.xaml create mode 100644 Content.Client/TapeRecorder/Ui/TapeRecorderWindow.xaml.cs create mode 100644 Content.Server/TapeRecorder/TapeRecorderSystem.cs create mode 100644 Content.Shared/TapeRecorder/Components/ActiveTapeRecorderComponent.cs create mode 100644 Content.Shared/TapeRecorder/Components/TapeCassetteComponent.cs create mode 100644 Content.Shared/TapeRecorder/Components/TapeRecorderComponent.cs create mode 100644 Content.Shared/TapeRecorder/Events/TapeRecorderEvents.cs create mode 100644 Content.Shared/TapeRecorder/SharedTapeRecorderSystem.cs create mode 100644 Content.Shared/TapeRecorder/TapeCassetteRecordedMessage.cs create mode 100644 Resources/Audio/Items/Taperecorder/attributions.yml create mode 100644 Resources/Audio/Items/Taperecorder/taperecorder_play.ogg create mode 100644 Resources/Audio/Items/Taperecorder/taperecorder_rewind.ogg create mode 100644 Resources/Audio/Items/Taperecorder/taperecorder_stop.ogg create mode 100644 Resources/Locale/en-US/taperecorder/taperecorder.ftl create mode 100644 Resources/Locale/ru-RU/ss14-ru/prototypes/entities/objects/devices/tape_recorder.ftl create mode 100644 Resources/Locale/ru-RU/taperecorder/taperecorder.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Devices/tape_recorder.yml create mode 100644 Resources/Textures/Objects/Devices/cassette_tapes.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/cassette_tapes.rsi/tape_greyscale.png create mode 100644 Resources/Textures/Objects/Devices/cassette_tapes.rsi/tape_ribbonoverlay.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/inhand-left.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/inhand-right.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/meta.json create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/taperecorder_empty.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/taperecorder_idle.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/taperecorder_playing.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/taperecorder_recording.png create mode 100644 Resources/Textures/Objects/Devices/tape_recorder.rsi/taperecorder_rewinding.png create mode 100644 Resources/Textures/Objects/Storage/boxes.rsi/recorder.png diff --git a/Content.Client/TapeRecorder/TapeRecorderSystem.cs b/Content.Client/TapeRecorder/TapeRecorderSystem.cs new file mode 100644 index 00000000000..c228ea356c2 --- /dev/null +++ b/Content.Client/TapeRecorder/TapeRecorderSystem.cs @@ -0,0 +1,24 @@ +using Content.Shared.TapeRecorder; + +namespace Content.Client.TapeRecorder; + +/// +/// Required for client side prediction stuff +/// +public sealed class TapeRecorderSystem : SharedTapeRecorderSystem +{ + private TimeSpan _lastTickTime = TimeSpan.Zero; + + public override void Update(float frameTime) + { + if (!Timing.IsFirstTimePredicted) + return; + + //We need to know the exact time period that has passed since the last update to ensure the tape position is sync'd with the server + //Since the client can skip frames when lagging, we cannot use frameTime + var realTime = (float) (Timing.CurTime - _lastTickTime).TotalSeconds; + _lastTickTime = Timing.CurTime; + + base.Update(realTime); + } +} \ No newline at end of file diff --git a/Content.Client/TapeRecorder/Ui/TapeRecorderBoundUserInterface.cs b/Content.Client/TapeRecorder/Ui/TapeRecorderBoundUserInterface.cs new file mode 100644 index 00000000000..33df6267480 --- /dev/null +++ b/Content.Client/TapeRecorder/Ui/TapeRecorderBoundUserInterface.cs @@ -0,0 +1,64 @@ +using Content.Shared.TapeRecorder.Components; +using Content.Shared.TapeRecorder.Events; +using Robust.Shared.Prototypes; +using Robust.Shared.Timing; + +namespace Content.Client.TapeRecorder.Ui; + +public sealed class TapeRecorderBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey) +{ + [Dependency] private readonly IEntityManager _entMan = default!; + + [ViewVariables] + private TapeRecorderWindow? _window; + + [ViewVariables] + private TimeSpan _printCooldown; + + protected override void Open() + { + base.Open(); + + _window = new(_entMan, Owner); + _window.OnClose += Close; + _window.OnModeChanged += ChangeMode; + _window.OnPrintTranscript += PrintTranscript; + _window.OpenCentered(); + } + + private void ChangeMode(TapeRecorderMode mode) + { + SendMessage(new ChangeModeTapeRecorderMessage(mode)); + } + + private void PrintTranscript() + { + SendMessage(new PrintTapeRecorderMessage()); + + _window?.UpdatePrint(true); + + Timer.Spawn(_printCooldown, () => + { + _window?.UpdatePrint(false); + }); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + base.UpdateState(state); + + if (state is not TapeRecorderState cast) + return; + + _printCooldown = cast.PrintCooldown; + + _window?.UpdateState(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (disposing) + _window?.Dispose(); + } +} diff --git a/Content.Client/TapeRecorder/Ui/TapeRecorderWindow.xaml b/Content.Client/TapeRecorder/Ui/TapeRecorderWindow.xaml new file mode 100644 index 00000000000..0e22a7815fe --- /dev/null +++ b/Content.Client/TapeRecorder/Ui/TapeRecorderWindow.xaml @@ -0,0 +1,23 @@ + + + + + + + + +