Only adjust DPS boss damage counter for monsters spawned and considered part of the boss fight. Add in ability for attacks to deal true damage. Change hurt function to handle a True Damage Flag. Stone Throw now breaks pillars it hits. Release Build 9675.

removeExposedPackKey
sigonasr2 7 months ago
parent c6c3e5cf72
commit dc2394cd17
  1. 21
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 1
      Adventures in Lestoria/AdventuresInLestoria.h
  3. 5
      Adventures in Lestoria/BreakingPillar.cpp
  4. 1
      Adventures in Lestoria/DamageNumber.h
  5. 2
      Adventures in Lestoria/GameEvent.cpp
  6. 8
      Adventures in Lestoria/LargeStone.cpp
  7. 31
      Adventures in Lestoria/Monster.cpp
  8. 10
      Adventures in Lestoria/Monster.h
  9. 2
      Adventures in Lestoria/Version.h
  10. BIN
      x64/Release/Adventures in Lestoria.exe

@ -744,14 +744,16 @@ const HurtList AiL::Hurt(vf2d pos,float radius,int damage,bool upperLevel,float
if(CheckForMonsterCollisions){
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()))){
const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()));
if(InRange){
HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z);
hitList.push_back({&*m,returnVal});
}
}
}
if(CheckForPlayerCollisions){
if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(GetPlayer()->GetPos(),12*GetPlayer()->GetSizeMult()))){
const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(GetPlayer()->GetPos(),12*GetPlayer()->GetSizeMult()));
if(InRange){
HurtReturnValue returnVal=GetPlayer()->Hurt(damage,upperLevel,z);
hitList.push_back({GetPlayer(),returnVal});
}
@ -759,6 +761,21 @@ const HurtList AiL::Hurt(vf2d pos,float radius,int damage,bool upperLevel,float
return hitList;
}
const HurtList AiL::HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName)const{
if(!MONSTER_DATA.count(std::string(monsterName)))ERR(std::format("WARNING! Cannot check for monster type name {}! Does not exist!",monsterName));
HurtList hitList;
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
const bool InRange=geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m->GetPos(),12*m->GetSizeMult()));
if(m->GetName()==monsterName&&InRange){
HurtReturnValue returnVal=m->Hurt(damage,upperLevel,z);
hitList.push_back({&*m,returnVal});
}
}
return hitList;
}
void AiL::ProximityKnockback(const vf2d pos,const float radius,const float knockbackAmt,const HurtType knockbackTargets)const{
const bool CheckForMonsterCollisions=knockbackTargets&HurtType::MONSTER;
const bool CheckForPlayerCollisions=knockbackTargets&HurtType::PLAYER;

@ -357,6 +357,7 @@ public:
Overlay&GetOverlay();
void SetWindSpeed(vf2d newWindSpd);
const vf2d&GetWindSpeed()const;
const HurtList HurtMonsterType(vf2d pos,float radius,int damage,bool upperLevel,float z,const std::string_view monsterName)const;
struct TileGroupData{
vi2d tilePos;

@ -50,8 +50,9 @@ void Monster::STRATEGY::BREAKING_PILLAR(Monster&m,float fElapsedTime,std::string
m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 1 Animation Name"));
}else m.animation.ModifyDisplaySprite(m.internal_animState,ConfigString("Break Phase 2 Animation Name"));
if(m.IsDead()&&!m.B(A::MARKED_DEAD)){ //Kill and fade out the pillar.
m.SetStrategyDeathFunction([](GameEvent&deathEvent,Monster&m,const std::string&monsterName){
m.lifetime=0.01f;
m.B(A::MARKED_DEAD)=true;
}
return false;
});
}

@ -59,6 +59,7 @@ struct DamageNumber{
const static float MOVE_UP_TIME;
float originalRiseSpd=0.f;
DamageNumber();
//The friendly flag indicates if the number was for a friendly/player target or if it's for a monster target (set to false)
DamageNumber(vf2d pos,int damage,bool friendly=false,DamageNumberType type=HEALTH_LOSS);
void RecalculateSize();
};

@ -37,7 +37,6 @@ All rights reserved.
#pragma endregion
#include "GameEvent.h"
#include "Monster.h"
#include <map>
std::vector<std::unique_ptr<GameEvent>>GameEvent::events;
@ -68,6 +67,5 @@ MonsterStrategyGameEvent::MonsterStrategyGameEvent(std::function<bool(GameEvent&
:runFunc(func),m(m),strategy(strategy){}
void MonsterStrategyGameEvent::Update(){
INCLUDE_MONSTER_DATA
isActive=runFunc(*this,this->m,this->strategy);
}

@ -65,6 +65,14 @@ void LargeStone::Update(float fElapsedTime){
game->AddEffect(std::make_unique<FallingDebris>(pos+vf2d{radius,randomDir}.cart(),0.f,"circle.png",OnUpperLevel(),vf2d{1.5f,1.5f}*util::random(2.f),util::random(1.f),vf2d{vf2d{radius,randomDir}.cart().x,-util::random(30.f)-20.f},BLACK));
}
const HurtList pillarList{game->HurtMonsterType(pos,radius,3,OnUpperLevel(),GetZ(),"Stone Golem Pillar")};
for(auto&[ptr,wasHurt]:pillarList){
Monster*const monsterPtr{std::get<Monster*>(ptr)};
if(wasHurt)ERR(std::format("WARNING! Somehow a {} has taken non-true damage! THIS SHOULD NOT BE HAPPENING!",monsterPtr->GetDisplayName()));
monsterPtr->_DealTrueDamage(3);
}
game->ProximityKnockback(pos,radius,knockbackAmt,HurtType::MONSTER|HurtType::PLAYER);

@ -602,7 +602,11 @@ const bool Monster::AttackAvoided(const float attackZ)const{
}
bool Monster::Hurt(int damage,bool onUpperLevel,float z){
if(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)) return false;
return _Hurt(damage,onUpperLevel,z,TrueDamageFlag::NORMAL_DAMAGE);
}
bool Monster::_Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag damageRule){
const bool TrueDamage=damageRule==TrueDamageFlag::IGNORE_DAMAGE_RULES;
if(!TrueDamage&&(Invulnerable()||!IsAlive()||onUpperLevel!=OnUpperLevel()||AttackAvoided(z)))return false;
if(game->InBossEncounter()){
game->StartBossEncounter();
}
@ -620,13 +624,19 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
mod_dmg-=mod_dmg*GetDamageReductionFromBuffs();
mod_dmg=std::ceil(mod_dmg);
if(TrueDamage){
mod_dmg=damage; //True damage override, ignore all damage changes.
crit=false; //True damage disables critting.
}
hp=std::max(0,hp-int(mod_dmg));
if(lastHitTimer>0){
damageNumberPtr.get()->damage+=int(mod_dmg);
damageNumberPtr.get()->pauseTime=0.4f;
damageNumberPtr.get()->RecalculateSize();
} else {
}else{
damageNumberPtr=std::make_shared<DamageNumber>(pos,int(mod_dmg));
DAMAGENUMBER_LIST.push_back(damageNumberPtr);
}
@ -650,7 +660,7 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
SoundEffect::PlaySFX(GetHurtSound(),GetPos());
}
}
if(game->InBossEncounter()){
if(game->InBossEncounter()&&isBoss){
game->BossDamageDealt(int(mod_dmg));
}
@ -881,9 +891,7 @@ void Monster::OnDeath(){
}
)
if(hasStrategyDeathFunction){
GameEvent::AddEvent(std::make_unique<MonsterStrategyGameEvent>(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy()));
}
if(strategyDeathFunc)GameEvent::AddEvent(std::make_unique<MonsterStrategyGameEvent>(strategyDeathFunc,*this,MONSTER_DATA[name].GetAIStrategy()));
SpawnDrops();
@ -975,8 +983,13 @@ const float Monster::GetCollisionDamage()const{
else return MONSTER_DATA[name].GetCollisionDmg();
}
//Sets the strategy death function that runs when a monster dies.
// The function should return false to indicate the event is over. If the event should keep running, return true.
//Arguments are:
// GameEvent& - The death event itself.
// Monster& - The monster reference
// const std::string& - The strategy name.
void Monster::SetStrategyDeathFunction(std::function<bool(GameEvent&,Monster&,const std::string&)>func){
hasStrategyDeathFunction=true;
strategyDeathFunc=func;
}
@ -1121,4 +1134,8 @@ const float Monster::GetHealthRatio()const{
const bool Monster::IsSolid()const{
return Immovable();
}
void Monster::_DealTrueDamage(const uint32_t damageAmt){
_Hurt(damageAmt,OnUpperLevel(),GetZ(),TrueDamageFlag::IGNORE_DAMAGE_RULES);
}

@ -59,6 +59,11 @@ enum class Attribute;
class GameEvent;
enum class TrueDamageFlag{
NORMAL_DAMAGE,
IGNORE_DAMAGE_RULES, //Deals true damage, ignoring established invulnerability/iframe rules. Will never miss and will not have its damage modified by any buffs/stats.
};
class DeathSpawnInfo{
std::string monsterSpawnName;
uint8_t spawnAmt;
@ -181,6 +186,7 @@ public:
const bool HasArrowIndicator()const;
const bool ReachedTargetPos(const float maxDistanceFromTarget=4.f)const;
const float GetHealthRatio()const;
void _DealTrueDamage(const uint32_t damageAmt);
private:
//NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!!
// The way this works is that monsters marked for deletion will cause the monster update loop to detect there's at least one or more monsters that must be deleted and will call erase_if on the list at the end of the iteration loop.
@ -231,7 +237,7 @@ private:
bool hasStrategyDeathFunction=false;
NPCData npcData;
float lastPathfindingCooldown=0.f;
std::function<bool(GameEvent&,Monster&,const std::string&)>strategyDeathFunc;
std::function<bool(GameEvent&,Monster&,const std::string&)>strategyDeathFunc{};
void SetStrategyDeathFunction(std::function<bool(GameEvent&,Monster&,const std::string&)>func);
ItemAttribute&Get(std::string_view attr);
//Returns false if the monster could not be moved to the requested location due to collision.
@ -258,6 +264,7 @@ private:
float fadeTimer{0.f};
bool markedForDeletion{false}; //DO NOT MODIFY DIRECTLY. Use MarkForDeletion() if this monster needs to be marked. NOTE: Marking a monster for deletion does not trigger any death events. It just simply removes the monster from the field!!
float solidFadeTimer{0.f};
bool _Hurt(int damage,bool onUpperLevel,float z,const TrueDamageFlag damageRule);
private:
struct STRATEGY{
static std::string ERR;
@ -314,5 +321,4 @@ struct MonsterSpawner{
vf2d GetPos();
bool DoesUpperLevelSpawning();
void SetTriggered(bool trigger,bool spawnMonsters=true);
friend std::ostream&operator<<(std::ostream&os,MonsterSpawner&rhs);
};

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
#define VERSION_BUILD 9667
#define VERSION_BUILD 9675
#define stringify(a) stringify_(a)
#define stringify_(a) #a

Loading…
Cancel
Save