Adjust all spaces to tabs for consistent whitespace formatting on source files. Implement Throw Poison Ability. Added general Color Mod Buff so coloring targets a certain color based on applied buff is simpler. Release Build 10389.

removeExposedPackKey
sigonasr2 7 months ago
parent f27caf4382
commit a5f7973c4d
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 1
      Adventures in Lestoria/Buff.h
  4. 15
      Adventures in Lestoria/BulletTypes.h
  5. 3
      Adventures in Lestoria/Monster.cpp
  6. 36
      Adventures in Lestoria/Player.cpp
  7. 93
      Adventures in Lestoria/PoisonBottle.cpp
  8. 176
      Adventures in Lestoria/Ranger.cpp
  9. 200
      Adventures in Lestoria/Thief.cpp
  10. 138
      Adventures in Lestoria/Trapper.cpp
  11. 2
      Adventures in Lestoria/Version.h
  12. 226
      Adventures in Lestoria/Warrior.cpp
  13. 137
      Adventures in Lestoria/Witch.cpp
  14. 162
      Adventures in Lestoria/Wizard.cpp
  15. 8
      Adventures in Lestoria/assets/config/classes/Witch.txt
  16. 2
      Adventures in Lestoria/assets/config/gfx/gfx.txt
  17. BIN
      Adventures in Lestoria/assets/gamepack.pak
  18. BIN
      Adventures in Lestoria/assets/poison_bottle.png
  19. BIN
      Adventures in Lestoria/assets/poison_pool.png
  20. BIN
      x64/Release/Adventures in Lestoria.exe

@ -985,6 +985,10 @@
</SubType> </SubType>
</ClCompile> </ClCompile>
<ClCompile Include="Pixel.cpp" /> <ClCompile Include="Pixel.cpp" />
<ClCompile Include="PoisonBottle.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="PurpleEnergyBall.cpp"> <ClCompile Include="PurpleEnergyBall.cpp">
<SubType> <SubType>
</SubType> </SubType>

@ -1151,6 +1151,9 @@
<ClCompile Include="PurpleEnergyBall.cpp"> <ClCompile Include="PurpleEnergyBall.cpp">
<Filter>Source Files\Bullet Types</Filter> <Filter>Source Files\Bullet Types</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="PoisonBottle.cpp">
<Filter>Source Files\Bullet Types</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="cpp.hint" /> <None Include="cpp.hint" />

@ -56,6 +56,7 @@ enum BuffType{
ONE_OFF, ONE_OFF,
OVER_TIME_DURING_CAST, OVER_TIME_DURING_CAST,
GLOW_PURPLE, GLOW_PURPLE,
COLOR_MOD,
}; };
enum class BuffRestorationType{ enum class BuffRestorationType{
ONE_OFF, ONE_OFF,

@ -329,4 +329,19 @@ private:
const vf2d initialScale; const vf2d initialScale;
const float homingRadius; const float homingRadius;
std::optional<std::weak_ptr<Monster>>homingTarget; std::optional<std::weak_ptr<Monster>>homingTarget;
};
struct PoisonBottle:public Bullet{
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
void Update(float fElapsedTime)override;
void ModifyOutgoingDamageData(HurtDamageInfo&data);
private:
const vf2d targetPos;
const vf2d startingPos;
const float totalFallTime;
const float originalRisingTime,originalFallingTime;
float risingTime,fallingTime;
const float initialZ;
const float totalRiseZAmt;
const float explodeRadius;
}; };

@ -442,7 +442,8 @@ void Monster::Draw()const{
if(GetBuffs(BuffType::GLOW_PURPLE).size()>0)glowPurpleBuff=GetBuffs(BuffType::GLOW_PURPLE)[0]; if(GetBuffs(BuffType::GLOW_PURPLE).size()>0)glowPurpleBuff=GetBuffs(BuffType::GLOW_PURPLE)[0];
if(GetBuffs(BuffType::SLOWDOWN).size()>0)blendCol=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)))}; if(GetBuffs(BuffType::COLOR_MOD).size()>0)blendCol=Pixel{uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).r*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).g*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration))),uint8_t(PixelRaw(GetBuffs(BuffType::COLOR_MOD)[0].intensity).b*abs(sin(1.4*GetBuffs(BuffType::COLOR_MOD)[0].duration)))};
else if(GetBuffs(BuffType::SLOWDOWN).size()>0)blendCol=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)))};
else if(glowPurpleBuff.has_value())blendCol=Pixel{uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(128+127*abs(sin(1.4*glowPurpleBuff.value().get().duration)))}; else if(glowPurpleBuff.has_value())blendCol=Pixel{uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(255*abs(sin(1.4*glowPurpleBuff.value().get().duration))),uint8_t(128+127*abs(sin(1.4*glowPurpleBuff.value().get().duration)))};
const vf2d hitTimerOffset=vf2d{sin(20*PI*lastHitTimer+randomFrameOffset),0.f}*2.f*GetSizeMult(); const vf2d hitTimerOffset=vf2d{sin(20*PI*lastHitTimer+randomFrameOffset),0.f}*2.f*GetSizeMult();

