Add knife butchering and blood gibbing (#6770)

This commit is contained in:
mirrorcult
2022-02-18 15:57:42 -07:00
committed by GitHub
parent 67661ddbdb
commit 676ca21b5f
28 changed files with 223 additions and 45 deletions

View File

@@ -71,6 +71,7 @@ namespace Content.Client.Entry
"Stomach",
"SpeedLoader",
"Hitscan",
"Sharp",
"StunOnCollide",
"ExaminableDamage",
"RandomPottedPlant",

View File

@@ -102,7 +102,12 @@ namespace Content.Server.Body.Components
}
}
_entMan.EventBus.RaiseLocalEvent(Owner, new BeingGibbedEvent(), false);
_entMan.QueueDeleteEntity(Owner);
}
}
public sealed class BeingGibbedEvent : EntityEventArgs
{
}
}

View File

@@ -34,6 +34,7 @@ public sealed class BloodstreamSystem : EntitySystem
SubscribeLocalEvent<BloodstreamComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<BloodstreamComponent, DamageChangedEvent>(OnDamageChanged);
SubscribeLocalEvent<BloodstreamComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<BloodstreamComponent, BeingGibbedEvent>(OnBeingGibbed);
}
public override void Update(float frameTime)
@@ -132,6 +133,11 @@ public sealed class BloodstreamSystem : EntitySystem
}
}
private void OnBeingGibbed(EntityUid uid, BloodstreamComponent component, BeingGibbedEvent args)
{
SpillAllSolutions(uid, component);
}
/// <summary>
/// Attempt to transfer provided solution to internal solution.
/// </summary>
@@ -193,4 +199,22 @@ public sealed class BloodstreamSystem : EntitySystem
return true;
}
/// <summary>
/// BLOOD FOR THE BLOOD GOD
/// </summary>
public void SpillAllSolutions(EntityUid uid, BloodstreamComponent? component = null)
{
if (!Resolve(uid, ref component))
return;
var max = component.BloodSolution.MaxVolume + component.BloodTemporarySolution.MaxVolume +
component.ChemicalSolution.MaxVolume;
var tempSol = new Solution() { MaxVolume = max };
tempSol.AddSolution(component.BloodSolution);
tempSol.AddSolution(component.BloodTemporarySolution);
tempSol.AddSolution(component.ChemicalSolution);
_spillableSystem.SpillAt(uid, tempSol, "PuddleBlood", true);
}
}

View File

@@ -7,6 +7,7 @@ using Content.Server.Chemistry.EntitySystems;
using Content.Server.Chemistry.Components;
using Content.Server.Fluids.Components;
using Content.Server.Hands.Components;
using Content.Server.Kitchen.Components;
using Content.Server.Plants;
using Content.Server.Popups;
using Content.Shared.ActionBlocker;
@@ -802,9 +803,10 @@ namespace Content.Server.Botany.Components
return true;
}
if (tagSystem.HasTag(usingItem, "BotanySharp"))
if (_entMan.HasComponent<SharpComponent>(usingItem))
{
return DoHarvest(user);
}
if (_entMan.TryGetComponent<ProduceComponent?>(usingItem, out var produce))

View File

@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.Botany.Components;
using Content.Server.Kitchen.Components;
using Content.Shared.Examine;
using Content.Shared.Random.Helpers;
using Content.Shared.Tag;
@@ -136,7 +137,7 @@ public sealed partial class BotanySystem
public bool CanHarvest(SeedPrototype proto, EntityUid? held = null)
{
return !proto.Ligneous || proto.Ligneous && held != null && _tags.HasTag(held.Value, "BotanySharp");
return !proto.Ligneous || proto.Ligneous && held != null && HasComp<SharpComponent>(held);
}
#endregion

View File

