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