@ -512,16 +512,16 @@ void Player::Update(float fElapsedTime){
SetAdditiveBlending(!IsUsingAdditiveBlending()); SetAdditiveBlending(!IsUsingAdditiveBlending());
} }
if(deadlyDashAfterDashTimer<=0.f){ if(deadlyDashAfterDashTimer<=0.f){
geom2d::line mouseDir{GetPos(),GetWorldAimingLocation(Player::USE_WALK_DIR)}; geom2d::line mouseDir{GetPos(),GetWorldAimingLocation(Player::USE_WALK_DIR)};
vf2d clampedMousePolarDir{util::pointTo(GetPos(),GetWorldAimingLocation(Player::USE_WALK_DIR)).polar()}; vf2d clampedMousePolarDir{util::pointTo(GetPos(),GetWorldAimingLocation(Player::USE_WALK_DIR)).polar()};
clampedMousePolarDir.x=std::clamp(mouseDir.length(),24.f,"Thief.Ability 2.Range"_F*24.f/100); clampedMousePolarDir.x=std::clamp(mouseDir.length(),24.f,"Thief.Ability 2.Range"_F*24.f/100);
const vf2d originalPos{GetPos()}; const vf2d originalPos{GetPos()};
deadlyDashEndingPos=GetPos()+clampedMousePolarDir.cart(); deadlyDashEndingPos=GetPos()+clampedMousePolarDir.cart();
SoundEffect::PlaySFX("Deadly Dash",GetPos()); SoundEffect::PlaySFX("Deadly Dash",GetPos());
SetPos(MoveUsingPathfinding(deadlyDashEndingPos)); SetPos(MoveUsingPathfinding(deadlyDashEndingPos));
SetAnimationBasedOnTargetingDirection("DEADLYDASH",util::angleTo(originalPos,GetPos())); SetAnimationBasedOnTargetingDirection("DEADLYDASH",util::angleTo(originalPos,GetPos()));
CreateBullet(DeadlyDash)(originalPos,GetPos(),"Thief.Ability 2.Hit Radius"_F,"Thief.Ability 2.Deadly Dash After Images Lingering Time"_F,GetAttack()*"Thief.Ability 2.Damage"_F,"Thief.Ability 2.Knockback Amount"_F,OnUpperLevel(),true,5.f,animation.currentStateName,WHITE)EndBullet; CreateBullet(DeadlyDash)(originalPos,GetPos(),"Thief.Ability 2.Hit Radius"_F,"Thief.Ability 2.Deadly Dash After Images Lingering Time"_F,GetAttack()*"Thief.Ability 2.Damage"_F,"Thief.Ability 2.Knockback Amount"_F,OnUpperLevel(),true,5.f,animation.currentStateName,WHITE)EndBullet;
SetState(State::NORMAL); SetState(State::NORMAL);
SetAdditiveBlending(false); SetAdditiveBlending(false);
game->SetupWorldShake(0.3f); game->SetupWorldShake(0.3f);
@ -536,7 +536,7 @@ void Player::Update(float fElapsedTime){
SetZ(0.f); SetZ(0.f);
break; break;
} }
SetZ((sin((1.f/totalLeapTime)*PI*leapTimer)/2.f+0.5f)*"Witch.Right Click Ability.Leap Max Z"_F); SetZ((sin((1.f/totalLeapTime)*PI*leapTimer)/2.f)*"Witch.Right Click Ability.Leap Max Z"_F);
SetVelocity(vf2d{"Witch.Right Click Ability.Leap Velocity"_F/100.f*24,transformTargetDir}.cart()); SetVelocity(vf2d{"Witch.Right Click Ability.Leap Velocity"_F/100.f*24,transformTargetDir}.cart());
animation.UpdateState(internal_animState,fElapsedTime); animation.UpdateState(internal_animState,fElapsedTime);
}break; }break;
@ -766,11 +766,11 @@ void Player::Update(float fElapsedTime){
#pragma region Thief #pragma region Thief
if(daggerThrowWaitTimer-fElapsedTime<=0.f){ if(daggerThrowWaitTimer-fElapsedTime<=0.f){
daggerThrowWaitTimer=INFINITY; daggerThrowWaitTimer=INFINITY;
float angleToCursor=atan2(GetWorldAimingLocation(Player::USE_WALK_DIR).y-GetPos().y,GetWorldAimingLocation(Player::USE_WALK_DIR).x-GetPos().x); float angleToCursor=atan2(GetWorldAimingLocation(Player::USE_WALK_DIR).y-GetPos().y,GetWorldAimingLocation(Player::USE_WALK_DIR).x-GetPos().x);
const float daggerLifetime{"Thief.Ability 1.Dagger Range"_F/"Thief.Ability 1.Dagger Speed"_F}; const float daggerLifetime{"Thief.Ability 1.Dagger Range"_F/"Thief.Ability 1.Dagger Speed"_F};
CreateBullet(Bullet)(GetPos(),vf2d{"Thief.Ability 1.Dagger Speed"_F,angleToCursor}.cart(),"Thief.Ability 1.Dagger Radius"_F,GetAttack()*"Thief.Ability 1.Damage"_I,"dagger.png",OnUpperLevel(),false,daggerLifetime,true,true,WHITE,{1.f,1.f},0.f,"Dagger Hit")EndBullet; CreateBullet(Bullet)(GetPos(),vf2d{"Thief.Ability 1.Dagger Speed"_F,angleToCursor}.cart(),"Thief.Ability 1.Dagger Radius"_F,GetAttack()*"Thief.Ability 1.Damage"_I,"dagger.png",OnUpperLevel(),false,daggerLifetime,true,true,WHITE,{1.f,1.f},0.f,"Dagger Hit")EndBullet;
} }
daggerThrowWaitTimer-=fElapsedTime; daggerThrowWaitTimer-=fElapsedTime;
#pragma endregion #pragma endregion
@ -1797,10 +1797,10 @@ const bool Player::IsUsingAdditiveBlending()const{
vf2d Player::MoveUsingPathfinding(vf2d targetPos){ vf2d Player::MoveUsingPathfinding(vf2d targetPos){
float pointMouseDirection=atan2(targetPos.y-GetPos().y,targetPos.x-GetPos().x); float pointMouseDirection=atan2(targetPos.y-GetPos().y,targetPos.x-GetPos().x);
vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)}; vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)};
float dist{geom2d::line<float>{GetPos(),targetPos}.length()}; float dist{geom2d::line<float>{GetPos(),targetPos}.length()};
vf2d teleportPoint=GetPos()+pointTowardsMouse*dist; vf2d teleportPoint=GetPos()+pointTowardsMouse*dist;
while(dist>0){ while(dist>0){
vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth; vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,OnUpperLevel()); geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,OnUpperLevel());
collisionRect.pos+=tilePos; collisionRect.pos+=tilePos;
@ -1811,12 +1811,12 @@ vf2d Player::MoveUsingPathfinding(vf2d targetPos){
auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle<float>(teleportPoint,4),collisionRect);}; auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle<float>(teleportPoint,4),collisionRect);};
#pragma endregion #pragma endregion
if(CanPathfindTo(GetPos(),teleportPoint,12) if(CanPathfindTo(GetPos(),teleportPoint,12)
&&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){ &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
return teleportPoint; return teleportPoint;
} }
dist-=4; dist-=4;
teleportPoint=GetPos()+pointTowardsMouse*dist; teleportPoint=GetPos()+pointTowardsMouse*dist;
} }
return GetPos(); return GetPos();
} }
@ -1825,11 +1825,11 @@ const std::unordered_set<std::string>&Player::GetMyClass()const{
} }
void Player::SetupAfterImage(){ void Player::SetupAfterImage(){
game->SetDrawTarget(afterImage.Sprite()); game->SetDrawTarget(afterImage.Sprite());
game->Clear(BLANK); game->Clear(BLANK);
game->DrawPartialSprite({},animation.GetFrame(internal_animState).GetSourceImage()->Sprite(),animation.GetFrame(internal_animState).GetSourceRect().pos,animation.GetFrame(internal_animState).GetSourceRect().size,1U,0U,{255,255,254}); //Off-white so that the sprite is rendered completely in white. game->DrawPartialSprite({},animation.GetFrame(internal_animState).GetSourceImage()->Sprite(),animation.GetFrame(internal_animState).GetSourceRect().pos,animation.GetFrame(internal_animState).GetSourceRect().size,1U,0U,{255,255,254}); //Off-white so that the sprite is rendered completely in white.
game->SetDrawTarget(nullptr); game->SetDrawTarget(nullptr);
afterImage.Decal()->Update(); afterImage.Decal()->Update();
removeLineTimer=TIME_BETWEEN_LINE_REMOVALS; removeLineTimer=TIME_BETWEEN_LINE_REMOVALS;
scanLine=1U; scanLine=1U;
} }

@ -0,0 +1,93 @@
#pragma region License
/*
License (OLC-3)
~~~~~~~~~~~~~~~
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions or derivations of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce the above
copyright notice. This list of conditions and the following disclaimer must be
reproduced in the documentation and/or other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
be used to endorse or promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
SUCH DAMAGE.
Portions of this software are copyright © 2024 The FreeType
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
All rights reserved.
*/
#pragma endregion
#include "BulletTypes.h"
#include "AdventuresInLestoria.h"
#include <ranges>
#include "util.h"
INCLUDE_game
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,"poison_bottle.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f),fallingTime(originalFallingTime){
this->z=z;
}
void PoisonBottle::Update(float fElapsedTime){
if(IsDeactivated())return;
image_angle+=0.5*PI*fElapsedTime;
const bool Landed{fallingTime<=0.f};
if(Landed){
z=0.f;
const float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
game->AddEffect(std::make_unique<Effect>(pos,0.f,"poison_pool.png",game->GetPlayer()->OnUpperLevel(),0.5f,1.2f,vf2d{poisonCircleScale,poisonCircleScale},vf2d{},WHITE,0.f,0.f,false),true);
for(int i:std::ranges::iota_view(0,200)){
float size{util::random_range(0.4f,0.8f)};
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
col.a=util::random_range(60,200);
game->AddEffect(std::make_unique<Effect>(pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col));
}
const HurtList hurtList{game->Hurt(pos,explodeRadius,damage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
for(const auto&[targetPtr,wasHit]:hurtList){
if(wasHit){
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])};
const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]};
const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
}
}
vel={};
fadeOutTime=0.25f;
Deactivate();
}else{
if(risingTime>0.f){
risingTime-=fElapsedTime;
z=util::lerp(initialZ,initialZ+totalRiseZAmt,1-(risingTime/originalRisingTime));
}else{
fallingTime-=fElapsedTime;
z=util::lerp(initialZ+totalRiseZAmt,0.f,1-(fallingTime/originalFallingTime));
}
}
}
void PoisonBottle::ModifyOutgoingDamageData(HurtDamageInfo&data){
if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY;
}

