Casting range clamping. Refactored hurt function to accept an upperlevel check so there's no requirement to do it manually anymore. Converted AoE damage functions for Meteor to use AoE hurt functions built-in. Fix upper bridge targeting indicator rendering.

pull/28/head
sigonasr2 1 year ago
parent 73d29a2ab5
commit 1faf121bb9
  1. 36
      Crawler/Crawler.cpp
  2. 1
      Crawler/Effect.h
  3. 6
      Crawler/FireBolt.cpp
  4. 4
      Crawler/LightningBolt.cpp
  5. 7
      Crawler/Meteor.cpp
  6. 6
      Crawler/Monster.cpp
  7. 5
      Crawler/Monster.h
  8. 18
      Crawler/Player.cpp
  9. 2
      Crawler/Player.h
  10. 20
      Crawler/PulsatingFire.cpp
  11. 2
      Crawler/Version.h
  12. 2
      Crawler/Warrior.cpp
  13. 2
      Crawler/Wizard.cpp
  14. 1601
      Crawler/pge.data
  15. 2
      Crawler/pge.js
  16. BIN
      Crawler/pge.wasm
  17. BIN
      x64/Release/Crawler.exe

@ -285,7 +285,7 @@ void Crawler::HandleUserInput(float fElapsedTime){
}
}
if(player->GetState()!=State::NORMAL){
if(player->GetState()!=State::NORMAL&&player->GetState()!=State::PREP_CAST){
setIdleAnimation=false;
}
@ -313,8 +313,9 @@ void Crawler::HandleUserInput(float fElapsedTime){
void Crawler::UpdateCamera(float fElapsedTime){
lastWorldShakeAdjust=std::max(0.f,lastWorldShakeAdjust-fElapsedTime);
if(worldShakeTime-fElapsedTime>0){
worldShakeVel={1000,-1000};
if(lastWorldShakeAdjust==0){
lastWorldShakeAdjust=0.02;
lastWorldShakeAdjust=0.04;
worldShakeVel.x*=-1;
worldShakeVel.y*=-1;
}
@ -375,8 +376,8 @@ void Crawler::UpdateBullets(float fElapsedTime){
if(!b->deactivated){
if(b->friendly){
for(Monster&m:MONSTER_LIST){
if(b->OnUpperLevel()==m.OnUpperLevel()&&geom2d::overlaps(geom2d::circle(m.GetPos(),12*m.GetSizeMult()),geom2d::circle(b->pos,b->radius))){
if(b->hitList.find(&m)==b->hitList.end()&&m.Hurt(b->damage)){
if(geom2d::overlaps(geom2d::circle(m.GetPos(),12*m.GetSizeMult()),geom2d::circle(b->pos,b->radius))){
if(b->hitList.find(&m)==b->hitList.end()&&m.Hurt(b->damage,b->OnUpperLevel())){
if(!b->hitsMultiple){
if(b->MonsterHit(m)){
it=BULLET_LIST.erase(it);
@ -391,8 +392,8 @@ void Crawler::UpdateBullets(float fElapsedTime){
}
}
} else {
if(b->OnUpperLevel()==player->OnUpperLevel()&&geom2d::overlaps(geom2d::circle(player->GetPos(),12*player->GetSizeMult()/2),geom2d::circle(b->pos,b->radius))){
if(player->Hurt(b->damage)){
if(geom2d::overlaps(geom2d::circle(player->GetPos(),12*player->GetSizeMult()/2),geom2d::circle(b->pos,b->radius))){
if(player->Hurt(b->damage,b->OnUpperLevel())){
if(b->PlayerHit(GetPlayer())){
it=BULLET_LIST.erase(it);
if(it==BULLET_LIST.end()){
@ -427,8 +428,8 @@ void Crawler::UpdateBullets(float fElapsedTime){
}
void Crawler::HurtEnemies(vf2d pos,float radius,int damage,bool upperLevel){
for(Monster&m:MONSTER_LIST){
if(m.OnUpperLevel()==upperLevel&&geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m.GetPos(),12*m.GetSizeMult()))){
m.Hurt(damage);
if(geom2d::overlaps(geom2d::circle(pos,radius),geom2d::circle(m.GetPos(),12*m.GetSizeMult()))){
m.Hurt(damage,upperLevel);
}
}
}
@ -575,11 +576,25 @@ void Crawler::RenderWorld(float fElapsedTime){
for(Bullet*b:bulletsLower){
b->Draw();
}
auto RenderPrecastTargetingIndicator=[&](){
if(player->GetState()==State::PREP_CAST){
float precastSize=GetPlayer()->castPrepAbility->precastInfo.size;
vf2d scale=vf2d{precastSize,precastSize}/3.f;
float precastRange=GetPlayer()->castPrepAbility->precastInfo.range;
vf2d scale=vf2d{precastSize,precastSize}*2/3.f;
vf2d centerPoint=GetWorldMousePos()-vf2d{game->GFX_Circle.Sprite()->width*scale.x/2,game->GFX_Circle.Sprite()->height*scale.y/2};
float distance=sqrt(pow(player->GetX()-GetWorldMousePos().x,2)+pow(player->GetY()-GetWorldMousePos().y,2));
if(distance>precastRange){//Clamp the distance.
vf2d pointToCursor = {GetWorldMousePos().x-player->GetX(),GetWorldMousePos().y-player->GetY()};
pointToCursor=pointToCursor.norm()*precastRange;
vf2d centerPoint=player->GetPos()+pointToCursor-vf2d{game->GFX_Circle.Sprite()->width*scale.x/2,game->GFX_Circle.Sprite()->height*scale.y/2};
view.DrawDecal(centerPoint,GFX_Circle.Decal(),scale,{255,0,0,96});
} else {
view.DrawDecal(centerPoint,GFX_Circle.Decal(),scale,{255,0,0,96});
}
}
};
if(!player->OnUpperLevel()){
RenderPrecastTargetingIndicator();
}
#pragma region Foreground Rendering
for(TileGroup&group:foregroundTileGroups){
@ -642,6 +657,9 @@ void Crawler::RenderWorld(float fElapsedTime){
for(Bullet*b:bulletsUpper){
b->Draw();
}
if(player->OnUpperLevel()){
RenderPrecastTargetingIndicator();
}
#pragma region Upper Foreground Rendering
for(TileGroup&group:upperForegroundTileGroups){
if(geom2d::overlaps(group.GetFadeRange(),player->pos)){

@ -39,6 +39,7 @@ struct PulsatingFire:Effect{
PulsatingFire(vf2d pos,float lifetime,AnimationState animation,bool upperLevel,vf2d size={1,1},float fadeout=0.0f,vf2d spd={},Pixel col=WHITE,float rotation=0,float rotationSpd=0,bool additiveBlending=false);
std::vector<float>pulsatingFireValues;
float lastParticleTimer=0;
float lastDamageTimer=0;
bool Update(float fElapsedTime)override;
void Draw()override;
};

@ -34,11 +34,7 @@ bool FireBolt::MonsterHit(Monster& monster)
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),util::random(0.5),AnimationState::DOT_PARTICLE,upperLevel,util::random(2),util::random(0.4),vf2d{util::random(300)-150,util::random(300)-150},Pixel{255,uint8_t(util::random(190)+60),60}));
}
game->SetupWorldShake(0.25);
for(Monster&m:MONSTER_LIST){
if(geom2d::line(monster.GetPos(),m.GetPos()).length()<=2.5*24){
m.Hurt(3*damage);
}
}
game->HurtEnemies(monster.GetPos(),2.5*24,3*damage,OnUpperLevel());
game->AddEffect(std::make_unique<Effect>(monster.GetPos(),0,AnimationState::SPLASH_EFFECT,upperLevel,5,0.25,vf2d{},Pixel{240,120,60}));
return false;
}

@ -52,10 +52,8 @@ bool LightningBolt::MonsterHit(Monster& monster)
if(&m==&monster||monster.OnUpperLevel()!=m.OnUpperLevel())continue;
geom2d::line<float>lineToTarget=geom2d::line<float>(monster.GetPos(),m.GetPos());
float dist=lineToTarget.length();
vf2d vec;
vec.norm();
if(dist<=72){
if(m.Hurt(game->GetPlayer()->GetAttack()*2)){
if(m.Hurt(game->GetPlayer()->GetAttack()*2,OnUpperLevel())){
EMITTER_LIST.push_back(std::make_unique<LightningBoltEmitter>(LightningBoltEmitter(monster.GetPos(),m.GetPos(),0.05,0.25,upperLevel)));
game->AddEffect(std::make_unique<Effect>(m.GetPos(),0.5,AnimationState::LIGHTNING_SPLASH,upperLevel,monster.GetSizeMult(),0.25,vf2d{},WHITE,util::random(PI)));
targetsHit++;

@ -24,12 +24,7 @@ bool Meteor::Update(float fElapsedTime){
vf2d effectPos=vf2d{cos(randomAngle),sin(randomAngle)}*randomRange+meteorOffset;
game->AddEffect(std::make_unique<Effect>(effectPos,0,AnimationState::DOT_PARTICLE,OnUpperLevel(),vf2d{util::random(2)+1,util::random(3)+1},util::random(3)+1,vf2d{util::random(10)-5,-util::random(20)-5},Pixel{255,uint8_t(randomColorTintG),uint8_t(randomColorTint),uint8_t(util::random(128)+128)},0,0,true),effectPos.y<meteorOffset.y);
}
for(Monster&m:MONSTER_LIST){
float dist=sqrt(pow(pos.y-m.GetPos().y,2)+pow(pos.x-m.GetPos().x,2));
if(dist<=4*24){
m.Hurt(game->GetPlayer()->GetAttack()*9);
}
}
game->HurtEnemies(pos,4*24,game->GetPlayer()->GetAttack()*9,OnUpperLevel());
game->AddEffect(std::make_unique<PulsatingFire>(pos,3,AnimationState::FIRE_RING1,OnUpperLevel(),vf2d{8,8},1),true);
}
return Effect::Update(fElapsedTime);

@ -288,7 +288,7 @@ void Monster::Draw(){
void Monster::Collision(Player*p){
if(MONSTER_DATA[type].GetCollisionDmg()>0&&!hasHitPlayer){
hasHitPlayer=true;
p->Hurt(MONSTER_DATA[type].GetCollisionDmg());
p->Hurt(MONSTER_DATA[type].GetCollisionDmg(),OnUpperLevel());
}
Collision();
}
@ -327,8 +327,8 @@ void Monster::Moved(){
AnimationState Monster::GetDeathAnimationName(){
return MONSTER_DATA[type].GetDeathAnimation();
}
bool Monster::Hurt(int damage){
if(hp<=0) return false;
bool Monster::Hurt(int damage,bool onUpperLevel){
if(hp<=0||onUpperLevel!=OnUpperLevel()) return false;
float mod_dmg=damage;
for(Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){
mod_dmg-=damage*b.intensity;

@ -97,8 +97,9 @@ protected:
Animate2D::Frame GetFrame();
void UpdateAnimation(AnimationState state);
bool Update(float fElapsedTime);
//Returns true when damage is actually dealt (there is a death check here.)
bool Hurt(int damage);
//Returns true when damage is actually dealt. Provide whether or not the attack is on the upper level or not. Monsters must be on the same level to get hit by it. (there is a death check and level check here.)
//If you need to hurt multiple enemies try Crawler::HurtEnemies()
bool Hurt(int damage,bool onUpperLevel);
bool IsAlive();
vf2d&GetTargetPos();
Key GetFacingDirection();

@ -304,15 +304,16 @@ void Player::Update(float fElapsedTime){
auto AllowedToCast=[&](Ability&ability){return !ability.precastInfo.precastTargetingRequired;};
//If pressed is set to false, uses held instead.
auto CheckAndPerformAbility=[&](Ability&ability,HWButton key){
if(ability.name!="???"){
if(ability.cooldown==0&&GetMana()>=ability.manaCost){
if(key.bPressed||key.bReleased&&&ability==castPrepAbility&&GetState()==State::PREP_CAST){
if(key.bHeld||key.bReleased&&&ability==castPrepAbility&&GetState()==State::PREP_CAST){
if(AllowedToCast(ability)&&ability.action({})){
ability.cooldown=ability.COOLDOWN_TIME;
mana-=ability.manaCost;
}else
if(ability.precastInfo.precastTargetingRequired&&GetState()!=State::PREP_CAST){
if(ability.precastInfo.precastTargetingRequired&&GetState()==State::NORMAL){
PrepareCast(ability);
}
}
@ -392,8 +393,8 @@ bool Player::HasIframes(){
return iframe_time>0;
}
bool Player::Hurt(int damage){
if(hp<=0||iframe_time!=0) return false;
bool Player::Hurt(int damage,bool onUpperLevel){
if(hp<=0||iframe_time!=0||OnUpperLevel()!=onUpperLevel) return false;
if(state==State::BLOCK)damage=0;
float mod_dmg=damage;
for(Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){
@ -497,7 +498,14 @@ std::vector<Buff>Player::GetBuffs(BuffType buff){
}
void Player::CastSpell(Ability&ability){
castInfo={ability.name,ability.precastInfo.castTime,ability.precastInfo.castTime,game->GetWorldMousePos()};
vf2d castPosition=game->GetWorldMousePos();
float distance=sqrt(pow(GetX()-game->GetWorldMousePos().x,2)+pow(GetY()-game->GetWorldMousePos().y,2));
if(distance>ability.precastInfo.range){//Clamp the distance.
vf2d pointToCursor = {game->GetWorldMousePos().x-GetX(),game->GetWorldMousePos().y-GetY()};
pointToCursor=pointToCursor.norm()*ability.precastInfo.range;
castPosition=GetPos()+pointToCursor;
}
castInfo={ability.name,ability.precastInfo.castTime,ability.precastInfo.castTime,castPosition};
SetState(State::CASTING);
}

@ -106,7 +106,7 @@ public:
void AddBuff(BuffType type,float duration,float intensity);
std::vector<Buff>GetBuffs(BuffType buff);
bool Hurt(int damage);
bool Hurt(int damage,bool onUpperLevel);
//specificClass is a bitwise-combination of classes from the Class enum. It makes sure certain animations only play if you are a certain class.
void UpdateAnimation(AnimationState animState,int specificClass=ANY);
Animate2D::Frame GetFrame();

@ -5,9 +5,10 @@
INCLUDE_game
INCLUDE_ANIMATION_DATA
INCLUDE_MONSTER_LIST
PulsatingFire::PulsatingFire(vf2d pos, float lifetime, AnimationState animation, bool upperLevel, vf2d size, float fadeout, vf2d spd, Pixel col, float rotation, float rotationSpd, bool additiveBlending)
:Effect(pos,lifetime,animation,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending),lastParticleTimer(lifetime){
:Effect(pos,lifetime,animation,upperLevel,size,fadeout,spd,col,rotation,rotationSpd,additiveBlending){
for(int i=0;i<8;i++){
pulsatingFireValues.push_back(util::random(1));
}
@ -15,17 +16,22 @@ PulsatingFire::PulsatingFire(vf2d pos, float lifetime, AnimationState animation,
bool PulsatingFire::Update(float fElapsedTime){
lastParticleTimer-=fElapsedTime;
lastDamageTimer-=fElapsedTime;
if(lastParticleTimer<=0){
int particleCount=rand()%10+1;
int particleCount=rand()%5+1;
for(int i=0;i<particleCount;i++){
float randomAngle=util::random(2*PI);
float randomRange=100*size.x*(1-util::random(0.25))*(1-util::random(0.25));
float randomColorTintG=256-(util::random(128)+util::random(128));
float randomColorTint=util::random(128);
game->AddEffect(std::make_unique<Effect>(pos+vf2d{cos(randomAngle),sin(randomAngle)}*randomRange,0,AnimationState::DOT_PARTICLE,OnUpperLevel(),vf2d{util::random(2)+1,1},util::random(4)+2,vf2d{util::random(20)-5,-util::random(30)-10},Pixel{255,uint8_t(randomColorTintG),uint8_t(randomColorTint),uint8_t(util::random(128)+128)},0,0,true));
float randomRange=12*size.x*(1-util::random(0.25))*(1-util::random(0.25));
float randomColorTintG=128-(util::random(64)+util::random(64));
float randomColorTint=util::random(16);
game->AddEffect(std::make_unique<Effect>(pos+vf2d{cos(randomAngle),sin(randomAngle)}*randomRange,0,AnimationState::DOT_PARTICLE,OnUpperLevel(),vf2d{util::random(2)+1,1},util::random(4)+2,vf2d{util::random(10)-5,-util::random(15)-5},Pixel{128,uint8_t(randomColorTintG),uint8_t(randomColorTint),uint8_t(util::random(128)+128)}));
}
lastParticleTimer=util::random(0.2)+0.025;
}
if(lastDamageTimer<=0){
lastDamageTimer=0.99;
game->HurtEnemies(pos,4*24,game->GetPlayer()->GetAttack()*1,OnUpperLevel());
}
return Effect::Update(fElapsedTime);
}
@ -52,6 +58,6 @@ void PulsatingFire::Draw(){
effectSpr=&ANIMATION_DATA[AnimationState::FIRE_RING1];
}
const Renderable*img=effectSpr->GetFrame(0).GetSourceImage();
game->view.DrawPartialDecal(pos-effectSpr->GetFrame(0).GetSourceRect().size/2*size,img->Decal(),effectSpr->GetFrame(0).GetSourceRect().pos,effectSpr->GetFrame(0).GetSourceRect().size,size,{255,uint8_t(pulsatingFireValues[i]*256),0,uint8_t(63*(sin(3*lifetime+PI*pulsatingFireValues[i]))+64)});
game->view.DrawPartialDecal(pos-effectSpr->GetFrame(0).GetSourceRect().size/2*size,img->Decal(),effectSpr->GetFrame(0).GetSourceRect().pos,effectSpr->GetFrame(0).GetSourceRect().size,size,{255,uint8_t(pulsatingFireValues[i]*256),0,uint8_t((63*(sin(3*lifetime+PI*pulsatingFireValues[i]))+64)*(fadeout/original_fadeoutTime))});
}
}

@ -2,7 +2,7 @@
#define VERSION_MAJOR 0
#define VERSION_MINOR 2
#define VERSION_PATCH 0
#define VERSION_BUILD 712
#define VERSION_BUILD 747
#define stringify(a) stringify_(a)
#define stringify_(a) #a

@ -45,7 +45,7 @@ bool Warrior::AutoAttack(){
closest=&m;
}
}
if(closest!=nullptr&&closest->Hurt(GetAttack())){
if(closest!=nullptr&&closest->Hurt(GetAttack(),OnUpperLevel())){
attack_cooldown_timer=ATTACK_COOLDOWN;
swordSwingTimer=0.2;
SetState(State::SWING_SWORD);

@ -15,7 +15,7 @@ Class Wizard::cl=WIZARD;
Ability Wizard::rightClickAbility={"Teleport",8,5,VERY_DARK_BLUE,DARK_BLUE};
Ability Wizard::ability1={"Firebolt",6,30};
Ability Wizard::ability2={"Lightning Bolt",6,25};
Ability Wizard::ability3={"Meteor",40,75,VERY_DARK_RED,VERY_DARK_RED,{1.5,900,400}};
Ability Wizard::ability3={"Meteor",40,75,VERY_DARK_RED,VERY_DARK_RED,PrecastData(1.5,9*24,4*24)};
Ability Wizard::ability4={"???",0,0};
AnimationState Wizard::idle_n=WIZARD_IDLE_N;
AnimationState Wizard::idle_e=WIZARD_IDLE_E;

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.
Loading…
Cancel
Save