# pragma region License
/*
License ( OLC - 3 )
~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~
Copyright 2024 Joshua Sigona < sigonasr2 @ gmail . 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 © 2024 The FreeType
Project ( www . freetype . org ) . Please see LICENSE_FT . txt for more information .
All rights reserved .
*/
# pragma endregion
# include "Class.h"
# include "DEFINES.h"
# include "Player.h"
# include "Effect.h"
# include "AdventuresInLestoria.h"
# include "config.h"
# include "SoundEffect.h"
# include "util.h"
# include "BulletTypes.h"
# include <ranges>
INCLUDE_MONSTER_LIST
INCLUDE_BULLET_LIST
INCLUDE_game
void Thief : : Initialize ( ) {
READFROMCONFIG ( Thief , THIEF ) ;
Thief : : idle_n = " THIEF_IDLE_N " ;
Thief : : idle_e = " THIEF_IDLE_E " ;
Thief : : idle_s = " THIEF_IDLE_S " ;
Thief : : idle_w = " THIEF_IDLE_W " ;
Thief : : walk_n = " THIEF_WALK_N " ;
Thief : : walk_e = " THIEF_WALK_E " ;
Thief : : walk_s = " THIEF_WALK_S " ;
Thief : : walk_w = " THIEF_WALK_W " ;
Thief : : ability4 = { } ;
}
SETUP_CLASS ( Thief )
void Thief : : OnUpdate ( float fElapsedTime ) {
}
bool Thief : : AutoAttack ( ) {
bool attack = false ;
Monster * closest = nullptr ;
float closest_dist = 999999 ;
for ( std : : shared_ptr < Monster > & m : MONSTER_LIST ) {
if ( m - > IsAlive ( ) & &
geom2d : : overlaps ( geom2d : : circle < float > ( GetPos ( ) , GetAttackRange ( ) / 100.f * 12 ) , geom2d : : circle < float > ( m - > GetPos ( ) , m - > GetSizeMult ( ) * 12 ) ) & &
geom2d : : line < float > ( GetWorldAimingLocation ( ) , m - > GetPos ( ) ) . length ( ) < closest_dist ) {
closest_dist = geom2d : : line < float > ( GetWorldAimingLocation ( ) , m - > GetPos ( ) ) . length ( ) ;
closest = & * m ;
}
}
float targetDirection ;
if ( closest ! = nullptr ) {
float dirToEnemy = geom2d : : line < float > ( GetPos ( ) , closest - > GetPos ( ) ) . vector ( ) . polar ( ) . y ;
targetDirection = dirToEnemy ;
SetAnimationBasedOnTargetingDirection ( " SWINGSWORD " , dirToEnemy ) ;
} else {
float dirToMouse = geom2d : : line < float > ( GetPos ( ) , GetWorldAimingLocation ( ) ) . vector ( ) . polar ( ) . y ;
targetDirection = dirToMouse ;
SetAnimationBasedOnTargetingDirection ( " SWINGSWORD " , dirToMouse ) ;
}
attack_cooldown_timer = ATTACK_COOLDOWN - GetAttackRecoveryRateReduction ( ) ;
swordSwingTimer = " Thief.Auto Attack.SwordAnimationSwingTime " _F ;
game - > AddEffect ( std : : make_unique < SwordSlash > ( 0.125f , " swordslash.png " s , " Thief.Auto Attack.DamageMult " _F , " Thief.Auto Attack.SwordSlashSweepAngle " _F , vf2d { 0.9f , 0.9f } * " Thief.Auto Attack.Range " _F / 100.f , 0.1f , vf2d { 0.f , 0.f } , WHITE , targetDirection ) ) ;
SetState ( State : : SWING_SWORD ) ;
SoundEffect : : PlaySFX ( " Warrior Auto Attack " , SoundEffect : : CENTERED ) ;
return true ;
}
void Thief : : InitializeClassAbilities ( ) {
# pragma region Thief Right-click Ability (Roll)
Thief : : rightClickAbility . action =
[ ] ( Player * p , vf2d pos = { } ) {
p - > SetState ( State : : ROLL ) ;
p - > rolling_timer = " Thief.Right Click Ability.Roll Time " _F ;
const float originalRollIntensity { " Thief.Right Click Ability.Movespeed Buff " _f [ 0 ] / 100.f } ;
float rollIntensity { originalRollIntensity } ;
if ( p - > HasEnchant ( " Tumble " ) ) rollIntensity + = originalRollIntensity * ( " Tumble " _ENC [ " BOOST PERCENTAGE " ] / 100.f ) ;
p - > AddBuff ( BuffType : : SPEEDBOOST , " Thief.Right Click Ability.Movespeed Buff " _f [ 1 ] , rollIntensity ) ;
geom2d : : line mouseDir { p - > GetPos ( ) , p - > GetWorldAimingLocation ( Player : : USE_WALK_DIR , Player : : INVERTED ) } ;
const float originalRollRange { " Thief.Right Click Ability.Max Roll Range " _F / 100 } ;
float rollRange { originalRollRange } ;
if ( p - > HasEnchant ( " Tumble " ) ) rollRange + = originalRollRange * ( " Tumble " _ENC [ " BOOST PERCENTAGE " ] / 100.f ) ;
float velocity = ( 0.5f * - p - > friction * " Thief.Right Click Ability.Roll Time " _F * " Thief.Right Click Ability.Roll Time " _F - std : : clamp ( mouseDir . length ( ) , 24.f , 24.f * rollRange ) ) / - " Thief.Right Click Ability.Roll Time " _F ; //Derived from kinetic motion formula.
p - > SetVelocity ( mouseDir . vector ( ) . norm ( ) * velocity ) ;
const float originalIframeTime { " Thief.Right Click Ability.Iframe Time " _F } ;
float iframeTime { originalIframeTime } ;
if ( p - > HasEnchant ( " Tumble " ) ) iframeTime + = originalIframeTime * " Tumble " _ENC [ " BOOST PERCENTAGE " ] / 100.f ;
p - > ApplyIframes ( iframeTime ) ;
p - > footstepTimer = 0.f ;
return true ;
} ;
# pragma endregion
# pragma region Thief Ability 1 (Hidden Dagger)
Thief : : ability1 . action =
[ ] ( Player * p , vf2d pos = { } ) {
geom2d : : line mouseDir { p - > GetWorldAimingLocation ( Player : : USE_WALK_DIR ) , p - > GetPos ( ) } ;
float velocity = ( 0.5f * - p - > friction * " Thief.Ability 1.RetreatTime " _F * " Thief.Ability 1.RetreatTime " _F - 24.f * " Thief.Ability 1.RetreatDistance " _F / 100 ) / - " Thief.Ability 1.RetreatTime " _F ; //Derived from kinetic motion formula.
p - > SetVelocity ( mouseDir . vector ( ) . norm ( ) * velocity ) ;
p - > retreatTimer = " Thief.Ability 1.RetreatTime " _F ;
p - > ApplyIframes ( " Thief.Ability 1.RetreatTime " _F ) ;
p - > ghostPositions . push_back ( p - > GetPos ( ) + vf2d { 0 , - p - > GetZ ( ) } ) ;
p - > ghostFrameTimer = p - > RETREAT_GHOST_FRAME_DELAY ;
p - > ghostRemoveTimer = p - > RETREAT_GHOST_FRAMES * p - > RETREAT_GHOST_FRAME_DELAY ;
p - > daggerThrowWaitTimer = " Thief.Ability 1.Dagger Throw Delay " _F ;
float angleToCursor = atan2 ( p - > GetWorldAimingLocation ( Player : : USE_WALK_DIR ) . y - p - > GetPos ( ) . y , p - > GetWorldAimingLocation ( Player : : USE_WALK_DIR ) . x - p - > GetPos ( ) . x ) ;
p - > SetAnimationBasedOnTargetingDirection ( " IDLE " , angleToCursor ) ;
p - > SetState ( State : : RETREAT ) ;
SoundEffect : : PlaySFX ( " Thief.Ability 1.Sound " _S , SoundEffect : : CENTERED ) ;
return true ;
} ;
# pragma endregion
# pragma region Thief Ability 2 (Deadly Dash)
Thief : : ability2 . action =
[ ] ( Player * p , vf2d pos = { } ) {
game - > AddEffect ( std : : make_unique < ShineEffect > ( p - > GetPos ( ) + vf2d { 4.f , 4.f } , 0.5f , 0.5f , " shine.png " , 1.5f , vf2d { } , WHITE , util : : random ( 2 * PI ) , PI / 2 , true ) ) ;
p - > ApplyIframes ( " Thief.Ability 2.Initial Wait " _F + " Thief.Ability 2.Ending Wait " _F + " Thief.Ability 2.Completed Dash Extra Iframe Time " _F ) ;
SoundEffect : : PlaySFX ( " Charge Up " , p - > GetPos ( ) ) ;
p - > SetState ( State : : DEADLYDASH ) ;
p - > deadlyDashWaitTimer = " Thief.Ability 2.Initial Wait " _F ;
p - > deadlyDashAfterDashTimer = p - > deadlyDashWaitTimer + " Thief.Ability 2.Ending Wait " _F ;
game - > SetWorldZoom ( 1.1f ) ;
SoundEffect : : PlaySFX ( " Ranger Retreat " , SoundEffect : : CENTERED ) ;
return true ;
} ;
# pragma endregion
# pragma region Thief Ability 3 (Adrenaline Rush)
Thief : : ability3 . action =
[ ] ( Player * p , vf2d pos = { } ) {
SoundEffect : : PlaySFX ( " Adrenaline Rush " , SoundEffect : : CENTERED ) ;
float adrenalineRushDuration { " Thief.Ability 3.Duration " _F } ;
if ( p - > HasEnchant ( " Adrenaline Stim " ) ) adrenalineRushDuration = " Adrenaline Stim " _ENC [ " NEW ADRENALINE RUSH DURATION " ] ;
p - > AddBuff ( BuffType : : ADRENALINE_RUSH , adrenalineRushDuration , 0.f ) ;
p - > AddTimer ( TimerType : : ADRENALINE_STIM , Timer { " Adrenaline Stim Extra Sound Timer " , 0.3f , [ ] ( ) { SoundEffect : : PlaySFX ( " Adrenaline Rush High Pitch " , SoundEffect : : CENTERED ) ; } , Timer : : MANUAL } ) ;
int healthCost { } ;
if ( p - > HasEnchant ( " Adrenaline Stim " ) ) healthCost = p - > GetMaxHealth ( ) * ( " Adrenaline Stim " _ENC [ " HEALTH PCT COST " ] / 100.f ) ;
if ( healthCost > 0 ) {
if ( p - > GetHealth ( ) < healthCost ) healthCost = p - > GetHealth ( ) - 1 ;
p - > Hurt ( healthCost , p - > OnUpperLevel ( ) , p - > GetZ ( ) , TrueDamageFlag : : IGNORE_DAMAGE_RULES , HurtFlag : : PLAYER_ABILITY ) ;
}
for ( int i : std : : ranges : : iota_view ( 0 , 50 ) ) {
float size { util : : random_range ( 0.4f , 0.8f ) } ;
game - > AddEffect ( std : : make_unique < Effect > ( p - > GetPos ( ) + vf2d { 8 , util : : random ( 2 * PI ) } . cart ( ) , util : : random_range ( 0.1f , 0.4f ) , " circle.png " , p - > OnUpperLevel ( ) , vf2d { size , size } , 0.3f , vf2d { util : : random_range ( - 6.f , 6.f ) , util : : random_range ( - 8.f , - 1.f ) } , PixelLerp ( WHITE , GREEN , util : : random ( 1 ) ) ) ) ;
}
return true ;
} ;
# pragma endregion
}