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.
This commit is contained in:
parent
78804d666b
commit
b2fc642723
@ -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;
|
||||
};
|
93
Adventures in Lestoria/DeadlyDash.cpp
Normal file
93
Adventures in Lestoria/DeadlyDash.cpp
Normal file
@ -0,0 +1,93 @@
|
||||
#pragma region License
|
||||
/*
|
||||
License (OLC-3)
|
||||
~~~~~~~~~~~~~~~
|
||||
|
||||
Copyright 2024 Joshua Sigona <sigonasr2@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions or derivations of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions or derivative works in binary form must reproduce the above
|
||||
copyright notice. This list of conditions and the following disclaimer must be
|
||||
reproduced in the documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its contributors may
|
||||
be used to endorse or promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
||||
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
||||
OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
|
||||
SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||
TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
|
||||
BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
||||
ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
SUCH DAMAGE.
|
||||
|
||||
Portions of this software are copyright © 2024 The FreeType
|
||||
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
|
||||
#include "BulletTypes.h"
|
||||
#include <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
|
||||
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user