Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
880689c
Added Flip and mothroach ability Wing dash
Jul 11, 2026
48036c1
added EatToGrow
Jul 12, 2026
985b1ee
added boomroach and syndiroach
Jul 14, 2026
c47a912
added cecropia_mothroach leopard_mothroach lunar_mothroach edible mus…
Jul 15, 2026
61ab31b
added boomroach booms!
Jul 15, 2026
7df2d68
added explosion resistance to boomroach and increase hp/resistances
Jul 15, 2026
7c91999
added Syndiroach - PDV aligned mothroach, can be aquired from uplink,…
Jul 15, 2026
314a1dc
pets including mothroach can now smoke. rejoice
Jul 15, 2026
7cf6db7
added - PetWearable to most neck/mask/head/eyes
Jul 16, 2026
2ab44b2
fixed shitmed replacing animal heart would make them unrevivable
Jul 16, 2026
01819b9
appeasing the yaml linter
Jul 16, 2026
355573b
yaml linter fix attempt 2
Jul 16, 2026
8b8bd30
added animal feet
Jul 17, 2026
ac05ba1
yaml linter fix 3?
Jul 17, 2026
09001ed
yaml linter attempt 4
Jul 17, 2026
1c72f4c
refined shitme amendments
Jul 17, 2026
e884bf4
Merge branch 'Monolith-Station:main' into MonolithMothroachrebalance
lordsej Jul 19, 2026
8c40291
Merge branch 'main' into MonolithMothroachrebalance
lordsej Jul 28, 2026
91ef241
added in the OwOroach for TSF
Jul 29, 2026
3b97a49
Capitalised OwOroach name for maximum OwO factor
Jul 29, 2026
a2b8d6d
more OwO capitalization for burger
Jul 29, 2026
9847f66
Merge branch 'main' into MonolithMothroachrebalance
lordsej Jul 29, 2026
88b1044
removed acidental double var vec from recent and fixed errors caused …
Jul 29, 2026
d3622db
Merge branch 'Monolith-Station:main' into MonolithMothroachrebalance
lordsej Aug 3, 2026
b448fe8
SyndiRoach has now been rebranded ImperiRoach and removed references …
Aug 3, 2026
a6382c1
Merge branch 'Monolith-Station:main' into MonolithMothroachrebalance
lordsej Aug 3, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Content.Server/Nutrition/EntitySystems/FoodSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
using Content.Shared.Stacks;
using Content.Shared.Storage;
using Content.Shared.Verbs;
using Content.Shared._Goobstation.EatToGrow;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Utility;
Expand Down Expand Up @@ -284,6 +285,8 @@ private void OnDoAfter(Entity<FoodComponent> entity, ref ConsumeDoAfterEvent arg
_reaction.DoEntityReaction(args.Target.Value, solution, ReactionMethod.Ingestion);
_stomach.TryTransferSolution(stomachToUse!.Value.Owner, split, stomachToUse);

var afterEatingEv = new AfterEatingEvent(entity.Owner);
RaiseLocalEvent(args.Target.Value, ref afterEatingEv);
var flavors = args.FlavorMessage;

if (forceFeed)
Expand Down
33 changes: 33 additions & 0 deletions Content.Server/_Goobstation/Dash/DashActionChatSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using Content.Server.Chat.Systems;
using Content.Shared._Goobstation.Dash;
using Content.Shared.Chat.Prototypes;
using Robust.Shared.Prototypes;

namespace Content.Server._Goobstation.Dash;

public sealed class DashActionChatSystem : EntitySystem
{
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<DashActionEvent>(OnDash);
}

private void OnDash(DashActionEvent args)
{
if (args.Emote == null)
return;

if (!_prototype.TryIndex<EmotePrototype>(args.Emote.Value, out var emote))
return;

_chat.TryEmoteWithChat(
args.Performer,
emote,
forceEmote: true);
}
}
81 changes: 81 additions & 0 deletions Content.Server/_Goobstation/EatToGrow/EatToGrowSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using Content.Shared._Goobstation.EatToGrow;
using Content.Shared.Mobs;
using Robust.Server.GameObjects;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Collision.Shapes;
using Robust.Shared.Physics.Systems;
using System.Numerics;
using Content.Shared.Sprite;

namespace Content.Server._Goobstation.EatToGrow;


