Wizard Staff of Animation (#34649)

This commit is contained in:
ActiveMammmoth
2025-02-12 23:46:02 -05:00
committed by GitHub
parent 0bb6f1979d
commit 9fad86342f
26 changed files with 272 additions and 36 deletions

View File

@@ -88,6 +88,7 @@ namespace Content.Client.Actions
return;
component.Whitelist = state.Whitelist;
component.Blacklist = state.Blacklist;
component.CanTargetSelf = state.CanTargetSelf;
BaseHandleState<EntityTargetActionComponent>(uid, component, state);
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.Magic;
using Content.Shared.Magic;
using Content.Shared.Magic.Events;
namespace Content.Client.Magic;

View File

@@ -32,6 +32,11 @@ public sealed class TargetOutlineSystem : EntitySystem
/// </summary>
public EntityWhitelist? Whitelist = null;
/// <summary>
/// Blacklist that the target must satisfy.
/// </summary>
public EntityWhitelist? Blacklist = null;
/// <summary>
/// Predicate the target must satisfy.
/// </summary>
@@ -93,15 +98,16 @@ public sealed class TargetOutlineSystem : EntitySystem
RemoveHighlights();
}
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, CancellableEntityEventArgs? validationEvent)
public void Enable(float range, bool checkObstructions, Func<EntityUid, bool>? predicate, EntityWhitelist? whitelist, EntityWhitelist? blacklist, CancellableEntityEventArgs? validationEvent)
{
Range = range;
CheckObstruction = checkObstructions;
Predicate = predicate;
Whitelist = whitelist;
Blacklist = blacklist;
ValidationEvent = validationEvent;
_enabled = Predicate != null || Whitelist != null || ValidationEvent != null;
_enabled = Predicate != null || Whitelist != null || Blacklist != null || ValidationEvent != null;
}
public override void Update(float frameTime)

View File

@@ -952,7 +952,7 @@ public sealed class ActionUIController : UIController, IOnStateChanged<GameplayS
var range = entityAction.CheckCanAccess ? action.Range : -1;
_interactionOutline?.SetEnabled(false);
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, null);
_targetOutline?.Enable(range, entityAction.CheckCanAccess, predicate, entityAction.Whitelist, entityAction.Blacklist, null);
}
/// <summary>

View File

@@ -1,4 +1,4 @@
using Content.Shared.Whitelist;
using Content.Shared.Whitelist;
using Robust.Shared.GameStates;
using Robust.Shared.Serialization;
@@ -25,6 +25,12 @@ public sealed partial class EntityTargetActionComponent : BaseTargetActionCompon
/// <remarks>No whitelist check when null.</remarks>
[DataField("whitelist")] public EntityWhitelist? Whitelist;
/// <summary>
/// Determines which entities are NOT valid targets for this action.
/// </summary>
/// <remarks>No blacklist check when null.</remarks>
[DataField] public EntityWhitelist? Blacklist;
/// <summary>
/// Whether this action considers the user as a valid target entity when using this action.
/// </summary>
@@ -35,11 +41,13 @@ public sealed partial class EntityTargetActionComponent : BaseTargetActionCompon
public sealed class EntityTargetActionComponentState : BaseActionComponentState
{
public EntityWhitelist? Whitelist;
public EntityWhitelist? Blacklist;
public bool CanTargetSelf;
public EntityTargetActionComponentState(EntityTargetActionComponent component, IEntityManager entManager) : base(component, entManager)
{
Whitelist = component.Whitelist;
Blacklist = component.Blacklist;
CanTargetSelf = component.CanTargetSelf;
}
}

View File

