Adjust all spaces to tabs for consistent whitespace formatting on source files. Implement Throw Poison Ability. Added general Color Mod Buff so coloring targets a certain color based on applied buff is simpler. Release Build 10389.
This commit is contained in:
parent
f27caf4382
commit
a5f7973c4d
@ -985,6 +985,10 @@
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Pixel.cpp" />
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||
<SubType>
|
||||
</SubType>
|
||||
|
@ -1151,6 +1151,9 @@
|
||||
<ClCompile Include="PurpleEnergyBall.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PoisonBottle.cpp">
|
||||
<Filter>Source Files\Bullet Types</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="cpp.hint" />
|
||||
|
@ -56,6 +56,7 @@ enum BuffType{
|
||||
ONE_OFF,
|
||||
OVER_TIME_DURING_CAST,
|
||||
GLOW_PURPLE,
|
||||
COLOR_MOD,
|
||||
};
|
||||
enum class BuffRestorationType{
|
||||
ONE_OFF,
|
||||
|
@ -329,4 +329,19 @@ private:
|
||||
const vf2d initialScale;
|
||||
const float homingRadius;
|
||||
std::optional<std::weak_ptr<Monster>>homingTarget;
|
||||
};
|
||||
|
||||
struct PoisonBottle:public Bullet{
|
||||
PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple=false,float lifetime=INFINITE,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle={0.f});
|
||||
void Update(float fElapsedTime)override;
|
||||
void ModifyOutgoingDamageData(HurtDamageInfo&data);
|
||||
private:
|
||||
const vf2d targetPos;
|
||||
const vf2d startingPos;
|
||||
const float totalFallTime;
|
||||
const float originalRisingTime,originalFallingTime;
|
||||
float risingTime,fallingTime;
|
||||
const float initialZ;
|
||||
const float totalRiseZAmt;
|
||||
const float explodeRadius;
|
||||
};
|
@ -442,7 +442,8 @@ void Monster::Draw()const{
|
||||
|
||||
if(GetBuffs(BuffType::GLOW_PURPLE).size()>0)glowPurpleBuff=GetBuffs(BuffType::GLOW_PURPLE)[0];
|
||||
|
||||
if(GetBuffs(BuffType::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();
|
||||
|
@ -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<float>{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::rect<float>collisionRect=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<float>(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<std::string>&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;
|
||||
}
|
||||
|
93
Adventures in Lestoria/PoisonBottle.cpp
Normal file
93
Adventures in Lestoria/PoisonBottle.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma region License
|
||||
/*
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above
|
||||
copyright notice. This list of conditions and the following disclaimer must be
|
||||
reproduced in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Portions of this software are copyright © 2024 The FreeType
|
||||
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
|
||||
#include "BulletTypes.h"
|
||||
#include "AdventuresInLestoria.h"
|
||||
#include <ranges>
|
||||
#include "util.h"
|
||||
|
||||
INCLUDE_game
|
||||
|
||||
PoisonBottle::PoisonBottle(vf2d pos,vf2d targetPos,float explodeRadius,float z,float totalFallTime,float totalRiseZAmt,int damage,bool upperLevel,bool hitsMultiple,float lifetime,bool friendly,Pixel col,vf2d scale,float image_angle)
|
||||
:Bullet(pos,util::pointTo(pos,targetPos)*util::distance(pos,targetPos)/totalFallTime,0.f,damage,"poison_bottle.png",upperLevel,hitsMultiple,lifetime,false,friendly,col,scale,image_angle),initialZ(z),explodeRadius(explodeRadius),totalRiseZAmt(totalRiseZAmt),totalFallTime(totalFallTime),startingPos(pos),targetPos(targetPos),originalRisingTime(totalFallTime*0.25f),risingTime(originalRisingTime),originalFallingTime(totalFallTime*0.75f),fallingTime(originalFallingTime){
|
||||
this->z=z;
|
||||
}
|
||||
|
||||
void PoisonBottle::Update(float fElapsedTime){
|
||||
if(IsDeactivated())return;
|
||||
image_angle+=0.5*PI*fElapsedTime;
|
||||
|
||||
const bool Landed{fallingTime<=0.f};
|
||||
if(Landed){
|
||||
z=0.f;
|
||||
const float poisonCircleScale{"Witch.Ability 2.Casting Size"_F/100.f};
|
||||
game->AddEffect(std::make_unique<Effect>(pos,0.f,"poison_pool.png",game->GetPlayer()->OnUpperLevel(),0.5f,1.2f,vf2d{poisonCircleScale,poisonCircleScale},vf2d{},WHITE,0.f,0.f,false),true);
|
||||
for(int i:std::ranges::iota_view(0,200)){
|
||||
float size{util::random_range(0.4f,0.8f)};
|
||||
Pixel col{PixelLerp(VERY_DARK_GREEN,DARK_GREEN,util::random(1))};
|
||||
col.a=util::random_range(60,200);
|
||||
game->AddEffect(std::make_unique<Effect>(pos+vf2d{util::random(16)*poisonCircleScale,util::random(2*PI)}.cart(),util::random_range(1.f,4.f),"circle.png",game->GetPlayer()->OnUpperLevel(),vf2d{size,size},util::random_range(0.2f,0.5f),vf2d{util::random_range(-6.f,6.f),util::random_range(-12.f,-4.f)},col));
|
||||
}
|
||||
const HurtList hurtList{game->Hurt(pos,explodeRadius,damage,OnUpperLevel(),z,HurtType::MONSTER,HurtFlag::PLAYER_ABILITY)};
|
||||
for(const auto&[targetPtr,wasHit]:hurtList){
|
||||
if(wasHit){
|
||||
Monster*monsterPtr{std::get<Monster*>(targetPtr)};
|
||||
const int buffDamage{int(game->GetPlayer()->GetAttack()*"Witch.Ability 2.Poison Debuff"_f[0])};
|
||||
const float buffDuration{"Witch.Ability 2.Poison Debuff"_f[2]};
|
||||
const float buffTimeBetweenTicks{"Witch.Ability 2.Poison Debuff"_f[1]};
|
||||
monsterPtr->AddBuff(BuffType::COLOR_MOD,buffDuration,Pixel{GREEN}.n);
|
||||
monsterPtr->ApplyDot(buffDuration,buffDamage,buffTimeBetweenTicks);
|
||||
}
|
||||
}
|
||||
vel={};
|
||||
fadeOutTime=0.25f;
|
||||
Deactivate();
|
||||
}else{
|
||||
if(risingTime>0.f){
|
||||
risingTime-=fElapsedTime;
|
||||
z=util::lerp(initialZ,initialZ+totalRiseZAmt,1-(risingTime/originalRisingTime));
|
||||
}else{
|
||||
fallingTime-=fElapsedTime;
|
||||
z=util::lerp(initialZ+totalRiseZAmt,0.f,1-(fallingTime/originalFallingTime));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PoisonBottle::ModifyOutgoingDamageData(HurtDamageInfo&data){
|
||||
if(friendly)data.hurtFlags|=HurtFlag::PLAYER_ABILITY;
|
||||
}
|
@ -50,15 +50,15 @@ INCLUDE_BULLET_LIST
|
||||
INCLUDE_game
|
||||
|
||||
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<ChargedArrow>(p->GetPos(),arrowVelocity*"Ranger.Ability 2.Speed"_F,12*"Ranger.Ability 2.Radius"_F/100,p->GetAttack()*"Ranger.Ability 2.DamageMult"_F,p->OnUpperLevel(),true));
|
||||
p->SetState(State::SHOOT_ARROW);
|
||||
p->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;i<arrowCount;i++){
|
||||
if("Ranger.Ability 3.ArrowCount"_I%2==0&&i=="Ranger.Ability 3.ArrowCount"_I/2)continue;
|
||||
const float halfAngle="Ranger.Ability 3.MultiShotSpread"_F*PI/180;
|
||||
const float leftAngle=-halfAngle;
|
||||
const float increment=halfAngle/"Ranger.Ability 3.ArrowCount"_I;
|
||||
const float newAngle=shootingAngle+leftAngle/2+i*increment;
|
||||
geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist);
|
||||
vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
|
||||
BULLET_LIST.push_back(std::make_unique<Arrow>(Arrow(p->GetPos(),extendedLine,vf2d{cos(newAngle)*"Ranger.Ability 3.ArrowSpd"_F,float(sin(newAngle)*"Ranger.Ability 3.ArrowSpd"_F-PI/8*"Ranger.Ability 3.ArrowSpd"_F)}+p->movementVelocity,12*"Ranger.Ability 3.ArrowRadius"_F/100,int(p->GetAttack()*"Ranger.Ability 3.DamageMult"_F),p->OnUpperLevel(),true)));
|
||||
}
|
||||
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<ChargedArrow>(p->GetPos(),arrowVelocity*"Ranger.Ability 2.Speed"_F,12*"Ranger.Ability 2.Radius"_F/100,p->GetAttack()*"Ranger.Ability 2.DamageMult"_F,p->OnUpperLevel(),true));
|
||||
p->SetState(State::SHOOT_ARROW);
|
||||
p->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;i<arrowCount;i++){
|
||||
if("Ranger.Ability 3.ArrowCount"_I%2==0&&i=="Ranger.Ability 3.ArrowCount"_I/2)continue;
|
||||
const float halfAngle="Ranger.Ability 3.MultiShotSpread"_F*PI/180;
|
||||
const float leftAngle=-halfAngle;
|
||||
const float increment=halfAngle/"Ranger.Ability 3.ArrowCount"_I;
|
||||
const float newAngle=shootingAngle+leftAngle/2+i*increment;
|
||||
geom2d::line pointTowardsCursor=geom2d::line(p->GetPos(),p->GetPos()+vf2d{cos(newAngle),sin(newAngle)}*shootingDist);
|
||||
vf2d extendedLine=pointTowardsCursor.upoint(1.1f);
|
||||
BULLET_LIST.push_back(std::make_unique<Arrow>(Arrow(p->GetPos(),extendedLine,vf2d{cos(newAngle)*"Ranger.Ability 3.ArrowSpd"_F,float(sin(newAngle)*"Ranger.Ability 3.ArrowSpd"_F-PI/8*"Ranger.Ability 3.ArrowSpd"_F)}+p->movementVelocity,12*"Ranger.Ability 3.ArrowRadius"_F/100,int(p->GetAttack()*"Ranger.Ability 3.DamageMult"_F),p->OnUpperLevel(),true)));
|
||||
}
|
||||
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
|
||||
}
|
@ -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<Monster>&m:MONSTER_LIST){
|
||||
if(m->IsAlive()&&
|
||||
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
|
||||
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
|
||||
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
|
||||
closest=&*m;
|
||||
}
|
||||
}
|
||||
bool attack=false;
|
||||
Monster*closest=nullptr;
|
||||
float closest_dist=999999;
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
if(m->IsAlive()&&
|
||||
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
|
||||
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
|
||||
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
|
||||
closest=&*m;
|
||||
}
|
||||
}
|
||||
|
||||
float targetDirection;
|
||||
float targetDirection;
|
||||
|
||||
if(closest!=nullptr){
|
||||
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
|
||||
targetDirection=dirToEnemy;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
|
||||
}else{
|
||||
float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y;
|
||||
targetDirection=dirToMouse;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
|
||||
}
|
||||
if(closest!=nullptr){
|
||||
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
|
||||
targetDirection=dirToEnemy;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
|
||||
}else{
|
||||
float dirToMouse=geom2d::line<float>(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<SwordSlash>(0.125f,"swordslash.png"s,"Thief.Auto Attack.DamageMult"_F,"Thief.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Thief.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
|
||||
game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Thief.Auto Attack.DamageMult"_F,"Thief.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Thief.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
|
||||
|
||||
SetState(State::SWING_SWORD);
|
||||
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<ShineEffect>(p->GetPos()+vf2d{4.f,4.f},0.5f,0.5f,"shine.png",1.5f,vf2d{},WHITE,util::random(2*PI),PI/2,true));
|
||||
p->ApplyIframes("Thief.Ability 2.Initial Wait"_F+"Thief.Ability 2.Ending Wait"_F+"Thief.Ability 2.Completed Dash Extra Iframe Time"_F);
|
||||
#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<ShineEffect>(p->GetPos()+vf2d{4.f,4.f},0.5f,0.5f,"shine.png",1.5f,vf2d{},WHITE,util::random(2*PI),PI/2,true));
|
||||
p->ApplyIframes("Thief.Ability 2.Initial Wait"_F+"Thief.Ability 2.Ending Wait"_F+"Thief.Ability 2.Completed Dash Extra Iframe Time"_F);
|
||||
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<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(WHITE,GREEN,util::random(1))));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#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<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(WHITE,GREEN,util::random(1))));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#pragma endregion
|
||||
}
|
@ -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<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(BLACK,RED,util::random(1))));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#pragma endregion
|
||||
#pragma region Trapper Ability 1 (Mark Target)
|
||||
Trapper::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
std::optional<std::weak_ptr<Monster>>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<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return 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<Effect>(p->GetPos()+vf2d{8,util::random(2*PI)}.cart(),util::random_range(0.1f,0.4f),"circle.png",p->OnUpperLevel(),vf2d{size,size},0.3f,vf2d{util::random_range(-6.f,6.f),util::random_range(-8.f,-1.f)},PixelLerp(BLACK,RED,util::random(1))));
|
||||
}
|
||||
return true;
|
||||
};
|
||||
#pragma endregion
|
||||
#pragma region Trapper Ability 1 (Mark Target)
|
||||
Trapper::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
std::optional<std::weak_ptr<Monster>>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<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{255,255,255,uint8_t(util::random_range(60,150))},0.f,0.f,true),true);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return 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
|
||||
}
|
@ -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
|
||||
|
@ -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<Monster>&m:MONSTER_LIST){
|
||||
if(m->IsAlive()&&
|
||||
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
|
||||
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
|
||||
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
|
||||
closest=&*m;
|
||||
}
|
||||
}
|
||||
if(GetState()!=State::SPIN){
|
||||
bool attack=false;
|
||||
Monster*closest=nullptr;
|
||||
float closest_dist=999999;
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
if(m->IsAlive()&&
|
||||
geom2d::overlaps(geom2d::circle<float>(GetPos(),attack_range*GetSizeMult()*12),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))&&
|
||||
geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length()<closest_dist){
|
||||
closest_dist=geom2d::line<float>(GetWorldAimingLocation(),m->GetPos()).length();
|
||||
closest=&*m;
|
||||
}
|
||||
}
|
||||
|
||||
float targetDirection;
|
||||
float targetDirection;
|
||||
|
||||
if(closest!=nullptr){
|
||||
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
|
||||
targetDirection=dirToEnemy;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
|
||||
}else{
|
||||
float dirToMouse=geom2d::line<float>(GetPos(),GetWorldAimingLocation()).vector().polar().y;
|
||||
targetDirection=dirToMouse;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToMouse);
|
||||
}
|
||||
if(closest!=nullptr){
|
||||
float dirToEnemy=geom2d::line<float>(GetPos(),closest->GetPos()).vector().polar().y;
|
||||
targetDirection=dirToEnemy;
|
||||
SetAnimationBasedOnTargetingDirection("SWINGSWORD",dirToEnemy);
|
||||
}else{
|
||||
float dirToMouse=geom2d::line<float>(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<SwordSlash>(0.125f,"swordslash.png"s,"Warrior.Auto Attack.DamageMult"_F,"Warrior.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Warrior.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
|
||||
game->AddEffect(std::make_unique<SwordSlash>(0.125f,"swordslash.png"s,"Warrior.Auto Attack.DamageMult"_F,"Warrior.Auto Attack.SwordSlashSweepAngle"_F,vf2d{0.9f,0.9f}*"Warrior.Auto Attack.Range"_F/100.f,0.1f,vf2d{0.f,0.f},WHITE,targetDirection));
|
||||
|
||||
SetState(State::SWING_SWORD);
|
||||
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<Effect>(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F));
|
||||
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
|
||||
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle<float>(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))){
|
||||
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<Bullet>(p->GetPos(),bulletVel,"Warrior.Ability 3.Radius"_F,p->GetAttack()*"Warrior.Ability 3.DamageMult"_F,"sonicslash.png",p->upperLevel,true,"Warrior.Ability 3.Lifetime"_F,true,true,WHITE,vf2d{"Warrior.Ability 3.Radius"_F/30,"Warrior.Ability 3.Radius"_F/30}));
|
||||
game->SetupWorldShake("Warrior.Ability 3.ShakeTime"_F);
|
||||
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<Effect>(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F));
|
||||
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
|
||||
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
|
||||
for(std::shared_ptr<Monster>&m:MONSTER_LIST){
|
||||
if(m->GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m->GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle<float>(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle<float>(m->GetPos(),m->GetSizeMult()*12))){
|
||||
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<Bullet>(p->GetPos(),bulletVel,"Warrior.Ability 3.Radius"_F,p->GetAttack()*"Warrior.Ability 3.DamageMult"_F,"sonicslash.png",p->upperLevel,true,"Warrior.Ability 3.Lifetime"_F,true,true,WHITE,vf2d{"Warrior.Ability 3.Radius"_F/30,"Warrior.Ability 3.Radius"_F/30}));
|
||||
game->SetupWorldShake("Warrior.Ability 3.ShakeTime"_F);
|
||||
SoundEffect::PlaySFX("Warrior Sonic Slash",SoundEffect::CENTERED);
|
||||
return true;
|
||||
};
|
||||
#pragma endregion
|
||||
}
|
@ -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::line<float>targetLine{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)};
|
||||
const float LeapMaxRange{"Witch.Right Click Ability.Leap Velocity"_F*"Witch.Right Click Ability.Leap Max Range Time"_F};
|
||||
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<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
|
||||
if(curseTarget.has_value()&&!curseTarget.value().expired()){
|
||||
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_ptr<Monster>m,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<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true);
|
||||
}
|
||||
return true;
|
||||
}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::line<float>targetLine{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)};
|
||||
const float LeapMaxRange{"Witch.Right Click Ability.Leap Velocity"_F*"Witch.Right Click Ability.Leap Max Range Time"_F};
|
||||
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<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 1.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
|
||||
if(curseTarget.has_value()&&!curseTarget.value().expired()){
|
||||
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_ptr<Monster>m,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<Effect>(geom2d::line<float>(p->GetPos(),targetPos).rpoint(drawDist),0.f,"mark_trail.png",p->OnUpperLevel(),fadeInTime,fadeOutTime,vf2d{effectSize,effectSize},vf2d{},Pixel{100,0,155,uint8_t(util::random_range(0,120))},0.f,0.f),true);
|
||||
}
|
||||
return true;
|
||||
}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
|
||||
}
|
@ -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<float>{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}.length(),0.f,"Wizard.Right Click Ability.TeleportRange"_F/100*24);
|
||||
if(dist<"Wizard.Right Click Ability.TilesMin"_I*12)return false;
|
||||
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::rect<float>collisionRect=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<float>(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<Effect>(p->GetPos()+vf2d{(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12,(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12},util::random("Wizard.Right Click Ability.ParticleLifetimeMax"_F)+"Wizard.Right Click Ability.ParticleLifetimeMin"_F,"circle.png",p->upperLevel,"Wizard.Right Click Ability.ParticleSize"_F,"Wizard.Right Click Ability.ParticleFadetime"_F,vf2d{util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F,util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F},"Wizard.Right Click Ability.ParticleColor"_Pixel));
|
||||
}
|
||||
SoundEffect::PlaySFX("Wizard Teleport",SoundEffect::CENTERED);
|
||||
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<Meteor>(pos,3,"meteor.png",p->OnUpperLevel(),vf2d{"Wizard.Ability 3.MeteorRadius"_F/100/4,"Wizard.Ability 3.MeteorRadius"_F/100/4},"Wizard.Ability 3.MeteorFadeoutTime"_F));
|
||||
return true;
|
||||
};
|
||||
#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<float>{p->GetPos(),p->GetWorldAimingLocation(Player::USE_WALK_DIR,Player::INVERTED)}.length(),0.f,"Wizard.Right Click Ability.TeleportRange"_F/100*24);
|
||||
if(dist<"Wizard.Right Click Ability.TilesMin"_I*12)return false;
|
||||
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::rect<float>collisionRect=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<float>(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<Effect>(p->GetPos()+vf2d{(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12,(util::random("Wizard.Right Click Ability.ParticleRange"_F/100*2)-"Wizard.Right Click Ability.ParticleRange"_F/100)*12},util::random("Wizard.Right Click Ability.ParticleLifetimeMax"_F)+"Wizard.Right Click Ability.ParticleLifetimeMin"_F,"circle.png",p->upperLevel,"Wizard.Right Click Ability.ParticleSize"_F,"Wizard.Right Click Ability.ParticleFadetime"_F,vf2d{util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F,util::random("Wizard.Right Click Ability.ParticleSpeedMax"_F*2)+"Wizard.Right Click Ability.ParticleSpeedMin"_F},"Wizard.Right Click Ability.ParticleColor"_Pixel));
|
||||
}
|
||||
SoundEffect::PlaySFX("Wizard Teleport",SoundEffect::CENTERED);
|
||||
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<Meteor>(pos,3,"meteor.png",p->OnUpperLevel(),vf2d{"Wizard.Ability 3.MeteorRadius"_F/100/4,"Wizard.Ability 3.MeteorRadius"_F/100/4},"Wizard.Ability 3.MeteorFadeoutTime"_F));
|
||||
return true;
|
||||
};
|
||||
#pragma endregion
|
||||
}
|
@ -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
|
||||
|
@ -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
|
||||
|
Binary file not shown.
BIN
Adventures in Lestoria/assets/poison_bottle.png
Normal file
BIN
Adventures in Lestoria/assets/poison_bottle.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 777 B |
BIN
Adventures in Lestoria/assets/poison_pool.png
Normal file
BIN
Adventures in Lestoria/assets/poison_pool.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 752 B |
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user