diff --git a/Content.Server/NPC/Components/NPCImprintingOnSpawnBehaviourComponent.cs b/Content.Server/NPC/Components/NPCImprintingOnSpawnBehaviourComponent.cs
new file mode 100644
index 0000000000..26439d2b30
--- /dev/null
+++ b/Content.Server/NPC/Components/NPCImprintingOnSpawnBehaviourComponent.cs
@@ -0,0 +1,34 @@
+using Content.Shared.Whitelist;
+using Robust.Shared.Collections;
+
+namespace Content.Server.NPC.Components;
+///
+/// A component that makes the entity friendly to nearby creatures it sees on init.
+///
+[RegisterComponent]
+public sealed partial class NPCImprintingOnSpawnBehaviourComponent : Component
+{
+ ///
+ /// filter who can be a friend to this creature
+ ///
+ [DataField]
+ public EntityWhitelist? Whitelist;
+
+ ///
+ /// when a creature appears, it will memorize all creatures in the radius to remember them as friends
+ ///
+ [DataField]
+ public float SpawnFriendsSearchRadius = 3f;
+
+ ///
+ /// if there is a FollowCompound in HTN, the target of the following will be selected from random nearby targets when it appears
+ ///
+ [DataField]
+ public bool Follow = true;
+
+ ///
+ /// is used to determine who became a friend from this component
+ ///
+ [DataField]
+ public List Friends = new();
+}
diff --git a/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs b/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs
new file mode 100644
index 0000000000..cfd3b08c61
--- /dev/null
+++ b/Content.Server/NPC/Systems/NPCImprintingOnSpawnBehaviourSystem.cs
@@ -0,0 +1,49 @@
+using System.Numerics;
+using Content.Shared.NPC.Components;
+using Content.Shared.NPC.Systems;
+using Robust.Shared.Collections;
+using Robust.Shared.Map;
+using Robust.Shared.Random;
+using NPCImprintingOnSpawnBehaviourComponent = Content.Server.NPC.Components.NPCImprintingOnSpawnBehaviourComponent;
+
+namespace Content.Server.NPC.Systems;
+
+public sealed partial class NPCImprintingOnSpawnBehaviourSystem : SharedNPCImprintingOnSpawnBehaviourSystem
+{
+ [Dependency] private readonly EntityLookupSystem _lookup = default!;
+ [Dependency] private readonly NPCSystem _npc = default!;
+ [Dependency] private readonly IRobustRandom _random = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent(OnMapInit);
+ }
+
+ private void OnMapInit(Entity imprinting, ref MapInitEvent args)
+ {
+ HashSet friends = new();
+ _lookup.GetEntitiesInRange(imprinting, imprinting.Comp.SpawnFriendsSearchRadius, friends);
+
+ foreach (var friend in friends)
+ {
+ if (imprinting.Comp.Whitelist?.IsValid(friend) != false)
+ {
+ AddImprintingTarget(imprinting, friend, imprinting.Comp);
+ }
+ }
+
+ if (imprinting.Comp.Follow && imprinting.Comp.Friends.Count > 0)
+ {
+ var mommy = _random.Pick(imprinting.Comp.Friends);
+ _npc.SetBlackboard(imprinting, NPCBlackboard.FollowTarget, new EntityCoordinates(mommy, Vector2.Zero));
+ }
+ }
+
+ public void AddImprintingTarget(EntityUid entity, EntityUid friend, NPCImprintingOnSpawnBehaviourComponent component)
+ {
+ component.Friends.Add(friend);
+ var exception = EnsureComp(entity);
+ exception.Ignored.Add(friend);
+ }
+}
diff --git a/Content.Server/NPC/Systems/NPCSystem.cs b/Content.Server/NPC/Systems/NPCSystem.cs
index 8abe0f7f54..9108629435 100644
--- a/Content.Server/NPC/Systems/NPCSystem.cs
+++ b/Content.Server/NPC/Systems/NPCSystem.cs
@@ -5,6 +5,7 @@ using Content.Shared.CCVar;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Systems;
using Content.Shared.NPC;
+using Content.Shared.NPC.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.Configuration;
using Robust.Shared.Player;
diff --git a/Content.Shared/NPC/Components/FactionExceptionComponent.cs b/Content.Shared/NPC/Components/FactionExceptionComponent.cs
index 54de0404c2..ba7940d502 100644
--- a/Content.Shared/NPC/Components/FactionExceptionComponent.cs
+++ b/Content.Shared/NPC/Components/FactionExceptionComponent.cs
@@ -7,7 +7,7 @@ namespace Content.Shared.NPC.Components;
/// Prevents an NPC from attacking ignored entities from enemy factions.
/// Can be added to if pettable, see PettableFriendComponent.
///
-[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem))]
+[RegisterComponent, NetworkedComponent, Access(typeof(NpcFactionSystem), typeof(SharedNPCImprintingOnSpawnBehaviourSystem))] // TO DO (Metalgearsloth): If we start adding a billion access overrides they should be going through a system as then there's no reason to have access, but I'll fix this when I rework npcs.
public sealed partial class FactionExceptionComponent : Component
{
///
diff --git a/Content.Shared/NPC/Systems/SharedNPCImprintingOnSpawnBehaviourSystem.cs b/Content.Shared/NPC/Systems/SharedNPCImprintingOnSpawnBehaviourSystem.cs
new file mode 100644
index 0000000000..f06d496e9e
--- /dev/null
+++ b/Content.Shared/NPC/Systems/SharedNPCImprintingOnSpawnBehaviourSystem.cs
@@ -0,0 +1,5 @@
+namespace Content.Shared.NPC.Systems;
+
+public abstract partial class SharedNPCImprintingOnSpawnBehaviourSystem : EntitySystem
+{
+}
diff --git a/Content.Shared/NPC/Systems/SharedNPCSystem.cs b/Content.Shared/NPC/Systems/SharedNPCSystem.cs
new file mode 100644
index 0000000000..247ab478a1
--- /dev/null
+++ b/Content.Shared/NPC/Systems/SharedNPCSystem.cs
@@ -0,0 +1,5 @@
+namespace Content.Shared.NPC.Systems;
+
+public abstract partial class SharedNPCSystem : EntitySystem
+{
+}
diff --git a/Resources/Locale/en-US/seeds/seeds.ftl b/Resources/Locale/en-US/seeds/seeds.ftl
index b398378288..10eb533770 100644
--- a/Resources/Locale/en-US/seeds/seeds.ftl
+++ b/Resources/Locale/en-US/seeds/seeds.ftl
@@ -41,6 +41,8 @@ seeds-bluetomato-name = blue tomato
seeds-bluetomato-display-name = blue tomato plant
seeds-bloodtomato-name = blood tomato
seeds-bloodtomato-display-name = blood tomato plant
+seeds-killertomato-name = tomato killer
+seeds-killertomato-display-name = tomato killer plant
seeds-eggplant-name = eggplant
seeds-eggplant-display-name = eggplants
seeds-apple-name = apple
diff --git a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml
index 633a4ff3ca..6fca4e6a63 100644
--- a/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml
+++ b/Resources/Prototypes/Entities/Mobs/NPCs/miscellaneous.yml
@@ -66,3 +66,98 @@
interactFailureString: petting-failure-generic
interactSuccessSound:
path: /Audio/Animals/lizard_happy.ogg
+
+- type: entity
+ id: MobTomatoKiller
+ parent:
+ - BaseSimpleMob
+ - MobDamageable
+ - MobBloodstream
+ - MobFlammable
+ - MobCombat
+ name: tomato killer
+ description: it seems today it's not you eating tomatoes, it's the tomatoes eating you.
+ components:
+ - type: Item
+ size: Huge
+ - type: NpcFactionMember
+ factions:
+ - SimpleHostile
+ - type: HTN
+ rootTask:
+ task: KillerTomatoCompound
+ - type: NPCImprintingOnSpawnBehaviour
+ whitelist:
+ components:
+ - HumanoidAppearance
+ - type: Sprite
+ sprite: Mobs/Demons/tomatokiller.rsi
+ noRot: true
+ layers:
+ - map: [ "enum.DamageStateVisualLayers.Base" ]
+ state: alive
+ - type: Bloodstream
+ bloodReagent: JuiceTomato
+ bloodMaxVolume: 50
+ chemicalMaxVolume: 30
+ - type: DamageStateVisuals
+ states:
+ Alive:
+ Base: alive
+ Dead:
+ Base: dead
+ - type: Butcherable
+ spawned:
+ - id: FoodMeatTomato
+ amount: 2
+ - type: Destructible
+ thresholds:
+ - trigger:
+ !type:DamageTypeTrigger
+ damageType: Blunt
+ damage: 100
+ behaviors:
+ - !type:GibBehavior { }
+ - type: MobThresholds
+ thresholds:
+ 0: Alive
+ 24: Dead
+ - type: Fixtures
+ fixtures:
+ fix1:
+ shape:
+ !type:PhysShapeCircle
+ radius: 0.30
+ density: 80
+ mask:
+ - MobMask
+ layer:
+ - MobLayer
+ - type: MeleeWeapon
+ hidden: true
+ damage:
+ groups:
+ Brute: 4
+ animation: WeaponArcBite
+ - type: Climbing
+ - type: NameIdentifier
+ group: GenericNumber
+ - type: SlowOnDamage
+ speedModifierThresholds:
+ 60: 0.7
+ 80: 0.5
+ - type: FootstepModifier
+ footstepSoundCollection:
+ path: /Audio/Effects/Footsteps/slime1.ogg
+ params:
+ volume: 3
+ - type: Tag
+ tags:
+ - FootstepSound
+ - Fruit
+ - type: Extractable
+ grindableSolutionName: bloodstream
+ - type: PotencyVisuals
+ - type: Appearance
+ - type: Produce
+ seedId: killerTomato
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml
index 2b232d643d..0a084dc246 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Hydroponics/seeds.yml
@@ -213,6 +213,16 @@
- type: Sprite
sprite: Objects/Specific/Hydroponics/blood_tomato.rsi
+- type: entity
+ parent: SeedBase
+ name: packet of killer tomato seeds
+ id: KillerTomatoSeeds
+ components:
+ - type: Seed
+ seedId: killerTomato
+ - type: Sprite
+ sprite: Objects/Specific/Hydroponics/tomatokiller.rsi
+
- type: entity
parent: SeedBase
name: packet of eggplant seeds
diff --git a/Resources/Prototypes/Hydroponics/seeds.yml b/Resources/Prototypes/Hydroponics/seeds.yml
index 71b20440f5..8bbbed6135 100644
--- a/Resources/Prototypes/Hydroponics/seeds.yml
+++ b/Resources/Prototypes/Hydroponics/seeds.yml
@@ -479,8 +479,10 @@
packetPrototype: BloodTomatoSeeds
productPrototypes:
- FoodBloodTomato
+ mutationPrototypes:
+ - killerTomato
harvestRepeat: Repeat
- lifespan: 25
+ lifespan: 60
maturation: 8
production: 6
yield: 2
@@ -500,6 +502,38 @@
Max: 4
PotencyDivisor: 25
+- type: seed
+ id: killerTomato
+ name: seeds-killertomato-name
+ noun: seeds-noun-seeds
+ displayName: seeds-killertomato-display-name
+ plantRsi: Objects/Specific/Hydroponics/tomatokiller.rsi
+ packetPrototype: KillerTomatoSeeds
+ productPrototypes:
+ - MobTomatoKiller
+ harvestRepeat: Repeat
+ lifespan: 25
+ maturation: 15
+ production: 6
+ yield: 2
+ potency: 10
+ waterConsumption: 0.60
+ nutrientConsumption: 0.70
+ idealLight: 8
+ idealHeat: 298
+ juicy: true
+ growthStages: 2
+ splatPrototype: PuddleSplatter
+ chemicals:
+ Blood:
+ Min: 1
+ Max: 10
+ PotencyDivisor: 10
+ JuiceTomato:
+ Min: 1
+ Max: 4
+ PotencyDivisor: 25
+
- type: seed
id: eggplant
name: seeds-eggplant-name
diff --git a/Resources/Prototypes/NPCs/mob.yml b/Resources/Prototypes/NPCs/mob.yml
index 740f7ca576..b0e1c8ae9b 100644
--- a/Resources/Prototypes/NPCs/mob.yml
+++ b/Resources/Prototypes/NPCs/mob.yml
@@ -77,3 +77,16 @@
- tasks:
- !type:HTNCompoundTask
task: IdleCompound
+
+- type: htnCompound
+ id: KillerTomatoCompound
+ branches:
+ - tasks:
+ - !type:HTNCompoundTask
+ task: MeleeCombatCompound
+ - tasks:
+ - !type:HTNCompoundTask
+ task: FollowCompound
+ - tasks:
+ - !type:HTNCompoundTask
+ task: IdleCompound
diff --git a/Resources/Textures/Mobs/Demons/tomatokiller.rsi/alive.png b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/alive.png
new file mode 100644
index 0000000000..5f459d518b
Binary files /dev/null and b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/alive.png differ
diff --git a/Resources/Textures/Mobs/Demons/tomatokiller.rsi/dead.png b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/dead.png
new file mode 100644
index 0000000000..4b3ee970a0
Binary files /dev/null and b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/dead.png differ
diff --git a/Resources/Textures/Mobs/Demons/tomatokiller.rsi/meta.json b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/meta.json
new file mode 100644
index 0000000000..57e4c7351f
--- /dev/null
+++ b/Resources/Textures/Mobs/Demons/tomatokiller.rsi/meta.json
@@ -0,0 +1,40 @@
+{
+ "version": 1,
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "license": "CC-BY-SA-3.0",
+ "copyright": " taken from TG on commit https://github.com/tgstation/tgstation/commit/7e5f13f558253e76865e81c9641b7ec68e57754b",
+ "states": [
+ {
+ "name": "alive",
+ "directions": 4,
+ "delays": [
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ],
+ [
+ 0.2,
+ 0.2,
+ 0.2
+ ]
+ ]
+ },
+ {
+ "name": "dead"
+ }
+ ]
+}
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/dead.png b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/dead.png
new file mode 100644
index 0000000000..0051c4dc73
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/dead.png differ
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/harvest.png b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/harvest.png
new file mode 100644
index 0000000000..46a3b38982
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/harvest.png differ
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/meta.json b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/meta.json
new file mode 100644
index 0000000000..84a4237a60
--- /dev/null
+++ b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/meta.json
@@ -0,0 +1,26 @@
+{
+ "version": 1,
+ "license": "CC-BY-SA-3.0",
+ "copyright": "Taken from https://github.com/vgstation-coders/vgstation13 at 1dbcf389b0ec6b2c51b002df5fef8dd1519f8068",
+ "size": {
+ "x": 32,
+ "y": 32
+ },
+ "states": [
+ {
+ "name": "dead"
+ },
+ {
+ "name": "harvest"
+ },
+ {
+ "name": "seed"
+ },
+ {
+ "name": "stage-1"
+ },
+ {
+ "name": "stage-2"
+ }
+ ]
+}
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/seed.png b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/seed.png
new file mode 100644
index 0000000000..110dc64a4f
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/seed.png differ
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-1.png b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-1.png
new file mode 100644
index 0000000000..0b1d58de9d
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-1.png differ
diff --git a/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-2.png b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-2.png
new file mode 100644
index 0000000000..6225f0c62d
Binary files /dev/null and b/Resources/Textures/Objects/Specific/Hydroponics/tomatokiller.rsi/stage-2.png differ