@@ -538,6 +538,7 @@ public abstract class SharedActionsSystem : EntitySystem
if (!ValidateEntityTargetBase(user,
target,
comp.Whitelist,
comp.Blacklist,
comp.CheckCanInteract,
comp.CanTargetSelf,
comp.CheckCanAccess,
@@ -552,6 +553,7 @@ public abstract class SharedActionsSystem : EntitySystem
private bool ValidateEntityTargetBase(EntityUid user,
EntityUid? targetEntity,
EntityWhitelist? whitelist,
EntityWhitelist? blacklist,
bool checkCanInteract,
bool canTargetSelf,
bool checkCanAccess,
@@ -563,6 +565,9 @@ public abstract class SharedActionsSystem : EntitySystem
if (_whitelistSystem.IsWhitelistFail(whitelist, target))
return false;
if (_whitelistSystem.IsBlacklistPass(blacklist, target))
return false;
if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target))
return false;
@@ -637,6 +642,7 @@ public abstract class SharedActionsSystem : EntitySystem
var entityValidated = ValidateEntityTargetBase(user,
entity,
comp.Whitelist,
null,
comp.CheckCanInteract,
comp.CanTargetSelf,
comp.CheckCanAccess,

View File

@@ -84,15 +84,18 @@ namespace Content.Shared.Damage
public sealed class DamageableComponentState : ComponentState
{
public readonly Dictionary<string, FixedPoint2> DamageDict;
public readonly string? DamageContainerId;
public readonly string? ModifierSetId;
public readonly FixedPoint2? HealthBarThreshold;
public DamageableComponentState(
Dictionary<string, FixedPoint2> damageDict,
string? damageContainerId,
string? modifierSetId,
FixedPoint2? healthBarThreshold)
{
DamageDict = damageDict;
DamageContainerId = damageContainerId;
ModifierSetId = modifierSetId;
HealthBarThreshold = healthBarThreshold;
}

View File

@@ -228,12 +228,12 @@ namespace Content.Shared.Damage
{
if (_netMan.IsServer)
{
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageModifierSetId, component.HealthBarThreshold);
args.State = new DamageableComponentState(component.Damage.DamageDict, component.DamageContainerID, component.DamageModifierSetId, component.HealthBarThreshold);
}
else
{
// avoid mispredicting damage on newly spawned entities.
args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageModifierSetId, component.HealthBarThreshold);
args.State = new DamageableComponentState(component.Damage.DamageDict.ShallowClone(), component.DamageContainerID, component.DamageModifierSetId, component.HealthBarThreshold);
}
}
@@ -266,6 +266,7 @@ namespace Content.Shared.Damage
return;
}
component.DamageContainerID = state.DamageContainerId;
component.DamageModifierSetId = state.ModifierSetId;
component.HealthBarThreshold = state.HealthBarThreshold;

View File

@@ -0,0 +1,11 @@
using Robust.Shared.GameStates;
namespace Content.Shared.Magic.Components;
// Used on whitelist for animate spell/wand
[RegisterComponent, NetworkedComponent]
public sealed partial class AnimateableComponent : Component
{
}

View File

@@ -0,0 +1,16 @@
using Content.Shared.Actions;
using Robust.Shared.Prototypes;
namespace Content.Shared.Magic.Events;
public sealed partial class AnimateSpellEvent : EntityTargetActionEvent, ISpeakSpell
{
[DataField]
public string? Speech { get; private set; }
[DataField]
public ComponentRegistry AddComponents = new();
[DataField]
public HashSet<string> RemoveComponents = new();
}

View File

@@ -1,3 +1,4 @@
using System.Linq;
using System.Numerics;
using Content.Shared.Actions;
using Content.Shared.Body.Components;
@@ -26,7 +27,10 @@ using Robust.Shared.Audio.Systems;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Network;
using Robust.Shared.Physics;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Systems;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Spawners;
@@ -80,6 +84,7 @@ public abstract class SharedMagicSystem : EntitySystem
SubscribeLocalEvent<RandomGlobalSpawnSpellEvent>(OnRandomGlobalSpawnSpell);
SubscribeLocalEvent<MindSwapSpellEvent>(OnMindSwapSpell);
SubscribeLocalEvent<VoidApplauseSpellEvent>(OnVoidApplause);
SubscribeLocalEvent<AnimateSpellEvent>(OnAnimateSpell);
}
private void OnBeforeCastSpell(Entity<MagicComponent> ent, ref BeforeCastSpellEvent args)
@@ -298,22 +303,8 @@ public abstract class SharedMagicSystem : EntitySystem
ev.Handled = true;
Speak(ev);
foreach (var toRemove in ev.ToRemove)
{
if (_compFact.TryGetRegistration(toRemove, out var registration))
RemComp(ev.Target, registration.Type);
}
foreach (var (name, data) in ev.ToAdd)
{
if (HasComp(ev.Target, data.Component.GetType()))
continue;
var component = (Component)_compFact.GetComponent(name);
var temp = (object)component;
_seriMan.CopyTo(data.Component, ref temp);
EntityManager.AddComponent(ev.Target, (Component)temp!);
}
RemoveComponents(ev.Target, ev.ToRemove);
AddComponents(ev.Target, ev.ToAdd);
}
// End Change Component Spells
#endregion
@@ -370,6 +361,29 @@ public abstract class SharedMagicSystem : EntitySystem
comp.Uid = performer;
}
}
private void AddComponents(EntityUid target, ComponentRegistry comps)
{
foreach (var (name, data) in comps)
{
if (HasComp(target, data.Component.GetType()))
continue;
var component = (Component)_compFact.GetComponent(name);
var temp = (object)component;
_seriMan.CopyTo(data.Component, ref temp);
EntityManager.AddComponent(target, (Component)temp!);
}
}
private void RemoveComponents(EntityUid target, HashSet<string> comps)
{
foreach (var toRemove in comps)
{
if (_compFact.TryGetRegistration(toRemove, out var registration))
RemComp(target, registration.Type);
}
}
// End Spell Helpers
#endregion
#region Touch Spells
@@ -514,6 +528,33 @@ public abstract class SharedMagicSystem : EntitySystem
_stun.TryParalyze(ev.Performer, ev.PerformerStunDuration, true);
}
#endregion
#region Animation Spells
private void OnAnimateSpell(AnimateSpellEvent ev)
{
if (ev.Handled || !PassesSpellPrerequisites(ev.Action, ev.Performer) || !TryComp<FixturesComponent>(ev.Target, out var fixtures) ||
!TryComp<PhysicsComponent>(ev.Target, out var physics))
return;
ev.Handled = true;
//Speak(ev);
RemoveComponents(ev.Target, ev.RemoveComponents);
AddComponents(ev.Target, ev.AddComponents);
var xform = Transform(ev.Target);
var fixture = fixtures.Fixtures.First();
_transform.Unanchor(ev.Target);
_physics.SetCanCollide(ev.Target, true, true, false, fixtures, physics);
_physics.SetCollisionMask(ev.Target, fixture.Key, fixture.Value, (int)CollisionGroup.FlyingMobMask, fixtures, physics);
_physics.SetCollisionLayer(ev.Target, fixture.Key, fixture.Value, (int)CollisionGroup.FlyingMobLayer, fixtures, physics);
_physics.SetBodyType(ev.Target, BodyType.KinematicController, fixtures, physics, xform);
_physics.SetBodyStatus(ev.Target, physics, BodyStatus.InAir, true);
_physics.SetFixedRotation(ev.Target, false, true, fixtures, physics);
}
#endregion
// End Spells
#endregion

