Implement Stone Elemental Burrowing attack, fixed Stone Elemental pathfinding towards target location, slightly increased visual indicator for stone pillar cast circle. Fixed sorted rendering for objects that appear before elements with collision. Refactored iteration loops for drawing so they don't cause multiple unnecessary draw iterations. Do not draw monsters that are marked for deletion anymore (prevent sudden pop-in when drawing on the last frame of fading out). Release Build 9350.

pull/57/head
sigonasr2 6 months ago
parent fa0caa5fa9
commit 45be6e80e6
  1. 327
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 3
      Adventures in Lestoria/LevitatingRock.cpp
  3. 13
      Adventures in Lestoria/Monster.cpp
  4. 2
      Adventures in Lestoria/Monster.h
  5. 2
      Adventures in Lestoria/Player.cpp
  6. 2
      Adventures in Lestoria/SlimeKing.cpp
  7. 72
      Adventures in Lestoria/Stone_Elemental.cpp
  8. 2
      Adventures in Lestoria/Version.h
  9. 41
      Adventures in Lestoria/assets/Campaigns/2_1.tmx
  10. 9
      Adventures in Lestoria/assets/maps/Monsters/Stone Pillar.tx
  11. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1266,94 +1266,214 @@ void AiL::RenderWorld(float fElapsedTime){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
#pragma region Rendering before Tile Depth Rendering
if(tilesWithCollision.size()>0){
const float topTileY=(*tilesWithCollision.begin())->group->GetCollisionRange().middle().y;
#pragma region Depth Ordered Rendering
{
auto it=monstersBeforeLower.begin();
while(it!=monstersBeforeLower.end()){
Monster&m=**it;
if(m.pos.y<topTileY){
m.Draw();
it=monstersBeforeLower.erase(it);
if(it==monstersBeforeLower.end())break;
continue;
}
break;
}
}
{
auto it=bulletsLower.begin();
while(it!=bulletsLower.end()){
const Bullet&b=**it;
if(b.pos.y<topTileY){
b.Draw();
it=bulletsLower.erase(it);
if(it==bulletsLower.end())break;
continue;
}
break;
}
}
{
auto it=backgroundEffectsLower.begin();
while(it!=backgroundEffectsLower.end()){
const Effect&e=**it;
if(e.pos.y<topTileY){
e.Draw();
it=backgroundEffectsLower.erase(it);
if(it==backgroundEffectsLower.end())break;
continue;
}
break;
}
}
{
auto it=dropsBeforeLower.begin();
while(it!=dropsBeforeLower.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<topTileY){
drop.Draw();
it=dropsBeforeLower.erase(it);
if(it==dropsBeforeLower.end())break;
continue;
}
break;
}
}
if(!player->rendered&&!player->upperLevel&&player->GetPos().y<topTileY){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
RenderPlayer(player->GetPos(),{1,1});
}
{
auto it=monstersAfterLower.begin();
while(it!=monstersAfterLower.end()){
Monster&m=**it;
if(m.pos.y<topTileY){
m.Draw();
it=monstersAfterLower.erase(it);
if(it==monstersAfterLower.end())break;
continue;
}
break;
}
}
{
auto it=dropsAfterLower.begin();
while(it!=dropsAfterLower.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<topTileY){
drop.Draw();
it=dropsAfterLower.erase(it);
if(it==dropsAfterLower.end())break;
continue;
}
break;
}
}
{
auto it=foregroundEffectsLower.begin();
while(it!=foregroundEffectsLower.end()){
const Effect&e=**it;
if(e.pos.y<topTileY){
e.Draw();
it=foregroundEffectsLower.erase(it);
if(it==foregroundEffectsLower.end())break;
continue;
}
break;
}
}
#pragma endregion
}
#pragma endregion
#pragma region Foreground Rendering w/Depth
for(TileRenderData*tile:tilesWithCollision){
#pragma region Depth Ordered Rendering
for(auto it=monstersBeforeLower.begin();it!=monstersBeforeLower.end();++it){
{
auto it=monstersBeforeLower.begin();
while(it!=monstersBeforeLower.end()){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.Draw();
it=monstersBeforeLower.erase(it);
if(it==monstersBeforeLower.end())break;
if(it!=monstersBeforeLower.begin())--it;
continue;
}
break;
}
for(auto it=bulletsLower.begin();it!=bulletsLower.end();++it){
}
{
auto it=bulletsLower.begin();
while(it!=bulletsLower.end()){
const Bullet&b=**it;
if(b.pos.y<tile->group->GetCollisionRange().middle().y){
b.Draw();
it=bulletsLower.erase(it);
if(it==bulletsLower.end())break;
if(it!=bulletsLower.begin())--it;
continue;
}
break;
}
for(auto it=backgroundEffectsLower.begin();it!=backgroundEffectsLower.end();++it){
}
{
auto it=backgroundEffectsLower.begin();
while(it!=backgroundEffectsLower.end()){
const Effect&e=**it;
if(e.pos.y<tile->group->GetCollisionRange().middle().y){
e.Draw();
it=backgroundEffectsLower.erase(it);
if(it==backgroundEffectsLower.end())break;
if(it!=backgroundEffectsLower.begin())--it;
continue;
}
break;
}
for(auto it=dropsBeforeLower.begin();it!=dropsBeforeLower.end();++it){
}
{
auto it=dropsBeforeLower.begin();
while(it!=dropsBeforeLower.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<tile->group->GetCollisionRange().middle().y){
drop.Draw();
it=dropsBeforeLower.erase(it);
if(it==dropsBeforeLower.end())break;
if(it!=dropsBeforeLower.begin())--it;
continue;
}
break;
}
if(!player->rendered&&!player->upperLevel&&player->GetPos().y<tile->group->GetCollisionRange().middle().y){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
RenderPlayer(player->GetPos(),{1,1});
}
if(!player->rendered&&!player->upperLevel&&player->GetPos().y<tile->group->GetCollisionRange().middle().y){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
for(auto it=monstersAfterLower.begin();it!=monstersAfterLower.end();++it){
RenderPlayer(player->GetPos(),{1,1});
}
{
auto it=monstersAfterLower.begin();
while(it!=monstersAfterLower.end()){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.Draw();
it=monstersAfterLower.erase(it);
if(it==monstersAfterLower.end())break;
if(it!=monstersAfterLower.begin())--it;
continue;
}
break;
}
for(auto it=dropsAfterLower.begin();it!=dropsAfterLower.end();++it){
}
{
auto it=dropsAfterLower.begin();
while(it!=dropsAfterLower.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<tile->group->GetCollisionRange().middle().y){
drop.Draw();
it=dropsAfterLower.erase(it);
if(it==dropsAfterLower.end())break;
if(it!=dropsAfterLower.begin())--it;
continue;
}
break;
}
for(auto it=foregroundEffectsLower.begin();it!=foregroundEffectsLower.end();++it){
}
{
auto it=foregroundEffectsLower.begin();
while(it!=foregroundEffectsLower.end()){
const Effect&e=**it;
if(e.pos.y<tile->group->GetCollisionRange().middle().y){
e.Draw();
it=foregroundEffectsLower.erase(it);
if(it==foregroundEffectsLower.end())break;
if(it!=foregroundEffectsLower.begin())--it;
continue;
}
break;
}
}
#pragma endregion
RenderTile(*tile,{255,255,255,uint8_t(255-tile->tileOpacity/TileGroup::FADE_TIME*TileGroup::FADE_AMT)});
float distToPlayer=geom2d::line<float>(player->GetPos(),tile->pos+vf2d{12,12}).length();
@ -1522,94 +1642,215 @@ void AiL::RenderWorld(float fElapsedTime){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
#pragma region Rendering before Tile Depth Rendering
if(tilesWithCollision.size()>0){
const float topTileY=(*tilesWithCollision.begin())->group->GetCollisionRange().middle().y;
#pragma region Depth Ordered Rendering
{
auto it=monstersBeforeUpper.begin();
while(it!=monstersBeforeUpper.end()){
Monster&m=**it;
if(m.pos.y<topTileY){
m.Draw();
it=monstersBeforeUpper.erase(it);
if(it==monstersBeforeUpper.end())break;
continue;
}
break;
}
}
{
auto it=bulletsUpper.begin();
while(it!=bulletsUpper.end()){
const Bullet&b=**it;
if(b.pos.y<topTileY){
b.Draw();
it=bulletsUpper.erase(it);
if(it==bulletsUpper.end())break;
continue;
}
break;
}
}
{
auto it=backgroundEffectsUpper.begin();
while(it!=backgroundEffectsUpper.end()){
const Effect&e=**it;
if(e.pos.y<topTileY){
e.Draw();
it=backgroundEffectsUpper.erase(it);
if(it==backgroundEffectsUpper.end())break;
continue;
}
break;
}
}
{
auto it=dropsBeforeUpper.begin();
while(it!=dropsBeforeUpper.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<topTileY){
drop.Draw();
it=dropsBeforeUpper.erase(it);
if(it==dropsBeforeUpper.end())break;
continue;
}
break;
}
}
if(!player->rendered&&!player->upperLevel&&player->GetPos().y<topTileY){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
RenderPlayer(player->GetPos(),{1,1});
}
{
auto it=monstersAfterUpper.begin();
while(it!=monstersAfterUpper.end()){
Monster&m=**it;
if(m.pos.y<topTileY){
m.Draw();
it=monstersAfterUpper.erase(it);
if(it==monstersAfterUpper.end())break;
continue;
}
break;
}
}
{
auto it=dropsAfterUpper.begin();
while(it!=dropsAfterUpper.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<topTileY){
drop.Draw();
it=dropsAfterUpper.erase(it);
if(it==dropsAfterUpper.end())break;
continue;
}
break;
}
}
{
auto it=foregroundEffectsUpper.begin();
while(it!=foregroundEffectsUpper.end()){
const Effect&e=**it;
if(e.pos.y<topTileY){
e.Draw();
it=foregroundEffectsUpper.erase(it);
if(it==foregroundEffectsUpper.end())break;
if(it!=foregroundEffectsUpper.begin())--it;
continue;
}
break;
}
}
#pragma endregion
}
#pragma endregion
#pragma region Upper Foreground Rendering w/Depth
for(TileRenderData*tile:tilesWithCollision){
#pragma region Depth Ordered Upper Rendering
for(auto it=monstersBeforeUpper.begin();it!=monstersBeforeUpper.end();++it){
{
auto it=monstersBeforeUpper.begin();
while(it!=monstersBeforeUpper.end()){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.Draw();
it=monstersBeforeUpper.erase(it);
if(it==monstersBeforeUpper.end())break;
if(it!=monstersBeforeUpper.begin())--it;
continue;
}
break;
}
for(auto it=bulletsUpper.begin();it!=bulletsUpper.end();++it){
}
{
auto it=bulletsUpper.begin();
while(it!=bulletsUpper.end()){
const Bullet&b=**it;
if(b.pos.y<tile->group->GetCollisionRange().middle().y){
b.Draw();
it=bulletsUpper.erase(it);
if(it==bulletsUpper.end())break;
if(it!=bulletsUpper.begin())--it;
continue;
}
break;
}
for(auto it=backgroundEffectsUpper.begin();it!=backgroundEffectsUpper.end();++it){
}
{
auto it=backgroundEffectsUpper.begin();
while(it!=backgroundEffectsUpper.end()){
const Effect&e=**it;
if(e.pos.y<tile->group->GetCollisionRange().middle().y){
e.Draw();
it=backgroundEffectsUpper.erase(it);
if(it==backgroundEffectsUpper.end())break;
if(it!=backgroundEffectsUpper.begin())--it;
continue;
}
break;
}
for(auto it=dropsBeforeUpper.begin();it!=dropsBeforeUpper.end();++it){
}
{
auto it=dropsBeforeUpper.begin();
while(it!=dropsBeforeUpper.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<tile->group->GetCollisionRange().middle().y){
drop.Draw();
it=dropsBeforeUpper.erase(it);
if(it==dropsBeforeUpper.end())break;
if(it!=dropsBeforeUpper.begin())--it;
continue;
}
break;
}
if(!player->rendered&&player->upperLevel&&player->GetPos().y<tile->group->GetCollisionRange().middle().y){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
RenderPlayer(player->GetPos(),{1,1});
}
if(!player->rendered&&player->upperLevel&&player->GetPos().y<tile->group->GetCollisionRange().middle().y){
player->rendered=true;
if(player->GetZ()>0){
vf2d shadowScale=vf2d{8*player->GetSizeMult()/3.f,1}/std::max(1.f,player->GetZ()/24);
view.DrawDecal(player->GetPos()-vf2d{3,3}*shadowScale/2+vf2d{0,6*player->GetSizeMult()},GFX["circle.png"].Decal(),shadowScale,BLACK);
}
for(auto it=monstersAfterUpper.begin();it!=monstersAfterUpper.end();++it){
RenderPlayer(player->GetPos(),{1,1});
}
{
auto it=monstersAfterUpper.begin();
while(it!=monstersAfterUpper.end()){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.Draw();
it=monstersAfterUpper.erase(it);
if(it==monstersAfterUpper.end())break;
if(it!=monstersAfterUpper.begin())--it;
continue;
}
break;
}
for(auto it=dropsAfterUpper.begin();it!=dropsAfterUpper.end();++it){
}
{
auto it=dropsAfterUpper.begin();
while(it!=dropsAfterUpper.end()){
const ItemDrop&drop=ItemDrop::drops[*it];
if(drop.pos.y<tile->group->GetCollisionRange().middle().y){
drop.Draw();
it=dropsAfterUpper.erase(it);
if(it==dropsAfterUpper.end())break;
if(it!=dropsAfterUpper.begin())--it;
continue;
}
break;
}
for(auto it=foregroundEffectsUpper.begin();it!=foregroundEffectsUpper.end();++it){
}
{
auto it=foregroundEffectsUpper.begin();
while(it!=foregroundEffectsUpper.end()){
const Effect&e=**it;
if(e.pos.y<tile->group->GetCollisionRange().middle().y){
e.Draw();
it=foregroundEffectsUpper.erase(it);
if(it==foregroundEffectsUpper.end())break;
if(it!=foregroundEffectsUpper.begin())--it;
continue;
}
break;
}
}
#pragma endregion
RenderTile(*tile,{255,255,255,uint8_t(255-tile->tileOpacity/TileGroup::FADE_TIME*TileGroup::FADE_AMT)});
float distToPlayer=geom2d::line<float>(player->GetPos(),tile->pos+vf2d{12,12}).length();
@ -2451,7 +2692,7 @@ void AiL::_PrepareLevel(MapName map,MusicChange changeMusic){
for(NPCData data:game->MAP_DATA[game->GetCurrentLevel()].npcs){
if(Unlock::IsUnlocked(data.unlockCondition)){
MONSTER_LIST.push_back(std::make_unique<Monster>(data.spawnPos,MONSTER_DATA[data.name]));
MONSTER_LIST.back()->iframe_timer=INFINITE;
MONSTER_LIST.back()->SetIframes(INFINITE);
MONSTER_LIST.back()->npcData=data;
}
}

@ -91,6 +91,7 @@ bool LevitatingRock::PlayerHit(Player*player){
deactivated=true;
fadeOutTime=0.5f;
player->Knockback(vel/3.f);
return true;
}
bool LevitatingRock::MonsterHit(Monster&monster){
@ -98,6 +99,8 @@ bool LevitatingRock::MonsterHit(Monster&monster){
deactivated=true;
fadeOutTime=0.5f;
monster.Knockback(vel/3.f);
return true;
}
void LevitatingRock::Draw()const{

@ -408,6 +408,7 @@ void Monster::UpdateFacingDirection(vf2d facingTargetPoint){
}
}
void Monster::Draw()const{
if(markedForDeletion)return;
Pixel blendCol=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;
if(GetZ()>0){
@ -632,7 +633,7 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
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);
SetIframes(GetFloat(Attribute::IFRAME_TIME_UPON_HIT));
return true;
}
@ -950,6 +951,10 @@ const Animate2D::FrameSequence&Monster::GetCurrentAnimation()const{
return ANIMATION_DATA[std::format("{}_{}",name,animation.currentStateName)];
}
const Animate2D::FrameSequence&Monster::GetAnimation(const std::string_view animationName)const{
return ANIMATION_DATA[std::format("{}_{}_{}",name,animationName,int(GetFacingDirection()))];
}
const bool Monster::HasLineOfSight(vf2d targetPos)const{
geom2d::line<float>losLine=geom2d::line<float>(GetPos(),targetPos);
float losLineLength=losLine.length();
@ -1002,7 +1007,7 @@ DeathSpawnInfo::DeathSpawnInfo(const std::string_view monsterName,const uint8_t
void DeathSpawnInfo::Spawn(const vf2d monsterDeathPos,const bool onUpperLevel){
for(uint8_t i=0;i<spawnAmt;i++){
game->SpawnMonster(monsterDeathPos+spawnLocOffset,MONSTER_DATA.at(monsterSpawnName),onUpperLevel).iframe_timer=0.25f;
game->SpawnMonster(monsterDeathPos+spawnLocOffset,MONSTER_DATA.at(monsterSpawnName),onUpperLevel).SetIframes(0.25f);
}
}
@ -1048,3 +1053,7 @@ void Monster::MarkForDeletion(){
const bool Monster::IsDead()const{
return !IsAlive();
}
void Monster::SetIframes(const float iframeTime){
iframe_timer=iframeTime;
}

@ -120,6 +120,7 @@ public:
void PerformAnimation(const std::string_view animationName);
void PerformAnimation(const std::string_view animationName,const Direction facingDir);
const Animate2D::FrameSequence&GetCurrentAnimation()const;
const Animate2D::FrameSequence&GetAnimation(const std::string_view animationName)const;
const bool OnUpperLevel()const;
void Moved();
//Returns false if a path could not be found.
@ -133,6 +134,7 @@ public:
void SetState(State::State newState);
static void InitializeStrategies();
const bool HasIframes()const;
void SetIframes(const float iframeTime);
const float GetZ()const;
void SetZ(float z);
const std::function<void(Monster&,float,std::string)>&GetStrategy()const;

@ -1487,7 +1487,7 @@ const vf2d Player::GetAimingLocation(bool useWalkDir,bool invert){
//Find the closest monster target.
vf2d closestPoint={std::numeric_limits<float>::max(),std::numeric_limits<float>::max()};
for(std::unique_ptr<Monster>&m:MONSTER_LIST){
if(m->IsAlive()){
if(m->IsAlive()&&!m->Invulnerable()){
geom2d::line<float>aimingLine=geom2d::line<float>(GetPos(),m->GetPos());
float distToMonster=aimingLine.length();
float distToClosestPoint=geom2d::line<float>(GetPos(),closestPoint).length();

@ -248,7 +248,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
m.size=ConfigInt("Phase1.Size")/100.f;
m.diesNormally=false;
m.F(A::IFRAME_TIME_UPON_HIT)=0;
m.iframe_timer=ConfigFloat("Phase5.IframeTimePerHit");
m.SetIframes(ConfigFloat("Phase5.IframeTimePerHit"));
m.phase=ConfigInt("StartPhase");
}break;
case 1:{

@ -65,18 +65,25 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
STONE_PILLAR_CAST,
SHOOT_STONE_CAST,
DIVE_UNDERGROUND_DIG,
DIVE_UNDERGROUND_MOVE,
DIVE_UNDERGROUND_SURFACE,
};
auto ReturnToWaitingPhase=[&](){
m.phase=WAITING;
m.PerformIdleAnimation();
m.F(A::ATTACK_COOLDOWN)=0.f;
};
switch(m.phase){
case WAITING:{
m.F(A::ATTACK_COOLDOWN)+=fElapsedTime;
if(m.F(A::ATTACK_COOLDOWN)>=ConfigFloat("Attack Wait Time")){
int randomAttackChoice=util::random()%2;
int randomAttackChoice=util::random()%3;
float distToPlayer=util::distance(m.GetPos(),game->GetPlayer()->GetPos());
//if(distToPlayer>=ConfigPixels("Auto Dive Range"))randomAttackChoice=2; //Force dig attack if too far away.
if(distToPlayer>=ConfigPixels("Auto Dive Range"))randomAttackChoice=2; //Force dig attack if too far away.
switch(randomAttackChoice){
case 0:{
@ -84,7 +91,7 @@ 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()/12.f),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"),"range_indicator.png",m.OnUpperLevel(),vf2d{1.f,1.f}*(MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()/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()/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);
}break;
case 1:{
@ -104,9 +111,9 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
}
}break;
case 2:{
m.PerformAnimation("BURROW UNDEGROUND");
m.PerformAnimation("BURROW UNDERGROUND");
m.phase=DIVE_UNDERGROUND_DIG;
m.F(A::CASTING_TIMER)=ConfigFloat("Burrow Wait Time");
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
}break;
}
}
@ -114,10 +121,8 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
case STONE_PILLAR_CAST:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
m.phase=WAITING;
game->SpawnMonster(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar"),m.OnUpperLevel());
m.PerformIdleAnimation();
m.F(A::ATTACK_COOLDOWN)=0.f;
ReturnToWaitingPhase();
game->Hurt(m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius(),m.GetAttack(),m.OnUpperLevel(),0.f,HurtType::PLAYER);
}
if(geom2d::overlaps(geom2d::circle<float>{m.V(A::LOCKON_POS),MONSTER_DATA.at("Stone Pillar").GetCollisionRadius()},geom2d::circle<float>{m.GetPos(),m.GetCollisionRadius()})){
@ -125,25 +130,66 @@ void Monster::STRATEGY::STONE_ELEMENTAL(Monster&m,float fElapsedTime,std::string
const vf2d targetWalkPos=stonePillarCastLine.rpoint(stonePillarCastLine.length()+48.f);
m.target=targetWalkPos;
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
m.PerformAnimation("ROCK TOSS CAST");
m.PerformAnimation("STONE PILLAR CAST");
m.UpdateFacingDirection(game->GetPlayer()->GetPos());
}
}break;
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.UpdateFacingDirection(game->GetPlayer()->GetPos());
if(m.F(A::CASTING_TIMER)<=0.f){
m.phase=WAITING;
m.PerformIdleAnimation();
m.F(A::ATTACK_COOLDOWN)=0.f;
ReturnToWaitingPhase();
}
}break;
case DIVE_UNDERGROUND_DIG:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
m.phase=DIVE_UNDERGROUND_MOVE;
float randomAngle=util::random(2*PI);
const float minDist=ConfigPixelsArr("Burrow Teleport Distance",0);
const float maxDist=ConfigPixelsArr("Burrow Teleport Distance",1);
const float distRange=maxDist-minDist;
float randomDist=util::random(distRange)+minDist;
m.V(A::LOCKON_POS)=game->GetPlayer()->GetPos()+vf2d{randomDist,randomAngle}.cart();
m.target=m.V(A::LOCKON_POS);
m.targetAcquireTimer=INFINITY;
m.SetState(State::MOVE_TOWARDS);
const float baseMoveSpd=100.f*m.GetMoveSpdMult();
const float distToTarget=geom2d::line<float>(m.GetPos(),m.V(A::LOCKON_POS)).length();
const float targetMoveSpdRatio=std::max(0.f,distToTarget/baseMoveSpd/ConfigFloat("Burrow Wait Time")-1);
m.AddBuff(BuffType::SPEEDBOOST,ConfigFloat("Burrow Wait Time"),targetMoveSpdRatio);
m.F(A::CASTING_TIMER)=ConfigFloat("Burrow Wait Time");
m.B(A::IGNORE_DEFAULT_ANIMATIONS)=true;
m.SetIframes(ConfigFloat("Burrow Wait Time")+m.GetAnimation("RISE FROM UNDERGROUND").GetTotalAnimationDuration());
}
}break;
case DIVE_UNDERGROUND_MOVE:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
RUN_TOWARDS(m,fElapsedTime,"Run Towards");
if(m.F(A::CASTING_TIMER)<=0.f){
m.B(A::IGNORE_DEFAULT_ANIMATIONS)=false;
m.PerformAnimation("RISE FROM UNDERGROUND");
m.phase=DIVE_UNDERGROUND_SURFACE;
m.targetAcquireTimer=0;
m.F(A::CASTING_TIMER)=m.GetCurrentAnimation().GetTotalAnimationDuration();
const float bulletAngRandomOffset{util::random(PI/2)};
for(int i=0;i<ConfigInt("Burrow Ring Bullet Count");i++){
const float bulletAngle=((2*PI)/ConfigInt("Burrow Ring Bullet Count"))*i+bulletAngRandomOffset;
CreateBullet(Bullet)(m.GetPos(),vf2d{ConfigFloat("Burrow Ring Bullet Speed"),bulletAngle}.cart(),ConfigFloat("Burrow Ring Bullet Size"),ConfigInt("Burrow Ring Bullet Damage"),m.OnUpperLevel(),false,{96,0,120},vf2d{ConfigFloat("Burrow Ring Bullet Size")/3,ConfigFloat("Burrow Ring Bullet Size")/3})EndBullet;
}
}
}break;
case DIVE_UNDERGROUND_SURFACE:{
m.F(A::CASTING_TIMER)-=fElapsedTime;
if(m.F(A::CASTING_TIMER)<=0.f){
ReturnToWaitingPhase();
}
}break;
}
}

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 0
#define VERSION_BUILD 9318
#define VERSION_BUILD 9350
#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="46">
<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">
<properties>
<property name="Backdrop" propertytype="Backdrop" value="mountain_day"/>
<property name="Background Music" propertytype="BGM" value="foresty1_1"/>
@ -1882,52 +1882,17 @@
<ellipse/>
</object>
<object id="28" name="Player Spawn" type="PlayerSpawnLocation" x="5112" y="8064" width="24" height="24"/>
<object id="35" template="../maps/Monsters/Goblin (Bombs).tx" x="4548" y="8016">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="36" template="../maps/Monsters/Hawk.tx" x="4554" y="8060">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="37" template="../maps/Monsters/Stone Elemental.tx" x="4551" y="8142">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="38" template="../maps/Monsters/Goblin (Bombs).tx" x="4548" y="8016">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="39" template="../maps/Monsters/Goblin (Bombs).tx" x="4548" y="8016">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="40" template="../maps/Monsters/Goblin (Bombs).tx" x="4634" y="7752">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="41" template="../maps/Monsters/Goblin (Bombs).tx" x="4590" y="8223">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="42" template="../maps/Monsters/Goblin (Bombs).tx" x="4364" y="8230">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="44" template="../maps/Monsters/Hawk.tx" x="4567" y="8053">
<object id="46" template="../maps/Monsters/Stone Elemental.tx" x="4642" y="7936">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>
</object>
<object id="45" template="../maps/Monsters/Hawk.tx" x="4636" y="7938">
<object id="47" template="../maps/Monsters/Stone Elemental.tx" x="4864" y="8082">
<properties>
<property name="spawner" type="object" value="15"/>
</properties>

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<template>
<tileset firstgid="1" source="../Monsters.tsx"/>
<object name="Stone Pillar" type="Monster" gid="16" width="52.8" height="52.8">
<properties>
<property name="spawner" type="object" value="48"/>
</properties>
</object>
</template>
Loading…
Cancel
Save