1
0
Fork 0
mirror of https://github.com/Phantop/LADXHD.git synced 2025-04-27 16:14:21 +00:00
LADXHD/InGame/GameObjects/Enemies/EnemyBooBuddy.cs
2023-12-14 17:21:22 -05:00

252 lines
10 KiB
C#

using System;
using Microsoft.Xna.Framework;
using ProjectZ.Base;
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.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.Enemies
{
internal class EnemyBooBuddy : GameObject
{
private readonly BodyComponent _body;
private readonly AiComponent _aiComponent;
private readonly Animator _animator;
private readonly AnimationComponent _animatorComponent;
private readonly AiDamageState _aiDamageState;
private readonly DamageFieldComponent _damageField;
private readonly DrawShadowCSpriteComponent _shadowDrawComponent;
private RectangleF _rangeBox;
private string _ligthKey;
private Vector2 _targetPositionOffset;
private float _speed;
private static float _positionRadiant = 0.76f;
private float _directionChangeCounter;
private Vector2 _targetVelocity;
private float _cooldownCounter;
private bool _cooldownPosition;
private int _fadeTime = 250;
public EnemyBooBuddy() : base("boo buddy") { }
public EnemyBooBuddy(Map.Map map, int posX, int posY, string lightKey) : base(map)
{
Tags = Values.GameObjectTag.Enemy;
EntityPosition = new CPosition(posX + 8, posY + 24, 0);
EntitySize = new Rectangle(-8, -21, 16, 21);
_ligthKey = lightKey;
_animator = AnimatorSaveLoad.LoadAnimator("Enemies/boo buddy");
_animator.Play("attack");
var sprite = new CSprite(EntityPosition);
_animatorComponent = new AnimationComponent(_animator, sprite, new Vector2(0, -5));
_rangeBox = map.GetField(posX, posY, 8);
_body = new BodyComponent(EntityPosition, -7, -20, 14, 14, 8)
{
IgnoresZ = true,
CollisionTypes = Values.CollisionTypes.None,
};
// randomize the speed and the target position so that the ghosts don't align with each other
_positionRadiant += (float)Math.PI;
_targetPositionOffset = new Vector2((float)Math.Sin(_positionRadiant) * 6, (float)Math.Cos(_positionRadiant) * 6 + 4);
_speed = 0.55f + Game1.RandomNumber.Next(0, 101) / 1000f;
_aiComponent = new AiComponent();
var stateAttacking = new AiState(UpdateAttacking) { Init = InitAttacking };
var stateCooldown = new AiState(UpdateCooldown) { Init = InitCooldown };
var stateFleeing = new AiState(UpdateFleeing) { Init = InitFleeing };
var stateFading = new AiState();
stateFading.Trigger.Add(new AiTriggerCountdown(_fadeTime, UpdateFading, FinishedFading));
_aiComponent.States.Add("attacking", stateAttacking);
_aiComponent.States.Add("cooldown", stateCooldown);
_aiComponent.States.Add("fleeing", stateFleeing);
_aiComponent.States.Add("fading", stateFading);
_aiDamageState = new AiDamageState(this, _body, _aiComponent, sprite, 4, true, false);
var damageCollider = new CBox(EntityPosition, -7, -20, 0, 14, 14, 8);
AddComponent(DamageFieldComponent.Index, _damageField = new DamageFieldComponent(damageCollider, HitType.Enemy, 2));
AddComponent(HittableComponent.Index, new HittableComponent(_body.BodyBox, OnHit));
AddComponent(AiComponent.Index, _aiComponent);
AddComponent(BodyComponent.Index, _body);
AddComponent(BaseAnimationComponent.Index, _animatorComponent);
AddComponent(DrawComponent.Index, new BodyDrawComponent(_body, sprite, Values.LayerPlayer));
AddComponent(DrawShadowComponent.Index, _shadowDrawComponent = new DrawShadowCSpriteComponent(sprite));
if (!string.IsNullOrEmpty(_ligthKey))
AddComponent(KeyChangeListenerComponent.Index, new KeyChangeListenerComponent(KeyChanged));
_aiComponent.ChangeState("attacking");
}
private void KeyChanged()
{
var lightOn = Game1.GameManager.SaveManager.GetString(_ligthKey) == "1";
if (lightOn)
_aiComponent.ChangeState("fleeing");
else if (_aiComponent.CurrentStateId == "fleeing")
_aiComponent.ChangeState("attacking");
}
private Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
{
if (damageType == HitType.Bow)
return _aiDamageState.OnHit(gameObject, direction, damageType, 1, pieceOfPower);
if (_aiComponent.CurrentStateId == "fleeing" || damageType == HitType.MagicRod)
return _aiDamageState.OnHit(gameObject, direction, damageType, 4, pieceOfPower);
// was hit in the attack state -> change int cooldown mode
if (_aiComponent.CurrentStateId == "attacking")
{
_aiComponent.ChangeState("cooldown");
return Values.HitCollision.Enemy;
}
return Values.HitCollision.None;
}
private void InitAttacking()
{
_animatorComponent.Sprite.Color = Color.White;
_shadowDrawComponent.IsActive = true;
_animator.Play("attack");
}
private void UpdateAttacking()
{
if (_rangeBox.Contains(MapManager.ObjLink.BodyRectangle))
{
// move towards the player
var playerDirection = MapManager.ObjLink.EntityPosition.Position + _targetPositionOffset - EntityPosition.Position;
if (playerDirection != Vector2.Zero)
playerDirection.Normalize();
_targetVelocity = playerDirection * _speed;
}
else
{
_directionChangeCounter -= Game1.DeltaTime;
// change direction
if (_directionChangeCounter <= 0)
{
_directionChangeCounter = Game1.RandomNumber.Next(750, 1000);
// move towards the center of the room with a random offset
var center = _rangeBox.Center + new Vector2(8, 12);
var centerOffset = center - EntityPosition.Position;
// depending on how far away the enemy is to the center of the room the direction can diverge more or less
var offsetLength = (int)centerOffset.Length();
var randomOffset = Game1.RandomNumber.Next(0, Math.Max(1, 50 - offsetLength)) / 50f * 2f * Math.PI;
var radius = Math.Atan2(centerOffset.Y, centerOffset.X) + randomOffset;
_targetVelocity = new Vector2((float)Math.Cos(radius), (float)Math.Sin(radius)) * _speed * 0.5f;
}
}
var percentage = (float)Math.Pow(0.9, Game1.TimeMultiplier);
_body.VelocityTarget = percentage * _body.VelocityTarget + (1 - percentage) * _targetVelocity;
_animatorComponent.MirroredH = _targetVelocity.X < 0;
}
private void InitCooldown()
{
// disable the damage field
_damageField.IsActive = false;
_animator.Play("hit");
_cooldownPosition = false;
_cooldownCounter = 0;
_body.VelocityTarget = Vector2.Zero;
}
private void UpdateCooldown()
{
_cooldownCounter += Game1.DeltaTime;
// blink
var isTransparent = _cooldownCounter % 133 < 66;
_animatorComponent.Sprite.Color = isTransparent ? Color.Transparent : Color.White;
_shadowDrawComponent.IsActive = !isTransparent;
// change location
if (!_cooldownPosition && _cooldownCounter > 500)
{
_cooldownPosition = true;
// the new position is the mirror position from the center
var center = _rangeBox.Center + new Vector2(8, 8);
var centerOffset = EntityPosition.Position - center;
EntityPosition.Set(center - centerOffset);
}
// finished cooldown
if (_cooldownCounter > 1000)
{
_aiComponent.ChangeState("attacking");
// reactivate the damage field
_damageField.IsActive = true;
}
}
private void InitFleeing()
{
_animatorComponent.Sprite.Color = Color.White;
_shadowDrawComponent.IsActive = true;
_animator.Play("flee");
}
private void UpdateFleeing()
{
// move away from the player
var playerDirection = EntityPosition.Position - MapManager.ObjLink.EntityPosition.Position;
if (playerDirection != Vector2.Zero)
playerDirection.Normalize();
var percentage = (float)Math.Pow(0.9, Game1.TimeMultiplier);
_body.VelocityTarget = percentage * _body.VelocityTarget + (1 - percentage) * playerDirection * 0.25f;
_animatorComponent.MirroredH = playerDirection.X < 0;
// remove enemy if he is too far away
var center = _rangeBox.Center + new Vector2(0, 12);
var centerOffset = EntityPosition.Position - center;
if (Math.Abs(centerOffset.X) > 80 || Math.Abs(centerOffset.Y) > 72)
_aiComponent.ChangeState("fading");
}
private void UpdateFading(double time)
{
// fade away
_animatorComponent.Sprite.Color = Color.White * (float)(time / _fadeTime);
}
private void FinishedFading()
{
Map.Objects.DeleteObjects.Add(this);
}
}
}