View File

@@ -26,6 +26,9 @@ spellbook-ethereal-jaunt-description = Slip into the ethereal plane to slip away
spellbook-mind-swap-name = Mind Swap
spellbook-mind-swap-description = Exchange bodies with another person!
spellbook-animate-name = Animate
spellbook-animate-description = Bring an inanimate object to life!
spellbook-smite-name = Smite
spellbook-smite-desc = Don't like them? EXPLODE them into giblets! Requires Wizard Robe & Hat.
@@ -49,6 +52,9 @@ spellbook-wand-polymorph-carp-description = For when you need a carp filet quick
spellbook-wand-locker-name = Wand of the Locker
spellbook-wand-locker-description = Shoot cursed lockers at your enemies and lock em away!
spellbook-staff-animation-name = Staff of Animation
spellbook-staff-animation-description = Bring inanimate objects to life!
# Events
spellbook-event-summon-ghosts-name = Summon Ghosts

View File

@@ -197,6 +197,19 @@
- !type:ListingLimitedStockCondition
stock: 1
- type: listing
id: SpellbookStaffAnimation
name: spellbook-staff-animation-name
description: spellbook-staff-animation-description
productEntity: AnimationStaff
cost:
WizCoin: 3
categories:
- SpellbookEquipment
conditions:
- !type:ListingLimitedStockCondition
stock: 1
# Event
- type: listing
id: SpellbookEventSummonGhosts

View File

@@ -1,4 +1,4 @@
# To be implemented: see #9072
# To be implemented: see #9072
- type: entity
name: staff of healing

View File

@@ -5,6 +5,7 @@
components:
- type: Item
size: Small
- type: Animateable
- type: Clickable
- type: InteractionOutline
- type: MovedByPressure

View File

@@ -5,6 +5,7 @@
description: A square piece of metal standing on four metal legs.
abstract: true
components:
- type: Animateable
- type: Damageable
damageContainer: StructuralInorganic
damageModifierSet: Metallic

View File

@@ -6,6 +6,7 @@
placement:
mode: PlaceFree
components:
- type: Animateable
- type: Clickable
- type: InteractionOutline
- type: Physics

View File

@@ -5,6 +5,7 @@
placement:
mode: SnapgridCenter
components:
- type: Animateable
- type: MeleeSound
soundGroups:
Brute:

View File

@@ -3,6 +3,7 @@
parent: BaseStructure
id: BaseMachine
components:
- type: Animateable
- type: InteractionOutline
- type: Anchorable
delay: 2
@@ -57,6 +58,7 @@
abstract: true
id: ConstructibleMachine
components:
- type: Animateable
- type: Machine
- type: ContainerContainer
containers:

View File

@@ -5,6 +5,7 @@
name: gas canister
description: A canister that can contain any type of gas. It can be attached to connector ports using a wrench.
components:
- type: Animateable
- type: InteractionOutline
- type: Transform
noRot: true

View File

