Added Yellow Slime jump attack behavior. Release Build 8023.
This commit is contained in:
parent
468302833f
commit
72c5a7394b
@ -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)=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");
|
||||
}
|
||||
}
|
||||
}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<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};
|
||||
}
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
}
|
||||
}break;
|
||||
default:{}
|
||||
}
|
||||
}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
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user