diff --git a/Movement tests.sln.DotSettings b/Movement tests.sln.DotSettings new file mode 100644 index 00000000..3d8771c9 --- /dev/null +++ b/Movement tests.sln.DotSettings @@ -0,0 +1,2 @@ + + False \ No newline at end of file diff --git a/forge/ForgeManager.cs b/forge/ForgeManager.cs index 6bd319c0..7bf580f8 100644 --- a/forge/ForgeManager.cs +++ b/forge/ForgeManager.cs @@ -9,11 +9,43 @@ public partial class ForgeManager : Node public CuesManager CuesManager { get; private set; } = new CuesManager(); public TagsManager TagsManager { get; private set; } = new TagsManager( [ + // entities "character.player", + "weapon", + + // Statuses "status.stunned", + + // Abilities + "abilities.weapon.land", + + // Events "events.combat.damage", "events.combat.hit", + "events.weapon.land", + + // Cooldowns "cooldown.empoweredAction", + "cooldown.empoweredSwordThrow", + + // Cues "cues.resources.mana", ]); -} \ No newline at end of file + + public static ForgeManager GetForgeManager(Node node) + { + return node.GetTree().Root.GetNode("ForgeManager"); + } + + public static TagsManager GetTagsManager(Node node) + { + return GetForgeManager(node).TagsManager; + } + + public static CuesManager GetCuesManager(Node node) + { + return GetForgeManager(node).CuesManager; + } + +} + diff --git a/forge/abilities/RAbilityBase.cs b/forge/abilities/RAbilityBase.cs new file mode 100644 index 00000000..e9a8c5cd --- /dev/null +++ b/forge/abilities/RAbilityBase.cs @@ -0,0 +1,41 @@ +using System; +using Gamesmiths.Forge.Abilities; +using Gamesmiths.Forge.Cues; +using Gamesmiths.Forge.Effects; +using Gamesmiths.Forge.Effects.Components; +using Gamesmiths.Forge.Effects.Duration; +using Gamesmiths.Forge.Effects.Magnitudes; +using Gamesmiths.Forge.Effects.Modifiers; +using Gamesmiths.Forge.Tags; +using Godot; +using Movementtests.interfaces; + +namespace Movementtests.forge.abilities; + +[GlobalClass] +public partial class RAbilityBase(float cost, float cooldown) : Resource, IAbilityBase +{ + [Export(PropertyHint.Range, "0,100,1,or_greater")] + public float Cost { get; set; } = cost; + [Export(PropertyHint.Range, "0,10,0.1,or_greater")] + public float Cooldown { get; set; } = cooldown; + + public RAbilityBase() : this(20.0f, 0.0f) + { + } + + public virtual AbilityData Ability(TagsManager tagsManager, Node3D owner) + { + throw new NotImplementedException(); + } + + public virtual EffectData CostEffect(TagsManager tagsManager) + { + throw new NotImplementedException(); + } + + public virtual EffectData CooldownEffect(TagsManager tagsManager) + { + throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/forge/abilities/RAbilityBase.cs.uid b/forge/abilities/RAbilityBase.cs.uid new file mode 100644 index 00000000..7f28b56d --- /dev/null +++ b/forge/abilities/RAbilityBase.cs.uid @@ -0,0 +1 @@ +uid://4eosgwb3h528 diff --git a/forge/abilities/REmpoweredAction.cs b/forge/abilities/REmpoweredAction.cs index 6f6079e8..9f1655a7 100644 --- a/forge/abilities/REmpoweredAction.cs +++ b/forge/abilities/REmpoweredAction.cs @@ -26,6 +26,16 @@ public partial class REmpoweredAction(float cost, float cooldown, float manaRege { } + public AbilityData Ability(TagsManager tagsManager) + { + return new AbilityData( + name: "Empowered Action", + costEffect: CostEffect(tagsManager), + cooldownEffects: [CooldownEffect(tagsManager)], + instancingPolicy: AbilityInstancingPolicy.PerEntity, + behaviorFactory: () => new EmpoweredActionBehavior()); + } + public EffectData CostEffect(TagsManager tagsManager) { return new( diff --git a/forge/abilities/RExplodingSwordThrow.cs b/forge/abilities/RExplodingSwordThrow.cs new file mode 100644 index 00000000..790c7cda --- /dev/null +++ b/forge/abilities/RExplodingSwordThrow.cs @@ -0,0 +1,104 @@ +using Gamesmiths.Forge.Abilities; +using Gamesmiths.Forge.Cues; +using Gamesmiths.Forge.Effects; +using Gamesmiths.Forge.Effects.Components; +using Gamesmiths.Forge.Effects.Duration; +using Gamesmiths.Forge.Effects.Magnitudes; +using Gamesmiths.Forge.Effects.Modifiers; +using Gamesmiths.Forge.Tags; +using Godot; + +namespace Movementtests.forge.abilities; + +[GlobalClass, Icon("res://assets/ui/IconGodotNode/white/icon_projectile.png")] +public partial class RExplodingSwordThrow(PackedScene? explosion, float cost, float cooldown) : RAbilityBase(cost, cooldown) +{ + [Export] public PackedScene? Explosion { get; set; } = explosion; + + public RExplodingSwordThrow() : this(null, 20.0f, 0.0f) + { + } + + public override AbilityData Ability(TagsManager tagsManager, Node3D owner) + { + return new AbilityData( + name: "Exploding Sword Throw", + costEffect: CostEffect(tagsManager), + cooldownEffects: [CooldownEffect(tagsManager)], + abilityTags: Tag.RequestTag(tagsManager, "abilities.weapon.land").GetSingleTagContainer(), + instancingPolicy: AbilityInstancingPolicy.PerEntity, + behaviorFactory: () => new ExplodingSwordThrowBehavior(owner, Explosion)); + } + + public override EffectData CostEffect(TagsManager tagsManager) + { + return new( + "Exploding Sword Throw Mana Cost", + new DurationData(DurationType.Instant), + new[] + { + new Modifier( + "PlayerAttributeSet.Mana", + ModifierOperation.FlatBonus, + new ModifierMagnitude( + MagnitudeCalculationType.ScalableFloat, + new ScalableFloat(-Cost) + ) + ) + }, + cues: new [] + { + new CueData( + CueTags: Tag.RequestTag(tagsManager, "cues.resources.mana").GetSingleTagContainer(), + MinValue: 0, + MaxValue: 100, + MagnitudeType: CueMagnitudeType.AttributeValueChange, + MagnitudeAttribute: "PlayerAttributeSet.Mana" + ) + }); + } + + + public override EffectData CooldownEffect(TagsManager tagsManager) + { + return new( + "Exploding Sword Throw Cooldown", + new DurationData( + DurationType.HasDuration, + new ModifierMagnitude( + MagnitudeCalculationType.ScalableFloat, + new ScalableFloat(Cooldown))), + effectComponents: new[] + { + new ModifierTagsEffectComponent( + tagsManager.RequestTagContainer(new[] { "cooldown.empoweredSwordThrow" }) + ) + }); + } +} + +public class ExplodingSwordThrowBehavior(Node3D owner, PackedScene? explosion) : IAbilityBehavior +{ + private Node3D _owner = owner; + private PackedScene? _explosion = explosion; + public void OnStarted(AbilityBehaviorContext context) + { + if (_explosion?.Instantiate() is not Explosion explosion) + { + context.InstanceHandle.End(); + return; + } + + explosion.Radius = 10f; + _owner.GetTree().GetRoot().AddChild(explosion); + explosion.GlobalPosition = _owner.GlobalPosition; + + context.AbilityHandle.CommitAbility(); + context.InstanceHandle.End(); + } + + public void OnEnded(AbilityBehaviorContext context) + { + + } +} \ No newline at end of file diff --git a/forge/abilities/RExplodingSwordThrow.cs.uid b/forge/abilities/RExplodingSwordThrow.cs.uid new file mode 100644 index 00000000..58097a30 --- /dev/null +++ b/forge/abilities/RExplodingSwordThrow.cs.uid @@ -0,0 +1 @@ +uid://rux15j7q78e8 diff --git a/interfaces/IAbilityBase.cs b/interfaces/IAbilityBase.cs new file mode 100644 index 00000000..d305e2b3 --- /dev/null +++ b/interfaces/IAbilityBase.cs @@ -0,0 +1,13 @@ +using Gamesmiths.Forge.Abilities; +using Gamesmiths.Forge.Effects; +using Gamesmiths.Forge.Tags; +using Godot; + +namespace Movementtests.interfaces; + +public interface IAbilityBase +{ + AbilityData Ability(TagsManager tagsManager, Node3D owner); + EffectData CostEffect(TagsManager tagsManager); + EffectData CooldownEffect(TagsManager tagsManager); +} \ No newline at end of file diff --git a/interfaces/IAbilityBase.cs.uid b/interfaces/IAbilityBase.cs.uid new file mode 100644 index 00000000..6132cfe2 --- /dev/null +++ b/interfaces/IAbilityBase.cs.uid @@ -0,0 +1 @@ +uid://de881c2xsbutk diff --git a/scenes/player_controller/PlayerController.tscn b/scenes/player_controller/PlayerController.tscn index 514ce31f..869cf672 100644 --- a/scenes/player_controller/PlayerController.tscn +++ b/scenes/player_controller/PlayerController.tscn @@ -7,6 +7,7 @@ [ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://inputs/base_mode/base_mode.tres" id="3_cresl"] [ext_resource type="Resource" uid="uid://dtmhtlix2amme" path="res://scenes/player_controller/resources/player_mana_regen.tres" id="3_n24vh"] [ext_resource type="PackedScene" uid="uid://c4ikbhojckpnc" path="res://scenes/components/health/CHealth.tscn" id="3_q7bng"] +[ext_resource type="Script" uid="uid://rux15j7q78e8" path="res://forge/abilities/RExplodingSwordThrow.cs" id="4_11013"] [ext_resource type="Script" uid="uid://baiapod3csndf" path="res://scenes/components/health/RHealth.cs" id="4_abfq8"] [ext_resource type="Resource" uid="uid://bjyd801wvverk" path="res://scenes/player_controller/resources/player_health.tres" id="4_m8gvy"] [ext_resource type="Resource" uid="uid://cpdaw41ah5gic" path="res://inputs/base_mode/rotate_y.tres" id="4_rxwoh"] @@ -18,7 +19,7 @@ [ext_resource type="Resource" uid="uid://t612lts1wi1s" path="res://inputs/base_mode/move_right.tres" id="6_q7bng"] [ext_resource type="Script" uid="uid://cwbvxlfvmocc1" path="res://scenes/player_controller/scripts/StairsSystem.cs" id="7_bmt5a"] [ext_resource type="Resource" uid="uid://brswsknpgwal2" path="res://inputs/base_mode/move_front.tres" id="7_m8gvy"] -[ext_resource type="Resource" uid="uid://7dpkk5rk3di5" path="res://scenes/player_controller/resources/forge_empowered_action.tres" id="7_qheee"] +[ext_resource type="Resource" uid="uid://7dpkk5rk3di5" path="res://scenes/player_controller/resources/forge/empowered_action.tres" id="7_qheee"] [ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://scenes/components/knockback/CKnockback.tscn" id="7_x835q"] [ext_resource type="Resource" uid="uid://s1l0n1iitc6m" path="res://inputs/base_mode/move_back.tres" id="8_jb43f"] [ext_resource type="Resource" uid="uid://j1o5ud0plk4" path="res://inputs/base_mode/aim_release.tres" id="8_lhb11"] @@ -55,6 +56,12 @@ [ext_resource type="Texture2D" uid="uid://c40orhfdgsim" path="res://assets/ui/IconGodotNode/white/icon_circle.png" id="45_u8rdp"] [ext_resource type="PackedScene" uid="uid://cyw8p0p6a78tl" path="res://scenes/ui/healthbar/healthbar.tscn" id="47_76kmc"] +[sub_resource type="Resource" id="Resource_5b7hb"] +script = ExtResource("4_11013") +Explosion = ExtResource("5_ue7xq") +Cost = 10.0 +metadata/_custom_type_script = "uid://rux15j7q78e8" + [sub_resource type="Resource" id="Resource_cb2lu"] script = ExtResource("2_x835q") DamageDealt = 10.0 @@ -119,6 +126,7 @@ collision_mask = 272 script = ExtResource("1_poq2x") EmpoweredAction = ExtResource("7_qheee") ManaRegen = ExtResource("3_n24vh") +AbilityLoadout = [SubResource("Resource_5b7hb")] AimAssistStrength = 0.3 AimAssistReductionWhenCloseToTarget = 0.1 AimAssistReductionStartDistance = 8.0 diff --git a/scenes/player_controller/PlayerUi.cs b/scenes/player_controller/PlayerUi.cs index 9ee604c5..969c0bfe 100644 --- a/scenes/player_controller/PlayerUi.cs +++ b/scenes/player_controller/PlayerUi.cs @@ -93,32 +93,25 @@ public partial class PlayerUi : Control, ICueHandler // One-shot effect (like impact) // Called when an instant effect with this cue is applied // Also called when a periodic effect with this cue executes its period - if (target == null || !parameters.HasValue) return; // Extract parameters float magnitude = parameters.Value.Magnitude; - float normalizedMagnitude = parameters.Value.NormalizedMagnitude; - // Play effects scaled by magnitude // PlayFireImpactSound(normalizedMagnitude); // SpawnFireImpactParticles(target, magnitude); - GD.Print(_manabar.CurrentHealth); _manabar.CurrentHealth += magnitude; } public void OnApply(IForgeEntity? target, CueParameters? parameters) { - return; } public void OnRemove(IForgeEntity? target, bool interrupted) { - return; } public void OnUpdate(IForgeEntity? target, CueParameters? parameters) { - return; } } diff --git a/scenes/player_controller/components/weapon/WeaponAttributeSet.cs b/scenes/player_controller/components/weapon/WeaponAttributeSet.cs new file mode 100644 index 00000000..c721b6cc --- /dev/null +++ b/scenes/player_controller/components/weapon/WeaponAttributeSet.cs @@ -0,0 +1,13 @@ +using Gamesmiths.Forge.Attributes; + +namespace Movementtests.scenes.player_controller.components.weapon; + +public class WeaponAttributeSet : AttributeSet +{ + public EntityAttribute Level { get; } + + public WeaponAttributeSet() + { + Level = InitializeAttribute(nameof(Level), 1, 1, 10); + } +} \ No newline at end of file diff --git a/scenes/player_controller/components/weapon/WeaponAttributeSet.cs.uid b/scenes/player_controller/components/weapon/WeaponAttributeSet.cs.uid new file mode 100644 index 00000000..a36d0c86 --- /dev/null +++ b/scenes/player_controller/components/weapon/WeaponAttributeSet.cs.uid @@ -0,0 +1 @@ +uid://mvc3bv0p021 diff --git a/scenes/player_controller/components/weapon/WeaponSystem.cs b/scenes/player_controller/components/weapon/WeaponSystem.cs index e9590a0e..c2d59464 100644 --- a/scenes/player_controller/components/weapon/WeaponSystem.cs +++ b/scenes/player_controller/components/weapon/WeaponSystem.cs @@ -1,13 +1,21 @@ using System; +using Gamesmiths.Forge.Core; +using Gamesmiths.Forge.Effects; +using Gamesmiths.Forge.Events; +using Gamesmiths.Forge.Tags; using Godot; using GodotStateCharts; using Movementtests.interfaces; +using Movementtests.scenes.player_controller.components.weapon; using Movementtests.systems.damage; +using Movementtests.tools; namespace Movementtests.systems; +public record struct WeaponLandPayload(int Damage, bool IsCritical); + [GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_sword.png")] -public partial class WeaponSystem : RigidBody3D, IDamageDealer +public partial class WeaponSystem : RigidBody3D, IDamageDealer, IForgeEntity { [Signal] public delegate void WeaponThrownEventHandler(); @@ -22,6 +30,12 @@ public partial class WeaponSystem : RigidBody3D, IDamageDealer [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] public float StraightThrowDuration { get; set; } = 0.1f; + public EntityAttributes Attributes { get; set; } = null!; + public EntityTags Tags { get; set; } = null!; + public EffectsManager EffectsManager { get; set; } = null!; + public EntityAbilities Abilities { get; set; } = null!; + public EventManager Events { get; set; } = null!; + private StateChart _weaponState = null!; public StateChartState InHandState = null!; public StateChartState FlyingState = null!; @@ -40,6 +54,8 @@ public partial class WeaponSystem : RigidBody3D, IDamageDealer public StandardMaterial3D WeaponLocationIndicatorMaterial { get; set; } = null!; public MeshInstance3D WeaponMesh { get; set; } = null!; + public Tag WeaponLandTag; + public void Init() { _weaponState = StateChart.Of(GetNode("StateChart")); @@ -57,6 +73,22 @@ public partial class WeaponSystem : RigidBody3D, IDamageDealer _startTransform = Transform; Freeze = true; Visible = false; + + var tagsManager = ForgeManager.GetTagsManager(this); + var cuesManager = ForgeManager.GetCuesManager(this); + var baseTags = new TagContainer( + tagsManager, + [ + Tag.RequestTag(tagsManager, "weapon") + ]); + + Attributes = new EntityAttributes(new WeaponAttributeSet()); + Tags = new EntityTags(baseTags); + EffectsManager = new EffectsManager(this, cuesManager); + Abilities = new(this); + Events = new(); + + WeaponLandTag = Tag.RequestTag(tagsManager, "events.weapon.land"); BodyEntered += OnThrownWeaponReachesGround; @@ -109,11 +141,26 @@ public partial class WeaponSystem : RigidBody3D, IDamageDealer tween.Finished += ThrowWeaponOnCurve; } + public void RaiseWeaponLandEvent(IForgeEntity? victim = null) + { + Events.Raise(new EventData + { + EventTags = WeaponLandTag.GetSingleTagContainer(), + Source = this, + Target = victim, + EventMagnitude = 25f, + Payload = new WeaponLandPayload(Damage: 25, IsCritical: true) + }); + } + public void PlantInEnemy(Node3D enemy) { GetTree().GetRoot().CallDeferred(Node.MethodName.RemoveChild, this); enemy.CallDeferred(Node.MethodName.AddChild, this); + if (enemy is IForgeEntity victim) RaiseWeaponLandEvent(victim); + else RaiseWeaponLandEvent(); + if (enemy is IDamageable damageable) { damageable.TakeDamage(new DamageRecord(GlobalPosition, RDamage)); @@ -145,6 +192,8 @@ public partial class WeaponSystem : RigidBody3D, IDamageDealer { PlantInEnemy(node); } + else RaiseWeaponLandEvent(); + CallDeferred(Node3D.MethodName.SetGlobalPosition, PlantLocation); CallDeferred(Node3D.MethodName.LookAt, GlobalTransform.Origin + PlantNormal, Vector3.Up, true); } diff --git a/scenes/player_controller/resources/forge_empowered_action.tres b/scenes/player_controller/resources/forge/empowered_action.tres similarity index 100% rename from scenes/player_controller/resources/forge_empowered_action.tres rename to scenes/player_controller/resources/forge/empowered_action.tres diff --git a/scenes/player_controller/resources/forge/exploding_sword_throw.tres b/scenes/player_controller/resources/forge/exploding_sword_throw.tres new file mode 100644 index 00000000..5eb1f54f --- /dev/null +++ b/scenes/player_controller/resources/forge/exploding_sword_throw.tres @@ -0,0 +1,8 @@ +[gd_resource type="Resource" script_class="RExplodingSwordThrow" format=3 uid="uid://cdxbwirfiaipi"] + +[ext_resource type="Script" uid="uid://rux15j7q78e8" path="res://forge/abilities/RExplodingSwordThrow.cs" id="1_5iq8v"] + +[resource] +script = ExtResource("1_5iq8v") +Cost = 20.0 +metadata/_custom_type_script = "uid://rux15j7q78e8" diff --git a/scenes/player_controller/scripts/PlayerController.cs b/scenes/player_controller/scripts/PlayerController.cs index 4a7231e4..f36c07a2 100644 --- a/scenes/player_controller/scripts/PlayerController.cs +++ b/scenes/player_controller/scripts/PlayerController.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using Gamesmiths.Forge.Abilities; using Gamesmiths.Forge.Core; using Gamesmiths.Forge.Effects; +using Gamesmiths.Forge.Effects.Components; +using Gamesmiths.Forge.Effects.Duration; +using Gamesmiths.Forge.Effects.Magnitudes; using Gamesmiths.Forge.Events; using Gamesmiths.Forge.Tags; @@ -102,6 +105,10 @@ public partial class PlayerController : CharacterBody3D, [ExportGroup("General")] [Export] public REmpoweredAction EmpoweredAction = null!; [Export] public RManaRegen ManaRegen = null!; + + [ExportGroup("Abilities")] + [ExportSubgroup("WeaponThrow")] + [Export] public RAbilityBase[] AbilityLoadout = []; // Combat stuff [ExportCategory("Combat")] @@ -429,26 +436,21 @@ public partial class PlayerController : CharacterBody3D, _aimAssisRayCast.TargetPosition = _aimAssisRayCast.TargetPosition.Normalized() * (TargetingDistance*1.5f); // Forge stuff - var forgeManager = GetTree().Root.GetNode("ForgeManager")!; + var tagsManager = ForgeManager.GetTagsManager(this); + var cuesManager = ForgeManager.GetCuesManager(this); var baseTags = new TagContainer( - forgeManager.TagsManager, + tagsManager, [ - Tag.RequestTag(forgeManager.TagsManager, "character.player") + Tag.RequestTag(tagsManager, "character.player") ]); Attributes = new EntityAttributes(new PlayerAttributeSet()); Tags = new EntityTags(baseTags); - EffectsManager = new EffectsManager(this, forgeManager.CuesManager); + EffectsManager = new EffectsManager(this, cuesManager); Abilities = new(this); Events = new(); - var empoweredActionData = new AbilityData( - name: "Empowered Action", - costEffect: EmpoweredAction.CostEffect(forgeManager.TagsManager), - cooldownEffects: [EmpoweredAction.CooldownEffect(forgeManager.TagsManager)], - instancingPolicy: AbilityInstancingPolicy.PerEntity, - behaviorFactory: () => new EmpoweredActionBehavior()); - + var empoweredActionData = EmpoweredAction.Ability(tagsManager); // Grant permanently _empoweredActionHandle = Abilities.GrantAbilityPermanently( empoweredActionData, @@ -456,7 +458,7 @@ public partial class PlayerController : CharacterBody3D, levelOverridePolicy: LevelComparison.None, sourceEntity: this); - var manaRegenEffect = new Effect(ManaRegen.ManaRegen(forgeManager.TagsManager), new EffectOwnership(this, this)); + var manaRegenEffect = new Effect(ManaRegen.ManaRegen(tagsManager), new EffectOwnership(this, this)); _manaRegenEffectHandle = EffectsManager.ApplyEffect(manaRegenEffect); var health = Attributes["PlayerAttributeSet.Health"].CurrentValue; // 100 @@ -702,9 +704,47 @@ public partial class PlayerController : CharacterBody3D, _attackDash.StateEntered += OnDashAttackStarted; _parryStandard.StateEntered += OnStandardParryStarted; _parryDash.StateEntered += OnDashParryStarted; + + foreach (var weaponLandAbility in AbilityLoadout) + { + var grantAbilityConfig = new GrantAbilityConfig( + weaponLandAbility.Ability(tagsManager, WeaponSystem), + ScalableLevel: new ScalableInt(1), + RemovalPolicy: AbilityDeactivationPolicy.CancelImmediately, + InhibitionPolicy: AbilityDeactivationPolicy.CancelImmediately, + TryActivateOnGrant: false, + TryActivateOnEnable: false, + LevelOverridePolicy: LevelComparison.Higher); + + var grantComponent = new GrantAbilityEffectComponent([grantAbilityConfig]); + var grantEffect = new EffectData( + "Grant Weapon Land Ability", + new DurationData(DurationType.Infinite), + effectComponents: [grantComponent]); + EffectsManager.ApplyEffect(new Effect(grantEffect, new EffectOwnership(this, this))); + } - // Testing out kill - // GetTree().CreateTimer(2).Timeout += () => Kill(this); + // Forge events + EventSubscriptionToken token = WeaponSystem.Events.Subscribe(WeaponSystem.WeaponLandTag, OnWeaponLanded); + } + + public void OnWeaponLanded(EventData data) + { + var source = data.Source; + var target = data.Target; + var magnitude = data.EventMagnitude; + var weaponLandPayload = data.Payload; + + var tagsManager = ForgeManager.GetTagsManager(this); + var weaponLandTag = Tag.RequestTag(tagsManager, "abilities.weapon.land").GetSingleTagContainer(); + if (weaponLandTag == null) return; + var anyActivated = Abilities.TryActivateAbilitiesByTag( + weaponLandTag, + target, + out var failures); + if (anyActivated) + { + } } ///////////////////////////