Add new armour options for K9s (Laika) (#4172)

* Add simple component for handling specialised clothing and armours

* Add awful artwork for k9 armours

* Update secdog template to include head and outer clothing gear

* Add tags for K9Armour and K9

* Ensure Laika has the k9 tag

* Add entity entries for K9 armour, update starting gear for Laika

* Add lathe recipes for new K9 armour

* Update secdog sprite by breaking tail out to separate layers

* Update sprite drawing for Secdogs

* Fix missing pixel in Laika's base sprite

* Add jetpack sprites for secdog

* Remove damage state visuals

* Add layingdown component to laika

* Update helmet sprites from HTMLSystem

* Update hardsuit sprites from HTMLSystem

* Fix prone on crit for secdogs

* Add new damage visual layer for tails

* Add static tail for Laika and hook it up to damage state visuals
This commit is contained in:
BarryNorfolk 2025-10-04 22:57:14 +02:00 committed by GitHub
parent f52b92c960
commit 848e0dcbe7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
53 changed files with 440 additions and 20 deletions

View File

@ -15,4 +15,5 @@ public enum DamageStateVisualLayers : byte
Base,
BaseUnshaded,
BaseUnshadedAccessory, // DeltaV - Pet clothing
Tail, // DeltaV - Pet tails wagging
}

View File

@ -0,0 +1,24 @@
using Content.Shared.Whitelist;
namespace Content.Shared._DV.Clothing.Components;
/// <summary>
/// Marks that this piece of clothing can only be worn by an entity
/// with a matching tag.
/// </summary>
[RegisterComponent]
public sealed partial class SpecialisedClothingComponent : Component
{
/// <summary>
/// Valid tags which must exist on the entity attempting to wear
/// this piece of clothing.
/// </summary>
[DataField]
public EntityWhitelist Whitelist = new();
/// <summary>
/// The specific text to show, if any, for why this equipment cannot be worn.
/// </summary>
[DataField]
public LocId? FailureReason;
}

View File

@ -0,0 +1,34 @@
using Content.Shared._DV.Clothing.Components;
using Content.Shared.Inventory.Events;
using Content.Shared.Whitelist;
namespace Content.Shared._DV.Clothing.EntitySystems;
public sealed class SpecialisedClothingSystem : EntitySystem
{
[Dependency] private readonly EntityWhitelistSystem _whitelistSystem = default!;
private readonly LocId _defaultReason = "specialized-clothing-default-failure";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SpecialisedClothingComponent, BeingEquippedAttemptEvent>(OnBeingEquipped);
}
/// <summary>
/// Handles when a piece of specialized equipment attempts to be equipped, blocking it
/// in the case where the equipee is an invalid user.
/// </summary>
/// <param name="ent">Clothing being equipped.</param>
/// <param name="args">Args for the event, notably the entity equipping the clothing.</param>
private void OnBeingEquipped(Entity<SpecialisedClothingComponent> ent, ref BeingEquippedAttemptEvent args)
{
if (_whitelistSystem.IsWhitelistPass(ent.Comp.Whitelist, args.EquipTarget))
return;
args.Reason = ent.Comp.FailureReason ?? _defaultReason;
args.Cancel();
}
}

View File

@ -0,0 +1 @@
specialized-clothing-default-failure = You can't quite fit into this

View File