public sealed class EatToGrowSystem : EntitySystem
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SharedPhysicsSystem _physics = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<EatToGrowComponent, AfterEatingEvent>(OnFoodEaten);
SubscribeLocalEvent<EatToGrowComponent, MobStateChangedEvent>(ShrinkOnDeath);
}

private void OnFoodEaten(Entity<EatToGrowComponent> ent, ref AfterEatingEvent args)
{
Grow(ent.Owner, ent.Comp, 1);
}

private void Grow(EntityUid eater, EatToGrowComponent comp, float scale)
{
// if growing would go over the limit, return
if (comp.CurrentScale >= comp.MaxGrowth)
return;
// Uses scale variable to multiply the growth, mainly used for shrinking
// Add growth
comp.CurrentScale += comp.Growth;
comp.CurrentScale = MathF.Min(comp.CurrentScale, comp.MaxGrowth);

EnsureComp<ScaleVisualsComponent>(eater);

var appearanceComponent = EnsureComp<AppearanceComponent>(eater);
if (!_appearance.TryGetData<Vector2>(eater, ScaleVisuals.Scale, out var oldScale, appearanceComponent))
oldScale = Vector2.One;

_appearance.SetData(eater, ScaleVisuals.Scale, oldScale + scale * new Vector2(comp.Growth, comp.Growth), appearanceComponent);

Dirty(eater, comp); // Sync updated growth to client.

// add 1 to times grown
comp.TimesGrown += 1;

// Grow the fixture by 1/4 the growth
if (TryComp(eater, out FixturesComponent? manager))
{
foreach (var (id, fixture) in manager.Fixtures)
{
if (fixture.Shape is PhysShapeCircle circle)
{
_physics.SetPositionRadius(
eater, id, fixture, circle,
circle.Position, circle.Radius + scale * (comp.Growth / 4), manager);
}
}
}
}
private void ShrinkOnDeath(Entity<EatToGrowComponent> eater, ref MobStateChangedEvent args)
{
// Copied from TryGrow, just need to grow in reverse
if (args.NewMobState != MobState.Dead || !TryComp<EatToGrowComponent>(eater, out var comp) || comp.ShrinkOnDeath == false)
return;

// shrink the entity
Grow(eater, comp, -comp.TimesGrown); // uses the negative of times grown to shrink the entity back to normal

// Reset data on shrink
comp.CurrentScale = 1f;
comp.TimesGrown = 0;
}
};
13 changes: 13 additions & 0 deletions Content.Server/_Goobstation/Ingestion/GoobEatingEvents.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Content.Server._Goobstation.Ingestion;

