diff --git a/Content.Server/Geras/GerasComponent.cs b/Content.Server/Geras/GerasComponent.cs new file mode 100644 index 0000000000..eaf792502f --- /dev/null +++ b/Content.Server/Geras/GerasComponent.cs @@ -0,0 +1,18 @@ +using Content.Shared.Actions; +using Content.Shared.Polymorph; +using Robust.Shared.Prototypes; + +namespace Content.Server.Geras; + +/// +/// This component assigns the entity with a polymorph action. +/// +[RegisterComponent] +public sealed partial class GerasComponent : Component +{ + [DataField] public ProtoId GerasPolymorphId = "SlimeMorphGeras"; + + [DataField] public ProtoId GerasAction = "ActionMorphGeras"; + + [DataField] public EntityUid? GerasActionEntity; +} diff --git a/Content.Server/Geras/GerasSystem.cs b/Content.Server/Geras/GerasSystem.cs new file mode 100644 index 0000000000..a5377a021e --- /dev/null +++ b/Content.Server/Geras/GerasSystem.cs @@ -0,0 +1,40 @@ +using Content.Server.Actions; +using Content.Server.Polymorph.Systems; +using Content.Server.Popups; +using Content.Shared.Actions; +using Content.Shared.Geras; +using Robust.Shared.Player; + +namespace Content.Server.Geras; + +/// +public sealed class GerasSystem : SharedGerasSystem +{ + [Dependency] private readonly ActionsSystem _actionsSystem = default!; + [Dependency] private readonly PolymorphSystem _polymorphSystem = default!; + [Dependency] private readonly PopupSystem _popupSystem = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMorphIntoGeras); + SubscribeLocalEvent(OnMapInit); + } + + private void OnMapInit(EntityUid uid, GerasComponent component, MapInitEvent args) + { + // try to add geras action + _actionsSystem.AddAction(uid, ref component.GerasActionEntity, component.GerasAction); + } + + private void OnMorphIntoGeras(EntityUid uid, GerasComponent component, MorphIntoGeras args) + { + var ent = _polymorphSystem.PolymorphEntity(uid, component.GerasPolymorphId); + + if (!ent.HasValue) + return; + + _popupSystem.PopupEntity(Loc.GetString("geras-popup-morph-message-others", ("entity", ent.Value)), ent.Value, Filter.PvsExcept(ent.Value), true); + _popupSystem.PopupEntity(Loc.GetString("geras-popup-morph-message-user"), ent.Value, ent.Value); + } +} diff --git a/Content.Server/Storage/EntitySystems/StorageSystem.cs b/Content.Server/Storage/EntitySystems/StorageSystem.cs index 27694ee61c..5d41e0a521 100644 --- a/Content.Server/Storage/EntitySystems/StorageSystem.cs +++ b/Content.Server/Storage/EntitySystems/StorageSystem.cs @@ -76,13 +76,13 @@ public sealed partial class StorageSystem : SharedStorageSystem }; if (uiOpen) { - verb.Text = Loc.GetString("verb-common-close-ui"); + verb.Text = Loc.GetString("comp-storage-verb-close-storage"); verb.Icon = new SpriteSpecifier.Texture( new("/Textures/Interface/VerbIcons/close.svg.192dpi.png")); } else { - verb.Text = Loc.GetString("verb-common-open-ui"); + verb.Text = Loc.GetString("comp-storage-verb-open-storage"); verb.Icon = new SpriteSpecifier.Texture( new("/Textures/Interface/VerbIcons/open.svg.192dpi.png")); } diff --git a/Content.Shared/Geras/SharedGerasSystem.cs b/Content.Shared/Geras/SharedGerasSystem.cs new file mode 100644 index 0000000000..f5dea466a2 --- /dev/null +++ b/Content.Shared/Geras/SharedGerasSystem.cs @@ -0,0 +1,16 @@ +using Content.Shared.Actions; + +namespace Content.Shared.Geras; + +/// +/// A Geras is the small morph of a slime. This system handles exactly that. +/// +public abstract class SharedGerasSystem : EntitySystem +{ + +} + +public sealed partial class MorphIntoGeras : InstantActionEvent +{ + +} diff --git a/Resources/Locale/en-US/components/storage-component.ftl b/Resources/Locale/en-US/components/storage-component.ftl index 29c858891a..e742c83f6a 100644 --- a/Resources/Locale/en-US/components/storage-component.ftl +++ b/Resources/Locale/en-US/components/storage-component.ftl @@ -8,3 +8,5 @@ comp-storage-cant-drop = You can't let go of { THE($entity) }! comp-storage-window-title = Storage Item comp-storage-window-weight = { $weight }/{ $maxWeight }, Max Size: {$size} comp-storage-window-slots = Slots: { $itemCount }/{ $maxCount }, Max Size: {$size} +comp-storage-verb-open-storage = Open Storage +comp-storage-verb-close-storage = Close Storage diff --git a/Resources/Locale/en-US/geras/geras.ftl b/Resources/Locale/en-US/geras/geras.ftl new file mode 100644 index 0000000000..3cd3f101ff --- /dev/null +++ b/Resources/Locale/en-US/geras/geras.ftl @@ -0,0 +1,2 @@ +geras-popup-morph-message-user = You shift and morph into a small version of you! +geras-popup-morph-message-others = {CAPITALIZE(THE($entity))} shifts and morphs into a blob of slime! diff --git a/Resources/Prototypes/Actions/types.yml b/Resources/Prototypes/Actions/types.yml index b91b26e357..aeffd5b8c5 100644 --- a/Resources/Prototypes/Actions/types.yml +++ b/Resources/Prototypes/Actions/types.yml @@ -92,7 +92,6 @@ state: gib event: !type:ActivateImplantEvent - - type: entity id: ActionActivateFreedomImplant name: Break Free @@ -171,6 +170,22 @@ state: icon event: !type:UseDnaScramblerImplantEvent +- type: entity + id: ActionMorphGeras + name: Morph into Geras + description: Morphs you into a Geras - a miniature version of you which allows you to move fast, at the cost of your inventory. + noSpawn: true + components: + - type: InstantAction + charges: 1 + itemIconStyle: BigAction + useDelay: 10 # prevent spam + priority: -20 + icon: + sprite: Mobs/Aliens/slimes.rsi + state: blue_adult_slime + event: !type:MorphIntoGeras + - type: entity id: ActionToggleSuitPiece name: Toggle Suit Piece diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml index 10e9218d6e..95c30a174f 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/slimes.yml @@ -1,16 +1,10 @@ - type: entity name: basic slime - id: MobAdultSlimes + id: BaseMobAdultSlimes parent: [ SimpleMobBase, MobCombat ] abstract: true description: It looks so much like jelly. I wonder what it tastes like? components: - - type: NpcFactionMember - factions: - - SimpleNeutral - - type: HTN - rootTask: - task: SimpleHostileCompound - type: Sprite drawdepth: Mobs sprite: Mobs/Aliens/slimes.rsi @@ -111,6 +105,19 @@ successChance: 0.5 interactSuccessString: petting-success-slimes interactFailureString: petting-failure-generic + - type: Speech + speechVerb: Slime + speechSounds: Slime + - type: TypingIndicator + proto: slime + +- type: entity + name: basic slime + id: MobAdultSlimes + parent: BaseMobAdultSlimes + abstract: true + description: It looks so much like jelly. I wonder what it tastes like? + components: - type: ReplacementAccent accent: slimes - type: GhostTakeoverAvailable @@ -118,11 +125,37 @@ makeSentient: true name: ghost-role-information-slimes-name description: ghost-role-information-slimes-description - - type: Speech - speechVerb: Slime - speechSounds: Slime - - type: TypingIndicator - proto: slime + - type: NpcFactionMember + factions: + - SimpleNeutral + - type: HTN + rootTask: + task: SimpleHostileCompound + +- type: entity + name: geras + description: A geras of a slime - the name is ironic, isn't it? + id: MobSlimesGeras + parent: BaseMobAdultSlimes + noSpawn: true + components: + # they portable... + - type: MultiHandedItem + - type: Item + size: Huge + - type: Sprite + color: "#FFFFFF55" + - type: MeleeWeapon + attackRate: 2 + damage: + types: + Blunt: 4 + - type: DamageStateVisuals + states: + Alive: + Base: blue_adult_slime + Dead: + Base: blue_adult_slime_dead - type: entity name: blue slime diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index 3eabb7dc07..5599825a0c 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -12,6 +12,30 @@ - type: Body prototype: Slime requiredLegs: 2 + # they like eat it idk lol + - type: Storage + grid: + - 0,0,1,2 + maxItemSize: Large + storageInsertSound: + path: /Audio/Voice/Slime/slime_squish.ogg + - type: ContainerContainer + containers: + storagebase: !type:Container + ents: [] + - type: UserInterface + interfaces: + - key: enum.StorageUiKey.Key + type: StorageBoundUserInterface + - key: enum.VoiceMaskUIKey.Key + type: VoiceMaskBoundUserInterface + - key: enum.HumanoidMarkingModifierKey.Key + type: HumanoidMarkingModifierBoundUserInterface + - key: enum.StrippingUiKey.Key + type: StrippableBoundUserInterface + # to prevent bag open/honk spam + - type: UseDelay + delay: 0.5 - type: HumanoidAppearance species: SlimePerson - type: Speech @@ -27,6 +51,16 @@ - type: Damageable damageContainer: Biological damageModifierSet: Slime + - type: Geras + - type: PassiveDamage # Around 8 damage a minute healed + allowedStates: + - Alive + damageCap: 65 + damage: + types: + Heat: -0.14 + groups: + Brute: -0.14 - type: DamageVisuals damageOverlayGroups: Brute: diff --git a/Resources/Prototypes/Polymorphs/polymorph.yml b/Resources/Prototypes/Polymorphs/polymorph.yml index b4249f8a3e..ce89f41d37 100644 --- a/Resources/Prototypes/Polymorphs/polymorph.yml +++ b/Resources/Prototypes/Polymorphs/polymorph.yml @@ -75,6 +75,17 @@ inventory: Transfer revertOnDeath: true +- type: polymorph + id: SlimeMorphGeras + configuration: + entity: MobSlimesGeras + transferName: false + transferHumanoidAppearance: false + inventory: Drop + transferDamage: true + revertOnDeath: true + revertOnCrit: true + # this is a test for transferring some visual appearance stuff - type: polymorph id: TestHumanMorph diff --git a/Resources/ServerInfo/Guidebook/Mobs/SlimePerson.xml b/Resources/ServerInfo/Guidebook/Mobs/SlimePerson.xml index fc72c60dbf..0374d4cb95 100644 --- a/Resources/ServerInfo/Guidebook/Mobs/SlimePerson.xml +++ b/Resources/ServerInfo/Guidebook/Mobs/SlimePerson.xml @@ -10,7 +10,21 @@ They exhale nitrous oxide and are unaffected by it. Their body can process 6 reagents at the same time instead of just 2. - Their Slime "blood" can not be regenerated from Iron. Slime Blood is technically a source of + Slimepeople can morph into a [bold]"geras"[/bold] (an archaic slimefolk term), which is a smaller slime form that can [bold]pass through grilles[/bold], + but forces them to drop their inventory and held items. It's handy for a quick getaway. A geras is small enough to pick up (with two hands) + and fits in a duffelbag. + + + + + + Slimepeople have an [bold]internal 2x2 storage inventory[/bold] inside of their slime membrane. Anyone can see what's inside and take it out of you without asking, + so be careful. They [bold]don't drop their internal storage when they morph into a geras, however![/bold] + + Slimepeople have slight accelerated regeneration compared to other humanoids. They're also capable of hardening their fists, and as such have stronger punches, + although they punch a little slower. + + Their slime "blood" can not be regenerated from Iron. Slime Blood is technically a source of moderately filling food for other species, although drinking the blood of your coworkers is usually frowned upon. They suffocate 80% slower, but take pressure damage 9% faster. This makes them by far the species most capable to survive in hard vacuum. For a while.