using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; using System.Linq; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] public partial class EnemyModelView2D : EnemyModelView, IEnemyModelView { private readonly string _idleName = "Idle"; private readonly string _walkingName = "Walking"; private readonly string _primaryAttackName = "Primary Attack"; private readonly string _secondaryAttackName = "Secondary Attack"; private readonly string _activateName = "Activate"; public override void _Notification(int what) => this.Notify(what); [Export] public EnemyLoreInfo EnemyLoreInfo { get; set; } = default!; [Node] public AnimatedSprite2D AnimatedSprite { get; set; } = default!; [Node] public Area3D Hitbox { get; set; } = default!; [Node] public AnimationPlayer AnimationPlayer { get; set; } = default!; [Node] public AnimationTree AnimationTree { get; set; } = default!; [ExportGroup("Enemy Model Properties")] [Export(PropertyHint.Range, "0.0, 1.0")] private float _upperThreshold { get; set; } = 0.5f; [Export(PropertyHint.Range, "-1.0, 0.0")] private float _lowerThreshold { get; set; } = -0.5f; EnemyDirection _enemyDirection { get; set; } = EnemyDirection.Forward; private AnimationNodeStateMachinePlayback _stateMachine; public void OnReady() { _stateMachine = (AnimationNodeStateMachinePlayback)AnimationTree.Get("parameters/playback"); Hitbox.BodyEntered += Hitbox_BodyEntered; } private void Hitbox_BodyEntered(Node3D body) => EmitSignal(SignalName.HitPlayer); public void SetCurrentDirection(Basis enemyBasis, Vector3 cameraDirection) => _enemyDirection = GetEnemyDirection(enemyBasis, cameraDirection, _upperThreshold, _lowerThreshold); public void PlayPrimaryAttackAnimation() => _stateMachine.Travel(_primaryAttackName); public void PlaySecondaryAttackAnimation() => _stateMachine.Travel(_secondaryAttackName); public void PlayPrimarySkillAnimation() => _stateMachine.Travel("Primary Skill"); public void PlayIdleAnimation() => _stateMachine.Travel(_idleName); public void PlayWalkAnimation() => _stateMachine.Travel(_walkingName); public void PlayActivateAnimation() { } public void PlayHitAnimation() { LoadShader("res://src/vfx/shaders/DamageHit.gdshader"); var tweener = GetTree().CreateTween(); tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 1.0f); } public void PlayDeathAnimation() { LoadShader("res://src/vfx/shaders/PixelMelt.gdshader"); var tweener = GetTree().CreateTween(); tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 0.8f); } private EnemyDirection GetEnemyDirection( Basis enemyBasis, Vector3 cameraDirection, float rotateUpperThreshold, float rotateLowerThreshold) { var enemyForwardDirection = enemyBasis.Z; var enemyLeftDirection = enemyBasis.X; var leftDotProduct = enemyLeftDirection.Dot(cameraDirection); var forwardDotProduct = enemyForwardDirection.Dot(cameraDirection); // Check if forward facing. If the dot product is -1, the enemy is facing the camera. if (forwardDotProduct < _lowerThreshold) { SetForward(); return EnemyDirection.Forward; } // Check if backward facing. If the dot product is 1, the enemy is facing the same direction as the camera. else if (forwardDotProduct > rotateUpperThreshold) { SetBack(); return EnemyDirection.Backward; } else { // If the dot product of the perpendicular direction is positive (up to 1), the enemy is facing to the left (since it's mirrored). if (leftDotProduct < _lowerThreshold) { SetRight(); return EnemyDirection.Left; } // Check if side facing. If the dot product is close to zero in the positive or negative direction, its close to the threshold for turning. if (leftDotProduct > rotateUpperThreshold) { SetLeft(); return EnemyDirection.Right; } } return _enemyDirection; } private void LoadShader(string shaderPath) { var shader = GD.Load(shaderPath); var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); foreach (var sprite in sprites) { sprite.Material = new ShaderMaterial(); var shaderMaterial = (ShaderMaterial)sprite.Material; shaderMaterial.Shader = shader; } } private void SetShaderValue(float shaderValue) { var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); foreach (var sprite in sprites) { var shaderMaterial = (ShaderMaterial)sprite.Material; shaderMaterial.SetShaderParameter("progress", shaderValue); } } private void SetForward() { _enemyDirection = EnemyDirection.Forward; } private void SetLeft() { _enemyDirection = EnemyDirection.Left; } private void SetRight() { _enemyDirection = EnemyDirection.Right; } private void SetBack() { _enemyDirection = EnemyDirection.Backward; } private enum EnemyDirection { Left, Right, Forward, Backward } }