@ -50,15 +50,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Ranger::Initialize(){ void Ranger::Initialize(){
READFROMCONFIG(Ranger,RANGER); READFROMCONFIG(Ranger,RANGER);
Ranger::idle_n="RANGER_IDLE_N"; Ranger::idle_n="RANGER_IDLE_N";
Ranger::idle_e="RANGER_IDLE_E"; Ranger::idle_e="RANGER_IDLE_E";
Ranger::idle_s="RANGER_IDLE_S"; Ranger::idle_s="RANGER_IDLE_S";
Ranger::idle_w="RANGER_IDLE_W"; Ranger::idle_w="RANGER_IDLE_W";
Ranger::walk_n="RANGER_WALK_N"; Ranger::walk_n="RANGER_WALK_N";
Ranger::walk_e="RANGER_WALK_E"; Ranger::walk_e="RANGER_WALK_E";
Ranger::walk_s="RANGER_WALK_S"; Ranger::walk_s="RANGER_WALK_S";
Ranger::walk_w="RANGER_WALK_W"; Ranger::walk_w="RANGER_WALK_W";
} }
SETUP_CLASS(Ranger) SETUP_CLASS(Ranger)
@ -67,86 +67,86 @@ void Ranger::OnUpdate(float fElapsedTime){
} }
bool Ranger::AutoAttack(){ bool Ranger::AutoAttack(){
geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation()); geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
vf2d extendedLine=pointTowardsCursor.upoint(1.1f); vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x); float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet; CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet;
BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile(); BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
SetState(State::SHOOT_ARROW); SetState(State::SHOOT_ARROW);
SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor); SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
return true; return true;
} }
void Ranger::InitializeClassAbilities(){ void Ranger::InitializeClassAbilities(){
#pragma region Ranger Right-click Ability (Retreat) #pragma region Ranger Right-click Ability (Retreat)
Ranger::rightClickAbility.action= Ranger::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()}; geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()};
float velocity=(0.5f*-p->friction*p->RETREAT_TIME*p->RETREAT_TIME-p->RETREAT_DISTANCE)/-p->RETREAT_TIME; //Derived from kinetic motion formula. float velocity=(0.5f*-p->friction*p->RETREAT_TIME*p->RETREAT_TIME-p->RETREAT_DISTANCE)/-p->RETREAT_TIME; //Derived from kinetic motion formula.
p->SetVelocity(mouseDir.vector().norm()*velocity); p->SetVelocity(mouseDir.vector().norm()*velocity);
p->retreatTimer=p->RETREAT_TIME; p->retreatTimer=p->RETREAT_TIME;
p->ApplyIframes(p->RETREAT_TIME); p->ApplyIframes(p->RETREAT_TIME);
p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()}); p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
p->ghostFrameTimer=p->RETREAT_GHOST_FRAME_DELAY; p->ghostFrameTimer=p->RETREAT_GHOST_FRAME_DELAY;
p->ghostRemoveTimer=p->RETREAT_GHOST_FRAMES*p->RETREAT_GHOST_FRAME_DELAY; p->ghostRemoveTimer=p->RETREAT_GHOST_FRAMES*p->RETREAT_GHOST_FRAME_DELAY;
float angleToCursor=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR).x-p->GetPos().x); float angleToCursor=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR).x-p->GetPos().x);
p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor); p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor);
p->SetState(State::RETREAT); p->SetState(State::RETREAT);
SoundEffect::PlaySFX("Ranger.Right Click Ability.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Right Click Ability.Sound"_S,SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Ranger Ability 1 (Rapid Fire) #pragma region Ranger Ability 1 (Rapid Fire)
Ranger::ability1.action= Ranger::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
p->remainingRapidFireShots=p->RAPID_FIRE_SHOOT_AMOUNT; p->remainingRapidFireShots=p->RAPID_FIRE_SHOOT_AMOUNT;
p->rapidFireTimer=p->RAPID_FIRE_SHOOT_DELAY; p->rapidFireTimer=p->RAPID_FIRE_SHOOT_DELAY;
if("Ranger.Ability 1.IsAnimationLocked"_I){ if("Ranger.Ability 1.IsAnimationLocked"_I){
p->SetState(State::ANIMATION_LOCK); p->SetState(State::ANIMATION_LOCK);
} }
SoundEffect::PlaySFX("Ranger.Ability 1.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Ability 1.Sound"_S,SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Ranger Ability 2 (Charged Shot) #pragma region Ranger Ability 2 (Charged Shot)
Ranger::ability2.action= Ranger::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
vf2d arrowVelocity=util::pointTo(p->GetPos(),p->GetWorldAimingLocation()); vf2d arrowVelocity=util::pointTo(p->GetPos(),p->GetWorldAimingLocation());
BULLET_LIST.push_back(std::make_unique<ChargedArrow>(p->GetPos(),arrowVelocity*"Ranger.Ability 2.Speed"_F,12*"Ranger.Ability 2.Radius"_F/100,p->GetAttack()*"Ranger.Ability 2.DamageMult"_F,p->OnUpperLevel(),true)); BULLET_LIST.push_back(std::make_unique<ChargedArrow>(p->GetPos(),arrowVelocity*"Ranger.Ability 2.Speed"_F,12*"Ranger.Ability 2.Radius"_F/100,p->GetAttack()*"Ranger.Ability 2.DamageMult"_F,p->OnUpperLevel(),true));
p->SetState(State::SHOOT_ARROW); p->SetState(State::SHOOT_ARROW);
p->rangerShootAnimationTimer=0.3f; p->rangerShootAnimationTimer=0.3f;
p->SetAnimationBasedOnTargetingDirection("SHOOT",atan2(arrowVelocity.y,arrowVelocity.x)); p->SetAnimationBasedOnTargetingDirection("SHOOT",atan2(arrowVelocity.y,arrowVelocity.x));
game->SetupWorldShake("Ranger.Ability 2.WorldShakeTime"_F); game->SetupWorldShake("Ranger.Ability 2.WorldShakeTime"_F);
p->Knockback(-1.f*arrowVelocity.norm()*"Ranger.Ability 2.Knockback"_F); p->Knockback(-1.f*arrowVelocity.norm()*"Ranger.Ability 2.Knockback"_F);
SoundEffect::PlaySFX("Ranger.Ability 2.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Ability 2.Sound"_S,SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Ranger Ability 3 (Multi Shot) #pragma region Ranger Ability 3 (Multi Shot)
Ranger::ability3.action= Ranger::ability3.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetWorldAimingLocation()); geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetWorldAimingLocation());
float shootingDist=pointTowardsCursor.length(); float shootingDist=pointTowardsCursor.length();
vf2d shootingDirMiddle=pointTowardsCursor.vector(); vf2d shootingDirMiddle=pointTowardsCursor.vector();
float shootingAngle=atan2(shootingDirMiddle.y,shootingDirMiddle.x); float shootingAngle=atan2(shootingDirMiddle.y,shootingDirMiddle.x);
int arrowCount="Ranger.Ability 3.ArrowCount"_I%2==0?"Ranger.Ability 3.ArrowCount"_I+1:"Ranger.Ability 3.ArrowCount"_I; int arrowCount="Ranger.Ability 3.ArrowCount"_I%2==0?"Ranger.Ability 3.ArrowCount"_I+1:"Ranger.Ability 3.ArrowCount"_I;
for(int i=0;i<arrowCount;i++){ for(int i=0;i<arrowCount;i++){
if("Ranger.Ability 3.ArrowCount"_I%2==0&&i=="Ranger.Ability 3.ArrowCount"_I/2)continue; if("Ranger.Ability 3.ArrowCount"_I%2==0&&i=="Ranger.Ability 3.ArrowCount"_I/2)continue;
const float halfAngle="Ranger.Ability 3.MultiShotSpread"_F*PI/180; const float halfAngle="Ranger.Ability 3.MultiShotSpread"_F*PI/180;
const float leftAngle=-halfAngle; const float leftAngle=-halfAngle;
const float increment=halfAngle/"Ranger.Ability 3.ArrowCount"_I; const float increment=halfAngle/"Ranger.Ability 3.ArrowCount"_I;
const float newAngle=shootingAngle+leftAngle/2+i*increment; const float newAngle=shootingAngle+leftAngle/2+i*increment;
geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist); geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist);
vf2d extendedLine=pointTowardsCursor.upoint(1.1f); vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
BULLET_LIST.push_back(std::make_unique<Arrow>(Arrow(p->GetPos(),extendedLine,vf2d{cos(newAngle)*"Ranger.Ability 3.ArrowSpd"_F,float(sin(newAngle)*"Ranger.Ability 3.ArrowSpd"_F-PI/8*"Ranger.Ability 3.ArrowSpd"_F)}+p->movementVelocity,12*"Ranger.Ability 3.ArrowRadius"_F/100,int(p->GetAttack()*"Ranger.Ability 3.DamageMult"_F),p->OnUpperLevel(),true))); BULLET_LIST.push_back(std::make_unique<Arrow>(Arrow(p->GetPos(),extendedLine,vf2d{cos(newAngle)*"Ranger.Ability 3.ArrowSpd"_F,float(sin(newAngle)*"Ranger.Ability 3.ArrowSpd"_F-PI/8*"Ranger.Ability 3.ArrowSpd"_F)}+p->movementVelocity,12*"Ranger.Ability 3.ArrowRadius"_F/100,int(p->GetAttack()*"Ranger.Ability 3.DamageMult"_F),p->OnUpperLevel(),true)));
} }
p->rangerShootAnimationTimer=0.3f; p->rangerShootAnimationTimer=0.3f;
p->SetState(State::SHOOT_ARROW); p->SetState(State::SHOOT_ARROW);
p->SetAnimationBasedOnTargetingDirection("SHOOT",shootingAngle); p->SetAnimationBasedOnTargetingDirection("SHOOT",shootingAngle);
SoundEffect::PlaySFX("Ranger.Ability 3.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Ability 3.Sound"_S,SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
} }

