mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
306 lines
11 KiB
C#
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;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|