/// <summary>
/// Raised directed at the eater after finishing eating the food before it's deleted.
/// </summary>
[ByRefEvent]
public readonly record struct AfterEatingEvent(EntityUid Food)
{
/// <summary>
/// The UID of the food entity that was fully eaten.
/// </summary>
public readonly EntityUid Food = Food;
}
17 changes: 10 additions & 7 deletions Content.Shared/Clothing/EntitySystems/HideLayerClothingSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ private void OnHideGotUnequipped(Entity<HideLayerClothingComponent> ent, ref Clo

private void SetLayerVisibility(
Entity<HideLayerClothingComponent?, ClothingComponent?> clothing,
Entity<HumanoidAppearanceComponent?> user,
EntityUid user,
bool hideLayers)
{
if (_timing.ApplyingState)
Expand All @@ -45,12 +45,15 @@ private void SetLayerVisibility(
if (!Resolve(clothing.Owner, ref clothing.Comp1, ref clothing.Comp2))
return;

if (!Resolve(user.Owner, ref user.Comp))
if (!TryComp<HumanoidAppearanceComponent>(user, out var humanoid))
return;

var humanoidEntity = new Entity<HumanoidAppearanceComponent>(user, humanoid);

hideLayers &= IsEnabled(clothing!);
hideLayers &= IsEnabled(new Entity<HideLayerClothingComponent, ClothingComponent>( clothing.Owner, clothing.Comp1!, clothing.Comp2! ));

var hideable = user.Comp.HideLayersOnEquip;
var hideable = humanoid.HideLayersOnEquip;

var inSlot = clothing.Comp2.InSlotFlag ?? SlotFlags.NONE;

// This method should only be getting called while the clothing is equipped (though possibly currently in
Expand All @@ -70,7 +73,7 @@ private void SetLayerVisibility(

// Only update this layer if we are currently equipped to the relevant slot.
if (validSlots.HasFlag(inSlot))
_humanoid.SetLayerVisibility(user!, layer, !hideLayers, inSlot, ref dirty);
_humanoid.SetLayerVisibility(humanoidEntity, layer, !hideLayers, inSlot, ref dirty);
}

// Fallback for obsolete field: assume we want to hide **all** layers, as long as we are equipped to any
Expand All @@ -82,12 +85,12 @@ private void SetLayerVisibility(
foreach (var layer in slots)
{
if (hideable.Contains(layer))
_humanoid.SetLayerVisibility(user!, layer, !hideLayers, inSlot, ref dirty);
_humanoid.SetLayerVisibility(humanoidEntity, layer, !hideLayers, inSlot, ref dirty);
}
}

if (dirty)
Dirty(user!);
Dirty(user, humanoid);
}

private bool IsEnabled(Entity<HideLayerClothingComponent, ClothingComponent> clothing)
Expand Down
1 change: 1 addition & 0 deletions Content.Shared/Nutrition/EntitySystems/IngestionSystem.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
using Content.Shared._Goobstation.EatToGrow;
11 changes: 11 additions & 0 deletions Content.Shared/_Goobstation/Dash/DashActionComponent.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

using Content.Shared.Actions;
using Content.Shared.Chat.Prototypes;
using Robust.Shared.Prototypes;
Expand All @@ -22,6 +24,9 @@ public sealed partial class DashActionEvent : WorldTargetActionEvent
[DataField]
public float Speed = 9.65f;

[DataField]
public float? StaminaDrain;

/// <summary>
/// Whether you need gravity to perform the dash. Keep in mind there's no friction without gravity so if this
/// is false, the performer gets every chance to be launched straight to Ohio on dashing without gravity.
Expand All @@ -34,4 +39,10 @@ public sealed partial class DashActionEvent : WorldTargetActionEvent
/// </summary>
[DataField]
public bool AffectedBySpeed = true;

/// <summary>
/// Animated emote to play on successful dash.
/// </summary>
[DataField]
public ProtoId<EmotePrototype>? Emote = "Flip";
}
22 changes: 18 additions & 4 deletions Content.Shared/_Goobstation/Dash/DashActionSystem.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
// SPDX-License-Identifier: AGPL-3.0-or-later

using Content.Shared.Emoting;
using Content.Shared.Actions;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Systems;
using Content.Shared.Gravity;
using Content.Shared.Movement.Components;
using Content.Shared.Throwing;
Expand All @@ -11,6 +17,7 @@ public sealed class DashActionSystem : EntitySystem
[Dependency] private readonly SharedGravitySystem _gravity = default!;
[Dependency] private readonly ThrowingSystem _throwing = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly StaminaSystem _stamina = default!;

public override void Initialize()
{
Expand All @@ -30,9 +37,9 @@ private void OnDashAction(DashActionEvent args)
if (args.NeedsGravity && _gravity.IsWeightless(args.Performer))
return;

args.Handled = true;
var vec = (_transform.ToMapCoordinates(args.Target).Position -
_transform.GetMapCoordinates(args.Performer).Position).Normalized() *
args.Distance;
_transform.GetMapCoordinates(args.Performer).Position).Normalized() * args.Distance;

var speed = args.Speed;

Expand All @@ -42,9 +49,16 @@ private void OnDashAction(DashActionEvent args)
speed *= speedcomp.CurrentSprintSpeed / speedcomp.BaseSprintSpeed;
}

_throwing.TryThrow(args.Performer, vec, speed, null, 0, null, false, false, false);
_throwing.TryThrow( args.Performer, vec, baseThrowSpeed: speed, user: null, pushbackRatio: 0, friction: null, compensateFriction: false, recoil: false, animated: false);

args.Handled = true;
if (args.StaminaDrain != null)
_stamina.TakeStaminaDamage(args.Performer, args.StaminaDrain.Value, visual: false, immediate: false);

if (args.Emote != null && TryComp<AnimatedEmotesComponent>(args.Performer, out var emotes))
{
emotes.Emote = args.Emote;
Dirty(args.Performer, emotes);
}
}

private void OnComponentInit(EntityUid uid, DashActionComponent comp, ref ComponentInit args)
Expand Down
8 changes: 8 additions & 0 deletions Content.Shared/_Goobstation/EatToGrow/AfterEatingEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
using Robust.Shared.GameObjects;

