The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
188 lines
8.3 KiB
188 lines
8.3 KiB
#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 "Class.h"
|
|
#include "DEFINES.h"
|
|
#include "Player.h"
|
|
#include "Effect.h"
|
|
#include "AdventuresInLestoria.h"
|
|
#include "config.h"
|
|
#include "SoundEffect.h"
|
|
#include "BulletTypes.h"
|
|
#include "util.h"
|
|
#include <ranges>
|
|
|
|
INCLUDE_MONSTER_LIST
|
|
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";
|
|
}
|
|
|
|
SETUP_CLASS(Witch)
|
|
|
|
void Witch::OnUpdate(float fElapsedTime){
|
|
if(attack_cooldown_timer>0){
|
|
idle_n="WITCH_IDLE_ATTACK_N";
|
|
idle_e="WITCH_IDLE_ATTACK_E";
|
|
idle_s="WITCH_IDLE_ATTACK_S";
|
|
idle_w="WITCH_IDLE_ATTACK_W";
|
|
walk_n="WITCH_ATTACK_N";
|
|
walk_e="WITCH_ATTACK_E";
|
|
walk_s="WITCH_ATTACK_S";
|
|
walk_w="WITCH_ATTACK_W";
|
|
} else {
|
|
idle_n="WITCH_IDLE_N";
|
|
idle_e="WITCH_IDLE_E";
|
|
idle_s="WITCH_IDLE_S";
|
|
idle_w="WITCH_IDLE_W";
|
|
walk_n="WITCH_WALK_N";
|
|
walk_e="WITCH_WALK_E";
|
|
walk_s="WITCH_WALK_S";
|
|
walk_w="WITCH_WALK_W";
|
|
}
|
|
if(GetState()==State::CASTING){
|
|
switch(GetFacingDirection()){
|
|
case UP:{
|
|
UpdateAnimation("WITCH_CAST_N",WIZARD|WITCH);
|
|
}break;
|
|
case DOWN:{
|
|
UpdateAnimation("WITCH_CAST_S",WIZARD|WITCH);
|
|
}break;
|
|
case LEFT:{
|
|
UpdateAnimation("WITCH_CAST_W",WIZARD|WITCH);
|
|
}break;
|
|
case RIGHT:{
|
|
UpdateAnimation("WITCH_CAST_E",WIZARD|WITCH);
|
|
}break;
|
|
}
|
|
}
|
|
}
|
|
|
|
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("Witch 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);
|
|
}
|
|
SoundEffect::PlaySFX("Curse of Pain",curseTarget.value().lock()->GetPos());
|
|
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 (Curse of Death)
|
|
Witch::ability3.action=
|
|
[](Player*p,vf2d pos={}){
|
|
std::optional<std::weak_ptr<Monster>>curseTarget{Monster::GetNearestMonster(pos,"Witch.Ability 3.Casting Range"_F/100.f*24,p->OnUpperLevel(),p->GetZ())};
|
|
if(curseTarget.has_value()&&!curseTarget.value().expired()){
|
|
const float curseDuration{"Witch.Ability 3.Curse Duration"_F};
|
|
const float damageAmpMult{"Witch.Ability 3.Damage Amplification"_F/100.f};
|
|
curseTarget.value().lock()->AddBuff(BuffType::DAMAGE_AMPLIFICATION,curseDuration,damageAmpMult);
|
|
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_range(0.6f,1.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{162,0,0,uint8_t(util::random_range(150,255))},0.f,0.f),true);
|
|
}
|
|
SoundEffect::PlaySFX("Curse of Death",curseTarget.value().lock()->GetPos());
|
|
return true;
|
|
}else return false;
|
|
};
|
|
#pragma endregion
|
|
} |