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

343 lines
12 KiB
C#

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using ProjectZ.Base;
using ProjectZ.InGame.GameObjects.Base;
using ProjectZ.InGame.GameObjects.Base.CObjects;
using ProjectZ.InGame.GameObjects.Base.Components;
using ProjectZ.InGame.Map;
using ProjectZ.InGame.SaveLoad;
using ProjectZ.InGame.Things;
namespace ProjectZ.InGame.GameObjects.Things
{
internal class ObjBomb : GameObject
{
public BodyComponent Body;
public bool DamageEnemies;
private readonly Animator _animator;
private readonly BodyDrawShadowComponent _bodyShadow;
private readonly CarriableComponent _carriableComponent;
private readonly BodyDrawComponent _drawComponent;
private readonly bool _playerBomb;
private readonly bool _floorExplode;
public const int BlinkTime = 1000 / 60 * 4;
private const int ExplosionTime = 1500;
private double _bombCounter;
private double _explostionTime;
private double _lastHitTime;
private double _deepWaterCounter;
private bool _exploded;
private bool _arrowMode;
public ObjBomb(Map.Map map, float posX, float posY, bool playerBomb, bool floorExplode, int explosionTime = ExplosionTime) : base(map)
{
if (!map.Is2dMap)
EntityPosition = new CPosition(posX, posY, 5);
else
EntityPosition = new CPosition(posX, posY - 5, 0);
EntitySize = new Rectangle(-8, -16, 16, 20);
Body = new BodyComponent(EntityPosition, -4, -8, 8, 8, 4)
{
Bounciness = 0.5f,
Bounciness2D = 0.5f,
Drag = 0.85f,
DragAir = 1.0f,
DragWater = 0.985f,
Gravity = -0.15f,
HoleAbsorb = FallDeath,
MoveCollision = OnCollision,
IgnoreInsideCollision = false,
};
if (map.Is2dMap)
{
Body.OffsetY = -1;
Body.Height = 1;
}
_playerBomb = playerBomb;
// explode on collision with the floor
_floorExplode = floorExplode;
_animator = AnimatorSaveLoad.LoadAnimator("Objects/bomb");
_animator.OnAnimationFinished = FinishedAnimation;
_animator.Play("idle");
_explostionTime = explosionTime;
_bombCounter = _explostionTime;
var sprite = new CSprite(EntityPosition);
var animationComponent = new AnimationComponent(_animator, sprite, Vector2.Zero);
_drawComponent = new BodyDrawComponent(Body, sprite, Values.LayerPlayer);
AddComponent(UpdateComponent.Index, new UpdateComponent(Update));
AddComponent(HittableComponent.Index, new HittableComponent(new CBox(EntityPosition, -4, -10, 8, 10, 8), OnHit));
AddComponent(BodyComponent.Index, Body);
AddComponent(BaseAnimationComponent.Index, animationComponent);
// can not push away the bombs from enemies; would probably be fun
if (playerBomb)
AddComponent(PushableComponent.Index, new PushableComponent(Body.BodyBox, OnPush) { RepelMultiplier = 0.5f });
AddComponent(CarriableComponent.Index, _carriableComponent = new CarriableComponent(
new CRectangle(EntityPosition, new Rectangle(-4, -8, 8, 8)), CarryInit, CarryUpdate, CarryThrow));
AddComponent(DrawComponent.Index, new DrawComponent(Draw, Values.LayerPlayer, EntityPosition));
AddComponent(DrawShadowComponent.Index, _bodyShadow = new BodyDrawShadowComponent(Body, sprite));
}
private void Update()
{
if (_exploded)
{
// use the collision data from the animation to deal damage
if (!_playerBomb)
{
var collisionRect = _animator.CollisionRectangle;
if (collisionRect != Rectangle.Empty)
{
var collisionBox = new Box(
EntityPosition.X + collisionRect.X,
EntityPosition.Y + collisionRect.Y, 0,
collisionRect.Width, collisionRect.Height, 16);
if (collisionBox.Intersects(MapManager.ObjLink._body.BodyBox.Box))
MapManager.ObjLink.HitPlayer(collisionBox, HitType.Bomb, 4);
}
}
// remove bomb if the animation is finished
if (!_animator.IsPlaying)
Map.Objects.DeleteObjects.Add(this);
}
else
{
// blink
if (_bombCounter < 500)
{
if (_bombCounter % (BlinkTime * 2) < BlinkTime)
_animator.Play("blink");
else
_animator.Play("idle");
}
_bombCounter -= Game1.DeltaTime;
if (_bombCounter <= 0)
Explode();
}
// fall into the water
if (!Map.Is2dMap && Body.IsGrounded && Body.CurrentFieldState.HasFlag(MapStates.FieldStates.DeepWater))
{
_deepWaterCounter -= Game1.DeltaTime;
if (_deepWaterCounter <= 0)
{
// spawn splash effect
var fallAnimation = new ObjAnimator(Map,
(int)(Body.Position.X + Body.OffsetX + Body.Width / 2.0f),
(int)(Body.Position.Y + Body.OffsetY + Body.Height / 2.0f),
Values.LayerPlayer, "Particles/fishingSplash", "idle", true);
Map.Objects.SpawnObject(fallAnimation);
Map.Objects.DeleteObjects.Add(this);
}
}
else if (Body.IsGrounded)
{
_deepWaterCounter = 75;
}
}
public void Draw(SpriteBatch spriteBatch)
{
_drawComponent.Draw(spriteBatch);
}
private void FallDeath()
{
// play sound effect
Game1.GameManager.PlaySoundEffect("D360-24-18");
var fallAnimation = new ObjAnimator(Map, 0, 0, Values.LayerBottom, "Particles/fall", "idle", true);
fallAnimation.EntityPosition.Set(new Vector2(
Body.Position.X + Body.OffsetX + Body.Width / 2.0f - 5,
Body.Position.Y + Body.OffsetY + Body.Height / 2.0f - 5));
Map.Objects.SpawnObject(fallAnimation);
Map.Objects.DeleteObjects.Add(this);
}
private Vector3 CarryInit()
{
_animator.Play("idle");
// the bomb was picked up
Body.IsActive = false;
return EntityPosition.ToVector3();
}
private bool CarryUpdate(Vector3 newPosition)
{
_bombCounter = ExplosionTime;
EntityPosition.X = newPosition.X;
if (!Map.Is2dMap)
{
EntityPosition.Y = newPosition.Y;
EntityPosition.Z = newPosition.Z;
}
else
{
EntityPosition.Y = newPosition.Y - newPosition.Z;
EntityPosition.Z = 0;
}
EntityPosition.NotifyListeners();
return true;
}
private void CarryThrow(Vector2 velocity)
{
Body.Drag = 0.75f;
Body.DragAir = 1.0f;
Body.IsGrounded = false;
Body.IsActive = true;
if (_playerBomb)
Body.Level = MapStates.GetLevel(MapManager.ObjLink._body.CurrentFieldState);
// do not throw the bomb up when the player lets it fall down (e.g. by walking into a door)
if (velocity == Vector2.Zero)
Body.Velocity = Vector3.Zero;
else
Body.Velocity = new Vector3(velocity.X * 0.45f, velocity.Y * 0.45f, 1.25f);
if (Map.Is2dMap)
Body.Velocity.Y = -0.75f;
Body.CollisionTypesIgnore = Values.CollisionTypes.ThrowWeaponIgnore;
_carriableComponent.IsActive = false;
}
public void Explode()
{
_exploded = true;
Body.Velocity = Vector3.Zero;
Body.IsActive = false;
_bodyShadow.IsActive = false;
_carriableComponent.IsActive = false;
// deals damage to the player or to the enemies
if (_playerBomb || DamageEnemies)
Map.Objects.Hit(this, new Vector2(EntityPosition.X, EntityPosition.Y),
new Box(EntityPosition.X - 20, EntityPosition.Y - 20 - 5, 0, 40, 40, 16), HitType.Bomb, 2, false);
Game1.GameManager.PlaySoundEffect("D378-12-0C");
_animator.Play("explode");
_animator.SetFrame(1);
// shake the screen
Game1.GameManager.ShakeScreen(150, 8, 2, 5, 2.5f);
}
private void FinishedAnimation()
{
// explode after the idle animation is finished
if (_animator.CurrentAnimation.Id == "idle")
Explode();
}
private void OnCollision(Values.BodyCollision collision)
{
if (Map.Is2dMap)
{
if ((collision & Values.BodyCollision.Horizontal) != 0)
{
Body.Velocity.X = -Body.Velocity.X * 0.25f;
Game1.GameManager.PlaySoundEffect("D360-09-09");
}
if ((collision & Values.BodyCollision.Bottom) != 0 && Body.Velocity.Y < -0.075f)
{
Body.DragAir *= 0.975f;
_carriableComponent.IsActive = true;
Game1.GameManager.PlaySoundEffect("D360-09-09");
}
}
if ((collision & Values.BodyCollision.Floor) != 0)
{
if (Body.Velocity.Z > 0.5f)
Game1.GameManager.PlaySoundEffect("D360-09-09");
//Body.Level = 0;
Body.Drag *= 0.8f;
_carriableComponent.IsActive = true;
if (_floorExplode && Body.Velocity.Z <= 0)
Explode();
}
}
private bool OnPush(Vector2 direction, PushableComponent.PushType type)
{
if (_exploded)
return false;
// push the bomb away
if (type == PushableComponent.PushType.Impact)
{
Body.Drag = 0.85f;
Body.Velocity = new Vector3(direction.X * 1.5f, direction.Y * 1.5f, Body.Velocity.Z);
return true;
}
return false;
}
private Values.HitCollision OnHit(GameObject gameObject, Vector2 direction, HitType damageType, int damage, bool pieceOfPower)
{
// got picked up by an arrow?
if (_playerBomb && _bombCounter + 175 > _explostionTime && gameObject is ObjArrow objArrow)
{
_arrowMode = true;
Body.IgnoresZ = true;
Body.IgnoreHoles = true;
Body.Velocity = Vector3.Zero;
EntityPosition.Z = 0;
objArrow.InitBombMode(this);
}
if (_arrowMode)
return Values.HitCollision.None;
if (_exploded || (_lastHitTime != 0 && Game1.TotalGameTime - _lastHitTime < 250) || damageType == HitType.Bow)
return Values.HitCollision.None;
_lastHitTime = Game1.TotalGameTime;
Body.Drag = 0.85f;
Body.DragAir = 0.85f;
Body.Velocity.X += direction.X * 4;
Body.Velocity.Y += direction.Y * 4;
if (Map.Is2dMap)
{
Body.DragAir = 0.925f;
Body.Velocity.Y = direction.Y * 2f;
}
return Values.HitCollision.Blocking;
}
}
}