LADXHD/InGame/GameObjects/Bosses/BossSlimeEyeHalf.cs

283 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.CObjects;
using ProjectZ.InGame.GameObjects.Base.Components;
using ProjectZ.InGame.GameObjects.Base.Components.AI;
using ProjectZ.InGame.GameObjects.Things;
using ProjectZ.InGame.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.Bosses
{
class BossSlimeEyeHalf : GameObject
{
private readonly BodyComponent _body;
private readonly AiComponent _aiComponent;
private readonly AiDamageState _damageState;
private readonly Animator _animator;
private readonly PushableComponent _pushableComponent;
private readonly BodyDrawShadowComponent _shadowComponent;
private readonly CSprite _sprite;
private Rectangle _fieldRectangle;
private readonly string _saveKey;
private readonly string _halfKey;
private const int Lives = 4;
private int _jumpHeight = 100;
private bool _highJump;
private bool _wasHit;
private bool _doubleJump;
public BossSlimeEyeHalf(Map.Map map, Vector2 position, string animationName, string saveKey) : base(map)
{
Tags = Values.GameObjectTag.Enemy;
EntityPosition = new CPosition(position.X, position.Y, 0);
EntitySize = new Rectangle(-16, -32, 32, 32);
_saveKey = saveKey;
_halfKey = _saveKey + "half";
Game1.GameManager.SaveManager.SetString(_halfKey, "0");
_animator = AnimatorSaveLoad.LoadAnimator("Nightmares/nightmare eye");
_animator.Play(animationName);
_sprite = new CSprite(EntityPosition);
var animationComponent = new AnimationComponent(_animator, _sprite, Vector2.Zero);
_fieldRectangle = map.GetField((int)position.X, (int)position.Y, 16);
_body = new BodyComponent(EntityPosition, -12, -24, 24, 22, 8)
{
Bounciness = 0.25f,
Gravity = -0.15f,
Drag = 0.85f,
DragAir = 0.98f,
IgnoreHeight = true
};
var hittableRectangle = new CBox(EntityPosition, -14, -24, 0, 28, 22, 8, true);
var damageCollider = new CBox(EntityPosition, -14, -24, 28, 22, 8);
var stateInit = new AiState();
stateInit.Trigger.Add(new AiTriggerCountdown(50, null, InitJump)); // 166
var stateWaiting = new AiState(UpdateWaiting);
stateWaiting.Trigger.Add(new AiTriggerRandomTime(ToJumping, 750, 1250));
var stateJumping = new AiState(UpdateJumping);
var stateHighJumping = new AiState();
stateHighJumping.Trigger.Add(new AiTriggerCountdown(300, UpdateHighJump, () => UpdateHighJump(0f)));
var stateHighJumpingWait = new AiState();
stateHighJumpingWait.Trigger.Add(new AiTriggerRandomTime(EndHighjump, 500, 1000));
_aiComponent = new AiComponent();
_aiComponent.States.Add("init", stateInit);
_aiComponent.States.Add("waiting", stateWaiting);
_aiComponent.States.Add("jumping", stateJumping);
_aiComponent.States.Add("highJump", stateHighJumping);
_aiComponent.States.Add("highJumpWaiting", stateHighJumpingWait);
_damageState = new AiDamageState(this, _body, _aiComponent, _sprite, Lives, false) { BossHitSound = true };
_damageState.AddBossDamageState(OnDeath);
_aiComponent.ChangeState("init");
_pushableComponent = new PushableComponent(_body.BodyBox, OnPush);
AddComponent(PushableComponent.Index, _pushableComponent);
AddComponent(AiComponent.Index, _aiComponent);
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageCollider, HitType.Enemy, 4));
AddComponent(HittableComponent.Index, new HittableComponent(hittableRectangle, OnHit));
AddComponent(BodyComponent.Index, _body);
AddComponent(BaseAnimationComponent.Index, animationComponent);
AddComponent(DrawComponent.Index, new BodyDrawComponent(_body, _sprite, Values.LayerPlayer));
AddComponent(DrawShadowComponent.Index, _shadowComponent = new BodyDrawShadowComponent(_body, _sprite)
{
ShadowWidth = 26,
ShadowHeight = 8,
OffsetY = -2,
Height = Map.ShadowHeight * 1.25f
});
_damageState.SetDamageState();
}
private void InitJump()
{
_aiComponent.ChangeState("jumping");
var direction = EntityPosition.Position - MapManager.ObjLink.EntityPosition.Position;
if (direction != Vector2.Zero)
direction.Normalize();
_body.Velocity = new Vector3(direction * 1.5f, 2.5f);
}
private void UpdateWaiting()
{
// start with the high jump?
if (_wasHit && _body.Velocity.Length() < 0.25f)
ToHighjump();
UpdateAnimation();
}
private void ToJumping()
{
if (_wasHit)
return;
_aiComponent.ChangeState("jumping");
// random direction
var direction = MapManager.ObjLink.EntityPosition.Position - EntityPosition.Position;
float radius;
// random direction
var toPlayer = false;
if (Game1.RandomNumber.Next(0, 4) < 3 && !_doubleJump)
{
radius = (Game1.RandomNumber.Next(0, 100) / 100f) * MathF.PI * 2;
}
else
{
toPlayer = true;
_doubleJump = false;
radius = MathF.Atan2(direction.Y, direction.X);
}
// 50% chance to jump twice towards the player
_doubleJump = Game1.RandomNumber.Next(0, 2) == 0;
var speed = (Game1.RandomNumber.Next(toPlayer ? 100 : 150, toPlayer ? 200 : 250) / 100f);
var jumpDirection = new Vector2(MathF.Cos(radius), MathF.Sin(radius)) * speed;
_body.Velocity = new Vector3(jumpDirection.X, jumpDirection.Y, 2);
}
private void UpdateJumping()
{
_sprite.Color = Color.White * MathF.Min((_jumpHeight - EntityPosition.Z) / 15f, 1);
if (_body.IsGrounded)
{
_body.Velocity = Vector3.Zero;
_aiComponent.ChangeState("waiting");
_pushableComponent.IsActive = true;
// came down from a high jump? -> shake the screen
if (_highJump)
{
_wasHit = false;
_highJump = false;
MapManager.ObjLink.GroundStun();
Game1.GameManager.PlaySoundEffect("D360-11-0B");
Game1.GameManager.ShakeScreen(250, 1, 2, 2.5f, 5.5f);
}
}
UpdateAnimation();
}
private void ToHighjump()
{
_aiComponent.ChangeState("highJump");
_body.Velocity = Vector3.Zero;
_body.IgnoresZ = true;
_highJump = true;
}
private void UpdateHighJump(double time)
{
EntityPosition.Z = (1 - MathF.Sin(((float)time / 300) * MathF.PI / 2)) * _jumpHeight;
_sprite.Color = Color.White * MathF.Min((float)time / 50f, 1);
_shadowComponent.Transparency = MathF.Min((float)time / 50f, 1);
if (time == 0)
_aiComponent.ChangeState("highJumpWaiting");
UpdateAnimation();
}
private void EndHighjump()
{
_aiComponent.ChangeState("jumping");
// clamp the position to land inside the room
var newPosition = MapManager.ObjLink.EntityPosition.Position;
newPosition.X = MathHelper.Clamp(newPosition.X,
_fieldRectangle.X + _body.Width / 2, _fieldRectangle.Right - _body.Width / 2);
newPosition.Y = MathHelper.Clamp(newPosition.Y + 8,
_fieldRectangle.Y - _body.OffsetY,
_fieldRectangle.Bottom - _body.OffsetY - _body.Height);
EntityPosition.Set(newPosition);
_body.Velocity.Z = 0;
_body.IgnoresZ = false;
_shadowComponent.Transparency = 1;
}
private void UpdateAnimation()
{
_animator.Play(_body.IsGrounded ? "half_floor" : "half_jump");
}
private void OnDeath()
{
if (!string.IsNullOrEmpty(_saveKey))
{
var key = Game1.GameManager.SaveManager.GetString(_halfKey);
if (key != "1")
Game1.GameManager.SaveManager.SetString(_halfKey, "1");
// killed both parts?
else
{
Game1.GameManager.PlaySoundEffect("D378-26-1A");
Game1.GameManager.SetMusic(-1, 2);
Game1.GameManager.SaveManager.SetString(_saveKey, "1");
// spawn big heart
Map.Objects.SpawnObject(new ObjItem(Map,
(int)EntityPosition.X - 8, (int)EntityPosition.Y - 24, "j", "d3_heartMeter", "heartMeterFull", null));
}
}
Map.Objects.DeleteObjects.Add(this);
}
private Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
{
if (_damageState.IsInDamageState())
return Values.HitCollision.None;
_damageState.OnHit(gameObject, direction, damageType, damage, pieceOfPower);
_body.Velocity.X = direction.X * 5f;
_body.Velocity.Y = direction.Y * 5f;
_wasHit = true;
return Values.HitCollision.Enemy;
}
private bool OnPush(Vector2 direction, PushableComponent.PushType type)
{
if (type != PushableComponent.PushType.Impact)
return false;
_wasHit = true;
_body.Velocity.X = direction.X * 1.5f;
_body.Velocity.Y = direction.Y * 1.5f;
return true;
}
}
}