Added Yellow Slime jump attack behavior. Release Build 8023.

pull/35/head
sigonasr2 11 months ago
parent 468302833f
commit 72c5a7394b
  1. 22
      Adventures in Lestoria/AdventuresInLestoria.cpp
  2. 1
      Adventures in Lestoria/MonsterAttribute.h
  3. 4
      Adventures in Lestoria/Player.cpp
  4. 1
      Adventures in Lestoria/Player.h
  5. 204
      Adventures in Lestoria/RunTowards.cpp
  6. 2
      Adventures in Lestoria/Version.h
  7. 6
      Adventures in Lestoria/assets/config/Monsters.txt
  8. 6
      Adventures in Lestoria/assets/config/audio/events.txt
  9. BIN
      x64/Release/Adventures in Lestoria.exe

@ -1139,6 +1139,15 @@ void AiL::RenderWorld(float fElapsedTime){
}
});
std::sort(tilesWithoutCollision.begin(),tilesWithoutCollision.end(),[](TileRenderData*tile1,TileRenderData*tile2){return tile1->layerID<tile2->layerID;});
for(auto it=monstersBeforeLower.begin();it!=monstersBeforeLower.end();++it){
Monster&m=**it;
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
for(auto it=monstersAfterLower.begin();it!=monstersAfterLower.end();++it){
Monster&m=**it;
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
#pragma region Foreground Rendering w/Depth
for(TileRenderData*tile:tilesWithCollision){
@ -1146,7 +1155,6 @@ void AiL::RenderWorld(float fElapsedTime){
for(auto it=monstersBeforeLower.begin();it!=monstersBeforeLower.end();++it){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
m.Draw();
it=monstersBeforeLower.erase(it);
if(it==monstersBeforeLower.end())break;
@ -1199,7 +1207,6 @@ void AiL::RenderWorld(float fElapsedTime){
for(auto it=monstersAfterLower.begin();it!=monstersAfterLower.end();++it){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
m.Draw();
it=monstersAfterLower.erase(it);
if(it==monstersAfterLower.end())break;
@ -1388,6 +1395,15 @@ void AiL::RenderWorld(float fElapsedTime){
}
});
std::sort(tilesWithoutCollision.begin(),tilesWithoutCollision.end(),[](TileRenderData*tile1,TileRenderData*tile2){return tile1->layerID<tile2->layerID;});
for(auto it=monstersBeforeUpper.begin();it!=monstersBeforeUpper.end();++it){
Monster&m=**it;
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
for(auto it=monstersAfterUpper.begin();it!=monstersAfterUpper.end();++it){
Monster&m=**it;
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
}
#pragma region Upper Foreground Rendering w/Depth
for(TileRenderData*tile:tilesWithCollision){
@ -1395,7 +1411,6 @@ void AiL::RenderWorld(float fElapsedTime){
for(auto it=monstersBeforeUpper.begin();it!=monstersBeforeUpper.end();++it){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
m.Draw();
it=monstersBeforeUpper.erase(it);
if(it==monstersBeforeUpper.end())break;
@ -1448,7 +1463,6 @@ void AiL::RenderWorld(float fElapsedTime){
for(auto it=monstersAfterUpper.begin();it!=monstersAfterUpper.end();++it){
Monster&m=**it;
if(m.pos.y<tile->group->GetCollisionRange().middle().y){
m.strategyDraw(this,m,MONSTER_DATA[m.GetName()].GetAIStrategy());
m.Draw();
it=monstersAfterUpper.erase(it);
if(it==monstersAfterUpper.end())break;

@ -105,4 +105,5 @@ enum class Attribute{
DEFENSIVE_COUNT,
ITEM_USE_COUNT,
LAST_JUMP_TIMER,
INITIALIZED,
};

@ -1445,4 +1445,8 @@ const float Player::GetHealthGrowthRate()const{
}
const float Player::GetAtkGrowthRate()const{
return atkGrowthRate;
}
const float Player::GetIframeTime()const{
return iframe_time;
}

@ -251,6 +251,7 @@ public:
void ResetVelocity();
const float GetHealthGrowthRate()const;
const float GetAtkGrowthRate()const;
const float GetIframeTime()const;
private:
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;

@ -40,17 +40,29 @@ All rights reserved.
#include "AdventuresInLestoria.h"
#include "MonsterStrategyHelpers.h"
#include "util.h"
#include "FallingDebris.h"
#include "SoundEffect.h"
INCLUDE_game
INCLUDE_MONSTER_DATA
INCLUDE_GFX
using A=Attribute;
void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string strategy){
const bool jumpingEnabled=ConfigFloat("JumpTimer")<0.f;
const bool jumpingEnabled=ConfigFloat("JumpTimer")>0.f;
if(!m.B(A::INITIALIZED)){
if(jumpingEnabled){
m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer");
}
m.B(A::INITIALIZED)=true;
}
m.targetAcquireTimer=std::max(0.f,m.targetAcquireTimer-fElapsedTime);
m.F(A::JUMP_LANDING_TIMER)=std::max(0.f,m.F(A::JUMP_LANDING_TIMER)-fElapsedTime);
if(m.targetAcquireTimer==0){
if(m.targetAcquireTimer==0&&m.GetState()!=State::JUMP){
m.targetAcquireTimer=ConfigFloat("WaitTime");
auto desiredTargetLine = geom2d::line(m.pos,game->GetPlayer()->GetPos());
@ -62,6 +74,7 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string str
}
m.SetState(State::MOVE_TOWARDS);
}
if(geom2d::line(m.pos,m.target).length()>4.f){
m.UpdateFacingDirection(m.target);
}
@ -71,107 +84,106 @@ void Monster::STRATEGY::RUN_TOWARDS(Monster&m,float fElapsedTime,std::string str
m.B(A::JUMP_TOWARDS_PLAYER)=true;
m.F(A::RECOVERY_TIME)=recoveryTime;
m.F(A::JUMP_MOVE_SPD)=jumpMoveSpd;
m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer");
m.SetState(State::JUMP);
};
const auto Landed=[&](){
SoundEffect::PlaySFX("Slime Land",m.GetPos());
};
switch(m.I(A::PHASE)){
case 0:{//Setup jump behaviors.
switch(m.GetState()){
case State::MOVE_TOWARDS:{
if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){
vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult();
m.SetPos(newPos);
if(m.GetPos()!=newPos){
bool pathFound=m.StartPathfinding(4);
if(!pathFound){
m.SetState(State::MOVE_TOWARDS);
//Choose a random position around us and move towards it.
float randomAngle=util::random(2*PI);
float randomDist=util::random(24*6);
m.target=m.GetPos()+vf2d{sin(randomAngle),cos(randomAngle)}*randomDist;
}
}
m.PerformJumpAnimation();
} else {
m.SetState(State::NORMAL);//Revert state once we've finished moving towards target.
m.PerformIdleAnimation();
}
if(jumpingEnabled){
m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer");
m.F(A::LAST_JUMP_TIMER)=std::max(0.f,m.F(A::LAST_JUMP_TIMER)-fElapsedTime);
if(m.F(A::LAST_JUMP_TIMER)==0.f){
if(geom2d::line(m.pos,m.target).length()<=ConfigInt("MaxPlayerJumpEngageDistance")/100.f*24){
StartJumpTowardsPlayer(ConfigFloat("JumpDelayTime"),ConfigFloat("JumpRecoveryTime"),ConfigFloat("JumpMoveSpd"));
}
m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer");
}
}
m.I(A::PHASE)++;
}break;
case 1:{//Main behaviors.
switch(m.state){
case State::MOVE_TOWARDS:{
if(geom2d::line(m.pos,m.target).length()>100*fElapsedTime*m.GetMoveSpdMult()){
vf2d newPos=m.pos+geom2d::line(m.pos,m.target).vector().norm()*100*fElapsedTime*m.GetMoveSpdMult();
m.SetPos(newPos);
if(m.GetPos()!=newPos){
bool pathFound=m.StartPathfinding(4);
if(!pathFound){
m.SetState(State::MOVE_TOWARDS);
//Choose a random position around us and move towards it.
float randomAngle=util::random(2*PI);
float randomDist=util::random(24*6);
m.target=m.GetPos()+vf2d{sin(randomAngle),cos(randomAngle)}*randomDist;
}
}
m.PerformJumpAnimation();
} else {
m.SetState(State::NORMAL);//Revert state once we've finished moving towards target.
m.PerformIdleAnimation();
}
if(jumpingEnabled){
m.F(A::LAST_JUMP_TIMER)=std::max(0.f,m.F(A::LAST_JUMP_TIMER)-fElapsedTime);
if(m.F(A::LAST_JUMP_TIMER)==0.f){
StartJumpTowardsPlayer(ConfigFloat("JumpDelayTime"),ConfigFloat("JumpRecoveryTime",ConfigFloat("JumpMoveSpd")));
}
}
}break;
case State::PATH_AROUND:{
m.PathAroundBehavior(fElapsedTime);
}break;
case State::JUMP:{
float jumpLandingTimerRatio=m.F(A::JUMP_LANDING_TIMER)/m.F(A::JUMP_ORIGINAL_LANDING_TIMER);
vf2d jumpTargetPos=m.V(A::JUMP_TARGET_POS);
if(m.B(A::JUMP_TOWARDS_PLAYER)){
jumpTargetPos=game->GetPlayer()->GetPos();
}
if(m.GetPos().x>jumpTargetPos.x){
m.SetX(std::max(jumpTargetPos.x,m.GetPos().x-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
} else
if(m.GetPos().x<jumpTargetPos.x){
m.SetX(std::min(jumpTargetPos.x,m.GetPos().x+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
}
if(m.GetPos().y>jumpTargetPos.y){
m.SetY(std::max(jumpTargetPos.y,m.GetPos().y-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
} else
if(m.GetPos().y<jumpTargetPos.y){
m.SetY(std::min(jumpTargetPos.y,m.GetPos().y+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
}
if(m.F(A::JUMP_LANDING_TIMER)>=m.F(A::JUMP_ORIGINAL_LANDING_TIMER)/2){
m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),1-jumpLandingTimerRatio));
}else{
m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),jumpLandingTimerRatio*2));
}
if(m.F(A::JUMP_LANDING_TIMER)==0){
m.state=State::RECOVERY;
game->SetupWorldShake(0.6f);
geom2d::line<float>lineToPlayer(m.GetPos(),game->GetPlayer()->GetPos());
float dist=lineToPlayer.length();
for(int i=0;i<200;i++){
float randomDir=util::random(2*PI);
game->AddEffect(std::make_unique<FallingDebris>(m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*m.GetSizeMult()*8,util::random(1),"circle.png",m.OnUpperLevel(),vf2d{1,1},0.5,vf2d{cos(randomDir)*util::random(5),sin(randomDir)*-util::random(15)-5}*30,BLACK),true);
}
if(dist<12*m.GetSizeMult()){
int jumpDamage=0;
if(ConfigInt("JumpAttackDamage")>0)jumpDamage=ConfigInt("JumpAttackDamage");
game->GetPlayer()->Hurt(jumpDamage,m.OnUpperLevel(),m.GetZ());
if(dist<0.001){
float randomDir=util::random(2*PI);
lineToPlayer={m.GetPos(),m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*1};
}
game->GetPlayer()->Knockback(lineToPlayer.vector().norm()*float(ConfigInt("JumpKnockbackFactor")));
if(m.phase!=2){ //In phase 2, the player can get slammed multiple times. No iframes for messing up.
game->GetPlayer()->SetIframes(1);
}
}
m.SetZ(0);
Landed(m.phase);
m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){});
} else
if(m.F(A::JUMP_LANDING_TIMER)<=ConfigFloat("JumpWarningIndicatorTime")){
m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){
Decal*dec=GFX["range_indicator.png"].Decal();
game->view.DrawRotatedDecal(m.GetPos(),dec,0,dec->sprite->Size()/2,vf2d{m.GetSizeMult(),m.GetSizeMult()},RED);
});
case State::PATH_AROUND:{
m.PathAroundBehavior(fElapsedTime);
}break;
case State::JUMP:{
float jumpLandingTimerRatio=m.F(A::JUMP_LANDING_TIMER)/m.F(A::JUMP_ORIGINAL_LANDING_TIMER);
vf2d jumpTargetPos=m.V(A::JUMP_TARGET_POS);
if(m.B(A::JUMP_TOWARDS_PLAYER)){
jumpTargetPos=game->GetPlayer()->GetPos();
}
if(m.GetPos().x>jumpTargetPos.x){
m.SetX(std::max(jumpTargetPos.x,m.GetPos().x-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
} else
if(m.GetPos().x<jumpTargetPos.x){
m.SetX(std::min(jumpTargetPos.x,m.GetPos().x+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
}
if(m.GetPos().y>jumpTargetPos.y){
m.SetY(std::max(jumpTargetPos.y,m.GetPos().y-m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
} else
if(m.GetPos().y<jumpTargetPos.y){
m.SetY(std::min(jumpTargetPos.y,m.GetPos().y+m.F(A::JUMP_MOVE_SPD)*game->GetElapsedTime()));
}
if(m.F(A::JUMP_LANDING_TIMER)>=m.F(A::JUMP_ORIGINAL_LANDING_TIMER)/2){
m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),1-jumpLandingTimerRatio));
}else{
m.SetZ(util::lerp(0,float(ConfigInt("JumpHeight")),jumpLandingTimerRatio*2));
}
if(m.F(A::JUMP_LANDING_TIMER)==0){
m.state=State::RECOVERY;
game->SetupWorldShake(0.6f);
geom2d::line<float>lineToPlayer(m.GetPos(),game->GetPlayer()->GetPos());
float dist=lineToPlayer.length();
for(int i=0;i<m.GetSizeMult()*25;i++){
float randomDir=util::random(2*PI);
game->AddEffect(std::make_unique<FallingDebris>(m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*m.GetSizeMult()*8,util::random(1),"circle.png",m.OnUpperLevel(),vf2d{1,1},0.5,vf2d{cos(randomDir)*util::random(5),sin(randomDir)*-util::random(15)-5}*30,BLACK),true);
}
if(dist<12*m.GetSizeMult()){
int jumpDamage=0;
if(ConfigInt("JumpAttackDamage")>0)jumpDamage=ConfigInt("JumpAttackDamage");
game->GetPlayer()->Hurt(jumpDamage,m.OnUpperLevel(),m.GetZ());
if(dist<0.001){
float randomDir=util::random(2*PI);
lineToPlayer={m.GetPos(),m.GetPos()+vf2d{cos(randomDir),sin(randomDir)}*1};
}
}break;
default:{}
game->GetPlayer()->Knockback(lineToPlayer.vector().norm()*float(ConfigInt("JumpKnockbackFactor")));
game->GetPlayer()->SetIframes(game->GetPlayer()->GetIframeTime()+0.3f);
}
m.SetZ(0);
Landed();
m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){});
} else
if(m.F(A::JUMP_LANDING_TIMER)<=ConfigFloat("JumpWarningIndicatorTime")){
m.SetStrategyDrawFunction([](AiL*game,Monster&m,const std::string&strategy){
Decal*dec=GFX["range_indicator.png"].Decal();
game->view.DrawRotatedDecal(m.GetPos(),dec,0,dec->sprite->Size()/2,vf2d{m.GetSizeMult(),m.GetSizeMult()},RED);
});
}
}break;
case State::RECOVERY:{
m.F(A::RECOVERY_TIME)=std::max(0.f,m.F(A::RECOVERY_TIME)-fElapsedTime);
if(m.F(A::RECOVERY_TIME)==0.f){
m.SetState(State::NORMAL);
m.F(A::LAST_JUMP_TIMER)=ConfigFloat("JumpTimer");
}
}break;
default:{}
}
}

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 0
#define VERSION_MINOR 5
#define VERSION_PATCH 0
#define VERSION_BUILD 8012
#define VERSION_BUILD 8023
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -139,6 +139,12 @@ Monsters
Death Sound = Slime Dead
Walk Sound = Slime Walk
# Jump property overrides
JumpTimer = 10.0
JumpAttackDamage = 20
JumpKnockbackFactor = 50.0
JumpMoveSpd = 70
# Drop Item Name, Drop Percentage(0-100%), Drop Min Quantity, Drop Max Quantity
DROP[0] = Yellow Slime Remains,30%,1,1
DROP[1] = Minor Recovery Potion,5%,1,1

@ -178,6 +178,12 @@ Events
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_dead2.ogg, 60%
}
Slime Land
{
CombatSound = True
# Specify file names, followed by volume %. Optional min and max pitch adjustment (Defaults are 90%,110%)
File[0] = slime_king_landing.ogg, 80%, 140%, 150%
}
Slime King Land
{
CombatSound = True

Loading…
Cancel
Save