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