#pragma once #include "olcUTIL_Animate2D.h" #include "Animation.h" #include "Monster.h" #include "State.h" #include "Ability.h" #include "Class.h" #include "Buff.h" #include "Pathfinding.h" #include "DamageNumber.h" #include "config.h" #include "Key.h" struct CastInfo{ std::string name; float castTimer; float castTotalTime; vf2d castPos; }; struct Player{ friend class Crawler; friend class sig::Animation; friend class Warrior; friend class Thief; friend class Ranger; friend class Trapper; friend class Wizard; friend class Witch; private: int hp="Player.BaseHealth"_I,maxhp=hp; int mana="Player.BaseMana"_I,maxmana=mana; int atk="Player.BaseAtk"_I; vf2d pos; float z=0; float moveSpd=1.0f; float size=1.0f; float spin_attack_timer=0; float spin_spd=0; float spin_angle=0; float lastAnimationFlip=0; float manaTickTimer=0; std::pair notEnoughManaDisplay={"",0}; float teleportAttemptWaitTime=0; //If a teleport fails, we wait awhile before trying again, it's expensive. State::State state=State::NORMAL; Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; Key lastReleasedMovementKey; void Update(float fElapsedTime); void AddAnimation(std::string state); std::vectorbuffList; CastInfo castInfo={"",0}; vf2d movementVelocity={};//This tells us if the player is moving (mostly controlled by user input) since their velocity is not used for regular movement. float lastHitTimer=0; //When this is greater than zero, if we get hit again it adds to our displayed combo number. std::shared_ptrdamageNumberPtr; void Initialize(); float iframe_time=0; protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; float ARROW_ATTACK_COOLDOWN="Ranger.Auto Attack.Cooldown"_F; void SetSwordSwingTimer(float val); void SetState(State::State newState); void SetFacingDirection(Key direction); void SetLastReleasedMovementKey(Key k); void Spin(float duration,float spinSpd); //Returns true if the move was valid and successful. bool SetX(float x); //Returns true if the move was valid and successful. bool SetY(float y); void SetZ(float z); //Returns true if the move was valid and successful. bool SetPos(vf2d pos); float friction="Player.Friction"_F; float attack_cooldown_timer=0; float teleportAnimationTimer=0; vf2d teleportTarget={}; vf2d teleportStartPosition={}; std::pair notificationDisplay={"",0}; bool upperLevel=false; vf2d vel={0,0}; float attack_range="Warrior.Auto Attack.Range"_F/100.f; Key facingDirection=DOWN; float swordSwingTimer=0; void CastSpell(Ability&ability); Ability*castPrepAbility; void PrepareCast(Ability&ability); vf2d precastLocation={}; void SetVelocity(vf2d vel); const float RETREAT_DISTANCE=24*"Ranger.Right Click Ability.RetreatDistance"_F/100; float RETREAT_TIME="Ranger.Right Click Ability.RetreatTime"_F; //How long the Retreat ability takes. const int RETREAT_GHOST_FRAMES=8; const float RETREAT_GHOST_FRAME_DELAY=0.025; float ghostFrameTimer=0; float ghostRemoveTimer=0; float blockTimer=0; float retreatTimer=0; std::vectorghostPositions; float rapidFireTimer=0; int remainingRapidFireShots=0; const float RAPID_FIRE_SHOOT_DELAY="Ranger.Ability 1.ArrowDelay"_F; const int RAPID_FIRE_SHOOT_AMOUNT="Ranger.Ability 1.ArrowCount"_I; public: Player(); //So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class //using a new object type... Because of that we'll take the pointer reference to the old object and copy some of its properties to this new //one. It's hackish but it means we can reduce the amount of extra boilerplate when class changing...I don't know how to feel about this. Player(Player*player); static float GROUND_SLAM_SPIN_TIME; vf2d&GetPos(); float GetX(); float GetY(); float GetZ(); int GetHealth(); int GetMaxHealth(); int GetMana(); int GetMaxMana(); int GetAttack(); float GetMoveSpdMult(); float GetSizeMult(); float GetAttackRangeMult(); float GetSpinAngle(); State::State GetState(); Key GetFacingDirection(); vf2d GetVelocity(); bool HasIframes(); void UpdateWalkingAnimation(Key direction); void UpdateIdleAnimation(Key direction); //The range is the search range in tiles. bool CanPathfindTo(vf2d pos,vf2d targetPos,float range=8); bool CanMove(); bool CanAct(); bool CanAct(Ability&ability); void Knockback(vf2d vel); void SetIframes(float duration); void AddBuff(BuffType type,float duration,float intensity); std::vectorGetBuffs(BuffType buff); void RemoveBuff(BuffType type); //Removes the first buff found. void RemoveAllBuffs(BuffType type); //Removes all buffs of a certain type. void RemoveAllBuffs(); //Remove every buff. bool Hurt(int damage,bool onUpperLevel,float z); //specificClass is a bitwise-combination of classes from the Class enum. It makes sure certain animations only play if you are a certain class. void UpdateAnimation(std::string animState,int specificClass=ANY); Animate2D::Frame GetFrame(); Key GetLastReleasedMovementKey(); float GetSwordSwingTimer(); bool OnUpperLevel(); //Triggers when the player has moved. void Moved(); virtual ~Player()=default; virtual Class GetClass()=0; virtual bool AutoAttack()=0; virtual void OnUpdate(float fElapsedTime)=0; virtual std::string GetClassName()=0; virtual Ability&GetRightClickAbility()=0; virtual Ability&GetAbility1()=0; virtual Ability&GetAbility2()=0; virtual Ability&GetAbility3()=0; virtual Ability&GetAbility4()=0; virtual std::string&GetWalkNAnimation()=0; virtual std::string&GetWalkEAnimation()=0; virtual std::string&GetWalkSAnimation()=0; virtual std::string&GetWalkWAnimation()=0; virtual std::string&GetIdleNAnimation()=0; virtual std::string&GetIdleEAnimation()=0; virtual std::string&GetIdleSAnimation()=0; virtual std::string&GetIdleWAnimation()=0; CastInfo&GetCastInfo(); void SetAnimationBasedOnTargetingDirection(float targetDirection); static InputGroup KEY_ABILITY1, KEY_ABILITY2, KEY_ABILITY3, KEY_ABILITY4, KEY_DEFENSIVE; }; struct Warrior:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Warrior(); Warrior(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only WARRIOR-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; struct Thief:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Thief(); Thief(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only THIEF-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; struct Ranger:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Ranger(); Ranger(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only RANGER-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; struct Trapper:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Trapper(); Trapper(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only TRAPPER-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; struct Wizard:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Wizard(); Wizard(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only WIZARD-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; struct Witch:Player{ static std::string name; static Class cl; static Ability rightClickAbility,ability1,ability2,ability3,ability4; static std::string walk_n,walk_e,walk_s,walk_w,idle_n,idle_e,idle_s,idle_w; static void Initialize(); Witch(); Witch(Player*player); Class GetClass()override; bool AutoAttack()override; //Include only WITCHs-specific implementations! void OnUpdate(float fElapsedTime)override; static void InitializeClassAbilities(); std::string GetClassName()override; Ability&GetRightClickAbility()override; Ability&GetAbility1()override; Ability&GetAbility2()override; Ability&GetAbility3()override; Ability&GetAbility4()override; std::string&GetWalkNAnimation()override; std::string&GetWalkEAnimation()override; std::string&GetWalkSAnimation()override; std::string&GetWalkWAnimation()override; std::string&GetIdleNAnimation()override; std::string&GetIdleEAnimation()override; std::string&GetIdleSAnimation()override; std::string&GetIdleWAnimation()override; }; #define READFROMCONFIG(class,enum) \ class::name=#class".ClassName"_S; \ class::cl=enum; \ class::rightClickAbility={ \ #class".Right Click Ability.Name"_S, \ #class".Right Click Ability.Short Name"_S, \ #class".Right Click Ability.Cooldown"_F, \ #class".Right Click Ability.Mana Cost"_I, \ &KEY_DEFENSIVE, \ #class".Right Click Ability.Icon"_S, \ {uint8_t(#class".Right Click Ability.Cooldown Bar Color 1"_f[0]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 1"_f[1]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 1"_f[2]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 1"_f[3]==0?255:#class".Right Click Ability.Cooldown Bar Color 1"_f[3])}, \ {uint8_t(#class".Right Click Ability.Cooldown Bar Color 2"_f[0]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 2"_f[1]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 2"_f[2]),uint8_t(#class".Right Click Ability.Cooldown Bar Color 2"_f[3]==0?255:#class".Right Click Ability.Cooldown Bar Color 2"_f[3])}, \ {#class".Right Click Ability.Precast Time"_F,#class".Right Click Ability.Casting Range"_I/100.f*24,#class".Right Click Ability.Casting Size"_I/100.f*24}, \ bool( #class".Right Click Ability.CancelCast"_I ) \ }; \ class::ability1={ \ #class".Ability 1.Name"_S, \ #class".Ability 1.Short Name"_S, \ #class".Ability 1.Cooldown"_F, \ #class".Ability 1.Mana Cost"_I, \ &KEY_ABILITY1, \ #class".Ability 1.Icon"_S, \ {uint8_t(#class".Ability 1.Cooldown Bar Color 1"_f[0]),uint8_t(#class".Ability 1.Cooldown Bar Color 1"_f[1]),uint8_t(#class".Ability 1.Cooldown Bar Color 1"_f[2]),uint8_t(#class".Ability 1.Cooldown Bar Color 1"_f[3]==0?255:#class".Ability 1.Cooldown Bar Color 1"_f[3])}, \ {uint8_t(#class".Ability 1.Cooldown Bar Color 2"_f[0]),uint8_t(#class".Ability 1.Cooldown Bar Color 2"_f[1]),uint8_t(#class".Ability 1.Cooldown Bar Color 2"_f[2]),uint8_t(#class".Ability 1.Cooldown Bar Color 2"_f[3]==0?255:#class".Ability 1.Cooldown Bar Color 2"_f[3])}, \ {#class".Ability 1.Precast Time"_F,#class".Ability 1.Casting Range"_I/100.f*24,#class".Ability 1.Casting Size"_I/100.f*24}, \ bool(#class".Ability 1.CancelCast"_I) \ }; \ class::ability2={ \ #class".Ability 2.Name"_S, \ #class".Ability 2.Short Name"_S, \ #class".Ability 2.Cooldown"_F, \ #class".Ability 2.Mana Cost"_I, \ &KEY_ABILITY2, \ #class".Ability 2.Icon"_S, \ {uint8_t(#class".Ability 2.Cooldown Bar Color 1"_f[0]),uint8_t(#class".Ability 2.Cooldown Bar Color 1"_f[1]),uint8_t(#class".Ability 2.Cooldown Bar Color 1"_f[2]),uint8_t(#class".Ability 2.Cooldown Bar Color 1"_f[3]==0?255:#class".Ability 2.Cooldown Bar Color 1"_f[3])}, \ {uint8_t(#class".Ability 2.Cooldown Bar Color 2"_f[0]),uint8_t(#class".Ability 2.Cooldown Bar Color 2"_f[1]),uint8_t(#class".Ability 2.Cooldown Bar Color 2"_f[2]),uint8_t(#class".Ability 2.Cooldown Bar Color 2"_f[3]==0?255:#class".Ability 2.Cooldown Bar Color 2"_f[3])}, \ {#class".Ability 2.Precast Time"_F,#class".Ability 2.Casting Range"_I/100.f*24,#class".Ability 2.Casting Size"_I/100.f*24}, \ bool(#class".Ability 2.CancelCast"_I) \ }; \ class::ability3={ \ #class".Ability 3.Name"_S, \ #class".Ability 3.Short Name"_S, \ #class".Ability 3.Cooldown"_F, \ #class".Ability 3.Mana Cost"_I, \ &KEY_ABILITY3, \ #class".Ability 3.Icon"_S, \ {uint8_t(#class".Ability 3.Cooldown Bar Color 1"_f[0]),uint8_t(#class".Ability 3.Cooldown Bar Color 1"_f[1]),uint8_t(#class".Ability 3.Cooldown Bar Color 1"_f[2]),uint8_t(#class".Ability 3.Cooldown Bar Color 1"_f[3]==0?255:#class".Ability 3.Cooldown Bar Color 1"_f[3])}, \ {uint8_t(#class".Ability 3.Cooldown Bar Color 2"_f[0]),uint8_t(#class".Ability 3.Cooldown Bar Color 2"_f[1]),uint8_t(#class".Ability 3.Cooldown Bar Color 2"_f[2]),uint8_t(#class".Ability 3.Cooldown Bar Color 2"_f[3]==0?255:#class".Ability 3.Cooldown Bar Color 2"_f[3])}, \ {#class".Ability 3.Precast Time"_F,#class".Ability 3.Casting Range"_I/100.f*24,#class".Ability 3.Casting Size"_I/100.f*24}, \ bool(#class".Ability 3.CancelCast"_I) \ }; \ class::ability4;