From d8b97e8d3764938cfde6d26ff18fc0cdc768c819 Mon Sep 17 00:00:00 2001 From: no <165581243+pissdemon@users.noreply.github.com> Date: Sun, 14 Apr 2024 18:48:59 +0200 Subject: [PATCH] Randomized lollipop and gumball flavors (#1074) * Give lollipops and gumballs random flavors * RandomizedCandy: Fix borg lollipop action * RandomizedCandy: Play sound when candy is fabricated * RandomizedCandy: Lollipop can now be put in mouth * RandomizedCandy: Quieten fab sound * RandomizedCandy: merge icons and reexport without pHYs chunk should speed up downloading these textures on 9600 baud modems * RandomizedCandy: loads more flavors and update description * RandomizedCandy: Fix duplicate candyFlavor IDs * RandomizedCandy: Fix attrib and preserve flavor order * RandomizedCandy: init on MapInit instead of ComponentInit * RandomizedCandy: Fix minor code style issues, file placement * RandomizedCandy: put new code into DeltaV namespace * RandomizedCandy: Removed redundant datafield tags Signed-off-by: no <165581243+pissdemon@users.noreply.github.com> --------- Signed-off-by: no <165581243+pissdemon@users.noreply.github.com> --- .../Borgs/RandomizedCandyVisualizer.cs | 26 ++ Content.Client/Entry/EntryPoint.cs | 1 + .../Abilities/Borgs/CandyFlavorPrototype.cs | 31 +++ .../Abilities/Borgs/RandomizedCandySystem.cs | 83 ++++++ .../Borgs/FabricateCandyComponent.cs | 18 +- .../Abilities/Borgs/FabricateCandySystem.cs | 18 +- .../Borgs/RandomizedCandyComponent.cs | 18 ++ .../en-US/deltav/flavors/flavor-profiles.ftl | 7 + .../DeltaV/Flavors/candyflavors.yml | 236 ++++++++++++++++++ .../Prototypes/DeltaV/Flavors/flavors.yml | 16 ++ .../Prototypes/Nyanotrasen/Actions/types.yml | 1 + .../Objects/Consumable/Food/candy.yml | 23 +- .../Food/candy.rsi/gumball-shine.png | Bin 0 -> 97 bytes .../Consumable/Food/candy.rsi/gumball.png | Bin 323 -> 168 bytes .../Food/candy.rsi/lollipop-ball.png | Bin 0 -> 135 bytes .../lollipop-equipped-MASK-vulpkanin.png | Bin 0 -> 109 bytes .../Food/candy.rsi/lollipop-equipped-MASK.png | Bin 0 -> 110 bytes .../Food/candy.rsi/lollipop-stickandshine.png | Bin 0 -> 118 bytes .../Consumable/Food/candy.rsi/meta.json | 19 +- 19 files changed, 486 insertions(+), 11 deletions(-) create mode 100644 Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs create mode 100644 Content.Server/DeltaV/Abilities/Borgs/CandyFlavorPrototype.cs create mode 100644 Content.Server/DeltaV/Abilities/Borgs/RandomizedCandySystem.cs create mode 100644 Content.Shared/DeltaV/Abilities/Borgs/RandomizedCandyComponent.cs create mode 100644 Resources/Prototypes/DeltaV/Flavors/candyflavors.yml create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/gumball-shine.png create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-ball.png create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-equipped-MASK-vulpkanin.png create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-equipped-MASK.png create mode 100644 Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-stickandshine.png diff --git a/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs b/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs new file mode 100644 index 00000000000..970af100a61 --- /dev/null +++ b/Content.Client/DeltaV/Abilities/Borgs/RandomizedCandyVisualizer.cs @@ -0,0 +1,26 @@ +using Content.Shared.DeltaV.Abilities.Borgs; +using Robust.Client.GameObjects; + +namespace Content.Client.DeltaV.Abilities.Borgs; + +/// +/// Responsible for coloring randomized candy. +/// +public sealed class RandomizedCandyVisualizer : VisualizerSystem +{ + protected override void OnAppearanceChange(EntityUid uid, RandomizedCandyComponent component, ref AppearanceChangeEvent args) + { + if (!TryComp(uid, out var sprite) + || !AppearanceSystem.TryGetData(uid, RandomizedCandyVisuals.Color, out var color, args.Component)) + { + return; + } + + sprite.LayerSetColor(CandyVisualLayers.Ball, color); + } +} + +public enum CandyVisualLayers : byte +{ + Ball +} diff --git a/Content.Client/Entry/EntryPoint.cs b/Content.Client/Entry/EntryPoint.cs index 47f11ee1616..6cddeeb2038 100644 --- a/Content.Client/Entry/EntryPoint.cs +++ b/Content.Client/Entry/EntryPoint.cs @@ -118,6 +118,7 @@ public override void Init() _prototypeManager.RegisterIgnore("wireLayout"); _prototypeManager.RegisterIgnore("alertLevels"); _prototypeManager.RegisterIgnore("nukeopsRole"); + _prototypeManager.RegisterIgnore("candyFlavor"); // Delta-V _componentFactory.GenerateNetIds(); _adminManager.Initialize(); diff --git a/Content.Server/DeltaV/Abilities/Borgs/CandyFlavorPrototype.cs b/Content.Server/DeltaV/Abilities/Borgs/CandyFlavorPrototype.cs new file mode 100644 index 00000000000..3b5d2dbe9df --- /dev/null +++ b/Content.Server/DeltaV/Abilities/Borgs/CandyFlavorPrototype.cs @@ -0,0 +1,31 @@ +using Content.Shared.Nutrition; +using Robust.Shared.Prototypes; + +namespace Content.Server.DeltaV.Abilities.Borgs; + +/// +/// Describes the color and flavor profile of lollipops and gumballs. Yummy! +/// +[Prototype("candyFlavor")] +public sealed partial class CandyFlavorPrototype : IPrototype +{ + /// + [IdDataField] + public string ID { get; private set; } = default!; + + /// + /// The display name for this candy. Not localized. + /// + [DataField] public string Name { get; private set; } = ""; + + /// + /// The color of the candy. + /// + [DataField] public Color Color { get; private set; } = Color.White; + + /// + /// How the candy tastes like. + /// + [DataField] + public HashSet> Flavors { get; private set; } = []; +} diff --git a/Content.Server/DeltaV/Abilities/Borgs/RandomizedCandySystem.cs b/Content.Server/DeltaV/Abilities/Borgs/RandomizedCandySystem.cs new file mode 100644 index 00000000000..e2ad0c24114 --- /dev/null +++ b/Content.Server/DeltaV/Abilities/Borgs/RandomizedCandySystem.cs @@ -0,0 +1,83 @@ +using System.Linq; +using Content.Server.Nutrition.Components; +using Content.Shared.DeltaV.Abilities.Borgs; +using Content.Shared.Nutrition; +using Robust.Shared.Prototypes; +using Robust.Shared.Random; +using Robust.Shared.Utility; + +namespace Content.Server.DeltaV.Abilities.Borgs; + +/// +/// Gives things with a a random flavor, with corresponding appearance and +/// examine text. +/// +public sealed partial class RandomizedCandySystem : EntitySystem +{ + [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly IPrototypeManager _prototypeManager = default!; + [Dependency] private readonly MetaDataSystem _metaData = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + + /// + /// Flavors that are masked by the candy. + /// + private static readonly string[] MaskedReagents = { "Sugar", "Iron" }; // sugar is obvious and iron is "metallic" :( + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + } + + private void OnInit(EntityUid uid, RandomizedCandyComponent candyComp, MapInitEvent args) + { + // pick a random flavor + var flavors = _prototypeManager.EnumeratePrototypes(); + var candyFlavor = _random.Pick(flavors.ToList()); + + // color the candy :3 + _appearance.SetData(uid, RandomizedCandyVisuals.Color, candyFlavor.Color); + + // flavor the candy! yummy + var flavorProfile = EnsureComp(uid); + flavorProfile.Flavors.Clear(); // it shouldn't be flavored but clear it anyway + foreach (var flavorId in candyFlavor.Flavors) + { + flavorProfile.Flavors.Add(flavorId); + } + flavorProfile.IgnoreReagents.UnionWith(MaskedReagents); // otherwise the nom text gets too long + + // update the candy's metadata with fluff + var meta = MetaData(uid); + if (!string.IsNullOrEmpty(candyFlavor.Name)) + _metaData.SetEntityName(uid, $"{candyFlavor.Name} {meta.EntityName}", meta); + _metaData.SetEntityDescription(uid, $"{meta.EntityDescription} {GetExamineFluff(candyFlavor.Flavors)}"); + Dirty(uid, meta); + } + + // this technically duplicates code from FlavorProfileSystem but what we would need to call + // is upstream code in a private method with fixed loc strings and unnecessary sorting, so i don't want to touch it + private string GetExamineFluff(HashSet> flavorIds) + { + var flavors = new List(); + foreach (var flavorId in flavorIds) + { + if (_prototypeManager.TryIndex(flavorId, out var flavor) && + Loc.TryGetString(flavor.FlavorDescription, out var flavorText)) + { + flavors.Add(flavorText); + } + } + + return flavors.Count switch + { + > 1 => Loc.GetString("candy-flavor-profile-multiple", + ("lastFlavor", flavors.Pop()), + ("flavors", string.Join(", ", flavors)) + ), + 1 => Loc.GetString("candy-flavor-profile", ("flavor", flavors.Single())), + _ => Loc.GetString("candy-flavor-profile-unknown") + }; + } +} diff --git a/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandyComponent.cs b/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandyComponent.cs index d0a399c1fea..36546359829 100644 --- a/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandyComponent.cs +++ b/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandyComponent.cs @@ -1,11 +1,25 @@ +using Robust.Shared.Audio; + namespace Content.Server.Abilities.Borgs; [RegisterComponent] public sealed partial class FabricateCandyComponent : Component { - [DataField("lollipopAction")] + [DataField] public EntityUid? LollipopAction; - [DataField("gumballAction")] + [DataField] public EntityUid? GumballAction; + + /// + /// The sound played when fabricating candy. + /// + [DataField] + public SoundSpecifier FabricationSound = new SoundPathSpecifier("/Audio/Machines/machine_vend.ogg") + { + Params = new AudioParams + { + Volume = -2f + } + }; } diff --git a/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandySystem.cs b/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandySystem.cs index 6451c7cbb27..b9c7540d750 100644 --- a/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandySystem.cs +++ b/Content.Server/Nyanotrasen/Abilities/Borgs/FabricateCandySystem.cs @@ -1,3 +1,5 @@ +using Robust.Server.Audio; +using Robust.Shared.Prototypes; using Content.Shared.Actions; using Content.Shared.Actions.Events; @@ -6,6 +8,8 @@ namespace Content.Server.Abilities.Borgs; public sealed partial class FabricateCandySystem : EntitySystem { [Dependency] private readonly SharedActionsSystem _actionsSystem = default!; + [Dependency] private readonly AudioSystem _audioSystem = default!; + public override void Initialize() { base.Initialize(); @@ -25,13 +29,19 @@ private void OnInit(EntityUid uid, FabricateCandyComponent component, ComponentI private void OnLollipop(FabricateLollipopActionEvent args) { - Spawn("FoodLollipop", Transform(args.Performer).Coordinates); - args.Handled = true; + OnCandy("FoodLollipop", args); } private void OnGumball(FabricateGumballActionEvent args) { - Spawn("FoodGumball", Transform(args.Performer).Coordinates); - args.Handled = true; + OnCandy("FoodGumball", args); + } + + private void OnCandy(EntProtoId proto, BaseActionEvent evt) + { + Spawn(proto, Transform(evt.Performer).Coordinates); + if (TryComp(evt.Performer, out FabricateCandyComponent? comp)) + _audioSystem.PlayPvs(comp.FabricationSound, evt.Performer); + evt.Handled = true; } } diff --git a/Content.Shared/DeltaV/Abilities/Borgs/RandomizedCandyComponent.cs b/Content.Shared/DeltaV/Abilities/Borgs/RandomizedCandyComponent.cs new file mode 100644 index 00000000000..57719760ee3 --- /dev/null +++ b/Content.Shared/DeltaV/Abilities/Borgs/RandomizedCandyComponent.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.DeltaV.Abilities.Borgs; + +/// +/// Marks this entity as being candy with a random flavor and color. +/// +[RegisterComponent, NetworkedComponent] +public sealed partial class RandomizedCandyComponent : Component +{ +} + +[Serializable, NetSerializable] +public enum RandomizedCandyVisuals : byte +{ + Color +} diff --git a/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl b/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl index 98e04f2818d..48f42e641b7 100644 --- a/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl +++ b/Resources/Locale/en-US/deltav/flavors/flavor-profiles.ftl @@ -27,3 +27,10 @@ flavor-complex-daiquiri = fashionable flavor-complex-arsonistsbrew = like ash and flame flavor-complex-healthcodeviolation = ominous flavor-complex-pumpkin = like pumpkin +flavor-complex-blellow = like an impossible color +flavor-complex-candy-strawberry = like strawberries +flavor-complex-candy-bubblegum = like bubble gum + +candy-flavor-profile = This one is supposed to taste {$flavor}. +candy-flavor-profile-multiple = This one is supposed to taste {$flavors} and {$lastFlavor}. +candy-flavor-profile-unknown = You have no idea what this one is supposed to taste like. diff --git a/Resources/Prototypes/DeltaV/Flavors/candyflavors.yml b/Resources/Prototypes/DeltaV/Flavors/candyflavors.yml new file mode 100644 index 00000000000..66f4bb0af44 --- /dev/null +++ b/Resources/Prototypes/DeltaV/Flavors/candyflavors.yml @@ -0,0 +1,236 @@ +# Flavors for lollipops and gumballs + +- type: candyFlavor + id: berry + name: berry + color: '#800080' + flavors: + - berry + +- type: candyFlavor + id: ginger + name: ginger + color: '#fbdba0' + flavors: + - gingersoda + +- type: candyFlavor + id: bananashake + name: banana shake + color: '#ffe135' + flavors: + - bananahonk + +- type: candyFlavor + id: vanilla + name: vanilla + color: '#f9e5bc' + flavors: + - creamy + - vanilla + +- type: candyFlavor + id: orange + name: orange + color: '#fa7000' + flavors: + - orange + +- type: candyFlavor + id: watermelon + name: watermelon + color: '#009345' + flavors: + - watermelon + +- type: candyFlavor + id: pineapple + name: pineapple + color: '#e6ae25' + flavors: + - pineapple + +- type: candyFlavor + id: rootbeer + name: root beer + color: '#290e05' + flavors: + - rootbeersoda + +- type: candyFlavor + id: lemon + name: lemon + color: '#fef250' + flavors: + - lemondrop + +- type: candyFlavor + id: cola + name: cola + color: '#3c3024' + flavors: + - cola + +- type: candyFlavor + id: icedtea + name: iced tea + color: '#e0a779' + flavors: + - icedtea + +- type: candyFlavor + id: peppermint + name: peppermint + color: '#47e7ad' + flavors: + - minty + +- type: candyFlavor + id: spearmint + name: spearmint + color: '#47e75b' + flavors: + - minty + +- type: candyFlavor + id: apple + name: apple + color: '#dd1533' + flavors: + - apple + +- type: candyFlavor + id: honey + name: honey + color: '#e79a3f' + flavors: + - honey + +- type: candyFlavor + id: bungo + name: bungo + color: '#ffe017' + flavors: + - bungo + +- type: candyFlavor + id: grape + name: grape + color: '#6c3461' + flavors: + - grape + +- type: candyFlavor + id: bubbletea + name: bubble tea + color: '#d2b18c' + flavors: + - bubbletea + +- type: candyFlavor + id: mystery + name: mystery flavor + color: '#202020' + flavors: + - spaceshroom # it's a "mysterious" flavor + +- type: candyFlavor + id: creamystrawberry + name: creamy strawberry + color: '#f8d9df' + flavors: + - creamy + - candystrawberry + +- type: candyFlavor + id: strawberryyogurt + name: strawberry yogurt + color: '#f8d9df' + flavors: + - sour + - candystrawberry + - milk + +- type: candyFlavor + id: sourapple + name: sour apple # mouth-puckering! + color: '#68aa2e' + flavors: + - sour + - apple + +- type: candyFlavor + id: bubblegum + name: bubble gum + color: '#f988d9' + flavors: + - candybubblegum + +- type: candyFlavor + id: chilibungo + name: chili bungo # don't ask, i found chili mango lollipops on google, and bungos supposedly taste similar to mango + color: '#813c23' + flavors: + - spicy + - bungo + +- type: candyFlavor + id: tequilasunrise + name: tequila sunrise + color: '#ffe48c' + flavors: + - tequilasunrise + +- type: candyFlavor + id: mime + name: mime + color: '#eeeeee' + flavors: + - nothing + +- type: candyFlavor + id: honeylemonginger + name: honey lemon ginger + color: '#e6d781' + flavors: + - sour + - honey + - gingersoda + +# weird flavors below! + +- type: candyFlavor + id: foof + name: foof + color: '#ff00ff' + flavors: + - pinkdrink + +- type: candyFlavor + id: feline + name: cat-flavored + color: '#7c4511' + flavors: + - cat + +- type: candyFlavor + id: blellow + name: blellow + color: '#737373' + flavors: + - blellow + +- type: candyFlavor + id: stale + name: stale + color: '#96818e' + flavors: + - terrible + - sadness + +- type: candyFlavor + id: weh + name: weh # sorry, doesn't actually make you weh! + color: '#59b23a' + flavors: + - weh diff --git a/Resources/Prototypes/DeltaV/Flavors/flavors.yml b/Resources/Prototypes/DeltaV/Flavors/flavors.yml index 382868db630..b76b0dccd99 100644 --- a/Resources/Prototypes/DeltaV/Flavors/flavors.yml +++ b/Resources/Prototypes/DeltaV/Flavors/flavors.yml @@ -115,3 +115,19 @@ id: arsonistsbrew flavorType: Complex description: flavor-complex-arsonistsbrew + +- type: flavor + id: blellow + flavorType: Complex + description: flavor-complex-blellow + +# this is prefixed with "candy" to avoid clashes with potential future strawberries upstream +- type: flavor + id: candystrawberry + flavorType: Complex + description: flavor-complex-candy-strawberry + +- type: flavor + id: candybubblegum + flavorType: Complex + description: flavor-complex-candy-bubblegum diff --git a/Resources/Prototypes/Nyanotrasen/Actions/types.yml b/Resources/Prototypes/Nyanotrasen/Actions/types.yml index e6e4bdc5a75..b089568f419 100644 --- a/Resources/Prototypes/Nyanotrasen/Actions/types.yml +++ b/Resources/Prototypes/Nyanotrasen/Actions/types.yml @@ -175,5 +175,6 @@ components: - type: InstantAction icon: { sprite: Nyanotrasen/Objects/Consumable/Food/candy.rsi, state: gumball } + iconColor: '#FFAED7' useDelay: 40 event: !type:FabricateGumballActionEvent diff --git a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Consumable/Food/candy.yml b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Consumable/Food/candy.yml index 11eb1fac1c5..971748013b3 100644 --- a/Resources/Prototypes/Nyanotrasen/Entities/Objects/Consumable/Food/candy.yml +++ b/Resources/Prototypes/Nyanotrasen/Entities/Objects/Consumable/Food/candy.yml @@ -2,7 +2,7 @@ parent: FoodBase id: FoodLollipop name: lollipop - description: For being such a good sport. + description: For being such a good sport! It's enriched with potent yet mostly safe-to-eat medicine. components: - type: SolutionContainerManager solutions: @@ -21,13 +21,23 @@ Quantity: 3 - type: Sprite sprite: Nyanotrasen/Objects/Consumable/Food/candy.rsi - state: lollipop + layers: + - state: lollipop-ball + map: [ "enum.CandyVisualLayers.Ball" ] + - state: lollipop-stickandshine + - type: Clothing + sprite: Nyanotrasen/Objects/Consumable/Food/candy.rsi + slots: [ mask ] + equippedPrefix: lollipop + quickEquip: false # would block eating otherwise + - type: Appearance + - type: RandomizedCandy - type: entity parent: FoodBase id: FoodGumball name: gumball - description: For being such a good sport. + description: Try as you might, you can't blow bubbles with it... it's enriched with medicine for minor ailments. components: - type: SolutionContainerManager solutions: @@ -44,4 +54,9 @@ Quantity: 3 - type: Sprite sprite: Nyanotrasen/Objects/Consumable/Food/candy.rsi - state: gumball + layers: + - state: gumball + map: [ "enum.CandyVisualLayers.Ball" ] + - state: gumball-shine + - type: Appearance + - type: RandomizedCandy diff --git a/Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/gumball-shine.png b/Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/gumball-shine.png new file mode 100644 index 0000000000000000000000000000000000000000..b9251d9778725619676880491ed1ec6936477fb0 GIT binary patch literal 97 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdz4Nn)xkch)?FBu9lFmNy%{+Zpz uDzl+Lg>9d+GQ)#aIsYFSA7E!_&}9u=BK%f+@!vN!lvI6;>1s;*b3=DjSL74G){)!Z!phSslL`iUdT1k0gQ7S`0VrE{6US4X6f{C7i zp4p$Czeg;ohKbLh*2~7Y^TX7x$ diff --git a/Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-ball.png b/Resources/Textures/Nyanotrasen/Objects/Consumable/Food/candy.rsi/lollipop-ball.png new file mode 100644 index 0000000000000000000000000000000000000000..61eb3ad0c7da7101752ed8459abd40ebb92e0034 GIT binary patch literal 135 zcmeAS@N?(olHy`uVBq!ia0vp^3LwnE1|*BCs=fdzKTj9Okch)?FIjUj7;?B?Ot1RB z-#^wNh}Xx^l}Y&1!+Aj;nza}Y@V@ePxiT^4_u`-FiJRBmO8xrnoVK)|MK@A