@ -51,115 +51,115 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Thief::Initialize(){ void Thief::Initialize(){
READFROMCONFIG(Thief,THIEF); READFROMCONFIG(Thief,THIEF);
Thief::idle_n="THIEF_IDLE_N"; Thief::idle_n="THIEF_IDLE_N";
Thief::idle_e="THIEF_IDLE_E"; Thief::idle_e="THIEF_IDLE_E";
Thief::idle_s="THIEF_IDLE_S"; Thief::idle_s="THIEF_IDLE_S";
Thief::idle_w="THIEF_IDLE_W"; Thief::idle_w="THIEF_IDLE_W";
Thief::walk_n="THIEF_WALK_N"; Thief::walk_n="THIEF_WALK_N";
Thief::walk_e="THIEF_WALK_E"; Thief::walk_e="THIEF_WALK_E";
Thief::walk_s="THIEF_WALK_S"; Thief::walk_s="THIEF_WALK_S";
Thief::walk_w="THIEF_WALK_W"; Thief::walk_w="THIEF_WALK_W";
} }
SETUP_CLASS(Thief) SETUP_CLASS(Thief)
void Thief::OnUpdate(float fElapsedTime){ void Thief::OnUpdate(float fElapsedTime){
} }
bool Thief::AutoAttack(){ bool Thief::AutoAttack(){
bool attack=false; bool attack=false;
Monster*closest=nullptr; Monster*closest=nullptr;
float closest_dist=999999; float closest_dist=999999;
for(std::shared_ptr<Monster>&m:MONSTER_LIST){ for(std::shared_ptr<Monster>&m:MONSTER_LIST){
if(m->IsAlive()&& if(m->IsAlive()&&
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&& geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){ geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length(); closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
closest=&*m; closest=&*m;
} }
} }
float targetDirection; float targetDirection;
if(closest!=nullptr){ if(closest!=nullptr){
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y; float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
targetDirection=dirToEnemy; targetDirection=dirToEnemy;
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy); SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
}else{ }else{
float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y; float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y;
targetDirection=dirToMouse; targetDirection=dirToMouse;
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse); SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
} }
attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
swordSwingTimer="Thief.Auto Attack.SwordAnimationSwingTime"_F; swordSwingTimer="Thief.Auto Attack.SwordAnimationSwingTime"_F;
game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Thief.Auto Attack.DamageMult"_F,"Thief.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Thief.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection)); game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Thief.Auto Attack.DamageMult"_F,"Thief.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Thief.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
SetState(State::SWING_SWORD); SetState(State::SWING_SWORD);
SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED); SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
return true; return true;
} }
void Thief::InitializeClassAbilities(){ void Thief::InitializeClassAbilities(){
#pragma region Thief Right-click Ability (Roll) #pragma region Thief Right-click Ability (Roll)
Thief::rightClickAbility.action= Thief::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
p->SetState(State::ROLL); p->SetState(State::ROLL);
p->rolling_timer="Thief.Right Click Ability.Roll Time"_F; p->rolling_timer="Thief.Right Click Ability.Roll Time"_F;
p->AddBuff(BuffType::SPEEDBOOST,"Thief.Right Click Ability.Movespeed Buff"_f[1],"Thief.Right Click Ability.Movespeed Buff"_f[0]/100.f); p->AddBuff(BuffType::SPEEDBOOST,"Thief.Right Click Ability.Movespeed Buff"_f[1],"Thief.Right Click Ability.Movespeed Buff"_f[0]/100.f);
geom2d::line mouseDir{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}; geom2d::line mouseDir{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)};
float velocity=(0.5f*-p->friction*"Thief.Right Click Ability.Roll Time"_F*"Thief.Right Click Ability.Roll Time"_F-std::clamp(mouseDir.length(),24.f,24.f*"Thief.Right Click Ability.Max Roll Range"_F/100))/-"Thief.Right Click Ability.Roll Time"_F; //Derived from kinetic motion formula. float velocity=(0.5f*-p->friction*"Thief.Right Click Ability.Roll Time"_F*"Thief.Right Click Ability.Roll Time"_F-std::clamp(mouseDir.length(),24.f,24.f*"Thief.Right Click Ability.Max Roll Range"_F/100))/-"Thief.Right Click Ability.Roll Time"_F; //Derived from kinetic motion formula.
p->SetVelocity(mouseDir.vector().norm()*velocity); p->SetVelocity(mouseDir.vector().norm()*velocity);
p->ApplyIframes("Thief.Right Click Ability.Iframe Time"_F); p->ApplyIframes("Thief.Right Click Ability.Iframe Time"_F);
p->footstepTimer=0.f; p->footstepTimer=0.f;
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Thief Ability 1 (Hidden Dagger) #pragma region Thief Ability 1 (Hidden Dagger)
Thief::ability1.action= Thief::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()}; geom2d::line mouseDir{p->GetWorldAimingLocation(Player::USE_WALK_DIR),p->GetPos()};
float velocity=(0.5f*-p->friction*"Thief.Ability 1.RetreatTime"_F*"Thief.Ability 1.RetreatTime"_F-24.f*"Thief.Ability 1.RetreatDistance"_F/100)/-"Thief.Ability 1.RetreatTime"_F; //Derived from kinetic motion formula. float velocity=(0.5f*-p->friction*"Thief.Ability 1.RetreatTime"_F*"Thief.Ability 1.RetreatTime"_F-24.f*"Thief.Ability 1.RetreatDistance"_F/100)/-"Thief.Ability 1.RetreatTime"_F; //Derived from kinetic motion formula.
p->SetVelocity(mouseDir.vector().norm()*velocity); p->SetVelocity(mouseDir.vector().norm()*velocity);
p->retreatTimer="Thief.Ability 1.RetreatTime"_F; p->retreatTimer="Thief.Ability 1.RetreatTime"_F;
p->ApplyIframes("Thief.Ability 1.RetreatTime"_F); p->ApplyIframes("Thief.Ability 1.RetreatTime"_F);
p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()}); p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
p->ghostFrameTimer=p->RETREAT_GHOST_FRAME_DELAY; p->ghostFrameTimer=p->RETREAT_GHOST_FRAME_DELAY;
p->ghostRemoveTimer=p->RETREAT_GHOST_FRAMES*p->RETREAT_GHOST_FRAME_DELAY; p->ghostRemoveTimer=p->RETREAT_GHOST_FRAMES*p->RETREAT_GHOST_FRAME_DELAY;
p->daggerThrowWaitTimer="Thief.Ability 1.Dagger Throw Delay"_F; p->daggerThrowWaitTimer="Thief.Ability 1.Dagger Throw Delay"_F;
float angleToCursor=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR).x-p->GetPos().x); float angleToCursor=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR).x-p->GetPos().x);
p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor); p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor);
p->SetState(State::RETREAT); p->SetState(State::RETREAT);
SoundEffect::PlaySFX("Thief.Ability 1.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Thief.Ability 1.Sound"_S,SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Thief Ability 2 (Deadly Dash) #pragma region Thief Ability 2 (Deadly Dash)
Thief::ability2.action= Thief::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
game->AddEffect(std::make_unique<ShineEffect>(p->GetPos()+vf2d{4.f,4.f},0.5f,0.5f,"shine.png",1.5f,vf2d{},WHITE,util::random(2*PI),PI/2,true)); game->AddEffect(std::make_unique<ShineEffect>(p->GetPos()+vf2d{4.f,4.f},0.5f,0.5f,"shine.png",1.5f,vf2d{},WHITE,util::random(2*PI),PI/2,true));
p->ApplyIframes("Thief.Ability 2.Initial Wait"_F+"Thief.Ability 2.Ending Wait"_F+"Thief.Ability 2.Completed Dash Extra Iframe Time"_F); p->ApplyIframes("Thief.Ability 2.Initial Wait"_F+"Thief.Ability 2.Ending Wait"_F+"Thief.Ability 2.Completed Dash Extra Iframe Time"_F);
SoundEffect::PlaySFX("Charge Up",p->GetPos()); SoundEffect::PlaySFX("Charge Up",p->GetPos());
p->SetState(State::DEADLYDASH); p->SetState(State::DEADLYDASH);
p->deadlyDashWaitTimer="Thief.Ability 2.Initial Wait"_F; p->deadlyDashWaitTimer="Thief.Ability 2.Initial Wait"_F;
p->deadlyDashAfterDashTimer=p->deadlyDashWaitTimer+"Thief.Ability 2.Ending Wait"_F; p->deadlyDashAfterDashTimer=p->deadlyDashWaitTimer+"Thief.Ability 2.Ending Wait"_F;
game->SetWorldZoom(1.1f); game->SetWorldZoom(1.1f);
SoundEffect::PlaySFX("Ranger Retreat",SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger Retreat",SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Thief Ability 3 (Adrenaline Rush) #pragma region Thief Ability 3 (Adrenaline Rush)
Thief::ability3.action= Thief::ability3.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
SoundEffect::PlaySFX("Adrenaline Rush",SoundEffect::CENTERED); SoundEffect::PlaySFX("Adrenaline Rush",SoundEffect::CENTERED);
p->AddBuff(BuffType::ADRENALINE_RUSH,"Thief.Ability 3.Duration"_F,0.f); p->AddBuff(BuffType::ADRENALINE_RUSH,"Thief.Ability 3.Duration"_F,0.f);
for(int i:std::ranges::iota_view(0,50)){ for(int i:std::ranges::iota_view(0,50)){
float size{util::random_range(0.4f,0.8f)}; float size{util::random_range(0.4f,0.8f)};
game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(WHITE,GREEN,util::random(1)))); game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(WHITE,GREEN,util::random(1))));
} }
return true; return true;
}; };
#pragma endregion #pragma endregion
} }

