Emaggable vendors + Familiars for Chaplain (#6961)

This commit is contained in:
Rane
2022-04-17 03:16:02 -04:00
committed by GitHub
parent 22735ab5ee
commit 7a6d3e69a8
20 changed files with 313 additions and 28 deletions

View File

@@ -18,6 +18,7 @@ namespace Content.Client.Entry
"Explosive",
"ExplosionResistance",
"Vocal",
"Summonable",
"OnUseTimerTrigger",
"WarpPoint",
"EmitSoundOnUse",

View File

@@ -2,6 +2,9 @@ using Content.Shared.Interaction;
using Content.Shared.Inventory;
using Content.Shared.MobState.Components;
using Content.Shared.Damage;
using Content.Shared.Verbs;
using Content.Shared.Tag;
using Content.Shared.ActionBlocker;
using Content.Server.Cooldown;
using Content.Server.Bible.Components;
using Content.Server.Popups;
@@ -20,12 +23,15 @@ namespace Content.Server.Bible
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly TagSystem _tagSystem = default!;
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<BibleComponent, AfterInteractEvent>(OnAfterInteract);
SubscribeLocalEvent<SummonableComponent, GetVerbsEvent<AlternativeVerb>>(AddSummonVerb);
}
private void OnAfterInteract(EntityUid uid, BibleComponent component, AfterInteractEvent args)
@@ -52,20 +58,20 @@ namespace Content.Server.Bible
{
_popupSystem.PopupEntity(Loc.GetString("bible-sizzle"), args.User, Filter.Entities(args.User));
SoundSystem.Play(Filter.Pvs(args.User), "/Audio/Effects/lightburn.ogg", args.User);
SoundSystem.Play(Filter.Pvs(args.User), component.SizzleSoundPath.GetSound(), args.User);
_damageableSystem.TryChangeDamage(args.User, component.DamageOnUntrainedUse, true);
return;
}
if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out var entityUid))
if (!_invSystem.TryGetSlotEntity(args.Target.Value, "head", out var entityUid) && !_tagSystem.HasTag(args.Target.Value, "Familiar"))
{
if (_random.Prob(component.FailChance))
{
var othersFailMessage = Loc.GetString("bible-heal-fail-others", ("user", args.User),("target", args.Target),("bible", uid));
var othersFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-others", ("user", args.User),("target", args.Target),("bible", uid));
_popupSystem.PopupEntity(othersFailMessage, args.User, Filter.Pvs(args.User).RemoveWhereAttachedEntity(puid => puid == args.User));
var selfFailMessage = Loc.GetString("bible-heal-fail-self", ("target", args.Target),("bible", uid));
var selfFailMessage = Loc.GetString(component.LocPrefix + "-heal-fail-self", ("target", args.Target),("bible", uid));
_popupSystem.PopupEntity(selfFailMessage, args.User, Filter.Entities(args.User));
SoundSystem.Play(Filter.Pvs(args.Target.Value), "/Audio/Effects/hit_kick.ogg", args.User);
@@ -74,15 +80,52 @@ namespace Content.Server.Bible
}
}
var othersMessage = Loc.GetString("bible-heal-success-others", ("user", args.User),("target", args.Target),("bible", uid));
var othersMessage = Loc.GetString(component.LocPrefix + "-heal-success-others", ("user", args.User),("target", args.Target),("bible", uid));
_popupSystem.PopupEntity(othersMessage, args.User, Filter.Pvs(args.User).RemoveWhereAttachedEntity(puid => puid == args.User));
var selfMessage = Loc.GetString("bible-heal-success-self", ("target", args.Target),("bible", uid));
var selfMessage = Loc.GetString(component.LocPrefix + "-heal-success-self", ("target", args.Target),("bible", uid));
_popupSystem.PopupEntity(selfMessage, args.User, Filter.Entities(args.User));
SoundSystem.Play(Filter.Pvs(args.Target.Value), "/Audio/Effects/holy.ogg", args.User);
SoundSystem.Play(Filter.Pvs(args.Target.Value), component.HealSoundPath.GetSound(), args.User);
_damageableSystem.TryChangeDamage(args.Target.Value, component.Damage, true);
}
private void AddSummonVerb(EntityUid uid, SummonableComponent component, GetVerbsEvent<AlternativeVerb> args)
{
if (!args.CanInteract || !args.CanAccess || component.AlreadySummoned || component.SpecialItemPrototype == null)
return;
if (component.RequiresBibleUser && !HasComp<BibleUserComponent>(args.User))
return;
AlternativeVerb verb = new()
{
Act = () =>
{
TransformComponent? position = Comp<TransformComponent>(args.User);
AttemptSummon(component, args.User, position);
},
Text = Loc.GetString("bible-summon-verb"),
Priority = 2
};
args.Verbs.Add(verb);
}
private void AttemptSummon(SummonableComponent component, EntityUid user, TransformComponent? position)
{
if (component.AlreadySummoned || component.SpecialItemPrototype == null)
return;
if (component.RequiresBibleUser && !HasComp<BibleUserComponent>(user))
return;
if (!Resolve(user, ref position))
return;
if (component.Deleted || Deleted(component.Owner))
return;
if (!_blocker.CanInteract(user, component.Owner))
return;
EntityManager.SpawnEntity(component.SpecialItemPrototype, position.Coordinates);
component.AlreadySummoned = true;
}
}
}

