mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
377 lines
15 KiB
C#
377 lines
15 KiB
C#
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.GameObjects.Things;
|
|
using ProjectZ.InGame.Map;
|
|
using ProjectZ.InGame.SaveLoad;
|
|
using ProjectZ.InGame.Things;
|
|
|
|
namespace ProjectZ.InGame.GameObjects.MidBoss
|
|
{
|
|
class MBossHinox : GameObject
|
|
{
|
|
private readonly Color[] _colors = new Color[] { new Color(248, 120, 8), new Color(248, 8, 40), new Color(24, 128, 248) };
|
|
|
|
private readonly Animator _animator;
|
|
private readonly BodyComponent _body;
|
|
private readonly AiComponent _aiComponent;
|
|
private readonly AiDamageState _aiDamageState;
|
|
private readonly DamageFieldComponent _damageFieldComponent;
|
|
private readonly CBox _grabBox;
|
|
|
|
private readonly string _saveKey;
|
|
|
|
private const int GrabTime = 300;
|
|
private const int Lives = 8;
|
|
|
|
private float _runParticleCount;
|
|
|
|
private Vector3 _grabStartPosition;
|
|
private int _grabDirection;
|
|
|
|
public MBossHinox() : base("hinox") { }
|
|
|
|
public MBossHinox(Map.Map map, int posX, int posY, string saveKey, int color) : base(map)
|
|
{
|
|
if (!string.IsNullOrEmpty(saveKey) &&
|
|
Game1.GameManager.SaveManager.GetString(saveKey) == "1")
|
|
{
|
|
IsDead = true;
|
|
return;
|
|
}
|
|
|
|
EntityPosition = new CPosition(posX + 16, posY + 32, 0);
|
|
EntitySize = new Rectangle(-16, -32, 32, 32);
|
|
|
|
_saveKey = saveKey;
|
|
|
|
_animator = AnimatorSaveLoad.LoadAnimator("MidBoss/hinox");
|
|
_animator.Play("idle_0");
|
|
|
|
color = MathHelper.Clamp(color, 0, _colors.Length - 1);
|
|
var sprite = new CSprite(EntityPosition) { SpriteShader = Resources.ColorShader, Color = _colors[color] };
|
|
var animationComponent = new AnimationComponent(_animator, sprite, Vector2.Zero);
|
|
|
|
_body = new BodyComponent(EntityPosition, -14, -20, 28, 20, 8)
|
|
{
|
|
IgnoreHoles = true,
|
|
FieldRectangle = Map.GetField(posX, posY, 8)
|
|
};
|
|
|
|
_aiComponent = new AiComponent();
|
|
|
|
var stateIdle = new AiState(UpdateIdle);
|
|
var stateWait = new AiState(UpdateWait) { Init = InitWait };
|
|
stateWait.Trigger.Add(new AiTriggerRandomTime(WalkOrRun, 1000, 1500));
|
|
var stateWalk = new AiState { Init = InitWalking };
|
|
stateWalk.Trigger.Add(new AiTriggerRandomTime(EndWalking, 750, 1250));
|
|
var statePreRun = new AiState(UpdatePreRun) { Init = InitPreRun };
|
|
statePreRun.Trigger.Add(new AiTriggerCountdown(750, null, () => _aiComponent.ChangeState("run")));
|
|
var stateRun = new AiState(UpdateRunning) { Init = InitRun };
|
|
stateRun.Trigger.Add(new AiTriggerRandomTime(EndRun, 500, 750));
|
|
var stateThrowLink = new AiState();
|
|
var stateThrowBomb = new AiState() { Init = InitThrowBomb };
|
|
stateThrowBomb.Trigger.Add(new AiTriggerCountdown(250, null, ThrowBomb));
|
|
var stateThrownBomb = new AiState(UpdateThrownBomb);
|
|
var stateGrab = new AiState { Init = InitGrab };
|
|
stateGrab.Trigger.Add(new AiTriggerCountdown(GrabTime, GrabTick, () => _aiComponent.ChangeState("grabbed")));
|
|
var stateGrabbed = new AiState();
|
|
stateGrabbed.Trigger.Add(new AiTriggerCountdown(500, null, () => _aiComponent.ChangeState("throw")));
|
|
var stateThrow = new AiState { Init = InitThrow };
|
|
stateThrow.Trigger.Add(new AiTriggerCountdown(600, null, () => _aiComponent.ChangeState("walk")));
|
|
|
|
_aiComponent.States.Add("idle", stateIdle);
|
|
_aiComponent.States.Add("wait", stateWait);
|
|
_aiComponent.States.Add("walk", stateWalk);
|
|
_aiComponent.States.Add("preRun", statePreRun);
|
|
_aiComponent.States.Add("run", stateRun);
|
|
_aiComponent.States.Add("throwLink", stateThrowLink);
|
|
_aiComponent.States.Add("throwBomb", stateThrowBomb);
|
|
_aiComponent.States.Add("thrownBomb", stateThrownBomb);
|
|
_aiComponent.States.Add("grab", stateGrab);
|
|
_aiComponent.States.Add("grabbed", stateGrabbed);
|
|
_aiComponent.States.Add("throw", stateThrow);
|
|
_aiDamageState = new AiDamageState(this, _body, _aiComponent, sprite, Lives, true, false)
|
|
{
|
|
BossHitSound = true
|
|
};
|
|
_aiDamageState.AddBossDamageState(OnDeath);
|
|
|
|
_aiComponent.ChangeState("idle");
|
|
|
|
_grabBox = new CBox(EntityPosition, -20, -20, 0, 40, 24, 8);
|
|
var damageCollider = new CBox(EntityPosition, -14, -24, 0, 28, 24, 8);
|
|
var hittableBox = new CBox(EntityPosition, -14, -28, 0, 28, 28, 8);
|
|
AddComponent(DamageFieldComponent.Index, _damageFieldComponent = new DamageFieldComponent(damageCollider, HitType.Enemy, 4));
|
|
AddComponent(PushableComponent.Index, new PushableComponent(_body.BodyBox, OnPush));
|
|
AddComponent(HittableComponent.Index, new HittableComponent(hittableBox, 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 DrawShadowCSpriteComponent(sprite));
|
|
}
|
|
|
|
private void UpdateIdle()
|
|
{
|
|
// player entered the room?
|
|
if (_body.FieldRectangle.Contains(MapManager.ObjLink.BodyRectangle))
|
|
{
|
|
// start boss music
|
|
Game1.GameManager.SetMusic(79, 2);
|
|
|
|
_aiComponent.ChangeState("walk");
|
|
}
|
|
}
|
|
|
|
private void InitGrab()
|
|
{
|
|
MapManager.ObjLink.Stun(2000);
|
|
MapManager.ObjLink.StartGrab();
|
|
|
|
Game1.GameManager.PlaySoundEffect("D370-22-16");
|
|
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
|
|
_damageFieldComponent.IsActive = false;
|
|
|
|
if (MapManager.ObjLink.PosX < EntityPosition.X)
|
|
_grabDirection = 1;
|
|
else
|
|
_grabDirection = -1;
|
|
|
|
_animator.Pause();
|
|
_animator.SetFrame(_grabDirection == 1 ? 0 : 1);
|
|
|
|
_grabStartPosition = MapManager.ObjLink.EntityPosition.ToVector3();
|
|
}
|
|
|
|
private void GrabTick(double counter)
|
|
{
|
|
var percentage = 1 - (float)(counter / GrabTime);
|
|
var grabEndPosition = new Vector3(EntityPosition.X + 16 * _grabDirection, EntityPosition.Y + 1, 26);
|
|
var newPosition = Vector3.Lerp(_grabStartPosition, grabEndPosition, percentage);
|
|
MapManager.ObjLink.EntityPosition.Set(newPosition);
|
|
}
|
|
|
|
private void InitThrow()
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-08-08");
|
|
|
|
// set the position to be inside of the hinox body to not start throwing the player into a collider
|
|
var grabEndPosition = new Vector3(EntityPosition.X + 16 * _grabDirection, EntityPosition.Y, 25);
|
|
MapManager.ObjLink.EntityPosition.Set(grabEndPosition);
|
|
MapManager.ObjLink.EndGrab();
|
|
MapManager.ObjLink.StartThrow(new Vector3(-4.5f * _grabDirection, 2.5f, 0));
|
|
Game1.GameManager.InflictDamage(4);
|
|
|
|
_damageFieldComponent.IsActive = true;
|
|
_animator.SetFrame(_grabDirection == 1 ? 1 : 0);
|
|
}
|
|
|
|
private void ContinueAnimation()
|
|
{
|
|
_animator.Continue();
|
|
_animator.SpeedMultiplier = 1.0f;
|
|
|
|
// set the animation to the next frame
|
|
_animator.ResetFrameCounter();
|
|
_animator.SetFrame((_animator.CurrentFrameIndex + 1) % _animator.CurrentAnimation.Frames.Length);
|
|
}
|
|
|
|
private void InitWait()
|
|
{
|
|
_animator.Pause();
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
}
|
|
|
|
private void UpdateWait()
|
|
{
|
|
// player left the room?
|
|
if (!_body.FieldRectangle.Contains(MapManager.ObjLink.BodyRectangle))
|
|
{
|
|
// stop boss music
|
|
Game1.GameManager.SetMusic(-1, 2);
|
|
_aiComponent.ChangeState("idle");
|
|
}
|
|
}
|
|
|
|
private void WalkOrRun()
|
|
{
|
|
var random = Game1.RandomNumber.Next(0, 2);
|
|
_aiComponent.ChangeState(random == 0 ? "preRun" : "walk");
|
|
_animator.Play("idle_0");
|
|
}
|
|
|
|
private void InitWalking()
|
|
{
|
|
ContinueAnimation();
|
|
|
|
// walk into a random direction
|
|
var direction = Game1.RandomNumber.Next(0, 4);
|
|
_body.VelocityTarget = AnimationHelper.DirectionOffset[direction] * 0.5f;
|
|
}
|
|
|
|
private void EndWalking()
|
|
{
|
|
_aiComponent.ChangeState("wait");
|
|
}
|
|
|
|
private void InitPreRun()
|
|
{
|
|
_animator.Continue();
|
|
_animator.SpeedMultiplier = 2.0f;
|
|
}
|
|
|
|
private void UpdatePreRun()
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-32-20", false);
|
|
}
|
|
|
|
private void InitRun()
|
|
{
|
|
// run towards the player
|
|
var direction = MapManager.ObjLink.EntityPosition.Position - EntityPosition.Position;
|
|
if (direction != Vector2.Zero)
|
|
direction.Normalize();
|
|
_body.VelocityTarget = direction * 1.5f;
|
|
}
|
|
|
|
private void UpdateRunning()
|
|
{
|
|
// grab the player
|
|
if (Game1.GameManager.CurrentHealth > 0 &&
|
|
_grabBox.Box.Rectangle().Intersects(MapManager.ObjLink.BodyRectangle))
|
|
_aiComponent.ChangeState("grab");
|
|
|
|
// spawn run particle
|
|
_runParticleCount -= Game1.DeltaTime;
|
|
if (_runParticleCount <= 0)
|
|
{
|
|
_runParticleCount = 133;
|
|
|
|
var animator = new ObjAnimator(Map,
|
|
(int)EntityPosition.X, (int)(EntityPosition.Y + 1),
|
|
0, -1 - (int)EntityPosition.Z, Values.LayerPlayer, "Particles/run", "spawn", true);
|
|
Map.Objects.SpawnObject(animator);
|
|
}
|
|
}
|
|
|
|
private void EndRun()
|
|
{
|
|
_aiComponent.ChangeState("wait");
|
|
}
|
|
|
|
private void InitThrowBomb()
|
|
{
|
|
_animator.Pause();
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
}
|
|
|
|
private void ThrowBomb()
|
|
{
|
|
var handOffset = _animator.CurrentFrameIndex == 0 ? 8 : -8;
|
|
var spawnPosition = new Vector2(EntityPosition.X + handOffset, EntityPosition.Y);
|
|
var throwDirection = MapManager.ObjLink.EntityPosition.Position - spawnPosition;
|
|
var maxRange = 48f;
|
|
var mult = 1.5f;
|
|
if (throwDirection.Length() > maxRange)
|
|
{
|
|
throwDirection.Normalize();
|
|
throwDirection *= mult;
|
|
}
|
|
else
|
|
throwDirection = (throwDirection / maxRange) * mult;
|
|
|
|
// spawn a bomb
|
|
var bomb = new ObjBomb(Map, 0, 0, false, true);
|
|
bomb.EntityPosition.Set(new Vector3(spawnPosition.X, spawnPosition.Y, 20));
|
|
bomb.Body.Velocity = new Vector3(throwDirection.X, throwDirection.Y, 1);
|
|
bomb.Body.Gravity = -0.2f;
|
|
bomb.Body.Bounciness = 0.25f;
|
|
bomb.Body.DragAir = 1.0f;
|
|
Map.Objects.SpawnObject(bomb);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-08-08");
|
|
|
|
// play throw animation
|
|
_animator.Play("throw_" + _animator.CurrentFrameIndex);
|
|
|
|
_aiComponent.ChangeState("thrownBomb");
|
|
}
|
|
|
|
private void UpdateThrownBomb()
|
|
{
|
|
// finished throw animation?
|
|
if (!_animator.IsPlaying)
|
|
WalkOrRun();
|
|
}
|
|
|
|
private void OnDeath()
|
|
{
|
|
if (!string.IsNullOrEmpty(_saveKey))
|
|
Game1.GameManager.SaveManager.SetString(_saveKey, "1");
|
|
|
|
// stop the music
|
|
Game1.GameManager.SetMusic(-1, 2);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D378-26-1A");
|
|
|
|
// 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)
|
|
{
|
|
if (type == PushableComponent.PushType.Impact)
|
|
_body.Velocity = new Vector3(direction.X, direction.Y, _body.Velocity.Z);
|
|
|
|
return true;
|
|
}
|
|
|
|
public Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
|
|
{
|
|
if (_aiComponent.CurrentStateId == "idle")
|
|
return Values.HitCollision.None;
|
|
|
|
if (_aiComponent.CurrentStateId == "throwBomb")
|
|
ThrowBomb();
|
|
|
|
// the boss will throw a bomb right after getting damaged
|
|
if (!_aiDamageState.IsInDamageState() &&
|
|
_aiComponent.CurrentStateId != "deathBoss" &&
|
|
_aiComponent.CurrentStateId != "preRun" &&
|
|
_aiComponent.CurrentStateId != "run")
|
|
_aiComponent.ChangeState("throwBomb");
|
|
|
|
if (damageType == HitType.Bow || damageType == HitType.Bomb || damageType == HitType.MagicRod)
|
|
damage *= 2;
|
|
|
|
if (damageType == HitType.Boomerang)
|
|
damage = 2;
|
|
|
|
var hitCollision = _aiDamageState.OnHit(gameObject, direction, damageType, damage, pieceOfPower);
|
|
|
|
// stop walking and stop the animation when dead
|
|
if (_aiDamageState.CurrentLives <= 0)
|
|
{
|
|
RemoveComponent(HittableComponent.Index);
|
|
|
|
_animator.Stop();
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
|
|
// make sure to let the player go if he was grabbed
|
|
MapManager.ObjLink.EndGrab();
|
|
}
|
|
|
|
return hitCollision;
|
|
}
|
|
}
|
|
}
|