@ -51,15 +51,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Trapper::Initialize(){ void Trapper::Initialize(){
READFROMCONFIG(Trapper,TRAPPER); READFROMCONFIG(Trapper,TRAPPER);
Trapper::idle_n="TRAPPER_IDLE_N"; Trapper::idle_n="TRAPPER_IDLE_N";
Trapper::idle_e="TRAPPER_IDLE_E"; Trapper::idle_e="TRAPPER_IDLE_E";
Trapper::idle_s="TRAPPER_IDLE_S"; Trapper::idle_s="TRAPPER_IDLE_S";
Trapper::idle_w="TRAPPER_IDLE_W"; Trapper::idle_w="TRAPPER_IDLE_W";
Trapper::walk_n="TRAPPER_WALK_N"; Trapper::walk_n="TRAPPER_WALK_N";
Trapper::walk_e="TRAPPER_WALK_E"; Trapper::walk_e="TRAPPER_WALK_E";
Trapper::walk_s="TRAPPER_WALK_S"; Trapper::walk_s="TRAPPER_WALK_S";
Trapper::walk_w="TRAPPER_WALK_W"; Trapper::walk_w="TRAPPER_WALK_W";
} }
SETUP_CLASS(Trapper) SETUP_CLASS(Trapper)
@ -69,66 +69,66 @@ void Trapper::OnUpdate(float fElapsedTime){
} }
bool Trapper::AutoAttack(){ bool Trapper::AutoAttack(){
geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation()); geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
vf2d extendedLine=pointTowardsCursor.upoint(1.1f); vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x); float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=ARROW_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet; CreateBullet(Arrow)(GetPos(),extendedLine,vf2d{cos(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F,float(sin(angleToCursor)*"Ranger.Auto Attack.ArrowSpd"_F-PI/8*"Ranger.Auto Attack.ArrowSpd"_F)}+movementVelocity/1.5f,"Ranger.Auto Attack.Radius"_F,int(GetAttack()*"Ranger.Auto Attack.DamageMult"_F),OnUpperLevel(),true)EndBullet;
BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile(); BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
SetState(State::SHOOT_ARROW); SetState(State::SHOOT_ARROW);
SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor); SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED); SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
return true; return true;
} }
void Trapper::InitializeClassAbilities(){ void Trapper::InitializeClassAbilities(){
#pragma region Trapper Right-click Ability (Sprint) #pragma region Trapper Right-click Ability (Sprint)
Trapper::rightClickAbility.action= Trapper::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
SoundEffect::PlaySFX("Sprint",SoundEffect::CENTERED); SoundEffect::PlaySFX("Sprint",SoundEffect::CENTERED);
p->AddBuff(BuffType::SPEEDBOOST,"Trapper.Right Click Ability.Movement Speed Buff"_f[1],"Trapper.Right Click Ability.Movement Speed Buff"_f[0]/100.f); p->AddBuff(BuffType::SPEEDBOOST,"Trapper.Right Click Ability.Movement Speed Buff"_f[1],"Trapper.Right Click Ability.Movement Speed Buff"_f[0]/100.f);
for(int i:std::ranges::iota_view(0,50)){ for(int i:std::ranges::iota_view(0,50)){
float size{util::random_range(0.4f,0.8f)}; float size{util::random_range(0.4f,0.8f)};
game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(BLACK,RED,util::random(1)))); game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(BLACK,RED,util::random(1))));
} }
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Trapper Ability 1 (Mark Target) #pragma region Trapper Ability 1 (Mark Target)
Trapper::ability1.action= Trapper::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
std::optional<std::weak_ptr<Monster>>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())}; std::optional<std::weak_ptr<Monster>>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())};
vf2d targetPos{pos}; vf2d targetPos{pos};
if(nearestMonster.has_value()){ if(nearestMonster.has_value()){
targetPos=nearestMonster.value().lock()->GetPos(); targetPos=nearestMonster.value().lock()->GetPos();
nearestMonster.value().lock()->ApplyMark("Trapper.Ability 1.Duration"_F,"Trapper.Ability 1.Stack Count"_I); nearestMonster.value().lock()->ApplyMark("Trapper.Ability 1.Duration"_F,"Trapper.Ability 1.Stack Count"_I);
for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/16))){ for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/16))){
float drawDist{i*16.f}; float drawDist{i*16.f};
float fadeInTime{i*0.05f}; float fadeInTime{i*0.05f};
float fadeOutTime{0.5f+i*0.05f}; float fadeOutTime{0.5f+i*0.05f};
float effectSize{util::random(0.4f)}; float effectSize{util::random(0.4f)};
game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true); game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true);
} }
return true; return true;
} }
return false; return false;
}; };
#pragma endregion #pragma endregion
#pragma region Trapper Ability 2 (Bear Trap) #pragma region Trapper Ability 2 (Bear Trap)
Trapper::ability2.action= Trapper::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
CreateBullet(BearTrap)(p->GetPos(),"Trapper.Ability 2.Trap Radius"_I,"Trapper.Ability 2.DamageMult"_F*p->GetAttack(),0.2f,0.5f,p->OnUpperLevel(),false,INFINITE,true,WHITE,{1.f,1.f})EndBullet; CreateBullet(BearTrap)(p->GetPos(),"Trapper.Ability 2.Trap Radius"_I,"Trapper.Ability 2.DamageMult"_F*p->GetAttack(),0.2f,0.5f,p->OnUpperLevel(),false,INFINITE,true,WHITE,{1.f,1.f})EndBullet;
SoundEffect::PlaySFX("Place Down Trap",p->GetPos()); SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection()); p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Trapper Ability 3 (Explosive Trap) #pragma region Trapper Ability 3 (Explosive Trap)
Trapper::ability3.action= Trapper::ability3.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
CreateBullet(ExplosiveTrap)(p->GetPos(),"Trapper.Ability 3.Trap Radius"_I,"Trapper.Ability 3.Explosion Radius"_F/100.f*24,"Trapper.Ability 3.Trap Auto Detonate Time"_F,"Trapper.Ability 3.DamageMult"_F*p->GetAttack(),0.2f,0.5f,"Trapper.Ability 3.Trap Activation Time"_F,p->OnUpperLevel(),false,INFINITE,true,WHITE,{1.f,1.f})EndBullet; CreateBullet(ExplosiveTrap)(p->GetPos(),"Trapper.Ability 3.Trap Radius"_I,"Trapper.Ability 3.Explosion Radius"_F/100.f*24,"Trapper.Ability 3.Trap Auto Detonate Time"_F,"Trapper.Ability 3.DamageMult"_F*p->GetAttack(),0.2f,0.5f,"Trapper.Ability 3.Trap Activation Time"_F,p->OnUpperLevel(),false,INFINITE,true,WHITE,{1.f,1.f})EndBullet;
SoundEffect::PlaySFX("Place Down Trap",p->GetPos()); SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection()); p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
return true; return true;
}; };
#pragma endregion #pragma endregion
} }

@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1 #define VERSION_MAJOR 1
#define VERSION_MINOR 2 #define VERSION_MINOR 2
#define VERSION_PATCH 3 #define VERSION_PATCH 3
#define VERSION_BUILD 10363 #define VERSION_BUILD 10389
#define stringify(a) stringify_(a) #define stringify(a) stringify_(a)
#define stringify_(a) #a #define stringify_(a) #a