@@ -1,4 +1,5 @@
using Content.Server.Botany.Components;
using Content.Server.Kitchen.Components;
using Content.Shared.ActionBlocker;
using Content.Shared.Interaction;
using Content.Shared.Random.Helpers;
@@ -9,8 +10,6 @@ namespace Content.Server.Botany.Systems;
public sealed class LogSystem : EntitySystem
{
[Dependency] private readonly TagSystem _tags = default!;
public override void Initialize()
{
base.Initialize();
@@ -20,7 +19,7 @@ public sealed class LogSystem : EntitySystem
private void OnInteractUsing(EntityUid uid, LogComponent component, InteractUsingEvent args)
{
if (_tags.HasTag(args.Used, "BotanySharp"))
if (HasComp<SharpComponent>(args.Used))
{
for (var i = 0; i < component.SpawnCount; i++)
{

View File

@@ -0,0 +1,14 @@
namespace Content.Server.Kitchen.Components;
/// <summary>
/// Applies to items that are capable of butchering entities, or
/// are otherwise sharp for some purpose.
/// </summary>
[RegisterComponent]
public sealed class SharpComponent : Component
{
public HashSet<EntityUid> Butchering = new();
[DataField("butcherDelayModifier")]
public float ButcherDelayModifier = 1.0f;
}

View File

@@ -92,7 +92,7 @@ namespace Content.Server.Kitchen.EntitySystems
if (!Resolve(uid, ref component) || !Resolve(victimUid, ref butcherable))
return;
component.MeatPrototype = butcherable.MeatPrototype;
component.MeatPrototype = butcherable.SpawnedPrototype;
component.MeatParts = butcherable.Pieces;
// This feels not okay, but entity is getting deleted on "Spike", for now...
@@ -164,7 +164,7 @@ namespace Content.Server.Kitchen.EntitySystems
return false;
}
if (!Resolve(victimUid, ref butcherable, false) || butcherable.MeatPrototype == null)
if (!Resolve(victimUid, ref butcherable, false) || butcherable.SpawnedPrototype == null)
{
_popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", victimUid), ("this", uid)), victimUid, Filter.Entities(userUid));
return false;
@@ -180,6 +180,9 @@ namespace Content.Server.Kitchen.EntitySystems
!Resolve(victimUid, ref butcherable) || butcherable.BeingButchered)
return false;
if (butcherable.Type != ButcheringType.Spike)
return false;
// THE WHAT? (again)
// Prevent dead from being spiked TODO: Maybe remove when rounds can be played and DOT is implemented
if (Resolve(victimUid, ref mobState, false) &&
@@ -201,7 +204,7 @@ namespace Content.Server.Kitchen.EntitySystems
butcherable.BeingButchered = true;
component.InUse = true;
var doAfterArgs = new DoAfterEventArgs(userUid, component.SpikeDelay, default, uid)
var doAfterArgs = new DoAfterEventArgs(userUid, component.SpikeDelay + butcherable.ButcherDelay, default, uid)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,

View File

@@ -0,0 +1,105 @@
using Content.Server.DoAfter;
using Content.Server.Kitchen.Components;
using Content.Server.Popups;
using Content.Shared.Body.Components;
using Content.Shared.Interaction;
using Content.Shared.MobState.Components;
using Content.Shared.Nutrition.Components;
using Content.Shared.Popups;
using Robust.Shared.Player;
namespace Content.Server.Kitchen.EntitySystems;
public sealed class SharpSystem : EntitySystem
{
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharpComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<SharpButcherDoafterComplete>(OnDoafterComplete);
SubscribeLocalEvent<SharpButcherDoafterCancelled>(OnDoafterCancelled);
}
private void OnAfterInteract(EntityUid uid, SharpComponent component, AfterInteractEvent args)
{
if (args.Target is null || !TryComp<SharedButcherableComponent>(args.Target, out var butcher))
return;
if (butcher.Type != ButcheringType.Knife)
return;
if (TryComp<MobStateComponent>(args.Target, out var mobState) && !mobState.IsDead())
return;
if (!component.Butchering.Add(args.Target.Value))
return;
var doAfter =
new DoAfterEventArgs(args.User, component.ButcherDelayModifier * butcher.ButcherDelay, default, args.Target)
{
BreakOnTargetMove = true,
BreakOnUserMove = true,
BreakOnDamage = true,
BreakOnStun = true,
NeedHand = true,
BroadcastFinishedEvent = new SharpButcherDoafterComplete { User = args.User, Entity = args.Target.Value, Sharp = uid },
BroadcastCancelledEvent = new SharpButcherDoafterCancelled { Entity = args.Target.Value, Sharp = uid }
};
_doAfterSystem.DoAfter(doAfter);
}
private void OnDoafterComplete(SharpButcherDoafterComplete ev)
{
if (!TryComp<SharedButcherableComponent>(ev.Entity, out var butcher))
return;
if (!TryComp<SharpComponent>(ev.Sharp, out var sharp))
return;
sharp.Butchering.Remove(ev.Entity);
EntityUid popupEnt = default;
for (int i = 0; i < butcher.Pieces; i++)
{
popupEnt = Spawn(butcher.SpawnedPrototype, Transform(ev.Entity).Coordinates);
}
_popupSystem.PopupEntity(Loc.GetString("butcherable-knife-butchered-success", ("target", ev.Entity), ("knife", ev.Sharp)),
popupEnt, Filter.Entities(ev.User));
if (TryComp<SharedBodyComponent>(ev.Entity, out var body))
{
body.Gib();
}
else
{
QueueDel(ev.Entity);
}
}
private void OnDoafterCancelled(SharpButcherDoafterCancelled ev)
{
if (!TryComp<SharpComponent>(ev.Sharp, out var sharp))
return;
sharp.Butchering.Remove(ev.Entity);
}
}
public sealed class SharpButcherDoafterComplete : EntityEventArgs
{
public EntityUid Entity;
public EntityUid Sharp;
public EntityUid User;
}
public sealed class SharpButcherDoafterCancelled : EntityEventArgs
{
public EntityUid Entity;
public EntityUid Sharp;
}

View File

@@ -14,7 +14,7 @@ namespace Content.Shared.Kitchen.Components
{
[ViewVariables]
[DataField("delay")]
public float SpikeDelay = 12.0f;
public float SpikeDelay = 7.0f;
[ViewVariables(VVAccess.ReadWrite)]
[DataField("sound")]

View File

@@ -15,13 +15,19 @@ namespace Content.Shared.Nutrition.Components
{
//TODO: List for sub-products like animal-hides, organs and etc?
[ViewVariables]
[DataField("meat", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string MeatPrototype = "FoodMeat";
[DataField("spawned", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
public string SpawnedPrototype = "FoodMeat";
[ViewVariables]
[DataField("pieces")]
public int Pieces = 5;
[DataField("butcherDelay")]
public float ButcherDelay = 8.0f;
[DataField("butcheringType")]
public ButcheringType Type = ButcheringType.Knife;
/// <summary>
/// Prevents butchering same entity on two and more spikes simultaneously and multiple doAfters on the same Spike
/// </summary>
@@ -32,7 +38,14 @@ namespace Content.Shared.Nutrition.Components
// CanDropOn behaviors as well (IDragDropOn)
bool IDraggable.CanDrop(CanDropEvent args)
{
return true;
return Type != ButcheringType.Knife;
}
}
public enum ButcheringType
{
Knife, // e.g. goliaths
Spike, // e.g. monkeys
Gibber // e.g. humans. TODO
}
}

View File

@@ -0,0 +1 @@
butcherable-knife-butchered-success = You butcher { THE($target) } with { THE($knife) }.

View File

@@ -100,6 +100,7 @@
netsync: false
- type: Puddle
slipThreshold: 20
overflowVolume: 50
- type: Evaporation
evaporateTime: 400 # very slow
- type: Appearance

View File

@@ -37,7 +37,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: InteractionPopup
successChance: 0.2
@@ -132,7 +132,7 @@
crit: dead-0
dead: dead-0
- type: Butcherable
meat: FoodMeatChicken
spawned: FoodMeatChicken
pieces: 1
- type: InteractionPopup
successChance: 0.8
@@ -160,7 +160,7 @@
crit: dead-0
dead: dead-0
- type: Butcherable
meat: FoodMeatDuck
spawned: FoodMeatDuck
pieces: 1
- type: InteractionPopup
successChance: 0.9
@@ -188,7 +188,7 @@
crit: dead-1
dead: dead-1
- type: Butcherable
meat: FoodMeatDuck
spawned: FoodMeatDuck
pieces: 1
- type: InteractionPopup
successChance: 0.9
@@ -216,7 +216,7 @@
crit: dead-2
dead: dead-2
- type: Butcherable
meat: FoodMeatDuck
spawned: FoodMeatDuck
pieces: 1
- type: InteractionPopup
successChance: 0.9
@@ -321,7 +321,7 @@
quantity: 25
updateRate: 30
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 5
- type: Grammar
attributes:
@@ -365,7 +365,7 @@
dead: dead
- type: AsteroidRockVisualizer
- type: Butcherable
meat: FoodMeatCrab
spawned: FoodMeatCrab
pieces: 2
- type: InteractionPopup
successChance: 0.5
@@ -403,7 +403,7 @@
quantity: 25
updateRate: 20
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 4
- type: Grammar
attributes:
@@ -433,7 +433,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeatChicken
spawned: FoodMeatChicken
pieces: 2
- type: InteractionPopup # TODO: Make it so there's a separate chance to make certain animals outright hostile towards you.
successChance: 0.1 # Yeah, good luck with that.
@@ -473,7 +473,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 4
- type: entity
@@ -581,7 +581,8 @@
sprite: Mobs/Effects/onfire.rsi
normalState: Monkey_burning
- type: Butcherable
meat: FoodMeat
butcheringType: Spike
spawned: FoodMeat
pieces: 3
- type: MonkeyAccent
@@ -645,7 +646,7 @@
- ReagentId: Nutriment
Quantity: 5
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: ReplacementAccent
accent: mouse
@@ -730,7 +731,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: InteractionPopup
successChance: 0.3
@@ -773,7 +774,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: InteractionPopup
successChance: 0.6
@@ -817,7 +818,7 @@
crit: dead
dead: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: InteractionPopup
successChance: 0.6
@@ -857,7 +858,7 @@
crit: penguin_dead
dead: penguin_dead
- type: Butcherable
meat: FoodMeatPenguin
spawned: FoodMeatPenguin
pieces: 3
- type: InteractionPopup
successChance: 0.5
@@ -900,7 +901,7 @@
# dead: dead
# crit: dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 1
- type: InteractionPopup
successChance: 0.6
@@ -942,7 +943,7 @@
crit: tarantula_dead
dead: tarantula_dead
- type: Butcherable
meat: FoodMeatSpider
spawned: FoodMeatSpider
pieces: 2
- type: InteractionPopup
successChance: 0.5

View File

@@ -43,7 +43,7 @@
crit: crit
dead: dead
- type: Butcherable
meat: FoodMeat # TODO: CrapMeat or FishMeat
spawned: FoodMeat # TODO: CrapMeat or FishMeat # - 2022-02-17 LMAO crap meat
pieces: 2
- type: entity

View File

@@ -34,7 +34,7 @@
crit: corgi_dead
dead: corgi_dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 3
- type: ReplacementAccent
accent: dog
@@ -211,7 +211,7 @@
crit: cat_dead
dead: cat_dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 2
- type: ReplacementAccent
accent: cat
@@ -333,7 +333,7 @@
crit: sloth_dead
dead: sloth_dead
- type: Butcherable
meat: FoodMeat
spawned: FoodMeat
pieces: 3
- type: InteractionPopup
successChance: 0.9

View File

@@ -76,5 +76,6 @@
dead: dead
- type: Puller
- type: Butcherable
meat: FoodMeatXeno
spawned: FoodMeatXeno
butcheringType: Spike
pieces: 5

View File

@@ -283,7 +283,8 @@
type: StrippableBoundUserInterface
- type: Puller
- type: Butcherable
meat: FoodMeat
butcheringType: Spike # TODO human.
spawned: FoodMeat
- type: Recyclable
safe: false
- type: Speech

View File

@@ -135,7 +135,8 @@
messages: [ "slime-hurt-by-water-popup" ]
probability: 0.25
- type: Butcherable
meat: FoodMeatSlime
butcheringType: Spike
spawned: FoodMeatSlime
- type: entity
save: false

View File

@@ -110,4 +110,5 @@
- type: Inventory
speciesId: vox
- type: Butcherable
meat: FoodMeatChicken
butcheringType: Spike
spawned: FoodMeatChicken

View File

@@ -45,9 +45,7 @@
id: HydroponicsToolScythe
description: A sharp and curved blade on a long fibremetal handle, this tool makes it easy to reap what you sow.
components:
- type: Tag
tags:
- BotanySharp
- type: Sharp
- type: Sprite
sprite: Objects/Tools/Hydroponics/scythe.rsi
state: icon
@@ -72,7 +70,7 @@
- type: Tag
tags:
- BotanyHatchet
- BotanySharp
- type: Sharp
- type: Sprite
sprite: Objects/Tools/Hydroponics/hatchet.rsi
state: icon

View File

@@ -141,6 +141,7 @@
parent: BaseToolSurgery
description: For cutting wood and other objects to pieces. Or sawing bones, in case of emergency.
components:
- type: Sharp
- type: Utensil
types:
- Knife

View File

@@ -10,6 +10,7 @@
Slash: 12.5
Heat: 12.5
Blunt: -7
- type: Sharp
- type: Sprite
sprite: Objects/Weapons/Melee/e_sword.rsi
layers:

View File

@@ -7,6 +7,7 @@
- type: Tag
tags:
- FireAxe
- type: Sharp
- type: Sprite
sprite: Objects/Weapons/Melee/fireaxe.rsi
state: icon

View File

@@ -6,6 +6,7 @@
- type: Tag
tags:
- Knife
- type: Sharp
- type: Utensil
types:
- Knife

View File

@@ -7,6 +7,7 @@
- type: Tag
tags:
- Spear
- type: Sharp
- type: Sprite
sprite: Objects/Weapons/Melee/spear.rsi
state: spear

View File

@@ -4,6 +4,7 @@
id: CaptainSabre
description: A ceremonial weapon belonging to the captain of the station.
components:
- type: Sharp
- type: Sprite
sprite: Objects/Weapons/Melee/captain_sabre.rsi
state: icon
@@ -24,6 +25,7 @@
id: Katana
description: Ancient craftwork made with not so ancient plasteel.
components:
- type: Sharp
- type: Tag
tags:
- Katana
@@ -44,6 +46,7 @@
id: Machete
description: A large, vicious looking blade.
components:
- type: Sharp
- type: Tag
tags:
- Machete
@@ -64,6 +67,7 @@
id: Claymore
description: An ancient war blade.
components:
- type: Sharp
- type: Sprite
sprite: Objects/Weapons/Melee/claymore.rsi
state: icon

View File

@@ -24,9 +24,6 @@
- type: Tag
id: BotanyHoe
- type: Tag
id: BotanySharp
- type: Tag
id: BotanyShovel