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_ptr- item){return item->IsWeapon();});
- std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(weapons),[](std::shared_ptr
- item){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_ptr
- item){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_ptr
- item){return item->IsArmor();});
- std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(armor),[](std::shared_ptr
- item){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_ptr
- item){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