diff --git a/Content.Shared/Body/Components/BodyComponent.cs b/Content.Shared/Body/Components/BodyComponent.cs index 113dbb3592..022d3a7799 100644 --- a/Content.Shared/Body/Components/BodyComponent.cs +++ b/Content.Shared/Body/Components/BodyComponent.cs @@ -21,6 +21,13 @@ public sealed class BodyComponent : Component, IDraggable [DataField("gibSound")] public SoundSpecifier GibSound = new SoundCollectionSpecifier("gib"); + /// + /// The amount of legs required to move at full speed. + /// If 0, then legs do not impact speed. + /// + [DataField("requiredLegs")] + public int RequiredLegs; + bool IDraggable.CanStartDrag(StartDragDropEvent args) { return true; diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs index 237e63fe24..92a0c88a2a 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Body.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Body.cs @@ -164,6 +164,32 @@ public partial class SharedBodySystem } } + /// + /// Returns all body part slots in the graph, including ones connected by + /// body parts which are null. + /// + /// + /// + /// + public IEnumerable GetAllBodyPartSlots(EntityUid partId, BodyPartComponent? part = null) + { + if (!Resolve(partId, ref part, false)) + yield break; + + foreach (var slot in part.Children.Values) + { + if (!TryComp(slot.Child, out var childPart)) + continue; + + yield return slot; + + foreach (var child in GetAllBodyPartSlots(slot.Child.Value, childPart)) + { + yield return child; + } + } + } + public virtual HashSet GibBody(EntityUid? partId, bool gibOrgans = false, BodyComponent? body = null, bool deleteItems = false) { diff --git a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs index f6951f0024..3c2ada65b2 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.Parts.cs @@ -6,6 +6,7 @@ using Content.Shared.Body.Organ; using Content.Shared.Body.Part; using Content.Shared.Damage; using Content.Shared.Damage.Prototypes; +using Content.Shared.Movement.Components; using Content.Shared.Random.Helpers; using Robust.Shared.Containers; using Robust.Shared.GameStates; @@ -211,6 +212,9 @@ public partial class SharedBodySystem if (part.Body is { } newBody) { + if (part.PartType == BodyPartType.Leg) + UpdateMovementSpeed(newBody); + var partAddedEvent = new BodyPartAddedEvent(slot.Id, part); RaiseLocalEvent(newBody, ref partAddedEvent); @@ -254,10 +258,11 @@ public partial class SharedBodySystem var args = new BodyPartRemovedEvent(slot.Id, part); RaiseLocalEvent(oldBody, ref args); - if (part.PartType == BodyPartType.Leg && - !GetBodyChildrenOfType(oldBody, BodyPartType.Leg).Any()) + if (part.PartType == BodyPartType.Leg) { - Standing.Down(oldBody); + UpdateMovementSpeed(oldBody); + if(!GetBodyChildrenOfType(oldBody, BodyPartType.Leg).Any()) + Standing.Down(oldBody); } if (part.IsVital && !GetBodyChildrenOfType(oldBody, part.PartType).Any()) @@ -282,6 +287,44 @@ public partial class SharedBodySystem return true; } + public void UpdateMovementSpeed(EntityUid body, BodyComponent? component = null, MovementSpeedModifierComponent? movement = null) + { + if (!Resolve(body, ref component, ref movement, false)) + return; + + if (component.RequiredLegs <= 0) + return; + + if (component.Root?.Child is not { } root) + return; + + var allSlots = GetAllBodyPartSlots(root).ToHashSet(); + var allLegs = new HashSet(); + foreach (var slot in allSlots) + { + if (slot.Type == BodyPartType.Leg && slot.Child is { } child) + allLegs.Add(child); + } + + var walkSpeed = 0f; + var sprintSpeed = 0f; + var acceleration = 0f; + foreach (var leg in allLegs) + { + if (!TryComp(leg, out var legModifier)) + continue; + + walkSpeed += legModifier.BaseWalkSpeed; + sprintSpeed += legModifier.BaseSprintSpeed; + acceleration += legModifier.Acceleration; + } + + walkSpeed /= component.RequiredLegs; + sprintSpeed /= component.RequiredLegs; + acceleration /= component.RequiredLegs; + Movement.ChangeBaseSpeed(body, walkSpeed, sprintSpeed, acceleration, movement); + } + public bool DropPartAt(EntityUid? partId, EntityCoordinates dropAt, BodyPartComponent? part = null) { if (partId == null || !DropPart(partId, part)) diff --git a/Content.Shared/Body/Systems/SharedBodySystem.cs b/Content.Shared/Body/Systems/SharedBodySystem.cs index b91aea1628..4d195e0a50 100644 --- a/Content.Shared/Body/Systems/SharedBodySystem.cs +++ b/Content.Shared/Body/Systems/SharedBodySystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Damage; +using Content.Shared.Movement.Systems; using Content.Shared.Standing; using Robust.Shared.Containers; using Robust.Shared.Prototypes; @@ -14,6 +15,7 @@ public abstract partial class SharedBodySystem : EntitySystem [Dependency] protected readonly SharedContainerSystem Containers = default!; [Dependency] protected readonly DamageableSystem Damageable = default!; [Dependency] protected readonly StandingStateSystem Standing = default!; + [Dependency] protected readonly MovementSpeedModifierSystem Movement = default!; public override void Initialize() { diff --git a/Resources/Prototypes/Body/Parts/animal.yml b/Resources/Prototypes/Body/Parts/animal.yml index 98e7bf5e6d..fb4ac59b2a 100644 --- a/Resources/Prototypes/Body/Parts/animal.yml +++ b/Resources/Prototypes/Body/Parts/animal.yml @@ -37,6 +37,7 @@ components: - type: BodyPart partType: Leg + - type: MovementSpeedModifier - type: entity id: FeetAnimal diff --git a/Resources/Prototypes/Body/Parts/diona.yml b/Resources/Prototypes/Body/Parts/diona.yml index 2a1d33fc4e..b2ce8c47ce 100644 --- a/Resources/Prototypes/Body/Parts/diona.yml +++ b/Resources/Prototypes/Body/Parts/diona.yml @@ -118,6 +118,9 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier + baseWalkSpeed : 1.5 + baseSprintSpeed : 3.5 - type: entity id: RightLegDiona @@ -131,6 +134,9 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier + baseWalkSpeed : 1.5 + baseSprintSpeed : 3.5 - type: entity id: LeftFootDiona diff --git a/Resources/Prototypes/Body/Parts/human.yml b/Resources/Prototypes/Body/Parts/human.yml index be8a9e4edc..c999a6a0ff 100644 --- a/Resources/Prototypes/Body/Parts/human.yml +++ b/Resources/Prototypes/Body/Parts/human.yml @@ -136,6 +136,7 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier - type: entity id: RightLegHuman @@ -152,6 +153,7 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier - type: entity id: LeftFootHuman diff --git a/Resources/Prototypes/Body/Parts/reptilian.yml b/Resources/Prototypes/Body/Parts/reptilian.yml index a98faf6c21..34b21d21ee 100644 --- a/Resources/Prototypes/Body/Parts/reptilian.yml +++ b/Resources/Prototypes/Body/Parts/reptilian.yml @@ -134,6 +134,9 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier + baseWalkSpeed : 2.7 + baseSprintSpeed : 4.5 - type: entity id: RightLegReptilian @@ -150,6 +153,9 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier + baseWalkSpeed : 2.7 + baseSprintSpeed : 4.5 - type: entity id: LeftFootReptilian diff --git a/Resources/Prototypes/Body/Parts/skeleton.yml b/Resources/Prototypes/Body/Parts/skeleton.yml index af2ea2c6eb..dc9d1ac1cd 100644 --- a/Resources/Prototypes/Body/Parts/skeleton.yml +++ b/Resources/Prototypes/Body/Parts/skeleton.yml @@ -149,6 +149,7 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier - type: entity id: RightLegSkeleton @@ -165,6 +166,7 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier - type: entity id: LeftFootSkeleton diff --git a/Resources/Prototypes/Body/Parts/slime.yml b/Resources/Prototypes/Body/Parts/slime.yml index 0ec7e611c4..1cf5da75fd 100644 --- a/Resources/Prototypes/Body/Parts/slime.yml +++ b/Resources/Prototypes/Body/Parts/slime.yml @@ -135,6 +135,7 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier - type: entity id: RightLegSlime @@ -151,6 +152,7 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier - type: entity id: LeftFootSlime diff --git a/Resources/Prototypes/Body/Parts/vox.yml b/Resources/Prototypes/Body/Parts/vox.yml index e820511455..8606ebac14 100644 --- a/Resources/Prototypes/Body/Parts/vox.yml +++ b/Resources/Prototypes/Body/Parts/vox.yml @@ -136,6 +136,7 @@ - type: BodyPart partType: Leg symmetry: Left + - type: MovementSpeedModifier - type: entity id: RightLegVox @@ -152,6 +153,7 @@ - type: BodyPart partType: Leg symmetry: Right + - type: MovementSpeedModifier - type: entity id: LeftFootVox diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml index 6a361cf23a..bf02f311c0 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/animals.yml @@ -711,6 +711,7 @@ speechSounds: Monkey - type: Body prototype: Primate + requiredLegs: 1 # TODO: More than 1 leg - type: DamageStateVisuals states: Alive: diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml index 1324c2f577..cd29daacd1 100644 --- a/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml +++ b/Resources/Prototypes/Entities/Mobs/NPCs/regalrat.yml @@ -50,6 +50,7 @@ Piercing: 8 - type: Body prototype: Rat + requiredLegs: 1 # TODO: More than 1 leg - type: Hunger # probably should be prototyped thresholds: Overfed: 200 @@ -225,6 +226,7 @@ Piercing: 3 - type: Body prototype: Rat + requiredLegs: 1 # TODO: More than 1 leg - type: Hunger # probably should be prototyped thresholds: Overfed: 200 diff --git a/Resources/Prototypes/Entities/Mobs/Species/base.yml b/Resources/Prototypes/Entities/Mobs/Species/base.yml index 608cf0e7a2..9665486e79 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/base.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/base.yml @@ -177,6 +177,7 @@ species: Human - type: Body prototype: Human + requiredLegs: 2 - type: Damageable damageContainer: Biological - type: RadiationReceiver @@ -366,6 +367,7 @@ species: Human - type: Body prototype: Human + requiredLegs: 2 - type: Damageable damageContainer: Biological - type: MobState diff --git a/Resources/Prototypes/Entities/Mobs/Species/diona.yml b/Resources/Prototypes/Entities/Mobs/Species/diona.yml index 5bbd02ae05..5c58efab1b 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/diona.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/diona.yml @@ -15,6 +15,7 @@ state: full - type: Body prototype: Diona + requiredLegs: 2 - type: Damageable damageContainer: Biological damageModifierSet: Diona diff --git a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml index 733de0f758..884cf7ce23 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/dwarf.yml @@ -24,6 +24,7 @@ scale: 1, 0.8 - type: Body prototype: Human + requiredLegs: 2 - type: entity save: false diff --git a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml index ceae93a83f..3ba3fee94f 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/reptilian.yml @@ -14,6 +14,7 @@ state: full - type: Body prototype: Reptilian + requiredLegs: 2 - type: LizardAccent - type: Speech speechSounds: Lizard diff --git a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml index 71cabfab5f..1faf9a31a5 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/skeleton.yml @@ -12,6 +12,7 @@ state: full - type: Body prototype: Skeleton + requiredLegs: 2 gibSound: /Audio/Effects/bone_rattle.ogg - type: Damageable damageContainer: Biological diff --git a/Resources/Prototypes/Entities/Mobs/Species/slime.yml b/Resources/Prototypes/Entities/Mobs/Species/slime.yml index be38a22c4c..0f183594fe 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/slime.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/slime.yml @@ -11,6 +11,7 @@ state: full - type: Body prototype: Slime + requiredLegs: 2 - type: Humanoid species: SlimePerson - type: Speech diff --git a/Resources/Prototypes/Entities/Mobs/Species/vox.yml b/Resources/Prototypes/Entities/Mobs/Species/vox.yml index d5963eb15d..499dc2587a 100644 --- a/Resources/Prototypes/Entities/Mobs/Species/vox.yml +++ b/Resources/Prototypes/Entities/Mobs/Species/vox.yml @@ -81,6 +81,7 @@ - map: [ "pocket2" ] - type: Body prototype: Vox + requiredLegs: 2 # Vox nitrogen stuff is handled in their metabolism - type: Respirator damage: @@ -110,4 +111,5 @@ species: Vox - type: Body prototype: Vox + requiredLegs: 2