@ -48,15 +48,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Warrior::Initialize(){ void Warrior::Initialize(){
READFROMCONFIG(Warrior,WARRIOR); READFROMCONFIG(Warrior,WARRIOR);
Warrior::idle_n="WARRIOR_IDLE_N"; Warrior::idle_n="WARRIOR_IDLE_N";
Warrior::idle_e="WARRIOR_IDLE_E"; Warrior::idle_e="WARRIOR_IDLE_E";
Warrior::idle_s="WARRIOR_IDLE_S"; Warrior::idle_s="WARRIOR_IDLE_S";
Warrior::idle_w="WARRIOR_IDLE_W"; Warrior::idle_w="WARRIOR_IDLE_W";
Warrior::walk_n="WARRIOR_WALK_N"; Warrior::walk_n="WARRIOR_WALK_N";
Warrior::walk_e="WARRIOR_WALK_E"; Warrior::walk_e="WARRIOR_WALK_E";
Warrior::walk_s="WARRIOR_WALK_S"; Warrior::walk_s="WARRIOR_WALK_S";
Warrior::walk_w="WARRIOR_WALK_W"; Warrior::walk_w="WARRIOR_WALK_W";
} }
SETUP_CLASS(Warrior) SETUP_CLASS(Warrior)
@ -66,110 +66,110 @@ void Warrior::OnUpdate(float fElapsedTime){
} }
bool Warrior::AutoAttack(){ bool Warrior::AutoAttack(){
if(GetState()!=State::SPIN){ if(GetState()!=State::SPIN){
bool attack=false; bool attack=false;
Monster*closest=nullptr; Monster*closest=nullptr;
float closest_dist=999999; float closest_dist=999999;
for(std::shared_ptr<Monster>&m:MONSTER_LIST){ for(std::shared_ptr<Monster>&m:MONSTER_LIST){
if(m->IsAlive()&& if(m->IsAlive()&&
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&& geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){ geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length(); closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
closest=&*m; closest=&*m;
} }
} }
float targetDirection; float targetDirection;
if(closest!=nullptr){ if(closest!=nullptr){
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y; float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
targetDirection=dirToEnemy; targetDirection=dirToEnemy;
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy); SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
}else{ }else{
float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y; float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y;
targetDirection=dirToMouse; targetDirection=dirToMouse;
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse); SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
} }
attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
swordSwingTimer="Warrior.Auto Attack.SwordAnimationSwingTime"_F; swordSwingTimer="Warrior.Auto Attack.SwordAnimationSwingTime"_F;
game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Warrior.Auto Attack.DamageMult"_F,"Warrior.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Warrior.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection)); game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Warrior.Auto Attack.DamageMult"_F,"Warrior.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Warrior.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
SetState(State::SWING_SWORD); SetState(State::SWING_SWORD);
SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED); SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
} }
return true; return true;
} }
void Warrior::InitializeClassAbilities(){ void Warrior::InitializeClassAbilities(){
#pragma region Warrior Right-click Ability (Block) #pragma region Warrior Right-click Ability (Block)
Warrior::rightClickAbility.action= Warrior::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
if(p->GetState()==State::NORMAL||p->GetState()==State::CASTING){ if(p->GetState()==State::NORMAL||p->GetState()==State::CASTING){
rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME; rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME;
p->SetState(State::BLOCK); p->SetState(State::BLOCK);
p->blockTimer="Warrior.Right Click Ability.Duration"_F; p->blockTimer="Warrior.Right Click Ability.Duration"_F;
p->AddBuff(BuffType::BLOCK_SLOWDOWN,"Warrior.Right Click Ability.Duration"_F,"Warrior.Right Click Ability.SlowAmt"_F); p->AddBuff(BuffType::BLOCK_SLOWDOWN,"Warrior.Right Click Ability.Duration"_F,"Warrior.Right Click Ability.SlowAmt"_F);
return true; return true;
} }
return false; return false;
}; };
#pragma endregion #pragma endregion
#pragma region Warrior Ability 1 (Battlecry) #pragma region Warrior Ability 1 (Battlecry)
Warrior::ability1.action= Warrior::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
game->AddEffect(std::make_unique<Effect>(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F)); game->AddEffect(std::make_unique<Effect>(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F));
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"}); p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F); p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
for(std::shared_ptr<Monster>&m:MONSTER_LIST){ for(std::shared_ptr<Monster>&m:MONSTER_LIST){
if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle<float>(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))){ if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle<float>(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))){
m->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F); m->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F);
} }
} }
SoundEffect::PlaySFX("Warrior Battlecry",SoundEffect::CENTERED); SoundEffect::PlaySFX("Warrior Battlecry",SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Warrior Ability 2 (Ground Slam) #pragma region Warrior Ability 2 (Ground Slam)
Warrior::ability2.action= Warrior::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
p->Spin(GROUND_SLAM_SPIN_TIME,"Warrior.Ability 2.SpinSpd"_F*PI); p->Spin(GROUND_SLAM_SPIN_TIME,"Warrior.Ability 2.SpinSpd"_F*PI);
p->iframe_time="Warrior.Ability 2.IframeTime"_F; p->iframe_time="Warrior.Ability 2.IframeTime"_F;
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Warrior Ability 3 (Sonic Slash) #pragma region Warrior Ability 3 (Sonic Slash)
Warrior::ability3.action= Warrior::ability3.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
p->SetState(State::SWING_SONIC_SWORD); p->SetState(State::SWING_SONIC_SWORD);
p->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 3.StuckTime"_F,1); p->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 3.StuckTime"_F,1);
vf2d bulletVel={}; vf2d bulletVel={};
switch(p->GetFacingDirection()){ switch(p->GetFacingDirection()){
case UP:{ case UP:{
p->vel.y="Warrior.Ability 3.AbilityPushback"_F; p->vel.y="Warrior.Ability 3.AbilityPushback"_F;
bulletVel.y=-"Warrior.Ability 3.BulletSpd"_F; bulletVel.y=-"Warrior.Ability 3.BulletSpd"_F;
p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_N",WARRIOR); p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_N",WARRIOR);
}break; }break;
case LEFT:{ case LEFT:{
p->vel.x="Warrior.Ability 3.AbilityPushback"_F; p->vel.x="Warrior.Ability 3.AbilityPushback"_F;
bulletVel.x=-"Warrior.Ability 3.BulletSpd"_F; bulletVel.x=-"Warrior.Ability 3.BulletSpd"_F;
p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_W",WARRIOR); p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_W",WARRIOR);
}break; }break;
case RIGHT:{ case RIGHT:{
p->vel.x=-"Warrior.Ability 3.AbilityPushback"_F; p->vel.x=-"Warrior.Ability 3.AbilityPushback"_F;
bulletVel.x="Warrior.Ability 3.BulletSpd"_F; bulletVel.x="Warrior.Ability 3.BulletSpd"_F;
p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_E",WARRIOR); p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_E",WARRIOR);
}break; }break;
case DOWN:{ case DOWN:{
p->vel.y=-"Warrior.Ability 3.AbilityPushback"_F; p->vel.y=-"Warrior.Ability 3.AbilityPushback"_F;
bulletVel.y="Warrior.Ability 3.BulletSpd"_F; bulletVel.y="Warrior.Ability 3.BulletSpd"_F;
p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_S",WARRIOR); p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_S",WARRIOR);
}break; }break;
} }
BULLET_LIST.push_back(std::make_unique<Bullet>(p->GetPos(),bulletVel,"Warrior.Ability 3.Radius"_F,p->GetAttack()*"Warrior.Ability 3.DamageMult"_F,"sonicslash.png",p->upperLevel,true,"Warrior.Ability 3.Lifetime"_F,true,true,WHITE,vf2d{"Warrior.Ability 3.Radius"_F/30,"Warrior.Ability 3.Radius"_F/30})); BULLET_LIST.push_back(std::make_unique<Bullet>(p->GetPos(),bulletVel,"Warrior.Ability 3.Radius"_F,p->GetAttack()*"Warrior.Ability 3.DamageMult"_F,"sonicslash.png",p->upperLevel,true,"Warrior.Ability 3.Lifetime"_F,true,true,WHITE,vf2d{"Warrior.Ability 3.Radius"_F/30,"Warrior.Ability 3.Radius"_F/30}));
game->SetupWorldShake("Warrior.Ability 3.ShakeTime"_F); game->SetupWorldShake("Warrior.Ability 3.ShakeTime"_F);
SoundEffect::PlaySFX("Warrior Sonic Slash",SoundEffect::CENTERED); SoundEffect::PlaySFX("Warrior Sonic Slash",SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
} }

