diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj b/Adventures in Lestoria/Adventures in Lestoria.vcxproj index eafc87cf..e3ed1d0b 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj @@ -780,6 +780,10 @@ + + + + diff --git a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters index 0aef3de4..a6471bda 100644 --- a/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters +++ b/Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters @@ -1127,6 +1127,9 @@ Source Files\Effects + + Source Files\Bullet Types + diff --git a/Adventures in Lestoria/AdventuresInLestoria.cpp b/Adventures in Lestoria/AdventuresInLestoria.cpp index 3fdad925..cf9660f2 100644 --- a/Adventures in Lestoria/AdventuresInLestoria.cpp +++ b/Adventures in Lestoria/AdventuresInLestoria.cpp @@ -568,7 +568,6 @@ void AiL::HandleUserInput(float fElapsedTime){ if(abs(player->movementVelocity.x)>abs(player->movementVelocity.y)){ //Greater Horizontal movement. if(player->movementVelocity.x!=0.f){ player->SetFacingDirection(player->movementVelocity.x>0?RIGHT:LEFT); - player->SetLastReleasedMovementKey(player->GetFacingDirection()); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ player->UpdateWalkingAnimation(player->GetFacingDirection(),animationSpd); } @@ -576,7 +575,6 @@ void AiL::HandleUserInput(float fElapsedTime){ }else{ //Greater Vertical movement. if(player->movementVelocity.y!=0.f){ player->SetFacingDirection(player->movementVelocity.y>0?DOWN:UP); - player->SetLastReleasedMovementKey(player->GetFacingDirection()); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ player->UpdateWalkingAnimation(player->GetFacingDirection(),animationSpd); } @@ -594,7 +592,6 @@ void AiL::HandleUserInput(float fElapsedTime){ } } if(UpReleased()){ - player->SetLastReleasedMovementKey(UP); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ if(RightHeld()){ player->UpdateWalkingAnimation(RIGHT); @@ -608,7 +605,6 @@ void AiL::HandleUserInput(float fElapsedTime){ } } if(RightReleased()){ - player->SetLastReleasedMovementKey(RIGHT); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ if(UpHeld()){ player->UpdateWalkingAnimation(UP); @@ -622,7 +618,6 @@ void AiL::HandleUserInput(float fElapsedTime){ } } if(LeftReleased()){ - player->SetLastReleasedMovementKey(LEFT); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ if(RightHeld()){ player->UpdateWalkingAnimation(RIGHT); @@ -636,7 +631,6 @@ void AiL::HandleUserInput(float fElapsedTime){ } } if(DownReleased()){ - player->SetLastReleasedMovementKey(DOWN); if(player->GetState()==State::NORMAL||player->GetState()==State::PREP_CAST){ if(RightHeld()){ player->UpdateWalkingAnimation(RIGHT); @@ -655,7 +649,7 @@ void AiL::HandleUserInput(float fElapsedTime){ } if(setIdleAnimation){ - switch(player->GetLastReleasedMovementKey()){ + switch(player->GetFacingDirection()){ case UP:{ player->UpdateIdleAnimation(UP); }break; @@ -2908,6 +2902,7 @@ void AiL::ChangePlayerClass(Class cl){ player.reset(NEW Witch(player.get())); }break; } + player->myClass={player->GetClassName()}; player->SetLevel(level); player->levelCap=levelCap; player->stats=previousStats; diff --git a/Adventures in Lestoria/Bullet.cpp b/Adventures in Lestoria/Bullet.cpp index 813c0f99..2c397989 100644 --- a/Adventures in Lestoria/Bullet.cpp +++ b/Adventures in Lestoria/Bullet.cpp @@ -266,4 +266,8 @@ void Bullet::Deactivate(){ const double Bullet::GetTimeAlive()const{ return aliveTime; +} + +const double Bullet::GetAliveTime()const{ + return aliveTime; } \ No newline at end of file diff --git a/Adventures in Lestoria/Bullet.h b/Adventures in Lestoria/Bullet.h index b890a50e..edca8806 100644 --- a/Adventures in Lestoria/Bullet.h +++ b/Adventures in Lestoria/Bullet.h @@ -40,6 +40,7 @@ All rights reserved. #include "olcUTIL_Animate2D.h" #include "Monster.h" #include "DEFINES.h" +using HitList=std::unordered_set>; enum class BulletType{ UNDEFINED, @@ -99,7 +100,7 @@ protected: public: Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; - std::set>hitList; + HitList hitList; virtual ~Bullet()=default; Bullet(vf2d pos,vf2d vel,float radius,int damage,bool upperLevel,bool friendly=false,Pixel col=WHITE,vf2d scale={1,1},float image_angle=0.f); //Initializes a bullet with an animation. @@ -127,4 +128,5 @@ public: const BulletType GetBulletType()const; const bool IsActivated()const; const bool IsDeactivated()const; + const double GetAliveTime()const; }; \ No newline at end of file diff --git a/Adventures in Lestoria/BulletTypes.h b/Adventures in Lestoria/BulletTypes.h index 695be991..1a893364 100644 --- a/Adventures in Lestoria/BulletTypes.h +++ b/Adventures in Lestoria/BulletTypes.h @@ -261,4 +261,19 @@ private: SpellCircle indicator; const float knockbackAmt; float lastTrailEffect{}; +}; + +//While not a bullet directly, the DeadlyDash class generates a bunch of afterimages and collision checks. +struct DeadlyDash:public Bullet{ + DeadlyDash(vf2d startPos,vf2d endPos,float radius,float afterImagesLingeringTime,int damage,float knockbackAmt,bool upperLevel,bool friendly,float afterImagesSpreadDist,const std::string&animation,Pixel col); +protected: + void Draw(const Pixel blendCol)const override; +private: + const vf2d startPos; + const vf2d endPos; + const float checkRadius; + const float afterImagesSpreadDist; + const float afterImagesLingeringTime; + const float knockbackAmt; + std::string animation; }; \ No newline at end of file diff --git a/Adventures in Lestoria/DeadlyDash.cpp b/Adventures in Lestoria/DeadlyDash.cpp new file mode 100644 index 00000000..f8fc286f --- /dev/null +++ b/Adventures in Lestoria/DeadlyDash.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 +#include "util.h" +#include "AdventuresInLestoria.h" + +INCLUDE_ANIMATION_DATA +INCLUDE_game + +DeadlyDash::DeadlyDash(vf2d startPos,vf2d endPos,float radius,float afterImagesLingeringTime,int damage,float knockbackAmt,bool upperLevel,bool friendly,float afterImagesSpreadDist,const std::string&animation,Pixel col) +:animation(animation),startPos(startPos),endPos(endPos),checkRadius(radius),afterImagesSpreadDist(afterImagesSpreadDist),afterImagesLingeringTime(afterImagesLingeringTime),knockbackAmt(knockbackAmt),Bullet({},{},0.f,damage,"circle.png",upperLevel,true,INFINITE,false,friendly,col,{1.f,1.f},0.f){ + const geom2d::lineafterImageLine{startPos,endPos}; + const int afterImageCount{int(afterImageLine.length()/afterImagesSpreadDist)+1}; + lifetime=afterImageCount*afterImagesLingeringTime; + for(int i:std::ranges::iota_view(0,afterImageCount)){ + const float offsetDist{afterImagesSpreadDist*(i+1)}; + const vf2d afterImagePos{geom2d::line{startPos,endPos}.rpoint(offsetDist)}; + if(friendly){ + const HurtList&hitList{game->HurtNotHit(afterImagePos,radius,damage,this->hitList,OnUpperLevel(),GetZ(),HurtType::MONSTER)}; + for(auto&[entityPtr,wasHit]:hitList){ + if(wasHit){ + Monster*monster{std::get(entityPtr)}; + monster->ProximityKnockback(afterImagePos,knockbackAmt); + this->hitList.insert(monster); + } + } + }else{ + const HurtList&hitList{game->HurtNotHit(afterImagePos,radius,damage,this->hitList,OnUpperLevel(),GetZ(),HurtType::PLAYER)}; + for(auto&[entityPtr,wasHit]:hitList){ + if(wasHit){ + Player*player{std::get(entityPtr)}; + player->ProximityKnockback(afterImagePos,knockbackAmt); + } + } + } + } +} + +void DeadlyDash::Draw(const Pixel blendCol)const{ + const geom2d::lineafterImageLine{startPos,endPos}; + const int afterImageCount{int(afterImageLine.length()/afterImagesSpreadDist)+1}; + for(int i:std::ranges::iota_view(0,afterImageCount)){ + const float fadeTimeBegins{(i+1)*afterImagesLingeringTime}; + uint8_t alpha{255U}; + if(GetAliveTime()>fadeTimeBegins)alpha=std::max(0.f,util::lerp(255,0,(GetAliveTime()-fadeTimeBegins)/afterImagesLingeringTime)); + const Animate2D::FrameSequence&animation{ANIMATION_DATA[this->animation]}; + const float animationFrameTimer{i*animation.m_fFrameDuration}; + + const float offsetDist{afterImagesSpreadDist*(i+1)}; + const vf2d drawPos{geom2d::line{startPos,endPos}.rpoint(offsetDist)}; + const Animate2D::Frame&frame{animation.GetFrame(animationFrameTimer)}; + game->SetDecalMode(DecalMode::ADDITIVE); + game->view.DrawPartialRotatedDecal(drawPos,frame.GetSourceImage()->Decal(),0.f,frame.GetSourceRect().size/2,frame.GetSourceRect().pos,frame.GetSourceRect().size,{game->GetPlayer()->GetSizeMult(),game->GetPlayer()->GetSizeMult()},{col.r,col.g,col.b,alpha}); + game->SetDecalMode(DecalMode::NORMAL); + } +} \ No newline at end of file diff --git a/Adventures in Lestoria/InventoryCreator.cpp b/Adventures in Lestoria/InventoryCreator.cpp index 4dde8f68..9a9807fa 100644 --- a/Adventures in Lestoria/InventoryCreator.cpp +++ b/Adventures in Lestoria/InventoryCreator.cpp @@ -139,7 +139,7 @@ std::function [](InventoryScrollableWindowComponent&component,ITCategory cat){ std::vector>weapons; std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::shared_ptritem){return item->IsWeapon();}); - std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(weapons),[](std::shared_ptritem){return item->GetEnhancementInfo().size()>0&&game->GetCurrentChapter()>=item->GetEnhancementInfo()[0].chapterAvailable&&item->GetClass()==game->GetPlayer()->GetClassName()&&item->IsWeapon();}); + std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(weapons),[](std::shared_ptritem){return item->GetEnhancementInfo().size()>0&&game->GetCurrentChapter()>=item->GetEnhancementInfo()[0].chapterAvailable&&item->GetClass().count(game->GetPlayer()->GetClassName())&&item->IsWeapon();}); std::sort(weapons.begin(),weapons.end(),[](const std::weak_ptr&it1,const std::weak_ptr&it2){ return ItemSortRules::GetItemSortRanking(it1) [](InventoryScrollableWindowComponent&component,ITCategory cat){ std::vector>armor; std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::shared_ptritem){return item->IsArmor();}); - std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(armor),[](std::shared_ptritem){return item->GetEnhancementInfo().size()>0&&game->GetCurrentChapter()>=item->GetEnhancementInfo()[0].chapterAvailable&&item->GetClass()==game->GetPlayer()->GetClassName()&&item->IsArmor();}); + std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(armor),[](std::shared_ptritem){return item->GetEnhancementInfo().size()>0&&game->GetCurrentChapter()>=item->GetEnhancementInfo()[0].chapterAvailable&&item->GetClass().count(game->GetPlayer()->GetClassName())&&item->IsArmor();}); std::sort(armor.begin(),armor.end(),[](const std::weak_ptr&it1,const std::weak_ptr&it2){ return ItemSortRules::GetItemSortRanking(it1) INCLUDE_game INCLUDE_DATA @@ -148,7 +149,7 @@ void ItemInfo::InitializeItems(){ Stats minStats; Stats maxStats; bool useDuringCast=false; - std::string equippableClass=""; + std::unordered_setequippableClass; EventName useSound; for(auto&[itemKey,itemValue]:data[key].GetKeys()){ std::string keyName=itemKey; @@ -190,8 +191,10 @@ void ItemInfo::InitializeItems(){ useSound=data[key][keyName].GetString(); }else if(keyName=="Class"){ - equippableClass=data[key][keyName].GetString(); - classutils::StringToClass(equippableClass); //If this errors out then we specified an invalid class! + for(int i:std::ranges::iota_view(0U,data[key][keyName].GetValueCount())){ + equippableClass.insert(data[key][keyName].GetString(i)); + classutils::StringToClass(data[key][keyName].GetString(i)); //If this errors out then we specified an invalid class! + } }else if(keyName.starts_with("Alternative Name")){ if(ITEM_CONVERSIONS.count(data[key][keyName].GetString()))ERR(std::format("Item {} already exists in Item Conversion database! Cannot add a duplicate entry!",data[key][keyName].GetString())); @@ -1258,11 +1261,11 @@ const std::vector>Inventory::GetInventory(){ return itemList; } -const std::string&ItemInfo::GetClass()const{ - return equippableClass.length()==0?game->GetPlayer()->GetClassName():equippableClass; +const std::unordered_set&ItemInfo::GetClass()const{ + return equippableClass.size()==0?game->GetPlayer()->GetMyClass():equippableClass; } -const std::string&Item::GetClass()const{ +const std::unordered_set&Item::GetClass()const{ return it->GetClass(); } diff --git a/Adventures in Lestoria/Item.h b/Adventures in Lestoria/Item.h index 6222ea08..4f2570df 100644 --- a/Adventures in Lestoria/Item.h +++ b/Adventures in Lestoria/Item.h @@ -47,6 +47,7 @@ All rights reserved. #include "CraftingRequirement.h" #include "FunctionPriming.h" #include "IT.h" +#include class AiL; class ItemInfo; @@ -226,7 +227,7 @@ public: const bool CanBeSold()const; const bool CanBePurchased()const; const Stats&RandomStats()const; - const std::string&GetClass()const; + const std::unordered_set&GetClass()const; void RandomizeStats(); const bool HasRandomizedStats()const; const EnhancementInfo&GetEnhancementInfo()const; @@ -330,7 +331,7 @@ class ItemInfo{ //If true, this item's action is activated at the beginning of the cast instead of after the cast completes. bool useDuringCast=false; //If blank, any class can equip this item. - std::string equippableClass=""; + std::unordered_setequippableClass; Stats minStats; Stats maxStats; private: @@ -371,7 +372,7 @@ public: const Stats GetMinStats()const; const Stats GetMaxStats()const; //Get the class that can equip this item. - const std::string&GetClass()const; + const std::unordered_set&GetClass()const; Stats RandomizeStats(); }; diff --git a/Adventures in Lestoria/Player.cpp b/Adventures in Lestoria/Player.cpp index f2d3505c..5cb6212a 100644 --- a/Adventures in Lestoria/Player.cpp +++ b/Adventures in Lestoria/Player.cpp @@ -86,12 +86,12 @@ InputGroup Player::KEY_ITEM3; std::vector>Player::moneyListeners; Player::Player() - :lastReleasedMovementKey(DOWN),facingDirection(DOWN),state(State::NORMAL){ + :facingDirection(DOWN),state(State::NORMAL){ Initialize(); } Player::Player(Player*player) - :pos(player->GetPos()),vel(player->GetVelocity()),iframe_time(player->iframe_time),lastReleasedMovementKey(DOWN), + :pos(player->GetPos()),vel(player->GetVelocity()),iframe_time(player->iframe_time), facingDirection(DOWN),state(State::NORMAL){ Initialize(); } @@ -501,8 +501,16 @@ void Player::Update(float fElapsedTime){ SetAdditiveBlending(!IsUsingAdditiveBlending()); } if(deadlyDashAfterDashTimer<=0.f){ + geom2d::line mouseDir{GetPos(),GetWorldAimingLocation()}; + vf2d clampedMousePolarDir{util::pointTo(GetPos(),GetWorldAimingLocation()).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(); SoundEffect::PlaySFX("Deadly Dash",GetPos()); - SetPos(deadlyDashEndingPos); + 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; SetState(State::NORMAL); SetAdditiveBlending(false); game->SetupWorldShake(0.3f); @@ -774,7 +782,7 @@ bool Player::CanAct(){ } const bool Player::CanAct(const Ability&ability){ - return knockUpTimer==0&&!ability.waitForRelease&&(ability.canCancelCast||state!=State::CASTING)&&state!=State::ANIMATION_LOCK&&state!=State::DEADLYDASH&& + return knockUpTimer==0&&!ability.waitForRelease&&(ability.canCancelCast||state!=State::CASTING)&&state!=State::ANIMATION_LOCK&&state!=State::DEADLYDASH&&state!=State::RETREAT&& (GameState::STATE==GameState::states[States::GAME_RUN] ||GameState::STATE==GameState::states[States::GAME_HUB]&&!ability.itemAbility); } @@ -867,14 +875,6 @@ Animate2D::Frame Player::GetFrame(){ return animation.GetFrame(internal_animState); } -void Player::SetLastReleasedMovementKey(Key k){ - lastReleasedMovementKey=k; -} - -Key Player::GetLastReleasedMovementKey(){ - return lastReleasedMovementKey; -} - void Player::SetFacingDirection(Key direction){ facingDirection=direction; } @@ -962,6 +962,7 @@ void Player::UpdateWalkingAnimation(Key direction, const float frameMult){ case DOWN:anim=GetWalkSAnimation();break; case LEFT:anim=GetWalkWAnimation();break; } + SetFacingDirection(direction); UpdateAnimation(anim,0,frameMult); } @@ -973,6 +974,7 @@ void Player::UpdateIdleAnimation(Key direction){ case DOWN:anim=GetIdleSAnimation();break; case LEFT:anim=GetIdleWAnimation();break; } + SetFacingDirection(direction); UpdateAnimation(anim); } @@ -1061,12 +1063,15 @@ void Player::SetAnimationBasedOnTarget(const std::string_view animation_name,con } void Player::SetAnimationBasedOnTargetingDirection(const std::string_view animation_name,float targetDirection){ + while(targetDirection<-PI)targetDirection+=2*PI; + while(targetDirection>PI)targetDirection-=2*PI; auto FacingEast=[&](){return targetDirection<=PI/4&&targetDirection>-PI/4;}; auto FacingWest=[&](){return targetDirection>=3*PI/4||targetDirection<-3*PI/4;}; auto FacingSouth=[&](){return targetDirection<=3*PI/4&&targetDirection>PI/4;}; //Assume facing North. char facingChar{'N'}; + Key facingKey{UP}; auto Capitalize=[](const std::string_view str){ std::string newStr; @@ -1075,10 +1080,20 @@ void Player::SetAnimationBasedOnTargetingDirection(const std::string_view animat return newStr; }; - if(FacingSouth())facingChar='S'; - if(FacingEast())facingChar='E'; - if(FacingWest())facingChar='W'; + if(FacingSouth()){ + facingChar='S'; + facingKey=DOWN; + } + if(FacingEast()){ + facingChar='E'; + facingKey=RIGHT; + } + if(FacingWest()){ + facingChar='W'; + facingKey=LEFT; + } + SetFacingDirection(facingKey); UpdateAnimation(std::format("{}_{}_{}",Capitalize(GetClassName()),animation_name,facingChar)); } @@ -1670,4 +1685,37 @@ void Player::SetAdditiveBlending(const bool additiveBlending){ const bool Player::IsUsingAdditiveBlending()const{ return renderedSpriteUsesAdditiveBlending; +} + +vf2d Player::MoveUsingPathfinding(vf2d targetPos){ + float pointMouseDirection=atan2(targetPos.y-GetPos().y,targetPos.x-GetPos().x); + vf2d pointTowardsMouse={cos(pointMouseDirection),sin(pointMouseDirection)}; + float dist{geom2d::line{GetPos(),targetPos}.length()}; + vf2d teleportPoint=GetPos()+pointTowardsMouse*dist; + while(dist>0&&game->HasTileCollision(game->GetCurrentLevel(),teleportPoint)&&CanPathfindTo(GetPos(),teleportPoint,12)){ + dist-=4; + teleportPoint=GetPos()+pointTowardsMouse*dist; + } + vi2d tilePos=vi2d(teleportPoint/float(game->GetCurrentMapData().tilewidth))*game->GetCurrentMapData().tilewidth; + geom2d::rectcollisionRect=game->GetTileCollision(game->GetCurrentLevel(),teleportPoint,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 + + while(dist>0){ + if(CanPathfindTo(GetPos(),teleportPoint,12) + &&(NoTileCollisionExistsHere()||NoPlayerCollisionWithTile())){ + return teleportPoint; + } + dist-=4; + } + return GetPos(); +} + +const std::unordered_set&Player::GetMyClass()const{ + return myClass; } \ No newline at end of file diff --git a/Adventures in Lestoria/Player.h b/Adventures in Lestoria/Player.h index 8855b357..5c1f00ef 100644 --- a/Adventures in Lestoria/Player.h +++ b/Adventures in Lestoria/Player.h @@ -200,7 +200,6 @@ public: //specificClass is a bitwise-combination of classes from the Class enum. It makes sure certain animations only play if you are a certain class.= void UpdateAnimation(std::string animState,int specificClass=ANY,const float frameMult=1.f); Animate2D::Frame GetFrame(); - Key GetLastReleasedMovementKey(); float GetSwordSwingTimer(); bool OnUpperLevel(); void ResetLastCombatTime(); @@ -286,6 +285,8 @@ public: //Flag to make the player character render using additive blending / normal blending. void SetAdditiveBlending(const bool additiveBlending); const bool IsUsingAdditiveBlending()const; + vf2d MoveUsingPathfinding(vf2d targetPos); + const std::unordered_set&GetMyClass()const; private: int hp="Warrior.BaseHealth"_I; int mana="Player.BaseMana"_I; @@ -318,7 +319,6 @@ private: State::State state=State::NORMAL; Animate2D::Animationanimation; Animate2D::AnimationState internal_animState; - Key lastReleasedMovementKey; void AddAnimation(std::string state); std::vectorbuffList; CastInfo castInfo={"",0}; @@ -362,13 +362,13 @@ private: vf2d deadlyDashEndingPos{}; bool renderedSpriteUsesAdditiveBlending{false}; float deadlyDashAdditiveBlendingToggleTimer{}; + std::unordered_setmyClass{}; protected: const float ATTACK_COOLDOWN="Warrior.Auto Attack.Cooldown"_F; const float MAGIC_ATTACK_COOLDOWN="Wizard.Auto Attack.Cooldown"_F; float ARROW_ATTACK_COOLDOWN="Ranger.Auto Attack.Cooldown"_F; void SetSwordSwingTimer(float val); void SetFacingDirection(Key direction); - void SetLastReleasedMovementKey(Key k); void Spin(float duration,float spinSpd); float friction="Player.Friction"_F; float attack_cooldown_timer=0; diff --git a/Adventures in Lestoria/Ranger.cpp b/Adventures in Lestoria/Ranger.cpp index 4cb1c6e8..b32f3203 100644 --- a/Adventures in Lestoria/Ranger.cpp +++ b/Adventures in Lestoria/Ranger.cpp @@ -87,7 +87,7 @@ void Ranger::InitializeClassAbilities(){ 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->iframe_time=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; diff --git a/Adventures in Lestoria/TODO.txt b/Adventures in Lestoria/TODO.txt index 2fe5491e..c26291a7 100644 --- a/Adventures in Lestoria/TODO.txt +++ b/Adventures in Lestoria/TODO.txt @@ -21,7 +21,7 @@ Add rectangular hitbox posssibility to the game for monsters. (specifically for DEMO ==== -Add Spell Names on info screen. +Enable weapons for the new classes in the weapons crafting menu. PGETinker notes =============== diff --git a/Adventures in Lestoria/Thief.cpp b/Adventures in Lestoria/Thief.cpp index def3875e..e8e010b0 100644 --- a/Adventures in Lestoria/Thief.cpp +++ b/Adventures in Lestoria/Thief.cpp @@ -43,6 +43,7 @@ All rights reserved. #include "config.h" #include "SoundEffect.h" #include "util.h" +#include "BulletTypes.h" INCLUDE_MONSTER_LIST INCLUDE_BULLET_LIST @@ -141,19 +142,13 @@ void Thief::InitializeClassAbilities(){ 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+0.2f); - geom2d::line mouseDir{p->GetPos(),p->GetWorldAimingLocation()}; - p->SetAnimationBasedOnTargetingDirection("DEADLYDASH",mouseDir.vector().polar().y); + 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()); - - vf2d clampedMousePolarDir{util::pointTo(p->GetPos(),p->GetWorldAimingLocation()).polar()}; - clampedMousePolarDir.x=std::max(24.f,mouseDir.length()); - - p->deadlyDashEndingPos=p->GetPos()+clampedMousePolarDir.cart(); 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 diff --git a/Adventures in Lestoria/Version.h b/Adventures in Lestoria/Version.h index 1a7578ca..7a8a4945 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 10116 +#define VERSION_BUILD 10146 #define stringify(a) stringify_(a) #define stringify_(a) #a diff --git a/Adventures in Lestoria/Wizard.cpp b/Adventures in Lestoria/Wizard.cpp index b96b714d..1dc7021d 100644 --- a/Adventures in Lestoria/Wizard.cpp +++ b/Adventures in Lestoria/Wizard.cpp @@ -138,7 +138,7 @@ void Wizard::InitializeClassAbilities(){ p->teleportAnimationTimer="Wizard.Right Click Ability.AnimationTime"_F; p->teleportTarget=teleportPoint; p->teleportStartPosition=p->GetPos(); - p->iframe_time="Wizard.Right Click Ability.IframeTime"_F; + p->ApplyIframes("Wizard.Right Click Ability.IframeTime"_F); for(int i=0;i<"Wizard.Right Click Ability.ParticleCount"_I;i++){ 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)); } diff --git a/Adventures in Lestoria/assets/config/MonsterStrategies.txt b/Adventures in Lestoria/assets/config/MonsterStrategies.txt index f04e2ead..db01c3bd 100644 --- a/Adventures in Lestoria/assets/config/MonsterStrategies.txt +++ b/Adventures in Lestoria/assets/config/MonsterStrategies.txt @@ -550,7 +550,7 @@ MonsterStrategy Backpedal Movespeed = 50% - Charge Knockback Amount = 1.4 + Charge Knockback Amount = 1.0 } Goblin Dagger { diff --git a/Adventures in Lestoria/assets/config/classes/Thief.txt b/Adventures in Lestoria/assets/config/classes/Thief.txt index 72fc9239..9929cdb8 100644 --- a/Adventures in Lestoria/assets/config/classes/Thief.txt +++ b/Adventures in Lestoria/assets/config/classes/Thief.txt @@ -98,9 +98,16 @@ Thief Damage = 4x Range = 450 + Hit Radius = 12px Initial Wait = 0.2s Ending Wait = 0.3s + # Extra amount of iframe time when the dash completes. + Completed Dash Extra Iframe Time = 0.3s + + Deadly Dash After Images Lingering Time = 0.045s + Knockback Amount = 250 + #RGB Values. Color 1 is the circle at full cooldown, Color 2 is the color at empty cooldown. Cooldown Bar Color 1 = 64, 0, 0, 192 Cooldown Bar Color 2 = 128, 0, 0, 192 diff --git a/Adventures in Lestoria/assets/config/items/Weapons.txt b/Adventures in Lestoria/assets/config/items/Weapons.txt index 6e211498..70e03253 100644 --- a/Adventures in Lestoria/assets/config/items/Weapons.txt +++ b/Adventures in Lestoria/assets/config/items/Weapons.txt @@ -4,7 +4,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Warrior + Class = Warrior,Thief # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -138,7 +138,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Ranger + Class = Ranger,Trapper # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -261,7 +261,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Wizard + Class = Wizard,Witch # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -384,7 +384,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Warrior + Class = Warrior,Thief # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -507,7 +507,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Ranger + Class = Ranger,Trapper # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -630,7 +630,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Wizard + Class = Wizard,Witch # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -753,7 +753,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Warrior + Class = Warrior,Thief # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -867,7 +867,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Ranger + Class = Ranger,Trapper # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -981,7 +981,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Wizard + Class = Wizard,Witch # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1095,7 +1095,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Warrior + Class = Warrior,Thief # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1209,7 +1209,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Ranger + Class = Ranger,Trapper # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1323,7 +1323,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Wizard + Class = Wizard,Witch # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1437,7 +1437,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Warrior + Class = Warrior,Thief # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1552,7 +1552,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Ranger + Class = Ranger,Trapper # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names @@ -1668,7 +1668,7 @@ Equipment { Slot = Weapon ItemCategory = Equipment - Class = Wizard + Class = Wizard,Witch # Stat Values of the item based on Enhancement level. # See ItemStats.txt for valid stat names diff --git a/x64/Release/Adventures in Lestoria.exe b/x64/Release/Adventures in Lestoria.exe index dde08f6a..513a04d9 100644 Binary files a/x64/Release/Adventures in Lestoria.exe and b/x64/Release/Adventures in Lestoria.exe differ