using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] public partial class ThrownItem : RigidBody3D, IThrownItem { public override void _Notification(int what) => this.Notify(what); [Dependency] public IPlayer Player => this.DependOn(); [Dependency] public IGame Game => this.DependOn(); public IBaseInventoryItem ItemThatIsThrown { get; set; } private EffectService _effectService; private ItemReroller _itemReroller; [Node] public Sprite2D Sprite { get; set; } = default!; [Node] public Area3D Collision { get; set; } = default!; public void OnResolved() { BodyEntered += ThrownItem_BodyEntered; Collision.AreaEntered += Collision_AreaEntered; GlobalPosition = Player.GlobalPosition; Sprite.Texture = ItemThatIsThrown.GetTexture(); AddCollisionExceptionWith((Node)Player); Collision.SetCollisionLayerValue(3, false); _itemReroller = new ItemReroller(ItemDatabase.Instance); } private void Collision_AreaEntered(Area3D area) { if (area.GetOwner() is IEnemy enemy) { CalculateEffect(enemy); QueueFree(); } } private void ThrownItem_BodyEntered(Node body) { if (ItemThatIsThrown is ThrowableItem) { QueueFree(); } else { RemoveCollisionExceptionWith((Node)Player); Collision.SetCollisionLayerValue(3, true); LinearVelocity = Vector3.Zero; AngularVelocity = Vector3.Zero; GravityScale = 0.5f; } } public void Throw(EffectService effectService) { _effectService = effectService; ApplyCentralImpulse(-Player.GlobalBasis.Z.Normalized() * ItemThatIsThrown.ThrowSpeed); } public void RescueItem() { ContactMonitor = false; Freeze = true; PlayRescueAnimation(); Game.RescuedItems.Items.Add(ItemThatIsThrown); } private void PlayRescueAnimation() { LoadShader("res://src/vfx/shaders/PixelMelt.gdshader"); var tweener = GetTree().CreateTween(); SetShaderValue(true); tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 0.3f, 2f); tweener.TweenCallback(Callable.From(QueueFree)); } private void LoadShader(string shaderPath) { var shader = GD.Load(shaderPath); Sprite.Material = new ShaderMaterial(); var shaderMaterial = (ShaderMaterial)Sprite.Material; shaderMaterial.Shader = shader; } private void SetShaderValue(float shaderValue) { var shaderMaterial = (ShaderMaterial)Sprite.Material; shaderMaterial.SetShaderParameter("progress", shaderValue); } private void SetShaderValue(bool shaderValue) { var shaderMaterial = (ShaderMaterial)Sprite.Material; shaderMaterial.SetShaderParameter("reverse", shaderValue); } private void CalculateEffect(IEnemy enemy) { if (ItemThatIsThrown.ItemTag == ItemTag.MysteryItem) ItemThatIsThrown = _itemReroller.RerollItem(ItemThatIsThrown, Player.Inventory, false); if (ItemThatIsThrown is EffectItem usableItem) { switch (usableItem.UsableItemTag) { case UsableItemTag.LowerTargetTo1HP: enemy.HealthComponent.SetCurrentHealth(1); break; case UsableItemTag.TeleportToRandomLocation: _effectService.TeleportToRandomRoom(enemy); break; default: var damageDealt = DamageCalculator.CalculateDamage(new AttackData(usableItem.ThrowDamage, ElementType.None), enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet); enemy.HealthComponent.Damage(damageDealt); break; } } else { var damageDealt = DamageCalculator.CalculateDamage(new AttackData(ItemThatIsThrown.ThrowDamage, ElementType.None), enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet); enemy.HealthComponent.Damage(damageDealt); } } public void OnExitTree() { BodyEntered -= ThrownItem_BodyEntered; Collision.AreaEntered -= Collision_AreaEntered; } }