namespace Content.Shared._Goobstation.EatToGrow;
/// <summary>
/// Raised directed at the eater after finishing eating the food before it is deleted.
/// </summary>
[ByRefEvent]
public readonly record struct AfterEatingEvent(EntityUid Food);
22 changes: 22 additions & 0 deletions Content.Shared/_Goobstation/EatToGrow/EatToGrowComponent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using Content.Shared.Damage;
using Robust.Shared.GameStates;

namespace Content.Shared._Goobstation.EatToGrow;
[RegisterComponent, NetworkedComponent]
public sealed partial class EatToGrowComponent : Component
{
[DataField]
public float Growth = 0.1f; // percentage growth

[DataField]
public float MaxGrowth = 5.0f; // max allowed scale multiplier

[DataField]
public float CurrentScale = 1.0f; // current scale

[DataField]
public bool ShrinkOnDeath = true; // Revert to original size on death?

[DataField]
public int TimesGrown = 0; // how many times have they grown?
}
23 changes: 23 additions & 0 deletions Content.Shared/_Goobstation/EatToGrow/EatToGrowComponentState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;

namespace Content.Shared._Goobstation.EatToGrow;

[Serializable, NetSerializable]
public sealed class EatToGrowComponentState : ComponentState
{
public readonly float Growth;
public readonly float MaxGrowth;

public readonly float CurrentScale;
public readonly bool ShrinkOnDeath;
public readonly int TimesGrown;
public EatToGrowComponentState(float growth, float maxGrowth, float currentScale, bool shrinkOnDeath, int timesGrown)
{
Growth = growth;
MaxGrowth = maxGrowth;
CurrentScale = currentScale;
ShrinkOnDeath = shrinkOnDeath;
TimesGrown = timesGrown;
}
}
6 changes: 6 additions & 0 deletions Resources/Audio/_Goobstation/Effects/attributions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

- files: ["moth_wings.ogg"]
license: "CC0-1.0"
copyright: "Original by tothrec2"
source: "https://freesound.org/people/tothrec2/sounds/596541/"
Binary file not shown.
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/_Goobstation/emotes.ftl
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
chat-emote-name-spin = Spin
chat-emote-name-jump = Jump
chat-emote-name-flip = Do a flip
chat-emote-msg-spin = spins!
chat-emote-msg-jump = jumps!
chat-emote-msg-flip = does a flip!


# Names
chat-emote-name-trill = Trill
Expand Down
3 changes: 3 additions & 0 deletions Resources/Locale/en-US/_Mono/store/pdv-uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ uplink-pirate-c4-desc = Use it to breach walls, dispose of bodies, break equipme
uplink-pirate-c4-bundle-name = C-4 Bundle
uplink-pirate-c4-bundle-desc = Enough C-4 to blow your way into a vault and out through the back.

uplink-pirate-MobImperiroach-name = ImperiRoach
uplink-pirate-MobImperiroach-desc = Call in a handy ImperiRoach equipped with a microbomb implant. Explodes when seriously injured. Can use harsh language and upset feelings.

uplink-pirate-empgrenade-box-name = EMP Grenade Box
uplink-pirate-empgrenade-box-desc = A box containing 4 EMP grenades.

Expand Down
4 changes: 4 additions & 0 deletions Resources/Locale/en-US/_NF/store/security-uplink-catalog.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -162,3 +162,7 @@ uplink-security-contraband-forensics-module-name = Contraband Forenics Module
uplink-security-contraband-forensics-module-desc = A program for scanning and reporting contraband dead drops and pods for bounties. Slots into a forensic scanner.
uplink-security-mechpulserifle-name = CL-94 Pulse Emitter
uplink-security-mechpulserifle-desc = A mech-mounted Pulse Rifle.
uplink-security-implanter-tsf-name = TSF Radio Implanter
uplink-security-implanter-tsf-desc = Implants a Tsf radio implant, allowing covert communication without a headset.
uplink-security-MobOwORoach-name = OwORoach Reninforcement Radio
uplink-security-MobOwORoach-desc = Your very own OwORoach friend sent by High Command for dubious and unknown reasons, equipped with a microbomb implant. Explodes when seriously injured. Can use harsh language and upset feelings.
Loading
Loading