# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2018 - 2023 OneLoneCoder . com
Redistribution and use in source and binary forms , with or without modification ,
are permitted provided that the following conditions are met :
1. Redistributions or derivations of source code must retain the above copyright
notice , this list of conditions and the following disclaimer .
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice . This list of conditions and the following disclaimer must be
reproduced in the documentation and / or other materials provided with the distribution .
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission .
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS " AS IS " AND ANY
EXPRESS OR IMPLIED WARRANTIES , INCLUDING , BUT NOT LIMITED TO , THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED . IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT , INDIRECT ,
INCIDENTAL , SPECIAL , EXEMPLARY , OR CONSEQUENTIAL DAMAGES ( INCLUDING , BUT NOT LIMITED
TO , PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES ; LOSS OF USE , DATA , OR PROFITS ; OR
BUSINESS INTERRUPTION ) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY , WHETHER IN
CONTRACT , STRICT LIABILITY , OR TORT ( INCLUDING NEGLIGENCE OR OTHERWISE ) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE , EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE .
Portions of this software are copyright © 2023 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# pragma once
# include "olcUTIL_Animate2D.h"
# include "Animation.h"
# include "Monster.h"
# include "State.h"
# include "Ability.h"
# include "Buff.h"
# include "Pathfinding.h"
# include "config.h"
# include "Key.h"
# include "Class.h"
# include "Item.h"
# include "AttributableStat.h"
# undef GetClassName
struct DamageNumber ;
struct CastInfo {
std : : string name ;
float castTimer ;
float castTotalTime ;
vf2d castPos ;
} ;
class PlayerStats {
friend class Inventory ;
static ItemAttributable equipStats ; //The stats after gear calculations are applied.
static ItemAttributable baseStats ;
static void RecalculateEquipStats ( ) ; //Called when equipment is updated.
public :
static const int GetStat ( ItemAttribute stat ) ; //Get stats with equipment applied.
static const int GetBaseStat ( ItemAttribute stat ) ;
static void SetBaseStat ( ItemAttribute stat , int val ) ;
} ;
struct Player {
friend class Crawler ;
friend class sig : : Animation ;
friend struct Warrior ;
friend struct Thief ;
friend struct Ranger ;
friend struct Trapper ;
friend struct Wizard ;
friend struct Witch ;
friend class State_GameRun ;
friend class Inventory ;
friend void ItemOverlay : : Draw ( ) ;
private :
int hp = " Warrior.BaseHealth " _I ;
int mana = " Player.BaseMana " _I , maxmana = mana ;
float hpGrowthRate = " Warrior.HealthGrowthRate " _F ;
float atkGrowthRate = " Warrior.AtkGrowthRate " _F ;
vf2d pos ;
float z = 0 ;
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 < std : : string , float > notEnoughManaDisplay = { " " , 0.f } ;
float teleportAttemptWaitTime = 0 ; //If a teleport fails, we wait awhile before trying again, it's expensive.
State : : State state = State : : NORMAL ;
Animate2D : : Animation < std : : string > animation ;
Animate2D : : AnimationState internal_animState ;
Key lastReleasedMovementKey ;
void AddAnimation ( std : : string state ) ;
std : : vector < Buff > buffList ;
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_ptr < DamageNumber > damageNumberPtr ;
void Initialize ( ) ;
float iframe_time = 0 ;
float lastCombatTime = 0 ;
Ability useItem1 ;
Ability useItem2 ;
Ability useItem3 ;
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 SetFacingDirection ( Key direction ) ;
void SetLastReleasedMovementKey ( Key k ) ;
void Spin ( float duration , float spinSpd ) ;
float friction = " Player.Friction " _F ;
float attack_cooldown_timer = 0 ;
float teleportAnimationTimer = 0 ;
vf2d teleportTarget = { } ;
vf2d teleportStartPosition = { } ;
std : : pair < std : : string , float > notificationDisplay = { " " , 0.f } ;
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.025f ;
float ghostFrameTimer = 0 ;
float ghostRemoveTimer = 0 ;
float blockTimer = 0 ;
float retreatTimer = 0 ;
std : : vector < vf2d > ghostPositions ;
float rapidFireTimer = 0 ;
int remainingRapidFireShots = 0 ;
float endZoneStandTime = 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 ( ) ;
const int GetStat ( ItemAttribute a ) const ;
const int GetBaseStat ( ItemAttribute a ) const ;
void SetBaseStat ( ItemAttribute a , int val ) ;
const int GetMaxHealth ( ) const ;
const int GetHealth ( ) const ;
const int GetMana ( ) const ;
const int GetMaxMana ( ) const ;
const int GetAttack ( ) ;
float GetMoveSpdMult ( ) ;
float GetSizeMult ( ) ;
void SetSizeMult ( float size ) ;
float GetAttackRangeMult ( ) ;
float GetSpinAngle ( ) ;
State : : State GetState ( ) ;
Key GetFacingDirection ( ) ;
vf2d GetVelocity ( ) ;
bool HasIframes ( ) ;
void Update ( float fElapsedTime ) ;
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 RestoreMana ( int amt , bool suppressDamageNumber = false ) ;
void ConsumeMana ( int amt ) ;
//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 ) ;
void SetState ( State : : State newState ) ;
void AddBuff ( BuffType type , float duration , float intensity ) ;
std : : vector < Buff > GetBuffs ( 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 ) ;
//Return false if healing was not possible.
bool Heal ( int damage ) ;
//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 ( ) ;
void ResetLastCombatTime ( ) ;
bool IsOutOfCombat ( ) ;
float GetEndZoneStandTime ( ) ;
//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 ;
void CheckEndZoneCollision ( ) ;
CastInfo & GetCastInfo ( ) ;
void SetAnimationBasedOnTargetingDirection ( float targetDirection ) ;
void SetItem1UseFunc ( Ability a ) ;
void SetItem2UseFunc ( Ability a ) ;
void SetItem3UseFunc ( Ability a ) ;
static InputGroup KEY_ABILITY1 , KEY_ABILITY2 , KEY_ABILITY3 , KEY_ABILITY4 , KEY_DEFENSIVE , KEY_ITEM1 , KEY_ITEM2 , KEY_ITEM3 ;
} ;
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.Description"_s.concat(), \
# class".Right Click Ability.Cooldown"_F, \
# class".Right Click Ability.Mana Cost"_I, \
& KEY_DEFENSIVE , \
" Ability Icons/ " + # 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.Description"_s.concat(), \
# class".Ability 1.Cooldown"_F, \
# class".Ability 1.Mana Cost"_I, \
& KEY_ABILITY1 , \
" Ability Icons/ " + # 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.Description"_s.concat(), \
# class".Ability 2.Cooldown"_F, \
# class".Ability 2.Mana Cost"_I, \
& KEY_ABILITY2 , \
" Ability Icons/ " + # 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.Description"_s.concat(), \
# class".Ability 3.Cooldown"_F, \
# class".Ability 3.Mana Cost"_I, \
& KEY_ABILITY3 , \
" Ability Icons/ " + # 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 ;