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

288 lines
10 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.Things;
using ProjectZ.InGame.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.MidBoss
{
internal class MBossBallAndChainSoldier : GameObject
{
private readonly Animator _animator;
private readonly BodyComponent _body;
private readonly AnimationComponent _animatorComponent;
private readonly CSprite _sprite;
private readonly AiComponent _ai;
private readonly MBossBallAndChain _ballAndChain;
private readonly AiTriggerTimer _walkTimer;
private readonly AiDamageState _damageState;
private readonly Rectangle _fieldRectangle;
private const string _leafSaveKey = "ow_goldLeafBalls";
private string _strKey;
private float _currentBallSpeed = 300;
private const float BallDistance = 10;
private const float BallDistanceThrow = 56;
private float _ballState;
private float _throwDirection;
private int _ballCounter;
private bool _startThrowing;
private bool _isThrowing;
private float _ballRadiant;
private float _distance;
private bool _wasBlocked;
public MBossBallAndChainSoldier() : base("ballAndChain") { }
public MBossBallAndChainSoldier(Map.Map map, int posX, int posY, string strKey) : base(map)
{
EntityPosition = new CPosition(posX + 8, posY + 16, 0);
EntitySize = new Rectangle(-8, -16, 16, 16);
_strKey = strKey;
_fieldRectangle = map.GetField(posX, posY, 16);
_fieldRectangle.X += 16;
// was already defeated?
if (!string.IsNullOrEmpty(_strKey) && Game1.GameManager.SaveManager.GetString(_strKey) == "1")
{
IsDead = true;
// spawn the leaf if is was not already collected
var objLeaf = new ObjItem(Map, posX, posY, null, _leafSaveKey, "goldLeaf", null);
if (!objLeaf.IsDead)
Map.Objects.SpawnObject(objLeaf);
return;
}
_animator = AnimatorSaveLoad.LoadAnimator("MidBoss/ball and chain soldier");
_animator.Play("swing1");
_sprite = new CSprite(EntityPosition);
_animatorComponent = new AnimationComponent(_animator, _sprite, new Vector2(-8, -16));
_body = new BodyComponent(EntityPosition, -7, -12, 14, 12, 8);
_ai = new AiComponent();
var stateWalk = new AiState(UpdateWalk);
stateWalk.Trigger.Add(_walkTimer = new AiTriggerTimer(500));
var stateSwing = new AiState(UpdateSwing);
var stateThrow = new AiState(UpdateThrow);
_ai.States.Add("walk", stateWalk);
_ai.States.Add("swing", stateSwing);
_ai.States.Add("throw", stateThrow);
new AiFallState(_ai, _body, null, KillBoss, 500);
_ai.Trigger.Add(new AiTriggerUpdate(UpdateBall));
_ai.ChangeState("walk");
var damageCollider = new CBox(EntityPosition, -8, -16, 0, 16, 16, 8);
AddComponent(AnimationComponent.Index, _animatorComponent);
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageCollider, HitType.Enemy, 4));
_damageState = new AiDamageState(this, _body, _ai, _sprite, 8, false)
{
OnDeath = OnDeath
};
AddComponent(HittableComponent.Index, new HittableComponent(_body.BodyBox, _damageState.OnHit));
AddComponent(AiComponent.Index, _ai);
AddComponent(BodyComponent.Index, _body);
AddComponent(DrawComponent.Index, new BodyDrawComponent(_body, _sprite, Values.LayerPlayer));
AddComponent(DrawShadowComponent.Index, new BodyDrawShadowComponent(_body, _sprite));
_ballAndChain = new MBossBallAndChain(map, this);
Map.Objects.SpawnObject(_ballAndChain);
}
private void OnDeath(bool pieceOfPower)
{
KillBoss();
_damageState.BaseOnDeath(pieceOfPower);
}
private void KillBoss()
{
var playerDirection = MapManager.ObjLink.EntityPosition.Position - EntityPosition.Position;
if (playerDirection != Vector2.Zero)
playerDirection.Normalize();
playerDirection *= 1.75f;
// spawn the golden leaf jumping towards the player
var objLeaf = new ObjItem(Map, 0, 0, null, _leafSaveKey, "goldLeaf", null);
if (!objLeaf.IsDead)
{
objLeaf.EntityPosition.Set(new Vector3(EntityPosition.X, EntityPosition.Y, EntityPosition.Z));
objLeaf.SetVelocity(new Vector3(playerDirection.X, playerDirection.Y, 1.0f));
objLeaf.Collectable = false;
Map.Objects.SpawnObject(objLeaf);
}
// save
if (!string.IsNullOrEmpty(_strKey))
Game1.GameManager.SaveManager.SetString(_strKey, "1");
Map.Objects.DeleteObjects.Add(_ballAndChain);
}
private void ToWalk()
{
_ai.ChangeState("walk");
_startThrowing = false;
_isThrowing = false;
_ballAndChain.Deactivate();
}
private void UpdateWalk()
{
if (!_walkTimer.State)
return;
var playerDirection = MapManager.ObjLink.EntityPosition.Position - EntityPosition.Position;
if (playerDirection.Length() < 48)
{
ToSwing();
return;
}
// do not walk towards the player if he is not in the field
if (!_fieldRectangle.Contains(MapManager.ObjLink.EntityPosition.Position))
return;
// walk towards the player
playerDirection.Normalize();
_body.VelocityTarget = playerDirection / 4;
}
private void ToSwing()
{
_ai.ChangeState("swing");
_body.VelocityTarget = Vector2.Zero;
_ballCounter = 0;
}
private void UpdateSwing()
{
}
private void ToThrow()
{
_ai.ChangeState("throw");
_isThrowing = true;
_wasBlocked = false;
_ballAndChain.Activate();
}
private void UpdateThrow()
{
}
private void UpdateBall()
{
Vector2 ballOffset;
if (_isThrowing)
{
if (_wasBlocked)
_ballState += (Game1.DeltaTime / 500.0f) * MathF.PI;
else
_ballState += (Game1.DeltaTime / 1000.0f) * MathF.PI;
// finished animation?
if (_ballState > _throwDirection + MathF.PI)
{
ToWalk();
}
}
else
{
var isSwinging = _ai.CurrentStateId == "swing";
var target = isSwinging ? 300.0f : 500.0f;
_currentBallSpeed = AnimationHelper.MoveToTarget(_currentBallSpeed, target, target * 0.1f * Game1.TimeMultiplier);
Game1.DebugText += "\nball: " + (int)_currentBallSpeed;
// 2 times per second or 4 if he is swinging fast
_ballState += Game1.DeltaTime / _currentBallSpeed * MathF.PI * 2;
if (_startThrowing && _ballState >= _throwDirection)
{
_currentBallSpeed = 500.0f;
ToThrow();
_ballState = _throwDirection + ((_ballState - _throwDirection) / 1000.0f) * MathF.PI;
}
else if (_ballState >= MathF.PI * 2)
{
_ballState -= MathF.PI * 2;
if (isSwinging)
{
_ballCounter++;
if (_ballCounter >= 2)
{
_startThrowing = true;
var playerDirection = MapManager.ObjLink.BodyRectangle.Center -
new Vector2(EntityPosition.X - 5, EntityPosition.Y - 15);
var playerAngle = MathF.Atan2(playerDirection.Y, playerDirection.X) + MathF.PI * 5 / 2;
if (playerAngle >= MathF.PI * 2)
playerAngle -= MathF.PI * 2;
_throwDirection = playerAngle;
}
}
}
}
_animator.Play("swing" + (_ballState < MathF.PI ? "0" : "1"));
// calculate the ball offset
if (!_isThrowing)
{
ballOffset = new Vector2(-MathF.Cos(_ballState), -MathF.Sin(_ballState)) * BallDistance;
}
else
{
if (_wasBlocked)
{
if (BallDistance < _distance - 2.5f * Game1.TimeMultiplier)
_distance -= 2.5f * Game1.TimeMultiplier;
else
_distance = BallDistance;
ballOffset = new Vector2(-MathF.Cos(_ballState), -MathF.Sin(_ballState)) * _distance;
}
else
{
var throwState = MathF.Sin(_ballState - _throwDirection);
_distance = MathHelper.Lerp(BallDistance, BallDistanceThrow, throwState);
var tan = MathF.Tan((((_ballState - _throwDirection) / MathF.PI) * 2 - 1) * MathF.Atan(10)) / 10;
_ballRadiant = _throwDirection + (tan * 0.5f + 0.5f) * MathF.PI;
ballOffset = new Vector2(-MathF.Cos(_ballRadiant), -MathF.Sin(_ballRadiant)) * _distance;
}
}
_ballAndChain.EntityPosition.Set(new Vector2(EntityPosition.X - 5, EntityPosition.Y - 7) + ballOffset);
}
public void BlockBall()
{
_wasBlocked = true;
_ballState = _ballRadiant;
}
}
}