@ -0,0 +1,110 @@
- type: entity
abstract: true
id: K9ClothingBase
components:
- type: SpecialisedClothing
whitelist:
tags:
- K9
# K9 Combat suit
- type: entity
parent: [ K9ClothingBase, ClothingOuterHardsuitBase ]
id: ClothingOuterHardsuitCombatK9
name: k9 combat hardsuit
description: A canine-built combat suit designed to protect its against all manner of enemy combatants in low pressure environments. This one has station security markings.
components:
- type: Sprite
sprite: _DV/Clothing/OuterClothing/Hardsuits/Combat/k9.rsi
- type: Clothing
sprite: _DV/Clothing/OuterClothing/Hardsuits/Combat/k9.rsi
- type: PressureProtection
highPressureMultiplier: 0.50
lowPressureMultiplier: 1000
- type: ExplosionResistance
damageCoefficient: 0.50
- type: Armor
modifiers:
coefficients:
Blunt: 0.60
Slash: 0.60
Piercing: 0.60
Radiation: 0.75
Caustic: 0.75
Heat: 0.75
- type: StaminaResistance
damageCoefficient: 0.3
- type: ClothingSpeedModifier
walkModifier: 0.85
sprintModifier: 0.85
- type: ToggleableClothing
clothingPrototype: ClothingHeadHelmetHardsuitCombatK9
- type: AllowSuitStorage
- type: Insulated
- type: Tag
tags:
- K9Armor
- type: entity
parent: [ K9ClothingBase, ClothingHeadHardsuitBase, ClothingHeadSuitWithLightBase ]
id: ClothingHeadHelmetHardsuitCombatK9
name: k9 combat hardsuit helmet
components:
- type: Sprite
sprite: _DV/Clothing/Head/Hardsuits/Combat/k9.rsi
- type: Clothing
sprite: _DV/Clothing/Head/Hardsuits/Combat/k9.rsi
- type: Tag
tags:
- K9Armor
# K9 Riot suit
- type: entity
parent: ClothingOuterHardsuitCombatK9
id: ClothingOuterHardsuitCombatRiotK9
name: k9 riot hardsuit
description: A canine-built combat suit designed for crowd control against armed combatants in low pressure environments.
components:
- type: Sprite
sprite: _DV/Clothing/OuterClothing/Hardsuits/Combat/k9_riot.rsi
- type: Clothing
sprite: _DV/Clothing/OuterClothing/Hardsuits/Combat/k9_riot.rsi
- type: Armor
modifiers:
coefficients:
Blunt: 0.65
Slash: 0.65
Piercing: 0.65
Radiation: 0.80
Caustic: 0.80
Heat: 0.80
- type: ToggleableClothing
clothingPrototype: ClothingOuterHardsuitCombatRiotK9Helmet
- type: entity
parent: [ K9ClothingBase, ClothingHeadHardsuitBase, ClothingHeadSuitWithLightBase ]
id: ClothingOuterHardsuitCombatRiotK9Helmet
name: K9 riot helmet
components:
- type: Sprite
sprite: _DV/Clothing/Head/Hardsuits/Combat/k9_riot.rsi
- type: Clothing
sprite: _DV/Clothing/Head/Hardsuits/Combat/k9_riot.rsi
- type: Tag
tags:
- K9Armor
# K9 Durathread
- type: entity
parent: [ K9ClothingBase, ClothingOuterArmorDuraVest ]
id: ClothingOuterArmorDuraVestK9
name: K9 durathread vest
description: A tight-fitting and sturdy armor vest for canines, reinforced with durathread weave to protect against sharp objects and blunt force trauma.
components:
- type: Sprite
sprite: _DV/Clothing/OuterClothing/Armor/duravest.rsi
states: icon-secdog
- type: Tag
tags:
- K9Armor

View File

@ -91,7 +91,21 @@
sprite: _DV/Mobs/Pets/secdog.rsi
layers:
- map: ["enum.DamageStateVisualLayers.Base"]
state: secdog
state: base
- map: [ "outerClothing" ]
- map: ["enum.DamageStateVisualLayers.Tail"]
state: tail_animated
- type: DamageStateVisuals
states:
Alive:
Base: base
Tail: tail_animated
Critical:
Base: base
Tail: tail_static
Dead:
Base: base
Tail: tail_static
- type: HTN
rootTask:
task: SimpleHostileCompound
@ -141,14 +155,6 @@
interfaces:
enum.StrippingUiKey.Key:
type: StrippableBoundUserInterface
- type: DamageStateVisuals
states:
Alive:
Base: secdog
Critical:
Base: secdog_crit
Dead:
Base: secdog_dead
- type: Butcherable
spawned:
- id: FoodMeat
@ -178,6 +184,10 @@
- type: Vocal
sounds:
Unsexed: MobDog
- type: LayingDown
- type: RotationVisuals
defaultRotation: 90
horizontalRotation: 90
- type: entity
parent: MobMouse

View File

@ -53,6 +53,7 @@
- CannotSuicide
- VimPilot
- DoorBumpOpener
- K9
- type: StealTarget
stealGroup: AnimalSecurity # DeltaV - Adjusts because we have multiple possible sec animals
- type: Temperature

View File

@ -1,6 +1,17 @@
- type: inventoryTemplate
id: secdog
slots:
- name: head
slotTexture: head
slotFlags: HEAD
uiWindowPos: 1,3
strippingWindowPos: 1,0
displayName: Head
whitelist:
tags:
- K9Armor
- name: mask
slotTexture: mask
slotFlags: MASK
@ -11,6 +22,17 @@
tags:
- PetWearable
- name: outerClothing
slotTexture: suit
slotFlags: OUTERCLOTHING
stripTime: 6
uiWindowPos: 1,1
strippingWindowPos: 1,2
displayName: Suit
whitelist:
tags:
- K9Armor
- name: suitstorage
slotTexture: suit_storage
slotFlags: SUITSTORAGE

View File

@ -6,6 +6,7 @@
- ClothingNeckShockCollar
- ClothingOuterArmorPlateCarrier # plate carrier body armour
- ClothingOuterArmorDuraVest # stabproof vest body armour
- ClothingOuterArmorDuraVestK9 # k9 stabproof vest body armour
- ClothingOuterArmorReflective
- HoloprojectorSecurity
- RadioHandheldSecurity
@ -90,6 +91,8 @@
- ClothingOuterHardsuitJuggernautReverseEngineered
- ClothingOuterHardsuitSyndieReverseEngineered
- ClothingOuterHardsuitCombatOfficer
- ClothingOuterHardsuitCombatK9 # k9 combat suit
- ClothingOuterHardsuitCombatRiotK9 # k9 riot suit
- type: latheRecipePack
id: PrisonerSoftsuits

