Move damage numbers to be rendered on top of the HUD instead of below it. Add in boss indicators that appear while a boss is off-screen. Fix bugs with knockback buffs being applied in the wrong location, making them effectively useless. Fix bugs with player velocity being nan when standing directly on top of a monster spawn. Fix idle animation during stone elemental rock toss cast. Reduce enemy collision hitboxes to more sensible and playable numbers with new collision system. Release Build 9413.

mac-build
sigonasr2 6 months ago
parent 9e9f46f461
commit 2d2c123c33
  1. 150
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 2
      Adventures in Lestoria/AdventuresInLestoria.h
  3. 2
      Adventures in Lestoria/Item.cpp
  4. 2
      Adventures in Lestoria/LevitatingRock.cpp
  5. 2
      Adventures in Lestoria/LoadingScreen.cpp
  6. 40
      Adventures in Lestoria/Monster.cpp
  7. 2
      Adventures in Lestoria/MonsterData.cpp
  8. 26
      Adventures in Lestoria/Player.cpp
  9. 4
      Adventures in Lestoria/SaveFile.cpp
  10. 2
      Adventures in Lestoria/State_Death.cpp
  11. 4
      Adventures in Lestoria/State_LevelComplete.cpp
  12. 4
      Adventures in Lestoria/State_OverworldMap.cpp
  13. 6
      Adventures in Lestoria/Stone_Elemental.cpp
  14. 2
      Adventures in Lestoria/Version.h
  15. 10
      Adventures in Lestoria/assets/Campaigns/2_1.tmx
  16. BIN
      Adventures in Lestoria/assets/bossIndicator.png
  17. BIN
      Adventures in Lestoria/assets/bossIndicatorStripe.png
  18. 2
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  19. 4
      Adventures in Lestoria/assets/config/Monsters.txt
  20. 1
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  21. BIN
      Adventures in Lestoria/assets/gamepack.pak
  22. 20
      Adventures in Lestoria/olcPGEX_ViewPort.h
  23. 24
      Adventures in Lestoria/olcPixelGameEngine.h
  24. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1911,58 +1911,6 @@ void AiL::RenderWorld(float fElapsedTime){
}
#pragma endregion
for(std::vector<std::shared_ptr<DamageNumber>>::iterator it=DAMAGENUMBER_LIST.begin();it!=DAMAGENUMBER_LIST.end();++it){
DamageNumber*dn=(*it).get();
#define NumberScalesWithDamage true
#define NormalNumber false
auto DrawDamageNumber=[&](const bool ScaleWithNumber,std::string_view text,std::pair<Pixel,Pixel>colorsEnemy,std::pair<Pixel,Pixel>colorsFriendly,vf2d scaling={1.f,1.f}){
vf2d textSize=GetTextSizeProp(text)*scaling;
if(!dn->friendly){
vf2d additionalScaling={1.f,1.f};
if(ScaleWithNumber)additionalScaling=dn->size;
textSize*=additionalScaling;
vf2d drawPos=dn->pos-textSize/2.f;
drawPos.x=std::clamp(drawPos.x,view.GetWorldTL().x,view.GetWorldBR().x-textSize.x);
drawPos.y=std::clamp(drawPos.y,view.GetWorldTL().y,view.GetWorldBR().y-textSize.y);
view.DrawShadowStringPropDecal(drawPos,text,colorsEnemy.first,colorsEnemy.second,scaling*additionalScaling);
}else{
vf2d drawPos=dn->pos-textSize/2.f;
drawPos.x=std::clamp(drawPos.x,view.GetWorldTL().x,view.GetWorldBR().x-textSize.x);
drawPos.y=std::clamp(drawPos.y,view.GetWorldTL().y,view.GetWorldBR().y-textSize.y);
view.DrawShadowStringPropDecal(drawPos,text,colorsFriendly.first,colorsFriendly.second,scaling);
}
};
switch(dn->type){
case HEALTH_LOSS:{
std::string text=std::to_string(dn->damage);
DrawDamageNumber(NumberScalesWithDamage,text,{DARK_RED,{0,0,0,0}},{RED,VERY_DARK_GREY});
}break;
case HEALTH_GAIN:{
std::string text="+"+std::to_string(dn->damage);
DrawDamageNumber(NormalNumber,text,{DARK_GREEN,{0,0,0,0}},{GREEN,VERY_DARK_GREY});
}break;
case MANA_GAIN:{
std::string text="+"+std::to_string(dn->damage);
DrawDamageNumber(NormalNumber,text,{BLUE,VERY_DARK_GREY},{BLUE,VERY_DARK_GREY});
}break;
case INTERRUPT:{
std::string text="Interrupted!";
DrawDamageNumber(NormalNumber,text,{BLACK,VERY_DARK_GREY},{BLACK,VERY_DARK_GREY},{0.5f,1});
}break;
case CRIT:{
std::string text=std::to_string(dn->damage);
DrawDamageNumber(NumberScalesWithDamage,text,{YELLOW,DARK_YELLOW},{BLACK,{0,0,0,0}});
}break;
}
}
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
m->strategyDrawOverlay(this,*m,MONSTER_DATA[m->GetName()].GetAIStrategy());
}
@ -2008,6 +1956,38 @@ void AiL::RenderHud(){
}
}
};
#pragma region Boss Indicators
if(bossIndicatorPos.has_value()){
const vf2d&bossIndicator=bossIndicatorPos.value();
const bool BossIsOutsideView=!geom2d::overlaps(geom2d::rect<float>{view.GetWorldTL(),view.GetWorldVisibleArea()},bossIndicator);
if(BossIsOutsideView){
const bool flicker=sinf(GetRunTime())>0.5f&&sinf(GetRunTime())<0.55f;
#pragma region Side Indicators
const float yPos{std::clamp(view.WorldToScreen(bossIndicator).y,0.f,view.GetWorldVisibleArea().y)};
if(bossIndicator.x<view.GetWorldTL().x){ //Left-side indicator
DrawRotatedDecal({0,yPos},GFX["bossIndicator.png"].Decal(),0.f,{0,16},{8,1},flicker?RED:DARK_RED);
}else
if(bossIndicator.x>view.GetWorldBR().x){
DrawRotatedDecal({float(ScreenWidth()-8),yPos},GFX["bossIndicator.png"].Decal(),0.f,{0,16},{8,1},flicker?RED:DARK_RED);
}
#pragma endregion
#pragma region Top+Bottom Indicators
const float xPos{std::clamp(view.WorldToScreen(bossIndicator).x,0.f,view.GetWorldVisibleArea().x)};
if(bossIndicator.y<view.GetWorldTL().y){ //Left-side indicator
DrawRotatedDecal({xPos,4},GFX["bossIndicator.png"].Decal(),PI/2,{0.5f,16.f},{8,1},flicker?RED:DARK_RED);
}else
if(bossIndicator.y>view.GetWorldBR().y){
DrawRotatedDecal({xPos,float(ScreenHeight()-4)},GFX["bossIndicator.png"].Decal(),PI/2,{0.5f,16.f},{8,1},flicker?RED:DARK_RED);
}
#pragma endregion
}
}
#pragma endregion
minimap.Update();
minimap.Draw();
@ -2040,7 +2020,7 @@ void AiL::RenderHud(){
Pixel healthOutlineCol=BLACK;
if(player->GetHealth()/player->GetMaxHealth()<="Player.Health Warning Pct"_F/100.f){
float runTimeAmt=fmod(GetRuntime(),"Player.Health Warning Flicker Time"_F*2);
float runTimeAmt=fmod(GetRunTime(),"Player.Health Warning Flicker Time"_F*2);
if(runTimeAmt<"Player.Health Warning Flicker Time"_F){
healthOutlineCol="Player.Health Warning Outline Color"_Pixel;
}
@ -2084,6 +2064,59 @@ void AiL::RenderHud(){
}
DisplayBossEncounterInfo();
for(std::vector<std::shared_ptr<DamageNumber>>::iterator it=DAMAGENUMBER_LIST.begin();it!=DAMAGENUMBER_LIST.end();++it){
DamageNumber*dn=(*it).get();
#define NumberScalesWithDamage true
#define NormalNumber false
auto DrawDamageNumber=[&](const bool ScaleWithNumber,std::string_view text,std::pair<Pixel,Pixel>colorsEnemy,std::pair<Pixel,Pixel>colorsFriendly,vf2d scaling={1.f,1.f}){
vf2d textSize=GetTextSizeProp(text)*scaling;
if(!dn->friendly){
vf2d additionalScaling={1.f,1.f};
if(ScaleWithNumber)additionalScaling=dn->size;
textSize*=additionalScaling;
vf2d drawPos=dn->pos-textSize/2.f;
drawPos.x=std::clamp(drawPos.x,view.GetWorldTL().x,view.GetWorldBR().x-textSize.x);
drawPos.y=std::clamp(drawPos.y,view.GetWorldTL().y,view.GetWorldBR().y-textSize.y);
view.DrawShadowStringPropDecal(drawPos,text,colorsEnemy.first,colorsEnemy.second,scaling*additionalScaling);
}else{
vf2d drawPos=dn->pos-textSize/2.f;
drawPos.x=std::clamp(drawPos.x,view.GetWorldTL().x,view.GetWorldBR().x-textSize.x);
drawPos.y=std::clamp(drawPos.y,view.GetWorldTL().y,view.GetWorldBR().y-textSize.y);
view.DrawShadowStringPropDecal(drawPos,text,colorsFriendly.first,colorsFriendly.second,scaling);
}
};
switch(dn->type){
case HEALTH_LOSS:{
std::string text=std::to_string(dn->damage);
DrawDamageNumber(NumberScalesWithDamage,text,{DARK_RED,{0,0,0,0}},{RED,VERY_DARK_GREY});
}break;
case HEALTH_GAIN:{
std::string text="+"+std::to_string(dn->damage);
DrawDamageNumber(NormalNumber,text,{DARK_GREEN,{0,0,0,0}},{GREEN,VERY_DARK_GREY});
}break;
case MANA_GAIN:{
std::string text="+"+std::to_string(dn->damage);
DrawDamageNumber(NormalNumber,text,{BLUE,VERY_DARK_GREY},{BLUE,VERY_DARK_GREY});
}break;
case INTERRUPT:{
std::string text="Interrupted!";
DrawDamageNumber(NormalNumber,text,{BLACK,VERY_DARK_GREY},{BLACK,VERY_DARK_GREY},{0.5f,1});
}break;
case CRIT:{
std::string text=std::to_string(dn->damage);
DrawDamageNumber(NumberScalesWithDamage,text,{YELLOW,DARK_YELLOW},{BLACK,{0,0,0,0}});
}break;
}
}
#ifdef _DEBUG
if("debug_player_info"_I){
DrawShadowStringDecal({0,128},player->GetPos().str());
@ -2390,6 +2423,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
}
#pragma endregion
bossIndicatorPos.reset();
SPAWNER_LIST.clear();
SPAWNER_CONTROLLER={};
foregroundTileGroups.clear();
@ -3775,7 +3809,7 @@ void AiL::ValidateGameStatus(){
void AiL::RenderVersionInfo(){
saveGameDisplayTime=std::max(0.f,saveGameDisplayTime-game->GetElapsedTime());
if(saveGameDisplayTime>3.f){
DrawShadowStringDecal({4.f,4.f},"Saving Game...",{255,255,255,uint8_t((sin(game->GetRuntime())+1)*127)},{0,0,0,uint8_t((sin(game->GetRuntime())+1)*127)});
DrawShadowStringDecal({4.f,4.f},"Saving Game...",{255,255,255,uint8_t((sin(game->GetRunTime())+1)*127)},{0,0,0,uint8_t((sin(game->GetRunTime())+1)*127)});
}else
if(saveGameDisplayTime>0.f){
uint8_t alpha=uint8_t(util::lerp(0,255,saveGameDisplayTime/3.f));
@ -4252,6 +4286,8 @@ rcode AiL::LoadResource(Renderable&renderable,std::string_view imgPath,bool filt
void AiL::UpdateMonsters(){
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
if(m->HasArrowIndicator())bossIndicatorPos=m->GetPos();
if(m->markedForDeletion){
AMonsterIsMarkedForDeletion();
continue;
@ -4289,6 +4325,8 @@ void AiL::ShowDamageVignetteOverlay(){
}
void AiL::GlobalGameUpdates(){
bossIndicatorPos={};
levelTime+=GetElapsedTime();
SteamAPI_RunCallbacks();
STEAMINPUT(
@ -4388,4 +4426,8 @@ void AiL::UpdateEntities(){
void AiL::AMonsterIsMarkedForDeletion(){
aMonsterIsMarkedForDeletion=true;
}
void AiL::SetBossIndicatorPos(const vf2d pos){
bossIndicatorPos=pos;
}

@ -206,6 +206,7 @@ private:
Audio audioEngine;
SteamKeyboardCallbackHandler*steamKeyboardCallbackListener=nullptr;
SteamStatsReceivedHandler*steamStatsReceivedHandlerListener=nullptr;
std::optional<vf2d>bossIndicatorPos{};
public:
AiL();
bool OnUserCreate() override;
@ -345,6 +346,7 @@ public:
void UpdateEntities();
Minimap minimap;
void AMonsterIsMarkedForDeletion(); //The way this is implemented 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.
void SetBossIndicatorPos(const vf2d pos);
struct TileGroupData{
vi2d tilePos;

@ -1015,7 +1015,7 @@ const std::string Stats::GetStatsString(const Stats&maxStats,CompactText compact
std::string col="";
if(maxStats.attributes.count(attr)&&int(val)>=int(maxStats.attributes.at(attr))){
Pixel shimmeringCol=PixelLerp({255,196,60},{254,217,133},sin((70*game->GetRuntime())/2.f+0.5f));
Pixel shimmeringCol=PixelLerp({255,196,60},{254,217,133},sin((70*game->GetRunTime())/2.f+0.5f));
col=util::PixelToHTMLColorCode(shimmeringCol);
}

@ -79,7 +79,7 @@ void LevitatingRock::Update(float fElapsedTime){
if(!RocksHaveLaunched){
deactivated=true;
drawOffsetY=cos(PI*game->GetRuntime())*3.f;
drawOffsetY=cos(PI*game->GetRunTime())*3.f;
}
else if(targetVel.has_value()){
vel=targetVel.value();

@ -90,7 +90,7 @@ void LoadingScreen::Draw(){
}
game->GetPlayer()->GetWalkEAnimation();
Animate2D::FrameSequence&playerWalkE=ANIMATION_DATA[game->GetPlayer()->GetWalkEAnimation()];
game->DrawPartialRotatedDecal({(float(currentProgress)/totalProgress)*(WINDOW_SIZE.x-48.f),WINDOW_SIZE.y-36.f},playerWalkE.GetFrame(game->GetRuntime()).GetSourceImage()->Decal(),game->GetPlayer()->GetSpinAngle(),{12,12},playerWalkE.GetFrame(game->GetRuntime()).GetSourceRect().pos,playerWalkE.GetFrame(game->GetRuntime()).GetSourceRect().size,playerScale*scale,blendCol);
game->DrawPartialRotatedDecal({(float(currentProgress)/totalProgress)*(WINDOW_SIZE.x-48.f),WINDOW_SIZE.y-36.f},playerWalkE.GetFrame(game->GetRunTime()).GetSourceImage()->Decal(),game->GetPlayer()->GetSpinAngle(),{12,12},playerWalkE.GetFrame(game->GetRunTime()).GetSourceRect().pos,playerWalkE.GetFrame(game->GetRunTime()).GetSourceRect().size,playerScale*scale,blendCol);
}
}

@ -271,6 +271,8 @@ bool Monster::Update(float fElapsedTime){
lastFacingDirectionChange+=fElapsedTime;
timeSpentAlive+=fElapsedTime;
if(HasArrowIndicator())game->SetBossIndicatorPos(GetPos());
#pragma region Handle Monster Lifetime and fade timer.
if(fadeTimer>0.f){
fadeTimer=std::max(0.f,fadeTimer-fElapsedTime);
@ -338,9 +340,18 @@ bool Monster::Update(float fElapsedTime){
m->Collision(*this);
geom2d::line line(pos,m->GetPos());
float dist = line.length();
while(dist<=0.001){
line={pos+vf2d{util::random(0.2f)-0.1f,util::random(0.2f)-0.1f},m->GetPos()};
dist=line.length();
}
m->SetPos(line.rpoint(dist*1.1f));
if(!Immovable()&&m->IsAlive()){
vel=line.vector().norm()*-128;
float knockbackStrength=1.f;
std::vector<Buff> knockbackBuffs=m->GetBuffs(COLLISION_KNOCKBACK_STRENGTH);
for(Buff&b:knockbackBuffs){
knockbackStrength+=b.intensity;
}
Knockback(line.vector().norm()*-128*knockbackStrength);
}
}
}
@ -502,33 +513,11 @@ void Monster::Collision(Player*p){
}
}
#pragma region Knockback due to buffs
vf2d knockbackVecNorm=geom2d::line<float>(GetPos(),p->GetPos()).vector().norm();
float knockbackStrength=0.f;
std::vector<Buff> knockbackBuffs=GetBuffs(COLLISION_KNOCKBACK_STRENGTH);
for(Buff&b:knockbackBuffs){
knockbackStrength+=b.intensity;
}
p->Knockback(knockbackVecNorm*knockbackStrength);
#pragma endregion
B(Attribute::COLLIDED_WITH_PLAYER)=true;
Collision();
}
void Monster::Collision(Monster&m){
#pragma region Knockback due to buffs
vf2d knockbackVecNorm=geom2d::line<float>(GetPos(),m.GetPos()).vector().norm();
float knockbackStrength=0.f;
std::vector<Buff> knockbackBuffs=GetBuffs(COLLISION_KNOCKBACK_STRENGTH);
for(Buff&b:knockbackBuffs){
knockbackStrength+=b.intensity;
}
m.Knockback(knockbackVecNorm*knockbackStrength);
#pragma endregion
Collision();
}
void Monster::Collision(){
@ -892,7 +881,10 @@ geom2d::circle<float>Monster::BulletCollisionHitbox(){
void Monster::Knockback(const vf2d&vel){
//A new angle will be applied, but will be constrained by whichever applied velocity is strongest (either the current velocity, or the new one). This prevents continuous uncapped velocities to knockbacks applied.
float maxVelThreshold=std::max(vel.mag(),this->vel.mag());
if(vel==vf2d{})return;
float maxVelThreshold;
if(this->vel==vf2d{})maxVelThreshold=vel.mag();
else maxVelThreshold=std::max(vel.mag(),this->vel.mag());
this->vel+=vel;
float newVelAngle=this->vel.polar().y;
this->vel=vf2d{maxVelThreshold,newVelAngle}.cart();

@ -437,7 +437,7 @@ const std::optional<float>MonsterData::GetLifetime()const{
return lifetime;
}
const float MonsterData::GetCollisionRadius()const{
return collisionRadius;
return collisionRadius*0.6f;
}
const bool MonsterData::HasArrowIndicator()const{
return hasArrowIndicator;

@ -306,7 +306,10 @@ State::State Player::GetState(){
void Player::Knockback(vf2d vel){
//A new angle will be applied, but will be constrained by whichever applied velocity is strongest (either the current velocity, or the new one). This prevents continuous uncapped velocities to knockbacks applied.
float maxVelThreshold=std::max(vel.mag(),this->vel.mag());
if(vel==vf2d{})return;
float maxVelThreshold;
if(this->vel==vf2d{})maxVelThreshold=vel.mag();
else maxVelThreshold=std::max(vel.mag(),this->vel.mag());
this->vel+=vel;
float newVelAngle=this->vel.polar().y;
this->vel=vf2d{maxVelThreshold,newVelAngle}.cart();
@ -522,18 +525,27 @@ void Player::Update(float fElapsedTime){
}
geom2d::line line(pos,m->GetPos());
float dist = line.length();
while(dist<=0.001){
line={pos+vf2d{util::random(0.2f)-0.1f,util::random(0.2f)-0.1f},m->GetPos()};
dist=line.length();
}
if(!m->Immovable()){
if(dist<=0.001){
m->SetPos(m->GetPos()+vf2d{util::random(2)-1,util::random(2)-1});
}else{
m->SetPos(line.rpoint(dist*1.1f));
}
m->SetPos(line.rpoint(dist*1.1f));
}
if(m->IsAlive()&&!m->IsNPC()){ //Don't set the knockback if this monster is actually an NPC. Let's just push them around.
Knockback(line.vector().norm()*-128.f);
float knockbackStrength=1.f;
std::vector<Buff>knockbackBuffs=m->GetBuffs(COLLISION_KNOCKBACK_STRENGTH);
for(Buff&b:knockbackBuffs){
knockbackStrength+=b.intensity;
}
Knockback(line.vector().norm()*-128.f*knockbackStrength);
}
}
}
if(!std::isfinite(vel.x)||!std::isfinite(vel.y)){
ERR(std::format("WARNING! The velocity vector for the player is NOT normal! Current vel:{} . Attempting manual resetting of velocity.",vel.str()));
vel={};
}
if(vel.x>0){
vel.x=std::max(0.f,vel.x-friction*fElapsedTime);
} else {

@ -134,7 +134,7 @@ const void SaveFile::SaveGame(){
saveFile["Overworld Map Location"].SetString(State_OverworldMap::GetCurrentConnectionPoint().name);
saveFile["Chapter"].SetInt(game->GetCurrentChapter());
saveFile["Save Name"].SetString(std::string(GetSaveFileName()));
saveFile["Game Time"].SetReal(game->GetRuntime());
saveFile["Game Time"].SetReal(game->GetRunTime());
saveFile["TravelingMerchant"].SetString(std::string(Merchant::GetCurrentTravelingMerchant().GetKeyName()));
saveFile["Minimap Display Mode"].SetInt(int(game->minimap.GetMinimapMode()));
@ -216,7 +216,7 @@ const void SaveFile::SaveGame(){
utils::datafile::Read(metadata,"save_file_path"_S+"metadata.dat");
}
}
metadata.GetProperty(std::format("save{}",saveFileID)).SetReal(game->GetRuntime(),0U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetReal(game->GetRunTime(),0U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetInt(game->GetCurrentChapter(),1U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetInt(game->GetPlayer()->Level(),2U);
metadata.GetProperty(std::format("save{}",saveFileID)).SetString(game->GetPlayer()->GetClassName(),3U);

@ -84,7 +84,7 @@ void State_Death::OnUserUpdate(AiL*game){
}
Input::StopVibration();
game->SetMosaicEffect(uint8_t(util::lerp(9.f,1.f,(gameSlowdownPct-7)/3.f)));
Input::SetLightbar(PixelLerp(BLACK,DARK_RED,sin(1.5f*game->GetRuntime())/2.f+0.5f));
Input::SetLightbar(PixelLerp(BLACK,DARK_RED,sin(1.5f*game->GetRunTime())/2.f+0.5f));
}
if(gameSlowdownPct<10.f){

@ -139,8 +139,8 @@ void State_LevelComplete::DrawOverlay(AiL*game){
game->DrawRotatedDecal(levelUpTextPos+vf2d{2.f,1.f},GFX["overworld_arrow.png"].Decal(),-PI/2,GFX["overworld_arrow.png"].Sprite()->Size(),{1.f,1.f},BLACK);
game->DrawRotatedDecal(levelUpTextPos+vf2d{2.f,0.f},GFX["overworld_arrow.png"].Decal(),-PI/2,GFX["overworld_arrow.png"].Sprite()->Size(),{1.f,1.f},YELLOW);
game->DrawShadowStringPropDecal(levelUpTextPos+vf2d{-69.f,4.f},std::format("HP +{}",int(game->GetPlayer()->GetHealthGrowthRate())),PixelLerp({226,234,244},WHITE,sin((40*game->GetRuntime())/2.f+0.5f)));
game->DrawShadowStringPropDecal(levelUpTextPos+vf2d{-69.f,12.f},std::format("ATK+{}",int(game->GetPlayer()->GetAtkGrowthRate())),PixelLerp({226,234,244},WHITE,sin((40*game->GetRuntime())/2.f+0.5f)));
game->DrawShadowStringPropDecal(levelUpTextPos+vf2d{-69.f,4.f},std::format("HP +{}",int(game->GetPlayer()->GetHealthGrowthRate())),PixelLerp({226,234,244},WHITE,sin((40*game->GetRunTime())/2.f+0.5f)));
game->DrawShadowStringPropDecal(levelUpTextPos+vf2d{-69.f,12.f},std::format("ATK+{}",int(game->GetPlayer()->GetAtkGrowthRate())),PixelLerp({226,234,244},WHITE,sin((40*game->GetRunTime())/2.f+0.5f)));
}
void State_LevelComplete::TurnOffXPSound(){

@ -163,7 +163,7 @@ void State_OverworldMap::Draw(AiL*game){
if(!Unlock::IsUnlocked(cp)){
game->view.FillRectDecal(cp.rect.pos,cp.rect.size,{0,0,0,128});
}else{
float exclamationScale=fmod(game->GetRuntime(),1.0f)<0.2f?1.f:1.2f;
float exclamationScale=fmod(game->GetRunTime(),1.0f)<0.2f?1.f:1.2f;
if(!cp.Visited()){
game->view.DrawDecal(cp.rect.pos+vf2d{-1.f,0.f},GFX["exclamation.png"].Decal(),{exclamationScale,exclamationScale},BLACK);
game->view.DrawDecal(cp.rect.pos+vf2d{-1.f,-1.f},GFX["exclamation.png"].Decal(),{exclamationScale,exclamationScale});
@ -209,7 +209,7 @@ void State_OverworldMap::Draw(AiL*game){
direction++;
}
float arrowDist=fmod(game->GetRuntime(),1.0f)<0.5f?14:18;
float arrowDist=fmod(game->GetRunTime(),1.0f)<0.5f?14:18;
for(auto&[index,medianAngle]:neighbors){
game->view.DrawRotatedDecal(game->GetPlayer()->GetPos()+vf2d{arrowDist,GetAngle(medianAngle)}.cart()+vf2d{0.f,1.f},GFX["overworld_arrow.png"].Decal(),GetAngle(medianAngle),GFX["overworld_arrow.png"].Sprite()->Size()/2,{1.f,1.f},{0,0,0});

@ -96,8 +96,8 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
m.phase=STONE_PILLAR_CAST;
m.F(A::CASTING_TIMER)=ConfigFloat("Stone Pillar Cast Time");
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos();
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*1.1f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd"))),true);
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*0.75f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),true);
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*1.25f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Circle Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Circle Rotation Spd"))),true);
game->AddEffect(std::make_unique<Effect>(m.V(A::LOCKON_POS),ConfigFloat("Stone Pillar Cast Time"),"spell_insignia.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()*MONSTER_DATA.at("Stone Pillar").GetSizeMult()/12.f)*0.9f,0.3f,vf2d{},ConfigPixel("Stone Pillar Spell Insignia Color"),util::random(2*PI),util::degToRad(ConfigFloat("Stone Pillar Spell Insignia Rotation Spd"))),true);
}break;
case 1:{
m.PerformAnimation("ROCK TOSS CAST");
@ -142,7 +142,7 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
case SHOOT_STONE_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)>=ConfigFloat("Rock Toss Wait Time")-ConfigFloat("Rock Toss Track Time")){
m.PerformIdleAnimation();
m.PerformAnimation("STONE PILLAR CAST");
m.UpdateFacingDirection(game->GetPlayer()->GetPos());
}
if(m.F(A::CASTING_TIMER)<=0.f){

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 0
#define VERSION_BUILD 9382
#define VERSION_BUILD 9413
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="238" height="369" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="53">
<map version="1.10" tiledversion="1.10.2" class="Map" orientation="orthogonal" renderorder="right-down" width="238" height="369" tilewidth="24" tileheight="24" infinite="0" nextlayerid="7" nextobjectid="55">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="mountain_day"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -1897,5 +1897,13 @@
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="53" name="Spawn Group 2" type="SpawnGroup" x="3948" y="7830" width="496" height="424">
<ellipse/>
</object>
<object id="54" template="../maps/Monsters/Boar.tx" x="4080" y="7962">
<properties>
<property name="spawner" type="object" value="53"/>
</properties>
</object>
</objectgroup>
</map>

Binary file not shown.

After

Width:  |  Height:  |  Size: 641 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 570 B

@ -550,7 +550,7 @@ MonsterStrategy
Backpedal Movespeed = 50%
Charge Knockback Amount = 140
Charge Knockback Amount = 1.4
}
Goblin Dagger
{

@ -792,8 +792,8 @@ Monsters
MoveSpd = 0%
# The Pillar is supposed to be 350 radius.
Size = 350%
Collision Radius = 8
Size = 300%
Collision Radius = 7
Lifetime = 5s
XP = 0

@ -96,6 +96,7 @@ Images
GFX_SpellInsignia = spell_insignia.png
GFX_Rock = rock.png
GFX_RockOutline = rock_outline.png
GFX_BossIndicator = bossIndicator.png
# Ability Icons
GFX_Warrior_BattleCry_Icon = Ability Icons/battlecry.png

@ -658,7 +658,7 @@ void olc::ViewPort::DrawStringDecal(const olc::vf2d& pos, std::string_view sText
pge->SetDrawTarget(nullptr);
newDecal->Update();
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
}
@ -673,7 +673,7 @@ void olc::ViewPort::DrawStringDecal(Font&font, const olc::vf2d& pos, const std::
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale/4,col);
}
@ -701,7 +701,7 @@ void olc::ViewPort::DrawStringPropDecal(const olc::vf2d& pos, std::string_view s
pge->SetDrawTarget(nullptr);
newDecal->Update();
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
}
@ -747,8 +747,8 @@ void olc::ViewPort::DrawShadowStringDecal(const olc::vf2d& pos, std::string_view
pge->SetDrawTarget(nullptr);
newShadowDecal->Update();
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
}
@ -795,8 +795,8 @@ void olc::ViewPort::DrawShadowStringPropDecal(const olc::vf2d& pos, std::string_
pge->SetDrawTarget(nullptr);
newShadowDecal->Update();
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
pge->garbageCollector[key+"_SHADOW"].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},pge->garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,pge->garbageCollector[key].decal,scale,col);
}
@ -812,9 +812,9 @@ void olc::ViewPort::DrawShadowStringDecal(Font&font, const olc::vf2d& pos, const
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
std::erase_if(pge->garbageCollector,[&](auto&key){
if(key.second.expireTime<pge->GetRuntime()){
if(key.second.expireTime<pge->GetRunTime()){
delete key.second.decal;
return true;
}
@ -841,7 +841,7 @@ void olc::ViewPort::DrawDropShadowStringDecal(Font&font, const olc::vf2d& pos, c
pge->garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
pge->garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
pge->garbageCollector[key].expireTime=pge->GetRuntime()+120.0f;
pge->garbageCollector[key].expireTime=pge->GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0},pge->garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0.5f},pge->garbageCollector[key].decal,scale/4,shadowCol);

@ -1066,7 +1066,7 @@ namespace olc
// Specify which Sprite should be the target of drawing functions, use nullptr
// to specify the primary screen
void SetDrawTarget(Sprite* target);
double GetRuntime() const;
double GetRunTime() const;
void SetRuntime(const double runTime);
// Gets the current Frames Per Second
uint32_t GetFPS() const;
@ -2257,7 +2257,7 @@ namespace olc
return 0;
}
double PixelGameEngine::GetRuntime() const
double PixelGameEngine::GetRunTime() const
{ return dRunTime; }
void PixelGameEngine::SetRuntime(const double runTime){
@ -2271,7 +2271,7 @@ namespace olc
}
void PixelGameEngine::ClearTimedOutGarbage(){
std::erase_if(garbageCollector,[&](auto&key){
if(key.second.expireTime<GetRuntime()){
if(key.second.expireTime<GetRunTime()){
delete key.second.decal;
return true;
}
@ -3600,7 +3600,7 @@ namespace olc
SetDrawTarget(nullptr);
newDecal->Update();
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale,col);
}
@ -3629,7 +3629,7 @@ namespace olc
SetDrawTarget(nullptr);
newDecal->Update();
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale,col);
}
@ -3675,8 +3675,8 @@ namespace olc
SetDrawTarget(nullptr);
newShadowDecal->Update();
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col);
}
@ -3692,7 +3692,7 @@ namespace olc
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos,garbageCollector[key].decal,scale/4,col);
}
@ -3707,7 +3707,7 @@ namespace olc
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
for(float y=-shadowSizeFactor;y<=shadowSizeFactor+0.1;y+=shadowSizeFactor/2){
for(float x=-shadowSizeFactor;x<=shadowSizeFactor+0.1;x+=shadowSizeFactor/2){
if(x!=0||y!=0){
@ -3729,7 +3729,7 @@ namespace olc
garbageCollector[key].decal=font.RenderStringToDecal(sText,WHITE);
garbageCollector[key].originalStr=std::string(sText.begin(),sText.end());
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
DrawDecal(pos+vf2d{0,0.5f},garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0},garbageCollector[key].decal,scale/4,shadowCol);
DrawDecal(pos+vf2d{0.5f,0.5f},garbageCollector[key].decal,scale/4,shadowCol);
@ -3778,8 +3778,8 @@ namespace olc
SetDrawTarget(nullptr);
newShadowDecal->Update();
}
garbageCollector[key].expireTime=GetRuntime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRuntime()+120.0f;
garbageCollector[key].expireTime=GetRunTime()+120.0f;
garbageCollector[key+"_SHADOW"].expireTime=GetRunTime()+120.0f;
DrawDecal(pos-vf2d{shadowSizeFactor,shadowSizeFactor},garbageCollector[key+"_SHADOW"].decal,scale/4,shadowCol);
DrawDecal(pos,garbageCollector[key].decal,scale,col);
}

Loading…
Cancel
Save