mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
344 lines
14 KiB
C#
344 lines
14 KiB
C#
using Microsoft.Xna.Framework;
|
|
using ProjectZ.Base;
|
|
using ProjectZ.InGame.GameObjects.Base;
|
|
using ProjectZ.InGame.GameObjects.Base.CObjects;
|
|
using ProjectZ.InGame.GameObjects.Base.Components;
|
|
using ProjectZ.InGame.GameObjects.Dungeon;
|
|
using ProjectZ.InGame.Map;
|
|
using ProjectZ.InGame.Things;
|
|
|
|
namespace ProjectZ.InGame.GameObjects.Things
|
|
{
|
|
internal class ObjStone : GameObject
|
|
{
|
|
private readonly BodyComponent _body;
|
|
private readonly BoxCollisionComponent _collisionComponent;
|
|
private readonly CarriableComponent _carriableComponent;
|
|
|
|
private readonly CBox _upperBox;
|
|
private readonly CBox _lowerBox;
|
|
private readonly CBox _damageBox;
|
|
|
|
private readonly Point _spawnPosition;
|
|
|
|
private readonly string _spawnItem;
|
|
private readonly string _pickupKey;
|
|
private readonly string _dialogPath;
|
|
|
|
private readonly bool _potMessage;
|
|
|
|
private int _offsetY = 3;
|
|
|
|
private bool _thrown;
|
|
private bool _isAlive = true;
|
|
private bool _damagePlayer;
|
|
private bool _isHeavy;
|
|
|
|
public ObjStone(Map.Map map, int posX, int posY, string spriteId, string spawnItem, string pickupKey, string dialogPath, bool isHeavy, bool potMessage) : base(map, spriteId)
|
|
{
|
|
var sprite = Resources.GetSprite(spriteId);
|
|
|
|
EntityPosition = new CPosition(posX + 8, posY + 16 - _offsetY, 0);
|
|
EntitySize = new Rectangle(
|
|
-sprite.SourceRectangle.Width / 2, _offsetY - sprite.SourceRectangle.Height * 2, sprite.SourceRectangle.Width, sprite.SourceRectangle.Height * 2 + 4);
|
|
|
|
_spawnPosition = new Point(posX, posY + 2);
|
|
|
|
_spawnItem = spawnItem;
|
|
_pickupKey = pickupKey;
|
|
_dialogPath = dialogPath;
|
|
|
|
_isHeavy = isHeavy;
|
|
_potMessage = potMessage;
|
|
|
|
_upperBox = new CBox(EntityPosition, -4, -8 + _offsetY, 0, 8, 8, 4, true);
|
|
_lowerBox = new CBox(EntityPosition, -4, -8 + _offsetY, 0, 8, 8, 4);
|
|
|
|
_damageBox = new CBox(EntityPosition, -7, -14 + _offsetY, 0, 14, 14, 12, true);
|
|
|
|
var height = map.Is2dMap ? 15 : 13;
|
|
var heightOffset = map.Is2dMap ? 0 : 2;
|
|
var collisionBox = new CBox(EntityPosition, -sprite.SourceRectangle.Width / 2, -height + _offsetY, 0, sprite.SourceRectangle.Width, height - heightOffset, 12, true);
|
|
_body = new BodyComponent(EntityPosition, -4, -8 + _offsetY, 8, 8, 12)
|
|
{
|
|
CollisionTypes = Values.CollisionTypes.Normal,
|
|
MoveCollision = OnCollision,
|
|
HoleAbsorb = OnHoleAbsorb,
|
|
DragAir = 1.0f,
|
|
Gravity = -0.125f,
|
|
IgnoreHeight = true
|
|
};
|
|
|
|
var cSprite = new CSprite(spriteId, EntityPosition, new Vector2(-sprite.SourceRectangle.Width / 2, -sprite.SourceRectangle.Height + _offsetY));
|
|
|
|
if (!string.IsNullOrEmpty(_dialogPath))
|
|
AddComponent(PushableComponent.Index, new PushableComponent(collisionBox, OnPush) { InertiaTime = 50 });
|
|
AddComponent(BodyComponent.Index, _body);
|
|
AddComponent(CarriableComponent.Index, _carriableComponent = new CarriableComponent(
|
|
new CRectangle(EntityPosition, new Rectangle(
|
|
-sprite.SourceRectangle.Width / 2, -13 + _offsetY, sprite.SourceRectangle.Width, 13)), CarryInit, CarryUpdate, CarryThrow)
|
|
{
|
|
IsHeavy = _isHeavy
|
|
});
|
|
AddComponent(CollisionComponent.Index, _collisionComponent = new BoxCollisionComponent(collisionBox, Values.CollisionTypes.Normal | Values.CollisionTypes.Hookshot));
|
|
AddComponent(UpdateComponent.Index, new UpdateComponent(Update));
|
|
AddComponent(DrawComponent.Index, new DrawCSpriteComponent(cSprite, Values.LayerPlayer));
|
|
AddComponent(DrawShadowComponent.Index, new BodyDrawShadowComponent(_body, cSprite));
|
|
}
|
|
|
|
public bool MakeFlyingStone()
|
|
{
|
|
// was already picked up?
|
|
if (!_isAlive || !_collisionComponent.IsActive)
|
|
return false;
|
|
|
|
_body.IgnoresZ = true;
|
|
_collisionComponent.IsActive = false;
|
|
_carriableComponent.IsActive = false;
|
|
|
|
var damageBox = new CBox(EntityPosition, -6, -13, 0, 12, 20, 8, true);
|
|
AddComponent(DamageFieldComponent.Index, new DamageFieldComponent(damageBox, HitType.Enemy, 3) { OnDamage = DamagePlayer });
|
|
|
|
// deal damage to the player
|
|
_damagePlayer = true;
|
|
|
|
return true;
|
|
}
|
|
|
|
private bool DamagePlayer()
|
|
{
|
|
if (MapManager.ObjLink.HitPlayer(_damageBox.Box, HitType.Enemy, 2))
|
|
{
|
|
OnCollision();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void ThrowStone(Vector2 direction)
|
|
{
|
|
_thrown = true;
|
|
_body.VelocityTarget = direction;
|
|
_body.CollisionTypes = Values.CollisionTypes.None;
|
|
}
|
|
|
|
public void LetGo()
|
|
{
|
|
_body.IgnoresZ = false;
|
|
}
|
|
|
|
private bool OnPush(Vector2 direction, PushableComponent.PushType pushType)
|
|
{
|
|
if (pushType == PushableComponent.PushType.Impact)
|
|
return false;
|
|
|
|
Game1.GameManager.StartDialogPath(_dialogPath);
|
|
|
|
return false;
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
if (!_thrown)
|
|
return;
|
|
|
|
// this is used because the normal collision detection looks strang when throwing directly towards a lower wall
|
|
var outBox = Box.Empty;
|
|
if (!Map.Is2dMap &&
|
|
Map.Objects.Collision(_upperBox.Box, Box.Empty, Values.CollisionTypes.Normal, 0, _body.Level, ref outBox) &&
|
|
Map.Objects.Collision(_lowerBox.Box, Box.Empty, Values.CollisionTypes.Normal, 0, _body.Level, ref outBox))
|
|
OnCollision();
|
|
|
|
if (_damagePlayer)
|
|
return;
|
|
|
|
// TODO: find the right hittype with the correct amount of damage or create a extra one?
|
|
var hitCollision = Map.Objects.Hit(this, _damageBox.Box.Center, _damageBox.Box, HitType.ThrownObject, 2, false);
|
|
|
|
// hit something?
|
|
if (hitCollision != Values.HitCollision.None &&
|
|
hitCollision != Values.HitCollision.NoneBlocking)
|
|
{
|
|
OnCollision();
|
|
}
|
|
}
|
|
|
|
private Vector3 CarryInit()
|
|
{
|
|
if (_spawnItem != null)
|
|
{
|
|
// spawn item
|
|
var objItem = new ObjItem(Map, _spawnPosition.X, _spawnPosition.Y, "j", _pickupKey, _spawnItem, "");
|
|
if (!objItem.IsDead)
|
|
Map.Objects.SpawnObject(objItem);
|
|
else if (_spawnItem == "fairy")
|
|
{
|
|
// spawn fairy
|
|
var objFairy = new ObjDungeonFairy(Map, (int)EntityPosition.X, (int)EntityPosition.Y, 0);
|
|
Map.Objects.SpawnObject(objFairy);
|
|
}
|
|
}
|
|
// @HACK: if we spawn an item we use the pickupKey as the item save key
|
|
// set the pickup key
|
|
else if (!string.IsNullOrEmpty(_pickupKey))
|
|
Game1.GameManager.SaveManager.SetString(_pickupKey, "1");
|
|
|
|
// the stone was picked up
|
|
_collisionComponent.IsActive = false;
|
|
_body.IsActive = false;
|
|
// we ignore move collisions and use the update methode to get nicer looking collisions
|
|
if (!Map.Is2dMap)
|
|
_body.CollisionTypes = Values.CollisionTypes.None;
|
|
|
|
return new Vector3(EntityPosition.X, EntityPosition.Y - _offsetY, EntityPosition.Z);
|
|
}
|
|
|
|
private bool CarryUpdate(Vector3 newPosition)
|
|
{
|
|
EntityPosition.X = newPosition.X;
|
|
|
|
if (!Map.Is2dMap)
|
|
{
|
|
EntityPosition.Y = newPosition.Y - _offsetY;
|
|
EntityPosition.Z = newPosition.Z;
|
|
}
|
|
else
|
|
{
|
|
EntityPosition.Y = newPosition.Y - _offsetY - newPosition.Z;
|
|
EntityPosition.Z = 0;
|
|
}
|
|
|
|
EntityPosition.NotifyListeners();
|
|
return true;
|
|
}
|
|
|
|
private void CarryThrow(Vector2 velocity)
|
|
{
|
|
_thrown = true;
|
|
_body.IsGrounded = false;
|
|
_body.IsActive = true;
|
|
_body.Velocity = new Vector3(velocity.X, velocity.Y, 0) * 1.0f;
|
|
_body.Level = MapStates.GetLevel(MapManager.ObjLink._body.CurrentFieldState);
|
|
}
|
|
|
|
private void OnCollision(Values.BodyCollision direction)
|
|
{
|
|
// not sure why we check for the floor collision
|
|
if ((direction & Values.BodyCollision.Floor) != 0 || (_thrown && Map.Is2dMap))
|
|
OnCollision();
|
|
}
|
|
|
|
private void OnCollision()
|
|
{
|
|
if (!_isAlive)
|
|
return;
|
|
|
|
if (_body.CurrentFieldState.HasFlag(MapStates.FieldStates.DeepWater))
|
|
{
|
|
// 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);
|
|
}
|
|
else
|
|
{
|
|
if (_potMessage)
|
|
Game1.GameManager.StartDialogPath("break_pot");
|
|
|
|
Game1.GameManager.PlaySoundEffect(_isHeavy ? "D378-41-29" : "D378-09-09");
|
|
|
|
// spawn small particle stones
|
|
SpawnParticles(EntityPosition.ToVector3());
|
|
if (_isHeavy)
|
|
SpawnParticles(new Vector3(EntityPosition.X, EntityPosition.Y, EntityPosition.Z + 12));
|
|
}
|
|
|
|
// remove the stone object from the map
|
|
Map.Objects.DeleteObjects.Add(this);
|
|
|
|
_isAlive = false;
|
|
}
|
|
|
|
// would be nicer if it would not directly absorb the stone
|
|
private void OnHoleAbsorb()
|
|
{
|
|
if (!_isAlive)
|
|
return;
|
|
|
|
// remove the stone object from the map
|
|
Map.Objects.DeleteObjects.Add(this);
|
|
|
|
// 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);
|
|
|
|
_isAlive = false;
|
|
}
|
|
|
|
private void SpawnParticles(Vector3 position)
|
|
{
|
|
var diff = 200f;
|
|
|
|
var mult = 0.125f;
|
|
var bodyVelocity = new Vector3(
|
|
_body.Velocity.X * mult, _body.Velocity.Y * mult, 1.25f);
|
|
|
|
if (Map.Is2dMap)
|
|
{
|
|
if ((_body.VelocityCollision & Values.BodyCollision.Horizontal) != 0)
|
|
bodyVelocity.X = -bodyVelocity.X * 0.5f;
|
|
if ((_body.VelocityCollision & Values.BodyCollision.Vertical) != 0)
|
|
bodyVelocity.Y = -bodyVelocity.Y * 0.5f;
|
|
}
|
|
|
|
var rndMin = 50;
|
|
var rndMax = 75;
|
|
|
|
Vector3 vector0;
|
|
Vector3 vector1;
|
|
Vector3 vector2;
|
|
Vector3 vector3;
|
|
|
|
if (Map.Is2dMap)
|
|
{
|
|
bodyVelocity.Y = 0;
|
|
bodyVelocity.Z = 0;
|
|
rndMin = 55;
|
|
vector0 = new Vector3(-0.25f, -3, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector1 = new Vector3(-0.75f, -2.75f, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector2 = new Vector3(0.25f, -3, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector3 = new Vector3(0.75f, -2.75f, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
}
|
|
else
|
|
{
|
|
vector0 = new Vector3(-1, -1, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector1 = new Vector3(-1, 0, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector2 = new Vector3(1, -1, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
vector3 = new Vector3(1, 0, 0) * Game1.RandomNumber.Next(rndMin, rndMax) / diff;
|
|
}
|
|
|
|
vector0 += bodyVelocity;
|
|
vector1 += bodyVelocity;
|
|
vector2 += bodyVelocity;
|
|
vector3 += bodyVelocity;
|
|
|
|
var stone0 = new ObjSmallStone(Map, (int)position.X - 2, (int)position.Y - 13 + _offsetY, (int)position.Z, vector0, true);
|
|
var stone1 = new ObjSmallStone(Map, (int)position.X - 1, (int)position.Y - 8 + _offsetY, (int)position.Z, vector1, true);
|
|
var stone2 = new ObjSmallStone(Map, (int)position.X + 3, (int)position.Y - 13 + _offsetY, (int)position.Z, vector2, false);
|
|
var stone3 = new ObjSmallStone(Map, (int)position.X + 2, (int)position.Y - 8 + _offsetY, (int)position.Z, vector3, false);
|
|
|
|
Map.Objects.SpawnObject(stone0);
|
|
Map.Objects.SpawnObject(stone1);
|
|
Map.Objects.SpawnObject(stone2);
|
|
Map.Objects.SpawnObject(stone3);
|
|
}
|
|
}
|
|
} |