mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
399 lines
15 KiB
C#
399 lines
15 KiB
C#
using System;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
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.Things;
|
|
|
|
namespace ProjectZ.InGame.GameObjects.Bosses
|
|
{
|
|
class BossMoldorm : GameObject
|
|
{
|
|
private BossMoldormTail _tail;
|
|
|
|
private BodyDrawComponent _bodyDrawComponent;
|
|
private BodyComponent _body;
|
|
private AiComponent _aiComponent;
|
|
private CSprite _sprite;
|
|
|
|
private Rectangle _headSourceRectangle = new Rectangle(2, 4, 28, 24);
|
|
private Rectangle _headSourceRectangleDamage = new Rectangle(34, 4, 28, 24);
|
|
|
|
private Rectangle[] _tailRectangles = {
|
|
new Rectangle(8, 40, 16, 16), new Rectangle(8, 40, 16, 16), new Rectangle(41, 41, 14, 14)
|
|
};
|
|
private Rectangle[] _tailRectanglesDamage = {
|
|
new Rectangle(8, 72, 16, 16), new Rectangle(8, 72, 16, 16), new Rectangle(41, 73, 14, 14)
|
|
};
|
|
|
|
private Vector2[] _tailPositions = new Vector2[4];
|
|
private float[] _tailDistance = { 6.5f, 4.5f, 4.5f, 4.5f };
|
|
|
|
private Vector2[] _savedPosition = new Vector2[20];
|
|
|
|
private string _saveKey;
|
|
private string _triggerKey;
|
|
|
|
private float _saveInterval = 33;
|
|
private float _saveCounter;
|
|
private int _saveIndex;
|
|
|
|
private float _directionChangeMultiplier;
|
|
private float _direction;
|
|
private float _runCounter;
|
|
private int _changeDirCount;
|
|
private int _dir = 1;
|
|
private int _lives = 4;
|
|
|
|
private int _dyingState = 4;
|
|
private int _tailState = 2;
|
|
private float _dyingCounter = 250;
|
|
|
|
private bool _blinking;
|
|
|
|
public BossMoldorm() : base("moldorm") { }
|
|
|
|
public BossMoldorm(Map.Map map, int posX, int posY, string saveKey, string triggerKey) : base(map)
|
|
{
|
|
if (!string.IsNullOrEmpty(saveKey) &&
|
|
Game1.GameManager.SaveManager.GetString(saveKey) == "1")
|
|
{
|
|
IsDead = true;
|
|
return;
|
|
}
|
|
|
|
Tags = Values.GameObjectTag.Enemy;
|
|
|
|
EntityPosition = new CPosition(posX + 16, posY + 16, 0);
|
|
EntitySize = new Rectangle(-24, -24, 48, 48);
|
|
|
|
for (var i = 0; i < 4; i++)
|
|
_tailPositions[i] = EntityPosition.Position;
|
|
for (var i = 0; i < _savedPosition.Length; i++)
|
|
_savedPosition[i] = EntityPosition.Position;
|
|
|
|
_saveKey = saveKey;
|
|
_triggerKey = triggerKey;
|
|
|
|
_sprite = new CSprite(EntityPosition);
|
|
_sprite.SprTexture = Resources.SprNightmares;
|
|
_sprite.SourceRectangle = _headSourceRectangle;
|
|
_sprite.Center = new Vector2(16, 12);
|
|
|
|
_direction = (float)Math.PI * 2;
|
|
_sprite.Rotation = -_direction - (float)Math.PI / 2;
|
|
|
|
_body = new BodyComponent(EntityPosition, -7, -7, 14, 14, 8)
|
|
{
|
|
MoveCollision = OnCollision,
|
|
AbsorbPercentage = 1f,
|
|
Gravity = -0.1f,
|
|
DragAir = 1.0f,
|
|
Drag = 0.925f,
|
|
CollisionTypes =
|
|
Values.CollisionTypes.Normal |
|
|
Values.CollisionTypes.Hole,
|
|
FieldRectangle = map.GetField(posX, posY)
|
|
};
|
|
|
|
_aiComponent = new AiComponent();
|
|
|
|
var stateWaiting = new AiState();
|
|
var stateMoving = new AiState(UpdateMoving);
|
|
var stateRunning = new AiState(UpdateRunning);
|
|
var stateDamage = new AiState(UpdateDamage);
|
|
stateDamage.Trigger.Add(new AiTriggerCountdown(500, UpdateDamageTick, ToRunning));
|
|
var stateDying = new AiState(UpdateDying);
|
|
|
|
_aiComponent.States.Add("waiting", stateWaiting);
|
|
_aiComponent.States.Add("moving", stateMoving);
|
|
_aiComponent.States.Add("running", stateRunning);
|
|
_aiComponent.States.Add("damage", stateDamage);
|
|
_aiComponent.States.Add("dying", stateDying);
|
|
|
|
_aiComponent.ChangeState("waiting");
|
|
|
|
_bodyDrawComponent = new BodyDrawComponent(_body, _sprite, 1);
|
|
var damageCollider = new CBox(EntityPosition, -6, -6, 0, 12, 12, 8);
|
|
|
|
AddComponent(KeyChangeListenerComponent.Index, new KeyChangeListenerComponent(OnKeyChang));
|
|
AddComponent(AiComponent.Index, _aiComponent);
|
|
AddComponent(BodyComponent.Index, _body);
|
|
AddComponent(PushableComponent.Index, new PushableComponent(_body.BodyBox, OnPush) { RepelMultiplier = 2.0f });
|
|
AddComponent(HittableComponent.Index, new HittableComponent(damageCollider, OnHit));
|
|
AddComponent(DrawComponent.Index, new DrawComponent(Draw, Values.LayerPlayer, EntityPosition));
|
|
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageCollider, HitType.Enemy, 4));
|
|
|
|
// add the tail to the map
|
|
_tail = new BossMoldormTail(map, this);
|
|
map.Objects.SpawnObject(_tail);
|
|
}
|
|
|
|
private void ToWalking()
|
|
{
|
|
_aiComponent.ChangeState("moving");
|
|
_sprite.SourceRectangle = _headSourceRectangle;
|
|
_blinking = false;
|
|
}
|
|
|
|
private void UpdateMoving()
|
|
{
|
|
Move(1);
|
|
UpdateTailPositions(1);
|
|
}
|
|
|
|
private void ToRunning()
|
|
{
|
|
_aiComponent.ChangeState("running");
|
|
_sprite.SourceRectangle = _headSourceRectangle;
|
|
_blinking = false;
|
|
}
|
|
|
|
private void UpdateRunning()
|
|
{
|
|
// dont stop running with one live left
|
|
if (_lives > 1)
|
|
{
|
|
_runCounter -= Game1.DeltaTime;
|
|
if (_runCounter <= 0)
|
|
_aiComponent.ChangeState("moving");
|
|
}
|
|
|
|
Move(1.75f);
|
|
UpdateTailPositions(1.75f);
|
|
}
|
|
|
|
private void Move(float speedMultiplier)
|
|
{
|
|
_changeDirCount -= (int)(Game1.DeltaTime * speedMultiplier);
|
|
if (_changeDirCount < 0)
|
|
ChangeDirection();
|
|
|
|
_direction += _dir * 0.05f * Game1.TimeMultiplier * speedMultiplier;
|
|
|
|
_sprite.Rotation = -_direction - (float)Math.PI / 2;
|
|
|
|
// move
|
|
var vecDirection = new Vector2((float)Math.Sin(_direction), (float)Math.Cos(_direction));
|
|
_body.VelocityTarget = vecDirection * 1.0f * speedMultiplier;
|
|
|
|
_directionChangeMultiplier = AnimationHelper.MoveToTarget(_directionChangeMultiplier, 1, 0.1f * Game1.TimeMultiplier);
|
|
}
|
|
|
|
private void UpdateDamage()
|
|
{
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
UpdateTailPositions(1.5f);
|
|
}
|
|
|
|
private void UpdateDamageTick(double time)
|
|
{
|
|
_blinking = (time % 150) >= 75;
|
|
|
|
_direction += _dir * (float)Math.Sin(((time + 150) / 800f) * Math.PI) * 0.1f * Game1.TimeMultiplier;
|
|
_sprite.Rotation = -_direction - (float)Math.PI / 2;
|
|
_sprite.SourceRectangle = _blinking ? _headSourceRectangleDamage : _headSourceRectangle;
|
|
}
|
|
|
|
private void ToDying()
|
|
{
|
|
_body.Velocity = new Vector3(_body.VelocityTarget.X, _body.VelocityTarget.Y, _body.Velocity.Z);
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
|
|
_aiComponent.ChangeState("dying");
|
|
Game1.GameManager.PlaySoundEffect("D370-16-10");
|
|
}
|
|
|
|
private void UpdateDying()
|
|
{
|
|
UpdateTailPositions(_body.Velocity.Length());
|
|
|
|
_dyingCounter -= Game1.DeltaTime;
|
|
|
|
if (_dyingCounter <= 0)
|
|
{
|
|
_dyingCounter = 250;
|
|
_dyingState--;
|
|
|
|
if (_tail != null)
|
|
{
|
|
Map.Objects.DeleteObjects.Add(_tail);
|
|
_tail = null;
|
|
}
|
|
else
|
|
_tailState--;
|
|
|
|
if (_dyingState < -10)
|
|
{
|
|
Map.Objects.DeleteObjects.Add(this);
|
|
|
|
// stop boss music
|
|
Game1.GameManager.SetMusic(-1, 2);
|
|
|
|
// set the save key
|
|
Game1.GameManager.SaveManager.SetString(_saveKey, "1");
|
|
|
|
// spawn big heart
|
|
Map.Objects.SpawnObject(new ObjItem(Map, (int)EntityPosition.X - 8, (int)EntityPosition.Y - 8, "j", "d1_nHeart", "heartMeterFull", null));
|
|
|
|
Game1.GameManager.PlaySoundEffect("D378-26-1A");
|
|
}
|
|
else if (_dyingState < 0)
|
|
{
|
|
_dyingCounter = 50;
|
|
Game1.GameManager.PlaySoundEffect("D378-19-13");
|
|
|
|
// spawn explosion effect arount the head
|
|
var position = new Point((int)(Math.Sin(-_dyingState / 1.5f) * 8), (int)(Math.Cos(-_dyingState / 1.5f) * 8));
|
|
Map.Objects.SpawnObject(new ObjAnimator(Map,
|
|
(int)EntityPosition.X - 8 + position.X,
|
|
(int)EntityPosition.Y - 8 + position.Y, Values.LayerTop, "Particles/spawn", "run", true));
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D378-19-13");
|
|
|
|
// spawn explosion at the tail
|
|
Map.Objects.SpawnObject(new ObjAnimator(Map,
|
|
(int)_tailPositions[_dyingState].X - 8,
|
|
(int)_tailPositions[_dyingState].Y - 8, Values.LayerTop, "Particles/spawn", "run", true));
|
|
}
|
|
}
|
|
}
|
|
|
|
private void Draw(SpriteBatch spriteBatch)
|
|
{
|
|
// draw the tail
|
|
var tailRectangles = _blinking ? _tailRectanglesDamage : _tailRectangles;
|
|
for (var i = _tailState; i >= 0; i--)
|
|
{
|
|
spriteBatch.Draw(Resources.SprNightmares, _tailPositions[i] -
|
|
new Vector2(tailRectangles[i].Width / 2f, tailRectangles[i].Height / 2f), tailRectangles[i], Color.White);
|
|
}
|
|
|
|
// draw the head
|
|
_bodyDrawComponent.Draw(spriteBatch);
|
|
}
|
|
|
|
public Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
|
|
{
|
|
return Values.HitCollision.RepellingParticle;
|
|
}
|
|
|
|
public Values.HitCollision OnHitTail(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
|
|
{
|
|
if (_aiComponent.CurrentStateId == "damage" || _aiComponent.CurrentStateId == "dying")
|
|
return Values.HitCollision.None;
|
|
|
|
// can only be damaged with the sword
|
|
if ((damageType & HitType.Sword) == 0)
|
|
return Values.HitCollision.None;
|
|
|
|
_lives -= damage;
|
|
_runCounter = 2000;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D370-07-07");
|
|
|
|
if (_lives > 0)
|
|
_aiComponent.ChangeState("damage");
|
|
else
|
|
ToDying();
|
|
|
|
return Values.HitCollision.Enemy;
|
|
}
|
|
|
|
private void UpdateTailPositions(float speedMultiplier)
|
|
{
|
|
SavePosition(speedMultiplier);
|
|
|
|
var indexCount = _saveIndex + (_saveCounter / _saveInterval);
|
|
var timeDiff = _saveCounter + _saveInterval * 1000;
|
|
|
|
for (var i = 0; i < _tailPositions.Length; i++)
|
|
{
|
|
indexCount -= _tailDistance[i];
|
|
if (indexCount < 0)
|
|
indexCount += _savedPosition.Length;
|
|
|
|
timeDiff -= _tailDistance[i] * _saveInterval;
|
|
var index = (int)indexCount;
|
|
|
|
_tailPositions[i] = Vector2.Lerp(
|
|
_savedPosition[index], _savedPosition[(index + 1) % _savedPosition.Length],
|
|
(timeDiff % _saveInterval) / _saveInterval);
|
|
}
|
|
|
|
// set the position of the tail
|
|
_tail?.EntityPosition.Set(_tailPositions[_tailPositions.Length - 1]);
|
|
}
|
|
|
|
private void SavePosition(float speedMultiplier)
|
|
{
|
|
var position = EntityPosition.Position + _body.VelocityTarget * (Game1.DeltaTime / 16.6667f) * speedMultiplier;
|
|
_saveCounter += Game1.DeltaTime * speedMultiplier;
|
|
var diff = _saveCounter % _saveInterval;
|
|
|
|
var updateSteps = (int)(_saveCounter / _saveInterval);
|
|
_saveIndex = (_saveIndex + updateSteps) % _savedPosition.Length;
|
|
var index = _saveIndex;
|
|
|
|
var currentDirection = _direction;
|
|
|
|
while (_saveCounter >= _saveInterval)
|
|
{
|
|
_saveCounter -= _saveInterval;
|
|
|
|
index--;
|
|
if (index < 0)
|
|
index = _savedPosition.Length - 1;
|
|
|
|
var vecDir = new Vector2((float)Math.Sin(currentDirection), (float)Math.Cos(currentDirection));
|
|
_savedPosition[index] = position - vecDir * (diff / 16.6667f);
|
|
|
|
position = _savedPosition[index];
|
|
diff = _saveInterval;
|
|
currentDirection -= _dir * 0.025f * (_saveInterval / 16.6667f);
|
|
}
|
|
}
|
|
|
|
private void OnCollision(Values.BodyCollision collision)
|
|
{
|
|
if (Game1.RandomNumber.Next(0, 2) == 0)
|
|
_dir = -_dir;
|
|
|
|
if ((collision & Values.BodyCollision.Horizontal) != 0)
|
|
_direction = (float)Math.Atan2(-_body.VelocityTarget.X * _directionChangeMultiplier, _body.VelocityTarget.Y);
|
|
else if ((collision & Values.BodyCollision.Vertical) != 0)
|
|
_direction = (float)Math.Atan2(_body.VelocityTarget.X, -_body.VelocityTarget.Y * _directionChangeMultiplier);
|
|
|
|
_directionChangeMultiplier *= 0.75f;
|
|
}
|
|
|
|
private bool OnPush(Vector2 direction, PushableComponent.PushType type)
|
|
{
|
|
if (type == PushableComponent.PushType.Impact)
|
|
_body.Velocity = new Vector3(direction.X * 0.25f, direction.Y * 0.25f, _body.Velocity.Z);
|
|
|
|
return true;
|
|
}
|
|
|
|
private void ChangeDirection()
|
|
{
|
|
_changeDirCount = Game1.RandomNumber.Next(500, 2500);
|
|
_dir = -_dir;
|
|
}
|
|
|
|
private void OnKeyChang()
|
|
{
|
|
// activate the boss if the trigger key was set
|
|
if (_aiComponent.CurrentStateId == "waiting" &&
|
|
!string.IsNullOrEmpty(_triggerKey) && Game1.GameManager.SaveManager.GetString(_triggerKey) == "1")
|
|
{
|
|
_aiComponent.ChangeState("moving");
|
|
}
|
|
}
|
|
}
|
|
} |