diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
index 27dcf3a1..35c88273 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj
@@ -985,6 +985,10 @@
+
+
+
+
diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
index d2e6c3fd..d6ad0df6 100644
--- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
+++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
@@ -1151,6 +1151,9 @@
Source Files\Bullet Types
+
+ Source Files\Bullet Types
+
diff --git a/Adventures in Lestoria/Buff.h b/Adventures in Lestoria/Buff.h
index a12ee519..dbfbbe07 100644
--- a/Adventures in Lestoria/Buff.h
+++ b/Adventures in Lestoria/Buff.h
@@ -56,6 +56,7 @@ enum BuffType{
ONE_OFF,
OVER_TIME_DURING_CAST,
GLOW_PURPLE,
+ COLOR_MOD,
};
enum class BuffRestorationType{
ONE_OFF,
diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h
index 48f5cda2..bcefe572 100644
--- a/Adventures in Lestoria/BulletTypes.h
+++ b/Adventures in Lestoria/BulletTypes.h
@@ -329,4 +329,19 @@ private:
const vf2d initialScale;
const float homingRadius;
std::optional>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;
};
\ No newline at end of file
diff --git a/Adventures in Lestoria/Monster.cpp b/Adventures in Lestoria/Monster.cpp
index 24a5c003..6ca1faf3 100644
--- a/Adventures in Lestoria/Monster.cpp
+++ b/Adventures in Lestoria/Monster.cpp
@@ -442,7 +442,8 @@ void Monster::Draw()const{
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)))};
const vf2d hitTimerOffset=vf2d{sin(20*PI*lastHitTimer+randomFrameOffset),0.f}*2.f*GetSizeMult();
diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp
index a8115815..69881520 100644
--- a/Adventures in Lestoria/Player.cpp
+++ b/Adventures in Lestoria/Player.cpp
@@ -512,16 +512,16 @@ void Player::Update(float fElapsedTime){
SetAdditiveBlending(!IsUsingAdditiveBlending());
}
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()};
clampedMousePolarDir.x=std::clamp(mouseDir.length(),24.f,"Thief.Ability 2.Range"_F*24.f/100);
const vf2d originalPos{GetPos()};
- deadlyDashEndingPos=GetPos()+clampedMousePolarDir.cart();
+ deadlyDashEndingPos=GetPos()+clampedMousePolarDir.cart();
SoundEffect::PlaySFX("Deadly Dash",GetPos());
SetPos(MoveUsingPathfinding(deadlyDashEndingPos));
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);
SetAdditiveBlending(false);
game->SetupWorldShake(0.3f);
@@ -536,7 +536,7 @@ void Player::Update(float fElapsedTime){
SetZ(0.f);
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());
animation.UpdateState(internal_animState,fElapsedTime);
}break;
@@ -766,11 +766,11 @@ void Player::Update(float fElapsedTime){
#pragma region Thief
if(daggerThrowWaitTimer-fElapsedTime<=0.f){
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;
#pragma endregion
@@ -1797,10 +1797,10 @@ const bool Player::IsUsingAdditiveBlending()const{
vf2d Player::MoveUsingPathfinding(vf2d targetPos){
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{GetPos(),targetPos}.length()};
- vf2d teleportPoint=GetPos()+pointTowardsMouse*dist;
- while(dist>0){
+ vf2d teleportPoint=GetPos()+pointTowardsMouse*dist;
+ while(dist>0){
vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,OnUpperLevel());
collisionRect.pos+=tilePos;
@@ -1811,12 +1811,12 @@ vf2d Player::MoveUsingPathfinding(vf2d targetPos){
auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle(teleportPoint,4),collisionRect);};
#pragma endregion
if(CanPathfindTo(GetPos(),teleportPoint,12)
- &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
+ &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
return teleportPoint;
}
dist-=4;
- teleportPoint=GetPos()+pointTowardsMouse*dist;
- }
+ teleportPoint=GetPos()+pointTowardsMouse*dist;
+ }
return GetPos();
}
@@ -1825,11 +1825,11 @@ const std::unordered_set&Player::GetMyClass()const{
}
void Player::SetupAfterImage(){
- game->SetDrawTarget(afterImage.Sprite());
- 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->SetDrawTarget(nullptr);
- afterImage.Decal()->Update();
+ game->SetDrawTarget(afterImage.Sprite());
+ 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->SetDrawTarget(nullptr);
+ afterImage.Decal()->Update();
removeLineTimer=TIME_BETWEEN_LINE_REMOVALS;
scanLine=1U;
}
diff --git a/Adventures in Lestoria/PoisonBottle.cpp b/Adventures in Lestoria/PoisonBottle.cpp
new file mode 100644
index 00000000..6ebaa0a1
--- /dev/null
+++ b/Adventures in Lestoria/PoisonBottle.cpp
@@ -0,0 +1,93 @@
+#pragma region License
+/*
+License (OLC-3)
+~~~~~~~~~~~~~~~
+
+Copyright 2024 Joshua Sigona
+
+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
+#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(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(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(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;
+}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Ranger.cpp b/Adventures in Lestoria/Ranger.cpp
index b32f3203..d762ea04 100644
--- a/Adventures in Lestoria/Ranger.cpp
+++ b/Adventures in Lestoria/Ranger.cpp
@@ -50,15 +50,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Ranger::Initialize(){
- READFROMCONFIG(Ranger,RANGER);
- Ranger::idle_n="RANGER_IDLE_N";
- Ranger::idle_e="RANGER_IDLE_E";
- Ranger::idle_s="RANGER_IDLE_S";
- Ranger::idle_w="RANGER_IDLE_W";
- Ranger::walk_n="RANGER_WALK_N";
- Ranger::walk_e="RANGER_WALK_E";
- Ranger::walk_s="RANGER_WALK_S";
- Ranger::walk_w="RANGER_WALK_W";
+ READFROMCONFIG(Ranger,RANGER);
+ Ranger::idle_n="RANGER_IDLE_N";
+ Ranger::idle_e="RANGER_IDLE_E";
+ Ranger::idle_s="RANGER_IDLE_S";
+ Ranger::idle_w="RANGER_IDLE_W";
+ Ranger::walk_n="RANGER_WALK_N";
+ Ranger::walk_e="RANGER_WALK_E";
+ Ranger::walk_s="RANGER_WALK_S";
+ Ranger::walk_w="RANGER_WALK_W";
}
SETUP_CLASS(Ranger)
@@ -67,86 +67,86 @@ void Ranger::OnUpdate(float fElapsedTime){
}
bool Ranger::AutoAttack(){
- geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
- vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
- float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
- 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;
- BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
- SetState(State::SHOOT_ARROW);
- SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
- SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
- return true;
+ geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
+ vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
+ float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
+ 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;
+ BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
+ SetState(State::SHOOT_ARROW);
+ SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
+ SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
+ return true;
}
void Ranger::InitializeClassAbilities(){
- #pragma region Ranger Right-click Ability (Retreat)
- Ranger::rightClickAbility.action=
- [](Player*p,vf2d pos={}){
- 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.
- p->SetVelocity(mouseDir.vector().norm()*velocity);
- p->retreatTimer=p->RETREAT_TIME;
- p->ApplyIframes(p->RETREAT_TIME);
- p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
- p->ghostFrameTimer=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);
- p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor);
- p->SetState(State::RETREAT);
- SoundEffect::PlaySFX("Ranger.Right Click Ability.Sound"_S,SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Ranger Ability 1 (Rapid Fire)
- Ranger::ability1.action=
- [](Player*p,vf2d pos={}){
- p->remainingRapidFireShots=p->RAPID_FIRE_SHOOT_AMOUNT;
- p->rapidFireTimer=p->RAPID_FIRE_SHOOT_DELAY;
- if("Ranger.Ability 1.IsAnimationLocked"_I){
- p->SetState(State::ANIMATION_LOCK);
- }
- SoundEffect::PlaySFX("Ranger.Ability 1.Sound"_S,SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Ranger Ability 2 (Charged Shot)
- Ranger::ability2.action=
- [](Player*p,vf2d pos={}){
- vf2d arrowVelocity=util::pointTo(p->GetPos(),p->GetWorldAimingLocation());
- BULLET_LIST.push_back(std::make_unique(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->rangerShootAnimationTimer=0.3f;
- p->SetAnimationBasedOnTargetingDirection("SHOOT",atan2(arrowVelocity.y,arrowVelocity.x));
- game->SetupWorldShake("Ranger.Ability 2.WorldShakeTime"_F);
- p->Knockback(-1.f*arrowVelocity.norm()*"Ranger.Ability 2.Knockback"_F);
- SoundEffect::PlaySFX("Ranger.Ability 2.Sound"_S,SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Ranger Ability 3 (Multi Shot)
- Ranger::ability3.action=
- [](Player*p,vf2d pos={}){
- geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetWorldAimingLocation());
- float shootingDist=pointTowardsCursor.length();
- vf2d shootingDirMiddle=pointTowardsCursor.vector();
- 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;
- for(int i=0;iGetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist);
- vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
- BULLET_LIST.push_back(std::make_unique(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->SetState(State::SHOOT_ARROW);
- p->SetAnimationBasedOnTargetingDirection("SHOOT",shootingAngle);
- SoundEffect::PlaySFX("Ranger.Ability 3.Sound"_S,SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
+ #pragma region Ranger Right-click Ability (Retreat)
+ Ranger::rightClickAbility.action=
+ [](Player*p,vf2d pos={}){
+ 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.
+ p->SetVelocity(mouseDir.vector().norm()*velocity);
+ p->retreatTimer=p->RETREAT_TIME;
+ p->ApplyIframes(p->RETREAT_TIME);
+ p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
+ p->ghostFrameTimer=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);
+ p->SetAnimationBasedOnTargetingDirection("IDLE",angleToCursor);
+ p->SetState(State::RETREAT);
+ SoundEffect::PlaySFX("Ranger.Right Click Ability.Sound"_S,SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Ranger Ability 1 (Rapid Fire)
+ Ranger::ability1.action=
+ [](Player*p,vf2d pos={}){
+ p->remainingRapidFireShots=p->RAPID_FIRE_SHOOT_AMOUNT;
+ p->rapidFireTimer=p->RAPID_FIRE_SHOOT_DELAY;
+ if("Ranger.Ability 1.IsAnimationLocked"_I){
+ p->SetState(State::ANIMATION_LOCK);
+ }
+ SoundEffect::PlaySFX("Ranger.Ability 1.Sound"_S,SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Ranger Ability 2 (Charged Shot)
+ Ranger::ability2.action=
+ [](Player*p,vf2d pos={}){
+ vf2d arrowVelocity=util::pointTo(p->GetPos(),p->GetWorldAimingLocation());
+ BULLET_LIST.push_back(std::make_unique(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->rangerShootAnimationTimer=0.3f;
+ p->SetAnimationBasedOnTargetingDirection("SHOOT",atan2(arrowVelocity.y,arrowVelocity.x));
+ game->SetupWorldShake("Ranger.Ability 2.WorldShakeTime"_F);
+ p->Knockback(-1.f*arrowVelocity.norm()*"Ranger.Ability 2.Knockback"_F);
+ SoundEffect::PlaySFX("Ranger.Ability 2.Sound"_S,SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Ranger Ability 3 (Multi Shot)
+ Ranger::ability3.action=
+ [](Player*p,vf2d pos={}){
+ geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetWorldAimingLocation());
+ float shootingDist=pointTowardsCursor.length();
+ vf2d shootingDirMiddle=pointTowardsCursor.vector();
+ 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;
+ for(int i=0;iGetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist);
+ vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
+ BULLET_LIST.push_back(std::make_unique(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->SetState(State::SHOOT_ARROW);
+ p->SetAnimationBasedOnTargetingDirection("SHOOT",shootingAngle);
+ SoundEffect::PlaySFX("Ranger.Ability 3.Sound"_S,SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Thief.cpp b/Adventures in Lestoria/Thief.cpp
index 627b2900..ce208aae 100644
--- a/Adventures in Lestoria/Thief.cpp
+++ b/Adventures in Lestoria/Thief.cpp
@@ -51,115 +51,115 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Thief::Initialize(){
- READFROMCONFIG(Thief,THIEF);
- Thief::idle_n="THIEF_IDLE_N";
- Thief::idle_e="THIEF_IDLE_E";
- Thief::idle_s="THIEF_IDLE_S";
- Thief::idle_w="THIEF_IDLE_W";
- Thief::walk_n="THIEF_WALK_N";
- Thief::walk_e="THIEF_WALK_E";
- Thief::walk_s="THIEF_WALK_S";
- Thief::walk_w="THIEF_WALK_W";
+ READFROMCONFIG(Thief,THIEF);
+ Thief::idle_n="THIEF_IDLE_N";
+ Thief::idle_e="THIEF_IDLE_E";
+ Thief::idle_s="THIEF_IDLE_S";
+ Thief::idle_w="THIEF_IDLE_W";
+ Thief::walk_n="THIEF_WALK_N";
+ Thief::walk_e="THIEF_WALK_E";
+ Thief::walk_s="THIEF_WALK_S";
+ Thief::walk_w="THIEF_WALK_W";
}
SETUP_CLASS(Thief)
void Thief::OnUpdate(float fElapsedTime){
-
+
}
bool Thief::AutoAttack(){
- bool attack=false;
- Monster*closest=nullptr;
- float closest_dist=999999;
- for(std::shared_ptr&m:MONSTER_LIST){
- if(m->IsAlive()&&
- geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))&&
- geom2d::line(GetWorldAimingLocation(),m->GetPos()).length()(GetWorldAimingLocation(),m->GetPos()).length();
- closest=&*m;
- }
- }
+ bool attack=false;
+ Monster*closest=nullptr;
+ float closest_dist=999999;
+ for(std::shared_ptr&m:MONSTER_LIST){
+ if(m->IsAlive()&&
+ geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))&&
+ geom2d::line(GetWorldAimingLocation(),m->GetPos()).length()(GetWorldAimingLocation(),m->GetPos()).length();
+ closest=&*m;
+ }
+ }
- float targetDirection;
+ float targetDirection;
- if(closest!=nullptr){
- float dirToEnemy=geom2d::line(GetPos(),closest->GetPos()).vector().polar().y;
- targetDirection=dirToEnemy;
- SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
- }else{
- float dirToMouse=geom2d::line(GetPos(),GetWorldAimingLocation()).vector().polar().y;
- targetDirection=dirToMouse;
- SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
- }
+ if(closest!=nullptr){
+ float dirToEnemy=geom2d::line(GetPos(),closest->GetPos()).vector().polar().y;
+ targetDirection=dirToEnemy;
+ SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
+ }else{
+ float dirToMouse=geom2d::line(GetPos(),GetWorldAimingLocation()).vector().polar().y;
+ targetDirection=dirToMouse;
+ SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
+ }
- attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
- swordSwingTimer="Thief.Auto Attack.SwordAnimationSwingTime"_F;
+ attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
+ swordSwingTimer="Thief.Auto Attack.SwordAnimationSwingTime"_F;
- game->AddEffect(std::make_unique(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(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);
- SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
- return true;
+ SetState(State::SWING_SWORD);
+ SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
+ return true;
}
void Thief::InitializeClassAbilities(){
- #pragma region Thief Right-click Ability (Roll)
- Thief::rightClickAbility.action=
- [](Player*p,vf2d pos={}){
- p->SetState(State::ROLL);
- 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);
- 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.
- p->SetVelocity(mouseDir.vector().norm()*velocity);
- p->ApplyIframes("Thief.Right Click Ability.Iframe Time"_F);
- p->footstepTimer=0.f;
- return true;
- };
- #pragma endregion
- #pragma region Thief Ability 1 (Hidden Dagger)
- Thief::ability1.action=
- [](Player*p,vf2d pos={}){
- 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.
- p->SetVelocity(mouseDir.vector().norm()*velocity);
- p->retreatTimer="Thief.Ability 1.RetreatTime"_F;
- p->ApplyIframes("Thief.Ability 1.RetreatTime"_F);
- p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
- p->ghostFrameTimer=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;
- 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->SetState(State::RETREAT);
- SoundEffect::PlaySFX("Thief.Ability 1.Sound"_S,SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Thief Ability 2 (Deadly Dash)
- Thief::ability2.action=
- [](Player*p,vf2d pos={}){
- game->AddEffect(std::make_unique(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);
+ #pragma region Thief Right-click Ability (Roll)
+ Thief::rightClickAbility.action=
+ [](Player*p,vf2d pos={}){
+ p->SetState(State::ROLL);
+ 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);
+ 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.
+ p->SetVelocity(mouseDir.vector().norm()*velocity);
+ p->ApplyIframes("Thief.Right Click Ability.Iframe Time"_F);
+ p->footstepTimer=0.f;
+ return true;
+ };
+ #pragma endregion
+ #pragma region Thief Ability 1 (Hidden Dagger)
+ Thief::ability1.action=
+ [](Player*p,vf2d pos={}){
+ 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.
+ p->SetVelocity(mouseDir.vector().norm()*velocity);
+ p->retreatTimer="Thief.Ability 1.RetreatTime"_F;
+ p->ApplyIframes("Thief.Ability 1.RetreatTime"_F);
+ p->ghostPositions.push_back(p->GetPos()+vf2d{0,-p->GetZ()});
+ p->ghostFrameTimer=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;
+ 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->SetState(State::RETREAT);
+ SoundEffect::PlaySFX("Thief.Ability 1.Sound"_S,SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Thief Ability 2 (Deadly Dash)
+ Thief::ability2.action=
+ [](Player*p,vf2d pos={}){
+ game->AddEffect(std::make_unique(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);
SoundEffect::PlaySFX("Charge Up",p->GetPos());
- p->SetState(State::DEADLYDASH);
- p->deadlyDashWaitTimer="Thief.Ability 2.Initial Wait"_F;
- p->deadlyDashAfterDashTimer=p->deadlyDashWaitTimer+"Thief.Ability 2.Ending Wait"_F;
- game->SetWorldZoom(1.1f);
- SoundEffect::PlaySFX("Ranger Retreat",SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Thief Ability 3 (Adrenaline Rush)
- Thief::ability3.action=
- [](Player*p,vf2d pos={}){
- SoundEffect::PlaySFX("Adrenaline Rush",SoundEffect::CENTERED);
- p->AddBuff(BuffType::ADRENALINE_RUSH,"Thief.Ability 3.Duration"_F,0.f);
- for(int i:std::ranges::iota_view(0,50)){
- float size{util::random_range(0.4f,0.8f)};
- game->AddEffect(std::make_unique(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;
- };
- #pragma endregion
+ p->SetState(State::DEADLYDASH);
+ p->deadlyDashWaitTimer="Thief.Ability 2.Initial Wait"_F;
+ p->deadlyDashAfterDashTimer=p->deadlyDashWaitTimer+"Thief.Ability 2.Ending Wait"_F;
+ game->SetWorldZoom(1.1f);
+ SoundEffect::PlaySFX("Ranger Retreat",SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Thief Ability 3 (Adrenaline Rush)
+ Thief::ability3.action=
+ [](Player*p,vf2d pos={}){
+ SoundEffect::PlaySFX("Adrenaline Rush",SoundEffect::CENTERED);
+ p->AddBuff(BuffType::ADRENALINE_RUSH,"Thief.Ability 3.Duration"_F,0.f);
+ for(int i:std::ranges::iota_view(0,50)){
+ float size{util::random_range(0.4f,0.8f)};
+ game->AddEffect(std::make_unique(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;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Trapper.cpp b/Adventures in Lestoria/Trapper.cpp
index dfd49dcb..8e836187 100644
--- a/Adventures in Lestoria/Trapper.cpp
+++ b/Adventures in Lestoria/Trapper.cpp
@@ -51,15 +51,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Trapper::Initialize(){
- READFROMCONFIG(Trapper,TRAPPER);
- Trapper::idle_n="TRAPPER_IDLE_N";
- Trapper::idle_e="TRAPPER_IDLE_E";
- Trapper::idle_s="TRAPPER_IDLE_S";
- Trapper::idle_w="TRAPPER_IDLE_W";
- Trapper::walk_n="TRAPPER_WALK_N";
- Trapper::walk_e="TRAPPER_WALK_E";
- Trapper::walk_s="TRAPPER_WALK_S";
- Trapper::walk_w="TRAPPER_WALK_W";
+ READFROMCONFIG(Trapper,TRAPPER);
+ Trapper::idle_n="TRAPPER_IDLE_N";
+ Trapper::idle_e="TRAPPER_IDLE_E";
+ Trapper::idle_s="TRAPPER_IDLE_S";
+ Trapper::idle_w="TRAPPER_IDLE_W";
+ Trapper::walk_n="TRAPPER_WALK_N";
+ Trapper::walk_e="TRAPPER_WALK_E";
+ Trapper::walk_s="TRAPPER_WALK_S";
+ Trapper::walk_w="TRAPPER_WALK_W";
}
SETUP_CLASS(Trapper)
@@ -69,66 +69,66 @@ void Trapper::OnUpdate(float fElapsedTime){
}
bool Trapper::AutoAttack(){
- geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
- vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
- float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
- 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;
- BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
- SetState(State::SHOOT_ARROW);
- SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
- SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
- return true;
+ geom2d::line pointTowardsCursor(GetPos(),GetWorldAimingLocation());
+ vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
+ float angleToCursor=atan2(extendedLine.y-GetPos().y,extendedLine.x-GetPos().x);
+ 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;
+ BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
+ SetState(State::SHOOT_ARROW);
+ SetAnimationBasedOnTargetingDirection("SHOOT",angleToCursor);
+ SoundEffect::PlaySFX("Ranger.Auto Attack.Sound"_S,SoundEffect::CENTERED);
+ return true;
}
void Trapper::InitializeClassAbilities(){
- #pragma region Trapper Right-click Ability (Sprint)
- Trapper::rightClickAbility.action=
- [](Player*p,vf2d pos={}){
- 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);
- for(int i:std::ranges::iota_view(0,50)){
- float size{util::random_range(0.4f,0.8f)};
- game->AddEffect(std::make_unique(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;
- };
- #pragma endregion
- #pragma region Trapper Ability 1 (Mark Target)
- Trapper::ability1.action=
- [](Player*p,vf2d pos={}){
- std::optional>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())};
- vf2d targetPos{pos};
- if(nearestMonster.has_value()){
- targetPos=nearestMonster.value().lock()->GetPos();
- 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))){
- float drawDist{i*16.f};
- float fadeInTime{i*0.05f};
- float fadeOutTime{0.5f+i*0.05f};
- float effectSize{util::random(0.4f)};
- game->AddEffect(std::make_unique(geom2d::line(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 false;
- };
- #pragma endregion
- #pragma region Trapper Ability 2 (Bear Trap)
- Trapper::ability2.action=
- [](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;
- SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
- p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
- return true;
- };
- #pragma endregion
- #pragma region Trapper Ability 3 (Explosive Trap)
- Trapper::ability3.action=
- [](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;
- SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
- p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
- return true;
- };
- #pragma endregion
+ #pragma region Trapper Right-click Ability (Sprint)
+ Trapper::rightClickAbility.action=
+ [](Player*p,vf2d pos={}){
+ 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);
+ for(int i:std::ranges::iota_view(0,50)){
+ float size{util::random_range(0.4f,0.8f)};
+ game->AddEffect(std::make_unique(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;
+ };
+ #pragma endregion
+ #pragma region Trapper Ability 1 (Mark Target)
+ Trapper::ability1.action=
+ [](Player*p,vf2d pos={}){
+ std::optional>nearestMonster{Monster::GetNearestMonster(pos,Trapper::ability1.precastInfo.range,p->OnUpperLevel(),p->GetZ())};
+ vf2d targetPos{pos};
+ if(nearestMonster.has_value()){
+ targetPos=nearestMonster.value().lock()->GetPos();
+ 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))){
+ float drawDist{i*16.f};
+ float fadeInTime{i*0.05f};
+ float fadeOutTime{0.5f+i*0.05f};
+ float effectSize{util::random(0.4f)};
+ game->AddEffect(std::make_unique(geom2d::line(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 false;
+ };
+ #pragma endregion
+ #pragma region Trapper Ability 2 (Bear Trap)
+ Trapper::ability2.action=
+ [](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;
+ SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
+ p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
+ return true;
+ };
+ #pragma endregion
+ #pragma region Trapper Ability 3 (Explosive Trap)
+ Trapper::ability3.action=
+ [](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;
+ SoundEffect::PlaySFX("Place Down Trap",p->GetPos());
+ p->SetAnimationBasedOnTargetingDirection("SETTRAP",p->GetFacingDirection());
+ return true;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h
index 080a41f5..91056f49 100644
--- a/Adventures in Lestoria/Version.h
+++ b/Adventures in Lestoria/Version.h
@@ -39,7 +39,7 @@ All rights reserved.
#define VERSION_MAJOR 1
#define VERSION_MINOR 2
#define VERSION_PATCH 3
-#define VERSION_BUILD 10363
+#define VERSION_BUILD 10389
#define stringify(a) stringify_(a)
#define stringify_(a) #a
diff --git a/Adventures in Lestoria/Warrior.cpp b/Adventures in Lestoria/Warrior.cpp
index 81cdde58..fcb4d30b 100644
--- a/Adventures in Lestoria/Warrior.cpp
+++ b/Adventures in Lestoria/Warrior.cpp
@@ -48,15 +48,15 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Warrior::Initialize(){
- READFROMCONFIG(Warrior,WARRIOR);
- Warrior::idle_n="WARRIOR_IDLE_N";
- Warrior::idle_e="WARRIOR_IDLE_E";
- Warrior::idle_s="WARRIOR_IDLE_S";
- Warrior::idle_w="WARRIOR_IDLE_W";
- Warrior::walk_n="WARRIOR_WALK_N";
- Warrior::walk_e="WARRIOR_WALK_E";
- Warrior::walk_s="WARRIOR_WALK_S";
- Warrior::walk_w="WARRIOR_WALK_W";
+ READFROMCONFIG(Warrior,WARRIOR);
+ Warrior::idle_n="WARRIOR_IDLE_N";
+ Warrior::idle_e="WARRIOR_IDLE_E";
+ Warrior::idle_s="WARRIOR_IDLE_S";
+ Warrior::idle_w="WARRIOR_IDLE_W";
+ Warrior::walk_n="WARRIOR_WALK_N";
+ Warrior::walk_e="WARRIOR_WALK_E";
+ Warrior::walk_s="WARRIOR_WALK_S";
+ Warrior::walk_w="WARRIOR_WALK_W";
}
SETUP_CLASS(Warrior)
@@ -66,110 +66,110 @@ void Warrior::OnUpdate(float fElapsedTime){
}
bool Warrior::AutoAttack(){
- if(GetState()!=State::SPIN){
- bool attack=false;
- Monster*closest=nullptr;
- float closest_dist=999999;
- for(std::shared_ptr&m:MONSTER_LIST){
- if(m->IsAlive()&&
- geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))&&
- geom2d::line(GetWorldAimingLocation(),m->GetPos()).length()(GetWorldAimingLocation(),m->GetPos()).length();
- closest=&*m;
- }
- }
+ if(GetState()!=State::SPIN){
+ bool attack=false;
+ Monster*closest=nullptr;
+ float closest_dist=999999;
+ for(std::shared_ptr&m:MONSTER_LIST){
+ if(m->IsAlive()&&
+ geom2d::overlaps(geom2d::circle(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))&&
+ geom2d::line(GetWorldAimingLocation(),m->GetPos()).length()(GetWorldAimingLocation(),m->GetPos()).length();
+ closest=&*m;
+ }
+ }
- float targetDirection;
+ float targetDirection;
- if(closest!=nullptr){
- float dirToEnemy=geom2d::line(GetPos(),closest->GetPos()).vector().polar().y;
- targetDirection=dirToEnemy;
- SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
- }else{
- float dirToMouse=geom2d::line(GetPos(),GetWorldAimingLocation()).vector().polar().y;
- targetDirection=dirToMouse;
- SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
- }
+ if(closest!=nullptr){
+ float dirToEnemy=geom2d::line(GetPos(),closest->GetPos()).vector().polar().y;
+ targetDirection=dirToEnemy;
+ SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
+ }else{
+ float dirToMouse=geom2d::line(GetPos(),GetWorldAimingLocation()).vector().polar().y;
+ targetDirection=dirToMouse;
+ SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
+ }
- attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
- swordSwingTimer="Warrior.Auto Attack.SwordAnimationSwingTime"_F;
+ attack_cooldown_timer=ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
+ swordSwingTimer="Warrior.Auto Attack.SwordAnimationSwingTime"_F;
- game->AddEffect(std::make_unique(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(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);
- SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
- }
- return true;
+ SetState(State::SWING_SWORD);
+ SoundEffect::PlaySFX("Warrior Auto Attack",SoundEffect::CENTERED);
+ }
+ return true;
}
void Warrior::InitializeClassAbilities(){
- #pragma region Warrior Right-click Ability (Block)
- Warrior::rightClickAbility.action=
- [](Player*p,vf2d pos={}){
- if(p->GetState()==State::NORMAL||p->GetState()==State::CASTING){
- rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME;
- p->SetState(State::BLOCK);
- 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);
- return true;
- }
- return false;
- };
- #pragma endregion
- #pragma region Warrior Ability 1 (Battlecry)
- Warrior::ability1.action=
- [](Player*p,vf2d pos={}){
- game->AddEffect(std::make_unique(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::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
- for(std::shared_ptr&m:MONSTER_LIST){
- if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))){
- m->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F);
- }
- }
- SoundEffect::PlaySFX("Warrior Battlecry",SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Warrior Ability 2 (Ground Slam)
- Warrior::ability2.action=
- [](Player*p,vf2d pos={}){
- p->Spin(GROUND_SLAM_SPIN_TIME,"Warrior.Ability 2.SpinSpd"_F*PI);
- p->iframe_time="Warrior.Ability 2.IframeTime"_F;
- return true;
- };
- #pragma endregion
- #pragma region Warrior Ability 3 (Sonic Slash)
- Warrior::ability3.action=
- [](Player*p,vf2d pos={}){
- p->SetState(State::SWING_SONIC_SWORD);
- p->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 3.StuckTime"_F,1);
- vf2d bulletVel={};
- switch(p->GetFacingDirection()){
- case UP:{
- p->vel.y="Warrior.Ability 3.AbilityPushback"_F;
- bulletVel.y=-"Warrior.Ability 3.BulletSpd"_F;
- p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_N",WARRIOR);
- }break;
- case LEFT:{
- p->vel.x="Warrior.Ability 3.AbilityPushback"_F;
- bulletVel.x=-"Warrior.Ability 3.BulletSpd"_F;
- p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_W",WARRIOR);
- }break;
- case RIGHT:{
- p->vel.x=-"Warrior.Ability 3.AbilityPushback"_F;
- bulletVel.x="Warrior.Ability 3.BulletSpd"_F;
- p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_E",WARRIOR);
- }break;
- case DOWN:{
- p->vel.y=-"Warrior.Ability 3.AbilityPushback"_F;
- bulletVel.y="Warrior.Ability 3.BulletSpd"_F;
- p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_S",WARRIOR);
- }break;
- }
- BULLET_LIST.push_back(std::make_unique(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);
- SoundEffect::PlaySFX("Warrior Sonic Slash",SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
+ #pragma region Warrior Right-click Ability (Block)
+ Warrior::rightClickAbility.action=
+ [](Player*p,vf2d pos={}){
+ if(p->GetState()==State::NORMAL||p->GetState()==State::CASTING){
+ rightClickAbility.cooldown=rightClickAbility.COOLDOWN_TIME;
+ p->SetState(State::BLOCK);
+ 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);
+ return true;
+ }
+ return false;
+ };
+ #pragma endregion
+ #pragma region Warrior Ability 1 (Battlecry)
+ Warrior::ability1.action=
+ [](Player*p,vf2d pos={}){
+ game->AddEffect(std::make_unique(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::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
+ for(std::shared_ptr&m:MONSTER_LIST){
+ if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle(m->GetPos(),m->GetSizeMult()*12))){
+ m->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 1.SlowdownDuration"_F,"Warrior.Ability 1.SlowdownAmt"_F);
+ }
+ }
+ SoundEffect::PlaySFX("Warrior Battlecry",SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Warrior Ability 2 (Ground Slam)
+ Warrior::ability2.action=
+ [](Player*p,vf2d pos={}){
+ p->Spin(GROUND_SLAM_SPIN_TIME,"Warrior.Ability 2.SpinSpd"_F*PI);
+ p->iframe_time="Warrior.Ability 2.IframeTime"_F;
+ return true;
+ };
+ #pragma endregion
+ #pragma region Warrior Ability 3 (Sonic Slash)
+ Warrior::ability3.action=
+ [](Player*p,vf2d pos={}){
+ p->SetState(State::SWING_SONIC_SWORD);
+ p->AddBuff(BuffType::SLOWDOWN,"Warrior.Ability 3.StuckTime"_F,1);
+ vf2d bulletVel={};
+ switch(p->GetFacingDirection()){
+ case UP:{
+ p->vel.y="Warrior.Ability 3.AbilityPushback"_F;
+ bulletVel.y=-"Warrior.Ability 3.BulletSpd"_F;
+ p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_N",WARRIOR);
+ }break;
+ case LEFT:{
+ p->vel.x="Warrior.Ability 3.AbilityPushback"_F;
+ bulletVel.x=-"Warrior.Ability 3.BulletSpd"_F;
+ p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_W",WARRIOR);
+ }break;
+ case RIGHT:{
+ p->vel.x=-"Warrior.Ability 3.AbilityPushback"_F;
+ bulletVel.x="Warrior.Ability 3.BulletSpd"_F;
+ p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_E",WARRIOR);
+ }break;
+ case DOWN:{
+ p->vel.y=-"Warrior.Ability 3.AbilityPushback"_F;
+ bulletVel.y="Warrior.Ability 3.BulletSpd"_F;
+ p->UpdateAnimation("WARRIOR_SWINGSONICSWORD_S",WARRIOR);
+ }break;
+ }
+ BULLET_LIST.push_back(std::make_unique(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);
+ SoundEffect::PlaySFX("Warrior Sonic Slash",SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Witch.cpp b/Adventures in Lestoria/Witch.cpp
index 47c3c45d..3f6fe3bf 100644
--- a/Adventures in Lestoria/Witch.cpp
+++ b/Adventures in Lestoria/Witch.cpp
@@ -51,21 +51,21 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Witch::Initialize(){
- READFROMCONFIG(Witch,WITCH);
- Witch::idle_n="WITCH_IDLE_N";
- Witch::idle_e="WITCH_IDLE_E";
- Witch::idle_s="WITCH_IDLE_S";
- Witch::idle_w="WITCH_IDLE_W";
- Witch::walk_n="WITCH_WALK_N";
- Witch::walk_e="WITCH_WALK_E";
- Witch::walk_s="WITCH_WALK_S";
- Witch::walk_w="WITCH_WALK_W";
+ READFROMCONFIG(Witch,WITCH);
+ Witch::idle_n="WITCH_IDLE_N";
+ Witch::idle_e="WITCH_IDLE_E";
+ Witch::idle_s="WITCH_IDLE_S";
+ Witch::idle_w="WITCH_IDLE_W";
+ Witch::walk_n="WITCH_WALK_N";
+ Witch::walk_e="WITCH_WALK_E";
+ Witch::walk_s="WITCH_WALK_S";
+ Witch::walk_w="WITCH_WALK_W";
}
SETUP_CLASS(Witch)
void Witch::OnUpdate(float fElapsedTime){
- if(attack_cooldown_timer>0){
+ if(attack_cooldown_timer>0){
idle_n="WITCH_IDLE_ATTACK_N";
idle_e="WITCH_IDLE_ATTACK_E";
idle_s="WITCH_IDLE_ATTACK_S";
@@ -106,64 +106,67 @@ bool Witch::AutoAttack(){
attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
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;
- BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
- SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
+ BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
+ SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
return true;
}
void Witch::InitializeClassAbilities(){
- #pragma region Witch Right-click Ability (Transform)
- Witch::rightClickAbility.action=
- [](Player*p,vf2d pos={}){
- p->SetupAfterImage();
- p->afterImagePos=p->leapStartingPos=p->GetPos();
- geom2d::linetargetLine{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};
- 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->SetAnimationBasedOnTargetingDirection("TRANSFORM",p->transformTargetDir);
- p->ApplyIframes("Witch.Right Click Ability.Leap Max Range Time"_F+0.1f);
- p->SetState(State::LEAP);
- SoundEffect::PlaySFX("Meow",p->GetPos());
- return true;
- };
- #pragma endregion
- #pragma region Witch Ability 1 (Curse of Pain)
- Witch::ability1.action=
- [](Player*p,vf2d pos={}){
- std::optional>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
- if(curseTarget.has_value()&&!curseTarget.value().expired()){
- const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]};
- const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]};
- const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]};
- curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks,
- [](std::weak_ptrm,Buff&b){
- expireCallbackFunc:
- 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);
- const vf2d targetPos{curseTarget.value().lock()->GetPos()};
- for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/8))){
- float drawDist{i*8.f};
- float fadeInTime{i*0.05f};
- float fadeOutTime{0.5f+i*0.05f};
- float effectSize{util::random(0.2f)};
- game->AddEffect(std::make_unique(geom2d::line(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;
- }else return false;
- };
- #pragma endregion
- #pragma region Witch Ability 2 (???)
- Witch::ability2.action=
- [](Player*p,vf2d pos={}){
- return false;
- };
- #pragma endregion
- #pragma region Witch Ability 3 (???)
- Witch::ability3.action=
- [](Player*p,vf2d pos={}){
- return false;
- };
- #pragma endregion
+ #pragma region Witch Right-click Ability (Transform)
+ Witch::rightClickAbility.action=
+ [](Player*p,vf2d pos={}){
+ p->SetupAfterImage();
+ p->afterImagePos=p->leapStartingPos=p->GetPos();
+ geom2d::linetargetLine{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};
+ 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->SetAnimationBasedOnTargetingDirection("TRANSFORM",p->transformTargetDir);
+ p->ApplyIframes("Witch.Right Click Ability.Leap Max Range Time"_F+0.1f);
+ p->SetState(State::LEAP);
+ SoundEffect::PlaySFX("Meow",p->GetPos());
+ return true;
+ };
+ #pragma endregion
+ #pragma region Witch Ability 1 (Curse of Pain)
+ Witch::ability1.action=
+ [](Player*p,vf2d pos={}){
+ std::optional>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
+ if(curseTarget.has_value()&&!curseTarget.value().expired()){
+ const float buffTimeBetweenTicks{"Witch.Ability 1.Curse Debuff"_f[1]};
+ const float buffDamageMult{"Witch.Ability 1.Curse Debuff"_f[0]};
+ const float buffDuration{"Witch.Ability 1.Curse Debuff"_f[2]};
+ curseTarget.value().lock()->ApplyDot(buffDuration,p->GetAttack()*buffDamageMult,buffTimeBetweenTicks,
+ [](std::weak_ptrm,Buff&b){
+ expireCallbackFunc:
+ 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);
+ const vf2d targetPos{curseTarget.value().lock()->GetPos()};
+ for(int i:std::ranges::iota_view(0,int(util::distance(p->GetPos(),targetPos)/8))){
+ float drawDist{i*8.f};
+ float fadeInTime{i*0.05f};
+ float fadeOutTime{0.5f+i*0.05f};
+ float effectSize{util::random(0.2f)};
+ game->AddEffect(std::make_unique(geom2d::line(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;
+ }else return false;
+ };
+ #pragma endregion
+ #pragma region Witch Ability 2 (Throw Poison)
+ Witch::ability2.action=
+ [](Player*p,vf2d pos={}){
+ const float totalFallTime{util::lerp(0.f,0.3f,util::distance(p->GetPos(),pos)/("Witch.Ability 2.Casting Range"_F/100.f*24))};
+
+ 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;
+ return true;
+ };
+ #pragma endregion
+ #pragma region Witch Ability 3 (???)
+ Witch::ability3.action=
+ [](Player*p,vf2d pos={}){
+ return false;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/Wizard.cpp b/Adventures in Lestoria/Wizard.cpp
index 1dc7021d..3743ac6e 100644
--- a/Adventures in Lestoria/Wizard.cpp
+++ b/Adventures in Lestoria/Wizard.cpp
@@ -50,21 +50,21 @@ INCLUDE_BULLET_LIST
INCLUDE_game
void Wizard::Initialize(){
- READFROMCONFIG(Wizard,WIZARD);
- Wizard::idle_n="WIZARD_IDLE_N";
- Wizard::idle_e="WIZARD_IDLE_E";
- Wizard::idle_s="WIZARD_IDLE_S";
- Wizard::idle_w="WIZARD_IDLE_W";
- Wizard::walk_n="WIZARD_WALK_N";
- Wizard::walk_e="WIZARD_WALK_E";
- Wizard::walk_s="WIZARD_WALK_S";
- Wizard::walk_w="WIZARD_WALK_W";
+ READFROMCONFIG(Wizard,WIZARD);
+ Wizard::idle_n="WIZARD_IDLE_N";
+ Wizard::idle_e="WIZARD_IDLE_E";
+ Wizard::idle_s="WIZARD_IDLE_S";
+ Wizard::idle_w="WIZARD_IDLE_W";
+ Wizard::walk_n="WIZARD_WALK_N";
+ Wizard::walk_e="WIZARD_WALK_E";
+ Wizard::walk_s="WIZARD_WALK_S";
+ Wizard::walk_w="WIZARD_WALK_W";
}
SETUP_CLASS(Wizard)
void Wizard::OnUpdate(float fElapsedTime){
- if(attack_cooldown_timer>0){
+ if(attack_cooldown_timer>0){
idle_n="WIZARD_IDLE_ATTACK_N";
idle_e="WIZARD_IDLE_ATTACK_E";
idle_s="WIZARD_IDLE_ATTACK_S";
@@ -105,78 +105,78 @@ bool Wizard::AutoAttack(){
attack_cooldown_timer=MAGIC_ATTACK_COOLDOWN-GetAttackRecoveryRateReduction();
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;
- BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
- SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
+ BULLET_LIST.back()->SetIsPlayerAutoAttackProjectile();
+ SoundEffect::PlaySFX("Wizard Auto Attack",SoundEffect::CENTERED);
return true;
}
void Wizard::InitializeClassAbilities(){
- #pragma region Wizard Right-click Ability (Teleport)
- Wizard::rightClickAbility.action=
- [](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);
- vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)};
- float dist=std::clamp(geom2d::line{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;
- 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))){
- dist-=4;
- teleportPoint=p->GetPos()+pointTowardsMouse*dist;
- }
- vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
- geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,p->OnUpperLevel());
- #pragma region lambdas
- auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
- #pragma endregion
- collisionRect.pos+=tilePos;
- #pragma region lambdas
- auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle(teleportPoint,4),collisionRect);};
- #pragma endregion
- if(p->lastPathfindingCooldown==0.f){
- if(dist>0&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))
- &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
- p->SetState(State::TELEPORT);
- p->teleportAnimationTimer="Wizard.Right Click Ability.AnimationTime"_F;
- p->teleportTarget=teleportPoint;
- p->teleportStartPosition=p->GetPos();
- p->ApplyIframes("Wizard.Right Click Ability.IframeTime"_F);
- for(int i=0;i<"Wizard.Right Click Ability.ParticleCount"_I;i++){
- game->AddEffect(std::make_unique(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);
- p->lastPathfindingCooldown=0.1f;
- return true;
- } else {
- p->notificationDisplay={"Cannot Teleport to that location!",0.5};
- p->lastPathfindingCooldown=0.1f;
- return false;
- }
- }
- return false;
- };
- #pragma endregion
- #pragma region Wizard Ability 1 (Fire Bolt)
- Wizard::ability1.action=
- [](Player*p,vf2d pos={}){
- 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;
- SoundEffect::PlaySFX("Wizard Fire Bolt Shoot",SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Wizard Ability 2 (Lightning Bolt)
- Wizard::ability2.action=
- [](Player*p,vf2d pos={}){
- 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;
- SoundEffect::PlaySFX("Wizard Lightning Bolt Shoot",SoundEffect::CENTERED);
- return true;
- };
- #pragma endregion
- #pragma region Wizard Ability 3 (Meteor)
- Wizard::ability3.action=
- [](Player*p,vf2d pos={}){
- game->AddEffect(std::make_unique(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;
- };
- #pragma endregion
+ #pragma region Wizard Right-click Ability (Teleport)
+ Wizard::rightClickAbility.action=
+ [](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);
+ vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)};
+ float dist=std::clamp(geom2d::line{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;
+ 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))){
+ dist-=4;
+ teleportPoint=p->GetPos()+pointTowardsMouse*dist;
+ }
+ vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth;
+ geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,p->OnUpperLevel());
+ #pragma region lambdas
+ auto NoTileCollisionExistsHere=[&](){return collisionRect==game->NO_COLLISION;};
+ #pragma endregion
+ collisionRect.pos+=tilePos;
+ #pragma region lambdas
+ auto NoPlayerCollisionWithTile=[&](){return !geom2d::overlaps(geom2d::circle(teleportPoint,4),collisionRect);};
+ #pragma endregion
+ if(p->lastPathfindingCooldown==0.f){
+ if(dist>0&&p->CanPathfindTo(p->GetPos(),teleportPoint,float("Wizard.Right Click Ability.TilesMax"_I))
+ &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){
+ p->SetState(State::TELEPORT);
+ p->teleportAnimationTimer="Wizard.Right Click Ability.AnimationTime"_F;
+ p->teleportTarget=teleportPoint;
+ p->teleportStartPosition=p->GetPos();
+ p->ApplyIframes("Wizard.Right Click Ability.IframeTime"_F);
+ for(int i=0;i<"Wizard.Right Click Ability.ParticleCount"_I;i++){
+ game->AddEffect(std::make_unique(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);
+ p->lastPathfindingCooldown=0.1f;
+ return true;
+ } else {
+ p->notificationDisplay={"Cannot Teleport to that location!",0.5};
+ p->lastPathfindingCooldown=0.1f;
+ return false;
+ }
+ }
+ return false;
+ };
+ #pragma endregion
+ #pragma region Wizard Ability 1 (Fire Bolt)
+ Wizard::ability1.action=
+ [](Player*p,vf2d pos={}){
+ 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;
+ SoundEffect::PlaySFX("Wizard Fire Bolt Shoot",SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Wizard Ability 2 (Lightning Bolt)
+ Wizard::ability2.action=
+ [](Player*p,vf2d pos={}){
+ 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;
+ SoundEffect::PlaySFX("Wizard Lightning Bolt Shoot",SoundEffect::CENTERED);
+ return true;
+ };
+ #pragma endregion
+ #pragma region Wizard Ability 3 (Meteor)
+ Wizard::ability3.action=
+ [](Player*p,vf2d pos={}){
+ game->AddEffect(std::make_unique(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;
+ };
+ #pragma endregion
}
\ No newline at end of file
diff --git a/Adventures in Lestoria/assets/config/classes/Witch.txt b/Adventures in Lestoria/assets/config/classes/Witch.txt
index cde166c3..3ed1d273 100644
--- a/Adventures in Lestoria/assets/config/classes/Witch.txt
+++ b/Adventures in Lestoria/assets/config/classes/Witch.txt
@@ -86,13 +86,17 @@ Witch
Short Name = POISON
Description = Throw a poison bottle at target location. All targets caught in the range take poison damage over time.
Icon = throw_poison.png
- Cooldown = 16
- Mana Cost = 40
+ Cooldown = 1
+ Mana Cost = 0
# Whether or not this ability cancels casts.
CancelCast = 0
+ Damage Mult = 5x
# Damage per tick, time between ticks, total debuff time
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.
Cooldown Bar Color 1 = 64, 0, 0, 192
diff --git a/Adventures in Lestoria/assets/config/gfx/gfx.txt b/Adventures in Lestoria/assets/config/gfx/gfx.txt
index b4787d70..2a7c0b5e 100644
--- a/Adventures in Lestoria/assets/config/gfx/gfx.txt
+++ b/Adventures in Lestoria/assets/config/gfx/gfx.txt
@@ -115,6 +115,8 @@ Images
GFX_ExplosiveTrap = explosive_trap.png
GFX_Explosion = explosionframes.png
GFX_PurpleEnergyBallHit = purpleenergyball_hit.png
+ GFX_PoisonPool = poison_pool.png
+ GFX_PoisonBottle = poison_bottle.png
GFX_Thief_Sheet = nico-thief.png
GFX_Trapper_Sheet = nico-trapper.png
diff --git a/Adventures in Lestoria/assets/gamepack.pak b/Adventures in Lestoria/assets/gamepack.pak
index 8c8e2176..606c7e97 100644
Binary files a/Adventures in Lestoria/assets/gamepack.pak and b/Adventures in Lestoria/assets/gamepack.pak differ
diff --git a/Adventures in Lestoria/assets/poison_bottle.png b/Adventures in Lestoria/assets/poison_bottle.png
new file mode 100644
index 00000000..9fdb31b6
Binary files /dev/null and b/Adventures in Lestoria/assets/poison_bottle.png differ
diff --git a/Adventures in Lestoria/assets/poison_pool.png b/Adventures in Lestoria/assets/poison_pool.png
new file mode 100644
index 00000000..ad83e5fd
Binary files /dev/null and b/Adventures in Lestoria/assets/poison_pool.png differ
diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe
index 5c37d572..c086c3db 100644
Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