LADXHD/InGame/GameObjects/Enemies/EnemyBonePutter.cs

246 lines
10 KiB
C#
Raw Normal View History

2023-12-14 22:21:22 +00:00
using System;
using Microsoft.Xna.Framework;
using ProjectZ.InGame.GameObjects.Base;
using ProjectZ.InGame.GameObjects.Base.Components;
using ProjectZ.InGame.GameObjects.Base.CObjects;
using ProjectZ.InGame.GameObjects.Base.Components.AI;
using ProjectZ.InGame.GameObjects.Things;
using ProjectZ.InGame.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
using ProjectZ.InGame.GameObjects.Dungeon;
namespace ProjectZ.InGame.GameObjects.Enemies
{
internal class EnemyBonePutter : GameObject
{
private readonly BodyComponent _body;
private readonly AiComponent _aiComponent;
private readonly Animator _animator;
private readonly AnimationComponent _animatorComponent;
private readonly AiDamageState _damageState;
private readonly Vector2 _roomCenter;
private Vector2 _startPosition;
private Vector2 _targetPosition;
private float _flyCounter;
private float _flyTime;
private int _bombThrowCounter;
private const float JumpSpeed = 0.25f;
public EnemyBonePutter() : base("bone putter") { }
public EnemyBonePutter(Map.Map map, int posX, int posY, bool hasWings) : base(map)
{
Tags = Values.GameObjectTag.Enemy;
EntityPosition = new CPosition(posX + 8, posY + 16, 16);
EntitySize = new Rectangle(-8, -32, 16, 32);
_animator = AnimatorSaveLoad.LoadAnimator("Enemies/bone putter");
var sprite = new CSprite(EntityPosition);
_animatorComponent = new AnimationComponent(_animator, sprite, Vector2.Zero);
_body = new BodyComponent(EntityPosition, -4, -8, 8, 8, 8)
{
MoveCollision = OnCollision,
HoleOnPull = OnHolePull,
IgnoresZ = true,
Gravity = -0.075f,
DragAir = 0.875f,
AvoidTypes = Values.CollisionTypes.Hole | Values.CollisionTypes.NPCWall,
FieldRectangle = map.GetField(posX, posY)
};
var roomRectangle = map.GetField(posX, posY);
_roomCenter = new Vector2(roomRectangle.Center.X, roomRectangle.Center.Y + 8);
_aiComponent = new AiComponent();
var stateFlying = new AiState(UpdateFlying) { Init = InitFlying };
var stateJumping = new AiState(UpdateJumping) { Init = InitJumping };
var stateHole = new AiState();
_aiComponent.States.Add("flying", stateFlying);
_aiComponent.States.Add("jumping", stateJumping);
_aiComponent.States.Add("holePull", stateHole);
new AiFallState(_aiComponent, _body, null, null, 200);
_damageState = new AiDamageState(this, _body, _aiComponent, sprite, hasWings ? 3 : 1, false);
_aiComponent.ChangeState(hasWings ? "flying" : "jumping");
var hittableBox = new CBox(EntityPosition, -6, -15, 2, 12, 14, 8, true);
var damageBox = new CBox(EntityPosition, -6, -15, 2, 12, 14, 4, true);
var pushBox = new CBox(EntityPosition, -6, -15, 2, 12, 14, 4, true);
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageBox, HitType.Enemy, 2));
AddComponent(HittableComponent.Index, new HittableComponent(hittableBox, OnHit));
AddComponent(AiComponent.Index, _aiComponent);
AddComponent(BodyComponent.Index, _body);
AddComponent(BaseAnimationComponent.Index, _animatorComponent);
AddComponent(PushableComponent.Index, new PushableComponent(pushBox, OnPush));
AddComponent(DrawComponent.Index, new BodyDrawComponent(_body, sprite, Values.LayerPlayer));
AddComponent(DrawShadowComponent.Index, new BodyDrawShadowComponent(_body, sprite));
}
public bool StartJump()
{
return _aiComponent.CurrentStateId == "jumping" && _body.IsGrounded && EntityPosition.Z <= 0;
}
private void SpawnFairy()
{
// remove the enemy
Map.Objects.DeleteObjects.Add(this);
// spawn explosion effect that ends in a fairy spawning
var animationExplosion = new ObjAnimator(Map, (int)EntityPosition.X - 8, (int)(EntityPosition.Y - EntityPosition.Z - 16), Values.LayerTop, "Particles/spawn", "run", true);
animationExplosion.Animator.OnAnimationFinished = () =>
{
// remove the explosion animation
animationExplosion.Map.Objects.DeleteObjects.Add(animationExplosion);
// spawn fairy
animationExplosion.Map.Objects.SpawnObject(new ObjDungeonFairy(animationExplosion.Map, (int)EntityPosition.X, (int)(EntityPosition.Y - EntityPosition.Z - 4), 0));
};
Map.Objects.SpawnObject(animationExplosion);
}
private void InitFlying()
{
_animator.Play("fly");
_startPosition = EntityPosition.Position;
_targetPosition = _startPosition;
_flyCounter = 0;
}
private void UpdateFlying()
{
_flyCounter -= Game1.DeltaTime;
if (_flyCounter > 0)
{
var lerpPercentage = 0.5f - MathF.Sin(_flyCounter / _flyTime * MathF.PI - MathF.PI / 2) * 0.5f;
var newPosition = Vector2.Lerp(_startPosition, _targetPosition, lerpPercentage);
EntityPosition.Set(newPosition);
}
else
{
ThrowBomb();
EntityPosition.Set(_targetPosition);
_startPosition = _targetPosition;
// the target direction will be in the direction of the center if we are farther away from the center
var centerDirection = EntityPosition.Position - _roomCenter;
var centerDistance = centerDirection.Length();
if (centerDirection != Vector2.Zero)
centerDirection.Normalize();
var centerRadian = MathF.Atan2(centerDirection.Y, centerDirection.X);
// set a new target position
var centerOffset = Math.Clamp((50 - centerDistance) / 25, 0, 1);
var randomRotation = centerRadian - (MathF.PI + Game1.RandomNumber.Next(0, 628) / 100f) * centerOffset;
var randomDistance = Game1.RandomNumber.Next(12, 20);
_targetPosition.X = _startPosition.X - MathF.Cos(randomRotation) * randomDistance;
_targetPosition.Y = _startPosition.Y - MathF.Sin(randomRotation) * randomDistance;
_animatorComponent.MirroredH = _targetPosition.X > _startPosition.X;
_animatorComponent.UpdateSprite();
// random fly time
_flyTime = Game1.RandomNumber.Next(600, 800);
_flyCounter = _flyTime;
}
}
private void ThrowBomb()
{
_bombThrowCounter--;
if (_bombThrowCounter > 0)
return;
var playerDistance = MapManager.ObjLink.EntityPosition.Position - EntityPosition.Position;
if (playerDistance.Length() > 64)
return;
_bombThrowCounter = Game1.RandomNumber.Next(2, 6);
// spawn a bomb
var bomb = new ObjBomb(Map, 0, 0, false, true);
bomb.EntityPosition.Set(new Vector3(EntityPosition.X, EntityPosition.Y + 1, 18));
bomb.Body.Velocity = new Vector3(0, 0, 0.25f);
bomb.Body.Gravity = -0.1f;
bomb.Body.Bounciness = 0.4f;
Map.Objects.SpawnObject(bomb);
}
private void InitJumping()
{
_animator.Play("jump");
_body.IgnoresZ = false;
_body.IsGrounded = false;
_body.JumpStartHeight = 0;
}
private void UpdateJumping()
{
if (_body.IsGrounded)
Jump();
}
private void Jump()
{
// jump into a random direction
var randomRotation = Game1.RandomNumber.Next(0, 628) / 100f;
_body.VelocityTarget.X = -MathF.Cos(randomRotation) * JumpSpeed;
_body.VelocityTarget.Y = -MathF.Sin(randomRotation) * JumpSpeed;
_body.Velocity.Z = 1.5f;
_animatorComponent.MirroredH = _body.VelocityTarget.X > 0;
_animatorComponent.UpdateSprite();
}
private Values.HitCollision OnHit(GameObject originObject, Vector2 direction, HitType type, int damage, bool pieceOfPower)
{
if (type == HitType.Bow)
damage = 3;
if (_aiComponent.CurrentStateId == "flying" && (type == HitType.MagicPowder || type == HitType.Boomerang))
{
SpawnFairy();
return Values.HitCollision.Enemy;
}
if (_aiComponent.CurrentStateId == "flying")
_aiComponent.ChangeState("jumping");
return _damageState.OnHit(originObject, direction, type, damage, pieceOfPower);
}
private void OnHolePull(Vector2 direction, float percentage)
{
if (percentage < 0.5f)
return;
_aiComponent.ChangeState("holePull");
}
private bool OnPush(Vector2 direction, PushableComponent.PushType type)
{
if (type == PushableComponent.PushType.Impact)
_body.Velocity = new Vector3(direction.X * 1.75f, direction.Y * 1.75f, _body.Velocity.Z);
return true;
}
private void OnCollision(Values.BodyCollision collision)
{
if ((collision & Values.BodyCollision.Horizontal) != 0)
_body.Velocity.X = -_body.Velocity.X * 0.5f;
if ((collision & Values.BodyCollision.Vertical) != 0)
_body.Velocity.Y = -_body.Velocity.Y * 0.5f;
}
}
}