View File

@@ -1,18 +1,13 @@
using System;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Robust.Shared.Serialization.Manager.Attributes;
using Content.Shared.Sound;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.ViewVariables;
using Robust.Shared.Prototypes;
namespace Content.Server.Bible.Components
{
[RegisterComponent]
public sealed class BibleComponent : Component
{
// Damage that will be healed on a success
[DataField("damage", required: true)]
[ViewVariables(VVAccess.ReadWrite)]
@@ -33,6 +28,15 @@ namespace Content.Server.Bible.Components
public TimeSpan LastAttackTime;
public TimeSpan CooldownEnd;
[DataField("cooldownTime")]
public float CooldownTime { get; } = 5f;
[DataField("sizzleSound")]
public SoundSpecifier SizzleSoundPath = new SoundPathSpecifier("/Audio/Effects/lightburn.ogg");
[DataField("healSound")]
public SoundSpecifier HealSoundPath = new SoundPathSpecifier("/Audio/Effects/holy.ogg");
[DataField("locPrefix")]
public string LocPrefix = "bible";
}
}

View File

@@ -0,0 +1,23 @@
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
using Robust.Shared.Prototypes;
namespace Content.Server.Bible.Components
{
/// <summary>
/// This lets you summon a mob or item with an alternative verb on the item
/// </summary>
[RegisterComponent]
public sealed class SummonableComponent : Component
{
/// <summary>
/// Used for a special item only the Chaplain can summon. Usually a mob, but supports regular items too.
/// </summary>
[DataField("specialItem", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
public string? SpecialItemPrototype = null;
public bool AlreadySummoned = false;
[DataField("requriesBibleUser")]
public bool RequiresBibleUser = true;
}
}

View File

@@ -9,6 +9,7 @@ using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
using Content.Server.VendingMachines.systems;
using static Content.Shared.Wires.SharedWiresComponent;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.VendingMachines
{
@@ -16,9 +17,10 @@ namespace Content.Server.VendingMachines
public sealed class VendingMachineComponent : SharedVendingMachineComponent, IWires
{
public bool Ejecting;
public bool Emagged = false;
public TimeSpan AnimationDuration = TimeSpan.Zero;
[DataField("pack")]
public string PackPrototypeId = string.Empty;
[ViewVariables] [DataField("pack", customTypeSerializer:typeof(PrototypeIdSerializer<VendingMachineInventoryPrototype>))] public string PackPrototypeId = string.Empty;
[ViewVariables] [DataField("emagPack", customTypeSerializer:typeof(PrototypeIdSerializer<VendingMachineInventoryPrototype>))] public string EmagPackPrototypeId = string.Empty;
public string SpriteName = "";
public bool Broken;
/// <summary>

View File

@@ -10,6 +10,7 @@ using Robust.Server.GameObjects;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Content.Shared.Acts;
using Content.Shared.Emag.Systems;
using static Content.Shared.VendingMachines.SharedVendingMachineComponent;
using Content.Shared.Throwing;
@@ -31,6 +32,7 @@ namespace Content.Server.VendingMachines.systems
SubscribeLocalEvent<VendingMachineComponent, InventorySyncRequestMessage>(OnInventoryRequestMessage);
SubscribeLocalEvent<VendingMachineComponent, VendingMachineEjectMessage>(OnInventoryEjectMessage);
SubscribeLocalEvent<VendingMachineComponent, BreakageEventArgs>(OnBreak);
SubscribeLocalEvent<VendingMachineComponent, GotEmaggedEvent>(OnEmagged);
}
private void OnComponentInit(EntityUid uid, VendingMachineComponent component, ComponentInit args)
@@ -75,6 +77,16 @@ namespace Content.Server.VendingMachines.systems
TryUpdateVisualState(uid, VendingMachineVisualState.Broken, vendComponent);
}
private void OnEmagged(EntityUid uid, VendingMachineComponent component, GotEmaggedEvent args)
{
if (component.Emagged || component.EmagPackPrototypeId == string.Empty)
return;
AddVendEntries(component, component.EmagPackPrototypeId);
component.Emagged = true;
args.Handled = true;
}
public bool IsPowered(EntityUid uid, VendingMachineComponent? vendComponent = null)
{
if (!Resolve(uid, ref vendComponent))
@@ -121,6 +133,27 @@ namespace Content.Server.VendingMachines.systems
vendComponent.Inventory = inventory;
}
/// <summary>
/// Add more entries for any reason AFTER initialization (emag, machine upgrades, etc)
/// </summary>
public void AddVendEntries(VendingMachineComponent component, string pack)
{
if (!_prototypeManager.TryIndex(pack, out VendingMachineInventoryPrototype? packPrototype))
{
Logger.Error($"Pack has no valid inventory prototype: {pack}");
return;
}
foreach (var (id, amount) in packPrototype.StartingInventory)
{
if (!_prototypeManager.TryIndex(id, out EntityPrototype? prototype))
{
continue;
}
component.Inventory.Add(new VendingMachineInventoryEntry(id, amount));
}
}
public void Deny(EntityUid uid, VendingMachineComponent? vendComponent = null)
{
if (!Resolve(uid, ref vendComponent))

View File

@@ -1,5 +1,10 @@
bible-heal-success-self = You hit {THE($target)} with {THE($bible)}, and their wounds close in a flash of holy light!
bible-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and their wounds close in a flash of holy light!
bible-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, dazing them!
bible-heal-fail-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and it lands with a sad thack, dazing them!
bible-heal-success-self = You hit {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} wounds close in a flash of holy light!
bible-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} wounds close in a flash of holy light!
bible-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, dazing {OBJECT($target)}!
bible-heal-fail-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and it lands with a sad thack, dazing {OBJECT($target)}!
bible-sizzle = The book sizzles in your hands!
bible-summon-verb = Summon familiar
necro-heal-success-self = You hit {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts!
necro-heal-success-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and {POSS-ADJ($target)} flesh warps as it melts!
necro-heal-fail-self = You hit {THE($target)} with {THE($bible)}, and it lands with a sad thwack, failing to smite {OBJECT($target)}.
necro-heal-fail-others = {CAPITALIZE(THE($user))} hits {THE($target)} with {THE($bible)}, and it lands with a sad thack, failing to smite {OBJECT($target)}.

View File

@@ -7,16 +7,25 @@
ClothingUniformJumpskirtChaplain: 2
ClothingOuterHoodieChaplain: 1
ClothingOuterHoodieBlack: 1
ClothingOuterRobesCult: 1
ClothingOuterArmorCult: 1
ClothingHeadHatHoodChaplainHood: 1
ClothingHeadHatHoodCulthood: 1
ClothingHeadHatHoodNunHood: 1
ClothingHeadHatFez: 1
ClothingHeadHatPlaguedoctor: 1
ClothingHeadHatWitch: 1
ClothingHeadHatWitch1: 1
ClothingHeadHelmetCult: 1
ClothingHeadsetService: 2
ClothingOuterPlagueSuit: 1
ClothingMaskPlague: 1
ClothingHeadsetService: 2
- type: vendingMachineInventory
id: PietyVendEmagInventory
spriteName: chapel
startingInventory:
ClothingOuterArmorCult: 1
ClothingHeadHelmetCult: 1
ClothingOuterRobesCult: 3
ClothingHeadHatHoodCulthood: 3
ClothingShoesCult: 4
BedsheetCult: 4
BibleNecronomicon: 1

View File

@@ -83,6 +83,18 @@
Cold: 1.5
Poison: 0.8
- type: damageModifierSet
id: Infernal
coefficients:
Blunt: 0.8
Slash: 0.8
Piercing: 0.8
Cold: 0.8
Heat: 0.2
# Holy: 3 If we ever get some holy or magic sort of damage type they should be vulnerable
flatReductions:
Heat: 3
- type: damageModifierSet
id: Scale # Skin tougher, bones weaker, strong stomachs, cold-blooded (kindof)
coefficients:

View File

@@ -16,7 +16,7 @@
coefficients:
Blunt: 0.5
Slash: 0.8
Piercing: 0.8
Piercing: 0.8
- type: entity
parent: ClothingHeadEVAHelmetBase
@@ -41,6 +41,13 @@
- type: Clothing
sprite: Clothing/Head/Helmets/cult.rsi
- type: IngestionBlocker
- type: Armor
modifiers:
coefficients:
Blunt: 0.8
Slash: 0.8
Piercing: 0.9
Heat: 0.8
- type: entity
parent: ClothingHeadEVAHelmetBase
@@ -248,4 +255,3 @@
Slash: 0.5
Piercing: 0.5
Heat: 0.9

View File

@@ -49,6 +49,8 @@
flavorKind: organic
- type: Bloodstream
bloodMaxVolume: 50
- type: ReplacementAccent
accent: mouse
- type: entity
name: bee

View File

@@ -88,6 +88,72 @@
- type: Grammar
attributes:
gender: epicene
- type: Bloodstream
bloodReagent: DemonsBlood
bloodlossDamage:
types:
Bloodloss:
1
bloodlossHealDamage:
types:
Bloodloss:
-0.25
- type: Damageable
damageContainer: Biological
damageModifierSet: Infernal
- type: Temperature
heatDamageThreshold: 4000 #They come from hell, so...
coldDamageThreshold: 260
currentTemperature: 310.15
coldDamage:
types:
Cold : 1 #per second, scales with temperature & other constants
specificHeat: 42
heatDamage:
types:
Heat : 1 #per second, scales with temperature & other constants
- type: entity
name: Cerberus
parent: MobCorgiNarsi
id: MobCorgiCerberus
description: This pupper is not wholesome.
components:
- type: GhostTakeoverAvailable
makeSentient: true
name: Cerberus, Evil Familiar
description: Obey your master. Spread chaos.
rules: You are an intelligent, demonic dog. Try to help the chaplain and any of his flock. As an antagonist, you're otherwise unrestrained.
- type: UnarmedCombat
range: 1.5
arcwidth: 0
arc: bite
damage:
types:
Piercing: 8
Slash: 7
- type: UtilityAI
behaviorSets:
- Idle
- type: AiFactionTag
factions:
- SimpleNeutral
- type: InteractionPopup
successChance: 0.5
interactSuccessString: petting-success-corrupted-corgi
interactFailureString: petting-failure-corrupted-corgi
- type: MobState
thresholds:
0: !type:NormalMobState {}
80: !type:CriticalMobState {}
160: !type:DeadMobState {}
- type: Grammar
attributes:
gender: male
proper: true
- type: Tag
tags:
- Familiar
- type: entity
name: Ian
@@ -135,6 +201,25 @@
proper: true
gender: male
- type: entity
name: Remilia
parent: MobBat
id: MobBatRemilia
description: The chaplain's familiar. Likes fruit.
components:
- type: GhostTakeoverAvailable
makeSentient: true
name: Remilia, the chaplain's familiar
description: Obey your master. Eat fruit.
rules: Follow the chaplain around. Don't cause any trouble unless the chaplain tells you to.
- type: Grammar
attributes:
gender: female
proper: true
- type: Tag
tags:
- Familiar
- type: entity
name: Lisa
parent: MobCorgi

View File

@@ -68,6 +68,8 @@
types:
Bloodloss:
-0.25
- type: DrawableSolution
solution: bloodstream
- type: Damageable
damageContainer: Biological
- type: AtmosExposed
@@ -182,6 +184,5 @@
heatDamage:
types:
Heat : 1 #per second, scales with temperature & other constants
- type: Bloodstream
bloodMaxVolume: 150

View File

@@ -16,6 +16,8 @@
damageOnUntrainedUse: ## What a non-chaplain takes when attempting to heal someone
groups:
Burn: 30
- type: Summonable
specialItem: MobBatRemilia
- type: ItemCooldown
- type: Sprite
netsync: false
@@ -30,3 +32,34 @@
storageSoundCollection:
collection: storageRustle
- type: entity
parent: Bible
name: necronomicon
description: "There's a note: Klatuu, Verata, Nikto -- Don't forget it again!"
id: BibleNecronomicon
components:
- type: Bible
damage:
groups:
Caustic: 20 ## ~15 dps
damageOnFail:
groups:
Brute: 15
Airloss: 25
damageOnUntrainedUse:
groups:
Caustic: 50
failChance: 0
locPrefix: "necro"
healSound: "/Audio/Effects/lightburn.ogg"
cooldownTime: 1.3
- type: Summonable
specialItem: MobCorgiCerberus
- type: Sprite
netsync: false
sprite: Objects/Specific/Chapel/necronomicon.rsi
state: icon
- type: Item
size: 15
sprite: Objects/Specific/Chapel/necronomicon.rsi
prefix: inhand

View File

@@ -924,6 +924,7 @@
components:
- type: VendingMachine
pack: PietyVendInventory
emagPack: PietyVendEmagInventory
- type: Sprite
sprite: Structures/Machines/VendingMachines/chapdrobe.rsi
layers:

View File

@@ -135,6 +135,9 @@
- type: Tag
id: ExplosivePassable
- type: Tag
id: Familiar
- type: Tag
id: FireAlarmElectronics

Binary file not shown.

After

Width:  |  Height:  |  Size: 976 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 496 B

View File

@@ -0,0 +1,22 @@
{
"version": 1,
"license": "CC-BY-SA-3.0",
"copyright": "Taken from tgstation at https://github.com/tgstation/tgstation/commit/40d89d11ea4a5cb81d61dc1018b46f4e7d32c62a",
"size": {
"x": 32,
"y": 32
},
"states": [
{
"name": "icon"
},
{
"name": "inhand-left",
"directions": 4
},
{
"name": "inhand-right",
"directions": 4
}
]
}