LADXHD/InGame/GameObjects/MidBoss/MBossStoneHinox.cs
2023-12-14 17:21:22 -05:00

306 lines
11 KiB
C#

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.Dungeon;
using ProjectZ.InGame.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.MidBoss
{
class MBossStoneHinox : GameObject
{
private readonly Animator _animator;
private readonly BodyComponent _body;
private readonly AiComponent _aiComponent;
private readonly AiDamageState _aiDamageState;
private readonly AnimationComponent _animationComponent;
private Vector2[] _walkOffset = { new Vector2(-24, 0), new Vector2(24, 0), new Vector2(0, 24) };
private readonly Vector2 _spawnPosition;
private Vector2 _targetPosition;
private int _lastWalkFrame;
private bool _wasHit;
private readonly string _saveKey;
public MBossStoneHinox() : base("stone hinox") { }
public MBossStoneHinox(Map.Map map, int posX, int posY, string saveKey) : base(map)
{
EntityPosition = new CPosition(posX + 16, posY + 32, 0);
EntitySize = new Rectangle(-16, -32, 32, 32);
_saveKey = saveKey;
// was already killed?
if (!string.IsNullOrEmpty(_saveKey) &&
Game1.GameManager.SaveManager.GetString(_saveKey) == "1")
{
IsDead = true;
return;
}
_spawnPosition = EntityPosition.Position;
_animator = AnimatorSaveLoad.LoadAnimator("MidBoss/stone hinox");
_animator.Play("idle");
var sprite = new CSprite(EntityPosition);
_animationComponent = new AnimationComponent(_animator, sprite, Vector2.Zero);
_body = new BodyComponent(EntityPosition, -14, -20, 28, 20, 8)
{
Gravity = -0.1f,
FieldRectangle = Map.GetField(posX, posY, 16)
};
_aiComponent = new AiComponent();
var stateIdle = new AiState(UpdateIdle);
var stateIdleDelay = new AiState(UpdateIdleDelay);
var stateWalk = new AiState(UpdateWalk) { Init = InitWalking };
var stateJump = new AiState(UpdateJump) { Init = InitJump };
var stateHitFloor = new AiState { Init = InitHitFloor };
stateHitFloor.Trigger.Add(new AiTriggerCountdown(500, null, EndHitFloor));
var statePostAttack = new AiState { Init = InitPostAttack };
statePostAttack.Trigger.Add(new AiTriggerCountdown(400, null, EndPostAttack));
var statePostHit = new AiState { Init = InitPostHit };
statePostHit.Trigger.Add(new AiTriggerCountdown(300, null, EndPostHit));
_aiComponent.States.Add("idle", stateIdle);
_aiComponent.States.Add("idleDelay", stateIdleDelay);
_aiComponent.States.Add("walk", stateWalk);
_aiComponent.States.Add("jump", stateJump);
_aiComponent.States.Add("attack", stateHitFloor);
_aiComponent.States.Add("postAttack", statePostAttack);
_aiComponent.States.Add("postHit", statePostHit);
_aiDamageState = new AiDamageState(this, _body, _aiComponent, sprite, 8, false, false)
{
HitMultiplierX = 0,
HitMultiplierY = 0,
BossHitSound = true
};
_aiDamageState.DamageSpriteShader = Resources.DamageSpriteShader1;
_aiDamageState.AddBossDamageState(OnDeathAnimationEnd);
_aiComponent.ChangeState("idle");
var damageCollider = new CBox(EntityPosition, -14, -24, 0, 28, 24, 8);
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageCollider, HitType.Enemy, 4));
AddComponent(PushableComponent.Index, new PushableComponent(_body.BodyBox, OnPush));
AddComponent(HittableComponent.Index, new HittableComponent(_body.BodyBox, OnHit));
AddComponent(AiComponent.Index, _aiComponent);
AddComponent(BodyComponent.Index, _body);
AddComponent(BaseAnimationComponent.Index, _animationComponent);
AddComponent(DrawComponent.Index, new BodyDrawComponent(_body, sprite, Values.LayerPlayer));
AddComponent(DrawShadowComponent.Index, new BodyDrawShadowComponent(_body, sprite) { ShadowWidth = 16, ShadowHeight = 6 });
}
private void UpdateIdle()
{
if (_body.FieldRectangle.Contains(MapManager.ObjLink.BodyRectangle))
{
Game1.GameManager.StartDialogPath("stone_hinox");
_aiComponent.ChangeState("idleDelay");
}
}
private void UpdateIdleDelay()
{
if (!Game1.GameManager.DialogIsRunning())
_aiComponent.ChangeState("jump");
}
private void InitJump()
{
_body.IsGrounded = false;
_body.Velocity.Z = 2;
_animator.Pause();
}
private void UpdateJump()
{
// jump towards the spawn position
var spawnDirection = _spawnPosition - EntityPosition.Position;
if (spawnDirection.Length() > 0.75f * Game1.TimeMultiplier)
{
spawnDirection.Normalize();
_body.VelocityTarget = 0.75f * spawnDirection;
}
else
{
_body.VelocityTarget = Vector2.Zero;
EntityPosition.Set(_spawnPosition);
}
// end jump
if (_body.IsGrounded)
{
_body.VelocityTarget = Vector2.Zero;
EntityPosition.Set(_spawnPosition);
_aiComponent.ChangeState("attack");
Game1.GameManager.PlaySoundEffect("D360-11-0B");
// shake the screen
Game1.GameManager.ShakeScreen(800, 1, 2, 2, 7.5f);
MapManager.ObjLink.GroundStun(800);
// spawn stones
SpawnStone();
SpawnStone();
}
}
private void InitHitFloor()
{
_animationComponent.MirroredH = _animator.CurrentFrameIndex == 0;
_animator.Play("attack");
}
private void EndHitFloor()
{
SpawnStone();
_aiComponent.ChangeState("postAttack");
}
private void SpawnStone()
{
var objStone = new MBossStoneHinoxStone(Map,
new Vector3(_spawnPosition.X + Game1.RandomNumber.Next(0, 120) - 60, _spawnPosition.Y - 8 - Game1.RandomNumber.Next(0, 8), 16),
new Vector3(0, 1, 1), (int)_spawnPosition.X);
Map.Objects.SpawnObject(objStone);
}
private void InitPostAttack()
{
_animator.Play("fast");
_animator.SetFrame(_animationComponent.MirroredH ? 1 : 0);
_animationComponent.MirroredH = false;
}
private void EndPostAttack()
{
if (_wasHit)
{
_wasHit = false;
_aiComponent.ChangeState("jump");
}
else
{
var newState = Game1.RandomNumber.Next(0, 2);
_aiComponent.ChangeState(newState == 0 ? "walk" : "jump");
_aiComponent.ChangeState("jump");
}
}
private void InitPostHit()
{
_animator.Pause();
}
private void EndPostHit()
{
_aiComponent.ChangeState("postAttack");
}
private void InitWalking()
{
var frameIndex = _animator.CurrentFrameIndex;
_animator.Play("idle");
_animator.SetFrame(frameIndex);
_lastWalkFrame = frameIndex;
// walk into a random direction if we are at the spawn position
if (EntityPosition.Position == _spawnPosition)
{
var randomDirection = Game1.RandomNumber.Next(0, 3);
_targetPosition = _spawnPosition + _walkOffset[randomDirection];
}
else
{
_targetPosition = _spawnPosition;
}
}
private void UpdateWalk()
{
// only move when a new frame from the animation is shown
if (_lastWalkFrame == _animator.CurrentFrameIndex)
return;
_lastWalkFrame = _animator.CurrentFrameIndex;
// finished walking?
if (EntityPosition.Position == _targetPosition)
{
var newState = Game1.RandomNumber.Next(0, 2);
_aiComponent.ChangeState(newState == 0 ? "walk" : "jump");
}
// walk towards the target position
var newPosition = EntityPosition.Position;
if (EntityPosition.X != _targetPosition.X)
newPosition.X += 4 * Math.Sign(_targetPosition.X - EntityPosition.X);
if (EntityPosition.Y != _targetPosition.Y)
newPosition.Y += 4 * Math.Sign(_targetPosition.Y - EntityPosition.Y);
EntityPosition.Set(newPosition);
}
private void OnDeathAnimationEnd()
{
if (!string.IsNullOrEmpty(_saveKey))
Game1.GameManager.SaveManager.SetString(_saveKey, "1");
// stop boss music
Game1.GameManager.SetMusic(-1, 2);
// spawns a fairy
Game1.GameManager.PlaySoundEffect("D360-27-1B");
Map.Objects.SpawnObject(new ObjDungeonFairy(Map, (int)EntityPosition.X, (int)EntityPosition.Y, 8));
Map.Objects.DeleteObjects.Add(this);
}
private bool OnPush(Vector2 direction, PushableComponent.PushType type)
{
return true;
}
private Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
{
if (_aiComponent.CurrentStateId == "idle")
return Values.HitCollision.None;
if (_aiComponent.CurrentStateId == "walk" && _aiDamageState.CurrentLives > damage)
{
_aiComponent.ChangeState("postHit");
_animator.Pause();
_wasHit = true;
}
if (damageType == HitType.Bomb || damageType == HitType.Boomerang || damageType == HitType.Bow || damageType == HitType.MagicRod)
damage = 4;
var hitCollision = _aiDamageState.OnHit(gameObject, direction, damageType, damage, false);
if (_aiDamageState.CurrentLives <= 0)
{
_body.VelocityTarget = Vector2.Zero;
_animator.Pause();
}
if (hitCollision != Values.HitCollision.None)
return hitCollision | Values.HitCollision.Repelling | Values.HitCollision.Repelling0;
return hitCollision;
}
}
}