diff --git a/Content.Client/_Goobstation/Bingle/BingleFallingVisualsSystem.cs b/Content.Client/_Goobstation/Bingle/BingleFallingVisualsSystem.cs
new file mode 100644
index 0000000000..c310b308b2
--- /dev/null
+++ b/Content.Client/_Goobstation/Bingle/BingleFallingVisualsSystem.cs
@@ -0,0 +1,80 @@
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+using Robust.Shared.Animations;
+using Content.Shared._Goobstation.Bingle;
+
+namespace Content.Client._Goobstation.Bingle;
+
+///
+/// Handles the falling animation for entities that fall into a Binglepit. shamlesly copied from chasm
+///
+public sealed class BingleFallingVisualsSystem : EntitySystem
+{
+ [Dependency] private readonly AnimationPlayerSystem _anim = default!;
+
+ private readonly string _chasmFallAnimationKey = "chasm_fall";
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent(OnComponentInit);
+ SubscribeLocalEvent(OnComponentRemove);
+ }
+
+ private void OnComponentInit(EntityUid uid, BinglePitFallingComponent component, ComponentInit args)
+ {
+ if (!TryComp(uid, out var sprite) ||
+ TerminatingOrDeleted(uid))
+ {
+ return;
+ }
+
+ component.OriginalScale = sprite.Scale;
+
+ var player = EnsureComp(uid);
+ if (_anim.HasRunningAnimation(player, _chasmFallAnimationKey))
+ return;
+
+ _anim.Play((uid, player), GetFallingAnimation(component), _chasmFallAnimationKey);
+ }
+
+ private void OnComponentRemove(EntityUid uid, BinglePitFallingComponent component, ComponentRemove args)
+ {
+ if (!TryComp(uid, out var sprite) || TerminatingOrDeleted(uid))
+ return;
+
+ if (!TryComp(uid, out var player))
+ return;
+
+ //var player = EnsureComp(uid);
+ if (_anim.HasRunningAnimation(player, _chasmFallAnimationKey))
+ _anim.Stop(player, _chasmFallAnimationKey);
+
+ sprite.Scale = component.OriginalScale;
+ }
+
+ private Animation GetFallingAnimation(BinglePitFallingComponent component)
+ {
+ var length = component.AnimationTime;
+
+ return new Animation()
+ {
+ Length = length,
+ AnimationTracks =
+ {
+ new AnimationTrackComponentProperty()
+ {
+ ComponentType = typeof(SpriteComponent),
+ Property = nameof(SpriteComponent.Scale),
+ KeyFrames =
+ {
+ new AnimationTrackProperty.KeyFrame(component.OriginalScale, 0.0f),
+ new AnimationTrackProperty.KeyFrame(component.AnimationScale, length.Seconds),
+ },
+ InterpolationMode = AnimationInterpolationMode.Cubic
+ }
+ }
+ };
+ }
+}
diff --git a/Content.Client/_Goobstation/Bingle/BingleSystem.cs b/Content.Client/_Goobstation/Bingle/BingleSystem.cs
new file mode 100644
index 0000000000..48364e049d
--- /dev/null
+++ b/Content.Client/_Goobstation/Bingle/BingleSystem.cs
@@ -0,0 +1,43 @@
+using Robust.Client.GameObjects;
+using Content.Shared._Goobstation.Bingle;
+using Content.Shared.CombatMode;
+
+namespace Content.Client._Goobstation.Bingle;
+
+///
+/// handles the upgrade apperance from normal bingle to upgraded bingle
+///
+public sealed class BingleSystem : EntitySystem
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeNetworkEvent(OnUpgradeChange);
+ SubscribeLocalEvent(OnCombatToggle);
+ }
+
+ //make eyse glow red when combat mode engaged.
+ private void OnCombatToggle(EntityUid uid, BingleComponent component, ToggleCombatActionEvent args)
+ {
+ if (!TryComp(uid, out var sprite))
+ return;
+ if (!TryComp(uid, out var combat))
+ return;
+ if (!sprite.LayerMapTryGet(BingleVisual.Combat, out var layer))
+ return;
+
+ sprite.LayerSetVisible(layer, combat.IsInCombatMode);
+ }
+
+ private void OnUpgradeChange(BingleUpgradeEntityMessage args)
+ {
+ var uid = GetEntity(args.Bingle);
+
+ if (!TryComp(uid, out var sprite))
+ return;
+ if (!sprite.LayerMapTryGet(BingleVisual.Upgraded, out var layer))
+ return;
+
+ sprite.LayerSetVisible(layer, true);
+ }
+}
diff --git a/Content.Server/_Goobstation/Bingle/BinglePitSystem.cs b/Content.Server/_Goobstation/Bingle/BinglePitSystem.cs
new file mode 100644
index 0000000000..cea0ac3199
--- /dev/null
+++ b/Content.Server/_Goobstation/Bingle/BinglePitSystem.cs
@@ -0,0 +1,177 @@
+using System.Numerics;
+using Robust.Shared.Audio.Systems;
+using Robust.Shared.Containers;
+using Robust.Shared.Timing;
+using Robust.Server.GameObjects;
+using Content.Server.Stunnable;
+using Content.Shared.Ghost.Roles.Components;
+using Content.Shared.StepTrigger.Systems;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Destructible;
+using Content.Shared.Stunnable;
+using Content.Shared.Humanoid;
+using Content.Shared.Weapons.Melee.Events;
+using Content.Shared.Movement.Events;
+using Content.Shared._Goobstation.Bingle;
+using Content.Shared.Popups;
+using Content.Shared.Movement.Pulling.Components;
+using Content.Shared.Movement.Pulling.Systems;
+
+namespace Content.Server._Goobstation.Bingle;
+
+public sealed class BinglePitSystem : EntitySystem
+{
+ [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
+ [Dependency] private readonly BingleSystem _bingleSystem = default!;
+ [Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly StunSystem _stun = default!;
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly IEntityManager _entityManager = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+ [Dependency] private readonly PullingSystem _pulling = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnStepTriggered);
+ SubscribeLocalEvent(OnStepTriggerAttempt);
+ SubscribeLocalEvent(OnInit);
+ SubscribeLocalEvent(OnDestruction);
+ SubscribeLocalEvent(OnAttacked);
+ SubscribeLocalEvent(OnUpdateCanMove);
+ }
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var falling))
+ {
+ if (_timing.CurTime < falling.NextDeletionTime)
+ continue;
+
+ _containerSystem.Insert(uid, falling.Pit.Pit);
+ EnsureComp(uid); // used stuned to prevent any funny being done inside the pit
+ RemCompDeferred(uid, falling);
+ }
+ }
+
+ private void OnInit(EntityUid uid, BinglePitComponent component, MapInitEvent args)
+ {
+ if (!Transform(uid).Coordinates.IsValid(EntityManager))
+ QueueDel(uid);
+ component.Pit = _containerSystem.EnsureContainer(uid, "pit");
+ }
+
+ private void OnStepTriggered(EntityUid uid, BinglePitComponent component, ref StepTriggeredOffEvent args)
+ {
+ // dont swallow bingles
+ if (HasComp(args.Tripper))
+ return;
+ // need to be at levl 2 or above to swallow anything alive
+ if (HasComp(args.Tripper) && component.Level < 2)
+ return;
+ if (HasComp(args.Tripper))
+ return;
+
+ StartFalling(uid, component, args.Tripper);
+
+ if (component.BinglePoints >= component.SpawnNewAt)
+ {
+ SpawnBingle(uid, component);
+ component.BinglePoints -= component.SpawnNewAt;
+ }
+ }
+
+ public void StartFalling(EntityUid uid, BinglePitComponent component, EntityUid tripper, bool playSound = true)
+ {
+ component.BinglePoints += HasComp(tripper)
+ ? component.PointsForAlive + (HasComp(tripper)
+ ? component.AdditionalPointsForHuman : 0) : 1;
+
+ if (TryComp(tripper, out var pullable) && pullable.BeingPulled)
+ _pulling.TryStopPull(tripper, pullable);
+
+ var fall = EnsureComp(tripper);
+ fall.Pit = component;
+ fall.NextDeletionTime = _timing.CurTime + fall.DeletionTime;
+ _stun.TryKnockdown(tripper, fall.DeletionTime, false);
+
+ if (playSound)
+ _audio.PlayPvs(component.FallingSound, uid);
+
+ }
+
+ private void OnStepTriggerAttempt(EntityUid uid, BinglePitComponent component, ref StepTriggerAttemptEvent args)
+ => args.Continue = true;
+
+ public void SpawnBingle(EntityUid uid, BinglePitComponent component)
+ {
+ Spawn(component.GhostRoleToSpawn, Transform(uid).Coordinates);
+
+ component.MinionsMade++;
+ if (component.MinionsMade >= component.UpgradeMinionsAfter)
+ {
+ component.MinionsMade = 0;
+ component.Level++;
+ UpgradeBingles(uid, component);
+ }
+ }
+
+ public void UpgradeBingles(EntityUid uid, BinglePitComponent component)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var queryUid, out var queryBingleComp))
+ if (queryBingleComp.MyPit != null && queryBingleComp.MyPit.Value == uid)
+ _bingleSystem.UpgradeBingle(queryUid, queryBingleComp);
+ if (component.Level <= component.MaxSize)
+ ScaleUpPit(uid, component);
+
+ _popup.PopupEntity(Loc.GetString("bingle-pit-grow"), uid);
+ }
+
+ private void OnDestruction(EntityUid uid, BinglePitComponent component, DestructionEventArgs args)
+ {
+ if (component.Pit != null)
+ foreach (var pitUid in _containerSystem.EmptyContainer(component.Pit))
+ {
+ RemComp(pitUid);
+ _stun.TryKnockdown(pitUid, TimeSpan.FromSeconds(2), false);
+ }
+
+ RemoveAllBingleGhostRoles(uid, component);//remove all unclaimed ghostroles when pit is destroyed
+
+ //Remove all falling when pit is destroyed, in the small chance somone is inbetween start and insert
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var fallingUid, out var fallingComp))
+ RemCompDeferred(fallingUid, fallingComp);
+ }
+
+ public void RemoveAllBingleGhostRoles(EntityUid uid, BinglePitComponent component)
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var queryGRMSUid, out var queryGRMScomp))
+ if (queryGRMScomp.Prototype == "MobBingle")
+ if (Transform(uid).Coordinates == Transform(queryGRMSUid).Coordinates)
+ QueueDel(queryGRMSUid); // remove any unspawned bngle when pit is destroyed
+ }
+ private void OnAttacked(EntityUid uid, BinglePitComponent component, AttackedEvent args)
+ {
+ if (_containerSystem.ContainsEntity(uid, args.User))
+ EnsureComp(args.User);
+ }
+
+ private void OnUpdateCanMove(EntityUid uid, BinglePitFallingComponent component, UpdateCanMoveEvent args)
+ => args.Cancel();
+
+ private void ScaleUpPit(EntityUid uid, BinglePitComponent component)
+ {
+ if (!TryComp(uid, out var appearanceComponent))
+ appearanceComponent = _entityManager.EnsureComponent(uid);
+ var appearance = _entityManager.System();
+ _entityManager.EnsureComponent(uid);
+
+ appearance.SetData(uid, ScaleVisuals.Scale, Vector2.One * component.Level, appearanceComponent);
+ }
+}
diff --git a/Content.Server/_Goobstation/Bingle/BingleSystem.cs b/Content.Server/_Goobstation/Bingle/BingleSystem.cs
new file mode 100644
index 0000000000..c272529934
--- /dev/null
+++ b/Content.Server/_Goobstation/Bingle/BingleSystem.cs
@@ -0,0 +1,64 @@
+using Content.Shared.Weapons.Melee;
+using Content.Shared.Interaction.Events;
+using Content.Shared.Popups;
+using Content.Shared._Goobstation.Bingle;
+using Robust.Shared.Map;
+using System.Numerics;
+
+namespace Content.Server._Goobstation.Bingle;
+
+public sealed class BingleSystem : EntitySystem
+{
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnMapInit);
+ SubscribeLocalEvent(OnAttackAttempt);
+ }
+
+ private void OnMapInit(EntityUid uid, BingleComponent component, MapInitEvent args)
+ {
+ var cords = Transform(uid).Coordinates;
+ if (!cords.IsValid(EntityManager) || cords.Position == Vector2.Zero)
+ return;
+ if (MapId.Nullspace == Transform(uid).MapID)
+ return;
+
+ if (component.Prime)
+ component.MyPit = Spawn("BinglePit", cords);
+ else
+ {
+ var query = EntityQueryEnumerator();
+ while (query.MoveNext(out var queryUid, out var _))
+ if (cords == Transform(queryUid).Coordinates)
+ component.MyPit = queryUid;
+ }
+ }
+
+ //ran by the pit to upgrade bingle damage
+ public void UpgradeBingle(EntityUid uid, BingleComponent component)
+ {
+ if (component.Upgraded)
+ return;
+ if (!TryComp(uid, out var weponComp))
+ return;
+
+ weponComp.Damage = component.UpgradeDamage;
+ component.Upgraded = true;
+ Dirty(uid, weponComp);
+
+ _popup.PopupEntity(Loc.GetString("bingle-upgrade-success"), uid, uid);
+
+ RaiseNetworkEvent(new BingleUpgradeEntityMessage(GetNetEntity(uid)));
+ }
+
+ private void OnAttackAttempt(EntityUid uid, BingleComponent component, AttackAttemptEvent args)
+ {
+ //Prevent Friendly Bingle fire
+ if (HasComp(args.Target) || HasComp(args.Target))
+ args.Cancel();
+ }
+}
+
diff --git a/Content.Shared/_Goobstation/Bingle/BingleComponent.cs b/Content.Shared/_Goobstation/Bingle/BingleComponent.cs
new file mode 100644
index 0000000000..903bb7e265
--- /dev/null
+++ b/Content.Shared/_Goobstation/Bingle/BingleComponent.cs
@@ -0,0 +1,31 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Content.Shared.Damage;
+using Robust.Shared.Network;
+
+namespace Content.Shared._Goobstation.Bingle;
+
+[RegisterComponent, NetworkedComponent]
+public sealed partial class BingleComponent : Component
+{
+ [DataField]
+ public bool Upgraded = false;
+ [DataField]
+ public DamageSpecifier UpgradeDamage = default!;
+ [DataField]
+ public bool Prime = false;
+
+ public EntityUid? MyPit;
+}
+[Serializable, NetSerializable]
+public sealed class BingleUpgradeEntityMessage(NetEntity bingle) : EntityEventArgs
+{
+ public NetEntity Bingle = bingle;
+}
+
+[Serializable, NetSerializable]
+public enum BingleVisual : byte
+{
+ Upgraded,
+ Combat
+}
diff --git a/Content.Shared/_Goobstation/Bingle/BinglePitComponent.cs b/Content.Shared/_Goobstation/Bingle/BinglePitComponent.cs
new file mode 100644
index 0000000000..6e99b0ae5a
--- /dev/null
+++ b/Content.Shared/_Goobstation/Bingle/BinglePitComponent.cs
@@ -0,0 +1,45 @@
+using Robust.Shared.Containers;
+using Robust.Shared.Audio;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared._Goobstation.Bingle;
+
+[RegisterComponent]
+public sealed partial class BinglePitComponent : Component
+{
+ ///
+ /// ammount of stored
+ ///
+ [DataField]
+ public float BinglePoints = 0f;
+ public float PointsForAlive = 5f;
+ public float AdditionalPointsForHuman = 5f;
+ ///
+ /// amount of Bingle Points needed for a new bingle
+ ///
+ [DataField]
+ public float SpawnNewAt = 10f;
+
+ ///
+ /// amount bingles needed to evolve / gain a level / expand the ... THE FACTORY MUST GROW
+ ///
+ [DataField]
+ public float MinionsMade = 0f;
+
+ [DataField]
+ public float UpgradeMinionsAfter = 12f;
+
+ ///
+ /// the Bingle pit's level
+ ///
+ [DataField]
+ public float Level = 1f;
+ ///
+ /// Where the entities go when it falls into the pit, empties when it is destroyed.
+ ///
+ public Container Pit = default!;
+ [DataField]
+ public float MaxSize = 3f;
+ public SoundSpecifier FallingSound = new SoundPathSpecifier("/Audio/Effects/falling.ogg");
+ public EntProtoId GhostRoleToSpawn = "SpawnPointGhostBingle";
+}
diff --git a/Content.Shared/_Goobstation/Bingle/BinglePitFallingComponent.cs b/Content.Shared/_Goobstation/Bingle/BinglePitFallingComponent.cs
new file mode 100644
index 0000000000..f0a96b01fc
--- /dev/null
+++ b/Content.Shared/_Goobstation/Bingle/BinglePitFallingComponent.cs
@@ -0,0 +1,40 @@
+using System.Numerics;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared._Goobstation.Bingle;
+
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentPause]
+public sealed partial class BinglePitFallingComponent : Component
+{
+ ///
+ /// Time it should take for the falling animation (scaling down) to complete.
+ ///
+ [DataField("animationTime")]
+ public TimeSpan AnimationTime = TimeSpan.FromSeconds(1.5f);
+
+ ///
+ /// Time it should take in seconds for the entity to actually delete
+ ///
+ [DataField("deletionTime")]
+ public TimeSpan DeletionTime = TimeSpan.FromSeconds(1.8f);
+
+ [DataField("nextDeletionTime", customTypeSerializer:typeof(TimeOffsetSerializer))]
+ [AutoPausedField]
+ public TimeSpan NextDeletionTime = TimeSpan.Zero;
+
+ ///
+ /// Original scale of the object so it can be restored if the component is removed in the middle of the animation
+ ///
+ public Vector2 OriginalScale = Vector2.Zero;
+
+ ///
+ /// Scale that the animation should bring entities to.
+ ///
+ public Vector2 AnimationScale = new Vector2(0.01f, 0.01f);
+
+ ///
+ /// the pit your about to fall into
+ ///
+ public BinglePitComponent Pit = new BinglePitComponent();
+}
diff --git a/Resources/Locale/en-US/_Goobstation/bingle/bingle.ftl b/Resources/Locale/en-US/_Goobstation/bingle/bingle.ftl
new file mode 100644
index 0000000000..b0595e478c
--- /dev/null
+++ b/Resources/Locale/en-US/_Goobstation/bingle/bingle.ftl
@@ -0,0 +1,20 @@
+
+bingle-accent-1 = Bingle
+bingle-accent-2 = Bing
+bingle-accent-3 = Bongle
+bingle-accent-4 = Bong
+bingle-accent-5 = BINGLE
+
+chat-speech-verb-name-bingle = Bingle
+bingle-verb-1 = Croaks
+bingle-verb-2 = Mumbles
+bingle-verb-3 = Harks
+bingle-verb-4 = Grumbles
+
+bingle-station-announcement = Bingle Bingle Bingle
+
+ghost-role-information-bingle-name = Bingle
+ghost-role-information-bingle-description = The Pit is love. The Pit is life. The Pit must grow
+
+bingle-upgrade-success = You feel stronger
+bingle-pit-grow = The pit grows larger
diff --git a/Resources/Prototypes/Entities/Structures/Specific/Binglepit.yml b/Resources/Prototypes/Entities/Structures/Specific/Binglepit.yml
new file mode 100644
index 0000000000..e7adc03a2e
--- /dev/null
+++ b/Resources/Prototypes/Entities/Structures/Specific/Binglepit.yml
@@ -0,0 +1,62 @@
+- type: entity
+ id: BinglePit
+ name: binglepit
+ description: Looks Hungry
+ placement:
+ mode: SnapgridCenter
+ components:
+ - type: BinglePit
+ - type: StepTrigger
+ requiredTriggeredSpeed: 0
+ intersectRatio: 0.4
+ blacklist:
+ tags:
+ - Catwalk
+ triggerGroups:
+ types:
+ - Chasm
+ - type: Transform
+ anchored: true
+ - type: Physics
+ bodyType: Static
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeAabb
+ bounds: "-0.5,-0.5,0.5,0.5"
+ layer:
+ - WallLayer
+ mask:
+ - ItemMask
+ density: 1000
+ hard: false
+ - type: Sprite
+ drawdepth: FloorTiles
+ layers:
+ - sprite: _Goobstation/Mobs/Bingle/bingle.rsi
+ state: pit
+ - type: InteractionOutline
+ - type: Clickable
+ - type: Damageable
+ damageContainer: Inorganic
+ damageModifierSet: Metallic
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTrigger
+ damage: 256
+ behaviors:
+ - !type:DoActsBehavior
+ acts: [ "Destruction" ]
+ - type: EmitSoundOnSpawn
+ sound:
+ path: /Audio/Weapons/Guns/Gunshots/rocket_launcher.ogg
+ - type: RequireProjectileTarget
+ - type: SolutionContainerManager
+ solutions:
+ drainBuffer:
+ maxVol: 1000
+ - type: Drain
+ unitsPerSecond: 10
+ unitsDestroyedPerSecond: 10
diff --git a/Resources/Prototypes/_Goobstation/Accents/full_replacements.yml b/Resources/Prototypes/_Goobstation/Accents/full_replacements.yml
new file mode 100644
index 0000000000..e8e46d0d01
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/Accents/full_replacements.yml
@@ -0,0 +1,21 @@
+# Accents that work off of full replacements rather than word replacements.
+
+- type: accent
+ id: gondola
+ fullReplacements:
+ - accent-words-gondola-1
+
+- type: accent
+ id: bingle
+ fullReplacements:
+ - bingle-accent-1 # for the odds
+ - bingle-accent-1
+ - bingle-accent-1
+ - bingle-accent-1
+ - bingle-accent-1
+ - bingle-accent-1
+ - bingle-accent-1
+ - bingle-accent-2
+ - bingle-accent-3
+ - bingle-accent-4
+ - bingle-accent-5
diff --git a/Resources/Prototypes/_Goobstation/Entities/Markers/Spawners/bingle.yml b/Resources/Prototypes/_Goobstation/Entities/Markers/Spawners/bingle.yml
new file mode 100644
index 0000000000..72195d1b5f
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/Entities/Markers/Spawners/bingle.yml
@@ -0,0 +1,28 @@
+- type: entity
+ id: SpawnPointGhostBingle
+ name: ghost role spawn point
+ suffix: Bingle
+ parent: MarkerBase
+ components:
+ - type: GhostRole
+ name: ghost-role-information-bingle-name
+ description: ghost-role-information-bingle-description
+ rules: ghost-role-information-freeagent-rules
+ raffle:
+ settings: short
+ - type: GhostRoleMobSpawner
+ prototype: MobBingle
+ - type: Sprite
+ sprite: Markers/jobs.rsi
+ layers:
+ - state: green
+ - sprite: _Goobstation/Mobs/Bingle/bingle.rsi
+ state: icon
+
+- type: entity
+ id: SpawnPointGhostBinglePrime
+ parent: SpawnPointGhostBingle
+ suffix: Prime
+ components:
+ - type: GhostRoleMobSpawner
+ prototype: MobBinglePrime
diff --git a/Resources/Prototypes/_Goobstation/Entities/Mobs/Player/bingle.yml b/Resources/Prototypes/_Goobstation/Entities/Mobs/Player/bingle.yml
new file mode 100644
index 0000000000..7153c49a5f
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/Entities/Mobs/Player/bingle.yml
@@ -0,0 +1,104 @@
+- type: entity
+ name: bingle
+ id: MobBingle
+ parent: MobSpaceBasic
+ description: bingle
+ components:
+ - type: Bingle
+ upgradeDamage:
+ types:
+ Blunt: 14
+ Structural: 28
+ - type: GhostTakeoverAvailable
+ - type: GhostRole
+ name: ghost-role-information-bingle-name
+ description: ghost-role-information-bingle-description
+ rules: ghost-role-information-freeagent-rules
+ raffle:
+ settings: short
+ - type: Sprite
+ drawdepth: Mobs
+ sprite: _Goobstation/Mobs/Bingle/bingle.rsi
+ layers:
+ - map: ["enum.DamageStateVisualLayers.Base", "movement"]
+ state: alive
+ - map: ["enum.BingleVisual.Upgraded"]
+ state: Upgraded
+ visible: false
+ - map: ["enum.BingleVisual.Combat"]
+ state: combat
+ visible: false
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 140: Dead # by request
+ - type: MobState
+ allowedStates:
+ - Alive
+ - Dead
+ - type: DamageStateVisuals
+ rotate: true
+ states:
+ Alive:
+ Base: alive
+ Dead:
+ Base: dead
+ - type: Bloodstream
+ bloodMaxVolume: 100
+ bloodReagent: Nitrogen # by request
+ - type: MeleeWeapon
+ soundHit:
+ path: /Audio/Weapons/Xeno/alien_claw_flesh3.ogg
+ angle: 0
+ wideAnimationRotation: 0
+ animation: WeaponArcSlash
+ damage:
+ types:
+ Blunt: 7
+ Structural: 14 # breaks down the walls and airlocks in 10 hits
+ bluntStaminaDamageFactor: 2.0
+ - type: MovementSpeedModifier
+ baseWalkSpeed : 2 # same as slime
+ baseSprintSpeed : 4
+ - type: Stamina
+ critThreshold: 120
+ - type: StaminaDamageOnHit # from stunbaton
+ damage: 15
+ overtime: 40
+ sound: /Audio/Weapons/egloves.ogg
+ - type: Damageable
+ damageContainer: OrganicPart # unsure if this is the right type
+ damageModifierSet: Bingle
+ - type: FootstepModifier
+ footstepSoundCollection:
+ path: /Audio/Effects/Footsteps/slime1.ogg
+ params:
+ volume: 3
+ - type: TypingIndicator
+ proto: alien
+ - type: Speech
+ speechVerb: Bingle
+ speechSounds: Slime
+ - type: ReplacementAccent # prevent talking
+ accent: bingle
+ - type: PassiveDamage #passive Healing
+ allowedStates:
+ - Alive
+ damage:
+ types:
+ Poison: -0.1
+ groups:
+ Brute: -0.1
+ Burn: -0.1
+ - type: NightVision
+ color: "#008080"
+ activateSound: null
+ deactivateSound: null
+
+- type: entity
+ id: MobBinglePrime
+ parent: MobBingle
+ suffix: Prime
+ components:
+ - type: Bingle
+ prime: true
diff --git a/Resources/Prototypes/_Goobstation/GameRules/bingleEvent.yml b/Resources/Prototypes/_Goobstation/GameRules/bingleEvent.yml
new file mode 100644
index 0000000000..087cba6897
--- /dev/null
+++ b/Resources/Prototypes/_Goobstation/GameRules/bingleEvent.yml
@@ -0,0 +1,14 @@
+- type: entity
+ id: BingleSpawn
+ parent: BaseStationEventShortDelay
+ components:
+ - type: StationEvent
+ startAnnouncement: bingle-station-announcement
+ startAudio:
+ path: /Audio/Announcements/attention.ogg
+ earliestStart: 20
+ weight: 6
+ duration: 50
+ minimumPlayers: 15
+ - type: RandomSpawnRule
+ prototype: SpawnPointGhostBinglePrime
diff --git a/Resources/Prototypes/_Goobstation/Voice/speech_verbs.yml b/Resources/Prototypes/_Goobstation/Voice/speech_verbs.yml
index 52a8d0d40e..ebce1c50be 100644
--- a/Resources/Prototypes/_Goobstation/Voice/speech_verbs.yml
+++ b/Resources/Prototypes/_Goobstation/Voice/speech_verbs.yml
@@ -6,3 +6,36 @@
path: /Audio/Machines/beep.ogg
exclaimSound:
path: /Audio/Machines/beep.ogg
+
+- type: speechVerb
+ id: Dead
+ name: chat-speech-verb-name-dead
+ speechVerbStrings:
+ - chat-speech-verb-dead-1
+ - chat-speech-verb-dead-2
+ - chat-speech-verb-dead-3
+ - chat-speech-verb-dead-4
+ - chat-speech-verb-dead-5
+ - chat-speech-verb-dead-6
+ - chat-speech-verb-dead-7
+ - chat-speech-verb-dead-8
+ - chat-speech-verb-dead-9
+
+- type: speechVerb
+ id: VoiceModulator
+ name: chat-speech-verb-VoiceModulator-name
+ speechVerbStrings:
+ - chat-speech-verb-VoiceModulator-1
+ - chat-speech-verb-VoiceModulator-2
+ - chat-speech-verb-VoiceModulator-3
+ - chat-speech-verb-VoiceModulator-4
+ - chat-speech-verb-VoiceModulator-5
+
+- type: speechVerb
+ id: Bingle
+ name: chat-speech-verb-name-bingle
+ speechVerbStrings:
+ - bingle-verb-1
+ - bingle-verb-2
+ - bingle-verb-3
+ - bingle-verb-4
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/Upgraded.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/Upgraded.png
new file mode 100644
index 0000000000..6877c80670
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/Upgraded.png differ
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/alive.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/alive.png
new file mode 100644
index 0000000000..8684a63cee
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/alive.png differ
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/combat.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/combat.png
new file mode 100644
index 0000000000..df53764118
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/combat.png differ
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/dead.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/dead.png
new file mode 100644
index 0000000000..82d1c47d3f
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/dead.png differ
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/icon.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/icon.png
new file mode 100644
index 0000000000..a551757224
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/icon.png differ
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/meta.json b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/meta.json
new file mode 100644
index 0000000000..f9b9f4472d
--- /dev/null
+++ b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/meta.json
@@ -0,0 +1,39 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Brainrot HQ",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "alive",
+ "directions": 4
+ },
+ {
+ "name": "pit",
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "icon"
+ },
+ {
+ "name": "dead"
+ },
+ {
+ "name": "Upgraded",
+ "directions": 4
+ },
+ {
+ "name": "combat",
+ "directions": 4
+ }
+ ]
+}
diff --git a/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/pit.png b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/pit.png
new file mode 100644
index 0000000000..8c9e88cc44
Binary files /dev/null and b/Resources/Textures/_Goobstation/Mobs/Bingle/bingle.rsi/pit.png differ