diff --git a/Content.Server/_White/_Engi/Suhariki/SuharikiComponent.cs b/Content.Server/_White/_Engi/Suhariki/SuharikiComponent.cs new file mode 100644 index 0000000000..a437ed567e --- /dev/null +++ b/Content.Server/_White/_Engi/Suhariki/SuharikiComponent.cs @@ -0,0 +1,53 @@ +using Robust.Shared.Prototypes; +using Content.Shared.Damage; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; +using Content.Shared.Chat.Prototypes; +using Robust.Shared.Audio; + +namespace Content.Server._White._Engi.Suhariki; + +/// +/// Makes you loose your tooth and have funny accent. +/// WD Engi Exclusive. +/// +[RegisterComponent, Access(typeof(SuharikiSystem))] +public sealed partial class SuharikiComponent : Component + +{ + /// + /// Amount and type of damage that will be dealt on event. + /// + [DataField(required: true)] + [ViewVariables(VVAccess.ReadWrite)] + public DamageSpecifier Damage = new(); + + /// + /// Chance of event activation. + /// + [DataField] + public float Chance = 0; + + /// + /// Amount of rolls for event. + /// + [DataField] + public int StonesInFood = 1; + + /// + /// The prototype that will be spawned on event. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + public string HoldingPrototype = "SuharikiTooth"; + + /// + /// Emote triggered on event. + /// + [DataField(customTypeSerializer: typeof(PrototypeIdSerializer))] + public string EmoteId = "Scream"; + + /// + /// Sound triggered on event. + /// + [DataField] + public SoundSpecifier UseSound { get; set; } = new SoundPathSpecifier("/Audio/White/_Engi/Object/Misc/Suhariki/tooth_break.ogg"); +} diff --git a/Content.Server/_White/_Engi/Suhariki/SuharikiSystem.cs b/Content.Server/_White/_Engi/Suhariki/SuharikiSystem.cs new file mode 100644 index 0000000000..035e55e97e --- /dev/null +++ b/Content.Server/_White/_Engi/Suhariki/SuharikiSystem.cs @@ -0,0 +1,152 @@ +using Content.Server.Inventory; +using Content.Server.Nutrition.Components; +using Content.Server.Popups; +using Content.Shared.Interaction.Events; +using Content.Shared.Nutrition.EntitySystems; +using Robust.Shared.Random; +using Content.Shared.Damage; +using Content.Server.Effects; +using Robust.Shared.Player; +using Content.Server.Administration.Logs; +using Content.Shared.Database; +using Content.Server.Chat.Systems; +using Content.Server.Nutrition.EntitySystems; +using Robust.Shared.Timing; +using Robust.Shared.Audio; +using Robust.Shared.Audio.Systems; +using Content.Shared.Verbs; +using Robust.Shared.Utility; +using Content.Shared.Body.Components; +using Content.Server.Body.Systems; +using Content.Server.Body.Components; +using Content.Server.Speech.Components; + +namespace Content.Server._White._Engi.Suhariki; + +/// +/// WD Engi Exclusive. +/// +public sealed class SuharikiSystem : EntitySystem +{ + [Dependency] private readonly PopupSystem _popup = default!; + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly DamageableSystem _damageableSystem = default!; + [Dependency] private readonly ColorFlashEffectSystem _color = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly ChatSystem _chat = default!; + [Dependency] private readonly FoodSystem _food = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; + [Dependency] private readonly BodySystem _body = default!; + private EntityQuery _metaQuery; + + public override void Initialize() + { + base.Initialize(); + _metaQuery = GetEntityQuery(); + SubscribeLocalEvent(OnUseInHand, after: new[] { typeof(OpenableSystem), typeof(ServerInventorySystem) }); + SubscribeLocalEvent>(AddEatVerb); + } + + private void OnUseInHand(Entity entity, ref UseInHandEvent ev) + { + if (ev.Handled) + return; + + var result = TryUse(ev.User, ev.User, entity, entity.Comp); + ev.Handled = result.Handled; + } + + private (bool Success, bool Handled) TryUse(EntityUid user, EntityUid target, EntityUid food, SuharikiComponent scomponent) + { + var foodComponents = _metaQuery.GetComponent(food).EntityPrototype; + + if (foodComponents == null) + return (false, false); + + foodComponents.Components.TryGetComponent("Food", out var component); + + if (component == null) + return (false, false); + + // idk what I'm doing but otherwise the owner is "0" and it throws fatal error + component.Owner = food; + + var result = _food.TryFeed(user, target, food, (FoodComponent) component); + + if (result.Success) + Timer.Spawn(300, () => RollEvent(scomponent, user)); + + return (true, true); + } + + private void RollEvent(SuharikiComponent scomponent, EntityUid user) + { + if (scomponent.StonesInFood < 1) + return; + + scomponent.StonesInFood -= 1; + + // Pseudorandom shit + if (!_random.Prob(scomponent.Chance)) + return; + + // Normally slimes don't care about hard food + var whomst = _metaQuery.GetComponent(user).EntityPrototype; + var isUserSlime = whomst!.ID.Contains("slime", StringComparison.OrdinalIgnoreCase); + if (isUserSlime) + return; + + var modifiedDamage = _damageableSystem.TryChangeDamage(user, scomponent.Damage, true); + var deleted = Deleted(user); + if (modifiedDamage is null || !EntityManager.EntityExists(user)) + return; + if (!modifiedDamage.Any() || deleted) + return; + // Damaging the user with appropriate effects + var stringTopopup = Loc.GetString("suhariki-lost", ("user", user)); + _popup.PopupEntity(Name(user) + " " + stringTopopup, user, Shared.Popups.PopupType.LargeCaution); + _color.RaiseEffect(Color.Red, new List { user }, Filter.Pvs(user, entityManager: EntityManager)); + _audio.PlayPvs(scomponent.UseSound, user, AudioParams.Default.WithVolume(-1f)); + _adminLogger.Add(LogType.Damaged, + LogImpact.Medium, + $"This idiot {ToPrettyString(user):user} tried to eat Suhariki and lost tooth, received {modifiedDamage.GetTotal():damage} damage and recieved FrontalLisp accent"); + + // Spawns prototype + var coords = Transform(user).Coordinates; + Spawn(scomponent.HoldingPrototype, coords.Offset(_random.NextVector2(0.2f))); + + // Scream + Timer.Spawn(300, () => _chat.TryEmoteWithChat(user, scomponent.EmoteId)); + + // Adding FrontalLisp accent + EnsureComp(user); + + return; + + } + + private void AddEatVerb(Entity entity, ref GetVerbsEvent ev) + { + if (entity.Owner == ev.User || + !ev.CanInteract || + !ev.CanAccess || + !TryComp(ev.User, out var body) || + !_body.TryGetBodyOrganComponents(ev.User, out var stomachs)) + return; + + var user = ev.User; + AlternativeVerb verb = new() + { + Act = () => + { + TryUse(user, user, entity, entity.Comp); + }, + Icon = new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/VerbIcons/cutlery.svg.192dpi.png")), + Text = Loc.GetString("food-system-verb-eat"), + Priority = -1 + }; + + ev.Verbs.Add(verb); + } + +} diff --git a/Resources/Changelog/ChangelogHonk.yml b/Resources/Changelog/ChangelogHonk.yml index 934e4c725e..fcd66c2ee2 100644 --- a/Resources/Changelog/ChangelogHonk.yml +++ b/Resources/Changelog/ChangelogHonk.yml @@ -105,3 +105,9 @@ type: Add id: 16 time: '2024-11-21T18:00:13.0000000+00:00' +- author: BIG_Zi_348 + changes: + - message: "Сухарики всегда были на станции, надо просто поискать их в технических туннелях и может быть, вам повезёт." + type: Add + id: 17 + time: '2024-11-24T18:00:13.0000000+00:00' diff --git a/Resources/Locale/ru-RU/_Engi/suhariki.ftl b/Resources/Locale/ru-RU/_Engi/suhariki.ftl new file mode 100644 index 0000000000..c6f4aaaa7a --- /dev/null +++ b/Resources/Locale/ru-RU/_Engi/suhariki.ftl @@ -0,0 +1,7 @@ +ent-FoodSnackSuhariki = сухарики + .desc = Старая реликвия из прошлого, на этикетке едва можно прочитать слово «Кириешки». +ent-FoodPacketSuharikiTrash = упаковка от сухариков +ent-SuharikiTooth = зуб + .desc = Может быть он твой, а может и не твой. + +suhariki-lost = сломал свой зуб! diff --git a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml index 9dd4c37ca2..377c29f1ba 100644 --- a/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml +++ b/Resources/Prototypes/Catalog/Fills/Lockers/misc.yml @@ -186,6 +186,8 @@ prob: 0.10 - id: BarberScissors prob: 0.05 + - id: FoodSnackSuhariki # WD from Engi to Amour + prob: 0.10 # Syndicate loot - id: null prob: 0.95 @@ -594,6 +596,8 @@ prob: 0.10 - id: BarberScissors prob: 0.05 + - id: FoodSnackSuhariki # WD from Engi to Amour + prob: 0.10 # Syndicate loot - id: null prob: 0.95 diff --git a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_snacks.yml b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_snacks.yml index 9f4b3e5983..9aa49c21b0 100644 --- a/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_snacks.yml +++ b/Resources/Prototypes/Entities/Markers/Spawners/Random/Food_Drinks/food_snacks.yml @@ -45,5 +45,6 @@ - FoodFrozenSnowconeRainbow - FoodSnackPistachios - FoodSnackSemki + - FoodSnackSuhariki # WD from Engi to Amour chance: 0.8 offset: 0.0 diff --git a/Resources/Prototypes/_White/_Engi/Entities/Objects/Consumable/Food/suhariki.yml b/Resources/Prototypes/_White/_Engi/Entities/Objects/Consumable/Food/suhariki.yml new file mode 100644 index 0000000000..1ad6277d38 --- /dev/null +++ b/Resources/Prototypes/_White/_Engi/Entities/Objects/Consumable/Food/suhariki.yml @@ -0,0 +1,78 @@ +# WD Engi Exclusive +- type: entity + parent: BaseItem + id: FoodSnackSuhariki + name: suhariki + description: Old relic of the past, you can barely read the label "Kirieshki". + components: + - type: Food + trash: FoodPacketSuharikiTrash + - type: FlavorProfile + flavors: + - cheap + - bread + - type: Sprite + sprite: White/_Engi/Objects/Consumable/Food/Suhariki/suhariki.rsi + state: suhariki + - type: Tag + tags: + - FoodSnack + - type: BadFood + - type: Suhariki + chance: 0.25 + damage: + types: + Blunt: 10 + - type: Item + size: Tiny + sprite: White/_Engi/Objects/Consumable/Food/Suhariki/suhariki.rsi + heldPrefix: suhariki + - type: SolutionContainerManager + solutions: + food: + maxVol: 5 # No extra room for condiments + reagents: + - ReagentId: Nutriment + Quantity: 1 + - ReagentId: Omnizine + Quantity: 4 + - type: SpaceGarbage + - type: StaticPrice + price: 500 + +- type: entity + noSpawn: true + parent: FoodPacketTrash + id: FoodPacketSuharikiTrash + name: suhariki bag + components: + - type: Sprite + sprite: White/_Engi/Objects/Consumable/Food/Suhariki/suhariki.rsi + state: suhariki-trash + - type: Item + size: Tiny + sprite: White/_Engi/Objects/Consumable/Food/Suhariki/suhariki.rsi + heldPrefix: suhariki + +- type: entity + noSpawn: true + name: Зуб + parent: BaseItem + id: SuharikiTooth + description: Может быть он твой, а может и не твой. + components: + - type: Sprite + sprite: White/_Engi/Objects/Consumable/Food/Suhariki/tooth.rsi + state: icon + - type: Item + size: Tiny + - type: EmitSoundOnLand + sound: + path: /Audio/White/_Engi/Object/Misc/Suhariki/tooth_drop_1.ogg + - type: EmitSoundOnCollide + sound: + path: /Audio/White/_Engi/Object/Misc/Suhariki/tooth_drop_2.ogg + - type: SpaceGarbage + - type: StaticPrice + price: 100 + diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/meta.json b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/meta.json new file mode 100644 index 0000000000..49ba5139da --- /dev/null +++ b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/meta.json @@ -0,0 +1,25 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "deadisko and BIG_Zi_348", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "suhariki" + }, + { + "name": "suhariki-trash" + }, + { + "name": "suhariki-inhand-right", + "directions": 4 + }, + { + "name": "suhariki-inhand-left", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-left.png b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-left.png new file mode 100644 index 0000000000..5a37a5debe Binary files /dev/null and b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-left.png differ diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-right.png b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-right.png new file mode 100644 index 0000000000..95c620f2eb Binary files /dev/null and b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-inhand-right.png differ diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-trash.png b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-trash.png new file mode 100644 index 0000000000..f528d725b2 Binary files /dev/null and b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki-trash.png differ diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki.png b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki.png new file mode 100644 index 0000000000..5e46236261 Binary files /dev/null and b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/suhariki.rsi/suhariki.png differ diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/icon.png b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/icon.png new file mode 100644 index 0000000000..43d576fdae Binary files /dev/null and b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/icon.png differ diff --git a/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/meta.json b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/meta.json new file mode 100644 index 0000000000..87bfd9d670 --- /dev/null +++ b/Resources/Textures/White/_Engi/Consumable/Food/Suhariki/tooth.rsi/meta.json @@ -0,0 +1,14 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "xd", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + } + ] +}