Fix Wizards and Rangers being able to override iframe timer when using Teleport and Retreat respectively. Remove last released key state. Seemed redundant when animation facing direction exists. Make player animation changes be reflected in the facing direction variable. Add pathfinding to Thief's Deadly Dash attack so it can't go through barriers. Added new class counterpoints as equippable classes for the prior weapons. Release Build 10146.

removeExposedPackKey
sigonasr2 4 months ago
parent 78804d666b
commit b2fc642723
  1. 4
      Adventures in Lestoria/Adventures in Lestoria.vcxproj
  2. 3
      Adventures in Lestoria/Adventures in Lestoria.vcxproj.filters
  3. 9
      Adventures in Lestoria/AdventuresInLestoria.cpp
  4. 4
      Adventures in Lestoria/Bullet.cpp
  5. 4
      Adventures in Lestoria/Bullet.h
  6. 15
      Adventures in Lestoria/BulletTypes.h
  7. 93
      Adventures in Lestoria/DeadlyDash.cpp
  8. 4
      Adventures in Lestoria/InventoryCreator.cpp
  9. 15
      Adventures in Lestoria/Item.cpp
  10. 7
      Adventures in Lestoria/Item.h
  11. 78
      Adventures in Lestoria/Player.cpp
  12. 6
      Adventures in Lestoria/Player.h
  13. 2
      Adventures in Lestoria/Ranger.cpp
  14. 2
      Adventures in Lestoria/TODO.txt
  15. 11
      Adventures in Lestoria/Thief.cpp
  16. 2
      Adventures in Lestoria/Version.h
  17. 2
      Adventures in Lestoria/Wizard.cpp
  18. 2
      Adventures in Lestoria/assets/config/MonsterStrategies.txt
  19. 7
      Adventures in Lestoria/assets/config/classes/Thief.txt
  20. 30
      Adventures in Lestoria/assets/config/items/Weapons.txt
  21. BIN
      x64/Release/Adventures in Lestoria.exe

@ -780,6 +780,10 @@
</SubType>
</ClCompile>
<ClCompile Include="DamageNumber.cpp" />
<ClCompile Include="DeadlyDash.cpp">
<SubType>
</SubType>
</ClCompile>
<ClCompile Include="DeathMenu.cpp">
<SubType>
</SubType>

@ -1127,6 +1127,9 @@
<ClCompile Include="ShineEffect.cpp">
<Filter>Source Files\Effects</Filter>
</ClCompile>
<ClCompile Include="DeadlyDash.cpp">
<Filter>Source Files\Bullet Types</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="cpp.hint" />

@ -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;

@ -266,4 +266,8 @@ void Bullet::Deactivate(){
const double Bullet::GetTimeAlive()const{
return aliveTime;
}
const double Bullet::GetAliveTime()const{
return aliveTime;
}

@ -40,6 +40,7 @@ All rights reserved.
#include "olcUTIL_Animate2D.h"
#include "Monster.h"
#include "DEFINES.h"
using HitList=std::unordered_set<std::variant<Monster*,Player*>>;
enum class BulletType{
UNDEFINED,
@ -99,7 +100,7 @@ protected:
public:
Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState;
std::set<std::variant<Player*,Monster*>>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;
};

@ -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;
};

@ -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 <ranges>
#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::line<float>afterImageLine{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<float>{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<Monster*>(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<Player*>(entityPtr)};
player->ProximityKnockback(afterImagePos,knockbackAmt);
}
}
}
}
}
void DeadlyDash::Draw(const Pixel blendCol)const{
const geom2d::line<float>afterImageLine{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<float>{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);
}
}

@ -139,7 +139,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
[](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>weapons;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(weapons),[](std::shared_ptr<Item>item){return item->IsWeapon();});
std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(weapons),[](std::shared_ptr<Item>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>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<Item>&it1,const std::weak_ptr<Item>&it2){
return ItemSortRules::GetItemSortRanking(it1)<ItemSortRules::GetItemSortRanking(it2);
@ -180,7 +180,7 @@ std::function<void(InventoryScrollableWindowComponent&component,ITCategory cat)>
[](InventoryScrollableWindowComponent&component,ITCategory cat){
std::vector<std::weak_ptr<Item>>armor;
std::copy_if(Inventory::get("Equipment").begin(),Inventory::get("Equipment").end(),std::back_inserter(armor),[](std::shared_ptr<Item>item){return item->IsArmor();});
std::copy_if(Inventory::blacksmithInventory.begin(),Inventory::blacksmithInventory.end(),std::back_inserter(armor),[](std::shared_ptr<Item>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>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<Item>&it1,const std::weak_ptr<Item>&it2){
return ItemSortRules::GetItemSortRanking(it1)<ItemSortRules::GetItemSortRanking(it2);

@ -50,6 +50,7 @@ All rights reserved.
#ifndef __EMSCRIPTEN__
#include "steam/isteamuserstats.h"
#endif
#include <ranges>
INCLUDE_game
INCLUDE_DATA
@ -148,7 +149,7 @@ void ItemInfo::InitializeItems(){
Stats minStats;
Stats maxStats;
bool useDuringCast=false;
std::string equippableClass="";
std::unordered_set<std::string>equippableClass;
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<std::shared_ptr<Item>>Inventory::GetInventory(){
return itemList;
}
const std::string&ItemInfo::GetClass()const{
return equippableClass.length()==0?game->GetPlayer()->GetClassName():equippableClass;
const std::unordered_set<std::string>&ItemInfo::GetClass()const{
return equippableClass.size()==0?game->GetPlayer()->GetMyClass():equippableClass;
}
const std::string&Item::GetClass()const{
const std::unordered_set<std::string>&Item::GetClass()const{
return it->GetClass();
}

@ -47,6 +47,7 @@ All rights reserved.
#include "CraftingRequirement.h"
#include "FunctionPriming.h"
#include "IT.h"
#include <unordered_set>
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<std::string>&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_set<std::string>equippableClass;
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<std::string>&GetClass()const;
Stats RandomizeStats();
};

@ -86,12 +86,12 @@ InputGroup Player::KEY_ITEM3;
std::vector<std::weak_ptr<MenuComponent>>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<float>{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::rect<float>collisionRect=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<float>(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<std::string>&Player::GetMyClass()const{
return myClass;
}

@ -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<std::string>&GetMyClass()const;
private:
int hp="Warrior.BaseHealth"_I;
int mana="Player.BaseMana"_I;
@ -318,7 +319,6 @@ private:
State::State state=State::NORMAL;
Animate2D::Animation<std::string>animation;
Animate2D::AnimationState internal_animState;
Key lastReleasedMovementKey;
void AddAnimation(std::string state);
std::vector<Buff>buffList;
CastInfo castInfo={"",0};
@ -362,13 +362,13 @@ private:
vf2d deadlyDashEndingPos{};
bool renderedSpriteUsesAdditiveBlending{false};
float deadlyDashAdditiveBlendingToggleTimer{};
std::unordered_set<std::string>myClass{};
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;

@ -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;

@ -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
===============

@ -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<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+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

@ -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

@ -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<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));
}

@ -550,7 +550,7 @@ MonsterStrategy
Backpedal Movespeed = 50%
Charge Knockback Amount = 1.4
Charge Knockback Amount = 1.0
}
Goblin Dagger
{

@ -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

@ -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

Loading…
Cancel
Save