From 266f9782282ea5bfd0c7828ccc1ef594d1deae6a Mon Sep 17 00:00:00 2001 From: beck-thompson <107373427+beck-thompson@users.noreply.github.com> Date: Tue, 18 Aug 2026 20:13:53 +0000 Subject: [PATCH] Add xenoborg bio knife (#45152) * bio knife * fix licence * fix damage override * remove weird null * Abstrac the methods * add new icon * add bool * add hash set --- .../Metabolism/MetabolizerSystem.cs | 38 ++++++++++ Content.Shared/Verbs/VerbCategory.cs | 3 + .../BonusDamageOnMetabolismComponent.cs | 38 ++++++++++ .../BonusDamageOnMetabolismSystem.cs | 65 ++++++++++++++++++ Resources/Locale/en-US/verbs/verb-system.ftl | 1 + .../Specific/Robotics/borg_modules.yml | 17 +++++ .../Entities/Objects/Weapons/Melee/knife.yml | 28 ++++++++ .../Actions/actions_borg.rsi/meta.json | 5 +- .../actions_borg.rsi/xenoborg-bio-knife.png | Bin 0 -> 979 bytes .../borgmodule.rsi/icon-xenoborg-bio.png | Bin 0 -> 293 bytes .../Robotics/borgmodule.rsi/meta.json | 5 +- .../Weapons/Melee/bioknife.rsi/icon.png | Bin 0 -> 428 bytes .../Weapons/Melee/bioknife.rsi/meta.json | 14 ++++ 13 files changed, 212 insertions(+), 2 deletions(-) create mode 100644 Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismComponent.cs create mode 100644 Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismSystem.cs create mode 100644 Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-bio-knife.png create mode 100644 Resources/Textures/Objects/Specific/Robotics/borgmodule.rsi/icon-xenoborg-bio.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/bioknife.rsi/icon.png create mode 100644 Resources/Textures/Objects/Weapons/Melee/bioknife.rsi/meta.json diff --git a/Content.Shared/Metabolism/MetabolizerSystem.cs b/Content.Shared/Metabolism/MetabolizerSystem.cs index 4df78e1a84..76e46a8a49 100644 --- a/Content.Shared/Metabolism/MetabolizerSystem.cs +++ b/Content.Shared/Metabolism/MetabolizerSystem.cs @@ -340,5 +340,43 @@ public sealed partial class MetabolizerSystem : EntitySystem return ent.Comp.MetabolizerTypes.Add(metabolizer); } + + /// + /// Checks if the given organ has the given metabolizer. + /// + /// The organ we are checking. + /// The metabolizer we are checking for. + /// Returns true if the organ has the given metabolizer, false if the organ doesn't (this includes it not being a metabolizer at all) + public bool HasMetabolizer(Entity targetOrgan, ProtoId targetMetabolizer) + { + if (!Resolve(targetOrgan.Owner, ref targetOrgan.Comp, false)) + return false; + + if (targetOrgan.Comp.MetabolizerTypes is null) + return false; + + if (!targetOrgan.Comp.MetabolizerTypes.Contains(targetMetabolizer)) + return false; + + return true; + } + + /// + /// Checks if a given body has a metabolizer of the target type. + /// + /// The body we are checking for the metabolizer. + /// The metabolizer we are checking for. + /// + public bool BodyHasMetabolizer(EntityUid targetBody, ProtoId targetMetabolizer) + { + var metabolisms = _body.EnumerateOrgans(targetBody); + foreach (var organ in metabolisms) + { + if (HasMetabolizer((organ, organ.Comp2), targetMetabolizer)) + return true; + } + + return false; + } } diff --git a/Content.Shared/Verbs/VerbCategory.cs b/Content.Shared/Verbs/VerbCategory.cs index c824858af4..6ca28af6c0 100644 --- a/Content.Shared/Verbs/VerbCategory.cs +++ b/Content.Shared/Verbs/VerbCategory.cs @@ -88,5 +88,8 @@ namespace Content.Shared.Verbs public static readonly VerbCategory Adjust = new("verb-categories-adjust", "/Textures/Interface/VerbIcons/screwdriver.png"); + + public static readonly VerbCategory Metabolizers = + new("verb-categories-select-target", "/Textures/Interface/VerbIcons/group.svg.192dpi.png"); } } diff --git a/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismComponent.cs b/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismComponent.cs new file mode 100644 index 0000000000..4bc4817194 --- /dev/null +++ b/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismComponent.cs @@ -0,0 +1,38 @@ +using Content.Shared.Damage; +using Content.Shared.Metabolism; +using Content.Shared.Mobs; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; + +namespace Content.Shared.Weapons.Melee.Metabolizer; + +/// +/// Deals bonus damage when hitting an entity with the selected metabolizer +/// +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] +public sealed partial class BonusDamageOnMetabolismComponent : Component +{ + /// + /// List of metabolizers that cannot be selected. + /// + [DataField] + public List> ExcludedMetabolizers = new(); + + /// + /// The currently selected metabolizer, can be null for none. + /// + [DataField, AutoNetworkedField] + public ProtoId? SelectedMetabolizer; + + /// + /// Amount of extra damage to deal when hitting an entity with the SelectedMetabolizer metabolism + /// + [DataField(required: true)] + public DamageSpecifier Damage = new(); + + /// + /// Will only do damage to mobs in one of these states. If null, doesn't care about the state of the mob. + /// + [DataField] + public HashSet ValidMobStates = [ MobState.Alive, MobState.Critical ]; +} diff --git a/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismSystem.cs b/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismSystem.cs new file mode 100644 index 0000000000..a68af6071f --- /dev/null +++ b/Content.Shared/Weapons/Melee/Metabolizer/BonusDamageOnMetabolismSystem.cs @@ -0,0 +1,65 @@ +using System.Linq; +using Content.Shared.Metabolism; +using Content.Shared.Mobs.Components; +using Content.Shared.Mobs.Systems; +using Content.Shared.Verbs; +using Content.Shared.Weapons.Melee.Events; + +namespace Content.Shared.Weapons.Melee.Metabolizer; + +public sealed partial class BonusDamageOnMetabolismSystem : EntitySystem +{ + [Dependency] private MetabolizerSystem _metabolizer = default!; + + [SubscribeLocalEvent] + private void OnGetVerb(Entity ent, ref GetVerbsEvent args) + { + if (!args.CanAccess || !args.CanInteract) + return; + + var allMetabolizers = ProtoMan.EnumeratePrototypes().ToList().OrderBy(x => Loc.GetString(x.LocalizedName)); + + byte index = 0; + foreach (var metabolizer in allMetabolizers) + { + if (ent.Comp.ExcludedMetabolizers.Contains(metabolizer)) + continue; + + var currIndex = index; + var verb = new Verb + { + Priority = currIndex, + Category = VerbCategory.Metabolizers, + Disabled = ent.Comp.SelectedMetabolizer == metabolizer, + Act = () => + { + ent.Comp.SelectedMetabolizer = metabolizer; + Dirty(ent); + }, + Text = Loc.GetString(metabolizer.LocalizedName), + }; + args.Verbs.Add(verb); + index++; + } + } + + [SubscribeLocalEvent] + private void OnSwingTrigger(Entity ent, ref MeleeHitEvent args) + { + if (ent.Comp.SelectedMetabolizer is null) + return; + + foreach (var hitEntity in args.HitEntities) + { + if (TryComp(hitEntity, out var mobState) && !ent.Comp.ValidMobStates.Contains(mobState.CurrentState)) + continue; + + if (!_metabolizer.BodyHasMetabolizer(hitEntity, ent.Comp.SelectedMetabolizer.Value)) + continue; + + // Add the bonus damage and quit! + args.BonusDamage =+ ent.Comp.Damage; + return; + } + } +} diff --git a/Resources/Locale/en-US/verbs/verb-system.ftl b/Resources/Locale/en-US/verbs/verb-system.ftl index cc9167d906..e396618ebe 100644 --- a/Resources/Locale/en-US/verbs/verb-system.ftl +++ b/Resources/Locale/en-US/verbs/verb-system.ftl @@ -29,6 +29,7 @@ verb-categories-select-type = Select Type verb-categories-fax = Set Destination verb-categories-power-level = Power Level verb-categories-adjust = Adjust +verb-categories-select-target = Set Target verb-common-toggle-light = Toggle light verb-common-close = Close diff --git a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml index d0d8777036..449d533e6a 100644 --- a/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml +++ b/Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml @@ -1827,6 +1827,23 @@ - type: BorgModuleIcon icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-sword2-module } +- type: entity + parent: [ BaseXenoborgModuleScout, BaseProviderBorgModule, BaseXenoborgContraband ] + id: XenoborgModuleBioKinfe + name: bio knife xenoborg module + description: Module with an bio knife. + components: + - type: Sprite + layers: + - state: xenoborg_scout + - state: icon-xenoborg-sword2 + - type: ItemBorgModule + hands: + - item: XenoborgBioKnife + - item: JetpackXenoborg + - type: BorgModuleIcon + icon: { sprite: Interface/Actions/actions_borg.rsi, state: xenoborg-bio-knife } + - type: entity parent: [ BaseXenoborgModuleStealth, BaseProviderBorgModule, BaseXenoborgContraband ] id: XenoborgModuleHypo diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml index e10669f291..6faa52681b 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml @@ -188,6 +188,34 @@ sprite: Objects/Weapons/Melee/kukri_knife.rsi storedSprite: null +- type: entity + parent: [ CombatKnife, BaseXenoborgContraband ] + id: XenoborgBioKnife + name: bio knife + description: Melds itself to specific metabolizers and will deal extra damage to those targets! + components: + - type: Sprite + sprite: Objects/Weapons/Melee/bioknife.rsi + state: icon + - type: MeleeWeapon + attackRate: 1.0 + damage: + types: + Slash: 10 + - type: Item + sprite: Objects/Weapons/Melee/bioknife.rsi + - type: BonusDamageOnMetabolism + selectedMetabolizer: Human + excludedMetabolizers: + - Vox # Their biology is resistant to this! + # The rest of these are just so we don't clog up the menu with useless items. + - Bloodsucker + - Dragon + - Changeling + damage: + types: + Poison: 10 + - type: entity parent: [ClothingHeadHatGreyFlatcap, BaseMinorContraband] id: BladedFlatcapGrey diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json index 72aa505acb..0f1c2a0c1c 100644 --- a/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json +++ b/Resources/Textures/Interface/Actions/actions_borg.rsi/meta.json @@ -1,7 +1,7 @@ { "version": 1, "license": "CC-BY-SA-3.0", - "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9, inflatable module by FungiFellow (GitHub), Module actions by Scarky0. chem, adv-chem, and adv-mining by mubururu_, xenoborg actions by Samuka-C (github), advclown by ThatGuyUSA. c20r and esword by RedBookcase on Github, artistry-module by Kittygyat, with help from TiniestShark, xenoborg-laser3-module and xenoborg-deflector-module by august-sun(GitHub), Xenoborg Nanite action by YoungThugSS14 (Gihutb)", + "copyright": "Taken from vgstation at commit https://github.com/vgstation-coders/vgstation13/commit/cdbcb1e858b11f083994a7a269ed67ef5b452ce9, inflatable module by FungiFellow (GitHub), Module actions by Scarky0. chem, adv-chem, and adv-mining by mubururu_, xenoborg actions by Samuka-C (github), advclown by ThatGuyUSA. c20r and esword by RedBookcase on Github, artistry-module by Kittygyat, with help from TiniestShark, xenoborg-laser3-module and xenoborg-deflector-module by august-sun(GitHub), Xenoborg Nanite action and xenoborg-bio-knife by YoungThugSS14 (Gihutb)", "size": { "x": 32, "y": 32 @@ -193,6 +193,9 @@ { "name":"xenoborg-sword2-module" }, + { + "name":"xenoborg-bio-knife" + }, { "name":"xenoborg-tile-module" }, diff --git a/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-bio-knife.png b/Resources/Textures/Interface/Actions/actions_borg.rsi/xenoborg-bio-knife.png new file mode 100644 index 0000000000000000000000000000000000000000..55d07fe3a14864c25517160115f0e2cac03ee52d GIT binary patch literal 979 zcmZ`&Z%7ky82-((od~)Qei+o47EutyAOlw&%t9+GmPHU|ZABlV%$zM-x6@6FShmbh z7TKDH(2~#zk+#^}q|~r{% zTX*uhc+dLaklnf?cT;%jde0^OGt-{3aaoiuj7*t_5R07o9CCa5&Fbx)`PgWER(5%< zS?q3JIkHt*y%UIKhF4&lNW+jP5#M~v4B2lFy{m5wJ$jBcj>f1r57E|nY=lwY8Dizb z?s1}*n$o)IctA|mspv(6bt>ABhlalkAY2Kt5Qj|!f%TyymQH2OtgsVhlYl+Zc`_Y=*FxO>@r+8;1 zn(dAYrll&G9;5DAsYiU%pirBu}Sdhsg@u*LR@fw=TXml_0vK?-NY zM%fi7(^pM%szG+|A8ynb5lbX7thBTu(;^I>6D>+XQ0i9%|WW9 zwa}VDGYKK9-6?1b(#q4jGEozyg>A9FGtv?Z$mSctPG!c+iSXYtK~x3?65#C zkK&-aJ79qYs$U&*y=Deb)-|w)7?20k9oUqgfN zd%vJ|H1;8x7xw9u&yWV}KQ74zvtxZ~5DB6!t1eZ=_8h~dHv9@7iaj>uMI!qv7D@oL zyH~DhHo6ELZ1YY_fKRo&k(bTm;$au}aw$N^xBNo*Zo$N)UYm56f?^$9ARAHLGrE(_ zaBSJ?*nGc>Gi0q4@e?FN9O>)xYH623XbCAslBN=n9-i|!DogxO3Ljrb<&|Wc;|B9$ u$$O1_cYpNSwSk3J%W507^>757#dm_7=8hT8eT9klo~KFyh>nTu$sZZAYL$MSD+10g0sLQ zvY3H^TNs2H8D`Cq01C2~c>21szvAF!6VrUWEg%pmRPE{F7!q+fIYEMTae_!sQ=kDC zn}ornRR_}(Pt+GCEAo7to_^mjr!(GpQ>D(5EQjrX0`EGrfxu3+%1urUoie@#-iaov z4V^K)C#Fq)UA;-RAxBYlTCfPKLAPcv+a%$Kr#HWUFirXQzo$3L7pI=mfLO$|3FyYX W@}Px$W=TXrR9J=Wl`(6=KorOSmTpo(N|xKYC~;6i2GHkL=N2DOO}3x?k#jH zWat;@Cy=fZ7i-7vQYmJB4keOHB0ZD4xa1Fk+`UJB|Gbxb0V*9qL9n9e{!}-h*E_Gp zW1csq_*%JvAXt&_FYxm0r(*s7Jtg-}bi#jrpLTEGY)n#cr3UiA0id=opc>w_>Tulw zI5&mwLRkg!z?B&ojqcF3>cXB394cDS>0FXwTwy*7Q?dWxg$*R(Q5EL1PzsJBE!YQ- zBm+m@3S_}ucOc}*z;Wm#WQ-92tk>T#3Bbtqgbg2uA zF-Ckiri-&B*_J~9$n&NY|M6&r10dUSsNFKD-7*P4d2l`)6M*EUUvYR;_ENw{g)_z| z>UkM(;pn7~+G}Q>N@b4XkM{?Bel4`avmQ>i