mirror of
https://github.com/Phantop/LADXHD.git
synced 2024-11-01 04:14:22 +00:00
4871 lines
183 KiB
C#
4871 lines
183 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Globalization;
|
|
using Microsoft.Xna.Framework;
|
|
using Microsoft.Xna.Framework.Graphics;
|
|
using Microsoft.Xna.Framework.Input;
|
|
using ProjectZ.Base;
|
|
using ProjectZ.InGame.Controls;
|
|
using ProjectZ.InGame.GameObjects.Base;
|
|
using ProjectZ.InGame.GameObjects.Base.CObjects;
|
|
using ProjectZ.InGame.GameObjects.Base.Components;
|
|
using ProjectZ.InGame.GameObjects.Base.Systems;
|
|
using ProjectZ.InGame.GameObjects.Dungeon;
|
|
using ProjectZ.InGame.GameObjects.NPCs;
|
|
using ProjectZ.InGame.GameObjects.Things;
|
|
using ProjectZ.InGame.GameSystems;
|
|
using ProjectZ.InGame.Map;
|
|
using ProjectZ.InGame.SaveLoad;
|
|
using ProjectZ.InGame.Things;
|
|
|
|
namespace ProjectZ.InGame.GameObjects
|
|
{
|
|
public partial class ObjLink : GameObject
|
|
{
|
|
public enum State
|
|
{
|
|
Idle, Pushing, Grabbing, Pulling, Jumping, Attacking, Charging, Blocking, PreCarrying, Carrying, Throwing, CarryingItem, PickingUp, Falling,
|
|
Ocarina, OcarinaTelport, Rafting, Pushed,
|
|
FallRotateEntry,
|
|
Drowning, Drowned, Swimming,
|
|
Teleporting, MagicRod, Hookshot, Bombing, Powdering, Digging, BootKnockback,
|
|
TeleporterUpWait, TeleporterUp, TeleportFallWait, TeleportFall,
|
|
Dying, InitStunned, Stunned, Knockout,
|
|
SwordShow0, SwordShow1, SwordShowLv2,
|
|
ShowInstrumentPart0, ShowInstrumentPart1, ShowInstrumentPart2, ShowInstrumentPart3,
|
|
ShowToadstool,
|
|
CloakShow0, CloakShow1,
|
|
Intro, BedTransition,
|
|
Sequence, FinalInstruments,
|
|
Frozen
|
|
}
|
|
|
|
public State CurrentState;
|
|
|
|
private List<GameObject> _bombList = new List<GameObject>();
|
|
private List<GameObject> _ocarinaList = new List<GameObject>();
|
|
private List<GameObject> _destroyableWallList = new List<GameObject>();
|
|
|
|
// movement stuff
|
|
public float PosX => EntityPosition.X;
|
|
public float PosY => EntityPosition.Y;
|
|
public float PosZ => EntityPosition.Z;
|
|
|
|
private const float WalkSpeed = 1.0f;
|
|
private const float WalkSpeedPoP = 20 / 16f;
|
|
private const float BootsRunningSpeed = 2.0f;
|
|
private const float SwimSpeed = 0.5f;
|
|
private const float SwimSpeedA = 1.0f;
|
|
|
|
private float _currentWalkSpeed;
|
|
private float _waterSoundCounter;
|
|
|
|
private readonly Vector2[] _walkDirection = { new Vector2(-1, 0), new Vector2(0, -1), new Vector2(1, 0), new Vector2(0, 1) };
|
|
|
|
public Vector2 ForwardVector
|
|
{
|
|
get => _walkDirection[Direction];
|
|
}
|
|
|
|
private Vector2 _moveVelocity;
|
|
private Vector2 _lastMoveVelocity;
|
|
private Vector2 _lastBaseMoveVelocity;
|
|
public Vector2 LastMoveVector;
|
|
|
|
private Point _lastTilePosition;
|
|
|
|
public int Direction;
|
|
private bool _isWalking;
|
|
|
|
// player animations
|
|
public readonly Animator Animation;
|
|
private int _animationOffsetX = -7;
|
|
private int _animationOffsetY = -16;
|
|
|
|
private CSprite _sprite;
|
|
private float _spriteTransparency;
|
|
|
|
private bool _isVisible;
|
|
public bool IsVisible
|
|
{
|
|
get => _isVisible;
|
|
set
|
|
{
|
|
_isVisible = value;
|
|
_sprite.IsVisible = value;
|
|
}
|
|
}
|
|
|
|
// weapon animations
|
|
private Animator AnimatorWeapons;
|
|
|
|
private double _fallEntryCounter;
|
|
|
|
// hole stuff
|
|
private Vector2 _holeResetPoint;
|
|
private Vector2 _alternativeHoleResetPosition; // map change on hole fall
|
|
public string HoleResetRoom;
|
|
public string HoleResetEntryId;
|
|
public int HoleTeleporterId;
|
|
public bool WasHoleReset;
|
|
private double _holeTeleportCounter;
|
|
// counter to start the level change
|
|
private float _holeFallCounter;
|
|
private bool _isFallingIntoHole;
|
|
|
|
// body stuff
|
|
public BodyComponent _body;
|
|
public RectangleF BodyRectangle => _body.BodyBox.Box.Rectangle();
|
|
public RectangleF PlayerRectangle => new RectangleF(PosX - 4, PosY - 12 - PosZ, 8, 12);
|
|
private BodyDrawComponent _drawBody;
|
|
private BodyDrawShadowComponent _shadowComponent;
|
|
private DrawComponent.DrawTemplate _bodyDrawFunction;
|
|
|
|
// carried object
|
|
private GameObject _carriedGameObject;
|
|
private DrawComponent _carriedObjDrawComp;
|
|
private CarriableComponent _carriedComponent;
|
|
private Vector3 _carryStartPosition;
|
|
|
|
// show item
|
|
public GameItem ShowItem;
|
|
private Vector2 _showItemOffset;
|
|
// used to only collect the item after it was shown
|
|
private GameItemCollected _collectedShowItem;
|
|
private string _pickupDialogOverride;
|
|
private string _additionalPickupDialog;
|
|
private double _itemShowCounter;
|
|
private bool _showItem;
|
|
|
|
private bool _savedPreItemPickup;
|
|
public bool SavePreItemPickup
|
|
{
|
|
get { return _savedPreItemPickup; }
|
|
}
|
|
|
|
private const int dist0 = 30;
|
|
private const int dist1 = 15;
|
|
private readonly Vector2[] _showInstrumentOffset = {
|
|
new Vector2(-dist1, -dist0), new Vector2(dist1, -dist0), new Vector2(dist0, dist1), new Vector2(dist0, -dist1),
|
|
new Vector2(dist1, dist0),new Vector2(-dist1, dist0),new Vector2(-dist0, -dist1),new Vector2(-dist0, dist1) };
|
|
|
|
private readonly int[] _instrumentMusicIndex = { 31, 39, 40, 41, 42, 43, 44, 45 };
|
|
|
|
// show sword lv2
|
|
private float _showSwordLv2Counter;
|
|
private float _showSwordL2ParticleCounter;
|
|
private bool _shownSwordLv2Dialog;
|
|
|
|
// transition stuff
|
|
public Vector2? MapTransitionStart;
|
|
public Vector2? MapTransitionEnd;
|
|
public Vector2? NextMapPositionStart;
|
|
public Vector2? NextMapPositionEnd;
|
|
public string NextMapPositionId;
|
|
public int DirectionEntry;
|
|
public bool IsTransitioning;
|
|
public bool NextMapFallStart;
|
|
public bool NextMapFallRotateStart;
|
|
public bool TransitionOutWalking;
|
|
public bool TransitionInWalking;
|
|
private bool _wasTransitioning;
|
|
private bool _startBedTransition;
|
|
|
|
// rail jump
|
|
private Vector2 _railJumpStartPosition;
|
|
private Vector2 _railJumpTargetPosition;
|
|
private float _railJumpPositionZ;
|
|
private float _railJumpPercentage;
|
|
private float _railJumpHeight;
|
|
|
|
// swim stuff
|
|
private Vector2 _swimVelocity;
|
|
private float _swimBoostCount;
|
|
private float _diveCounter;
|
|
|
|
// store item picked up by the player
|
|
public GameItem StoreItem;
|
|
private int _storeItemWidth;
|
|
private int _storeItemHeight;
|
|
private Vector2 _storePickupPosition;
|
|
private bool _showStealMessage;
|
|
|
|
// follower
|
|
private GameObjectFollower _objFollower;
|
|
private ObjCock _objRooster;
|
|
private ObjMarin _objMaria;
|
|
|
|
private const string _spawnGhostKey = "spawn_ghost";
|
|
private ObjGhost _objGhost;
|
|
private bool _spawnGhost;
|
|
|
|
// boots
|
|
private bool _bootsHolding;
|
|
private bool _bootsRunning;
|
|
private bool _wasBootsRunning;
|
|
private bool _bootsStop;
|
|
private float _bootsCounter;
|
|
private float _bootsRunTime = 500;
|
|
private float _bootsParticleTime = 120;
|
|
|
|
// trapped state
|
|
private int _trapInteractionCount;
|
|
private bool _isTrapped;
|
|
private bool _trappedDisableItems;
|
|
|
|
// raft
|
|
private ObjRaft _objRaft;
|
|
private bool _isRafting;
|
|
|
|
// stonelifter pull
|
|
private const float PullTime = 100;
|
|
private const float PullMaxTime = 400;
|
|
private const float PullResetTime = -133;
|
|
private float _pullCounter;
|
|
private bool _isPulling;
|
|
private bool _wasPulling;
|
|
// pick up time
|
|
private const float PreCarryTime = 200;
|
|
private float _preCarryCounter;
|
|
|
|
// drown stuff
|
|
private Vector2 _drownResetPosition;
|
|
private float _drownCounter;
|
|
private float _drownResetCounter;
|
|
|
|
// sword stuff
|
|
public Box SwordDamageBox;
|
|
private float _swordPokeTime = 100;
|
|
private float _swordPokeCounter;
|
|
|
|
private Vector2[] _shootSwordOffset;
|
|
private bool _shotSword;
|
|
|
|
public CBox DamageCollider;
|
|
private Vector2 _hitVelocity;
|
|
public const int BlinkTime = 66;
|
|
public const int CooldownTime = BlinkTime * 16;
|
|
|
|
private double _hitCount;
|
|
private double _hitRepelTime;
|
|
private double _hitParticleTime;
|
|
|
|
private const float SwordChargeTime = 500;
|
|
private float _swordChargeCounter;
|
|
private bool _swordPoked;
|
|
private bool _stopCharging;
|
|
|
|
private Point[] _pokeAnimationOffset;
|
|
private bool _isHoldingSword;
|
|
private bool _isSwingingSword;
|
|
public bool CarrySword;
|
|
|
|
// items
|
|
private ObjBoomerang _boomerang = new ObjBoomerang();
|
|
private Vector2[] _boomerangOffset;
|
|
private Vector2[] _arrowOffset;
|
|
public bool HasFlippers;
|
|
|
|
// arrow
|
|
private const float ArrowSpeed = 3;
|
|
private const float ArrowSpeedPoP = 4;
|
|
|
|
// shield
|
|
public bool CarryShield;
|
|
private bool _wasBlocking;
|
|
|
|
// hookshot
|
|
public ObjHookshot Hookshot = new ObjHookshot();
|
|
private Vector2[] _hookshotOffset;
|
|
private bool _hookshotPull;
|
|
|
|
// magic rod
|
|
private Vector2[] _magicRodOffset;
|
|
private const float MagicRodSpeed = 3;
|
|
private const float MagicRodSpeedPoP = 4;
|
|
|
|
// shovel
|
|
private Vector2[] _shovelOffset;
|
|
private Point _digPosition;
|
|
private bool _hasDug;
|
|
private bool _canDig;
|
|
|
|
private Vector2[] _powderOffset;
|
|
private Vector2[] _bombOffset;
|
|
|
|
// ocarina
|
|
private float _ocarinaCounter;
|
|
private int _ocarinaNoteIndex;
|
|
private int _ocarinaSong;
|
|
|
|
// jump stuff
|
|
private bool _canJump = true;
|
|
private const float JumpAcceleration = 2.35f;
|
|
private float _railJumpSpeed;
|
|
// should probably have been a different state because we do not want to be able to use certain items while railjumping compared to normally jumping
|
|
private bool _railJump;
|
|
private bool _startedJumping;
|
|
private bool _hasStartedJumping;
|
|
|
|
// cloak transition
|
|
private int CloakTransitionTime = 2200;
|
|
private float _cloakTransitionCounter;
|
|
private float _cloakPercentage;
|
|
private int CloakTransitionOutTime = 2500;
|
|
private float _cloakTransitionOutCounter;
|
|
|
|
// teleport stuff
|
|
private ObjDungeonTeleporter _teleporter;
|
|
private string _teleportMap;
|
|
private string _teleporterId;
|
|
private float _teleportCounter;
|
|
private float _teleportCounterFull;
|
|
private int _teleportState;
|
|
|
|
// instrument stuff
|
|
// @TODO: replace
|
|
private Rectangle[] _noteSourceRectangles = { new Rectangle(145, 97, 10, 12), new Rectangle(156, 97, 6, 12) };
|
|
private bool[] _noteInit = { false, false };
|
|
private int[] _noteSpriteIndex = { 0, 0 };
|
|
|
|
private double _instrumentPickupTime;
|
|
private float _instrumentCounter;
|
|
private float _instrumentEndTime;
|
|
private int _instrumentIndex;
|
|
private int _instrumentCycleTime = 1000;
|
|
private bool _drawInstrumentEffect;
|
|
private bool _pickingUpInstrument;
|
|
private bool _pickingUpSword;
|
|
|
|
// push stuff
|
|
private Vector2 _pushStart;
|
|
private Vector2 _pushEnd;
|
|
private float _pushCounter;
|
|
private int _pushTime;
|
|
|
|
// used by the vaccuum
|
|
private float _rotationCounter;
|
|
|
|
// stunned state
|
|
private float _stunnedCounter;
|
|
private bool _stunnedParticles;
|
|
|
|
// final sequence
|
|
private int _finalIndex;
|
|
private double _finalSeqCounter;
|
|
|
|
// save position
|
|
public string SaveMap;
|
|
public Vector2 SavePosition;
|
|
public int SaveDirection;
|
|
|
|
// other stuff
|
|
public Point CollisionBoxSize;
|
|
private MapStates.FieldStates _lastFieldState;
|
|
|
|
public bool CanWalk;
|
|
public bool DisableItems;
|
|
public bool UpdatePlayer;
|
|
public bool IsPoking;
|
|
private bool _pokeStart;
|
|
private bool _isLocked;
|
|
private bool _isGrabbed;
|
|
private bool _isFlying;
|
|
private bool _inDungeon;
|
|
|
|
#if DEBUG
|
|
private bool _attackMode;
|
|
#endif
|
|
|
|
private DictAtlasEntry _stunnedParticleSprite;
|
|
|
|
public ObjLink() : base((Map.Map)null)
|
|
{
|
|
EntityPosition = new CPosition(0, 0, 0);
|
|
EntitySize = new Rectangle(-8, -16, 16, 16);
|
|
|
|
// load the player + sword animations
|
|
Animation = AnimatorSaveLoad.LoadAnimator("link0");
|
|
AnimatorWeapons = AnimatorSaveLoad.LoadAnimator("Objects/sword");
|
|
|
|
_stunnedParticleSprite = Resources.GetSprite("stunned particle");
|
|
|
|
CollisionBoxSize = new Point(8, 8);
|
|
|
|
_body = new BodyComponent(EntityPosition, -4, -10, 8, 10, 8)
|
|
{
|
|
IsPusher = true,
|
|
IsSlider = true,
|
|
MaxJumpHeight = 3,
|
|
Drag = 0.9f,
|
|
DragAir = 0.9f,
|
|
Gravity = -0.15f,
|
|
Gravity2D = 0.1f,
|
|
AbsorbPercentage = 1f,
|
|
HoleOnPull = OnHolePull,
|
|
HoleAbsorb = OnHoleAbsorb,
|
|
MoveCollision = OnMoveCollision,
|
|
CollisionTypes = Values.CollisionTypes.Normal |
|
|
Values.CollisionTypes.Enemy |
|
|
Values.CollisionTypes.PlayerItem |
|
|
Values.CollisionTypes.LadderTop,
|
|
};
|
|
|
|
DamageCollider = new CBox(EntityPosition, -5, -10, 10, 10, 8);
|
|
|
|
_powderOffset = new[]
|
|
{
|
|
new Vector2(-12, 0),
|
|
new Vector2(-2, -CollisionBoxSize.Y -5),
|
|
new Vector2(12, 0),
|
|
new Vector2(2, 10)
|
|
};
|
|
|
|
_boomerangOffset = new[]
|
|
{
|
|
new Vector2(-10, -3),
|
|
new Vector2(-2, -CollisionBoxSize.Y -1),
|
|
new Vector2(10, -3),
|
|
new Vector2(2, 6)
|
|
};
|
|
|
|
_arrowOffset = new[]
|
|
{
|
|
new Vector2(-10, 0),
|
|
new Vector2(-2, -CollisionBoxSize.Y -1),
|
|
new Vector2(10, 0),
|
|
new Vector2(2, 6)
|
|
};
|
|
|
|
_magicRodOffset = new[]
|
|
{
|
|
new Vector2(-10, -4),
|
|
new Vector2(-4, -CollisionBoxSize.Y - 8),
|
|
new Vector2(10, -4),
|
|
new Vector2(3, 4)
|
|
};
|
|
|
|
_shootSwordOffset = new[]
|
|
{
|
|
new Vector2(-10, -4),
|
|
new Vector2(-5, -CollisionBoxSize.Y - 8),
|
|
new Vector2(10, -4),
|
|
new Vector2(4, 4)
|
|
};
|
|
|
|
_hookshotOffset = new[]
|
|
{
|
|
new Vector2(-5, -4),
|
|
new Vector2(-3, -CollisionBoxSize.Y - 2),
|
|
new Vector2(5, -4),
|
|
new Vector2(3, 0)
|
|
};
|
|
|
|
_shovelOffset = new[]
|
|
{
|
|
new Vector2(-9, -1),
|
|
new Vector2(0, -14),
|
|
new Vector2(9, -1),
|
|
new Vector2(0, 1)
|
|
};
|
|
|
|
_bombOffset = new[]
|
|
{
|
|
new Vector2(-10, 0),
|
|
new Vector2(0, -CollisionBoxSize.Y - 2),
|
|
new Vector2(10, 0),
|
|
new Vector2(0, 8)
|
|
};
|
|
|
|
_pokeAnimationOffset = new[]
|
|
{
|
|
new Point(-16, -4),
|
|
new Point(-4, -CollisionBoxSize.Y - 16),
|
|
new Point(16, -4),
|
|
new Point(5, 12)
|
|
};
|
|
|
|
_sprite = new CSprite(EntityPosition);
|
|
// cant just change the offset value without changing the blocking rectangle
|
|
var animatorComponent = new AnimationComponent(Animation, _sprite, new Vector2(_animationOffsetX, _animationOffsetY));
|
|
|
|
// custom draw function
|
|
_drawBody = new BodyDrawComponent(_body, DrawLink, Values.LayerPlayer);
|
|
_bodyDrawFunction = _drawBody.Draw;
|
|
_drawBody.Draw = Draw;
|
|
|
|
AddComponent(KeyChangeListenerComponent.Index, new KeyChangeListenerComponent(OnKeyChange));
|
|
AddComponent(BodyComponent.Index, _body);
|
|
AddComponent(BaseAnimationComponent.Index, animatorComponent);
|
|
AddComponent(CollisionComponent.Index, new BodyCollisionComponent(_body, Values.CollisionTypes.Player));
|
|
AddComponent(UpdateComponent.Index, new UpdateComponent(Update));
|
|
AddComponent(DrawComponent.Index, _drawBody);
|
|
AddComponent(DrawShadowComponent.Index, _shadowComponent = new BodyDrawShadowComponent(_body, _sprite));
|
|
|
|
EntityPosition.AddPositionListener(typeof(CarriableComponent), UpdatePositionCarriedObject);
|
|
}
|
|
|
|
private void Update()
|
|
{
|
|
#if DEBUG
|
|
if (InputHandler.KeyPressed(Keys.Y))
|
|
Game1.GameManager.InitPieceOfPower();
|
|
if (InputHandler.KeyPressed(Keys.X))
|
|
_attackMode = !_attackMode;
|
|
if (_attackMode)
|
|
{
|
|
var damageBox = new Box(EntityPosition.X - 160, EntityPosition.Y - 140, 0, 320, 280, 16);
|
|
var damageOrigin = damageBox.Center;
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.Sword1, Game1.GameManager.PieceOfPowerIsActive ? 2 : 1, Game1.GameManager.PieceOfPowerIsActive);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.Bomb, 2, false);
|
|
Map.Objects.Hit(this, damageOrigin, damageBox, HitType.Bow, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.Hookshot, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.MagicRod, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.MagicPowder, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.PegasusBootsSword, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.PegasusBootsPush, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.ThrownObject, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.SwordShot, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.SwordHold, 2, false);
|
|
//Map.Objects.Hit(this, damageOrigin, damageBox, HitType.SwordSpin, 2, false);
|
|
}
|
|
#endif
|
|
|
|
if (CurrentState == State.FallRotateEntry)
|
|
{
|
|
_fallEntryCounter += Game1.DeltaTime;
|
|
Direction = (int)(DirectionEntry + (_fallEntryCounter + 96) / 48) % 4;
|
|
|
|
if (_body.IsGrounded)
|
|
CurrentState = State.Idle;
|
|
|
|
UpdateAnimation();
|
|
}
|
|
|
|
// @HACK
|
|
// this is only needed because the player should not be able to step into the door 1 frame after finishing the transition
|
|
// this would cause the door transition to not start
|
|
if (IsTransitioning || _wasTransitioning)
|
|
{
|
|
_wasTransitioning = IsTransitioning;
|
|
return;
|
|
}
|
|
|
|
// first photo sequence
|
|
if (CurrentState == State.Pushed)
|
|
{
|
|
_pushCounter += Game1.DeltaTime;
|
|
|
|
// push towards the target position
|
|
if (_pushCounter > _pushTime)
|
|
{
|
|
EntityPosition.Set(_pushEnd);
|
|
CurrentState = State.Idle;
|
|
}
|
|
else
|
|
{
|
|
var percentage = MathF.Sin((_pushCounter / _pushTime) * MathF.PI * 0.5f);
|
|
var newPosition = Vector2.Lerp(_pushStart, _pushEnd, percentage);
|
|
EntityPosition.Set(newPosition);
|
|
}
|
|
}
|
|
|
|
// need to update the bomb to make sure it does not explode while the player is not getting updated
|
|
if (_carriedComponent != null && _carriedComponent.IsPickedUp)
|
|
{
|
|
// used to updated the position to match the animation
|
|
// gets called twice when moving
|
|
// not sure how this could be done better
|
|
UpdatePositionCarriedObject(EntityPosition);
|
|
}
|
|
|
|
if (!UpdatePlayer)
|
|
{
|
|
UpdatePlayer = true;
|
|
|
|
// only update the animation
|
|
if (!Is2DMode)
|
|
UpdateAnimation();
|
|
else
|
|
Update2DFrozen();
|
|
|
|
UpdateOcarinaAnimation();
|
|
UpdateDive();
|
|
UpdateDrawComponents();
|
|
|
|
return;
|
|
}
|
|
|
|
UpdateHeartWarningSound();
|
|
|
|
if (CurrentState == State.FinalInstruments)
|
|
{
|
|
_finalSeqCounter -= Game1.DeltaTime;
|
|
if (_finalIndex == 0)
|
|
{
|
|
if (_finalSeqCounter <= 0)
|
|
{
|
|
_finalIndex = 1;
|
|
_finalSeqCounter += 2250;
|
|
Animation.Play("show1");
|
|
Game1.GameManager.PlaySoundEffect("D360-52-34");
|
|
}
|
|
}
|
|
else if (_finalIndex == 1)
|
|
{
|
|
if (_finalSeqCounter <= 0)
|
|
((MapShowSystem)Game1.GameManager.GameSystems[typeof(MapShowSystem)]).StartEnding();
|
|
}
|
|
|
|
return;
|
|
}
|
|
else if (CurrentState == State.CloakShow0)
|
|
{
|
|
_cloakTransitionCounter += Game1.DeltaTime;
|
|
_cloakPercentage = _cloakTransitionCounter / CloakTransitionTime;
|
|
|
|
if (_cloakTransitionCounter > CloakTransitionTime)
|
|
{
|
|
_cloakPercentage = 1;
|
|
|
|
if (ShowItem.Name == "cloakBlue")
|
|
Game1.GameManager.StartDialog("cloak_blue");
|
|
if (ShowItem.Name == "cloakRed")
|
|
Game1.GameManager.StartDialog("cloak_red");
|
|
|
|
CurrentState = State.CloakShow1;
|
|
|
|
// add the item to the inventory
|
|
if (_collectedShowItem != null)
|
|
{
|
|
Game1.GameManager.CollectItem(_collectedShowItem, 0);
|
|
_collectedShowItem = null;
|
|
}
|
|
|
|
ShowItem = null;
|
|
}
|
|
}
|
|
else if (CurrentState == State.CloakShow1)
|
|
{
|
|
_cloakTransitionOutCounter += Game1.DeltaTime;
|
|
|
|
var transitionSystem = (MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)];
|
|
transitionSystem.SetColorMode(Color.White, MathHelper.Clamp(_cloakTransitionOutCounter / 1000f, 0, 1));
|
|
|
|
if (_cloakTransitionOutCounter > CloakTransitionOutTime)
|
|
{
|
|
Game1.GameManager.StartDialogPath("color_fairy_4");
|
|
|
|
Direction = 3;
|
|
MapTransitionStart = EntityPosition.Position;
|
|
MapTransitionEnd = MapTransitionStart;
|
|
TransitionOutWalking = false;
|
|
|
|
// append a map change
|
|
((MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)]).AppendMapChange(
|
|
"overworld.map", "cloakOut", false, true, Color.White, true);
|
|
}
|
|
}
|
|
else if (CurrentState == State.ShowToadstool)
|
|
{
|
|
CurrentState = State.Idle;
|
|
}
|
|
else if (CurrentState == State.SwordShowLv2)
|
|
{
|
|
_showSwordL2ParticleCounter += Game1.DeltaTime;
|
|
if (_showSwordL2ParticleCounter > 4800 && !_shownSwordLv2Dialog)
|
|
{
|
|
_shownSwordLv2Dialog = true;
|
|
_showSwordL2ParticleCounter = 0;
|
|
Game1.GameManager.SetMusic(-1, 2);
|
|
Game1.GameManager.StartDialogPath("sword2Collected");
|
|
}
|
|
// make sure to show the sword while the dialog box is open
|
|
else if (_shownSwordLv2Dialog)
|
|
{
|
|
ShowItem = null;
|
|
CurrentState = State.Idle;
|
|
}
|
|
}
|
|
else if (CurrentState == State.PickingUp && !_pickingUpInstrument && !_pickingUpSword)
|
|
{
|
|
Game1.GameManager.FreezeWorldAroundPlayer = true;
|
|
}
|
|
else if (CurrentState == State.TeleporterUpWait)
|
|
{
|
|
_holeTeleportCounter += Game1.DeltaTime;
|
|
if (_holeTeleportCounter > 1000)
|
|
{
|
|
CurrentState = State.TeleporterUp;
|
|
|
|
_holeTeleportCounter -= 1000;
|
|
_shadowComponent.Transparency = 0;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-37-25");
|
|
}
|
|
}
|
|
else if (CurrentState == State.TeleporterUp)
|
|
{
|
|
_holeTeleportCounter += Game1.DeltaTime;
|
|
var time = 400;
|
|
|
|
EntityPosition.Z = (float)(_holeTeleportCounter / time) * 128;
|
|
Direction = (int)(_holeTeleportCounter / 64) % 4;
|
|
|
|
// fade in
|
|
var percentage = MathHelper.Clamp(1 - ((float)_holeTeleportCounter - (time - 100)) / 100, 0, 1);
|
|
_spriteTransparency = percentage;
|
|
_shadowComponent.Transparency = percentage;
|
|
|
|
if (_holeTeleportCounter > time)
|
|
{
|
|
_holeTeleportCounter -= time;
|
|
|
|
if (ObjOverworldTeleporter.TeleporterDictionary.TryGetValue(HoleTeleporterId, out var teleporter))
|
|
teleporter.SetNextTeleporterPosition();
|
|
else
|
|
CurrentState = State.Idle; // should not happen
|
|
}
|
|
}
|
|
else if (CurrentState == State.TeleportFallWait)
|
|
{
|
|
_holeTeleportCounter += Game1.DeltaTime;
|
|
var time = 350;
|
|
|
|
if (_holeTeleportCounter > time)
|
|
{
|
|
_holeTeleportCounter -= time - 50;
|
|
_body.Velocity = new Vector3(0, 0, 0);
|
|
CurrentState = State.TeleportFall;
|
|
}
|
|
}
|
|
else if (CurrentState == State.TeleportFall)
|
|
{
|
|
_holeTeleportCounter += Game1.DeltaTime;
|
|
Direction = (int)(_holeTeleportCounter / 64) % 4;
|
|
|
|
// fade in
|
|
var percentage = MathHelper.Clamp((float)_holeTeleportCounter / 100, 0, 1);
|
|
|
|
if (_body.IsGrounded)
|
|
{
|
|
percentage = 1;
|
|
CurrentState = State.Idle;
|
|
|
|
UpdateSaveLocation();
|
|
|
|
// save settings?
|
|
if (GameSettings.Autosave)
|
|
{
|
|
SaveGameSaveLoad.SaveGame(Game1.GameManager);
|
|
Game1.GameManager.InGameOverlay.InGameHud.ShowSaveIcon();
|
|
}
|
|
}
|
|
|
|
_spriteTransparency = percentage;
|
|
_shadowComponent.Transparency = percentage;
|
|
}
|
|
|
|
if (CurrentState == State.Knockout)
|
|
return;
|
|
|
|
// stunned player
|
|
if (CurrentState == State.InitStunned && _hitVelocity.Length() < 0.25f)
|
|
{
|
|
Animation.Play("stunned");
|
|
CurrentState = State.Stunned;
|
|
}
|
|
if (CurrentState == State.Stunned && _stunnedCounter > 0)
|
|
{
|
|
_stunnedCounter -= Game1.DeltaTime;
|
|
|
|
if (_stunnedCounter <= 0)
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
AnimatorWeapons.Update();
|
|
|
|
// update all the item stuff
|
|
// this need to be before the update method to correctly start jumping?
|
|
UpdateItem();
|
|
|
|
if (Is2DMode)
|
|
Update2D();
|
|
else
|
|
Update3D();
|
|
|
|
UpdateOcarina();
|
|
|
|
UpdateDamageShader();
|
|
_hitCount -= Game1.DeltaTime;
|
|
|
|
if (_savedPreItemPickup && (CurrentState == State.Idle || CurrentState == State.Swimming))
|
|
EndPickup();
|
|
|
|
// die?
|
|
if (Game1.GameManager.CurrentHealth <= 0 && !Game1.GameManager.UseShockEffect)
|
|
OnDeath();
|
|
|
|
UpdateDrawComponents();
|
|
|
|
DisableItems = false;
|
|
HoleResetRoom = null;
|
|
CanWalk = true;
|
|
_canJump = true;
|
|
_isLocked = false;
|
|
|
|
_hasStartedJumping = _startedJumping;
|
|
_startedJumping = false;
|
|
|
|
_currentWalkSpeed = Game1.GameManager.PieceOfPowerIsActive ? WalkSpeedPoP : WalkSpeed;
|
|
}
|
|
|
|
#region Draw
|
|
|
|
private void Draw(SpriteBatch spriteBatch)
|
|
{
|
|
Game1.DebugText += "Jump Timer: " + _railJumpPercentage + "\n";
|
|
Game1.DebugText += "Player State: " + CurrentState;
|
|
|
|
if (!IsVisible)
|
|
return;
|
|
|
|
// draw the player sprite behind the sword
|
|
if (Direction != 1 && !_isTrapped)
|
|
_bodyDrawFunction(spriteBatch);
|
|
|
|
// draw the sword/magic rod
|
|
if (CurrentState == State.Attacking ||
|
|
CurrentState == State.Charging ||
|
|
CurrentState == State.SwordShow0 ||
|
|
CurrentState == State.MagicRod ||
|
|
(_bootsRunning && CarrySword))
|
|
{
|
|
var changeColor = _swordChargeCounter <= 0 &&
|
|
Game1.TotalGameTime % (8 / 0.06) >= 4 / 0.06 &&
|
|
ObjectManager.CurrentEffect != Resources.DamageSpriteShader0.Effect;
|
|
|
|
// change the draw shader
|
|
if (changeColor)
|
|
{
|
|
spriteBatch.End();
|
|
ObjectManager.SpriteBatchBegin(spriteBatch, Resources.DamageSpriteShader0);
|
|
}
|
|
|
|
AnimatorWeapons.Draw(spriteBatch, new Vector2(EntityPosition.X - 7, EntityPosition.Y - 16 - EntityPosition.Z), Color.White);
|
|
|
|
if (changeColor)
|
|
{
|
|
spriteBatch.End();
|
|
ObjectManager.SpriteBatchBegin(spriteBatch, null);
|
|
}
|
|
}
|
|
|
|
// draw the sword after the first pickup
|
|
if (CurrentState == State.SwordShow1)
|
|
{
|
|
var itemSword = Game1.GameManager.ItemManager["sword1"];
|
|
var position = new Vector2(
|
|
BodyRectangle.X - itemSword.SourceRectangle.Value.Width / 2f,
|
|
(EntityPosition.Y - EntityPosition.Z - 15) - itemSword.SourceRectangle.Value.Height);
|
|
|
|
ItemDrawHelper.DrawItem(spriteBatch, itemSword, position, Color.White, 1, true);
|
|
}
|
|
|
|
// draw the toadstool
|
|
if (CurrentState == State.ShowToadstool)
|
|
{
|
|
var itemToadstool = Game1.GameManager.ItemManager["toadstool"];
|
|
var position = new Vector2(
|
|
BodyRectangle.X - itemToadstool.SourceRectangle.Value.Width / 2f,
|
|
(EntityPosition.Y - EntityPosition.Z - 15) - itemToadstool.SourceRectangle.Value.Height);
|
|
|
|
ItemDrawHelper.DrawItem(spriteBatch, itemToadstool, position, Color.White, 1);
|
|
}
|
|
|
|
// draw the player sprite in front of the sword
|
|
if (Direction == 1 && !_isTrapped)
|
|
_bodyDrawFunction(spriteBatch);
|
|
|
|
if (_drawInstrumentEffect)
|
|
DrawInstrumentEffect(spriteBatch);
|
|
|
|
// draw the picked up store item
|
|
if (StoreItem != null)
|
|
ItemDrawHelper.DrawItem(spriteBatch, StoreItem, _storePickupPosition, Color.White, 1, true);
|
|
|
|
// draw the shown item
|
|
if (ShowItem != null)
|
|
{
|
|
var itemPosition = EntityPosition.Position + _showItemOffset;
|
|
itemPosition.Y -= EntityPosition.Z;
|
|
|
|
if (CurrentState == State.CloakShow0)
|
|
{
|
|
ItemDrawHelper.DrawItem(spriteBatch, ShowItem, itemPosition, Color.White * (1 - _cloakPercentage), 1, true);
|
|
}
|
|
else if (ShowItem.Name == "sword2")
|
|
{
|
|
var swordImage = Resources.GetSprite("sword2Show");
|
|
DrawHelper.DrawNormalized(spriteBatch, swordImage.Texture, itemPosition, swordImage.ScaledRectangle, Color.White, swordImage.Scale);
|
|
}
|
|
else
|
|
ItemDrawHelper.DrawItem(spriteBatch, ShowItem, itemPosition, Color.White, 1, true);
|
|
}
|
|
|
|
// draw the object the player is carrying
|
|
if (_carriedObjDrawComp != null)
|
|
{
|
|
_carriedObjDrawComp.IsActive = true;
|
|
_carriedObjDrawComp.Draw(spriteBatch);
|
|
_carriedObjDrawComp.IsActive = false;
|
|
}
|
|
|
|
// draw the dots over the head in the stunned state
|
|
if (CurrentState == State.Stunned && _stunnedParticles)
|
|
{
|
|
var rotation = (float)(Game1.TotalGameTime / 1200) * MathF.PI * 2;
|
|
var offset0 = new Vector2(MathF.Cos(rotation) * 8 - 2, MathF.Sin(rotation) * 3 - 2);
|
|
DrawHelper.DrawNormalized(spriteBatch, _stunnedParticleSprite,
|
|
offset0 + new Vector2(EntityPosition.X, EntityPosition.Y - EntityPosition.Z - 18), Color.White);
|
|
|
|
var offset1 = new Vector2(MathF.Cos(rotation + MathF.PI) * 8 - 2, MathF.Sin(rotation + MathF.PI) * 3 - 2);
|
|
DrawHelper.DrawNormalized(spriteBatch, _stunnedParticleSprite,
|
|
offset1 + new Vector2(EntityPosition.X, EntityPosition.Y - EntityPosition.Z - 18), Color.White);
|
|
}
|
|
|
|
if (CurrentState == State.SwordShowLv2)
|
|
DrawSwordL2Particles(spriteBatch);
|
|
|
|
// draw the notes while showing an instrument
|
|
{
|
|
var leftNotePosition = new Vector2(EntityPosition.X - 8, EntityPosition.Y - 24);
|
|
DrawNote(spriteBatch, leftNotePosition, new Vector2(-0.4f, -1.0f), 0);
|
|
|
|
var rightNotePosition = new Vector2(EntityPosition.X + 8, EntityPosition.Y - 24);
|
|
DrawNote(spriteBatch, rightNotePosition, new Vector2(0.4f, -1.0f), 1);
|
|
}
|
|
|
|
if (CurrentState == State.FinalInstruments)
|
|
DrawFinalInstruments(spriteBatch);
|
|
|
|
if (Game1.DebugMode)
|
|
{
|
|
// draw the save hole position
|
|
spriteBatch.Draw(Resources.SprWhite,
|
|
new Vector2(_holeResetPoint.X - 5, _holeResetPoint.Y - 5), new Rectangle(0, 0,
|
|
10, 10), Color.HotPink * 0.65f);
|
|
|
|
// weapon damage rectangle
|
|
var swordRectangle = SwordDamageBox.Rectangle();
|
|
spriteBatch.Draw(Resources.SprWhite,
|
|
new Vector2(swordRectangle.X, swordRectangle.Y), new Rectangle(0, 0,
|
|
(int)swordRectangle.Width, (int)swordRectangle.Height), Color.Blue * 0.75f);
|
|
}
|
|
}
|
|
|
|
private void DrawSwordL2Particles(SpriteBatch spriteBatch)
|
|
{
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-32, -16), -125, 300, 200, 0);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-32, -16), -125 - 250, 300, 200, 0);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-32, -32), 0, 300, 200, 1);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-32, -32), -250, 300, 200, 1);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-24, -52), -50, 450, 50, 2);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(-24, -52), -50 - 250, 450, 50, 2);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(0, -64), -75, 450, 50, 3);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(0, -64), -75 - 250, 450, 50, 3);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(24, -52), -50, 450, 50, 4);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(24, -52), -50 - 250, 450, 50, 4);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(32, -32), 0, 300, 200, 5);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(32, -32), -250, 300, 200, 5);
|
|
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(32, -16), -125, 300, 200, 6);
|
|
DrawSwordParticle(spriteBatch, new Vector2(EntityPosition.X - 4, EntityPosition.Y - 22), new Vector2(32, -16), -125 - 250, 300, 200, 6);
|
|
}
|
|
|
|
private void DrawInstrumentEffect(SpriteBatch spriteBatch)
|
|
{
|
|
var fadeTime = 100;
|
|
var speed = 500;
|
|
var center = new Vector2(EntityPosition.X, EntityPosition.Y - 20);
|
|
|
|
{
|
|
var time = (float)(Game1.TotalGameTime % speed);
|
|
var state = MathF.Sin((time / speed) * MathF.PI * 0.475f);
|
|
var distance = 32 - 20 * state;
|
|
var transparency = MathHelper.Clamp(time / fadeTime, 0, 1) *
|
|
MathHelper.Clamp((speed - time) / fadeTime, 0, 1);
|
|
var sourceRectangle = time < (speed / 1.65f) ? new Rectangle(194, 114, 12, 12) : new Rectangle(194, 98, 12, 12);
|
|
|
|
for (var y = 0; y < 2; y++)
|
|
for (var x = 0; x < 2; x++)
|
|
{
|
|
var position = new Vector2(
|
|
center.X - 6 + (x * 2 - 1) * distance,
|
|
center.Y - 6 + (y * 2 - 1) * distance);
|
|
spriteBatch.Draw(Resources.SprItem, position, sourceRectangle,
|
|
Color.White * transparency, 0, Vector2.Zero, Vector2.One,
|
|
(x == 0 ? SpriteEffects.FlipHorizontally : SpriteEffects.None) |
|
|
(y == 0 ? SpriteEffects.FlipVertically : SpriteEffects.None), 0);
|
|
}
|
|
}
|
|
|
|
{
|
|
var time = (float)((Game1.TotalGameTime + speed / 2) % speed);
|
|
var state = MathF.Sin((time / speed) * MathF.PI * 0.475f);
|
|
var distance = 40 - 34 * state;
|
|
var transparency = MathHelper.Clamp(time / fadeTime, 0, 1) *
|
|
MathHelper.Clamp((speed - time) / fadeTime, 0, 1);
|
|
var sourceRectangle = time < (speed / 1.65f) ? new Rectangle(176, 116, 16, 8) : new Rectangle(176, 100, 16, 8);
|
|
|
|
for (var y = 0; y < 2; y++)
|
|
for (var x = 0; x < 2; x++)
|
|
{
|
|
var rotation = (float)((x * 2 + y) * Math.PI / 2);
|
|
|
|
var position = new Vector2(
|
|
center.X + (y == 0 ? (x * 2 - 1) * distance : 0),
|
|
center.Y + (y == 0 ? 0 : (x * 2 - 1) * distance));
|
|
|
|
spriteBatch.Draw(Resources.SprItem, position, sourceRectangle,
|
|
Color.White * transparency, rotation, new Vector2(16, 4), Vector2.One, SpriteEffects.None, 0);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void DrawFinalInstruments(SpriteBatch spriteBatch)
|
|
{
|
|
if (_finalIndex != 1)
|
|
return;
|
|
|
|
var percentage = 0.25f + Math.Clamp((float)(2500 - _finalSeqCounter) / 2000, 0, 1) * 0.75f;
|
|
|
|
// draw the instruments
|
|
for (var i = 0; i < 8; i++)
|
|
{
|
|
var itemInstrument = Game1.GameManager.ItemManager["instrument" + i];
|
|
var position = new Vector2(EntityPosition.X - 8, EntityPosition.Y - 60) + _showInstrumentOffset[i] * percentage;
|
|
ItemDrawHelper.DrawItem(spriteBatch, itemInstrument, position, Color.White, 1, true);
|
|
}
|
|
}
|
|
|
|
private void DrawLink(SpriteBatch spriteBatch)
|
|
{
|
|
_sprite.Draw(spriteBatch);
|
|
|
|
// draw the colored cloak
|
|
var texture = _sprite.SprTexture;
|
|
|
|
var cloakColor = Game1.GameManager.CloakColor;
|
|
if (CurrentState == State.CloakShow0 && ShowItem != null && ShowItem.Name == "cloakBlue")
|
|
cloakColor = Color.Lerp(cloakColor, ItemDrawHelper.CloakColors[1], _cloakPercentage);
|
|
else if (CurrentState == State.CloakShow0 && ShowItem != null && ShowItem.Name == "cloakRed")
|
|
cloakColor = Color.Lerp(cloakColor, ItemDrawHelper.CloakColors[2], _cloakPercentage);
|
|
|
|
_sprite.Color = cloakColor * _spriteTransparency;
|
|
_sprite.SprTexture = Resources.SprLinkCloak;
|
|
_sprite.Draw(spriteBatch);
|
|
|
|
_sprite.Color = Color.White * _spriteTransparency;
|
|
_sprite.SprTexture = texture;
|
|
}
|
|
|
|
private void DrawNote(SpriteBatch spriteBatch, Vector2 position, Vector2 direction, int noteIndex)
|
|
{
|
|
var timeOffset = noteIndex * _instrumentCycleTime / 2;
|
|
|
|
if (_instrumentCounter < timeOffset ||
|
|
(CurrentState != State.ShowInstrumentPart1 || _drawInstrumentEffect) &&
|
|
((_instrumentCounter - timeOffset) / _instrumentCycleTime + 1) * _instrumentCycleTime + timeOffset > _instrumentEndTime)
|
|
return;
|
|
|
|
var time = (_instrumentCounter + timeOffset) % _instrumentCycleTime;
|
|
|
|
var transparency = 1.0f;
|
|
// fade out
|
|
if (time > _instrumentCycleTime - 100)
|
|
{
|
|
_noteInit[noteIndex] = false;
|
|
transparency = (_instrumentCycleTime - time) / 100f;
|
|
}
|
|
// fade in
|
|
else if (time < 100)
|
|
{
|
|
if (!_noteInit[noteIndex])
|
|
{
|
|
_noteInit[noteIndex] = true;
|
|
_noteSpriteIndex[noteIndex] = Game1.RandomNumber.Next(0, 2);
|
|
|
|
}
|
|
transparency = time / 100;
|
|
}
|
|
|
|
position += direction * time * 0.02f + new Vector2(-direction.X, direction.Y) * (float)Math.Sin(time * 0.015) * 0.75f;
|
|
position += new Vector2(
|
|
-_noteSourceRectangles[_noteSpriteIndex[noteIndex]].Width / 2f,
|
|
-_noteSourceRectangles[_noteSpriteIndex[noteIndex]].Height);
|
|
|
|
spriteBatch.Draw(Resources.SprItem, position,
|
|
_noteSourceRectangles[_noteSpriteIndex[noteIndex]], Color.White * transparency);
|
|
}
|
|
|
|
private void DrawSwordParticle(SpriteBatch spriteBatch, Vector2 position, Vector2 direction, int timeOffset, int fullTime, int timeDelay, int index)
|
|
{
|
|
var fadeTime = 50;
|
|
var particleTime = (_showSwordL2ParticleCounter + timeOffset) % (fullTime + timeDelay);
|
|
var percentage = particleTime / fullTime;
|
|
var colorTransparency = Math.Min((fullTime - particleTime) / fadeTime, particleTime / fadeTime);
|
|
var particlePosition = position + percentage * direction;
|
|
var spriteParticle = Resources.GetSprite("sword_particle_" + index);
|
|
|
|
if (0 < particleTime && particleTime < fullTime)
|
|
DrawHelper.DrawNormalized(spriteBatch, spriteParticle.Texture,
|
|
particlePosition - spriteParticle.Origin, spriteParticle.ScaledRectangle, Color.White * colorTransparency, spriteParticle.Scale);
|
|
}
|
|
|
|
private void DrawLight(SpriteBatch spriteBatch)
|
|
{
|
|
DrawHelper.DrawLight(spriteBatch, new Rectangle((int)EntityPosition.X - 45, (int)EntityPosition.Y - 45, 90, 90), new Color(255, 255, 255) * 0.125f);
|
|
}
|
|
|
|
public void DrawTransition(SpriteBatch spriteBatch)
|
|
{
|
|
if (!IsVisible)
|
|
return;
|
|
|
|
_bodyDrawFunction(spriteBatch);
|
|
|
|
if (_drawInstrumentEffect)
|
|
DrawInstrumentEffect(spriteBatch);
|
|
|
|
// draw the shown item
|
|
if (ShowItem != null)
|
|
{
|
|
var itemPosition = EntityPosition.Position + _showItemOffset;
|
|
ItemDrawHelper.DrawItem(spriteBatch, ShowItem, itemPosition, Color.White, 1, true);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void OnKeyChange()
|
|
{
|
|
var strCloak = "cloak_transition";
|
|
var cloakTransition = Game1.GameManager.SaveManager.GetString(strCloak);
|
|
if (cloakTransition == "1")
|
|
{
|
|
_cloakTransitionCounter = 0;
|
|
_cloakPercentage = 0;
|
|
_cloakTransitionOutCounter = 0;
|
|
|
|
Game1.GameManager.SaveManager.RemoveString(strCloak);
|
|
Game1.GameManager.SaveManager.SetString(strCloak, "0");
|
|
|
|
CurrentState = State.CloakShow0;
|
|
}
|
|
|
|
// play animation?
|
|
var strAnimation = "link_direction";
|
|
var newDirection = Game1.GameManager.SaveManager.GetString(strAnimation);
|
|
if (!string.IsNullOrEmpty(newDirection))
|
|
{
|
|
Direction = int.Parse(newDirection);
|
|
UpdateAnimation();
|
|
Game1.GameManager.SaveManager.SetString(strAnimation, null);
|
|
}
|
|
|
|
// start moving? [set:link_move,-16,32]
|
|
var moveValue = Game1.GameManager.SaveManager.GetString("link_move");
|
|
if (!string.IsNullOrEmpty(moveValue))
|
|
{
|
|
var split = moveValue.Split(',');
|
|
var directionX = float.Parse(split[0], CultureInfo.InvariantCulture);
|
|
var directionY = float.Parse(split[1], CultureInfo.InvariantCulture);
|
|
|
|
var velocity = new Vector2(directionX, directionY);
|
|
_body.VelocityTarget = velocity;
|
|
Direction = AnimationHelper.GetDirection(velocity);
|
|
_isWalking = true;
|
|
|
|
Game1.GameManager.SaveManager.SetString("link_move_collision", "0");
|
|
Game1.GameManager.SaveManager.RemoveString("link_move");
|
|
}
|
|
|
|
var idleValue = Game1.GameManager.SaveManager.GetString("link_idle");
|
|
if (!string.IsNullOrEmpty(idleValue))
|
|
{
|
|
CurrentState = State.Idle;
|
|
Game1.GameManager.SaveManager.RemoveString("link_idle");
|
|
}
|
|
|
|
var hideHudValue = Game1.GameManager.SaveManager.GetString("hide_hud");
|
|
if (!string.IsNullOrEmpty(hideHudValue))
|
|
{
|
|
Game1.GameManager.InGameOverlay.HideHud(true);
|
|
Game1.GameManager.SaveManager.RemoveString("hide_hud");
|
|
}
|
|
|
|
// start moving? [set:link_push,-16,0,200]
|
|
var pushValue = Game1.GameManager.SaveManager.GetString("link_push");
|
|
if (!string.IsNullOrEmpty(pushValue))
|
|
{
|
|
var split = pushValue.Split(',');
|
|
|
|
// init movement
|
|
if (split.Length == 1)
|
|
{
|
|
_pushStart = EntityPosition.Position;
|
|
_pushEnd = new Vector2(80, 94);
|
|
_pushTime = int.Parse(split[0]);
|
|
}
|
|
else
|
|
{
|
|
var offsetX = float.Parse(split[0], CultureInfo.InvariantCulture);
|
|
var offsetY = float.Parse(split[1], CultureInfo.InvariantCulture);
|
|
_pushStart = EntityPosition.Position;
|
|
_pushEnd = _pushStart + new Vector2(offsetX, offsetY);
|
|
_pushTime = int.Parse(split[2]);
|
|
}
|
|
|
|
_pushCounter = 0;
|
|
CurrentState = State.Pushed;
|
|
|
|
Game1.GameManager.SaveManager.RemoveString("link_push");
|
|
}
|
|
|
|
// link animation
|
|
var animationValue = Game1.GameManager.SaveManager.GetString("link_animation");
|
|
if (!string.IsNullOrEmpty(animationValue))
|
|
{
|
|
Animation.Play(animationValue);
|
|
CurrentState = State.Sequence;
|
|
Game1.GameManager.SaveManager.RemoveString("link_animation");
|
|
}
|
|
|
|
var linkFinal = Game1.GameManager.SaveManager.GetString("link_final");
|
|
if (!string.IsNullOrEmpty(linkFinal))
|
|
{
|
|
_finalIndex = 0;
|
|
_finalSeqCounter = 1500;
|
|
Animation.Play("final_stand_down");
|
|
CurrentState = State.FinalInstruments;
|
|
Game1.GameManager.SetMusic(62, 2);
|
|
Game1.GameManager.SaveManager.RemoveString("link_final");
|
|
}
|
|
|
|
// start diving?
|
|
var diveValue = Game1.GameManager.SaveManager.GetString("link_dive");
|
|
if (!string.IsNullOrEmpty(diveValue))
|
|
{
|
|
_diveCounter = int.Parse(diveValue);
|
|
CurrentState = State.Swimming;
|
|
Game1.GameManager.SaveManager.RemoveString("link_dive");
|
|
}
|
|
|
|
// boomerang trading
|
|
// can be exchanged for: shovel, feather
|
|
var boomerangValue = Game1.GameManager.SaveManager.GetString("boomerang_trade");
|
|
if (!string.IsNullOrEmpty(boomerangValue))
|
|
{
|
|
Game1.GameManager.SaveManager.RemoveString("boomerang_trade");
|
|
|
|
if (Game1.GameManager.Equipment[1] != null &&
|
|
(Game1.GameManager.Equipment[1].Name == "shovel" ||
|
|
Game1.GameManager.Equipment[1].Name == "feather" ||
|
|
Game1.GameManager.Equipment[1].Name == "magicRod" ||
|
|
Game1.GameManager.Equipment[1].Name == "hookshot"))
|
|
{
|
|
Game1.GameManager.SaveManager.SetString("tradded_item", Game1.GameManager.Equipment[1].Name);
|
|
Game1.GameManager.Equipment[1] = null;
|
|
|
|
Game1.GameManager.StartDialogPath("npc_hidden_boomerang");
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.StartDialogPath("npc_hidden_reject");
|
|
}
|
|
}
|
|
|
|
var boomerangReturnValue = Game1.GameManager.SaveManager.GetString("boomerang_trade_return");
|
|
if (!string.IsNullOrEmpty(boomerangReturnValue))
|
|
{
|
|
Game1.GameManager.SaveManager.RemoveString("boomerang_trade_return");
|
|
|
|
// remove the boomerang
|
|
Game1.GameManager.RemoveItem("boomerang", 1);
|
|
|
|
// return the traded item
|
|
var tradedItem = Game1.GameManager.SaveManager.GetString("tradded_item");
|
|
var item = new GameItemCollected(tradedItem);
|
|
MapManager.ObjLink.PickUpItem(item, true);
|
|
_pickupDialogOverride = "npc_hidden_4";
|
|
}
|
|
|
|
var spawnGhostValue = Game1.GameManager.SaveManager.GetString(_spawnGhostKey);
|
|
if (!string.IsNullOrEmpty(spawnGhostValue))
|
|
{
|
|
_spawnGhost = true;
|
|
}
|
|
}
|
|
|
|
private void OnMoveCollision(Values.BodyCollision collision)
|
|
{
|
|
// knockback
|
|
if (CurrentState == State.Idle && _wasBootsRunning)
|
|
{
|
|
var knockBack = false;
|
|
|
|
if ((collision & Values.BodyCollision.Horizontal) != 0 && Direction % 2 == 0)
|
|
{
|
|
var dirX = (collision & Values.BodyCollision.Left) != 0 ? -1 : 1;
|
|
_body.Velocity.X = -dirX;
|
|
Game1.GameManager.ShakeScreen(750, 2, 1, 5.5f, 2.5f, dirX, 1);
|
|
knockBack = true;
|
|
}
|
|
if ((collision & Values.BodyCollision.Vertical) != 0 && Direction % 2 != 0)
|
|
{
|
|
var dirY = (collision & Values.BodyCollision.Top) != 0 ? -1 : 1;
|
|
_body.Velocity.Y = -dirY;
|
|
Game1.GameManager.ShakeScreen(750, 1, 2, 2.5f, 5.5f, 1, dirY);
|
|
knockBack = true;
|
|
}
|
|
|
|
if (knockBack)
|
|
{
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
_body.Velocity.Z = 2.0f;
|
|
CurrentState = State.BootKnockback;
|
|
|
|
var damageOrigin = BodyRectangle.Center;
|
|
var damageBox = _body.BodyBox.Box;
|
|
damageBox.X += AnimationHelper.DirectionOffset[Direction].X;
|
|
damageBox.Y += AnimationHelper.DirectionOffset[Direction].Y;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-11-0B");
|
|
|
|
Map.Objects.Hit(this, damageOrigin, damageBox, HitType.PegasusBootsPush, 0, false);
|
|
}
|
|
}
|
|
|
|
// what is this?
|
|
if ((collision & Values.BodyCollision.Floor) != 0)
|
|
{
|
|
_moveVelocity = _lastMoveVelocity * 0.5f;
|
|
_lastBaseMoveVelocity = _moveVelocity;
|
|
}
|
|
|
|
if (CurrentState == State.BootKnockback &&
|
|
(collision & Values.BodyCollision.Floor) != 0)
|
|
{
|
|
CurrentState = State.Idle;
|
|
_body.Velocity.Z = 0;
|
|
}
|
|
|
|
if (Is2DMode)
|
|
OnMoveCollision2D(collision);
|
|
else
|
|
{
|
|
// colliding horizontally or vertically? -> start pushing
|
|
if (CurrentState == State.Idle && _isWalking && (
|
|
(collision & Values.BodyCollision.Horizontal) != 0 && (Direction == 0 || Direction == 2) ||
|
|
(collision & Values.BodyCollision.Vertical) != 0 && (Direction == 1 || Direction == 3)))
|
|
{
|
|
var box = _body.BodyBox.Box;
|
|
// offset by one in the walk direction
|
|
box.X += AnimationHelper.DirectionOffset[Direction].X;
|
|
box.Y += AnimationHelper.DirectionOffset[Direction].Y;
|
|
var cBox = Box.Empty;
|
|
var outBox = Box.Empty;
|
|
// check if the object we are walking into is actually an object where the push animation should be played
|
|
if (Map.Objects.Collision(box, cBox, _body.CollisionTypes, Values.CollisionTypes.PushIgnore, Direction, _body.Level, ref outBox))
|
|
CurrentState = State.Pushing;
|
|
}
|
|
|
|
if (CurrentState == State.Swimming)
|
|
{
|
|
if ((collision & Values.BodyCollision.Horizontal) != 0)
|
|
_moveVelocity.X = 0;
|
|
if ((collision & Values.BodyCollision.Vertical) != 0)
|
|
_moveVelocity.Y = 0;
|
|
}
|
|
|
|
// used for scripting (final stript stop at the top of the stairs)
|
|
Game1.GameManager.SaveManager.SetString("link_move_collision", "1");
|
|
|
|
// stop the hit velocity if the are colliding with a wall
|
|
// this was done because the player pushes into the hitVelocity direction
|
|
if ((collision & Values.BodyCollision.Horizontal) != 0 && _body.VelocityTarget.X == 0)
|
|
_hitVelocity.X = 0;
|
|
if ((collision & Values.BodyCollision.Vertical) != 0 && _body.VelocityTarget.Y == 0)
|
|
_hitVelocity.Y = 0;
|
|
|
|
if (CurrentState == State.Charging &&
|
|
((collision & Values.BodyCollision.Left) != 0 && Direction == 0 ||
|
|
(collision & Values.BodyCollision.Top) != 0 && Direction == 1 ||
|
|
(collision & Values.BodyCollision.Right) != 0 && Direction == 2 ||
|
|
(collision & Values.BodyCollision.Bottom) != 0 && Direction == 3))
|
|
{
|
|
if (_swordPokeCounter <= 0)
|
|
{
|
|
IsPoking = true;
|
|
_pokeStart = true;
|
|
|
|
Animation.Play("poke_" + Direction);
|
|
AnimatorWeapons.Play("poke_" + Direction);
|
|
CurrentState = State.Attacking;
|
|
_swordChargeCounter = SwordChargeTime;
|
|
}
|
|
|
|
_swordPokeCounter -= Game1.DeltaTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void OnHolePull(Vector2 direction, float percentage)
|
|
{
|
|
// disable jumping if the player stands on top of a hole
|
|
// if the hole is 14x14 the player should not be able to stand between the holes and jump out of them
|
|
// player 8x10
|
|
// hole area while standing between 4 14x14 holes: 2x10 + 2x8 = 36
|
|
// 1 - 36/80 = 0.55
|
|
if (percentage >= 0.55f)
|
|
_canJump = false;
|
|
}
|
|
|
|
private void Update3D()
|
|
{
|
|
_isWalking = false;
|
|
WasHoleReset = false;
|
|
|
|
if (CurrentState == State.Intro)
|
|
{
|
|
var walkVelocity = ControlHandler.GetMoveVector2();
|
|
|
|
if (Animation.CurrentAnimation.Id == "intro_sit" &&
|
|
!Game1.GameManager.InGameOverlay.TextboxOverlay.IsOpen && walkVelocity.Length() > Values.ControllerDeadzone)
|
|
{
|
|
CurrentState = State.Idle;
|
|
Direction = 2;
|
|
StartRailJump(EntityPosition.Position + new Vector2(12, 4), 1, 1);
|
|
Animation.Play("intro_jump");
|
|
|
|
Game1.GameManager.SaveManager.SetString("played_intro", "1");
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
// finished jumping into the bed?
|
|
if (_startBedTransition && CurrentState == State.Idle)
|
|
{
|
|
CurrentState = State.BedTransition;
|
|
|
|
_startBedTransition = false;
|
|
|
|
Animation.Play("bed");
|
|
}
|
|
|
|
if (CurrentState == State.BedTransition)
|
|
return;
|
|
|
|
if (CurrentState == State.SwordShow0)
|
|
{
|
|
if (!Animation.IsPlaying)
|
|
{
|
|
Animation.Play("show2");
|
|
_showSwordLv2Counter = 500;
|
|
CurrentState = State.SwordShow1;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-07-07");
|
|
|
|
var animation = new ObjAnimator(Map, 0, 0, Values.LayerTop, "Particles/swordPoke", "run", true);
|
|
animation.EntityPosition.Set(new Vector2(
|
|
BodyRectangle.X,
|
|
EntityPosition.Y - EntityPosition.Z - 30));
|
|
Map.Objects.SpawnObject(animation);
|
|
}
|
|
else
|
|
return;
|
|
}
|
|
else if (CurrentState == State.SwordShow1)
|
|
{
|
|
_showSwordLv2Counter -= Game1.DeltaTime;
|
|
if (_showSwordLv2Counter < 0)
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
if (_isRafting && (CurrentState == State.Rafting || CurrentState == State.Charging))
|
|
{
|
|
var moveVelocity = ControlHandler.GetMoveVector2();
|
|
|
|
var moveVelocityLength = moveVelocity.Length();
|
|
if (moveVelocityLength > 1)
|
|
moveVelocity.Normalize();
|
|
|
|
if (moveVelocityLength > Values.ControllerDeadzone)
|
|
{
|
|
_isWalking = true;
|
|
_objRaft.TargetVelocity(moveVelocity * 0.5f);
|
|
|
|
if (CurrentState != State.Charging)
|
|
{
|
|
var vectorDirection = ToDirection(moveVelocity);
|
|
Direction = vectorDirection;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_isFlying && CurrentState == State.Carrying)
|
|
{
|
|
var moveVelocity = ControlHandler.GetMoveVector2();
|
|
|
|
var moveVelocityLength = moveVelocity.Length();
|
|
if (moveVelocityLength > 1)
|
|
moveVelocity.Normalize();
|
|
|
|
if (moveVelocityLength > Values.ControllerDeadzone)
|
|
{
|
|
_objRooster.TargetVelocity(moveVelocity, 0.5f, Direction);
|
|
|
|
var vectorDirection = ToDirection(moveVelocity);
|
|
Direction = vectorDirection;
|
|
}
|
|
}
|
|
|
|
// we need to prevent overlays from being opened because they do not stop the music and it would run out of sync
|
|
if ((ShowItem != null && ShowItem.Name.StartsWith("instrument")) ||
|
|
CurrentState == State.ShowInstrumentPart0 ||
|
|
CurrentState == State.ShowInstrumentPart1 ||
|
|
CurrentState == State.ShowInstrumentPart2 ||
|
|
CurrentState == State.ShowInstrumentPart3)
|
|
Game1.GameManager.InGameOverlay.DisableInventoryToggle = true;
|
|
|
|
if (CurrentState == State.ShowInstrumentPart0)
|
|
{
|
|
// is the sound effect still playing?
|
|
if (_instrumentPickupTime + 7500 < Game1.TotalGameTime)
|
|
{
|
|
Game1.GameManager.SetMusic(_instrumentMusicIndex[_instrumentIndex], 2);
|
|
Game1.GbsPlayer.Play();
|
|
Game1.GbsPlayer.SoundGenerator.SetStopTime(8);
|
|
CurrentState = State.ShowInstrumentPart1;
|
|
}
|
|
}
|
|
else if (CurrentState == State.ShowInstrumentPart1)
|
|
{
|
|
_instrumentCounter += Game1.DeltaTime;
|
|
|
|
if (_instrumentCounter > 3500)
|
|
{
|
|
_drawInstrumentEffect = true;
|
|
Game1.GameManager.PlaySoundEffect("D360-43-2B", false);
|
|
}
|
|
|
|
if (Game1.GbsPlayer.SoundGenerator.WasStopped && Game1.GbsPlayer.SoundGenerator.FinishedPlaying())
|
|
{
|
|
Game1.GameManager.SetMusic(-1, 0);
|
|
Game1.GameManager.SetMusic(-1, 2);
|
|
Game1.GameManager.PlaySoundEffect("D378-44-2C");
|
|
|
|
_instrumentCounter = 0;
|
|
CurrentState = State.ShowInstrumentPart2;
|
|
}
|
|
}
|
|
else if (CurrentState == State.ShowInstrumentPart2)
|
|
{
|
|
_instrumentCounter += Game1.DeltaTime;
|
|
|
|
var transitionSystem = (MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)];
|
|
transitionSystem.SetColorMode(Color.White, MathHelper.Clamp(_instrumentCounter / 500f, 0, 1));
|
|
|
|
if (_instrumentCounter > 2500)
|
|
{
|
|
Direction = 3;
|
|
UpdateAnimation();
|
|
|
|
CurrentState = State.ShowInstrumentPart3;
|
|
ShowItem = null;
|
|
_drawInstrumentEffect = false;
|
|
|
|
Game1.GameManager.StartDialogPath($"instrument{_instrumentIndex}Collected");
|
|
}
|
|
}
|
|
else if (CurrentState == State.ShowInstrumentPart3)
|
|
{
|
|
MapTransitionStart = EntityPosition.Position;
|
|
MapTransitionEnd = MapTransitionStart;
|
|
TransitionOutWalking = false;
|
|
|
|
EndPickup();
|
|
|
|
// append a map change
|
|
((MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)]).AppendMapChange(
|
|
"overworld.map", $"d{_instrumentIndex + 1}Finished", false, true, Color.White, true);
|
|
}
|
|
|
|
if (CurrentState == State.Teleporting)
|
|
{
|
|
if (_teleportCounterFull < 1250 || Direction <= 2)
|
|
_teleportCounter += Game1.DeltaTime;
|
|
|
|
_teleportCounterFull += Game1.DeltaTime;
|
|
var rotationSpeed = 150 - (float)Math.Sin((_teleportCounterFull / 2000f) * Math.PI) * 50;
|
|
if (_teleportCounter > rotationSpeed)
|
|
{
|
|
_teleportCounter -= rotationSpeed;
|
|
Direction = (Direction + 1) % 4;
|
|
UpdateAnimation();
|
|
}
|
|
|
|
var transitionSystem = (MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)];
|
|
|
|
if (_teleportState == 0 && _teleportCounterFull >= 1250)
|
|
{
|
|
if (_teleporter != null)
|
|
{
|
|
_teleportState = 1;
|
|
|
|
EntityPosition.Set(_teleporter.TeleportPosition);
|
|
_teleporter.Lock();
|
|
|
|
var goalPosition = Game1.GameManager.MapManager.GetCameraTarget();
|
|
MapManager.Camera.SoftUpdate(goalPosition);
|
|
}
|
|
else if (Direction == 3 && _teleportCounterFull >= 1450)
|
|
{
|
|
MapTransitionStart = EntityPosition.Position;
|
|
MapTransitionEnd = EntityPosition.Position;
|
|
TransitionOutWalking = false;
|
|
|
|
transitionSystem.AppendMapChange(_teleportMap, _teleporterId, false, true, Color.White, true);
|
|
}
|
|
|
|
transitionSystem.SetColorMode(Color.White, 1);
|
|
}
|
|
|
|
var fadeOutTime = 250.0f;
|
|
var fadeoutStart = 1750;
|
|
var fadeoutEnd = 1750 + fadeOutTime;
|
|
|
|
// fading in
|
|
if (_teleportCounterFull >= 750 && _teleportCounterFull < 1250)
|
|
{
|
|
transitionSystem.SetColorMode(Color.White, (_teleportCounterFull - 750) / 500f);
|
|
}
|
|
// fading out
|
|
else if (_teleportState == 1 && _teleportCounterFull >= fadeoutStart && _teleportCounterFull < fadeoutEnd)
|
|
{
|
|
transitionSystem.SetColorMode(Color.White, 1 - (_teleportCounterFull - fadeoutStart) / fadeOutTime);
|
|
}
|
|
// finished?
|
|
else if (_teleportState == 1 && _teleportCounterFull >= fadeoutEnd)
|
|
{
|
|
_drawBody.Layer = Values.LayerPlayer;
|
|
transitionSystem.SetColorMode(Color.White, 0);
|
|
CurrentState = State.Idle;
|
|
}
|
|
}
|
|
|
|
UpdateSwimming();
|
|
|
|
UpdateIgnoresZ();
|
|
|
|
// hinox should throw the player farther than normal
|
|
if (CurrentState == State.Stunned)
|
|
_body.DragAir = 0.95f;
|
|
else
|
|
_body.DragAir = 0.9f;
|
|
|
|
// save the last position the player is grounded to use for the reset position if the player drowns
|
|
if (CurrentState != State.Jumping && CurrentState != State.Drowning && CurrentState != State.Drowned && _body.IsGrounded)
|
|
{
|
|
var bodyCenter = new Vector2(EntityPosition.X, EntityPosition.Y - _body.Height / 2f);
|
|
// center the position
|
|
// can lead to the position being inside something
|
|
bodyCenter.X = (int)(bodyCenter.X / 16) * 16 + 8;
|
|
bodyCenter.Y = (int)(bodyCenter.Y / 16) * 16 + 8 + _body.Height / 2f;
|
|
|
|
// found new reset position?
|
|
if (!Map.GetFieldState(bodyCenter).HasFlag(MapStates.FieldStates.DeepWater))
|
|
{
|
|
var bodyBox = new Box(
|
|
bodyCenter.X + _body.OffsetX,
|
|
bodyCenter.Y + _body.OffsetY, 0, _body.Width, _body.Height, _body.Depth);
|
|
var cBox = Box.Empty;
|
|
|
|
// check it the player is not standing inside something
|
|
if (!Map.Objects.Collision(bodyBox, Box.Empty, _body.CollisionTypes | Values.CollisionTypes.DrownExclude, 0, 0, ref cBox))
|
|
_drownResetPosition = bodyCenter;
|
|
}
|
|
}
|
|
|
|
// walk
|
|
UpdateWalking();
|
|
|
|
if (CurrentState == State.Drowning)
|
|
{
|
|
if (_drownCounter < 300)
|
|
{
|
|
_body.Velocity = Vector3.Zero;
|
|
// align the player to the pixel grid
|
|
EntityPosition.Set(new Vector2(
|
|
MathF.Round(EntityPosition.X), MathF.Round(EntityPosition.Y)));
|
|
}
|
|
|
|
_drownCounter -= Game1.DeltaTime;
|
|
if (_drownCounter <= 0)
|
|
{
|
|
IsVisible = false;
|
|
CurrentState = State.Drowned;
|
|
_drownResetCounter = 500;
|
|
}
|
|
}
|
|
|
|
if (CurrentState == State.Drowned)
|
|
{
|
|
_drownResetCounter -= Game1.DeltaTime;
|
|
if (_drownResetCounter <= 0)
|
|
{
|
|
CurrentState = State.Idle;
|
|
CanWalk = true;
|
|
IsVisible = true;
|
|
|
|
_hitCount = CooldownTime;
|
|
Game1.GameManager.CurrentHealth -= 2;
|
|
|
|
_body.CurrentFieldState = MapStates.FieldStates.None;
|
|
EntityPosition.Set(_drownResetPosition);
|
|
}
|
|
}
|
|
|
|
if (CurrentState == State.Swimming)
|
|
{
|
|
if (_diveCounter > -100)
|
|
{
|
|
_diveCounter -= Game1.DeltaTime;
|
|
|
|
// stop diving
|
|
if (ControlHandler.ButtonPressed(CButtons.B))
|
|
_diveCounter = 0;
|
|
}
|
|
// start diving
|
|
else if (ControlHandler.ButtonPressed(CButtons.B))
|
|
{
|
|
StartDiving(1500);
|
|
}
|
|
|
|
if (_swimBoostCount > -300)
|
|
_swimBoostCount -= Game1.DeltaTime;
|
|
else if (ControlHandler.ButtonPressed(CButtons.A))
|
|
_swimBoostCount = 300;
|
|
|
|
if (_swimBoostCount > 0)
|
|
_moveVelocity *= SwimSpeedA;
|
|
else
|
|
_moveVelocity *= SwimSpeed;
|
|
|
|
var distance = _moveVelocity - _swimVelocity;
|
|
var length = distance.Length();
|
|
if (distance != Vector2.Zero)
|
|
distance.Normalize();
|
|
|
|
if (length < 0.045f)
|
|
_swimVelocity = _moveVelocity;
|
|
else
|
|
_swimVelocity += distance * (_swimBoostCount > 0 ? 0.06f : 0.045f) * Game1.TimeMultiplier;
|
|
|
|
_moveVelocity = _swimVelocity;
|
|
}
|
|
|
|
// slows down the walk movement when the player is hit
|
|
var moveMultiplier = MathHelper.Clamp(1f - _hitVelocity.Length(), 0, 1);
|
|
|
|
// move the player
|
|
if (CurrentState != State.Hookshot)
|
|
{
|
|
_body.VelocityTarget = _moveVelocity * moveMultiplier + _hitVelocity;
|
|
}
|
|
|
|
LastMoveVector = _moveVelocity;
|
|
_moveVelocity = Vector2.Zero;
|
|
|
|
if (_hitCount > 0 && _hitVelocity.Length() > 0.05f * Game1.TimeMultiplier)
|
|
{
|
|
var hitNormal = _hitVelocity;
|
|
hitNormal.Normalize();
|
|
|
|
var slowDownAmount = 0.05f + MathHelper.Clamp(_hitVelocity.Length() / 25f, 0, 0.05f);
|
|
|
|
_hitVelocity -= hitNormal * slowDownAmount * Game1.TimeMultiplier;
|
|
}
|
|
else
|
|
_hitVelocity = Vector2.Zero;
|
|
|
|
// update the jump logic
|
|
UpdateJump();
|
|
|
|
// hole falling logic
|
|
{
|
|
// update position used to reset the player if he falls into a hole
|
|
UpdateSavePosition();
|
|
|
|
// change the room?
|
|
if (_isFallingIntoHole)
|
|
{
|
|
_holeFallCounter -= Game1.DeltaTime;
|
|
|
|
if (_holeFallCounter <= 0)
|
|
{
|
|
_isFallingIntoHole = false;
|
|
|
|
if (HoleResetRoom != null)
|
|
{
|
|
// append a map change
|
|
((MapTransitionSystem)Game1.GameManager.GameSystems[
|
|
typeof(MapTransitionSystem)]).AppendMapChange(HoleResetRoom, HoleResetEntryId);
|
|
}
|
|
// teleport on hole fall?
|
|
else if (HoleTeleporterId >= 0)
|
|
{
|
|
_holeTeleportCounter = 0;
|
|
CurrentState = State.TeleporterUpWait;
|
|
}
|
|
}
|
|
}
|
|
|
|
HoleTeleporterId = -1;
|
|
|
|
// finished falling down the hole?
|
|
if (CurrentState == State.Falling && !Animation.IsPlaying)
|
|
OnHoleReset();
|
|
}
|
|
|
|
// update links animation
|
|
UpdateAnimation();
|
|
|
|
UpdateGhostSpawn();
|
|
|
|
// stop push animation
|
|
if (CurrentState == State.Pushing)
|
|
CurrentState = State.Idle;
|
|
|
|
_lastFieldState = _body.CurrentFieldState;
|
|
}
|
|
|
|
private void UpdateSwimming()
|
|
{
|
|
// we cant use the field state of the body because the raft updates the state while exiting
|
|
var fieldState = SystemBody.GetFieldState(_body);
|
|
|
|
// start/stop swimming or drowning
|
|
if (!_isRafting && !_isFlying && fieldState.HasFlag(MapStates.FieldStates.DeepWater) && CurrentState != State.Dying)
|
|
{
|
|
if (CurrentState != State.Jumping && _body.IsGrounded && CurrentState != State.PickingUp)
|
|
{
|
|
ReleaseCarriedObject();
|
|
var inLava = fieldState.HasFlag(MapStates.FieldStates.Lava);
|
|
|
|
if ((HasFlippers && !inLava) && CurrentState != State.Swimming)
|
|
{
|
|
CurrentState = State.Swimming;
|
|
|
|
// only push the player if he walks into the water and does not jump
|
|
if (!_lastFieldState.HasFlag(fieldState))
|
|
_body.Velocity = new Vector3(_body.VelocityTarget.X, _body.VelocityTarget.Y, 0) * 0.75f;
|
|
|
|
// splash effect
|
|
var splashAnimator = new ObjAnimator(Map, 0, 0, 0, 3, Values.LayerPlayer, "Particles/splash", "idle", true);
|
|
splashAnimator.EntityPosition.Set(new Vector2(
|
|
_body.Position.X + _body.OffsetX + _body.Width / 2f,
|
|
_body.Position.Y + _body.OffsetY + _body.Height - _body.Position.Z - 6));
|
|
Map.Objects.SpawnObject(splashAnimator);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-14-0E");
|
|
|
|
_diveCounter = 0;
|
|
_swimBoostCount = 0;
|
|
_swimVelocity = Vector2.Zero;
|
|
}
|
|
else if (!HasFlippers || inLava)
|
|
{
|
|
if (CurrentState != State.Drowning && CurrentState != State.Drowned)
|
|
{
|
|
// only push the player if he walks into the water and does not jump
|
|
if (!_lastFieldState.HasFlag(fieldState))
|
|
_body.Velocity = new Vector3(_body.VelocityTarget.X, _body.VelocityTarget.Y, 0) * 0.5f;
|
|
|
|
// splash effect
|
|
var splashAnimator = new ObjAnimator(Map, 0, 0, 0, 3, Values.LayerPlayer, "Particles/splash", "idle", true);
|
|
splashAnimator.EntityPosition.Set(new Vector2(
|
|
_body.Position.X + _body.OffsetX + _body.Width / 2f,
|
|
_body.Position.Y + _body.OffsetY + _body.Height - _body.Position.Z - 6));
|
|
Map.Objects.SpawnObject(splashAnimator);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D370-03-03");
|
|
|
|
CurrentState = State.Drowning;
|
|
_drownCounter = 650;
|
|
|
|
// blink in lava
|
|
_hitCount = inLava ? CooldownTime : 0;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (CurrentState == State.Swimming && (!IsTransitioning || !Map.Is2dMap))
|
|
CurrentState = State.Idle;
|
|
|
|
if (CurrentState == State.Swimming)
|
|
{
|
|
EntityPosition.Z = 0;
|
|
_body.IsGrounded = true;
|
|
}
|
|
}
|
|
|
|
private void UpdateIgnoresZ()
|
|
{
|
|
if (CurrentState == State.Swimming ||
|
|
CurrentState == State.Hookshot ||
|
|
CurrentState == State.TeleporterUp ||
|
|
CurrentState == State.TeleportFallWait || _isFlying || _isGrabbed || _isClimbing)
|
|
_body.IgnoresZ = true;
|
|
else
|
|
_body.IgnoresZ = false;
|
|
}
|
|
|
|
private void UpdateWalking()
|
|
{
|
|
if (CurrentState != State.Idle && (CurrentState != State.Carrying || _isFlying) && CurrentState != State.Charging && CurrentState != State.Swimming &&
|
|
CurrentState != State.CarryingItem && (CurrentState != State.MagicRod || _body.IsGrounded) && (CurrentState != State.Jumping || _railJump) &&
|
|
CurrentState != State.Pushing && CurrentState != State.Blocking && CurrentState != State.Attacking ||
|
|
!CanWalk || _isRafting) return;
|
|
|
|
var walkVelocity = Vector2.Zero;
|
|
if (!_isLocked && (CurrentState != State.Attacking || !_body.IsGrounded))
|
|
walkVelocity = ControlHandler.GetMoveVector2();
|
|
|
|
var walkVelLength = walkVelocity.Length();
|
|
if (walkVelLength > 1)
|
|
walkVelocity.Normalize();
|
|
|
|
var vectorDirection = ToDirection(walkVelocity);
|
|
|
|
if (_bootsRunning && (walkVelLength < Values.ControllerDeadzone || vectorDirection != (Direction + 2) % 4))
|
|
{
|
|
if (!_bootsStop)
|
|
{
|
|
_moveVelocity = AnimationHelper.DirectionOffset[Direction] * BootsRunningSpeed;
|
|
|
|
// can move up or down while running
|
|
if (Direction % 2 != 0)
|
|
_moveVelocity.X += walkVelocity.X;
|
|
else if (Direction % 2 == 0)
|
|
_moveVelocity.Y += walkVelocity.Y;
|
|
}
|
|
}
|
|
else if (walkVelLength > Values.ControllerDeadzone)
|
|
{
|
|
_bootsCounter %= _bootsParticleTime;
|
|
_bootsRunning = false;
|
|
|
|
#if DEBUG
|
|
if (InputHandler.KeyDown(Keys.LeftShift))
|
|
walkVelocity *= 0.25f;
|
|
#endif
|
|
|
|
// slow down in the grass
|
|
if (_body.CurrentFieldState.HasFlag(MapStates.FieldStates.Grass) && _body.IsGrounded)
|
|
_currentWalkSpeed *= 0.8f;
|
|
|
|
// slow down in the water
|
|
if (_body.CurrentFieldState.HasFlag(MapStates.FieldStates.Water) && _body.IsGrounded)
|
|
{
|
|
_currentWalkSpeed *= 0.8f;
|
|
|
|
_waterSoundCounter += Game1.DeltaTime;
|
|
if (_waterSoundCounter > 250)
|
|
{
|
|
_waterSoundCounter -= 250;
|
|
Game1.GameManager.PlaySoundEffect("D360-14-0E", false);
|
|
}
|
|
}
|
|
|
|
// do not walk when trapped
|
|
if (!_isTrapped)
|
|
{
|
|
_isWalking = true;
|
|
|
|
if (_body.IsGrounded)
|
|
{
|
|
// after hitting the ground we still have _lastMoveVelocity
|
|
if (!_body.WasGrounded)
|
|
_moveVelocity = Vector2.Zero;
|
|
|
|
_moveVelocity += walkVelocity * _currentWalkSpeed;
|
|
}
|
|
}
|
|
|
|
// update the direction the player is facing
|
|
if (CurrentState != State.Attacking && CurrentState != State.Charging)
|
|
Direction = vectorDirection;
|
|
}
|
|
|
|
_lastBaseMoveVelocity = _moveVelocity;
|
|
|
|
// when we walk of a cliff set the air move vector
|
|
// we need to make sure that the player did not started jumping
|
|
if (!_startedJumping && !_hasStartedJumping && _body.WasGrounded && !_body.IsGrounded)
|
|
_lastMoveVelocity = _moveVelocity;
|
|
|
|
// the player has momentum when he is in the air and can not be controlled directly like on the ground
|
|
if (!_body.IsGrounded || _body.Velocity.Z > 0)
|
|
{
|
|
var distance = (_lastMoveVelocity - walkVelocity * _currentWalkSpeed).Length();
|
|
|
|
// trying to move in the air? => slowly change the direction in the air
|
|
if (distance > 0 && walkVelocity != Vector2.Zero)
|
|
{
|
|
var amount = Math.Clamp((0.05f / distance) * Game1.TimeMultiplier, 0, 1);
|
|
_lastMoveVelocity = Vector2.Lerp(_lastMoveVelocity, walkVelocity * _currentWalkSpeed, amount);
|
|
}
|
|
|
|
_moveVelocity = _lastMoveVelocity;
|
|
}
|
|
}
|
|
|
|
private void UpdateAnimation()
|
|
{
|
|
if (Game1.GameManager.UseShockEffect)
|
|
return;
|
|
|
|
var shieldString = Game1.GameManager.ShieldLevel == 2 ? "ms_" : "s_";
|
|
if (!CarryShield)
|
|
shieldString = "_";
|
|
|
|
if (_bootsHolding || _bootsRunning)
|
|
{
|
|
if (!_bootsRunning)
|
|
Animation.Play("walk" + shieldString + Direction);
|
|
else
|
|
{
|
|
// run while blocking with the shield
|
|
Animation.Play((CarryShield ? "walkb" : "walk") + shieldString + Direction);
|
|
}
|
|
|
|
Animation.SpeedMultiplier = 2.0f;
|
|
return;
|
|
}
|
|
|
|
Animation.SpeedMultiplier = 1.0f;
|
|
|
|
if (CurrentState == State.Idle && !_isWalking ||
|
|
CurrentState == State.Charging && !_isWalking ||
|
|
CurrentState == State.Rafting && !_isWalking ||
|
|
CurrentState == State.Teleporting ||
|
|
CurrentState == State.ShowInstrumentPart3 ||
|
|
CurrentState == State.TeleportFall ||
|
|
CurrentState == State.TeleporterUp ||
|
|
CurrentState == State.FallRotateEntry)
|
|
Animation.Play("stand" + shieldString + Direction);
|
|
else if ((
|
|
CurrentState == State.Idle ||
|
|
CurrentState == State.Charging ||
|
|
CurrentState == State.Rafting) && _isWalking)
|
|
Animation.Play("walk" + shieldString + Direction);
|
|
else if (CurrentState == State.Blocking)
|
|
Animation.Play((!_isWalking ? "standb" : "walkb") + shieldString + Direction);
|
|
else if ((CurrentState == State.Carrying || CurrentState == State.CarryingItem) && !_isFlying)
|
|
Animation.Play((!_isWalking ? "standc_" : "walkc_") + Direction);
|
|
else if (CurrentState == State.Carrying && _isFlying)
|
|
Animation.Play("flying_" + Direction);
|
|
else if (CurrentState == State.Pushing)
|
|
Animation.Play("push_" + Direction);
|
|
else if (CurrentState == State.Grabbing)
|
|
Animation.Play("grab_" + Direction);
|
|
else if (CurrentState == State.Pulling)
|
|
Animation.Play("pull_" + Direction);
|
|
else if (CurrentState == State.Swimming)
|
|
{
|
|
Animation.Play(_diveCounter > 0 ? "dive" : "swim_" + Direction);
|
|
|
|
if (_swimVelocity.Length() < 0.1 && !IsTransitioning)
|
|
Animation.IsPlaying = false;
|
|
}
|
|
else if (CurrentState == State.Drowning)
|
|
Animation.Play(_drownCounter > 300 ? "swim_" + Direction : "dive");
|
|
}
|
|
|
|
private void UpdateHeartWarningSound()
|
|
{
|
|
if (Game1.GameManager.CurrentHealth <= 4)
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
|
|
private void UpdateDive()
|
|
{
|
|
_diveCounter -= Game1.DeltaTime;
|
|
}
|
|
|
|
private void UpdateDamageShader()
|
|
{
|
|
if (_hitCount > 0)
|
|
_sprite.SpriteShader = (CooldownTime - _hitCount) % (BlinkTime * 2) < BlinkTime ? Resources.DamageSpriteShader0 : null;
|
|
else
|
|
_sprite.SpriteShader = null;
|
|
}
|
|
|
|
private void UpdateSavePosition()
|
|
{
|
|
var bodyCenter = _body.BodyBox.Box.Center;
|
|
var currentTilePosition = new Point(((int)bodyCenter.X - Map.MapOffsetX * 16) / 160, ((int)bodyCenter.Y - Map.MapOffsetY * 16) / 128);
|
|
var tileDiff = currentTilePosition - _lastTilePosition;
|
|
var newResetPosition = _holeResetPoint;
|
|
_lastTilePosition = currentTilePosition;
|
|
|
|
// update position?
|
|
if (tileDiff != Point.Zero)
|
|
{
|
|
var tileSize = 16;
|
|
_alternativeHoleResetPosition = Vector2.Zero;
|
|
|
|
if (tileDiff.X == 0)
|
|
newResetPosition.X = EntityPosition.X;
|
|
else
|
|
{
|
|
if (tileDiff.X > 0)
|
|
newResetPosition.X = (int)(bodyCenter.X / tileSize) * tileSize;
|
|
else
|
|
newResetPosition.X = (int)(bodyCenter.X / tileSize + 1) * tileSize;
|
|
}
|
|
|
|
if (tileDiff.Y == 0)
|
|
newResetPosition.Y = EntityPosition.Y;
|
|
else
|
|
{
|
|
if (tileDiff.Y > 0)
|
|
newResetPosition.Y = (int)(bodyCenter.Y / tileSize) * tileSize;
|
|
else
|
|
newResetPosition.Y = (int)(bodyCenter.Y / tileSize + 1) * tileSize;
|
|
}
|
|
|
|
// check if there is no hole at the new position
|
|
var bodyBox = new Box(newResetPosition.X + _body.BodyBox.OffsetX, newResetPosition.Y + _body.BodyBox.OffsetY, 0, _body.Width, _body.Height, 8);
|
|
var outBox = Box.Empty;
|
|
if (!Map.Objects.Collision(bodyBox, Box.Empty, Values.CollisionTypes.Hole, 0, 0, ref outBox))
|
|
_holeResetPoint = newResetPosition;
|
|
}
|
|
}
|
|
|
|
public void UpdateSaveLocation()
|
|
{
|
|
MapManager.ObjLink.SaveMap = Map.MapName;
|
|
MapManager.ObjLink.SavePosition = EntityPosition.Position;
|
|
MapManager.ObjLink.SaveDirection = Direction;
|
|
}
|
|
|
|
private void SetHoleResetPosition(Vector2 newResetPosition)
|
|
{
|
|
_holeResetPoint = newResetPosition;
|
|
|
|
var offset = Map != null ? new Point(Map.MapOffsetX, Map.MapOffsetY) : Point.Zero;
|
|
_lastTilePosition = new Point(((int)newResetPosition.X - offset.X * 16) / 160, ((int)newResetPosition.Y - offset.Y * 16) / 128);
|
|
}
|
|
|
|
private void UpdateDrawComponents()
|
|
{
|
|
if (_drawInstrumentEffect)
|
|
_drawBody.Layer = Values.LayerTop;
|
|
else
|
|
_drawBody.Layer = (CurrentState == State.Swimming && _diveCounter > 0) ? Values.LayerBottom : Values.LayerPlayer;
|
|
|
|
if (CurrentState == State.Swimming && _diveCounter > 0 ||
|
|
CurrentState == State.Drowning ||
|
|
CurrentState == State.Drowned ||
|
|
CurrentState == State.BedTransition ||
|
|
_isTrapped)
|
|
_shadowComponent.IsActive = false;
|
|
else
|
|
_shadowComponent.IsActive = true;
|
|
}
|
|
|
|
private void StartDiving(int diveTime)
|
|
{
|
|
// splash effect
|
|
var splashAnimator = new ObjAnimator(Map, 0, 0, 0, 0, Values.LayerTop, "Particles/splash", "idle", true);
|
|
splashAnimator.EntityPosition.Set(new Vector2(
|
|
_body.Position.X + _body.OffsetX + _body.Width / 2f,
|
|
_body.Position.Y + _body.OffsetY + _body.Height - _body.Position.Z - 3));
|
|
Map.Objects.SpawnObject(splashAnimator);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-14-0E");
|
|
|
|
_diveCounter = diveTime;
|
|
}
|
|
|
|
private void OnHoleReset()
|
|
{
|
|
// change the room?
|
|
if (HoleResetRoom != null)
|
|
return;
|
|
|
|
_isFallingIntoHole = false;
|
|
|
|
CurrentState = State.Idle;
|
|
CanWalk = true;
|
|
|
|
_hitCount = CooldownTime;
|
|
Game1.GameManager.InflictDamage(2);
|
|
|
|
MoveToHoleResetPosition();
|
|
}
|
|
|
|
private void MoveToHoleResetPosition()
|
|
{
|
|
WasHoleReset = true;
|
|
EntityPosition.Set(_holeResetPoint);
|
|
|
|
// alternative reset point
|
|
var cBox = Box.Empty;
|
|
if (_alternativeHoleResetPosition != Vector2.Zero &&
|
|
Map.Objects.Collision(_body.BodyBox.Box, Box.Empty, _body.CollisionTypes, 0, 0, ref cBox))
|
|
{
|
|
EntityPosition.Set(_alternativeHoleResetPosition);
|
|
}
|
|
}
|
|
|
|
private bool InteractWithObject()
|
|
{
|
|
var boxSize = 6;
|
|
var interactionBox = new Box(
|
|
EntityPosition.X + _walkDirection[Direction].X * (BodyRectangle.Width / 2 + boxSize / 2) - boxSize / 2,
|
|
BodyRectangle.Center.Y + _walkDirection[Direction].Y * (BodyRectangle.Height / 2 + boxSize / 2) - boxSize / 2, 0,
|
|
boxSize, boxSize, 16);
|
|
|
|
return Map.Objects.InteractWithObject(interactionBox);
|
|
}
|
|
|
|
private void ReturnToIdle()
|
|
{
|
|
// Return to idle or to rafting if that was the player was rafting before
|
|
if (_isRafting)
|
|
CurrentState = State.Rafting;
|
|
else
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
private void UpdateGhostSpawn()
|
|
{
|
|
if (!_spawnGhost || !Map.IsOverworld)
|
|
return;
|
|
|
|
var dungeonEntryPosition = new Vector2(1840, 272);
|
|
var distance = MapManager.ObjLink.EntityPosition.Position - dungeonEntryPosition;
|
|
if (MathF.Abs(distance.X) > 512 || MathF.Abs(distance.Y) > 256)
|
|
{
|
|
_spawnGhost = false;
|
|
Game1.GameManager.SaveManager.RemoveString(_spawnGhostKey);
|
|
Game1.GameManager.CollectItem(new GameItemCollected("ghost") { Count = 1 }, 0);
|
|
UpdateFollower(false);
|
|
_objGhost.StartFollowing();
|
|
}
|
|
}
|
|
|
|
#region item stuff
|
|
|
|
private void UpdateItem()
|
|
{
|
|
if (CurrentState == State.Blocking)
|
|
CurrentState = State.Idle;
|
|
else
|
|
_wasBlocking = false;
|
|
|
|
if (CurrentState == State.Grabbing || CurrentState == State.Pulling)
|
|
CurrentState = State.Idle;
|
|
|
|
_isPulling = false;
|
|
_isHoldingSword = false;
|
|
_bootsHolding = false;
|
|
|
|
if (!_isLocked)
|
|
{
|
|
// interact with object
|
|
if ((CurrentState == State.Idle || CurrentState == State.Pushing || CurrentState == State.Swimming || CurrentState == State.CarryingItem) &&
|
|
ControlHandler.ButtonPressed(CButtons.A) && InteractWithObject())
|
|
InputHandler.ResetInputState();
|
|
|
|
if (_isTrapped && !_trappedDisableItems &&
|
|
(ControlHandler.ButtonPressed(CButtons.A) ||
|
|
ControlHandler.ButtonPressed(CButtons.B) ||
|
|
ControlHandler.ButtonPressed(CButtons.X) ||
|
|
ControlHandler.ButtonPressed(CButtons.Y)))
|
|
{
|
|
_trapInteractionCount--;
|
|
if (_trapInteractionCount <= 0)
|
|
FreeTrappedPlayer();
|
|
}
|
|
|
|
// use/hold item
|
|
if (!DisableItems && (!_isTrapped || !_trappedDisableItems))
|
|
{
|
|
for (var i = 0; i < Values.HandItemSlots; i++)
|
|
{
|
|
if (Game1.GameManager.Equipment[i] != null &&
|
|
ControlHandler.ButtonPressed((CButtons)((int)CButtons.A * Math.Pow(2, i))))
|
|
UseItem(Game1.GameManager.Equipment[i]);
|
|
|
|
if (Game1.GameManager.Equipment[i] != null &&
|
|
ControlHandler.ButtonDown((CButtons)((int)CButtons.A * Math.Pow(2, i))))
|
|
HoldItem(Game1.GameManager.Equipment[i],
|
|
ControlHandler.LastButtonDown((CButtons)((int)CButtons.A * Math.Pow(2, i))));
|
|
}
|
|
}
|
|
}
|
|
|
|
UpdatePegasusBoots();
|
|
|
|
// shield pushing
|
|
if (CurrentState == State.Blocking || _bootsRunning && CarryShield)
|
|
UpdateShieldPush();
|
|
|
|
// pick up animation
|
|
if (CurrentState == State.PreCarrying)
|
|
{
|
|
_preCarryCounter += Game1.DeltaTime;
|
|
|
|
// change the animation of the player depending on where the picked up object is
|
|
if (_preCarryCounter > 100)
|
|
Animation.Play("standc_" + Direction);
|
|
|
|
UpdatePositionCarriedObject(EntityPosition);
|
|
}
|
|
|
|
// stop attacking
|
|
if (CurrentState == State.Attacking && !Animation.IsPlaying)
|
|
{
|
|
_isSwingingSword = false;
|
|
|
|
if (!_isHoldingSword || _swordPoked || _stopCharging)
|
|
ReturnToIdle();
|
|
else
|
|
{
|
|
// start charging sword
|
|
CurrentState = State.Charging;
|
|
AnimatorWeapons.Play("stand_" + Direction);
|
|
_swordPokeCounter = _swordPokeTime;
|
|
}
|
|
}
|
|
|
|
if (CurrentState == State.Charging)
|
|
UpdateCharging();
|
|
|
|
// hit stuff with the sword
|
|
if (CurrentState == State.Attacking || _bootsRunning && CarrySword)
|
|
UpdateAttacking();
|
|
|
|
if (CurrentState == State.PickingUp)
|
|
UpdatePickup();
|
|
|
|
if (!Animation.IsPlaying &&
|
|
(CurrentState == State.Powdering || CurrentState == State.Bombing || CurrentState == State.MagicRod || CurrentState == State.Throwing))
|
|
ReturnToIdle();
|
|
|
|
if (CurrentState == State.Hookshot)
|
|
UpdateHookshot();
|
|
|
|
if (CurrentState == State.Digging)
|
|
UpdateDigging();
|
|
|
|
_wasPulling = _isPulling;
|
|
}
|
|
|
|
private void UseItem(GameItemCollected item)
|
|
{
|
|
switch (item.Name)
|
|
{
|
|
case "sword1":
|
|
case "sword2":
|
|
UseSword();
|
|
break;
|
|
case "feather":
|
|
UseFeather();
|
|
break;
|
|
case "toadstool":
|
|
UseToadstool();
|
|
break;
|
|
case "powder":
|
|
UsePowder();
|
|
break;
|
|
case "bomb":
|
|
UseBomb();
|
|
break;
|
|
case "bow":
|
|
UseArrow();
|
|
break;
|
|
case "shovel":
|
|
UseShovel();
|
|
break;
|
|
case "stonelifter":
|
|
case "stonelifter2":
|
|
UseStoneLifter();
|
|
break;
|
|
case "hookshot":
|
|
UseHookshot();
|
|
break;
|
|
case "boomerang":
|
|
UseBoomerang();
|
|
break;
|
|
case "magicRod":
|
|
UseMagicRod();
|
|
break;
|
|
case "ocarina":
|
|
UseOcarina();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void HoldItem(GameItemCollected item, bool lastKeyDown)
|
|
{
|
|
switch (item.Name)
|
|
{
|
|
case "sword1":
|
|
HoldSword();
|
|
break;
|
|
case "sword2":
|
|
HoldSword();
|
|
break;
|
|
case "shield":
|
|
case "mirrorShield":
|
|
HoldShield(lastKeyDown);
|
|
break;
|
|
case "stonelifter":
|
|
case "stonelifter2":
|
|
HoldStoneLifter();
|
|
break;
|
|
case "pegasusBoots":
|
|
HoldPegasusBoots();
|
|
break;
|
|
}
|
|
}
|
|
|
|
private void UseSword()
|
|
{
|
|
if (CurrentState != State.Idle && CurrentState != State.Pushing && CurrentState != State.Rafting &&
|
|
(CurrentState != State.Jumping || _railJump) && (CurrentState != State.Swimming || !Map.Is2dMap))
|
|
return;
|
|
|
|
var slashSounds = new[] { "D378-02-02", "D378-20-14", "D378-21-15", "D378-24-18" };
|
|
Game1.GameManager.PlaySoundEffect(slashSounds[Game1.RandomNumber.Next(0, 4)]);
|
|
|
|
Animation.Play("attack_" + Direction);
|
|
AnimatorWeapons.Play("attack_" + Direction);
|
|
_swordChargeCounter = SwordChargeTime;
|
|
IsPoking = false;
|
|
_pokeStart = false;
|
|
_stopCharging = false;
|
|
_swordPoked = false;
|
|
_shotSword = false;
|
|
StopRaft();
|
|
|
|
CurrentState = State.Attacking;
|
|
}
|
|
|
|
private void HoldSword()
|
|
{
|
|
_isHoldingSword = true;
|
|
}
|
|
|
|
private void UseFeather()
|
|
{
|
|
if (Is2DMode)
|
|
Jump2D();
|
|
else
|
|
Jump();
|
|
}
|
|
|
|
private void UseToadstool()
|
|
{
|
|
CurrentState = State.ShowToadstool;
|
|
Animation.Play("show2");
|
|
Game1.GameManager.StartDialogPath("toadstool_hole");
|
|
}
|
|
|
|
private void UsePowder()
|
|
{
|
|
if (CurrentState != State.Idle &&
|
|
CurrentState != State.Jumping &&
|
|
CurrentState != State.Rafting &&
|
|
(CurrentState != State.Swimming || !Map.Is2dMap))
|
|
return;
|
|
|
|
// remove one powder from the inventory
|
|
if (!Game1.GameManager.RemoveItem("powder", 1))
|
|
return;
|
|
|
|
var spawnPosition = new Vector2(EntityPosition.X, EntityPosition.Y) + _powderOffset[Direction];
|
|
Map.Objects.SpawnObject(new ObjPowder(Map, spawnPosition.X, spawnPosition.Y, EntityPosition.Z, true));
|
|
|
|
if (CurrentState != State.Jumping)
|
|
{
|
|
StopRaft();
|
|
|
|
CurrentState = State.Powdering;
|
|
Animation.Play("powder_" + Direction);
|
|
}
|
|
}
|
|
|
|
private void UseBomb()
|
|
{
|
|
// throw the object the player is currently carrying
|
|
if (_carriedGameObject != null)
|
|
{
|
|
ThrowCarriedObject();
|
|
return;
|
|
}
|
|
|
|
if (CurrentState != State.Idle &&
|
|
CurrentState != State.Rafting &&
|
|
(CurrentState != State.Swimming || !Map.Is2dMap))
|
|
return;
|
|
|
|
// pick up the bomb if there is one infront of the player
|
|
var recInteraction = new RectangleF(
|
|
EntityPosition.X + _walkDirection[Direction].X * (_body.Width / 2) - 4,
|
|
EntityPosition.Y - _body.Height / 2 + _walkDirection[Direction].Y * (_body.Height / 2) - 4, 8, 8);
|
|
|
|
// find a bomb to carry
|
|
_bombList.Clear();
|
|
Map.Objects.GetObjectsOfType(_bombList, typeof(ObjBomb),
|
|
(int)recInteraction.X, (int)recInteraction.Y, (int)recInteraction.Width, (int)recInteraction.Height);
|
|
|
|
// pick up the first bomb
|
|
foreach (var objBomb in _bombList)
|
|
{
|
|
var carriableComponent = objBomb.Components[CarriableComponent.Index] as CarriableComponent;
|
|
if (!carriableComponent.IsActive ||
|
|
!carriableComponent.Rectangle.Rectangle.Intersects(recInteraction))
|
|
continue;
|
|
|
|
carriableComponent?.StartGrabbing?.Invoke();
|
|
StartPickup(carriableComponent);
|
|
|
|
Animation.Play("pull_" + Direction);
|
|
|
|
return;
|
|
}
|
|
|
|
// remove one bomb from the inventory
|
|
if (!Game1.GameManager.RemoveItem("bomb", 1))
|
|
return;
|
|
|
|
var spawnPosition = new Vector2(EntityPosition.X, EntityPosition.Y) + _bombOffset[Direction];
|
|
Map.Objects.SpawnObject(new ObjBomb(Map, spawnPosition.X, spawnPosition.Y, true, false, 2000));
|
|
|
|
CurrentState = State.Bombing;
|
|
|
|
// play animation
|
|
Animation.Play("powder_" + Direction);
|
|
}
|
|
|
|
private void UseArrow()
|
|
{
|
|
if (CurrentState != State.Idle &&
|
|
CurrentState != State.Jumping &&
|
|
CurrentState != State.Rafting &&
|
|
CurrentState != State.Bombing &&
|
|
(CurrentState != State.Swimming || !Map.Is2dMap))
|
|
return;
|
|
|
|
// remove one powder from the inventory
|
|
if (!Game1.GameManager.RemoveItem("bow", 1))
|
|
return;
|
|
|
|
var spawnPosition = new Vector3(
|
|
EntityPosition.X + _arrowOffset[Direction].X, EntityPosition.Y + _arrowOffset[Direction].Y + (Map.Is2dMap ? -4 : 0), EntityPosition.Z + (Map.Is2dMap ? 0 : 4));
|
|
Map.Objects.SpawnObject(new ObjArrow(
|
|
Map, spawnPosition, Direction, Game1.GameManager.PieceOfPowerIsActive ? ArrowSpeedPoP : ArrowSpeed));
|
|
|
|
if (CurrentState != State.Jumping)
|
|
{
|
|
StopRaft();
|
|
|
|
CurrentState = State.Powdering;
|
|
Animation.Play("powder_" + Direction);
|
|
}
|
|
|
|
Game1.GameManager.PlaySoundEffect("D378-10-0A");
|
|
}
|
|
|
|
private void UseShovel()
|
|
{
|
|
if (CurrentState != State.Idle || _isClimbing)
|
|
return;
|
|
|
|
CurrentState = State.Digging;
|
|
_hasDug = false;
|
|
|
|
// play animation
|
|
Animation.Play("dig_" + Direction);
|
|
|
|
_digPosition = new Point(
|
|
(int)((EntityPosition.X + _shovelOffset[Direction].X) / Values.TileSize),
|
|
(int)((EntityPosition.Y + _shovelOffset[Direction].Y) / Values.TileSize));
|
|
|
|
_canDig = Map.CanDig(_digPosition);
|
|
|
|
if (_canDig)
|
|
Game1.GameManager.PlaySoundEffect("D378-14-0E");
|
|
else
|
|
Game1.GameManager.PlaySoundEffect("D360-07-07");
|
|
}
|
|
|
|
private void UseStoneLifter()
|
|
{
|
|
if (_carriedComponent == null || CurrentState != State.Carrying)
|
|
return;
|
|
|
|
if (Map.Is2dMap && _isClimbing)
|
|
return;
|
|
|
|
ThrowCarriedObject();
|
|
}
|
|
|
|
private void HoldStoneLifter()
|
|
{
|
|
if (CurrentState != State.Idle)
|
|
return;
|
|
|
|
GameObject grabbedObject = null;
|
|
|
|
if (_carriedComponent == null)
|
|
{
|
|
var recInteraction = new RectangleF(
|
|
EntityPosition.X + _walkDirection[Direction].X * (_body.Width / 2) - 1,
|
|
EntityPosition.Y - _body.Height / 2 + _walkDirection[Direction].Y * (_body.Height / 2) - 1, 2, 2);
|
|
|
|
// find an object to carry
|
|
grabbedObject = Map.Objects.GetCarryableObjects(recInteraction);
|
|
if (grabbedObject != null)
|
|
{
|
|
var carriableComponent = grabbedObject.Components[CarriableComponent.Index] as CarriableComponent;
|
|
if (carriableComponent.IsActive)
|
|
{
|
|
CurrentState = State.Grabbing;
|
|
|
|
if (!carriableComponent.IsHeavy || Game1.GameManager.StoneGrabberLevel > 1)
|
|
carriableComponent?.StartGrabbing?.Invoke();
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_wasPulling)
|
|
_pullCounter += Game1.DeltaTime;
|
|
else
|
|
_pullCounter = 0;
|
|
|
|
if (CurrentState == State.Grabbing)
|
|
{
|
|
var carriableComponent = grabbedObject.Components[CarriableComponent.Index] as CarriableComponent;
|
|
|
|
// is the player pulling in the opposite direction?
|
|
var moveVec = ControlHandler.GetMoveVector2();
|
|
|
|
if (carriableComponent?.Pull != null)
|
|
{
|
|
// do not continuously play the pull animation
|
|
if (!carriableComponent.Pull(_pullCounter > 0 ? moveVec : Vector2.Zero) && _pullCounter < 0)
|
|
_pullCounter = PullResetTime;
|
|
}
|
|
|
|
if (moveVec.Length() > 0.5)
|
|
{
|
|
// pulling into the oposite direction
|
|
var moveDir = AnimationHelper.GetDirection(moveVec);
|
|
if ((moveDir + 2) % 4 == Direction)
|
|
{
|
|
// do not show the pull animation while resetting
|
|
if (_pullCounter >= 0)
|
|
CurrentState = State.Pulling;
|
|
|
|
_isPulling = true;
|
|
|
|
if (!carriableComponent.IsHeavy || Game1.GameManager.StoneGrabberLevel > 1)
|
|
{
|
|
// start carrying the object
|
|
if (_pullCounter >= PullTime && grabbedObject != null)
|
|
StartPickup(carriableComponent);
|
|
|
|
if (_pullCounter > PullMaxTime)
|
|
_pullCounter = PullResetTime;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UseHookshot()
|
|
{
|
|
if (CurrentState != State.Idle && CurrentState != State.Rafting && (!Map.Is2dMap || CurrentState != State.Swimming))
|
|
return;
|
|
|
|
var hookshotDirection = CurrentState == State.Swimming ? _swimDirection : Direction;
|
|
|
|
var spawnPosition = new Vector3(
|
|
EntityPosition.X + _hookshotOffset[hookshotDirection].X,
|
|
EntityPosition.Y + _hookshotOffset[hookshotDirection].Y, EntityPosition.Z);
|
|
Hookshot.Start(Map, spawnPosition, AnimationHelper.DirectionOffset[hookshotDirection]);
|
|
Map.Objects.SpawnObject(Hookshot);
|
|
|
|
CurrentState = State.Hookshot;
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
_body.HoleAbsorption = Vector2.Zero;
|
|
_body.IgnoreHoles = true;
|
|
StopRaft();
|
|
|
|
// play animation
|
|
Animation.Play("powder_" + hookshotDirection);
|
|
}
|
|
|
|
private void UseBoomerang()
|
|
{
|
|
if ((CurrentState != State.Idle &&
|
|
CurrentState != State.Jumping &&
|
|
(CurrentState != State.Swimming || !Map.Is2dMap)) || !_boomerang.IsReady)
|
|
return;
|
|
|
|
var spawnPosition = new Vector3(EntityPosition.X + _boomerangOffset[Direction].X, EntityPosition.Y + _boomerangOffset[Direction].Y, EntityPosition.Z);
|
|
|
|
// can throw into multiple directions
|
|
var boomerangVector = _lastBaseMoveVelocity;
|
|
if (boomerangVector != Vector2.Zero)
|
|
boomerangVector.Normalize();
|
|
else
|
|
boomerangVector = _walkDirection[Direction];
|
|
|
|
_boomerang.Start(Map, spawnPosition, boomerangVector);
|
|
Map.Objects.SpawnObject(_boomerang);
|
|
|
|
if (CurrentState != State.Jumping)
|
|
{
|
|
CurrentState = State.Powdering;
|
|
Animation.Play("powder_" + Direction);
|
|
}
|
|
}
|
|
|
|
private void UseMagicRod()
|
|
{
|
|
if (CurrentState != State.Idle &&
|
|
CurrentState != State.Rafting &&
|
|
(CurrentState != State.Swimming || !Map.Is2dMap) &&
|
|
(CurrentState != State.Jumping || _railJump))
|
|
return;
|
|
|
|
var spawnPosition = new Vector3(EntityPosition.X + _magicRodOffset[Direction].X, EntityPosition.Y + _magicRodOffset[Direction].Y, EntityPosition.Z);
|
|
Map.Objects.SpawnObject(new ObjMagicRodShot(Map, spawnPosition, AnimationHelper.DirectionOffset[Direction] *
|
|
(Game1.GameManager.PieceOfPowerIsActive ? MagicRodSpeedPoP : MagicRodSpeed), Direction));
|
|
|
|
CurrentState = State.MagicRod;
|
|
_swordChargeCounter = SwordChargeTime;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D378-13-0D");
|
|
StopRaft();
|
|
|
|
// play animation
|
|
Animation.Play("rod_" + Direction);
|
|
AnimatorWeapons.Play("rod_" + Direction);
|
|
}
|
|
|
|
private void UseOcarina()
|
|
{
|
|
if (CurrentState != State.Idle || _isClimbing)
|
|
return;
|
|
|
|
_ocarinaNoteIndex = 0;
|
|
_ocarinaCounter = 0;
|
|
|
|
Game1.GbsPlayer.Pause();
|
|
|
|
if (Game1.GameManager.SelectedOcarinaSong == 0)
|
|
Game1.GameManager.PlaySoundEffect("D370-09-09");
|
|
else if (Game1.GameManager.SelectedOcarinaSong == 1)
|
|
Game1.GameManager.PlaySoundEffect("D370-11-0B");
|
|
else if (Game1.GameManager.SelectedOcarinaSong == 2)
|
|
Game1.GameManager.PlaySoundEffect("D370-10-0A");
|
|
else
|
|
Game1.GameManager.PlaySoundEffect("D370-21-15");
|
|
|
|
_ocarinaSong = Game1.GameManager.SelectedOcarinaSong;
|
|
CurrentState = State.Ocarina;
|
|
Direction = 3;
|
|
Animation.Play("ocarina");
|
|
}
|
|
|
|
private void UpdateOcarina()
|
|
{
|
|
if (CurrentState == State.Ocarina)
|
|
{
|
|
// finished playing the ocarina song?
|
|
if (!Animation.IsPlaying)
|
|
{
|
|
FinishedOcarinaSong();
|
|
return;
|
|
}
|
|
|
|
UpdateOcarinaAnimation();
|
|
}
|
|
else if (CurrentState == State.OcarinaTelport)
|
|
{
|
|
// show the animation while teleporting
|
|
CurrentState = State.Idle;
|
|
}
|
|
}
|
|
|
|
private void UpdateOcarinaAnimation()
|
|
{
|
|
if (CurrentState != State.Ocarina)
|
|
return;
|
|
|
|
_ocarinaCounter += Game1.DeltaTime;
|
|
if (_ocarinaCounter > 100 + _ocarinaNoteIndex * 910)
|
|
{
|
|
_ocarinaNoteIndex++;
|
|
|
|
var dir = _ocarinaNoteIndex % 2 == 1 ? -1 : 1;
|
|
var objNote = new ObjNote(Map, new Vector2(EntityPosition.X + dir * 7, EntityPosition.Y), dir);
|
|
Map.Objects.SpawnObject(objNote);
|
|
}
|
|
}
|
|
|
|
private void FinishedOcarinaSong()
|
|
{
|
|
// continue playing music
|
|
if (_ocarinaSong != 1)
|
|
Game1.GbsPlayer.Play();
|
|
|
|
if (_ocarinaSong == -1)
|
|
{
|
|
CurrentState = State.Idle;
|
|
Game1.GameManager.StartDialogPath("ocarina_bad");
|
|
return;
|
|
}
|
|
|
|
if (_ocarinaSong == 1)
|
|
{
|
|
CurrentState = State.OcarinaTelport;
|
|
|
|
MapTransitionStart = EntityPosition.Position;
|
|
MapTransitionEnd = EntityPosition.Position;
|
|
TransitionOutWalking = false;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-44-2C");
|
|
|
|
// load the map
|
|
var transitionSystem = (MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)];
|
|
|
|
if (Map.DungeonMode)
|
|
{
|
|
// respawn at the dungeon entry
|
|
MapManager.ObjLink.SetNextMapPosition(MapManager.ObjLink.SavePosition);
|
|
transitionSystem.AppendMapChange(MapManager.ObjLink.SaveMap, null, false, false, Color.White, true);
|
|
}
|
|
else
|
|
{
|
|
// append a map change
|
|
transitionSystem.AppendMapChange("overworld.map", "ocarina_entry", false, false, Color.White, true);
|
|
}
|
|
|
|
transitionSystem.StartTeleportTransition = true;
|
|
|
|
return;
|
|
}
|
|
|
|
CurrentState = State.Idle;
|
|
|
|
var recInteraction = new RectangleF(EntityPosition.X - 64, EntityPosition.Y - 64 - 8, 128, 128);
|
|
|
|
_ocarinaList.Clear();
|
|
Map.Objects.GetComponentList(_ocarinaList,
|
|
(int)recInteraction.X, (int)recInteraction.Y, (int)recInteraction.Width, (int)recInteraction.Height, OcarinaListenerComponent.Mask);
|
|
|
|
// notify ocarina listener components around the player
|
|
foreach (var objOcarinaListener in _ocarinaList)
|
|
{
|
|
if (recInteraction.Contains(objOcarinaListener.EntityPosition.Position))
|
|
{
|
|
var ocarinaComponent = (OcarinaListenerComponent)objOcarinaListener.Components[OcarinaListenerComponent.Index];
|
|
ocarinaComponent.OcarinaPlayedFunction(Game1.GameManager.SelectedOcarinaSong);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void HoldShield(bool lastKeyDown)
|
|
{
|
|
if (CurrentState != State.Idle && CurrentState != State.Pushing)
|
|
return;
|
|
|
|
if (!_wasBlocking)
|
|
Game1.GameManager.PlaySoundEffect("D378-22-16");
|
|
|
|
_wasBlocking = true;
|
|
CurrentState = State.Blocking;
|
|
}
|
|
|
|
private void HoldPegasusBoots()
|
|
{
|
|
if (CurrentState == State.BootKnockback || _isTrapped)
|
|
return;
|
|
|
|
_bootsHolding = true;
|
|
}
|
|
|
|
private void UpdateShieldPush()
|
|
{
|
|
if (Animation.CollisionRectangle.IsEmpty || _isTrapped)
|
|
return;
|
|
|
|
// push with the shield
|
|
var shieldRectangle = new Box(
|
|
EntityPosition.X + Animation.CollisionRectangle.X - 7,
|
|
EntityPosition.Y + Animation.CollisionRectangle.Y - 16, 0,
|
|
Animation.CollisionRectangle.Width,
|
|
Animation.CollisionRectangle.Height, 12);
|
|
|
|
var pushedRectangle = Map.Objects.PushObject(shieldRectangle,
|
|
_walkDirection[Direction] + _body.VelocityTarget * 0.5f, PushableComponent.PushType.Impact);
|
|
|
|
// get repelled from the pushed object
|
|
if (pushedRectangle != null)
|
|
{
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
_body.Velocity += new Vector3(
|
|
-_walkDirection[Direction].X * pushedRectangle.RepelMultiplier,
|
|
-_walkDirection[Direction].Y * pushedRectangle.RepelMultiplier, 0);
|
|
|
|
if (pushedRectangle.RepelParticle)
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-07-07");
|
|
// poke particle
|
|
Map.Objects.SpawnObject(new ObjAnimator(Map,
|
|
(int)(pushedRectangle.PushableBox.Box.X + pushedRectangle.PushableBox.Box.Width / 2),
|
|
(int)(pushedRectangle.PushableBox.Box.Y + pushedRectangle.PushableBox.Box.Height / 2),
|
|
Values.LayerTop, "Particles/swordPoke", "run", true));
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-09-09");
|
|
}
|
|
}
|
|
}
|
|
|
|
private void UpdateCharging()
|
|
{
|
|
// stop charging
|
|
if (_isHoldingSword)
|
|
{
|
|
// poke objects that walk into the sowrd
|
|
RectangleF collisionRectangle = AnimatorWeapons.CollisionRectangle;
|
|
var damageOrigin = BodyRectangle.Center;
|
|
SwordDamageBox = new Box(
|
|
collisionRectangle.X + EntityPosition.X + _animationOffsetX,
|
|
collisionRectangle.Y + EntityPosition.Y - EntityPosition.Z + _animationOffsetY, 0,
|
|
collisionRectangle.Width,
|
|
collisionRectangle.Height, 4);
|
|
|
|
var hitType = Game1.GameManager.SwordLevel == 1 ? HitType.Sword1 : HitType.Sword2;
|
|
var damage = Game1.GameManager.SwordLevel == 1 ? 1 : 2;
|
|
|
|
// red cloak doubles damage
|
|
if (Game1.GameManager.CloakType == GameManager.CloakRed)
|
|
damage *= 2;
|
|
// piece of power double the damage
|
|
if (Game1.GameManager.PieceOfPowerIsActive)
|
|
damage *= 2;
|
|
|
|
var pieceOfPower = Game1.GameManager.PieceOfPowerIsActive || Game1.GameManager.CloakType == GameManager.CloakRed;
|
|
var hitCollision = Map.Objects.Hit(this, damageOrigin, SwordDamageBox, hitType | HitType.SwordHold, damage, pieceOfPower, out var direction, true);
|
|
// start poking?
|
|
if (hitCollision != Values.HitCollision.None &&
|
|
hitCollision != Values.HitCollision.NoneBlocking)
|
|
{
|
|
_swordPoked = true;
|
|
Animation.Play("poke_" + Direction);
|
|
AnimatorWeapons.Play("poke_" + Direction);
|
|
CurrentState = State.Attacking;
|
|
|
|
// get repelled
|
|
RepelPlayer(hitCollision, direction);
|
|
}
|
|
else if (_swordChargeCounter > 0)
|
|
{
|
|
_swordChargeCounter -= Game1.DeltaTime;
|
|
|
|
// finished charging?
|
|
if (_swordChargeCounter <= 0)
|
|
Game1.GameManager.PlaySoundEffect("D360-04-04");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// start charge attack
|
|
if (_swordChargeCounter <= 0)
|
|
StartSwordSpin();
|
|
else
|
|
ReturnToIdle();
|
|
}
|
|
}
|
|
|
|
private void StartSwordSpin()
|
|
{
|
|
CurrentState = State.Attacking;
|
|
|
|
Animation.Play("swing_" + Direction);
|
|
AnimatorWeapons.Play("swing_" + Direction);
|
|
|
|
Game1.GameManager.PlaySoundEffect("D378-03-03");
|
|
|
|
_swordChargeCounter = SwordChargeTime;
|
|
_isSwingingSword = true;
|
|
}
|
|
|
|
private void UpdateAttacking()
|
|
{
|
|
if (_bootsRunning && CarrySword)
|
|
AnimatorWeapons.Play("stand_" + Direction);
|
|
|
|
if (AnimatorWeapons.CollisionRectangle.IsEmpty)
|
|
return;
|
|
|
|
var damageOrigin = BodyRectangle.Center;
|
|
if (Map.Is2dMap)
|
|
damageOrigin.Y -= 4;
|
|
|
|
RectangleF collisionRectangle = AnimatorWeapons.CollisionRectangle;
|
|
|
|
// this lerps the collision box between frames
|
|
// a rotation collision box would probably be a better option
|
|
if (AnimatorWeapons.CurrentAnimation.Frames.Length > AnimatorWeapons.CurrentFrameIndex + 1)
|
|
{
|
|
var frameState = (float)(AnimatorWeapons.FrameCounter / AnimatorWeapons.CurrentFrame.FrameTime);
|
|
var collisionRectangleNextFrame = AnimatorWeapons.GetCollisionBox(
|
|
AnimatorWeapons.CurrentAnimation.Frames[AnimatorWeapons.CurrentFrameIndex + 1]);
|
|
collisionRectangle = new RectangleF(
|
|
MathHelper.Lerp(collisionRectangle.X, collisionRectangleNextFrame.X, frameState),
|
|
MathHelper.Lerp(collisionRectangle.Y, collisionRectangleNextFrame.Y, frameState),
|
|
MathHelper.Lerp(collisionRectangle.Width, collisionRectangleNextFrame.Width, frameState),
|
|
MathHelper.Lerp(collisionRectangle.Height, collisionRectangleNextFrame.Height, frameState));
|
|
}
|
|
|
|
SwordDamageBox = new Box(
|
|
collisionRectangle.X + EntityPosition.X + _animationOffsetX,
|
|
collisionRectangle.Y + EntityPosition.Y - EntityPosition.Z + _animationOffsetY, 0,
|
|
collisionRectangle.Width,
|
|
collisionRectangle.Height, 4);
|
|
|
|
var hitType = _bootsRunning ? HitType.PegasusBootsSword :
|
|
(Game1.GameManager.SwordLevel == 1 ? HitType.Sword1 : HitType.Sword2);
|
|
|
|
var damage = Game1.GameManager.SwordLevel == 1 ? 1 : 2;
|
|
|
|
if (_isSwingingSword)
|
|
{
|
|
damage *= 2;
|
|
hitType |= HitType.SwordSpin;
|
|
}
|
|
|
|
if (_bootsRunning)
|
|
damage *= 2;
|
|
|
|
// piece of power double the damage
|
|
if (Game1.GameManager.PieceOfPowerIsActive)
|
|
damage *= 2;
|
|
|
|
// red cloak doubles the damage
|
|
if (Game1.GameManager.CloakType == GameManager.CloakRed)
|
|
damage *= 2;
|
|
|
|
var pieceOfPower = Game1.GameManager.PieceOfPowerIsActive || Game1.GameManager.SwordLevel == 2;
|
|
var hitCollision = Map.Objects.Hit(this, damageOrigin, SwordDamageBox, hitType, damage, pieceOfPower, out var direction, true);
|
|
|
|
if (_pokeStart)
|
|
{
|
|
_pokeStart = false;
|
|
|
|
if (hitCollision != Values.HitCollision.NoneBlocking)
|
|
{
|
|
var swordRectangle = AnimatorWeapons.CollisionRectangle;
|
|
var swordBox = new Box(
|
|
swordRectangle.X + EntityPosition.X + _animationOffsetX,
|
|
swordRectangle.Y + EntityPosition.Y - EntityPosition.Z + _animationOffsetY, 0,
|
|
swordRectangle.Width, swordRectangle.Height, 4);
|
|
var destroyableWall = DestroyableWall(swordBox);
|
|
|
|
if (destroyableWall)
|
|
Game1.GameManager.PlaySoundEffect("D378-23-17");
|
|
else
|
|
Game1.GameManager.PlaySoundEffect("D360-07-07");
|
|
|
|
var pokeParticle = new ObjAnimator(Map, 0, 0, Values.LayerTop, "Particles/swordPoke", "run", true);
|
|
pokeParticle.EntityPosition.X = EntityPosition.X + _pokeAnimationOffset[Direction].X;
|
|
pokeParticle.EntityPosition.Y = EntityPosition.Y + _pokeAnimationOffset[Direction].Y;
|
|
Map.Objects.SpawnObject(pokeParticle);
|
|
}
|
|
}
|
|
|
|
if (hitCollision != Values.HitCollision.None && hitCollision != Values.HitCollision.NoneBlocking)
|
|
_stopCharging = true;
|
|
|
|
// shoot the sword if the player has the l2 sword and full health
|
|
if (!_shotSword && Game1.GameManager.SwordLevel == 2 && Game1.GameManager.CurrentHealth >= Game1.GameManager.MaxHearths * 4 && AnimatorWeapons.CurrentFrameIndex == 2)
|
|
{
|
|
_shotSword = true;
|
|
|
|
var spawnPosition = new Vector3(EntityPosition.X + _shootSwordOffset[Direction].X, EntityPosition.Y + _shootSwordOffset[Direction].Y - EntityPosition.Z, 0);
|
|
var objSwordShot = new ObjSwordShot(Map, spawnPosition, Direction);
|
|
Map.Objects.SpawnObject(objSwordShot);
|
|
}
|
|
|
|
// spawn hit particle?
|
|
if ((hitCollision & Values.HitCollision.Particle) != 0 && _hitParticleTime + 225 < Game1.TotalGameTime)
|
|
{
|
|
_hitParticleTime = Game1.TotalGameTime;
|
|
SwordPoke(collisionRectangle);
|
|
}
|
|
|
|
RepelPlayer(hitCollision, direction);
|
|
}
|
|
|
|
private void RepelPlayer(Values.HitCollision collisionType, Vector2 direction)
|
|
{
|
|
// repel the player
|
|
if ((collisionType & Values.HitCollision.Repelling) != 0 &&
|
|
_hitRepelTime + 225 < Game1.TotalGameTime)
|
|
{
|
|
_hitRepelTime = Game1.TotalGameTime;
|
|
|
|
var multiplier = Map.Is2dMap ? 1.5f : (_bootsRunning ? 1.5f : 1.0f);
|
|
|
|
if ((collisionType & Values.HitCollision.Repelling0) != 0)
|
|
multiplier = 3.00f;
|
|
if ((collisionType & Values.HitCollision.Repelling1) != 0)
|
|
multiplier = 2.25f;
|
|
|
|
if (_bootsRunning)
|
|
_bootsStop = true;
|
|
|
|
_body.Velocity += new Vector3(-direction.X, -direction.Y, 0) * multiplier;
|
|
}
|
|
}
|
|
|
|
private void SwordPoke(RectangleF collisionRectangle)
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-07-07");
|
|
|
|
// poke particle
|
|
Map.Objects.SpawnObject(new ObjAnimator(Map,
|
|
(int)(EntityPosition.X - 8 + collisionRectangle.X + collisionRectangle.Width / 2),
|
|
(int)(EntityPosition.Y - 15 + collisionRectangle.Y + collisionRectangle.Height / 2),
|
|
Values.LayerTop, "Particles/swordPoke", "run", true));
|
|
}
|
|
|
|
private void UpdatePickup()
|
|
{
|
|
if (ShowItem == null)
|
|
return;
|
|
|
|
_itemShowCounter -= Game1.DeltaTime;
|
|
|
|
if (_itemShowCounter <= 0)
|
|
{
|
|
// show pick up text
|
|
if (_showItem && CurrentState == State.PickingUp)
|
|
{
|
|
_showItem = false;
|
|
|
|
// show pickup dialog
|
|
if (ShowItem.PickUpDialog != null)
|
|
{
|
|
if (string.IsNullOrEmpty(_pickupDialogOverride))
|
|
Game1.GameManager.StartDialogPath(ShowItem.PickUpDialog);
|
|
else
|
|
{
|
|
Game1.GameManager.StartDialogPath(_pickupDialogOverride);
|
|
_pickupDialogOverride = null;
|
|
}
|
|
|
|
if (!string.IsNullOrEmpty(_additionalPickupDialog))
|
|
{
|
|
Game1.GameManager.StartDialogPath(_additionalPickupDialog);
|
|
_additionalPickupDialog = null;
|
|
}
|
|
}
|
|
|
|
_itemShowCounter = 250;
|
|
|
|
if (ShowItem.Name == "sword1")
|
|
_itemShowCounter = 5850;
|
|
else if (ShowItem.Name.StartsWith("instrument"))
|
|
_itemShowCounter = 1000;
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.SaveManager.SetString("player_shows_item", "0");
|
|
|
|
// add the item to the inventory
|
|
if (_collectedShowItem != null)
|
|
{
|
|
Game1.GameManager.CollectItem(_collectedShowItem, 0);
|
|
_collectedShowItem = null;
|
|
}
|
|
|
|
// spawn the follower if one was picked up
|
|
UpdateFollower(false);
|
|
|
|
// sword spin
|
|
if (ShowItem.Name == "sword1")
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D378-03-03");
|
|
Animation.Play("swing_3");
|
|
AnimatorWeapons.Play("swing_3");
|
|
CurrentState = State.SwordShow0;
|
|
_swordChargeCounter = 1; // don't blink
|
|
ShowItem = null;
|
|
}
|
|
else if (ShowItem.Name.StartsWith("instrument"))
|
|
{
|
|
// make sure that the music is not playing
|
|
Game1.GameManager.StopPieceOfPower();
|
|
Game1.GameManager.StopGuardianAcorn();
|
|
|
|
_instrumentCounter = 0;
|
|
CurrentState = State.ShowInstrumentPart0;
|
|
}
|
|
else
|
|
{
|
|
ShowItem = null;
|
|
if (CurrentState == State.PickingUp)
|
|
CurrentState = State.Idle;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EndPickup()
|
|
{
|
|
_savedPreItemPickup = false;
|
|
SaveGameSaveLoad.ClearSaveState();
|
|
Game1.GameManager.SaveManager.DisableHistory();
|
|
}
|
|
|
|
private void UpdateHookshot()
|
|
{
|
|
if (Hookshot.IsMoving)
|
|
return;
|
|
|
|
_body.IgnoreHoles = false;
|
|
ReturnToIdle();
|
|
}
|
|
|
|
private void UpdateDigging()
|
|
{
|
|
if (Animation.CurrentFrameIndex > 0 && !_hasDug)
|
|
{
|
|
_hasDug = true;
|
|
if (_canDig)
|
|
Map.Dig(_digPosition, EntityPosition.Position, Direction);
|
|
}
|
|
|
|
if (!Animation.IsPlaying)
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
private void UpdatePegasusBoots()
|
|
{
|
|
_wasBootsRunning = _bootsRunning;
|
|
if (CurrentState != State.Idle || _isClimbing || Map.Is2dMap && Direction % 2 != 0)
|
|
{
|
|
_bootsHolding = false;
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
// stop running but start charging with a time boost
|
|
if (_bootsStop && _body.Velocity.Length() < 0.25f)
|
|
{
|
|
_bootsStop = false;
|
|
_bootsRunning = false;
|
|
_bootsCounter = _bootsRunTime - 300;
|
|
}
|
|
|
|
if (_bootsHolding || _bootsRunning)
|
|
{
|
|
var lastCounter = _bootsCounter;
|
|
_bootsCounter += Game1.DeltaTime;
|
|
|
|
// spawn particles
|
|
if (_bootsCounter % _bootsParticleTime < lastCounter % _bootsParticleTime)
|
|
{
|
|
// water splash effect while running water?
|
|
if (_body.CurrentFieldState.HasFlag(MapStates.FieldStates.Water))
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-14-0E");
|
|
|
|
var splashAnimator = new ObjAnimator(_body.Owner.Map, 0, 0, 0, 3, 1, "Particles/splash", "idle", true);
|
|
splashAnimator.EntityPosition.Set(new Vector2(
|
|
_body.Position.X + _body.OffsetX + _body.Width / 2f,
|
|
_body.Position.Y + _body.OffsetY + _body.Height - _body.Position.Z - 3));
|
|
Map.Objects.SpawnObject(splashAnimator);
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D378-07-07");
|
|
|
|
var animator = new ObjAnimator(Map, (int)EntityPosition.X, (int)(EntityPosition.Y + 1),
|
|
0, -1 - (int)EntityPosition.Z, Values.LayerPlayer, "Particles/run", "spawn", true);
|
|
Map.Objects.SpawnObject(animator);
|
|
}
|
|
}
|
|
|
|
// start running
|
|
if (!_bootsRunning && _bootsCounter > _bootsRunTime)
|
|
{
|
|
_bootsRunning = true;
|
|
_wasBootsRunning = true;
|
|
_bootsStop = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
_bootsCounter = 0;
|
|
}
|
|
}
|
|
|
|
private bool Jump(bool force = false, bool playSoundEffect = true)
|
|
{
|
|
if ((!force && (
|
|
CurrentState != State.Idle &&
|
|
CurrentState != State.Attacking &&
|
|
CurrentState != State.Charging &&
|
|
CurrentState != State.Pushing &&
|
|
CurrentState != State.Blocking &&
|
|
CurrentState != State.Rafting)) ||
|
|
_isTrapped || !_canJump)
|
|
{
|
|
if (_isTrapped && playSoundEffect)
|
|
Game1.GameManager.PlaySoundEffect("D360-13-0D");
|
|
|
|
return false;
|
|
}
|
|
|
|
if (!_body.IsGrounded)
|
|
return false;
|
|
|
|
// release the carried object if the player is carrying something
|
|
ReleaseCarriedObject();
|
|
|
|
if (playSoundEffect)
|
|
Game1.GameManager.PlaySoundEffect("D360-13-0D");
|
|
|
|
if (_isRafting)
|
|
{
|
|
// do not move while jumping
|
|
_moveVelocity = Vector2.Zero;
|
|
_lastMoveVelocity = Vector2.Zero;
|
|
StopRaft();
|
|
}
|
|
else
|
|
{
|
|
// base move velocity does not contain the velocity added in the air
|
|
// so when we hit the floor and directly jump afterwards we do not get the velocity of the previouse jump
|
|
_lastMoveVelocity = _lastBaseMoveVelocity;
|
|
}
|
|
|
|
_startedJumping = true;
|
|
_body.Velocity.Z = JumpAcceleration;
|
|
|
|
// while attacking the player can still jump but without the animation
|
|
if (CurrentState != State.Attacking &&
|
|
CurrentState != State.Charging)
|
|
{
|
|
// start the jump animation
|
|
Animation.Play("jump_" + Direction);
|
|
|
|
CurrentState = State.Jumping;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private void UpdateJump()
|
|
{
|
|
if (CurrentState != State.Jumping)
|
|
return;
|
|
|
|
if (_railJump)
|
|
{
|
|
_railJumpPercentage += Game1.TimeMultiplier * _railJumpSpeed;
|
|
var amount = MathF.Sin(_railJumpPercentage * (MathF.PI * 0.3f)) / MathF.Sin(MathF.PI * 0.3f);
|
|
var newPosition = Vector2.Lerp(_railJumpStartPosition, _railJumpTargetPosition, amount);
|
|
EntityPosition.Set(newPosition);
|
|
|
|
EntityPosition.Z = MathF.Sin(_railJumpPercentage * MathF.PI) * _railJumpHeight + _railJumpPercentage * _railJumpPositionZ;
|
|
|
|
if (_railJumpPercentage >= 1)
|
|
{
|
|
_railJump = false;
|
|
_body.IgnoreHeight = false;
|
|
_body.IgnoresZ = false;
|
|
_body.Velocity.Z = -1f;
|
|
_body.JumpStartHeight = _railJumpPositionZ;
|
|
EntityPosition.Set(_railJumpTargetPosition);
|
|
EntityPosition.Z = _railJumpPositionZ;
|
|
_lastMoveVelocity = Vector2.Zero;
|
|
}
|
|
}
|
|
|
|
// touched the ground
|
|
if (!_railJump && _body.IsGrounded && _body.Velocity.Z <= 0)
|
|
{
|
|
if ((_body.CurrentFieldState & (MapStates.FieldStates.Water | MapStates.FieldStates.DeepWater)) == 0)
|
|
Game1.GameManager.PlaySoundEffect("D378-07-07");
|
|
if ((_body.CurrentFieldState & MapStates.FieldStates.DeepWater) == 0)
|
|
Game1.GameManager.PlaySoundEffect("D360-14-0E");
|
|
|
|
ReturnToIdle();
|
|
}
|
|
}
|
|
|
|
private void ThrowCarriedObject()
|
|
{
|
|
Game1.GameManager.PlaySoundEffect("D360-08-08");
|
|
|
|
// play a little throw animation
|
|
Animation.Play("throw_" + Direction);
|
|
CurrentState = State.Throwing;
|
|
|
|
_carriedComponent.Throw(_walkDirection[Direction] * 3f);
|
|
RemoveCarriedObject();
|
|
}
|
|
|
|
private void StartPickup(CarriableComponent carriableComponent)
|
|
{
|
|
if (carriableComponent?.Init == null)
|
|
return;
|
|
|
|
_carriedComponent = carriableComponent;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D370-02-02");
|
|
|
|
_carryStartPosition = _carriedComponent.Init();
|
|
_carriedComponent.IsPickedUp = true;
|
|
CurrentState = State.PreCarrying;
|
|
_preCarryCounter = 0;
|
|
|
|
_carriedGameObject = carriableComponent.Owner;
|
|
_carriedObjDrawComp = carriableComponent.Owner.Components[DrawComponent.Index] as DrawComponent;
|
|
if (_carriedObjDrawComp != null)
|
|
_carriedObjDrawComp.IsActive = false;
|
|
}
|
|
|
|
private void UpdatePositionCarriedObject(CPosition newPosition)
|
|
{
|
|
if (_carriedComponent == null)
|
|
return;
|
|
|
|
var targetPosition = new Vector3(EntityPosition.X, EntityPosition.Y, EntityPosition.Z + _carriedComponent.CarryHeight);
|
|
|
|
if (CurrentState == State.PreCarrying)
|
|
{
|
|
// finished pickup animation?
|
|
if (_preCarryCounter >= PreCarryTime)
|
|
{
|
|
_preCarryCounter = PreCarryTime;
|
|
CurrentState = State.Carrying;
|
|
}
|
|
|
|
var pickupTime = 1 - MathF.Cos((_preCarryCounter / PreCarryTime) * (MathF.PI / 2));
|
|
|
|
var carryPositionXY = Vector2.Lerp(
|
|
new Vector2(_carryStartPosition.X, _carryStartPosition.Y),
|
|
new Vector2(targetPosition.X, targetPosition.Y),
|
|
1 - MathF.Cos(pickupTime * (MathF.PI / 2)));
|
|
var carryPositionZ = MathHelper.Lerp(_carryStartPosition.Z, targetPosition.Z,
|
|
MathF.Sin(pickupTime * (MathF.PI / 2)));
|
|
|
|
if (!_carriedComponent.UpdatePosition(new Vector3(carryPositionXY.X, carryPositionXY.Y, carryPositionZ)))
|
|
{
|
|
CurrentState = State.Idle;
|
|
ReleaseCarriedObject();
|
|
}
|
|
}
|
|
else if (!_isFlying)
|
|
{
|
|
// move the carried object up/down with the walk animation
|
|
if (Direction % 2 == 0)
|
|
targetPosition.Z += _isWalking ? Animation.CurrentFrameIndex : 1;
|
|
else if (Map.Is2dMap)
|
|
targetPosition.Z += 1;
|
|
|
|
if (!_carriedComponent.UpdatePosition(targetPosition))
|
|
{
|
|
CurrentState = State.Idle;
|
|
ReleaseCarriedObject();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
private void StopRaft()
|
|
{
|
|
if (_isRafting)
|
|
{
|
|
_objRaft.Body.VelocityTarget = Vector2.Zero;
|
|
_objRaft.Body.AdditionalMovementVT = Vector2.Zero;
|
|
_objRaft.Body.LastAdditionalMovementVT = Vector2.Zero;
|
|
}
|
|
}
|
|
|
|
private void StealItem()
|
|
{
|
|
StopHoldingItem();
|
|
|
|
// used in ObjStoreItem to not return the item to the shelf
|
|
Game1.GameManager.SaveManager.SetString("result", "0");
|
|
|
|
Game1.GameManager.SaveName = "Thief";
|
|
|
|
// add the item to the inventory
|
|
var strItem = Game1.GameManager.SaveManager.GetString("itemShopItem");
|
|
var strCount = Game1.GameManager.SaveManager.GetString("itemShopCount");
|
|
|
|
var item = new GameItemCollected(strItem)
|
|
{
|
|
Count = int.Parse(strCount)
|
|
};
|
|
|
|
// gets picked up
|
|
PickUpItem(item, false, false);
|
|
|
|
Game1.GameManager.SaveManager.SetString("stoleItem", "1");
|
|
_showStealMessage = true;
|
|
}
|
|
|
|
private void OnHoleAbsorb()
|
|
{
|
|
if (CurrentState == State.Falling ||
|
|
CurrentState == State.TeleporterUpWait ||
|
|
CurrentState == State.TeleporterUp ||
|
|
CurrentState == State.PickingUp ||
|
|
CurrentState == State.Dying)
|
|
return;
|
|
|
|
CurrentState = State.Falling;
|
|
|
|
FreeTrappedPlayer();
|
|
ReleaseCarriedObject();
|
|
|
|
_railJump = false;
|
|
_isFallingIntoHole = true;
|
|
_holeFallCounter = 350;
|
|
|
|
Animation.Play("fall");
|
|
Game1.GameManager.PlaySoundEffect("D370-12-0C");
|
|
}
|
|
|
|
private void OnDeath()
|
|
{
|
|
if (CurrentState == State.Dying)
|
|
return;
|
|
|
|
// has potion?
|
|
var potion = Game1.GameManager.GetItem("potion");
|
|
if (potion != null && potion.Count >= 1)
|
|
{
|
|
Game1.GameManager.RemoveItem("potion", 1);
|
|
Game1.GameManager.HealPlayer(99);
|
|
ItemDrawHelper.EnableHeartAnimationSound();
|
|
return;
|
|
}
|
|
|
|
Game1.GameManager.StopMusic(true);
|
|
Game1.GameManager.PlaySoundEffect("D370-08-08");
|
|
|
|
CurrentState = State.Dying;
|
|
Animation.Play("dying");
|
|
|
|
// set the correct start frame depending on the direction the player is facing
|
|
int[] dirToFrame = { 0, 2, 1, 3 };
|
|
Animation.SetFrame(dirToFrame[Direction]);
|
|
|
|
((GameOverSystem)Game1.GameManager.GameSystems[typeof(GameOverSystem)]).StartDeath();
|
|
}
|
|
|
|
private void ReleaseCarriedObject()
|
|
{
|
|
// let the carried item fall down
|
|
if (_carriedComponent == null)
|
|
return;
|
|
|
|
_carriedComponent.Throw(new Vector2(0, 0));
|
|
RemoveCarriedObject();
|
|
}
|
|
|
|
private void RemoveCarriedObject()
|
|
{
|
|
_carriedComponent.IsPickedUp = false;
|
|
_carriedComponent = null;
|
|
|
|
_carriedGameObject = null;
|
|
|
|
if (_carriedObjDrawComp != null)
|
|
{
|
|
_carriedObjDrawComp.IsActive = true;
|
|
_carriedObjDrawComp = null;
|
|
}
|
|
}
|
|
|
|
private void UpdateFollower(bool mapInit)
|
|
{
|
|
var hasFollower = false;
|
|
|
|
// check if marin is following the player
|
|
var itemMarin = Game1.GameManager.GetItem("marin");
|
|
if (itemMarin != null && itemMarin.Count > 0)
|
|
{
|
|
_objFollower = _objMaria;
|
|
hasFollower = true;
|
|
}
|
|
|
|
// check if the rooster is following the player
|
|
var itemRooster = Game1.GameManager.GetItem("rooster");
|
|
if (itemRooster != null && itemRooster.Count > 0)
|
|
{
|
|
_objFollower = _objRooster;
|
|
hasFollower = true;
|
|
}
|
|
|
|
// check if the ghost is following the player
|
|
var itemGhost = Game1.GameManager.GetItem("ghost");
|
|
if (itemGhost != null && itemGhost.Count > 0)
|
|
{
|
|
_objFollower = _objGhost;
|
|
hasFollower = true;
|
|
}
|
|
|
|
if (hasFollower)
|
|
{
|
|
// check if the follower is already spawned
|
|
if (_objFollower.Map != Map)
|
|
{
|
|
if (mapInit && NextMapPositionStart.HasValue)
|
|
_objFollower.EntityPosition.Set(NextMapPositionStart.Value);
|
|
else
|
|
_objFollower.EntityPosition.Set(EntityPosition.Position);
|
|
|
|
_objFollower.Map = Map;
|
|
Map.Objects.SpawnObject(_objFollower);
|
|
}
|
|
}
|
|
// remove the current follower from the map
|
|
else if (_objFollower != null)
|
|
{
|
|
Map.Objects.DeleteObjects.Add(_objFollower);
|
|
_objFollower = null;
|
|
}
|
|
}
|
|
|
|
private void UpdateStoreItemPosition(CPosition position)
|
|
{
|
|
_storePickupPosition.X = position.X - _storeItemWidth / 2f;
|
|
_storePickupPosition.Y = position.Y - EntityPosition.Z - 14 - _storeItemHeight;
|
|
}
|
|
|
|
#region public
|
|
|
|
public void InitGame()
|
|
{
|
|
Animation.Play((CarryShield ? "stands_" : "stand_") + Direction);
|
|
_spriteTransparency = 1;
|
|
|
|
_inDungeon = false;
|
|
|
|
NextMapFallStart = false;
|
|
NextMapFallRotateStart = false;
|
|
|
|
Game1.GameManager.SwordLevel = 0;
|
|
Game1.GameManager.ShieldLevel = 0;
|
|
Game1.GameManager.StoneGrabberLevel = 0;
|
|
|
|
Game1.GameManager.SelectedOcarinaSong = -1;
|
|
Game1.GameManager.OcarinaSongs[0] = 0;
|
|
Game1.GameManager.OcarinaSongs[1] = 0;
|
|
Game1.GameManager.OcarinaSongs[2] = 0;
|
|
|
|
Game1.GameManager.HasMagnifyingLens = false;
|
|
|
|
_spawnGhost = false;
|
|
HasFlippers = false;
|
|
StoreItem = null;
|
|
|
|
_body.IsActive = true;
|
|
|
|
_objMaria = new ObjMarin(Map, 0, 0);
|
|
_objRooster = new ObjCock(Map, 0, 0, null);
|
|
_objGhost = new ObjGhost(Map, 0, 0);
|
|
|
|
MapInit();
|
|
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
public void MapInit()
|
|
{
|
|
if (CurrentState != State.Swimming && CurrentState != State.OcarinaTelport)
|
|
CurrentState = State.Idle;
|
|
|
|
_boomerang.Reset();
|
|
Hookshot.Reset();
|
|
|
|
_hookshotPull = false;
|
|
|
|
_railJump = false;
|
|
IsVisible = true;
|
|
|
|
_isRafting = false;
|
|
_isFlying = false;
|
|
|
|
_isClimbing = false;
|
|
|
|
_isTrapped = false;
|
|
_shadowComponent.IsActive = true;
|
|
|
|
_isGrabbed = false;
|
|
|
|
ShowItem = null;
|
|
_collectedShowItem = null;
|
|
_objFollower = null;
|
|
|
|
_hitRepelTime = 0;
|
|
_hitParticleTime = 0;
|
|
|
|
_hitCount = 0;
|
|
_sprite.SpriteShader = null;
|
|
|
|
_moveVelocity = Vector2.Zero;
|
|
_lastMoveVelocity = Vector2.Zero;
|
|
_hitVelocity = Vector2.Zero;
|
|
_body.Velocity = Vector3.Zero;
|
|
|
|
_body.IgnoreHeight = false;
|
|
_body.IgnoreHoles = false;
|
|
_body.DeepWaterOffset = -3;
|
|
_body.Level = 0;
|
|
_body.IsGrounded = true;
|
|
|
|
_bootsHolding = false;
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
_carriedGameObject = null;
|
|
_carriedComponent = null;
|
|
_carriedObjDrawComp = null;
|
|
|
|
_drawInstrumentEffect = false;
|
|
|
|
_diveCounter = 0;
|
|
_swimVelocity = Vector2.Zero;
|
|
|
|
if (NextMapFallStart)
|
|
{
|
|
EntityPosition.Z = 64;
|
|
|
|
_body.Velocity.Z = -3.75f;
|
|
_body.IgnoresZ = false;
|
|
_body.JumpStartHeight = EntityPosition.Z;
|
|
|
|
NextMapFallStart = false;
|
|
}
|
|
|
|
if (NextMapFallRotateStart)
|
|
{
|
|
EntityPosition.Z = 160;
|
|
|
|
_body.Velocity.Z = -3.75f;
|
|
_body.IgnoresZ = false;
|
|
_body.IsGrounded = false;
|
|
_body.JumpStartHeight = EntityPosition.Z;
|
|
|
|
_fallEntryCounter = 0;
|
|
CurrentState = State.FallRotateEntry;
|
|
|
|
NextMapFallRotateStart = false;
|
|
}
|
|
|
|
if (NextMapPositionEnd.HasValue)
|
|
SetHoleResetPosition(NextMapPositionEnd.Value);
|
|
|
|
if (Is2DMode)
|
|
MapInit2D();
|
|
|
|
// reset guardian acorn and piece of power except when in a dungeon
|
|
if (!_inDungeon || Map == null || !Map.DungeonMode)
|
|
{
|
|
Game1.GameManager.StopGuardianAcorn();
|
|
Game1.GameManager.StopPieceOfPower();
|
|
}
|
|
|
|
if (Map != null && Map.DungeonMode)
|
|
_inDungeon = true;
|
|
else
|
|
_inDungeon = false;
|
|
|
|
Game1.GameManager.UseShockEffect = false;
|
|
}
|
|
|
|
public void InitEnding()
|
|
{
|
|
CurrentState = State.Sequence;
|
|
Animation.Play("stand_1");
|
|
}
|
|
|
|
public void FinishLoadingMap(Map.Map map)
|
|
{
|
|
Map = map;
|
|
Is2DMode = map.Is2dMap;
|
|
|
|
if (NextMapPositionStart.HasValue)
|
|
SetPosition(NextMapPositionStart.Value);
|
|
|
|
MapInit();
|
|
|
|
UpdateFollower(true);
|
|
|
|
if (_objFollower != null)
|
|
_objFollower.EntityPosition.Set(NextMapPositionStart.Value);
|
|
}
|
|
|
|
public void Respawn()
|
|
{
|
|
Animation.Play((CarryShield ? "stands_" : "stand_") + Direction);
|
|
|
|
StoreItem = null;
|
|
_body.IsActive = true;
|
|
|
|
var hearts = 3;
|
|
if (Game1.GameManager.MaxHearths >= 14)
|
|
hearts = 10;
|
|
else if (Game1.GameManager.MaxHearths >= 10)
|
|
hearts = 7;
|
|
else if (Game1.GameManager.MaxHearths >= 6)
|
|
hearts = 5;
|
|
|
|
Game1.GameManager.CurrentHealth = hearts * 4;
|
|
|
|
Game1.GameManager.DeathCount++;
|
|
|
|
MapInit();
|
|
}
|
|
|
|
public void StartIntro()
|
|
{
|
|
// set the music
|
|
Game1.GameManager.SetMusic(27, 2);
|
|
|
|
CurrentState = State.Intro;
|
|
|
|
Animation.Play("intro");
|
|
|
|
NextMapPositionStart = null;
|
|
NextMapPositionEnd = null;
|
|
SetPosition(new Vector2(56, 51));
|
|
MapManager.Camera.ForceUpdate(Game1.GameManager.MapManager.GetCameraTarget());
|
|
|
|
MapManager.ObjLink.SaveMap = Map.MapName;
|
|
MapManager.ObjLink.SavePosition = new Vector2(70, 70);
|
|
MapManager.ObjLink.SaveDirection = 3;
|
|
}
|
|
|
|
public void SetPosition(Vector2 newPosition)
|
|
{
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
EntityPosition.Set(new Vector2(newPosition.X, newPosition.Y));
|
|
}
|
|
|
|
public void FreezePlayer()
|
|
{
|
|
UpdatePlayer = false;
|
|
|
|
_isWalking = false;
|
|
_bootsRunning = false;
|
|
|
|
// stop movement
|
|
// on the boat the player should still move up/down while playing the sequence
|
|
if (Map != null && !Map.Is2dMap)
|
|
{
|
|
// make sure to fall down when jumping into a game sequence
|
|
_body.Velocity.X = 0;
|
|
_body.Velocity.Y = 0;
|
|
if (CurrentState == State.Jumping || CurrentState == State.Powdering)
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
_moveVelocity = Vector2.Zero;
|
|
_hitVelocity = Vector2.Zero;
|
|
_swimVelocity = Vector2.Zero;
|
|
|
|
// stop push animation
|
|
if (CurrentState == State.Pushing)
|
|
CurrentState = State.Idle;
|
|
|
|
if (Map != null && Map.Is2dMap)
|
|
UpdateAnimation2D();
|
|
else
|
|
UpdateAnimation();
|
|
}
|
|
|
|
public bool HitPlayer(Box box, HitType type, int damage, float pushMultiplier = 1.75f)
|
|
{
|
|
var boxDir = BodyRectangle.Center - box.Center;
|
|
|
|
// if the player is standing inside the box the hit is not blockable
|
|
var blockable = Math.Abs(boxDir.X) > box.Width / 2 ||
|
|
Math.Abs(boxDir.Y) > box.Height / 2;
|
|
|
|
var intersection = BodyRectangle.GetIntersection(box.Rectangle());
|
|
var direction = BodyRectangle.Center - intersection.Center;
|
|
|
|
if (direction == Vector2.Zero)
|
|
direction = boxDir;
|
|
if (direction != Vector2.Zero)
|
|
direction.Normalize();
|
|
|
|
return HitPlayer(direction * pushMultiplier, type, damage, blockable);
|
|
}
|
|
|
|
public bool HitPlayer(Vector2 direction, HitType type, int damage, bool blockable, int damageCooldown = CooldownTime)
|
|
{
|
|
if (_hitCount > 0 ||
|
|
CurrentState == State.Dying ||
|
|
CurrentState == State.PickingUp ||
|
|
CurrentState == State.Drowning ||
|
|
CurrentState == State.Drowned ||
|
|
CurrentState == State.Knockout ||
|
|
IsDiving() ||
|
|
Game1.GameManager.UseShockEffect ||
|
|
!UpdatePlayer ||
|
|
_isTrapped)
|
|
return false;
|
|
|
|
// block the attack?
|
|
if (blockable && (CurrentState == State.Blocking || _bootsRunning && CarryShield))
|
|
{
|
|
_bootsHolding = false;
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
// is the player blocking this direction
|
|
var vectorDirection = ToDirection(-direction);
|
|
if (Direction == vectorDirection)
|
|
return false;
|
|
}
|
|
|
|
// jump a little if we get hit by a spike
|
|
if ((type & HitType.Spikes) != 0)
|
|
{
|
|
_body.Velocity.Z = 1.0f;
|
|
}
|
|
|
|
// redirect the down force to the sides
|
|
if (Map.Is2dMap && _body.IsGrounded && direction.Y > 0)
|
|
{
|
|
direction.X += Math.Sign(direction.X) * Math.Abs(direction.Y) * 0.5f;
|
|
direction.Y = 0;
|
|
}
|
|
|
|
// fall down on damage taken while climbing
|
|
if (Map.Is2dMap && _isClimbing)
|
|
_isClimbing = false;
|
|
|
|
if (!_isRafting)
|
|
_hitVelocity += direction;
|
|
|
|
if (_hitCount > 0)
|
|
return false;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D370-03-03");
|
|
|
|
_hitCount = damageCooldown;
|
|
Game1.GameManager.InflictDamage(damage);
|
|
|
|
// TODO_2: this should be optional (in config file or game settings?)
|
|
//if(false)
|
|
{
|
|
// freeze the screen and shake
|
|
var freezeTime = 67;
|
|
var shakeMult = (100.0f / freezeTime) * MathF.PI;
|
|
Game1.FreezeTime = Game1.TotalGameTime + freezeTime;
|
|
Game1.GameManager.ShakeScreen(freezeTime, (int)(direction.X * 2), (int)(direction.Y * 2), shakeMult, shakeMult);
|
|
UpdateDamageShader();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void FreezeAnimationState()
|
|
{
|
|
CurrentState = State.Frozen;
|
|
Animation.Pause();
|
|
}
|
|
|
|
public void StartOcarinaDuo()
|
|
{
|
|
CurrentState = State.Ocarina;
|
|
|
|
_ocarinaNoteIndex = 0;
|
|
_ocarinaCounter = 0;
|
|
|
|
Animation.Play("ocarina_duo");
|
|
}
|
|
|
|
public void StopOcarinaDuo()
|
|
{
|
|
CurrentState = State.Idle;
|
|
}
|
|
|
|
public void StartFlying(ObjCock objCock)
|
|
{
|
|
_isFlying = true;
|
|
_objRooster = objCock;
|
|
}
|
|
|
|
public void StopFlying()
|
|
{
|
|
_isFlying = false;
|
|
|
|
_body.IgnoresZ = false;
|
|
_body.IsGrounded = false;
|
|
_body.JumpStartHeight = 0;
|
|
|
|
_lastMoveVelocity = Vector2.Zero;
|
|
|
|
if (_objRooster != null)
|
|
_objRooster.StopFlying();
|
|
}
|
|
|
|
public void SeqLockPlayer()
|
|
{
|
|
UpdatePlayer = false;
|
|
|
|
if (Map.Is2dMap)
|
|
UpdateAnimation2D();
|
|
else
|
|
UpdateAnimation();
|
|
}
|
|
|
|
public void LockPlayer()
|
|
{
|
|
_isLocked = true;
|
|
}
|
|
|
|
public void TrapPlayer(bool disableItems = false)
|
|
{
|
|
_isTrapped = true;
|
|
_trappedDisableItems = disableItems;
|
|
_trapInteractionCount = 8;
|
|
}
|
|
|
|
public bool StealShield()
|
|
{
|
|
// steal the shield if it is in the first 4 slots
|
|
for (var i = 0; i < 4; i++)
|
|
{
|
|
if (Game1.GameManager.Equipment[i] != null &&
|
|
Game1.GameManager.Equipment[i].Name == "shield")
|
|
{
|
|
Game1.GameManager.RemoveItem("shield", 1);
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public void FreeTrappedPlayer()
|
|
{
|
|
_isTrapped = false;
|
|
}
|
|
|
|
public void ShortenDive()
|
|
{
|
|
_diveCounter = 350;
|
|
}
|
|
|
|
public void StartRaftRiding(ObjRaft objRaft)
|
|
{
|
|
if (CurrentState != State.Jumping)
|
|
CurrentState = State.Rafting;
|
|
|
|
_isRafting = true;
|
|
_objRaft = objRaft;
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
_body.IgnoreHeight = true;
|
|
}
|
|
|
|
public void RaftJump(Vector2 targetPosition)
|
|
{
|
|
if (CurrentState == State.Jumping)
|
|
return;
|
|
|
|
CurrentState = State.Jumping;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-13-0D");
|
|
|
|
Direction = 3;
|
|
Animation.Play("jump_" + Direction);
|
|
|
|
if (_objRaft != null)
|
|
{
|
|
_objRaft.Jump(targetPosition, 100);
|
|
}
|
|
}
|
|
|
|
public void ExitRaft()
|
|
{
|
|
CurrentState = State.Idle;
|
|
|
|
_isRafting = false;
|
|
_objRaft = null;
|
|
|
|
EntityPosition.Set(new Vector2(EntityPosition.X, EntityPosition.Y - 1));
|
|
}
|
|
|
|
public void SetHoleResetPosition(Vector2 position, int direction)
|
|
{
|
|
if (direction == 0)
|
|
_alternativeHoleResetPosition = new Vector2(position.X + MathF.Ceiling(_body.Width / 2f), position.Y + 8 + MathF.Ceiling(_body.Height / 2f));
|
|
else if (direction == 1)
|
|
_alternativeHoleResetPosition = new Vector2(position.X + 8, position.Y + _body.Height);
|
|
else if (direction == 2)
|
|
_alternativeHoleResetPosition = new Vector2(position.X + 16 - MathF.Ceiling(_body.Width / 2f), position.Y + 8 + MathF.Ceiling(_body.Height / 2f));
|
|
else if (direction == 3)
|
|
_alternativeHoleResetPosition = new Vector2(position.X + 8, position.Y + 16);
|
|
|
|
// also used for the drown reseet point
|
|
_drownResetPosition = _alternativeHoleResetPosition;
|
|
}
|
|
|
|
public void Knockout(Vector2 direction, string resetDoor)
|
|
{
|
|
if (CurrentState == State.Knockout)
|
|
return;
|
|
|
|
CurrentState = State.Knockout;
|
|
|
|
MapTransitionStart = MapManager.ObjLink.EntityPosition.Position;
|
|
MapTransitionEnd = MapManager.ObjLink.EntityPosition.Position + direction * 80;
|
|
TransitionOutWalking = false;
|
|
|
|
// append a map change
|
|
var transitionSystem = ((MapTransitionSystem)Game1.GameManager.GameSystems[typeof(MapTransitionSystem)]);
|
|
transitionSystem.AppendMapChange(Map.MapName, resetDoor, false, false, Color.White, false);
|
|
transitionSystem.StartKnockoutTransition = true;
|
|
}
|
|
|
|
public void GroundStun(int stunTime = 1250)
|
|
{
|
|
// do not stun the player when he is in the air
|
|
if (_body.IsGrounded && CurrentState != State.Jumping)
|
|
Stun(stunTime);
|
|
}
|
|
|
|
public void Stun(int stunTime, bool particle = false)
|
|
{
|
|
if (CurrentState == State.Dying)
|
|
return;
|
|
|
|
CurrentState = State.InitStunned;
|
|
|
|
_stunnedParticles = particle;
|
|
_stunnedCounter = stunTime;
|
|
}
|
|
|
|
public void StartGrab()
|
|
{
|
|
_isGrabbed = true;
|
|
}
|
|
|
|
public void EndGrab()
|
|
{
|
|
_isGrabbed = false;
|
|
}
|
|
|
|
public void StartThrow(Vector3 direction)
|
|
{
|
|
_body.Velocity = direction;
|
|
_body.IsGrounded = false;
|
|
_body.JumpStartHeight = 0;
|
|
}
|
|
|
|
public void StartHoldingItem(GameItem item)
|
|
{
|
|
CurrentState = State.CarryingItem;
|
|
|
|
StoreItem = item;
|
|
|
|
_storeItemWidth = item.SourceRectangle.Value.Width;
|
|
_storeItemHeight = item.SourceRectangle.Value.Height;
|
|
|
|
EntityPosition.AddPositionListener(typeof(ObjLink), UpdateStoreItemPosition);
|
|
UpdateStoreItemPosition(EntityPosition);
|
|
|
|
Game1.GameManager.SaveManager.SetString("holdItem", "1");
|
|
}
|
|
|
|
public void StopHoldingItem()
|
|
{
|
|
CurrentState = State.Idle;
|
|
|
|
StoreItem = null;
|
|
|
|
// this removes all listeners with the ObjLink as a key
|
|
EntityPosition.PositionChangedDict.Remove(typeof(ObjLink));
|
|
|
|
Game1.GameManager.SaveManager.SetString("holdItem", "0");
|
|
}
|
|
|
|
public void SlowDown(float speed)
|
|
{
|
|
if (CurrentState != State.Jumping)
|
|
_currentWalkSpeed = speed;
|
|
}
|
|
|
|
public void StartBedTransition()
|
|
{
|
|
_startBedTransition = true;
|
|
}
|
|
|
|
public void StartJump()
|
|
{
|
|
if (CurrentState != State.Dying && CurrentState != State.PickingUp)
|
|
Jump(true);
|
|
}
|
|
|
|
public void StartRailJump(Vector2 goalPosition, float jumpHeightMultiply, float jumpSpeedMultiply, float goalPositionZ = 0)
|
|
{
|
|
if (CurrentState == State.Swimming)
|
|
CurrentState = State.Idle;
|
|
|
|
if (!Jump(false, false))
|
|
return;
|
|
|
|
Game1.GameManager.PlaySoundEffect("D360-08-08");
|
|
|
|
_railJump = true;
|
|
|
|
_railJumpStartPosition = EntityPosition.Position;
|
|
_railJumpTargetPosition = goalPosition;
|
|
|
|
// values for distance of 16
|
|
_railJumpSpeed = 0.045f * jumpSpeedMultiply;
|
|
_railJumpHeight = 12 * jumpHeightMultiply;
|
|
_railJumpPositionZ = goalPositionZ;
|
|
|
|
_railJumpPercentage = 0;
|
|
|
|
_body.IgnoreHeight = true;
|
|
_body.IgnoresZ = true;
|
|
_body.Velocity.Z = 0;
|
|
}
|
|
|
|
public Vector2 RailJumpTarget()
|
|
{
|
|
return _railJumpTargetPosition;
|
|
}
|
|
|
|
public float RailJumpSpeed()
|
|
{
|
|
return _railJumpSpeed;
|
|
}
|
|
|
|
public float RailJumpHeight()
|
|
{
|
|
return _railJumpHeight;
|
|
}
|
|
|
|
public float GetRailJumpAmount()
|
|
{
|
|
if (!_railJump)
|
|
return 0;
|
|
|
|
return _railJumpPercentage;
|
|
}
|
|
|
|
public void RotatePlayer()
|
|
{
|
|
if (_bootsRunning)
|
|
return;
|
|
|
|
_rotationCounter += Game1.DeltaTime;
|
|
// 8 frames per direction
|
|
if (_rotationCounter > 133)
|
|
{
|
|
_rotationCounter -= 133;
|
|
if (!_isWalking)
|
|
{
|
|
Direction = (Direction + 1) % 4;
|
|
// rotate the sword if the player is currently charging
|
|
if (CurrentState == State.Charging)
|
|
AnimatorWeapons.Play("stand_" + Direction);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void StartTeleportation(ObjDungeonTeleporter teleporter)
|
|
{
|
|
_teleporter = teleporter;
|
|
|
|
CurrentState = State.Teleporting;
|
|
_drawBody.Layer = Values.LayerTop;
|
|
|
|
_teleportState = 0;
|
|
_teleportCounter = 0;
|
|
_teleportCounterFull = 0;
|
|
|
|
}
|
|
|
|
public void ShockPlayer(int time)
|
|
{
|
|
// stop running to not continuously run into the enemy
|
|
_bootsHolding = false;
|
|
_bootsRunning = false;
|
|
_bootsCounter = 0;
|
|
|
|
CurrentState = State.Idle;
|
|
|
|
// shock the player
|
|
Game1.GameManager.UseShockEffect = true;
|
|
|
|
Game1.GameManager.ShakeScreen(time, 4, 0, 8.5f, 0);
|
|
|
|
Game1.GameManager.InflictDamage(4);
|
|
}
|
|
|
|
public void StartHookshotPull()
|
|
{
|
|
_hookshotPull = true;
|
|
if (Map.Is2dMap)
|
|
{
|
|
_body.Velocity.Y = 0;
|
|
_body.LastVelocityCollision = Values.BodyCollision.None;
|
|
}
|
|
|
|
// if the player is on the upper level he will not get pulled through water and we can move through colliders
|
|
if ((_body.CurrentFieldState & MapStates.FieldStates.UpperLevel) != 0)
|
|
{
|
|
_body.IsGrounded = false;
|
|
_body.Level = MapStates.GetLevel(_body.CurrentFieldState);
|
|
}
|
|
}
|
|
|
|
public bool UpdateHookshotPull()
|
|
{
|
|
var distance = _body.BodyBox.Box.Center - Hookshot.HookshotPosition.Position;
|
|
var pullVector = AnimationHelper.DirectionOffset[Direction];
|
|
|
|
// reached the end of the hook or collided with an object before
|
|
if (distance.Length() < (distance + pullVector).Length() ||
|
|
(_body.LastVelocityCollision != Values.BodyCollision.None && (_body.SlideOffset == Vector2.Zero || _body.BodyBox.Box.Contains(Hookshot.HookshotPosition.Position))) ||
|
|
CurrentState == State.Dying)
|
|
{
|
|
_hookshotPull = false;
|
|
_body.IgnoresZ = false;
|
|
_body.IgnoreHoles = false;
|
|
_body.Level = 0;
|
|
return false;
|
|
}
|
|
|
|
_body.VelocityTarget = pullVector * 3;
|
|
|
|
return true;
|
|
}
|
|
|
|
public void StartTeleportation(string teleportMap, string teleporterId)
|
|
{
|
|
_teleporter = null;
|
|
|
|
CurrentState = State.Teleporting;
|
|
_drawBody.Layer = Values.LayerTop;
|
|
|
|
_teleportMap = teleportMap;
|
|
_teleporterId = teleporterId;
|
|
_teleportState = 0;
|
|
_teleportCounter = 0;
|
|
_teleportCounterFull = 0;
|
|
|
|
ReleaseCarriedObject();
|
|
}
|
|
|
|
public void StartWorldTelportation(Vector2 newPosition)
|
|
{
|
|
CurrentState = State.TeleportFallWait;
|
|
|
|
var positionDistance = EntityPosition.Position - newPosition;
|
|
var fallPosition = new Vector3(newPosition.X, newPosition.Y, 128);
|
|
EntityPosition.Set(fallPosition);
|
|
|
|
if (_objFollower != null)
|
|
{
|
|
var itemGhost = Game1.GameManager.GetItem("ghost");
|
|
if (itemGhost != null && itemGhost.Count >= 0)
|
|
_objFollower.EntityPosition.Set(new Vector2(fallPosition.X, fallPosition.Y));
|
|
else
|
|
_objFollower.EntityPosition.Set(fallPosition);
|
|
}
|
|
|
|
// only jump to the new position if it is a different teleporter at a different location
|
|
if (positionDistance.Length() > 64)
|
|
MapManager.Camera.ForceUpdate(Game1.GameManager.MapManager.GetCameraTarget());
|
|
}
|
|
|
|
public void SetWalkingDirection(int direction)
|
|
{
|
|
Direction = direction;
|
|
UpdateAnimation();
|
|
}
|
|
|
|
public void PickUpItem(GameItemCollected itemCollected, bool showItem, bool showDialog = true, bool playSound = true)
|
|
{
|
|
if (itemCollected == null)
|
|
return;
|
|
|
|
var item = Game1.GameManager.ItemManager[itemCollected.Name];
|
|
// the base item has the max count and other information
|
|
var baseItem = Game1.GameManager.ItemManager[item.Name];
|
|
|
|
// save the game before entering the show animation to support exiting the game while the item is shown
|
|
_savedPreItemPickup = true;
|
|
if (item.PickUpDialog != null && !Game1.GameManager.SaveManager.HistoryEnabled)
|
|
{
|
|
SaveGameSaveLoad.FillSaveState(Game1.GameManager);
|
|
Game1.GameManager.SaveManager.EnableHistory();
|
|
}
|
|
|
|
_showItem = false;
|
|
_pickingUpInstrument = false;
|
|
_pickingUpSword = false;
|
|
|
|
// upgrade the sword
|
|
var equipmentPosition = 0;
|
|
if (item.Name == "sword1")
|
|
{
|
|
_pickingUpSword = true;
|
|
Game1.GameManager.SetMusic(14, 2);
|
|
}
|
|
else if (item.Name == "sword2")
|
|
{
|
|
equipmentPosition = Game1.GameManager.GetEquipmentSlot("sword1");
|
|
Game1.GameManager.RemoveItem("sword1", 99);
|
|
Game1.GameManager.CollectItem(itemCollected, equipmentPosition);
|
|
Game1.GameManager.SetMusic(14, 2);
|
|
}
|
|
else if (item.Name == "mirrorShield")
|
|
{
|
|
equipmentPosition = Game1.GameManager.GetEquipmentSlot("shield");
|
|
Game1.GameManager.RemoveItem("shield", 99);
|
|
Game1.GameManager.CollectItem(itemCollected, equipmentPosition);
|
|
}
|
|
else if (baseItem.Name == "shield")
|
|
{
|
|
var mirrorShield = Game1.GameManager.GetItem("mirrorShield");
|
|
if (mirrorShield != null)
|
|
{
|
|
Game1.GameManager.PlaySoundEffect(item.SoundEffectName, true, 1, 0, item.TurnDownMusic);
|
|
return;
|
|
}
|
|
}
|
|
else if (itemCollected.Name == "stonelifter2")
|
|
{
|
|
equipmentPosition = Game1.GameManager.GetEquipmentSlot("stonelifter");
|
|
Game1.GameManager.RemoveItem("stonelifter", 99);
|
|
Game1.GameManager.CollectItem(itemCollected, equipmentPosition);
|
|
}
|
|
else if (itemCollected.Name == "heartMeterFull")
|
|
{
|
|
Game1.GameManager.SetMusic(36, 2);
|
|
}
|
|
else if (itemCollected.Name == "heartMeter")
|
|
{
|
|
var heart = Game1.GameManager.GetItem("heartMeter");
|
|
// hearts was expanded => show different dialog
|
|
if (heart?.Count == 3)
|
|
_additionalPickupDialog = "heartMeterFilled";
|
|
}
|
|
|
|
// hearth
|
|
if (item.Name == "heart")
|
|
{
|
|
Game1.GameManager.CurrentHealth += itemCollected.Count * 4;
|
|
|
|
if (Game1.GameManager.CurrentHealth > Game1.GameManager.MaxHearths * 4)
|
|
Game1.GameManager.CurrentHealth = Game1.GameManager.MaxHearths * 4;
|
|
}
|
|
// pick up item is an accessory
|
|
else if ((item.ShowAnimation == 1 || item.ShowAnimation == 2) && showItem)
|
|
{
|
|
// stop player movement
|
|
_body.Velocity = Vector3.Zero;
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
_moveVelocity = Vector2.Zero;
|
|
_hitVelocity = Vector2.Zero;
|
|
|
|
// pick up and show an item
|
|
ShowItem = item;
|
|
|
|
// hold the item over the head with one or two hands (to the left side or the middle)
|
|
if (item.ShowAnimation == 1)
|
|
_showItemOffset.X = 0;
|
|
else
|
|
_showItemOffset.X = -4;
|
|
|
|
_showItemOffset.Y = -15;
|
|
|
|
if (ShowItem.Name == "guardianAcorn")
|
|
Game1.GameManager.InitGuardianAcorn();
|
|
else if (ShowItem.Name == "pieceOfPower")
|
|
Game1.GameManager.InitPieceOfPower();
|
|
|
|
// @HACK: piece of power shows the sword image when picked up
|
|
if (ShowItem.Name == "pieceOfPower")
|
|
{
|
|
var swordItem = Game1.GameManager.GetItem("sword1");
|
|
if (swordItem != null && swordItem.Count > 0)
|
|
ShowItem = Game1.GameManager.ItemManager["sword1PoP"];
|
|
else
|
|
ShowItem = Game1.GameManager.ItemManager["sword2PoP"];
|
|
}
|
|
|
|
// make sure to use the right source rectangle if the shown item does not have one
|
|
var sourceRectangle = ShowItem.SourceRectangle ?? baseItem.SourceRectangle.Value;
|
|
if (ShowItem.MapSprite != null)
|
|
sourceRectangle = ShowItem.MapSprite.SourceRectangle;
|
|
else if (baseItem.MapSprite != null)
|
|
sourceRectangle = baseItem.MapSprite.SourceRectangle;
|
|
|
|
// spawn pickup animation
|
|
if (item.ShowEffect)
|
|
Map.Objects.SpawnObject(new ObjPickupAnimation(Map,
|
|
EntityPosition.X + _showItemOffset.X, EntityPosition.Y - EntityPosition.Z + _showItemOffset.Y - sourceRectangle.Height / 2));
|
|
|
|
_showItemOffset -= new Vector2(sourceRectangle.Width / 2f, sourceRectangle.Height);
|
|
|
|
CurrentState = State.PickingUp;
|
|
Game1.GameManager.SaveManager.SetString("player_shows_item", "1");
|
|
Animation.Play("show" + item.ShowAnimation);
|
|
_itemShowCounter = item.ShowTime;
|
|
_showItem = true;
|
|
|
|
// make sure to collect the item the player is currently showing
|
|
if (_collectedShowItem != null)
|
|
Game1.GameManager.CollectItem(_collectedShowItem, 0);
|
|
|
|
_collectedShowItem = itemCollected;
|
|
|
|
if (ShowItem.Name == "sword2")
|
|
{
|
|
_shownSwordLv2Dialog = false;
|
|
_showSwordL2ParticleCounter = 0;
|
|
CurrentState = State.SwordShowLv2;
|
|
}
|
|
|
|
// not sure if this is what should happen here
|
|
ReleaseCarriedObject();
|
|
}
|
|
else
|
|
{
|
|
Game1.GameManager.CollectItem(itemCollected, equipmentPosition);
|
|
}
|
|
|
|
if (item.Name.StartsWith("instrument"))
|
|
{
|
|
// stop playing music
|
|
Game1.GameManager.SetMusic(26, 2);
|
|
|
|
_instrumentPickupTime = Game1.TotalGameTime;
|
|
|
|
_instrumentIndex = int.Parse(item.Name.Replace("instrument", ""));
|
|
_pickingUpInstrument = true;
|
|
}
|
|
|
|
if (item.PickUpDialog != null && !_showItem && showDialog)
|
|
{
|
|
Game1.GameManager.StartDialogPath(item.PickUpDialog);
|
|
}
|
|
|
|
// play sound
|
|
if (playSound && item.SoundEffectName != null)
|
|
Game1.GameManager.PlaySoundEffect(item.SoundEffectName, true, 1, 0, item.TurnDownMusic);
|
|
if (item.MusicName >= 0)
|
|
Game1.GameManager.SetMusic(item.MusicName, 1);
|
|
}
|
|
|
|
#region map change
|
|
|
|
public void SetNextMapPosition(Vector2 playerPosition)
|
|
{
|
|
// this will be used to set the position of the player after loading the map
|
|
// one of them should always be null
|
|
// the playerPosition is used after loading a savestate
|
|
NextMapPositionStart = playerPosition;
|
|
NextMapPositionEnd = playerPosition;
|
|
|
|
NextMapPositionId = null;
|
|
}
|
|
|
|
public void SetNextMapPosition(string nextMapPositionId)
|
|
{
|
|
// this will be used to set the position of the player after loading the map
|
|
// one of them should always be null
|
|
// the nextMapPositionId is used after going though a door
|
|
NextMapPositionId = nextMapPositionId;
|
|
|
|
NextMapPositionStart = null;
|
|
NextMapPositionEnd = null;
|
|
}
|
|
|
|
public void OnAppendMapChange()
|
|
{
|
|
if (_objMaria != null)
|
|
_objMaria.OnAppendMapChange();
|
|
}
|
|
|
|
public void StartTransitioning()
|
|
{
|
|
IsTransitioning = true;
|
|
|
|
_drawBody.Layer = Values.LayerTop;
|
|
|
|
// if the transitioning starts from a jump the player would have no animation otherwise
|
|
//_moved = true;
|
|
_isWalking = true;
|
|
_bootsRunning = false;
|
|
|
|
// stole item?
|
|
if (StoreItem != null)
|
|
StealItem();
|
|
|
|
ReleaseCarriedObject();
|
|
|
|
// release the cock if link is flying
|
|
if (MapManager.ObjLink.IsFlying())
|
|
MapManager.ObjLink.StopFlying();
|
|
|
|
// make sure the player walks
|
|
if (MapTransitionStart.HasValue && MapTransitionEnd.HasValue &&
|
|
CurrentState != State.Swimming && CurrentState != State.BedTransition && CurrentState != State.Knockout && CurrentState != State.OcarinaTelport)
|
|
CurrentState = State.Idle;
|
|
|
|
_body.VelocityTarget = Vector2.Zero;
|
|
|
|
if (Map.Is2dMap)
|
|
{
|
|
if (_ladderCollision)
|
|
{
|
|
_isClimbing = true;
|
|
Direction = 1;
|
|
}
|
|
|
|
// prefent the player from falling down while climbing up a ladder
|
|
//if ((Direction % 2) != 0)
|
|
_body.IgnoresZ = true;
|
|
// fall down
|
|
//else if (_body.Velocity.Y < 0)
|
|
_body.Velocity.Y = 0.0f;
|
|
}
|
|
else
|
|
{
|
|
_body.Velocity = Vector3.Zero;
|
|
}
|
|
}
|
|
|
|
public void UpdateMapTransitionOut(float state)
|
|
{
|
|
if (MapTransitionStart.HasValue && MapTransitionEnd.HasValue)
|
|
{
|
|
var newPosition = Vector2.Lerp(MapTransitionStart.Value, MapTransitionEnd.Value, state);
|
|
|
|
// fall down to the ground
|
|
//if (Map.Is2dMap && (Direction % 2) == 0)
|
|
// newPosition.Y = EntityPosition.Y;
|
|
|
|
SetPosition(newPosition);
|
|
}
|
|
|
|
// lock the camera while transitioning
|
|
if (!Map.Is2dMap || Direction == 1)
|
|
Game1.GameManager.MapManager.UpdateCameraY = MapTransitionStart == MapTransitionEnd;
|
|
|
|
_isWalking = TransitionOutWalking;
|
|
|
|
if (Is2DMode)
|
|
UpdateAnimation2D();
|
|
else
|
|
UpdateAnimation();
|
|
}
|
|
|
|
public void UpdateMapTransitionIn(float state)
|
|
{
|
|
// make sure to not start falling while transitioning into a 2d map with a ladder
|
|
if (state == 0 && Map.Is2dMap)
|
|
_body.IgnoresZ = true;
|
|
|
|
if (DirectionEntry >= 0)
|
|
Direction = DirectionEntry;
|
|
|
|
if (NextMapPositionStart.HasValue && NextMapPositionEnd.HasValue)
|
|
{
|
|
var newPosition = Vector2.Lerp(NextMapPositionStart.Value, NextMapPositionEnd.Value, state);
|
|
SetPosition(newPosition);
|
|
|
|
// transition the follower out
|
|
if (_objFollower != null && NextMapPositionStart.Value != NextMapPositionEnd.Value)
|
|
{
|
|
var followerPosition = Vector2.Lerp(NextMapPositionStart.Value, NextMapPositionEnd.Value, state * 0.5f);
|
|
_objFollower.SetPosition(followerPosition);
|
|
}
|
|
}
|
|
|
|
// lock the camera while transitioning
|
|
if (!Map.Is2dMap || Direction == 1)
|
|
Game1.GameManager.MapManager.UpdateCameraY = NextMapPositionStart == NextMapPositionEnd;
|
|
|
|
_isWalking = TransitionInWalking;
|
|
|
|
// set the hole and water reset position to be at the transition entrance
|
|
_holeResetPoint = EntityPosition.Position;
|
|
_drownResetPosition = EntityPosition.Position;
|
|
|
|
UpdateSwimming();
|
|
|
|
UpdateIgnoresZ();
|
|
|
|
if (Is2DMode)
|
|
UpdateAnimation2D();
|
|
else
|
|
UpdateAnimation();
|
|
}
|
|
|
|
public void EndTransitioning()
|
|
{
|
|
_body.HoleAbsorption = Vector2.Zero;
|
|
|
|
IsTransitioning = false;
|
|
|
|
if (!Map.Is2dMap)
|
|
{
|
|
_body.Velocity.X = 0;
|
|
_body.Velocity.Y = 0;
|
|
}
|
|
|
|
// this is because the water is deeper than 0
|
|
if ((SystemBody.GetFieldState(_body) & MapStates.FieldStates.DeepWater) == 0 && CurrentState != State.Swimming && !_isClimbing)
|
|
_body.IgnoresZ = false;
|
|
|
|
_drawBody.Layer = Values.LayerPlayer;
|
|
|
|
MapManager.Camera.CameraFollowMultiplier = 1.0f;
|
|
|
|
if (_showStealMessage)
|
|
{
|
|
_showStealMessage = false;
|
|
Game1.GameManager.StartDialogPath("shopkeeper_steal");
|
|
}
|
|
|
|
// restart the music
|
|
if (Game1.GameManager.PieceOfPowerIsActive || Game1.GameManager.GuardianAcornIsActive)
|
|
Game1.GameManager.StartPieceOfPowerMusic();
|
|
}
|
|
|
|
#endregion
|
|
|
|
public Vector2 GetSwimVelocity()
|
|
{
|
|
return _swimVelocity;
|
|
}
|
|
|
|
public ObjMarin GetMarin()
|
|
{
|
|
return _objMaria;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region is functions
|
|
|
|
public bool IsDiving()
|
|
{
|
|
return _diveCounter > 0;
|
|
}
|
|
|
|
public bool IsGrounded()
|
|
{
|
|
return _body.IsGrounded && !_railJump && !_isFlying;
|
|
}
|
|
|
|
public bool IsJumping()
|
|
{
|
|
return CurrentState == State.Jumping;
|
|
}
|
|
|
|
public bool IsRailJumping()
|
|
{
|
|
return _railJump;
|
|
}
|
|
|
|
public bool IsHoleAbsorb()
|
|
{
|
|
return _isFallingIntoHole;
|
|
}
|
|
|
|
public bool IsDashing()
|
|
{
|
|
return _bootsRunning;
|
|
}
|
|
|
|
public bool IsStunned()
|
|
{
|
|
return CurrentState == State.Stunned;
|
|
}
|
|
|
|
public bool IsTrapped()
|
|
{
|
|
return _isTrapped;
|
|
}
|
|
|
|
public bool IsFlying()
|
|
{
|
|
return _isFlying && CurrentState == State.Carrying;
|
|
}
|
|
|
|
public bool IsUsingHookshot()
|
|
{
|
|
return CurrentState == State.Hookshot;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|