# 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
# include "Monster.h"
# include "DamageNumber.h"
# include "Crawler.h"
# include "Bullet.h"
# include "BulletTypes.h"
# include "DEFINES.h"
# include "safemap.h"
# include "MonsterStrategyHelpers.h"
# include "util.h"
# include "MonsterAttribute.h"
# include "ItemDrop.h"
INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_DATA
INCLUDE_MONSTER_LIST
INCLUDE_DAMAGENUMBER_LIST
INCLUDE_game
INCLUDE_BULLET_LIST
INCLUDE_DATA
INCLUDE_GFX
safemap < std : : string , std : : function < void ( Monster & , float , std : : string ) > > STRATEGY_DATA ;
std : : map < std : : string , Renderable * > MonsterData : : imgs ;
Monster : : Monster ( vf2d pos , MonsterData data , bool upperLevel , bool bossMob ) :
pos ( pos ) , maxhp ( data . GetHealth ( ) ) , size ( data . GetSizeMult ( ) ) , targetSize ( data . GetSizeMult ( ) ) , strategy ( data . GetAIStrategy ( ) ) , name ( data . GetDisplayName ( ) ) , upperLevel ( upperLevel ) , isBoss ( bossMob ) , facingDirection ( DOWN ) {
bool firstAnimation = true ;
for ( std : : string & anim : data . GetAnimations ( ) ) {
animation . AddState ( anim , ANIMATION_DATA [ anim ] ) ;
if ( firstAnimation ) {
animation . ChangeState ( internal_animState , anim ) ;
firstAnimation = false ;
}
}
stats . A ( " Health " ) = data . GetHealth ( ) ;
stats . A ( " Attack " ) = data . GetAttack ( ) ;
stats . A ( " Move Spd % " ) = data . GetMoveSpdMult ( ) ;
randomFrameOffset = ( rand ( ) % 1000 ) / 1000.f ;
}
vf2d & Monster : : GetPos ( ) {
return pos ;
}
int Monster : : GetHealth ( ) {
return stats . A ( " Health " ) ;
}
int Monster : : GetAttack ( ) {
float mod_atk = float ( stats . A ( " Attack " ) ) ;
mod_atk + = Get ( " Attack % " ) ;
mod_atk + = Get ( " Attack " ) ;
return int ( mod_atk ) ;
}
float Monster : : GetMoveSpdMult ( ) {
float mod_moveSpd = stats . A ( " Move Spd % " ) ;
for ( Buff & b : GetBuffs ( SLOWDOWN ) ) {
mod_moveSpd - = stats . A ( " Move Spd % " ) * b . intensity ;
}
return mod_moveSpd ;
}
float Monster : : GetSizeMult ( ) {
return size ;
}
Animate2D : : Frame Monster : : GetFrame ( ) {
return animation . GetFrame ( internal_animState ) ;
}
void Monster : : UpdateAnimation ( std : : string state ) {
animation . ChangeState ( internal_animState , state ) ;
}
void Monster : : PerformJumpAnimation ( ) {
animation . ChangeState ( internal_animState , MONSTER_DATA [ name ] . GetJumpAnimation ( ) ) ;
}
void Monster : : PerformShootAnimation ( ) {
animation . ChangeState ( internal_animState , MONSTER_DATA [ name ] . GetShootAnimation ( ) ) ;
}
void Monster : : PerformIdleAnimation ( ) {
animation . ChangeState ( internal_animState , MONSTER_DATA [ name ] . GetIdleAnimation ( ) ) ;
}
bool Monster : : SetX ( float x ) {
vf2d newPos = { x , pos . y } ;
vi2d tilePos = vi2d ( newPos / float ( game - > GetCurrentMap ( ) . tilewidth ) ) * game - > GetCurrentMap ( ) . tilewidth ;
geom2d : : rect < int > collisionRect = game - > GetTileCollision ( game - > GetCurrentLevel ( ) , newPos , upperLevel ) ;
if ( collisionRect . pos = = vi2d { 0 , 0 } & & collisionRect . size = = vi2d { 1 , 1 } ) {
pos . x = std : : clamp ( x , game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) , float ( game - > GetCurrentMap ( ) . width * game - > GetCurrentMap ( ) . tilewidth - game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) ) ) ;
Moved ( ) ;
return true ;
} else {
geom2d : : rect < float > collision = { collisionRect . pos , collisionRect . size } ;
collision . pos + = tilePos ;
if ( ! geom2d : : overlaps ( geom2d : : circle < float > ( newPos , 12 * GetSizeMult ( ) ) , collision ) ) {
pos . x = std : : clamp ( x , game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) , float ( game - > GetCurrentMap ( ) . width * game - > GetCurrentMap ( ) . tilewidth - game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) ) ) ;
Moved ( ) ;
return true ;
}
}
return false ;
}
bool Monster : : SetY ( float y ) {
vf2d newPos = { pos . x , y } ;
vi2d tilePos = vi2d ( newPos / float ( game - > GetCurrentMap ( ) . tilewidth ) ) * game - > GetCurrentMap ( ) . tilewidth ;
geom2d : : rect < int > collisionRect = game - > GetTileCollision ( game - > GetCurrentLevel ( ) , newPos , upperLevel ) ;
if ( collisionRect . pos = = vi2d { 0 , 0 } & & collisionRect . size = = vi2d { 1 , 1 } ) {
pos . y = std : : clamp ( y , game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) , float ( game - > GetCurrentMap ( ) . height * game - > GetCurrentMap ( ) . tilewidth - game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) ) ) ;
Moved ( ) ;
return true ;
} else {
geom2d : : rect < float > collision = { collisionRect . pos , collisionRect . size } ;
collision . pos + = tilePos ;
if ( ! geom2d : : overlaps ( geom2d : : circle < float > ( newPos , game - > GetCurrentMap ( ) . tilewidth / 2 * GetSizeMult ( ) ) , collision ) ) {
pos . y = std : : clamp ( y , game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) , float ( game - > GetCurrentMap ( ) . height * game - > GetCurrentMap ( ) . tilewidth - game - > GetCurrentMap ( ) . tilewidth / 2.f * GetSizeMult ( ) ) ) ;
Moved ( ) ;
return true ;
}
}
return false ;
}
bool Monster : : Update ( float fElapsedTime ) {
lastHitTimer = std : : max ( 0.f , lastHitTimer - fElapsedTime ) ;
iframe_timer = std : : max ( 0.f , iframe_timer - fElapsedTime ) ;
if ( size ! = targetSize ) {
if ( size > targetSize ) {
size = std : : max ( targetSize , size - Crawler : : SIZE_CHANGE_SPEED * fElapsedTime ) ;
} else {
size = std : : min ( targetSize , size + Crawler : : SIZE_CHANGE_SPEED * fElapsedTime ) ;
}
}
if ( IsAlive ( ) ) {
for ( std : : vector < Buff > : : iterator it = buffList . begin ( ) ; it ! = buffList . end ( ) ; + + it ) {
Buff & b = * it ;
b . duration - = fElapsedTime ;
if ( b . duration < = 0 ) {
it = buffList . erase ( it ) ;
if ( it = = buffList . end ( ) ) break ;
}
}
if ( ! HasIframes ( ) ) {
for ( Monster & m : MONSTER_LIST ) {
if ( & m = = this ) continue ;
if ( ! m . HasIframes ( ) & & OnUpperLevel ( ) = = m . OnUpperLevel ( ) & & abs ( m . GetZ ( ) - GetZ ( ) ) < = 1 & & geom2d : : overlaps ( geom2d : : circle ( pos , 12 * size / 2 ) , geom2d : : circle ( m . GetPos ( ) , 12 * m . GetSizeMult ( ) / 2 ) ) ) {
m . Collision ( * this ) ;
geom2d : : line line ( pos , m . GetPos ( ) ) ;
float dist = line . length ( ) ;
m . SetPos ( line . rpoint ( dist * 1.1f ) ) ;
if ( m . IsAlive ( ) ) {
vel = line . vector ( ) . norm ( ) * - 128 ;
}
}
}
if ( ! game - > GetPlayer ( ) - > HasIframes ( ) & & abs ( game - > GetPlayer ( ) - > GetZ ( ) - GetZ ( ) ) < = 1 & & game - > GetPlayer ( ) - > OnUpperLevel ( ) = = OnUpperLevel ( ) & & geom2d : : overlaps ( geom2d : : circle ( pos , 12 * size / 2 ) , geom2d : : circle ( game - > GetPlayer ( ) - > GetPos ( ) , 12 * game - > GetPlayer ( ) - > GetSizeMult ( ) / 2 ) ) ) {
geom2d : : line line ( pos , game - > GetPlayer ( ) - > GetPos ( ) ) ;
float dist = line . length ( ) ;
SetPos ( line . rpoint ( - 0.1f ) ) ;
vel = line . vector ( ) . norm ( ) * - 128 ;
}
}
if ( GetState ( ) = = State : : NORMAL ) {
if ( game - > GetPlayer ( ) - > GetX ( ) > pos . x ) {
facingDirection = RIGHT ;
} else {
facingDirection = LEFT ;
}
}
Monster : : STRATEGY : : RUN_STRATEGY ( * this , fElapsedTime ) ;
if ( vel . x > 0 ) {
vel . x = std : : max ( 0.f , vel . x - friction * fElapsedTime ) ;
} else {
vel . x = std : : min ( 0.f , vel . x + friction * fElapsedTime ) ;
}
if ( vel . y > 0 ) {
vel . y = std : : max ( 0.f , vel . y - friction * fElapsedTime ) ;
} else {
vel . y = std : : min ( 0.f , vel . y + friction * fElapsedTime ) ;
}
if ( vel ! = vf2d { 0 , 0 } ) {
SetX ( pos . x + vel . x * fElapsedTime ) ;
SetY ( pos . y + vel . y * fElapsedTime ) ;
}
}
if ( ! IsAlive ( ) ) {
deathTimer + = fElapsedTime ;
if ( deathTimer > 3 ) {
return false ;
}
}
animation . UpdateState ( internal_animState , randomFrameOffset + fElapsedTime ) ;
randomFrameOffset = 0 ;
return true ;
}
Key Monster : : GetFacingDirection ( ) {
return facingDirection ;
}
void Monster : : Draw ( ) {
if ( GetZ ( ) > 0 ) {
vf2d shadowScale = vf2d { 8 * GetSizeMult ( ) / 3.f , 1 } / std : : max ( 1.f , GetZ ( ) / 24 ) ;
game - > view . DrawDecal ( GetPos ( ) - vf2d { 3 , 3 } * shadowScale / 2 + vf2d { 0 , 6 * GetSizeMult ( ) } , GFX [ " circle.png " ] . Decal ( ) , shadowScale , BLACK ) ;
}
game - > view . DrawPartialRotatedDecal ( GetPos ( ) - vf2d { 0 , GetZ ( ) } , GetFrame ( ) . GetSourceImage ( ) - > Decal ( ) , 0 , GetFrame ( ) . GetSourceRect ( ) . size / 2 , GetFrame ( ) . GetSourceRect ( ) . pos , GetFrame ( ) . GetSourceRect ( ) . size , vf2d ( GetSizeMult ( ) * ( GetFacingDirection ( ) = = RIGHT ? - 1 : 1 ) , GetSizeMult ( ) ) , GetBuffs ( BuffType : : SLOWDOWN ) . size ( ) > 0 ? Pixel { uint8_t ( 255 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) , uint8_t ( 255 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) , uint8_t ( 128 + 127 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) } : WHITE ) ;
}
void Monster : : DrawReflection ( float drawRatioX , float multiplierX ) {
game - > SetDecalMode ( DecalMode : : ADDITIVE ) ;
game - > view . DrawPartialRotatedDecal ( GetPos ( ) + vf2d { drawRatioX * GetFrame ( ) . GetSourceRect ( ) . size . x , GetZ ( ) + ( GetFrame ( ) . GetSourceRect ( ) . size . y - 16 ) * GetSizeMult ( ) } , GetFrame ( ) . GetSourceImage ( ) - > Decal ( ) , 0 , GetFrame ( ) . GetSourceRect ( ) . size / 2 , GetFrame ( ) . GetSourceRect ( ) . pos , GetFrame ( ) . GetSourceRect ( ) . size , vf2d ( GetSizeMult ( ) * ( GetFacingDirection ( ) = = RIGHT ? - 1 : 1 ) * multiplierX , GetSizeMult ( ) * - 1 ) , GetBuffs ( BuffType : : SLOWDOWN ) . size ( ) > 0 ? Pixel { uint8_t ( 255 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) , uint8_t ( 255 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) , uint8_t ( 128 + 127 * abs ( sin ( 1.4 * GetBuffs ( BuffType : : SLOWDOWN ) [ 0 ] . duration ) ) ) } : WHITE ) ;
game - > SetDecalMode ( DecalMode : : NORMAL ) ;
}
void Monster : : Collision ( Player * p ) {
if ( MONSTER_DATA [ name ] . GetCollisionDmg ( ) > 0 & & ! hasHitPlayer ) {
if ( p - > Hurt ( MONSTER_DATA [ name ] . GetCollisionDmg ( ) , OnUpperLevel ( ) , GetZ ( ) ) ) {
hasHitPlayer = true ;
}
}
Collision ( ) ;
}
void Monster : : Collision ( Monster & m ) {
Collision ( ) ;
}
void Monster : : Collision ( ) {
if ( strategy = = " Run Towards " & & GetState ( ) = = State : : MOVE_TOWARDS & & util : : random ( float ( Monster : : STRATEGY : : _GetInt ( * this , " BumpStopChance " , strategy ) ) ) < 1 ) { //The run towards strategy causes state to return to normal upon a collision.
SetState ( State : : NORMAL ) ;
}
}
void Monster : : SetVelocity ( vf2d vel ) {
this - > vel = vel ;
}
bool Monster : : SetPos ( vf2d pos ) {
bool resultX = SetX ( pos . x ) ;
bool resultY = SetY ( pos . y ) ;
if ( resultY & & ! resultX ) {
resultX = SetX ( pos . x ) ;
}
return resultX | | resultY ;
}
void Monster : : Moved ( ) {
std : : map < std : : string , std : : vector < ZoneData > > & zoneData = game - > GetZoneData ( game - > GetCurrentLevel ( ) ) ;
for ( ZoneData & upperLevelZone : zoneData [ " UpperZone " ] ) {
if ( geom2d : : overlaps ( upperLevelZone . zone , pos ) ) {
upperLevel = true ;
}
}
for ( ZoneData & lowerLevelZone : zoneData [ " LowerZone " ] ) {
if ( geom2d : : overlaps ( lowerLevelZone . zone , pos ) ) {
upperLevel = false ;
}
}
}
std : : string Monster : : GetDeathAnimationName ( ) {
return MONSTER_DATA [ name ] . GetDeathAnimation ( ) ;
}
bool Monster : : Hurt ( int damage , bool onUpperLevel , float z ) {
if ( ! IsAlive ( ) | | onUpperLevel ! = OnUpperLevel ( ) | | HasIframes ( ) | | abs ( GetZ ( ) - z ) > 1 ) return false ;
if ( game - > InBossEncounter ( ) ) {
game - > StartBossEncounter ( ) ;
}
game - > GetPlayer ( ) - > ResetLastCombatTime ( ) ;
float mod_dmg = float ( damage ) ;
for ( Buff & b : GetBuffs ( BuffType : : DAMAGE_REDUCTION ) ) {
mod_dmg - = damage * b . intensity ;
}
mod_dmg = std : : ceil ( mod_dmg ) ;
stats . A ( " Health " ) = std : : max ( 0.f , stats . A ( " Health " ) - int ( mod_dmg ) ) ;
if ( lastHitTimer > 0 ) {
damageNumberPtr . get ( ) - > damage + = int ( mod_dmg ) ;
damageNumberPtr . get ( ) - > pauseTime = 0.4f ;
} else {
damageNumberPtr = std : : make_shared < DamageNumber > ( pos , int ( mod_dmg ) ) ;
DAMAGENUMBER_LIST . push_back ( damageNumberPtr ) ;
}
lastHitTimer = 0.05f ;
if ( ! IsAlive ( ) ) {
OnDeath ( ) ;
} else {
stats . A ( " Health " ) = std : : max ( 1.f , stats . A ( " Health " ) ) ; //Make sure it stays alive if it's supposed to be alive...
}
if ( game - > InBossEncounter ( ) ) {
game - > BossDamageDealt ( int ( mod_dmg ) ) ;
}
GetInt ( Attribute : : HITS_UNTIL_DEATH ) = std : : max ( 0 , GetInt ( Attribute : : HITS_UNTIL_DEATH ) - 1 ) ;
iframe_timer = GetFloat ( Attribute : : IFRAME_TIME_UPON_HIT ) ;
return true ;
}
bool Monster : : IsAlive ( ) {
return stats . A ( " Health " ) > 0 | | ! diesNormally ;
}
vf2d & Monster : : GetTargetPos ( ) {
return target ;
}
MonsterSpawner : : MonsterSpawner ( ) { }
MonsterSpawner : : MonsterSpawner ( vf2d pos , vf2d range , std : : vector < std : : pair < std : : string , vf2d > > monsters , bool upperLevel , std : : string bossNameDisplay )
: pos ( pos ) , range ( range ) , monsters ( monsters ) , upperLevel ( upperLevel ) , bossNameDisplay ( bossNameDisplay ) {
}
bool MonsterSpawner : : SpawnTriggered ( ) {
return triggered ;
}
vf2d MonsterSpawner : : GetRange ( ) {
return range ;
}
vf2d MonsterSpawner : : GetPos ( ) {
return pos ;
}
void MonsterSpawner : : SetTriggered ( bool trigger , bool spawnMonsters ) {
triggered = trigger ;
if ( spawnMonsters ) {
for ( std : : pair < std : : string , vf2d > & monsterInfo : monsters ) {
game - > SpawnMonster ( pos + monsterInfo . second , MONSTER_DATA [ monsterInfo . first ] , DoesUpperLevelSpawning ( ) , bossNameDisplay ! = " " ) ;
}
if ( bossNameDisplay ! = " " ) {
game - > SetBossNameDisplay ( bossNameDisplay ) ;
}
}
}
bool MonsterSpawner : : DoesUpperLevelSpawning ( ) {
return upperLevel ;
}
bool Monster : : OnUpperLevel ( ) {
return upperLevel ;
}
void Monster : : AddBuff ( BuffType type , float duration , float intensity ) {
buffList . push_back ( Buff { type , duration , intensity } ) ;
}
void Monster : : StartPathfinding ( float pathingTime ) {
SetState ( State : : PATH_AROUND ) ;
path = game - > pathfinder . Solve_AStar ( pos , target , 12 , OnUpperLevel ( ) ) ;
if ( path . size ( ) > 0 ) {
pathIndex = 0 ;
//We gives this mob 5 seconds to figure out a path to the target.
targetAcquireTimer = pathingTime ;
}
}
void Monster : : PathAroundBehavior ( float fElapsedTime ) {
if ( path . size ( ) > 0 ) {
//Move towards the new path.
geom2d : : line moveTowardsLine = geom2d : : line ( pos , path [ pathIndex ] * float ( game - > GetCurrentMap ( ) . tilewidth ) ) ;
if ( moveTowardsLine . length ( ) > 2 ) {
SetPos ( pos + moveTowardsLine . vector ( ) . norm ( ) * 100 * fElapsedTime * GetMoveSpdMult ( ) ) ;
if ( moveTowardsLine . vector ( ) . x > 0 ) {
facingDirection = RIGHT ;
} else {
facingDirection = LEFT ;
}
} else {
if ( size_t ( pathIndex + 1 ) > = path . size ( ) ) {
//We have reached the end of the path!
targetAcquireTimer = 0 ;
} else {
pathIndex + + ;
}
}
} else {
//We actually can't do anything so just quit.
targetAcquireTimer = 0 ;
}
}
std : : vector < Buff > Monster : : GetBuffs ( BuffType buff ) {
std : : vector < Buff > filteredBuffs ;
std : : copy_if ( buffList . begin ( ) , buffList . end ( ) , std : : back_inserter ( filteredBuffs ) , [ buff ] ( Buff & b ) { return b . type = = buff ; } ) ;
return filteredBuffs ;
}
State : : State Monster : : GetState ( ) {
return state ;
}
void Monster : : SetState ( State : : State newState ) {
state = newState ;
}
void Monster : : InitializeStrategies ( ) {
STRATEGY_DATA . insert ( " Run Towards " , Monster : : STRATEGY : : RUN_TOWARDS ) ;
STRATEGY_DATA . insert ( " Shoot Afar " , Monster : : STRATEGY : : SHOOT_AFAR ) ;
STRATEGY_DATA . insert ( " Turret " , Monster : : STRATEGY : : TURRET ) ;
STRATEGY_DATA . insert ( " Slime King " , Monster : : STRATEGY : : SLIMEKING ) ;
STRATEGY_DATA . insert ( " Run Away " , Monster : : STRATEGY : : RUN_AWAY ) ;
STRATEGY_DATA . SetInitialized ( ) ;
}
bool Monster : : HasIframes ( ) {
return iframe_timer > 0 ;
}
float Monster : : GetZ ( ) {
return z ;
}
const std : : function < void ( Monster & , float , std : : string ) > & Monster : : GetStrategy ( ) const {
return STRATEGY_DATA [ strategy ] ;
}
void Monster : : SetSize ( float newSize , bool immediate ) {
if ( immediate ) {
size = targetSize = newSize ;
} else {
targetSize = newSize ;
}
}
void Monster : : SetZ ( float z ) {
this - > z = z ;
}
void Monster : : SetStrategyDrawFunction ( std : : function < void ( Crawler * ) > func ) {
strategyDraw = func ;
}
void Monster : : OnDeath ( ) {
animation . ChangeState ( internal_animState , GetDeathAnimationName ( ) ) ;
if ( isBoss ) {
game - > ReduceBossEncounterMobCount ( ) ;
}
for ( MonsterDropData data : MONSTER_DATA . at ( name ) . GetDropData ( ) ) {
if ( util : : random ( 100 ) < = data . dropChance ) {
//This isn't necessarily fair odds for each quantity dropped.
int dropQuantity = int ( data . minQty + std : : round ( util : : random ( float ( data . maxQty - data . minQty ) ) ) ) ;
for ( int i = 0 ; i < dropQuantity ; i + + ) {
ItemDrop : : SpawnItem ( data . item , GetPos ( ) , OnUpperLevel ( ) ) ;
}
}
}
}
const ItemAttributable & Monster : : GetStats ( ) const {
return stats ;
}
ItemAttribute & Monster : : Get ( std : : string_view attr ) {
return ItemAttribute : : Get ( attr , this ) ;
}