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