@@ -5,6 +5,7 @@
description: A standard-issue Nanotrasen storage unit.
abstract: true
components:
- type: Animateable
- type: ResistLocker
- type: Transform
noRot: true

View File

@@ -5,6 +5,7 @@
name: crate
description: A large container for items.
components:
- type: Animateable
- type: Transform
noRot: true
- type: Icon

View File

@@ -1,10 +1,11 @@
- type: entity
- type: entity
id: StorageTank
parent: BaseStructureDynamic
name: storage tank
description: A liquids storage tank.
abstract: true
components:
- type: Animateable
- type: Sprite
noRot: true
- type: InteractionOutline

View File

@@ -0,0 +1,70 @@
- type: entity
id: ActionAnimateSpell
name: Animate
description: Bring an inanimate object to life!
components:
- type: EntityTargetAction
useDelay: 0
charges: 5
itemIconStyle: BigAction
whitelist:
components:
- Animateable # Currently on: SeatBase, TableBase, ClosetBase, BaseMachine, ConstructibleMachine, BaseComputer, BaseItem, CrateGeneric, StorageTank, GasCanister
blacklist:
components:
- MindContainer
- NukeDisk
- GravityGenerator
- AnomalyGenerator
canTargetSelf: false
interactOnMiss: false
sound: !type:SoundPathSpecifier
path: /Audio/Magic/staff_animation.ogg
icon:
sprite: Objects/Magic/magicactions.rsi
state: spell_default
event: !type:AnimateSpellEvent
addComponents:
- type: MindContainer
- type: InputMover
- type: MobMover
- type: MovementSpeedModifier
- type: HTN
rootTask:
task: SimpleHostileCompound
- type: CombatMode
- type: MeleeWeapon
animation: WeaponArcPunch
wideAnimation: WeaponArcPunch
altDisarm: false
soundHit: /Audio/Weapons/smash.ogg
range: 1.2
angle: 0.0
damage:
types:
Blunt: 10
- type: NpcFactionMember
factions:
- Wizard
- type: NoSlip
- type: MovementAlwaysTouching
- type: CanMoveInAir
- type: Damageable
damageContainer: ManifestedSpirit
damageModifierSet: ManifestedSpirit
- type: Destructible
thresholds:
- trigger:
!type:DamageTrigger
damage: 100
behaviors:
- !type:DoActsBehavior
acts: ["Destruction"]
- !type:PlaySoundBehavior
sound:
collection: MetalBreak
- type: Hands
- type: CanEscapeInventory
removeComponents:
- RequireProjectileTarget
speech: action-speech-spell-animate

View File

@@ -1,4 +1,4 @@
# non-projectile / "gun" staves
# non-projectile / "gun" staves
# wand that gives lights an RGB effect.
- type: entity
@@ -32,6 +32,30 @@
enabled: true
radius: 2
- type: entity
id: AnimationStaff
parent: BaseItem
name: staff of animation
description: Brings inanimate objects to life!
components:
- type: Sprite
sprite: Objects/Weapons/Guns/Basic/staves.rsi
layers:
- state: animation
- type: ActionOnInteract
actions:
- ActionAnimateSpell
- type: Item
size: Normal
inhandVisuals:
left:
- state: animation-inhand-left
right:
- state: animation-inhand-right
- type: Tag
tags:
- WizardWand
- type: entity
id: ActionRgbLight
components:

View File

@@ -8,6 +8,7 @@
- Zombie
- Revolutionary
- AllHostile
- Wizard
- type: npcFaction
id: NanoTrasen
@@ -19,6 +20,7 @@
- Revolutionary
- Dragon
- AllHostile
- Wizard
- type: npcFaction
id: Mouse
@@ -48,6 +50,7 @@
- Zombie
- Revolutionary
- AllHostile
- Wizard
- type: npcFaction
id: SimpleNeutral
@@ -62,6 +65,7 @@
- Zombie
- Dragon
- AllHostile
- Wizard
- type: npcFaction
id: Xeno
@@ -73,6 +77,7 @@
- Zombie
- Revolutionary
- AllHostile
- Wizard
- type: npcFaction
id: Zombie
@@ -85,6 +90,7 @@
- PetsNT
- Revolutionary
- AllHostile
- Wizard
- type: npcFaction
id: Revolutionary
@@ -94,6 +100,7 @@
- SimpleHostile
- Dragon
- AllHostile
- Wizard
- type: npcFaction
id: AllHostile
@@ -109,3 +116,16 @@
- Xeno
- Zombie
- Revolutionary
- Wizard
- type: npcFaction
id: Wizard
hostile:
- NanoTrasen
- Dragon
- SimpleHostile
- Syndicate
- Xeno
- Zombie
- Revolutionary
- AllHostile