@ -51,21 +51,21 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Witch::Initialize(){ void Witch::Initialize(){
READFROMCONFIG(Witch,WITCH); READFROMCONFIG(Witch,WITCH);
Witch::idle_n="WITCH_IDLE_N"; Witch::idle_n="WITCH_IDLE_N";
Witch::idle_e="WITCH_IDLE_E"; Witch::idle_e="WITCH_IDLE_E";
Witch::idle_s="WITCH_IDLE_S"; Witch::idle_s="WITCH_IDLE_S";
Witch::idle_w="WITCH_IDLE_W"; Witch::idle_w="WITCH_IDLE_W";
Witch::walk_n="WITCH_WALK_N"; Witch::walk_n="WITCH_WALK_N";
Witch::walk_e="WITCH_WALK_E"; Witch::walk_e="WITCH_WALK_E";
Witch::walk_s="WITCH_WALK_S"; Witch::walk_s="WITCH_WALK_S";
Witch::walk_w="WITCH_WALK_W"; Witch::walk_w="WITCH_WALK_W";
} }
SETUP_CLASS(Witch) SETUP_CLASS(Witch)
void Witch::OnUpdate(float fElapsedTime){ void Witch::OnUpdate(float fElapsedTime){
if(attack_cooldown_timer>0){ if(attack_cooldown_timer>0){
idle_n="WITCH_IDLE_ATTACK_N"; idle_n="WITCH_IDLE_ATTACK_N";
idle_e="WITCH_IDLE_ATTACK_E"; idle_e="WITCH_IDLE_ATTACK_E";
idle_s="WITCH_IDLE_ATTACK_S"; idle_s="WITCH_IDLE_ATTACK_S";
@ -106,64 +106,67 @@ bool Witch::AutoAttack(){
attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
float angleToCursor=atan2(GetWorldAimingLocation().y-GetPos().y,GetWorldAimingLocation().x-GetPos().x); float angleToCursor=atan2(GetWorldAimingLocation().y-GetPos().y,GetWorldAimingLocation().x-GetPos().x);
CreateBullet(PurpleEnergyBall)(GetPos(),"Witch.Auto Attack.Radius"_F/100*12,"Witch.Auto Attack.Homing Range"_F/100*24,int(GetAttack()*"Witch.Auto Attack.DamageMult"_F),upperLevel,{cos(angleToCursor)*"Witch.Auto Attack.Speed"_F,sin(angleToCursor)*"Witch.Auto Attack.Speed"_F},false,INFINITE,true)EndBullet; CreateBullet(PurpleEnergyBall)(GetPos(),"Witch.Auto Attack.Radius"_F/100*12,"Witch.Auto Attack.Homing Range"_F/100*24,int(GetAttack()*"Witch.Auto Attack.DamageMult"_F),upperLevel,{cos(angleToCursor)*"Witch.Auto Attack.Speed"_F,sin(angleToCursor)*"Witch.Auto Attack.Speed"_F},false,INFINITE,true)EndBullet;
BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile(); BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED); SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
return true; return true;
} }
void Witch::InitializeClassAbilities(){ void Witch::InitializeClassAbilities(){
#pragma region Witch Right-click Ability (Transform) #pragma region Witch Right-click Ability (Transform)
Witch::rightClickAbility.action= Witch::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
p->SetupAfterImage(); p->SetupAfterImage();
p->afterImagePos=p->leapStartingPos=p->GetPos(); p->afterImagePos=p->leapStartingPos=p->GetPos();
geom2d::line<float>targetLine{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}; geom2d::line<float>targetLine{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)};
const float LeapMaxRange{"Witch.Right Click Ability.Leap Velocity"_F*"Witch.Right Click Ability.Leap Max Range Time"_F}; const float LeapMaxRange{"Witch.Right Click Ability.Leap Velocity"_F*"Witch.Right Click Ability.Leap Max Range Time"_F};
p->leapTimer=p->totalLeapTime=std::min("Witch.Right Click Ability.Leap Max Range Time"_F,util::lerp(0.f,"Witch.Right Click Ability.Leap Max Range Time"_F,targetLine.length()/(LeapMaxRange/100.f*24))); p->leapTimer=p->totalLeapTime=std::min("Witch.Right Click Ability.Leap Max Range Time"_F,util::lerp(0.f,"Witch.Right Click Ability.Leap Max Range Time"_F,targetLine.length()/(LeapMaxRange/100.f*24)));
p->transformTargetDir=targetLine.vector().polar().y; p->transformTargetDir=targetLine.vector().polar().y;
p->SetAnimationBasedOnTargetingDirection("TRANSFORM",p->transformTargetDir); p->SetAnimationBasedOnTargetingDirection("TRANSFORM",p->transformTargetDir);
p->ApplyIframes("Witch.Right Click Ability.Leap Max Range Time"_F+0.1f); p->ApplyIframes("Witch.Right Click Ability.Leap Max Range Time"_F+0.1f);
p->SetState(State::LEAP); p->SetState(State::LEAP);
SoundEffect::PlaySFX("Meow",p->GetPos()); SoundEffect::PlaySFX("Meow",p->GetPos());
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Witch Ability 1 (Curse of Pain) #pragma region Witch Ability 1 (Curse of Pain)
Witch::ability1.action= Witch::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())}; std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
if(curseTarget.has_value()&&!curseTarget.value().expired()){ if(curseTarget.has_value()&&!curseTarget.value().expired()){
const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]}; const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]};
const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]}; const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]};
const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]}; const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]};
curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks, curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks,
[](std::weak_ptr<Monster>m,Buff&b){ [](std::weak_ptr<Monster>m,Buff&b){
expireCallbackFunc: expireCallbackFunc:
m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT); m.lock()->Hurt(game->GetPlayer()->GetAttack()*"Witch.Ability 1.Final Tick Damage"_F,m.lock()->OnUpperLevel(),m.lock()->GetZ(),HurtFlag::DOT);
} }
); );
curseTarget.value().lock()->AddBuff(BuffType::GLOW_PURPLE,buffDuration,1.f); curseTarget.value().lock()->AddBuff(BuffType::GLOW_PURPLE,buffDuration,1.f);
const vf2d targetPos{curseTarget.value().lock()->GetPos()}; const vf2d targetPos{curseTarget.value().lock()->GetPos()};
for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/8))){ for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/8))){
float drawDist{i*8.f}; float drawDist{i*8.f};
float fadeInTime{i*0.05f}; float fadeInTime{i*0.05f};
float fadeOutTime{0.5f+i*0.05f}; float fadeOutTime{0.5f+i*0.05f};
float effectSize{util::random(0.2f)}; float effectSize{util::random(0.2f)};
game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true); game->AddEffect(std::make_unique<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true);
} }
return true; return true;
}else return false; }else return false;
}; };
#pragma endregion #pragma endregion
#pragma region Witch Ability 2 (???) #pragma region Witch Ability 2 (Throw Poison)
Witch::ability2.action= Witch::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
return false; const float totalFallTime{util::lerp(0.f,0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
};
#pragma endregion CreateBullet(PoisonBottle)(p->GetPos(),pos,"Witch.Ability 2.Casting Size"_F/100.f*24,12.f,totalFallTime,"Witch.Ability 2.Toss Max Z"_F,p->GetAttack()*"Witch.Ability 2.Damage Mult"_F,p->OnUpperLevel(),false,INFINITE,true,WHITE,vf2d{1.f,1.f},util::random(2*PI))EndBullet;
#pragma region Witch Ability 3 (???) return true;
Witch::ability3.action= };
[](Player*p,vf2d pos={}){ #pragma endregion
return false; #pragma region Witch Ability 3 (???)
}; Witch::ability3.action=
#pragma endregion [](Player*p,vf2d pos={}){
return false;
};
#pragma endregion
} }

