Files
MovementTests/scenes/projectile/Projectile.cs
2026-05-10 12:26:43 +02:00

151 lines
4.7 KiB
C#

using Godot;
using System;
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Gamesmiths.Forge.Abilities;
using Gamesmiths.Forge.Core;
using Gamesmiths.Forge.Cues;
using Gamesmiths.Forge.Effects;
using Gamesmiths.Forge.Events;
using Gamesmiths.Forge.Godot.Resources.Abilities;
using Gamesmiths.Forge.Statescript;
using Gamesmiths.Forge.Tags;
using Movementtests.interfaces;
using Movementtests.tools;
using Node = Godot.Node;
[GlobalClass, Icon("res://assets/ui/IconGodotNode/node_3D/icon_bullet.png"), Meta(typeof(IAutoNode))]
public partial class Projectile : RigidBody3D, IForgeEntity, ITargetable, IKillable, ISpawnable
{
public override void _Notification(int what) => this.Notify(what);
#region Dependencies
[Dependency]
public TagsManager TagsManager => this.DependOn<TagsManager>();
[Dependency]
public CuesManager CuesManager => this.DependOn<CuesManager>();
#endregion
#region IForgeEntity
// Perfectly forward the IForgeEntity interface to the ForgeEntity component
public EntityAttributes Attributes
{
get => ForgeEntity.Attributes;
set => ForgeEntity.Attributes = value;
}
public EntityTags Tags
{
get => ForgeEntity.Tags;
set => ForgeEntity.Tags = value;
}
public EffectsManager EffectsManager
{
get => ForgeEntity.EffectsManager;
set => ForgeEntity.EffectsManager = value;
}
public EntityAbilities Abilities
{
get => ForgeEntity.Abilities;
set => ForgeEntity.Abilities = value;
}
public EventManager Events
{
get => ForgeEntity.Events;
set => ForgeEntity.Events = value;
}
public Variables SharedVariables
{
get => ForgeEntity.SharedVariables;
set => ForgeEntity.SharedVariables = value;
}
#endregion
[Node("ForgeEntityNode")] public required ForgeEntityNode ForgeEntity { get; set;}
[Export] public float Damage = 10f;
[Export] public float Speed = 10f;
[Export] public float HomingFactor = 0.2f;
public Node3D? Target { get; set; }
public void Init()
{
ApplyCentralImpulse(ComputeImpulseToTarget());
}
public Vector3 ComputeImpulseToTarget()
{
if (Target == null) return Vector3.Zero;
return Vector3.Up * Speed;
var targetDir = GlobalPosition.DirectionTo(Target.GlobalPosition);
var hDir = new Vector3(targetDir.X, 0, targetDir.Z).Normalized();
var throwDir = (hDir + 3*Vector3.Up).Normalized();
var distanceTo = GlobalPosition.DistanceTo(Target.GlobalPosition);
return throwDir * distanceTo*0.8f;
}
public override void _IntegrateForces(PhysicsDirectBodyState3D state)
{
if (Target == null) return;
var targetPos = Target is ITargetable targetable ? targetable.GetTargetGlobalPosition() : Target.GlobalPosition;
var targetVel = state.LinearVelocity + GlobalPosition.DirectionTo(targetPos) * HomingFactor;
if (targetVel.Length() > Speed) targetVel = targetVel.Normalized() * Speed;
state.LinearVelocity = targetVel;
// var targetDir = GlobalPosition.DirectionTo(targetPos);
// var hDir = new Vector3(targetDir.X, 0, targetDir.Z).Normalized();
// var currentVelocityHDir = new Vector3(state.LinearVelocity.X, 0, state.LinearVelocity.Z);
// var finalHDir = hDir*HomingFactor + currentVelocityHDir;
// var combinedVelocity = new Vector3(finalHDir.X, state.LinearVelocity.Y, finalHDir.Z);
// if (combinedVelocity.Length() > Speed) combinedVelocity = combinedVelocity.Normalized() * Speed;
// state.LinearVelocity = combinedVelocity;
// state.LinearVelocity += targetDir/5.0f;
}
public void OnResolved()
{
BodyEntered += OnProjectileHit;
// Events.Subscribe(Tag.RequestTag(TagsManager, "events.weapon.flyingtick"), data => { GD.Print("tick"); });
}
private void OnProjectileHit(Node body)
{
if (body is IForgeEntity entity)
{
Events.Raise(new EventData
{
EventTags = Tag.RequestTag(TagsManager, "events.combat.hit").GetSingleTagContainer()!,
Source = this,
Target = entity,
EventMagnitude = Damage
});
}
Events.Raise(new EventData
{
EventTags = Tag.RequestTag(TagsManager, "events.weapon.planted").GetSingleTagContainer()!,
Source = this,
EventMagnitude = Damage
});
Kill();
}
public Vector3 GetTargetGlobalPosition()
{
return GlobalPosition;
}
public void Kill()
{
QueueFree(); // If thrown offmap
}
}