View File

@ -455,6 +455,16 @@
id: ClothingOuterHardsuitCombatOfficer
result: ClothingOuterHardsuitCombatOfficer
- type: latheRecipe
parent: BaseHardsuitArmoredRecipe
id: ClothingOuterHardsuitCombatK9
result: ClothingOuterHardsuitCombatK9
- type: latheRecipe
parent: BaseHardsuitArmoredRecipe
id: ClothingOuterHardsuitCombatRiotK9
result: ClothingOuterHardsuitCombatRiotK9
## Logistics
- type: latheRecipe
parent: BaseHardsuitRecipe

View File

@ -187,6 +187,14 @@
Steel: 500
Plastic: 1000
- type: latheRecipe
id: ClothingOuterArmorDuraVestK9
result: ClothingOuterArmorDuraVestK9
completetime: 10
materials:
Steel: 500
Plastic: 1000
- type: latheRecipe
id: ClothingOuterArmorReflective
result: ClothingOuterArmorReflective

View File

@ -93,6 +93,8 @@
cost: 8000
recipeUnlocks:
- ClothingOuterHardsuitCombatOfficer
- ClothingOuterHardsuitCombatK9
- ClothingOuterHardsuitCombatRiotK9
- type: technology
id: ExperimentalSalvageWeaponry

View File

@ -6,6 +6,7 @@
id: MobSecDogLaikaGear
equipment:
eyes: ClothingEyesGlassesSunglasses
outerClothing: ClothingOuterArmorDuraVestK9
#Oldstation Roles

View File

@ -180,6 +180,12 @@
- type: Tag
id: Hoodie
- type: Tag
id: K9Armor # Limits what equipment K9 units can wear
- type: Tag
id: K9 # Lets K9 units wear specialized clothing
- type: Tag
id: AncientBookDamagedCommon

Binary file not shown.

After

Width:  |  Height:  |  Size: 860 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -59,6 +59,10 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4,
@ -82,4 +86,4 @@
]
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 776 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -64,6 +64,10 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4,
@ -91,4 +95,4 @@
]
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Drawn by Ubaser.",
"copyright": "Drawn by Ubaser, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -41,9 +41,13 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4
}
]
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 611 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 520 B

View File

@ -0,0 +1,25 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Original work by HTMLSystem",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "icon-flash"
},
{
"name": "off-equipped-HELMET",
"directions": 4
},
{
"name": "on-equipped-HELMET",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 556 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 486 B

View File

@ -0,0 +1,25 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Original work by HTMLSystem",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "icon-flash"
},
{
"name": "off-equipped-HELMET",
"directions": 4
},
{
"name": "on-equipped-HELMET",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 895 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 708 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Original work by TJohnson.",
"copyright": "Original work by TJohnson. Added secdog by BarryNorfolk.",
"size": {
"x": 32,
"y": 32
@ -10,10 +10,17 @@
{
"name": "icon"
},
{
"name": "icon-secdog"
},
{
"name": "equipped-OUTERCLOTHING",
"directions": 4
},
{
"name": "equipped-OUTERCLOTHING-secdog",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 583 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 662 B

View File

@ -0,0 +1,26 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Original work by HTMLSystem",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "equipped-OUTERCLOTHING",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 539 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 671 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 771 B

View File

@ -0,0 +1,26 @@
{
"version": 1,
"license": "CC0-1.0",
"copyright": "Original work by HTMLSystem",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "equipped-OUTERCLOTHING",
"directions": 4
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

View File

@ -5,8 +5,12 @@
"y": 32
},
"license": "CC0-1.0",
"copyright": "Original work by TJohnson.",
"copyright": "Original work by TJohnson. Splits performed by BarryNorfolk",
"states": [
{
"name": "base",
"directions": 4
},
{
"name": "secdog",
"directions": 4,
@ -53,6 +57,56 @@
]
]
},
{
"name": "tail_static",
"directions": 4
},
{
"name": "tail_animated",
"directions": 4,
"delays": [
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
],
[
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1,
0.1
]
]
},
{
"name": "secdog_crit",
"delays": [

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 829 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -59,6 +59,10 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4,

Binary file not shown.

After

Width:  |  Height:  |  Size: 859 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -59,6 +59,10 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4,

Binary file not shown.

After

Width:  |  Height:  |  Size: 826 B

View File

@ -1,7 +1,7 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE. Further modified by TJohnson.",
"copyright": "Taken from tgstation at commit https://github.com/tgstation/tgstation/commit/1592a112e3d33eec4a0704b518a138d5a976f455, modified by Radezolid for SUITSTORAGE. Further modified by TJohnson, modified by BarryNorkfolk for SUITSTORAGE-secdog",
"size": {
"x": 32,
"y": 32
@ -59,6 +59,10 @@
"name": "equipped-SUITSTORAGE",
"directions": 4
},
{
"name": "equipped-SUITSTORAGE-secdog",
"directions": 4
},
{
"name": "on-equipped-SUITSTORAGE",
"directions": 4,