@ -50,21 +50,21 @@ INCLUDE_BULLET_LIST
INCLUDE_game INCLUDE_game
void Wizard::Initialize(){ void Wizard::Initialize(){
READFROMCONFIG(Wizard,WIZARD); READFROMCONFIG(Wizard,WIZARD);
Wizard::idle_n="WIZARD_IDLE_N"; Wizard::idle_n="WIZARD_IDLE_N";
Wizard::idle_e="WIZARD_IDLE_E"; Wizard::idle_e="WIZARD_IDLE_E";
Wizard::idle_s="WIZARD_IDLE_S"; Wizard::idle_s="WIZARD_IDLE_S";
Wizard::idle_w="WIZARD_IDLE_W"; Wizard::idle_w="WIZARD_IDLE_W";
Wizard::walk_n="WIZARD_WALK_N"; Wizard::walk_n="WIZARD_WALK_N";
Wizard::walk_e="WIZARD_WALK_E"; Wizard::walk_e="WIZARD_WALK_E";
Wizard::walk_s="WIZARD_WALK_S"; Wizard::walk_s="WIZARD_WALK_S";
Wizard::walk_w="WIZARD_WALK_W"; Wizard::walk_w="WIZARD_WALK_W";
} }
SETUP_CLASS(Wizard) SETUP_CLASS(Wizard)
void Wizard::OnUpdate(float fElapsedTime){ void Wizard::OnUpdate(float fElapsedTime){
if(attack_cooldown_timer>0){ if(attack_cooldown_timer>0){
idle_n="WIZARD_IDLE_ATTACK_N"; idle_n="WIZARD_IDLE_ATTACK_N";
idle_e="WIZARD_IDLE_ATTACK_E"; idle_e="WIZARD_IDLE_ATTACK_E";
idle_s="WIZARD_IDLE_ATTACK_S"; idle_s="WIZARD_IDLE_ATTACK_S";
@ -105,78 +105,78 @@ bool Wizard::AutoAttack(){
attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction(); attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
float angleToCursor=atan2(GetWorldAimingLocation().y-GetPos().y,GetWorldAimingLocation().x-GetPos().x); float angleToCursor=atan2(GetWorldAimingLocation().y-GetPos().y,GetWorldAimingLocation().x-GetPos().x);
CreateBullet(EnergyBolt)(GetPos(),{cos(angleToCursor)*"Wizard.Auto Attack.Speed"_F,sin(angleToCursor)*"Wizard.Auto Attack.Speed"_F},"Wizard.Auto Attack.Radius"_F/100*12,int(GetAttack()*"Wizard.Auto Attack.DamageMult"_F),upperLevel,true,WHITE)EndBullet; CreateBullet(EnergyBolt)(GetPos(),{cos(angleToCursor)*"Wizard.Auto Attack.Speed"_F,sin(angleToCursor)*"Wizard.Auto Attack.Speed"_F},"Wizard.Auto Attack.Radius"_F/100*12,int(GetAttack()*"Wizard.Auto Attack.DamageMult"_F),upperLevel,true,WHITE)EndBullet;
BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile(); BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED); SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
return true; return true;
} }
void Wizard::InitializeClassAbilities(){ void Wizard::InitializeClassAbilities(){
#pragma region Wizard Right-click Ability (Teleport) #pragma region Wizard Right-click Ability (Teleport)
Wizard::rightClickAbility.action= Wizard::rightClickAbility.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
float pointMouseDirection=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).x-p->GetPos().x); float pointMouseDirection=atan2(p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).y-p->GetPos().y,p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED).x-p->GetPos().x);
vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)}; vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)};
float dist=std::clamp(geom2d::line<float>{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}.length(),0.f,"Wizard.Right Click Ability.TeleportRange"_F/100*24); float dist=std::clamp(geom2d::line<float>{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}.length(),0.f,"Wizard.Right Click Ability.TeleportRange"_F/100*24);
if(dist<"Wizard.Right Click Ability.TilesMin"_I*12)return false; if(dist<"Wizard.Right Click Ability.TilesMin"_I*12)return false;
vf2d teleportPoint=p->GetPos()+pointTowardsMouse*dist; vf2d teleportPoint=p->GetPos()+pointTowardsMouse*dist;
while(dist>0&&game->HasTileCollision(game->GetCurrentLevel(),teleportPoint)&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))){ while(dist>0&&game->HasTileCollision(game->GetCurrentLevel(),teleportPoint)&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))){
dist-=4; dist-=4;
teleportPoint=p->GetPos()+pointTowardsMouse*dist; teleportPoint=p->GetPos()+pointTowardsMouse*dist;
} }
vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth; vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,p->OnUpperLevel()); geom2d::rect<float>collisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,p->OnUpperLevel());
#pragma region lambdas #pragma region lambdas
auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;}; auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
#pragma endregion #pragma endregion
collisionRect.pos+=tilePos; collisionRect.pos+=tilePos;
#pragma region lambdas #pragma region lambdas
auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle<float>(teleportPoint,4),collisionRect);}; auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle<float>(teleportPoint,4),collisionRect);};
#pragma endregion #pragma endregion
if(p->lastPathfindingCooldown==0.f){ if(p->lastPathfindingCooldown==0.f){
if(dist>0&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I)) if(dist>0&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))
&&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){ &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
p->SetState(State::TELEPORT); p->SetState(State::TELEPORT);
p->teleportAnimationTimer="Wizard.Right Click Ability.AnimationTime"_F; p->teleportAnimationTimer="Wizard.Right Click Ability.AnimationTime"_F;
p->teleportTarget=teleportPoint; p->teleportTarget=teleportPoint;
p->teleportStartPosition=p->GetPos(); p->teleportStartPosition=p->GetPos();
p->ApplyIframes("Wizard.Right Click Ability.IframeTime"_F); p->ApplyIframes("Wizard.Right Click Ability.IframeTime"_F);
for(int i=0;i<"Wizard.Right Click Ability.ParticleCount"_I;i++){ for(int i=0;i<"Wizard.Right Click Ability.ParticleCount"_I;i++){
game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12,(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12},util::random("Wizard.Right Click Ability.ParticleLifetimeMax"_F)+"Wizard.Right Click Ability.ParticleLifetimeMin"_F,"circle.png",p->upperLevel,"Wizard.Right Click Ability.ParticleSize"_F,"Wizard.Right Click Ability.ParticleFadetime"_F,vf2d{util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F,util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F},"Wizard.Right Click Ability.ParticleColor"_Pixel)); game->AddEffect(std::make_unique<Effect>(p->GetPos()+vf2d{(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12,(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12},util::random("Wizard.Right Click Ability.ParticleLifetimeMax"_F)+"Wizard.Right Click Ability.ParticleLifetimeMin"_F,"circle.png",p->upperLevel,"Wizard.Right Click Ability.ParticleSize"_F,"Wizard.Right Click Ability.ParticleFadetime"_F,vf2d{util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F,util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F},"Wizard.Right Click Ability.ParticleColor"_Pixel));
} }
SoundEffect::PlaySFX("Wizard Teleport",SoundEffect::CENTERED); SoundEffect::PlaySFX("Wizard Teleport",SoundEffect::CENTERED);
p->lastPathfindingCooldown=0.1f; p->lastPathfindingCooldown=0.1f;
return true; return true;
} else { } else {
p->notificationDisplay={"Cannot Teleport to that location!",0.5}; p->notificationDisplay={"Cannot Teleport to that location!",0.5};
p->lastPathfindingCooldown=0.1f; p->lastPathfindingCooldown=0.1f;
return false; return false;
} }
} }
return false; return false;
}; };
#pragma endregion #pragma endregion
#pragma region Wizard Ability 1 (Fire Bolt) #pragma region Wizard Ability 1 (Fire Bolt)
Wizard::ability1.action= Wizard::ability1.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x); float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x);
CreateBullet(FireBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F},"Wizard.Ability 1.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 1.InitialDamageMult"_F),p->upperLevel,true,"Wizard.Ability 1.BulletColor"_Pixel)EndBullet; CreateBullet(FireBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 1.BulletSpeed"_F},"Wizard.Ability 1.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 1.InitialDamageMult"_F),p->upperLevel,true,"Wizard.Ability 1.BulletColor"_Pixel)EndBullet;
SoundEffect::PlaySFX("Wizard Fire Bolt Shoot",SoundEffect::CENTERED); SoundEffect::PlaySFX("Wizard Fire Bolt Shoot",SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Wizard Ability 2 (Lightning Bolt) #pragma region Wizard Ability 2 (Lightning Bolt)
Wizard::ability2.action= Wizard::ability2.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x); float angleToCursor=atan2(p->GetWorldAimingLocation().y-p->GetPos().y,p->GetWorldAimingLocation().x-p->GetPos().x);
CreateBullet(LightningBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F},"Wizard.Ability 2.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 2.DamageMult"_F),p->upperLevel,true,"Wizard.Ability 2.BulletColor"_Pixel)EndBullet; CreateBullet(LightningBolt)(p->GetPos(),{cos(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F,sin(angleToCursor)*"Wizard.Ability 2.BulletSpeed"_F},"Wizard.Ability 2.Radius"_F/100*12,int(p->GetAttack()*"Wizard.Ability 2.DamageMult"_F),p->upperLevel,true,"Wizard.Ability 2.BulletColor"_Pixel)EndBullet;
SoundEffect::PlaySFX("Wizard Lightning Bolt Shoot",SoundEffect::CENTERED); SoundEffect::PlaySFX("Wizard Lightning Bolt Shoot",SoundEffect::CENTERED);
return true; return true;
}; };
#pragma endregion #pragma endregion
#pragma region Wizard Ability 3 (Meteor) #pragma region Wizard Ability 3 (Meteor)
Wizard::ability3.action= Wizard::ability3.action=
[](Player*p,vf2d pos={}){ [](Player*p,vf2d pos={}){
game->AddEffect(std::make_unique<Meteor>(pos,3,"meteor.png",p->OnUpperLevel(),vf2d{"Wizard.Ability 3.MeteorRadius"_F/100/4,"Wizard.Ability 3.MeteorRadius"_F/100/4},"Wizard.Ability 3.MeteorFadeoutTime"_F)); game->AddEffect(std::make_unique<Meteor>(pos,3,"meteor.png",p->OnUpperLevel(),vf2d{"Wizard.Ability 3.MeteorRadius"_F/100/4,"Wizard.Ability 3.MeteorRadius"_F/100/4},"Wizard.Ability 3.MeteorFadeoutTime"_F));
return true; return true;
}; };
#pragma endregion #pragma endregion
} }

@ -86,13 +86,17 @@ Witch
Short Name = POISON Short Name = POISON
Description = Throw a poison bottle at target location. All targets caught in the range take poison damage over time. Description = Throw a poison bottle at target location. All targets caught in the range take poison damage over time.
Icon = throw_poison.png Icon = throw_poison.png
Cooldown = 16 Cooldown = 1
Mana Cost = 40 Mana Cost = 0
# Whether or not this ability cancels casts. # Whether or not this ability cancels casts.
CancelCast = 0 CancelCast = 0
Damage Mult = 5x
# Damage per tick, time between ticks, total debuff time # Damage per tick, time between ticks, total debuff time
Poison Debuff = 0.5x, 3s, 30s Poison Debuff = 0.5x, 3s, 30s
Toss Max Range Time = 0.5s
Toss Max Z = 12px
#RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown. #RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown.
Cooldown Bar Color 1 = 64, 0, 0, 192 Cooldown Bar Color 1 = 64, 0, 0, 192

@ -115,6 +115,8 @@ Images
GFX_ExplosiveTrap = explosive_trap.png GFX_ExplosiveTrap = explosive_trap.png
GFX_Explosion = explosionframes.png GFX_Explosion = explosionframes.png
GFX_PurpleEnergyBallHit = purpleenergyball_hit.png GFX_PurpleEnergyBallHit = purpleenergyball_hit.png
GFX_PoisonPool = poison_pool.png
GFX_PoisonBottle = poison_bottle.png
GFX_Thief_Sheet = nico-thief.png GFX_Thief_Sheet = nico-thief.png
GFX_Trapper_Sheet = nico-trapper.png GFX_Trapper_Sheet = nico-trapper.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 777 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 752 B

Loading…
Cancel
Save