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

202 lines
7.2 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.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.Things
{
class ObjArrow : GameObject
{
private readonly Animator _animator;
private readonly AiComponent _aiComponent;
private readonly ShadowBodyDrawComponent _shadowBody;
private readonly CSprite _sprite;
private readonly BodyComponent _body;
private readonly BodyDrawComponent _drawComponent;
private readonly CBox _damageBox;
private const int DespawnTime = 375;
private const int FadeOutTime = 75;
private float _despawnPercentage = 1;
private int _dir;
private bool _isFalling;
private Vector2[] _bombOffset = new Vector2[] { new Vector2(-4, 0), new Vector2(0, -4), new Vector2(4, 0), new Vector2(0, 6) };
private ObjBomb _objBomb;
private bool _bombMode;
private Vector2 _startPosition;
private Point[] _collisionBoxSize = { new Point(2, 2), new Point(2, 2), new Point(2, 2), new Point(2, 2) };
public ObjArrow(Map.Map map, Vector3 position, int dir, float speed) : base(map)
{
EntityPosition = new CPosition(position.X, position.Y, position.Z);
EntitySize = new Rectangle(-8, -12, 16, 16);
_startPosition = new Vector2(position.X, position.Y);
_dir = dir;
var velocity = AnimationHelper.DirectionOffset[_dir] * speed;
_animator = AnimatorSaveLoad.LoadAnimator("Objects/spear");
_animator.Play(dir.ToString());
_sprite = new CSprite(EntityPosition);
var animationComponent = new AnimationComponent(_animator, _sprite, Vector2.Zero);
_body = new BodyComponent(EntityPosition,
-_collisionBoxSize[dir].X / 2, -_collisionBoxSize[dir].Y / 2,
_collisionBoxSize[dir].X, _collisionBoxSize[dir].Y, 8)
{
CollisionTypes = Values.CollisionTypes.Normal,
CollisionTypesIgnore = Values.CollisionTypes.ThrowWeaponIgnore,
MoveCollision = OnCollision,
VelocityTarget = velocity,
Bounciness = 0.35f,
Drag = 0.75f,
DragAir = 0.95f,
Gravity = -0.025f,
IgnoreHeight = true,
IgnoresZ = true,
IgnoreInsideCollision = false,
Level = MapStates.GetLevel(MapManager.ObjLink._body.CurrentFieldState)
};
_damageBox = new CBox(EntityPosition,
-_collisionBoxSize[dir].X / 2 - 1, -_collisionBoxSize[dir].Y - 1, 0,
_collisionBoxSize[dir].X + 2, _collisionBoxSize[dir].Y + 2, 8);
var stateIdle = new AiState(UpdateIdle);
var stateDespawn = new AiState() { Init = InitDespawn };
stateDespawn.Trigger.Add(new AiTriggerCountdown(DespawnTime, TickDespawn, () => TickDespawn(0)));
_aiComponent = new AiComponent();
_aiComponent.States.Add("idle", stateIdle);
_aiComponent.States.Add("despawn", stateDespawn);
_aiComponent.ChangeState("idle");
_drawComponent = new BodyDrawComponent(_body, _sprite, Values.LayerPlayer);
AddComponent(BodyComponent.Index, _body);
AddComponent(AiComponent.Index, _aiComponent);
AddComponent(BaseAnimationComponent.Index, animationComponent);
AddComponent(DrawComponent.Index, new DrawComponent(Draw, Values.LayerPlayer, EntityPosition));
AddComponent(DrawShadowComponent.Index, _shadowBody = new ShadowBodyDrawComponent(EntityPosition));
}
private void UpdateIdle()
{
var distance = _startPosition - EntityPosition.Position;
if (Math.Abs(distance.X) > 112 || Math.Abs(distance.Y) > 96)
{
_isFalling = true;
_body.IgnoresZ = false;
ExplodeBomb();
}
DealDamage();
}
private void ExplodeBomb()
{
if (!_bombMode)
return;
_bombMode = false;
_objBomb.Explode();
Map.Objects.DeleteObjects.Add(this);
}
private void InitDespawn()
{
_body.Gravity = -0.1f;
_body.IgnoresZ = false;
if (!_isFalling)
_body.Velocity = new Vector3(-_body.VelocityTarget.X * 0.35f, -_body.VelocityTarget.Y * 0.35f, 1f);
else
_body.Velocity = new Vector3(_body.VelocityTarget.X * 0.35f, _body.VelocityTarget.Y * 0.35f, 1f);
_body.VelocityTarget = Vector2.Zero;
_animator.Play(_dir == 2 ? "rotatel" : "rotate");
_animator.SetFrame((_dir + 1) % 4);
Game1.GameManager.PlaySoundEffect("D360-07-07");
}
private void TickDespawn(double time)
{
if (_animator.CurrentFrameIndex == (_dir + 2) % 4)
_animator.Pause();
_despawnPercentage = (float)(time / FadeOutTime);
if (_despawnPercentage > 1)
_despawnPercentage = 1;
_sprite.Color = Color.White * _despawnPercentage;
_shadowBody.Transparency = _despawnPercentage;
if (time <= 0)
Map.Objects.DeleteObjects.Add(this);
}
private void OnCollision(Values.BodyCollision collision)
{
if (_aiComponent.CurrentStateId == "despawn")
return;
_aiComponent.ChangeState("despawn");
// make sure the deal damage one last time
DealDamage();
ExplodeBomb();
}
private void DealDamage()
{
var collision = Map.Objects.Hit(this, EntityPosition.Position, _damageBox.Box, HitType.Bow, 2, false, false);
if ((collision & (Values.HitCollision.Blocking | Values.HitCollision.Enemy)) != 0)
{
Map.Objects.DeleteObjects.Add(this);
ExplodeBomb();
}
if ((collision & Values.HitCollision.Repelling) != 0)
{
_aiComponent.ChangeState("despawn");
ExplodeBomb();
}
}
private void Draw(SpriteBatch spriteBatch)
{
_drawComponent.Draw(spriteBatch);
// make sure to draw the bomb ontop of the arrow
if (_objBomb != null)
_objBomb.Draw(spriteBatch);
}
public void InitBombMode(ObjBomb bomb)
{
_bombMode = true;
_objBomb = bomb;
_body.CollisionTypesIgnore = Values.CollisionTypes.None;
EntityPosition.AddPositionListener(typeof(ObjBomb), (position) => bomb.EntityPosition.Set(position.Position + _bombOffset[_dir]));
}
}
}