Item Attribute systems reworked from being an enum class to being a class. Added support for the Buff Item Script to modify items. Implemented Damage Reduction proposal.
This commit is contained in:
parent
51cbf81204
commit
668a5ca1b1
@ -35,18 +35,131 @@ Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
||||
All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
#include "AttributableStat.h"
|
||||
#include "olcUTIL_DataFile.h"
|
||||
#include "DEFINES.h"
|
||||
#include "Item.h"
|
||||
#include "Player.h"
|
||||
#include "Monster.h"
|
||||
|
||||
INCLUDE_DATA
|
||||
|
||||
ItemAttributable&ItemAttributable::operator+=(Stats&rhs){
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
A(a)+=rhs.get_readOnly(a);
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
A(value)+=rhs.get_readOnly(value);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
ItemAttributable&ItemAttributable::operator+=(ItemAttributable&rhs){
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
A(a)+=rhs.get_readOnly(a);
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
A(value)+=rhs.get_readOnly(value);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
ItemAttribute::ItemAttribute(std::string_view name,bool isPct,std::string_view modifies)
|
||||
:name(name),isPct(isPct),modifies(modifies){}
|
||||
|
||||
void ItemAttribute::Initialize(){
|
||||
for(auto&[key,size]:DATA["Stats"]){
|
||||
std::string modifies="";
|
||||
if(DATA["Stats"][key].HasProperty("Modifies")){
|
||||
modifies=DATA["Stats"][key]["Modifies"].GetString();
|
||||
}
|
||||
attributes.insert(key,ItemAttribute(key,DATA["Stats"][key]["Percentage"].GetBool(),modifies));
|
||||
}
|
||||
attributes.SetInitialized();
|
||||
}
|
||||
|
||||
ItemAttribute&ItemAttribute::Get(const std::string_view name,const std::optional<std::variant<Player*,Monster*>>target){
|
||||
attributes.at(std::string(name)).target=target;
|
||||
return attributes.at(std::string(name));
|
||||
}
|
||||
const std::string_view ItemAttribute::Name()const{
|
||||
return name;
|
||||
}
|
||||
const bool ItemAttribute::DisplayAsPercent()const{
|
||||
return isPct;
|
||||
}
|
||||
|
||||
//WARNING! Implemented for map sorting!!!
|
||||
const bool ItemAttribute::operator<(const ItemAttribute&rhs)const{
|
||||
return name<rhs.name;
|
||||
};
|
||||
|
||||
const std::string_view ItemAttribute::Modifies()const{
|
||||
return modifies;
|
||||
};
|
||||
|
||||
float&operator+=(float&lhs,const ItemAttribute&rhs){
|
||||
if(rhs.target){
|
||||
#pragma region Acquire Stats based on Object Type
|
||||
float statModifierTotal=0;
|
||||
std::vector<Buff>statUpBuffs;
|
||||
const ItemAttributable*stats=nullptr;
|
||||
if(std::holds_alternative<Player*>(rhs.target.value())){
|
||||
statUpBuffs=std::get<Player*>(rhs.target.value())->GetBuffs(STAT_UP);
|
||||
stats=&std::get<Player*>(rhs.target.value())->GetStats();
|
||||
}else{
|
||||
statUpBuffs=std::get<Monster*>(rhs.target.value())->GetBuffs(STAT_UP);
|
||||
stats=&std::get<Monster*>(rhs.target.value())->GetStats();
|
||||
}
|
||||
for(const Buff&b:statUpBuffs){
|
||||
if(b.attr.count(rhs)){
|
||||
statModifierTotal+=b.intensity;
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
if(rhs.DisplayAsPercent()){ //This is a percentage-based stat modifier.
|
||||
if(rhs.Modifies().length()>0){
|
||||
lhs+=stats->A_Read(rhs.Modifies())*statModifierTotal;
|
||||
}else{
|
||||
lhs+=stats->A_Read(rhs)*statModifierTotal;
|
||||
}
|
||||
}else{ //This is a flat stat modifier.
|
||||
lhs+=statModifierTotal;
|
||||
}
|
||||
}else{
|
||||
ERR("WARNING! Did not specify a target using the Get() ItemAttribute parameter!");
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
float&operator-=(float&lhs,const ItemAttribute&rhs){
|
||||
if(rhs.target){
|
||||
#pragma region Acquire Stats based on Object Type
|
||||
float statModifierTotal=0;
|
||||
std::vector<Buff>statUpBuffs;
|
||||
const ItemAttributable*stats=nullptr;
|
||||
if(std::holds_alternative<Player*>(rhs.target.value())){
|
||||
statUpBuffs=std::get<Player*>(rhs.target.value())->GetBuffs(STAT_UP);
|
||||
stats=&std::get<Player*>(rhs.target.value())->GetStats();
|
||||
}else{
|
||||
statUpBuffs=std::get<Monster*>(rhs.target.value())->GetBuffs(STAT_UP);
|
||||
stats=&std::get<Monster*>(rhs.target.value())->GetStats();
|
||||
}
|
||||
for(const Buff&b:statUpBuffs){
|
||||
if(b.attr.count(rhs)){
|
||||
statModifierTotal+=b.intensity;
|
||||
}
|
||||
}
|
||||
#pragma endregion
|
||||
if(rhs.DisplayAsPercent()){ //This is a percentage-based stat modifier.
|
||||
if(rhs.Modifies().length()>0){
|
||||
lhs-=stats->A_Read(rhs.Modifies())*statModifierTotal;
|
||||
}else{
|
||||
lhs-=stats->A_Read(rhs)*statModifierTotal;
|
||||
}
|
||||
}else{ //This is a flat stat modifier.
|
||||
lhs-=statModifierTotal;
|
||||
}
|
||||
}else{
|
||||
ERR("WARNING! Did not specify a target using the Get() ItemAttribute parameter!");
|
||||
}
|
||||
return lhs;
|
||||
}
|
||||
|
||||
const bool ItemAttribute::operator==(const ItemAttribute&rhs)const{
|
||||
return name==rhs.name;
|
||||
};
|
||||
@ -37,67 +37,43 @@ All rights reserved.
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
#include "olcPixelGameEngine.h"
|
||||
#include "safemap.h"
|
||||
#include <variant>
|
||||
|
||||
using namespace std::string_view_literals;
|
||||
|
||||
#define A(attr) get(attr)
|
||||
#define A_Read(attr) get_readOnly(attr)
|
||||
|
||||
namespace DisplayType{
|
||||
enum DisplayType{
|
||||
DISPLAY_AS_NUMBER=0,
|
||||
DISPLAY_AS_PERCENT=1,
|
||||
};
|
||||
}
|
||||
struct AttributeData{
|
||||
class Player;
|
||||
class Monster;
|
||||
|
||||
class ItemAttribute{
|
||||
friend class Crawler;
|
||||
std::string name;
|
||||
DisplayType::DisplayType displayAsPercent;
|
||||
bool isPct;
|
||||
std::string modifies="";
|
||||
std::optional<std::variant<Player*,Monster*>>target;
|
||||
static void Initialize();
|
||||
public:
|
||||
inline static safemap<std::string,ItemAttribute>attributes;
|
||||
ItemAttribute(std::string_view name,bool isPct,std::string_view modifies=""sv);
|
||||
static ItemAttribute&Get(const std::string_view name,const std::optional<std::variant<Player*,Monster*>>target={});
|
||||
const std::string_view Name()const;
|
||||
const std::string_view Modifies()const;
|
||||
const bool DisplayAsPercent()const;
|
||||
const bool operator<(const ItemAttribute&rhs)const; //WARNING! Implemented for map sorting!!!
|
||||
friend float&operator+=(float&lhs,const ItemAttribute&rhs);
|
||||
friend float&operator-=(float&lhs,const ItemAttribute&rhs);
|
||||
const bool operator==(const ItemAttribute&rhs)const;
|
||||
};
|
||||
|
||||
enum class ItemAttribute{
|
||||
/*////////////////////////////////////////////*/
|
||||
/*//////*/ENUM_START,/*///////////////////////*/
|
||||
/*////////////////////////////////////////////*/
|
||||
//////////////
|
||||
defense,
|
||||
health,
|
||||
attack,
|
||||
moveSpdPct,
|
||||
cdrPct,
|
||||
critPct,
|
||||
critDmgPct,
|
||||
healthPct, //Percentage of health boost
|
||||
healthPctRecoveryPer6sec, //Percentage of health recovered every 6 seconds.
|
||||
healthPctRecoveryPer4sec, //Percentage of health recovered every 4 seconds.
|
||||
damageReductionPct, //Percentage of damage reduced directly.
|
||||
attackPct, //Percentage of damage increased by.
|
||||
|
||||
/////////NOTE: When adding a new item stat, provide its display name in the map below.
|
||||
/*////////////////////////////////////////////*/
|
||||
/*//////*/ENUM_END/*//////////////////////////*/
|
||||
/*////////////////////////////////////////////*/
|
||||
};
|
||||
|
||||
class Stats;
|
||||
struct Stats;
|
||||
|
||||
class ItemAttributable{
|
||||
friend class ItemInfo;
|
||||
protected:
|
||||
std::map<ItemAttribute,int>attributes;
|
||||
private:
|
||||
inline static std::map<std::string,ItemAttribute>stringToAttribute;
|
||||
inline static std::map<ItemAttribute,AttributeData>data{
|
||||
{ItemAttribute::defense,{"Defense",DisplayType::DISPLAY_AS_NUMBER}},
|
||||
{ItemAttribute::health,{"Health",DisplayType::DISPLAY_AS_NUMBER}},
|
||||
{ItemAttribute::attack,{"Attack",DisplayType::DISPLAY_AS_NUMBER}},
|
||||
{ItemAttribute::moveSpdPct,{"Move Spd",DisplayType::DISPLAY_AS_PERCENT}},
|
||||
{ItemAttribute::cdrPct,{"CDR",DisplayType::DISPLAY_AS_PERCENT}},
|
||||
{ItemAttribute::critPct,{"Crit Rate",DisplayType::DISPLAY_AS_PERCENT}},
|
||||
{ItemAttribute::critDmgPct,{"Crit Dmg",DisplayType::DISPLAY_AS_PERCENT}},
|
||||
{ItemAttribute::healthPct,{"Health %",DisplayType::DISPLAY_AS_PERCENT}}, //Percentage of health boost
|
||||
{ItemAttribute::healthPctRecoveryPer6sec,{"HP6 Recovery %",DisplayType::DISPLAY_AS_NUMBER}}, //Percentage of health recovered every 6 seconds.
|
||||
{ItemAttribute::healthPctRecoveryPer4sec,{"HP4 Recovery %",DisplayType::DISPLAY_AS_NUMBER}}, //Percentage of health recovered every 4 seconds.
|
||||
{ItemAttribute::damageReductionPct,{"Damage Reduction",DisplayType::DISPLAY_AS_PERCENT}}, //Percentage of damage reduced directly.
|
||||
{ItemAttribute::attackPct,{"Attack %",DisplayType::DISPLAY_AS_PERCENT}}, //Percentage of damage increased by.
|
||||
};
|
||||
std::map<ItemAttribute,float>attributes;
|
||||
public:
|
||||
ItemAttributable&operator+=(ItemAttributable&rhs);
|
||||
ItemAttributable&operator+=(Stats&rhs);
|
||||
@ -106,22 +82,20 @@ public:
|
||||
inline void copyTo(ItemAttributable&target){
|
||||
target.attributes=attributes;
|
||||
}
|
||||
inline int&get(ItemAttribute a){
|
||||
inline float&get(std::string_view a){
|
||||
return get(ItemAttribute::Get(a));
|
||||
}
|
||||
inline float&get(const ItemAttribute&a){
|
||||
return attributes[a];
|
||||
}
|
||||
inline const int get_readOnly(ItemAttribute a)const{
|
||||
inline const float get_readOnly(std::string_view a)const{
|
||||
return get_readOnly(ItemAttribute::Get(a));
|
||||
}
|
||||
inline const float get_readOnly(const ItemAttribute&a)const{
|
||||
if(attributes.count(a)){
|
||||
return attributes.at(a);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
inline static AttributeData GetDisplayInfo(ItemAttribute a){
|
||||
return data[a];
|
||||
}
|
||||
inline static std::string GetAttributeName(ItemAttribute attr){
|
||||
return data[attr].name;
|
||||
}
|
||||
inline static ItemAttribute GetAttributeFromString(std::string attrName){
|
||||
return stringToAttribute[attrName];
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@ -36,9 +36,11 @@ All rights reserved.
|
||||
*/
|
||||
#pragma endregion
|
||||
#pragma once
|
||||
#include <set>
|
||||
#include "AttributableStat.h"
|
||||
|
||||
enum BuffType{
|
||||
ATTACK_UP,
|
||||
ATTACK_PCT_UP,
|
||||
STAT_UP,
|
||||
DAMAGE_REDUCTION,
|
||||
SLOWDOWN,
|
||||
BLOCK_SLOWDOWN,
|
||||
@ -54,9 +56,18 @@ struct Buff{
|
||||
float timeBetweenTicks=1;
|
||||
float intensity=1;
|
||||
float nextTick=0;
|
||||
std::set<ItemAttribute> attr;
|
||||
std::function<void(Crawler*,int)>repeatAction;
|
||||
inline Buff(BuffType type,float duration,float intensity)
|
||||
:type(type),duration(duration),intensity(intensity){}
|
||||
inline Buff(BuffType type,float duration,float intensity,std::set<ItemAttribute> attr)
|
||||
:type(type),duration(duration),intensity(intensity),attr(attr){}
|
||||
inline Buff(BuffType type,float duration,float intensity,std::set<std::string> attr)
|
||||
:type(type),duration(duration),intensity(intensity){
|
||||
for(const std::string&s:attr){
|
||||
this->attr.insert(ItemAttribute::attributes.at(s));
|
||||
}
|
||||
}
|
||||
inline Buff(BuffType type,float duration,float intensity,float timeBetweenTicks,std::function<void(Crawler*,int)>repeatAction)
|
||||
:type(type),duration(duration),intensity(intensity),nextTick(duration-timeBetweenTicks),timeBetweenTicks(timeBetweenTicks),repeatAction(repeatAction){}
|
||||
};
|
||||
@ -61,14 +61,14 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
characterMenuWindow->ADD("Equip Slot Outline",MenuComponent)({{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
|
||||
characterMenuWindow->ADD("Character Rotating Display",CharacterRotatingDisplay)({{135,28},{90,windowSize.y-48}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal())END;
|
||||
|
||||
const static std::array<ItemAttribute,7>displayAttrs{
|
||||
ItemAttribute::health,
|
||||
ItemAttribute::attack,
|
||||
ItemAttribute::defense,
|
||||
ItemAttribute::moveSpdPct,
|
||||
ItemAttribute::cdrPct,
|
||||
ItemAttribute::critPct,
|
||||
ItemAttribute::critDmgPct,
|
||||
const static std::array<std::string,7>displayAttrs{
|
||||
"Health",
|
||||
"Attack",
|
||||
"Defense",
|
||||
"Move Spd %",
|
||||
"CDR",
|
||||
"Crit Rate",
|
||||
"Crit Dmg",
|
||||
};
|
||||
|
||||
|
||||
@ -85,8 +85,8 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(false);
|
||||
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(false);
|
||||
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true);
|
||||
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
||||
for(int counter=0;const std::string&attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
|
||||
statDisplayLabel->SetStatChangeAmt(0);
|
||||
}
|
||||
equipmentWindowOpened=false;
|
||||
@ -96,10 +96,9 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
equipSelectionSelectButton->Enable(false);
|
||||
|
||||
const static auto GetLabelText=[](ItemAttribute attribute){
|
||||
AttributeData data=ItemAttributable::GetDisplayInfo(attribute);
|
||||
std::string attrStr=data.name+":\n ";
|
||||
std::string attrStr=std::string(attribute.Name())+":\n ";
|
||||
attrStr+=std::to_string(game->GetPlayer()->GetStat(attribute));
|
||||
if(data.displayAsPercent){
|
||||
if(attribute.DisplayAsPercent()){
|
||||
attrStr+="%";
|
||||
}
|
||||
return attrStr;
|
||||
@ -149,8 +148,8 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
}
|
||||
}
|
||||
comp->SetSelected(true);
|
||||
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
||||
for(int counter=0;const std::string&attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
|
||||
statDisplayLabel->SetStatChangeAmt(0);
|
||||
}
|
||||
MenuItemItemButton*equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent->I(A::INDEXED_THEME)]);
|
||||
@ -168,13 +167,13 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
const std::weak_ptr<Item>buttonItem=button->GetItem();
|
||||
std::vector<int>statsBeforeEquip;
|
||||
EquipSlot slot=EquipSlot(button->I(Attribute::EQUIP_TYPE));
|
||||
for(ItemAttribute attribute:displayAttrs){
|
||||
for(const std::string&attribute:displayAttrs){
|
||||
statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute));
|
||||
}
|
||||
std::weak_ptr<Item>equippedItem=Inventory::GetEquip(slot);
|
||||
Inventory::EquipItem(buttonItem,slot);
|
||||
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
||||
for(int counter=0;const std::string&attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
|
||||
int statChangeAmt=game->GetPlayer()->GetStat(attribute)-statsBeforeEquip[counter];
|
||||
statDisplayLabel->SetStatChangeAmt(statChangeAmt);
|
||||
counter++;
|
||||
@ -190,8 +189,8 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
});
|
||||
equip->SetMouseOutFunc(
|
||||
[](MenuFuncData data){
|
||||
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
||||
for(int counter=0;const std::string&attribute:displayAttrs){
|
||||
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+std::string(ItemAttribute::Get(attribute).Name())+" Label");
|
||||
statDisplayLabel->SetStatChangeAmt(0);
|
||||
counter++;
|
||||
}
|
||||
@ -241,10 +240,9 @@ void Menu::InitializeCharacterMenuWindow(){
|
||||
characterMenuWindow->ADD("Stat Display Outline",MenuComponent)({{245,28},{62,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE)END;
|
||||
|
||||
int yOffset=0;
|
||||
for(ItemAttribute attribute:displayAttrs){
|
||||
std::string attrStr=GetLabelText(attribute);
|
||||
AttributeData data=ItemAttributable::GetDisplayInfo(attribute);
|
||||
auto attrLabel=characterMenuWindow->ADD("Attribute "+data.name+" Label",StatLabel)({{245,28+2+float(yOffset)},{62,18}},attribute,1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
|
||||
for(const std::string&attribute:displayAttrs){
|
||||
std::string attrStr=GetLabelText(ItemAttribute::Get(attribute));
|
||||
auto attrLabel=characterMenuWindow->ADD("Attribute "+attribute+" Label",StatLabel)({{245,28+2+float(yOffset)},{62,18}},ItemAttribute::Get(attribute),1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN)END;
|
||||
Menu::AddEquipStatListener(attrLabel);
|
||||
yOffset+=20;
|
||||
}
|
||||
|
||||
@ -131,6 +131,9 @@ Crawler::Crawler()
|
||||
std::string ITEM_SET_CONFIG = CONFIG_PATH + "item_set_config"_S;
|
||||
utils::datafile::Read(DATA,ITEM_SET_CONFIG);
|
||||
|
||||
std::string ITEM_STATS_CONFIG = CONFIG_PATH + "item_stats_config"_S;
|
||||
utils::datafile::Read(DATA,ITEM_STATS_CONFIG);
|
||||
|
||||
for(auto&[key,value]:DATA.GetProperty("ItemConfiguration").GetKeys()){
|
||||
std::string config = DATA["ItemConfiguration"][key].GetString();
|
||||
utils::datafile::Read(DATA,CONFIG_PATH + "item_directory"_S + config);
|
||||
@ -165,6 +168,8 @@ bool Crawler::OnUserCreate(){
|
||||
camera.SetTarget(player->GetPos());
|
||||
camera.SetWorldBoundary({0,0},GetCurrentMap().MapSize*GetCurrentMap().TileSize);
|
||||
camera.EnableWorldBoundary(false);
|
||||
|
||||
ItemAttribute::Initialize();
|
||||
|
||||
ItemInfo::InitializeItems();
|
||||
|
||||
@ -195,6 +200,7 @@ bool Crawler::OnUserCreate(){
|
||||
Inventory::AddItem("Shell Shoes");
|
||||
Inventory::AddItem("Bone Pants");
|
||||
Inventory::AddItem("Bone Gloves");
|
||||
Inventory::AddItem("Elixir of Bear Strength",3);
|
||||
|
||||
LoadLevel(LEVEL_NAMES["starting_map"_S]);
|
||||
ChangePlayerClass(WARRIOR);
|
||||
@ -206,6 +212,8 @@ bool Crawler::OnUserCreate(){
|
||||
|
||||
Merchant::Initialize();
|
||||
|
||||
Stats::InitializeDamageReductionTable();
|
||||
|
||||
utils::datafile::INITIAL_SETUP_COMPLETE=true;
|
||||
|
||||
ValidateGameStatus(); //Checks to make sure everything has been initialized properly.
|
||||
@ -722,7 +730,8 @@ void Crawler::RenderWorld(float fElapsedTime){
|
||||
playerScale.x=120*float(abs(pow(player->teleportAnimationTimer-0.175f,3)));
|
||||
pos=player->teleportStartPosition.lerp(player->teleportTarget,(0.35f-player->teleportAnimationTimer)/0.35f);
|
||||
}
|
||||
view.DrawPartialRotatedDecal(pos+vf2d{0,-player->GetZ()*(std::signbit(scale.y)?-1:1)},player->GetFrame().GetSourceImage()->Decal(),player->GetSpinAngle(),{12,12},player->GetFrame().GetSourceRect().pos,player->GetFrame().GetSourceRect().size,playerScale*scale,player->GetBuffs(BuffType::ATTACK_UP).size()>0?Pixel{255,uint8_t(255*abs(sin(1.4*player->GetBuffs(BuffType::ATTACK_UP)[0].duration))),uint8_t(255*abs(sin(1.4*player->GetBuffs(BuffType::ATTACK_UP)[0].duration)))}:WHITE);
|
||||
const std::vector<Buff>attackBuffs=player->GetStatBuffs({"Attack","Attack %"});
|
||||
view.DrawPartialRotatedDecal(pos+vf2d{0,-player->GetZ()*(std::signbit(scale.y)?-1:1)},player->GetFrame().GetSourceImage()->Decal(),player->GetSpinAngle(),{12,12},player->GetFrame().GetSourceRect().pos,player->GetFrame().GetSourceRect().size,playerScale*scale,attackBuffs.size()>0?Pixel{255,uint8_t(255*abs(sin(1.4*attackBuffs[0].duration))),uint8_t(255*abs(sin(1.4*attackBuffs[0].duration)))}:WHITE);
|
||||
SetDecalMode(DecalMode::NORMAL);
|
||||
if(player->GetState()==State::BLOCK){
|
||||
view.DrawDecal(player->GetPos()-vf2d{12,12},GFX["block.png"].Decal());
|
||||
@ -1244,7 +1253,9 @@ void Crawler::RenderHud(){
|
||||
if("debug_player_info"_I){
|
||||
DrawShadowStringDecal({0,128},player->GetPos().str());
|
||||
DrawShadowStringDecal({0,136},"Spd: "+std::to_string(player->GetMoveSpdMult()));
|
||||
DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).lock()->Amt()));
|
||||
if(!ISBLANK(GetLoadoutItem(0))){
|
||||
DrawShadowStringDecal({0,92},"Loadout Slot 1 Qty: "+std::to_string(GetLoadoutItem(0).lock()->Amt()));
|
||||
}
|
||||
DrawShadowStringDecal({0,1},"Selection: "+Menu::menus[INVENTORY_CONSUMABLES]->selection.str());
|
||||
DrawShadowStringDecal({0,12},"Button Hold Time: "+std::to_string(Menu::menus[INVENTORY_CONSUMABLES]->buttonHoldTime));
|
||||
}}
|
||||
@ -1844,9 +1855,9 @@ void Crawler::ChangePlayerClass(Class cl){
|
||||
player.reset(NEW Witch(player.get()));
|
||||
}break;
|
||||
}
|
||||
player->SetBaseStat(ItemAttribute::health,DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt());
|
||||
player->hp=player->GetBaseStat(ItemAttribute::health);
|
||||
player->SetBaseStat(ItemAttribute::attack,DATA.GetProperty(player->GetClassName()+".BaseAtk").GetInt());
|
||||
player->SetBaseStat("Health",DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt());
|
||||
player->hp=player->GetBaseStat("Health");
|
||||
player->SetBaseStat("Attack",DATA.GetProperty(player->GetClassName()+".BaseAtk").GetInt());
|
||||
player->hpGrowthRate=float(DATA.GetProperty(player->GetClassName()+".HealthGrowthRate").GetReal());
|
||||
player->atkGrowthRate=float(DATA.GetProperty(player->GetClassName()+".AtkGrowthRate").GetReal());
|
||||
player->money=oldMoney;
|
||||
|
||||
@ -568,6 +568,7 @@
|
||||
<Text Include="assets\config\items\items.txt" />
|
||||
<Text Include="assets\config\items\ItemScript.txt" />
|
||||
<Text Include="assets\config\items\ItemSets.txt" />
|
||||
<Text Include="assets\config\items\ItemStats.txt" />
|
||||
<Text Include="assets\config\levels.txt" />
|
||||
<Text Include="assets\config\Monsters.txt" />
|
||||
<Text Include="assets\config\MonsterStrategies.txt" />
|
||||
@ -581,10 +582,10 @@
|
||||
<Text Include="assets\config\story\Chapter 1.txt" />
|
||||
<Text Include="Crawler_Story_Chapter_1 (2).txt" />
|
||||
<Text Include="Crawler_System_Overview.txt" />
|
||||
<Text Include="ItemStats.txt" />
|
||||
<Text Include="NewClasses.txt" />
|
||||
<Text Include="InitialConcept.txt" />
|
||||
<Text Include="Slime_King_Encounter.txt" />
|
||||
<Text Include="StatCalculations.txt" />
|
||||
<Text Include="TODO.txt" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@ -82,6 +82,9 @@
|
||||
<Filter Include="Configurations\Item Merchants">
|
||||
<UniqueIdentifier>{8423ea80-23c9-48cb-a506-6b3bdfa8000e}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Documentation\Mechanics">
|
||||
<UniqueIdentifier>{a54ab812-9474-465f-9f0d-4dceecb9e963}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="olcPixelGameEngine.h">
|
||||
@ -725,9 +728,12 @@
|
||||
<Text Include="TODO.txt">
|
||||
<Filter>Documentation</Filter>
|
||||
</Text>
|
||||
<Text Include="ItemStats.txt">
|
||||
<Text Include="assets\config\items\ItemStats.txt">
|
||||
<Filter>Configurations\Items</Filter>
|
||||
</Text>
|
||||
<Text Include="StatCalculations.txt">
|
||||
<Filter>Documentation\Mechanics</Filter>
|
||||
</Text>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="assets\heart.ico">
|
||||
|
||||
@ -41,6 +41,7 @@ All rights reserved.
|
||||
#include "Crawler.h"
|
||||
#include "Menu.h"
|
||||
#include "Ability.h"
|
||||
#include "AttributableStat.h"
|
||||
|
||||
INCLUDE_game
|
||||
INCLUDE_DATA
|
||||
@ -57,6 +58,7 @@ std::map<std::string,ItemSet>ItemSet::sets;
|
||||
std::map<EquipSlot,std::weak_ptr<Item>>Inventory::equipment;
|
||||
std::map<std::string,EquipSlot>ItemInfo::nameToEquipSlot;
|
||||
int Item::IsBlankStaticCallCounter=0;
|
||||
safemap<int,float>Stats::maxDamageReductionTable;
|
||||
|
||||
ItemInfo::ItemInfo()
|
||||
:customProps({nullptr,nullptr}),img(nullptr){}
|
||||
@ -76,11 +78,6 @@ void ItemInfo::InitializeItems(){
|
||||
nameToEquipSlot["Ring2"]=EquipSlot::RING2;
|
||||
|
||||
InitializeScripts();
|
||||
|
||||
//Setup Strings to Attribute map.
|
||||
for(ItemAttribute attr=ItemAttribute(int(ItemAttribute::ENUM_START)+1);attr<ItemAttribute::ENUM_END;attr=ItemAttribute(int(attr)+1)){
|
||||
ItemAttributable::stringToAttribute[ItemAttributable::GetDisplayInfo(attr).name]=attr;
|
||||
}
|
||||
|
||||
InitializeSets();
|
||||
|
||||
@ -130,7 +127,7 @@ void ItemInfo::InitializeItems(){
|
||||
}else
|
||||
if(keyName=="StatValues"){
|
||||
for(int i=0;i<data[key]["StatValues"].GetValueCount();i++){
|
||||
statValueList.push_back(ItemAttributable::GetAttributeFromString(data[key]["StatValues"].GetString(i)));
|
||||
statValueList.push_back(ItemAttribute::Get(data[key]["StatValues"].GetString(i)));
|
||||
}
|
||||
}else
|
||||
if(keyName=="PartofSet"){
|
||||
@ -259,8 +256,19 @@ void ItemInfo::InitializeScripts(){
|
||||
});
|
||||
return true;
|
||||
};
|
||||
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
if(!DATA.GetProperty("ItemScript.Buff").HasProperty(key)){
|
||||
ERR("WARNING! Buff Item Script does not support Buff "<<std::quoted(value.Name())<<"!");
|
||||
}
|
||||
}
|
||||
|
||||
ITEM_SCRIPTS["Buff"]=[](Crawler*game,ItemProps props){
|
||||
game->GetPlayer()->AddBuff(BuffType::ATTACK_PCT_UP,props.GetFloatProp("Attack %",1),props.GetFloatProp("Attack %",0)/100);
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
float intensity=props.GetFloatProp(key,0);
|
||||
if(ItemAttribute::Get(key).DisplayAsPercent())intensity/=100;
|
||||
game->GetPlayer()->AddBuff(BuffType::STAT_UP,props.GetFloatProp(key,1),intensity,{ItemAttribute::Get(key)});
|
||||
}
|
||||
return true;
|
||||
};
|
||||
ITEM_SCRIPTS["RestoreDuringCast"]=[&](Crawler*game,ItemProps props){
|
||||
@ -612,11 +620,11 @@ void Inventory::EquipItem(const std::weak_ptr<Item>it,EquipSlot slot){
|
||||
if(equippedSlot!=EquipSlot::NONE)UnequipItem(equippedSlot);
|
||||
if(!GetEquip(slot).expired())UnequipItem(slot);
|
||||
Inventory::equipment[slot]=it;
|
||||
PlayerStats::RecalculateEquipStats();
|
||||
game->GetPlayer()->RecalculateEquipStats();
|
||||
};
|
||||
void Inventory::UnequipItem(EquipSlot slot){
|
||||
Inventory::equipment[slot]=Item::BLANK;
|
||||
PlayerStats::RecalculateEquipStats();
|
||||
game->GetPlayer()->RecalculateEquipStats();
|
||||
};
|
||||
EquipSlot Inventory::GetSlotEquippedIn(const std::weak_ptr<Item>it){
|
||||
for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){
|
||||
@ -654,7 +662,7 @@ void ItemInfo::InitializeSets(){
|
||||
datafile&statInfo=setInfo[std::to_string(pieceCount)];
|
||||
Stats bonuses;
|
||||
for(auto&[key,value]:statInfo.GetKeys()){
|
||||
ItemAttribute attr=bonuses.GetAttributeFromString(key);
|
||||
const ItemAttribute&attr=ItemAttribute::Get(key);
|
||||
bonuses.A(attr)=statInfo[key].GetInt(0);
|
||||
}
|
||||
ItemSet::AddSetBonus(setName,pieceCount,bonuses);
|
||||
@ -718,7 +726,7 @@ const std::string Stats::GetStatsString(CompactText compact)const{
|
||||
description+="\n";
|
||||
}
|
||||
}
|
||||
description+=ItemAttributable::GetAttributeName(attr)+": "+std::to_string(val)+(ItemAttributable::GetDisplayInfo(attr).displayAsPercent?"%":"");
|
||||
description+=std::string(attr.Name())+": "+std::to_string(int(val))+(attr.DisplayAsPercent()?"%":"");
|
||||
first=false;
|
||||
}
|
||||
return description;
|
||||
@ -783,4 +791,32 @@ const bool Item::UseDuringCast()const{
|
||||
|
||||
const bool ItemInfo::UseDuringCast()const{
|
||||
return useDuringCast;
|
||||
}
|
||||
|
||||
void Stats::InitializeDamageReductionTable(){
|
||||
float totalReduction=0;
|
||||
maxDamageReductionTable[0]=0;
|
||||
for(int i=1;i<=1000;i++){
|
||||
if(i<=10){
|
||||
totalReduction+=0.01;
|
||||
}else
|
||||
if(i<=30){
|
||||
totalReduction+=0.00'5;
|
||||
}else
|
||||
if(i<=70){
|
||||
totalReduction+=0.00'25;
|
||||
}else
|
||||
if(i<=150){
|
||||
totalReduction+=0.00'125;
|
||||
}else
|
||||
if(i<=310){
|
||||
totalReduction+=0.00'0625;
|
||||
}else
|
||||
if(i<=950){
|
||||
totalReduction+=0.00'03125;
|
||||
}else{
|
||||
totalReduction+=0.00'1;
|
||||
}
|
||||
maxDamageReductionTable[i]=totalReduction;
|
||||
}
|
||||
}
|
||||
@ -75,16 +75,21 @@ enum class CompactText{
|
||||
using enum CompactText;
|
||||
|
||||
struct Stats:ItemAttributable{
|
||||
static safemap<int,float>maxDamageReductionTable;
|
||||
static void InitializeDamageReductionTable();
|
||||
public:
|
||||
inline static const float&GetDamageReductionPct(int defense){
|
||||
return maxDamageReductionTable.at(std::clamp(defense,0,1000));
|
||||
}
|
||||
inline Stats&operator+=(Stats&rhs){
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
A(a)+=rhs.get_readOnly(a);
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
A(value)+=rhs.get_readOnly(value);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
inline Stats&operator+=(ItemAttributable&rhs){
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
A(a)+=rhs.get_readOnly(a);
|
||||
for(auto&[key,value]:ItemAttribute::attributes){
|
||||
A(value)+=rhs.get_readOnly(value);
|
||||
}
|
||||
return *this;
|
||||
};
|
||||
|
||||
@ -1,13 +0,0 @@
|
||||
# Provide any stat name followed by its increase amount to add it to the set. Valid stat names and value examples are below:
|
||||
#
|
||||
# Defense = 5 # Adds 5 defense
|
||||
# Health = 3 # Adds 3 Health
|
||||
# Attack = 7 # Adds 7 attack
|
||||
# Move Spd = 10% # Grants 10% more movement speed.
|
||||
# CDR = 14% # Grants 14% cooldown reduction.
|
||||
# Crit Rate = 5% # Grants 5% crit rate.
|
||||
# Crit Dmg = 25% # Grants 25% bonus crit damage.
|
||||
# Health % = 30% # Grants 30% Max health
|
||||
# HP6 Recovery % = 4% # Grants 4% HP recovery every 6 seconds.
|
||||
# HP4 Recovery % = 2% # Grants 2% HP recovery every 4 seconds.
|
||||
# Damage Reduction = 2% # Grants 2% direct damage reduction
|
||||
@ -100,7 +100,7 @@ class Menu:public IAttributable{
|
||||
friend class Crawler;
|
||||
friend struct Player;
|
||||
friend class ItemInfo;
|
||||
friend class PlayerStats;
|
||||
friend class EntityStats;
|
||||
|
||||
float buttonHoldTime=0;
|
||||
vi2d selection={-1,-1};
|
||||
|
||||
@ -60,7 +60,7 @@ safemap<std::string,std::function<void(Monster&,float,std::string)>>STRATEGY_DAT
|
||||
std::map<std::string,Renderable*>MonsterData::imgs;
|
||||
|
||||
Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob):
|
||||
pos(pos),hp(data.GetHealth()),maxhp(data.GetHealth()),atk(data.GetAttack()),moveSpd(data.GetMoveSpdMult()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(DOWN){
|
||||
pos(pos),maxhp(data.GetHealth()),size(data.GetSizeMult()),targetSize(data.GetSizeMult()),strategy(data.GetAIStrategy()),name(data.GetDisplayName()),upperLevel(upperLevel),isBoss(bossMob),facingDirection(DOWN){
|
||||
bool firstAnimation=true;
|
||||
for(std::string&anim:data.GetAnimations()){
|
||||
animation.AddState(anim,ANIMATION_DATA[anim]);
|
||||
@ -69,25 +69,27 @@ Monster::Monster(vf2d pos,MonsterData data,bool upperLevel,bool bossMob):
|
||||
firstAnimation=false;
|
||||
}
|
||||
}
|
||||
stats.A("Health")=data.GetHealth();
|
||||
stats.A("Attack")=data.GetAttack();
|
||||
stats.A("Move Spd %")=data.GetMoveSpdMult();
|
||||
randomFrameOffset=(rand()%1000)/1000.f;
|
||||
}
|
||||
vf2d&Monster::GetPos(){
|
||||
return pos;
|
||||
}
|
||||
int Monster::GetHealth(){
|
||||
return hp;
|
||||
return stats.A("Health");
|
||||
}
|
||||
int Monster::GetAttack(){
|
||||
float mod_atk=float(atk);
|
||||
for(Buff&b:GetBuffs(ATTACK_UP)){
|
||||
mod_atk+=atk*b.intensity;
|
||||
}
|
||||
float mod_atk=float(stats.A("Attack"));
|
||||
mod_atk+=Get("Attack %");
|
||||
mod_atk+=Get("Attack");
|
||||
return int(mod_atk);
|
||||
}
|
||||
float Monster::GetMoveSpdMult(){
|
||||
float mod_moveSpd=moveSpd;
|
||||
float mod_moveSpd=stats.A("Move Spd %");
|
||||
for(Buff&b:GetBuffs(SLOWDOWN)){
|
||||
mod_moveSpd-=moveSpd*b.intensity;
|
||||
mod_moveSpd-=stats.A("Move Spd %")*b.intensity;
|
||||
}
|
||||
return mod_moveSpd;
|
||||
}
|
||||
@ -287,7 +289,8 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
|
||||
for(Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){
|
||||
mod_dmg-=damage*b.intensity;
|
||||
}
|
||||
hp=std::max(0,hp-int(mod_dmg));
|
||||
mod_dmg=std::ceil(mod_dmg);
|
||||
stats.A("Health")=std::max(0.f,stats.A("Health")-int(mod_dmg));
|
||||
if(lastHitTimer>0){
|
||||
damageNumberPtr.get()->damage+=int(mod_dmg);
|
||||
damageNumberPtr.get()->pauseTime=0.4f;
|
||||
@ -299,7 +302,7 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
|
||||
if(!IsAlive()){
|
||||
OnDeath();
|
||||
}else{
|
||||
hp=std::max(1,hp); //Make sure it stays alive if it's supposed to be alive...
|
||||
stats.A("Health")=std::max(1.f,stats.A("Health")); //Make sure it stays alive if it's supposed to be alive...
|
||||
}
|
||||
if(game->InBossEncounter()){
|
||||
game->BossDamageDealt(int(mod_dmg));
|
||||
@ -310,7 +313,7 @@ bool Monster::Hurt(int damage,bool onUpperLevel,float z){
|
||||
}
|
||||
|
||||
bool Monster::IsAlive(){
|
||||
return hp>0||!diesNormally;
|
||||
return stats.A("Health")>0||!diesNormally;
|
||||
}
|
||||
vf2d&Monster::GetTargetPos(){
|
||||
return target;
|
||||
@ -456,4 +459,12 @@ void Monster::OnDeath(){
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const ItemAttributable&Monster::GetStats()const{
|
||||
return stats;
|
||||
}
|
||||
|
||||
ItemAttribute&Monster::Get(std::string_view attr){
|
||||
return ItemAttribute::Get(attr,this);
|
||||
}
|
||||
@ -109,44 +109,6 @@ struct MonsterData{
|
||||
|
||||
struct Monster:IAttributable{
|
||||
friend struct STRATEGY;
|
||||
private:
|
||||
std::string name;
|
||||
vf2d pos;
|
||||
vf2d vel={0,0};
|
||||
float friction=400;
|
||||
vf2d target={0,0};
|
||||
float targetAcquireTimer=0;
|
||||
int hp,maxhp;
|
||||
int atk;
|
||||
float moveSpd;
|
||||
float size;
|
||||
float attackCooldownTimer=0;
|
||||
float queueShotTimer=0;
|
||||
float z=0;
|
||||
float iframe_timer=0;
|
||||
Key facingDirection=DOWN;
|
||||
std::string strategy;
|
||||
State::State state=State::NORMAL;
|
||||
Animate2D::Animation<std::string>animation;
|
||||
Animate2D::AnimationState internal_animState;
|
||||
float randomFrameOffset=0.f;
|
||||
float deathTimer=0.f;
|
||||
std::vector<Buff>buffList;
|
||||
std::string GetDeathAnimationName();
|
||||
bool hasHitPlayer=false;
|
||||
bool canMove=true; //Set to false when stuck due to collisions.
|
||||
bool upperLevel=false;
|
||||
vf2d pathTarget={};
|
||||
std::vector<vf2d>path;
|
||||
int pathIndex=0;
|
||||
float lastHitTimer=0;
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
int phase=0;
|
||||
bool diesNormally=true; //If set to false, the monster death is handled in a special way. Set it to true when it's time to die.
|
||||
float targetSize=0;
|
||||
bool isBoss=false;
|
||||
void OnDeath();
|
||||
protected:
|
||||
public:
|
||||
Monster()=delete;
|
||||
Monster(vf2d pos,MonsterData data,bool upperLevel=false,bool bossMob=false);
|
||||
@ -195,6 +157,45 @@ public:
|
||||
void SetSize(float newSize,bool immediate=true);
|
||||
void SetStrategyDrawFunction(std::function<void(Crawler*)>func);
|
||||
std::function<void(Crawler*)>strategyDraw=[](Crawler*pge){};
|
||||
const ItemAttributable&GetStats()const;
|
||||
private:
|
||||
std::string name;
|
||||
vf2d pos;
|
||||
vf2d vel={0,0};
|
||||
float friction=400;
|
||||
vf2d target={0,0};
|
||||
float targetAcquireTimer=0;
|
||||
int maxhp;
|
||||
ItemAttributable stats;
|
||||
float size;
|
||||
float attackCooldownTimer=0;
|
||||
float queueShotTimer=0;
|
||||
float z=0;
|
||||
float iframe_timer=0;
|
||||
Key facingDirection=DOWN;
|
||||
std::string strategy;
|
||||
State::State state=State::NORMAL;
|
||||
Animate2D::Animation<std::string>animation;
|
||||
Animate2D::AnimationState internal_animState;
|
||||
float randomFrameOffset=0.f;
|
||||
float deathTimer=0.f;
|
||||
std::vector<Buff>buffList;
|
||||
std::string GetDeathAnimationName();
|
||||
bool hasHitPlayer=false;
|
||||
bool canMove=true; //Set to false when stuck due to collisions.
|
||||
bool upperLevel=false;
|
||||
vf2d pathTarget={};
|
||||
std::vector<vf2d>path;
|
||||
int pathIndex=0;
|
||||
float lastHitTimer=0;
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
int phase=0;
|
||||
bool diesNormally=true; //If set to false, the monster death is handled in a special way. Set it to true when it's time to die.
|
||||
float targetSize=0;
|
||||
bool isBoss=false;
|
||||
void OnDeath();
|
||||
ItemAttribute&Get(std::string_view attr);
|
||||
|
||||
private:
|
||||
struct STRATEGY{
|
||||
static int _GetInt(Monster&m,std::string param,std::string strategy,int index=0);
|
||||
|
||||
@ -48,6 +48,8 @@ All rights reserved.
|
||||
#include "Menu.h"
|
||||
#include "GameState.h"
|
||||
#include "MenuComponent.h"
|
||||
#include <string_view>
|
||||
|
||||
|
||||
INCLUDE_MONSTER_DATA
|
||||
INCLUDE_MONSTER_LIST
|
||||
@ -69,9 +71,6 @@ InputGroup Player::KEY_ITEM1;
|
||||
InputGroup Player::KEY_ITEM2;
|
||||
InputGroup Player::KEY_ITEM3;
|
||||
|
||||
ItemAttributable PlayerStats::equipStats;
|
||||
ItemAttributable PlayerStats::baseStats;
|
||||
|
||||
std::set<MenuComponent*>Player::moneyListeners;
|
||||
|
||||
Player::Player()
|
||||
@ -87,15 +86,15 @@ Player::Player(Player*player)
|
||||
|
||||
void Player::Initialize(){
|
||||
Player::GROUND_SLAM_SPIN_TIME="Warrior.Ability 2.SpinTime"_F;
|
||||
SetBaseStat(ItemAttribute::health,hp);
|
||||
SetBaseStat(ItemAttribute::defense,0);
|
||||
SetBaseStat(ItemAttribute::attack,"Warrior.BaseAtk"_I);
|
||||
SetBaseStat(ItemAttribute::moveSpdPct,100);
|
||||
SetBaseStat(ItemAttribute::cdrPct,0);
|
||||
SetBaseStat(ItemAttribute::critPct,0);
|
||||
SetBaseStat(ItemAttribute::critDmgPct,0);
|
||||
SetBaseStat(ItemAttribute::healthPct,0);
|
||||
SetBaseStat(ItemAttribute::healthPctRecoveryPer6sec,0);
|
||||
SetBaseStat("Health",hp);
|
||||
SetBaseStat("Defense",0);
|
||||
SetBaseStat("Attack","Warrior.BaseAtk"_I);
|
||||
SetBaseStat("Move Spd %",100);
|
||||
SetBaseStat("CDR",0);
|
||||
SetBaseStat("Crit Rate",0);
|
||||
SetBaseStat("Crit Dmg",0);
|
||||
SetBaseStat("Health %",0);
|
||||
SetBaseStat("HP6 Recovery %",0);
|
||||
}
|
||||
|
||||
bool Player::SetX(float x){
|
||||
@ -184,7 +183,7 @@ const int Player::GetHealth()const{
|
||||
}
|
||||
|
||||
const int Player::GetMaxHealth()const{
|
||||
return GetStat(ItemAttribute::health);
|
||||
return GetStat("Health");
|
||||
}
|
||||
|
||||
const int Player::GetMana()const{
|
||||
@ -196,22 +195,19 @@ const int Player::GetMaxMana()const{
|
||||
}
|
||||
|
||||
const int Player::GetAttack(){
|
||||
float mod_atk=float(GetStat(ItemAttribute::attack));
|
||||
for(Buff&b:GetBuffs(BuffType::ATTACK_UP)){
|
||||
mod_atk+=GetStat(ItemAttribute::attack)*b.intensity;
|
||||
}
|
||||
for(Buff&b:GetBuffs(BuffType::ATTACK_PCT_UP)){
|
||||
mod_atk+=GetStat(ItemAttribute::attack)*b.intensity;
|
||||
}
|
||||
float mod_atk=float(GetStat("Attack"));
|
||||
mod_atk+=Get("Attack");
|
||||
mod_atk+=Get("Attack %");
|
||||
return int(mod_atk);
|
||||
}
|
||||
|
||||
float Player::GetMoveSpdMult(){
|
||||
float mod_moveSpd=GetStat(ItemAttribute::moveSpdPct)/100.f;
|
||||
for(Buff&b:GetBuffs(BuffType::SLOWDOWN)){
|
||||
float mod_moveSpd=GetStat("Move Spd %")/100.f;
|
||||
mod_moveSpd+=ItemAttribute::Get("Move Spd %",this);
|
||||
for(const Buff&b:GetBuffs(BuffType::SLOWDOWN)){
|
||||
mod_moveSpd-=mod_moveSpd*b.intensity;
|
||||
}
|
||||
for(Buff&b:GetBuffs(BuffType::BLOCK_SLOWDOWN)){
|
||||
for(const Buff&b:GetBuffs(BuffType::BLOCK_SLOWDOWN)){
|
||||
mod_moveSpd-=mod_moveSpd*b.intensity;
|
||||
}
|
||||
return mod_moveSpd;
|
||||
@ -606,11 +602,31 @@ bool Player::HasIframes(){
|
||||
|
||||
bool Player::Hurt(int damage,bool onUpperLevel,float z){
|
||||
if(hp<=0||HasIframes()||OnUpperLevel()!=onUpperLevel||abs(GetZ()-z)>1) return false;
|
||||
if(GetState()==State::BLOCK)damage*=int(1-"Warrior.Right Click Ability.DamageReduction"_F);
|
||||
float mod_dmg=float(damage);
|
||||
lastCombatTime=0;
|
||||
for(Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){
|
||||
mod_dmg-=damage*b.intensity;
|
||||
if(GetState()==State::BLOCK){
|
||||
mod_dmg=0;
|
||||
}else{
|
||||
float otherDmgTaken=1-GetDamageReductionFromBuffs();
|
||||
float armorDmgTaken=1-GetDamageReductionFromArmor();
|
||||
lastCombatTime=0;
|
||||
|
||||
float finalPctDmgTaken=armorDmgTaken*otherDmgTaken;
|
||||
|
||||
if(finalPctDmgTaken<=0.06f){
|
||||
std::cout<<"WARNING! Damage Reduction has somehow ended up below 6%, which is over the cap!"<<std::endl;
|
||||
}
|
||||
|
||||
finalPctDmgTaken=std::max(0.0625f,finalPctDmgTaken);//Apply Damage Cap.
|
||||
|
||||
float minPctDmgReduction=0.00'05f*GetStat("Defense");
|
||||
float finalPctDmgReduction=1-finalPctDmgTaken;
|
||||
|
||||
float pctDmgReductionDiff=finalPctDmgReduction-minPctDmgReduction;
|
||||
float dmgRoll=minPctDmgReduction+util::random(pctDmgReductionDiff);
|
||||
|
||||
mod_dmg*=1-dmgRoll;
|
||||
|
||||
mod_dmg=std::ceil(mod_dmg);
|
||||
}
|
||||
hp=std::max(0,hp-int(mod_dmg));
|
||||
if(lastHitTimer>0){
|
||||
@ -719,6 +735,12 @@ void Player::UpdateIdleAnimation(Key direction){
|
||||
void Player::AddBuff(BuffType type,float duration,float intensity){
|
||||
buffList.push_back(Buff{type,duration,intensity});
|
||||
}
|
||||
void Player::AddBuff(BuffType type,float duration,float intensity,std::set<ItemAttribute>attr){
|
||||
buffList.push_back(Buff{type,duration,intensity,attr});
|
||||
}
|
||||
void Player::AddBuff(BuffType type,float duration,float intensity,std::set<std::string>attr){
|
||||
buffList.push_back(Buff{type,duration,intensity,attr});
|
||||
}
|
||||
void Player::AddBuff(BuffType type,float duration,float intensity,float timeBetweenTicks,std::function<void(Crawler*,int)>repeatAction){
|
||||
buffList.push_back(Buff{type,duration,intensity,timeBetweenTicks,repeatAction});
|
||||
}
|
||||
@ -727,9 +749,9 @@ bool Player::OnUpperLevel(){
|
||||
return upperLevel;
|
||||
}
|
||||
|
||||
std::vector<Buff>Player::GetBuffs(BuffType buff){
|
||||
const std::vector<Buff>Player::GetBuffs(BuffType buff)const{
|
||||
std::vector<Buff>filteredBuffs;
|
||||
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](Buff&b){return b.type==buff;});
|
||||
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(filteredBuffs),[buff](const Buff b){return b.type==buff;});
|
||||
return filteredBuffs;
|
||||
}
|
||||
|
||||
@ -809,7 +831,7 @@ void Player::SetIframes(float duration){
|
||||
}
|
||||
|
||||
bool Player::Heal(int damage){
|
||||
hp=std::clamp(hp+damage,0,GetStat(ItemAttribute::health));
|
||||
hp=std::clamp(hp+damage,0,GetStat("Health"));
|
||||
if(damage>0){
|
||||
DAMAGENUMBER_LIST.push_back(std::make_shared<DamageNumber>(GetPos(),damage,true,HEALTH_GAIN));
|
||||
}
|
||||
@ -873,23 +895,31 @@ void Player::SetItem3UseFunc(Ability a){
|
||||
useItem3=a;
|
||||
};
|
||||
|
||||
const int Player::GetStat(ItemAttribute a)const{
|
||||
return PlayerStats::GetStat(a);
|
||||
const int&Player::GetStat(std::string_view a)const{
|
||||
return GetStat(ItemAttribute::Get(a));
|
||||
}
|
||||
|
||||
const int Player::GetBaseStat(ItemAttribute a)const{
|
||||
return PlayerStats::GetBaseStat(a);
|
||||
const int&Player::GetStat(ItemAttribute a)const{
|
||||
return stats.GetStat(a);
|
||||
}
|
||||
|
||||
const int&Player::GetBaseStat(std::string_view a)const{
|
||||
return GetBaseStat(ItemAttribute::Get(a));
|
||||
}
|
||||
|
||||
const int&Player::GetBaseStat(ItemAttribute a)const{
|
||||
return stats.GetBaseStat(a);
|
||||
}
|
||||
|
||||
|
||||
void PlayerStats::RecalculateEquipStats(){
|
||||
void EntityStats::RecalculateEquipStats(){
|
||||
baseStats.copyTo(equipStats);
|
||||
for(int i=int(EquipSlot::HELMET);i<=int(EquipSlot::RING2);i<<=1){
|
||||
EquipSlot slot=EquipSlot(i);
|
||||
std::weak_ptr<Item>equip=Inventory::GetEquip(slot);
|
||||
if(ISBLANK(equip))continue;
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
equipStats.A(a)+=equip.lock()->GetStats().A_Read(a);
|
||||
for(auto&[key,size]:ItemAttribute::attributes){
|
||||
equipStats.A(key)+=equip.lock()->GetStats().A_Read(key);
|
||||
}
|
||||
}
|
||||
|
||||
@ -897,27 +927,36 @@ void PlayerStats::RecalculateEquipStats(){
|
||||
|
||||
for(const auto&[set,equipCount]:setCounts){
|
||||
const Stats&setStats=set[equipCount];
|
||||
for(ItemAttribute a=ItemAttribute(int(ItemAttribute::ENUM_START)+1);a<ItemAttribute::ENUM_END;a=ItemAttribute(int(a)+1)){
|
||||
equipStats.A(a)+=setStats.A_Read(a);
|
||||
for(auto&[key,size]:ItemAttribute::attributes){
|
||||
equipStats.A(key)+=setStats.A_Read(key);
|
||||
}
|
||||
}
|
||||
for(MenuComponent*component:Menu::equipStatListeners){
|
||||
component->OnEquipStatsUpdate();
|
||||
}
|
||||
}
|
||||
const int PlayerStats::GetStat(ItemAttribute stat){
|
||||
return equipStats.A(stat);
|
||||
const int&EntityStats::GetStat(ItemAttribute stat)const{
|
||||
return equipStats.A_Read(stat);
|
||||
}
|
||||
const int PlayerStats::GetBaseStat(ItemAttribute stat){
|
||||
return baseStats.A(stat);
|
||||
const int&EntityStats::GetStat(std::string_view stat)const{
|
||||
return equipStats.A_Read(stat);
|
||||
}
|
||||
const int&EntityStats::GetBaseStat(ItemAttribute stat)const{
|
||||
return baseStats.A_Read(stat);
|
||||
}
|
||||
const int&EntityStats::GetBaseStat(std::string_view stat)const{
|
||||
return baseStats.A_Read(stat);
|
||||
}
|
||||
|
||||
void PlayerStats::SetBaseStat(ItemAttribute stat,int val){
|
||||
void EntityStats::SetBaseStat(ItemAttribute stat,int val){
|
||||
baseStats.A(stat)=val;
|
||||
RecalculateEquipStats();
|
||||
}
|
||||
void Player::SetBaseStat(std::string_view a,int val){
|
||||
stats.SetBaseStat(ItemAttribute::Get(a),val);
|
||||
}
|
||||
void Player::SetBaseStat(ItemAttribute a,int val){
|
||||
PlayerStats::SetBaseStat(a,val);
|
||||
stats.SetBaseStat(a,val);
|
||||
}
|
||||
|
||||
const std::string&ItemSet::GetSetName()const{
|
||||
@ -926,14 +965,52 @@ const std::string&ItemSet::GetSetName()const{
|
||||
|
||||
uint32_t Player::GetMoney()const{
|
||||
return money;
|
||||
};
|
||||
}
|
||||
void Player::SetMoney(uint32_t newMoney){
|
||||
money=newMoney;
|
||||
for(auto&component:moneyListeners){
|
||||
component->OnPlayerMoneyUpdate(newMoney);
|
||||
}
|
||||
};
|
||||
}
|
||||
void Player::AddMoneyListener(MenuComponent*component){
|
||||
if(moneyListeners.count(component))ERR("WARNING! Trying to add a second copy of component "<<std::quoted(component->GetName()));
|
||||
moneyListeners.insert(component);
|
||||
}
|
||||
|
||||
|
||||
const float Player::GetDamageReductionFromBuffs()const{
|
||||
float dmgReduction=0;
|
||||
for(const Buff&b:GetBuffs(BuffType::DAMAGE_REDUCTION)){
|
||||
dmgReduction+=b.intensity;
|
||||
}
|
||||
return std::min(0.75f,dmgReduction);
|
||||
};
|
||||
const float Player::GetDamageReductionFromArmor()const{
|
||||
float dmgReduction=0;
|
||||
dmgReduction+=Stats::GetDamageReductionPct(GetStat("Defense"));
|
||||
return std::min(0.75f,dmgReduction);
|
||||
};
|
||||
|
||||
void Player::RecalculateEquipStats(){
|
||||
stats.RecalculateEquipStats();
|
||||
}
|
||||
|
||||
const std::vector<Buff>Player::GetStatBuffs(const std::vector<std::string>&attr)const{
|
||||
std::vector<Buff>statUpBuffs;
|
||||
std::copy_if(buffList.begin(),buffList.end(),std::back_inserter(statUpBuffs),[&](const Buff b){
|
||||
return b.type==STAT_UP&&std::find_if(attr.begin(),attr.end(),[&](const std::string&attr){return b.attr.count(ItemAttribute::Get(attr));})!=attr.end();
|
||||
});
|
||||
return statUpBuffs;
|
||||
}
|
||||
|
||||
const ItemAttributable&EntityStats::GetStats()const{
|
||||
return equipStats;
|
||||
}
|
||||
|
||||
const ItemAttributable&Player::GetStats()const{
|
||||
return stats.GetStats();
|
||||
};
|
||||
|
||||
ItemAttribute&Player::Get(std::string_view attr){
|
||||
return ItemAttribute::Get(attr,this);
|
||||
}
|
||||
189
Crawler/Player.h
189
Crawler/Player.h
@ -61,16 +61,18 @@ struct CastInfo{
|
||||
vf2d castPos;
|
||||
};
|
||||
|
||||
class PlayerStats{
|
||||
class EntityStats{
|
||||
friend class Inventory;
|
||||
static ItemAttributable equipStats; //The stats after gear calculations are applied.
|
||||
static ItemAttributable baseStats;
|
||||
|
||||
static void RecalculateEquipStats(); //Called when equipment is updated.
|
||||
ItemAttributable equipStats; //The stats after gear calculations are applied.
|
||||
ItemAttributable baseStats;
|
||||
public:
|
||||
static const int GetStat(ItemAttribute stat); //Get stats with equipment applied.
|
||||
static const int GetBaseStat(ItemAttribute stat);
|
||||
static void SetBaseStat(ItemAttribute stat,int val);
|
||||
void RecalculateEquipStats(); //Called when equipment is updated.
|
||||
const ItemAttributable&GetStats()const;
|
||||
const int&GetStat(ItemAttribute stat)const; //Get stats with equipment applied.
|
||||
const int&GetStat(std::string_view stat)const; //Get stats with equipment applied.
|
||||
const int&GetBaseStat(ItemAttribute stat)const;
|
||||
const int&GetBaseStat(std::string_view stat)const;
|
||||
void SetBaseStat(ItemAttribute a,int val);
|
||||
};
|
||||
|
||||
struct Player{
|
||||
@ -85,76 +87,6 @@ struct Player{
|
||||
friend class State_GameRun;
|
||||
friend class Inventory;
|
||||
friend void ItemOverlay::Draw();
|
||||
private:
|
||||
int hp="Warrior.BaseHealth"_I;
|
||||
int mana="Player.BaseMana"_I,maxmana=mana;
|
||||
float hpGrowthRate="Warrior.HealthGrowthRate"_F;
|
||||
float atkGrowthRate="Warrior.AtkGrowthRate"_F;
|
||||
vf2d pos;
|
||||
float z=0;
|
||||
float size=1.0f;
|
||||
float spin_attack_timer=0;
|
||||
float spin_spd=0;
|
||||
float spin_angle=0;
|
||||
float lastAnimationFlip=0;
|
||||
float manaTickTimer=0;
|
||||
std::pair<std::string,float> notEnoughManaDisplay={"",0.f};
|
||||
float teleportAttemptWaitTime=0; //If a teleport fails, we wait awhile before trying again, it's expensive.
|
||||
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};
|
||||
vf2d movementVelocity={};//This tells us if the player is moving (mostly controlled by user input) since their velocity is not used for regular movement.
|
||||
float lastHitTimer=0; //When this is greater than zero, if we get hit again it adds to our displayed combo number.
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
void Initialize();
|
||||
float iframe_time=0;
|
||||
float lastCombatTime=0;
|
||||
Ability useItem1;
|
||||
Ability useItem2;
|
||||
Ability useItem3;
|
||||
uint32_t money="Player.Starting Money"_I;
|
||||
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;
|
||||
float teleportAnimationTimer=0;
|
||||
vf2d teleportTarget={};
|
||||
vf2d teleportStartPosition={};
|
||||
std::pair<std::string,float> notificationDisplay={"",0.f};
|
||||
bool upperLevel=false;
|
||||
vf2d vel={0,0};
|
||||
float attack_range="Warrior.Auto Attack.Range"_F/100.f;
|
||||
Key facingDirection=DOWN;
|
||||
float swordSwingTimer=0;
|
||||
void CastSpell(Ability&ability);
|
||||
Ability*castPrepAbility;
|
||||
void PrepareCast(Ability&ability);
|
||||
vf2d precastLocation={};
|
||||
void SetVelocity(vf2d vel);
|
||||
const float RETREAT_DISTANCE=24*"Ranger.Right Click Ability.RetreatDistance"_F/100;
|
||||
float RETREAT_TIME="Ranger.Right Click Ability.RetreatTime"_F; //How long the Retreat ability takes.
|
||||
const int RETREAT_GHOST_FRAMES=8;
|
||||
const float RETREAT_GHOST_FRAME_DELAY=0.025f;
|
||||
float ghostFrameTimer=0;
|
||||
float ghostRemoveTimer=0;
|
||||
float blockTimer=0;
|
||||
float retreatTimer=0;
|
||||
std::vector<vf2d>ghostPositions;
|
||||
float rapidFireTimer=0;
|
||||
int remainingRapidFireShots=0;
|
||||
float endZoneStandTime=0;
|
||||
const float RAPID_FIRE_SHOOT_DELAY="Ranger.Ability 1.ArrowDelay"_F;
|
||||
const int RAPID_FIRE_SHOOT_AMOUNT="Ranger.Ability 1.ArrowCount"_I;
|
||||
public:
|
||||
Player();
|
||||
//So this is rather fascinating and only exists because we have the ability to change classes which means we need to initialize a class
|
||||
@ -166,14 +98,19 @@ public:
|
||||
float GetX();
|
||||
float GetY();
|
||||
float GetZ();
|
||||
const int GetStat(ItemAttribute a)const;
|
||||
const int GetBaseStat(ItemAttribute a)const;
|
||||
const int&GetStat(ItemAttribute a)const;
|
||||
const int&GetStat(std::string_view a)const;
|
||||
const int&GetBaseStat(ItemAttribute a)const;
|
||||
const int&GetBaseStat(std::string_view a)const;
|
||||
void SetBaseStat(std::string_view a,int val);
|
||||
void SetBaseStat(ItemAttribute a,int val);
|
||||
const int GetMaxHealth()const;
|
||||
const int GetHealth()const;
|
||||
const int GetMana()const;
|
||||
const int GetMaxMana()const;
|
||||
const int GetAttack();
|
||||
const float GetDamageReductionFromBuffs()const;
|
||||
const float GetDamageReductionFromArmor()const;
|
||||
float GetMoveSpdMult();
|
||||
float GetSizeMult();
|
||||
void SetSizeMult(float size);
|
||||
@ -205,12 +142,17 @@ public:
|
||||
void SetState(State::State newState);
|
||||
|
||||
void AddBuff(BuffType type,float duration,float intensity);
|
||||
void AddBuff(BuffType type,float duration,float intensity,std::set<ItemAttribute>attr);
|
||||
void AddBuff(BuffType type,float duration,float intensity,std::set<std::string>attr);
|
||||
void AddBuff(BuffType type,float duration,float intensity,float timeBetweenTicks,std::function<void(Crawler*,int)>repeatAction);
|
||||
std::vector<Buff>GetBuffs(BuffType buff);
|
||||
const std::vector<Buff>GetBuffs(BuffType buff)const;
|
||||
const std::vector<Buff>GetStatBuffs(const std::vector<std::string>&attr)const;
|
||||
void RemoveBuff(BuffType type); //Removes the first buff found.
|
||||
void RemoveAllBuffs(BuffType type); //Removes all buffs of a certain type.
|
||||
void RemoveAllBuffs(); //Remove every buff.
|
||||
|
||||
void RecalculateEquipStats();
|
||||
|
||||
bool Hurt(int damage,bool onUpperLevel,float z);
|
||||
//Return false if healing was not possible.
|
||||
bool Heal(int damage);
|
||||
@ -261,8 +203,82 @@ public:
|
||||
void SetMoney(uint32_t newMoney);
|
||||
|
||||
void CancelCast();
|
||||
const ItemAttributable&GetStats()const;
|
||||
private:
|
||||
int hp="Warrior.BaseHealth"_I;
|
||||
int mana="Player.BaseMana"_I,maxmana=mana;
|
||||
float hpGrowthRate="Warrior.HealthGrowthRate"_F;
|
||||
float atkGrowthRate="Warrior.AtkGrowthRate"_F;
|
||||
vf2d pos;
|
||||
float z=0;
|
||||
float size=1.0f;
|
||||
float spin_attack_timer=0;
|
||||
float spin_spd=0;
|
||||
float spin_angle=0;
|
||||
float lastAnimationFlip=0;
|
||||
float manaTickTimer=0;
|
||||
std::pair<std::string,float> notEnoughManaDisplay={"",0.f};
|
||||
float teleportAttemptWaitTime=0; //If a teleport fails, we wait awhile before trying again, it's expensive.
|
||||
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};
|
||||
vf2d movementVelocity={};//This tells us if the player is moving (mostly controlled by user input) since their velocity is not used for regular movement.
|
||||
float lastHitTimer=0; //When this is greater than zero, if we get hit again it adds to our displayed combo number.
|
||||
std::shared_ptr<DamageNumber>damageNumberPtr;
|
||||
void Initialize();
|
||||
float iframe_time=0;
|
||||
float lastCombatTime=0;
|
||||
Ability useItem1;
|
||||
Ability useItem2;
|
||||
Ability useItem3;
|
||||
uint32_t money="Player.Starting Money"_I;
|
||||
EntityStats stats;
|
||||
ItemAttribute&Get(std::string_view attr);
|
||||
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;
|
||||
float teleportAnimationTimer=0;
|
||||
vf2d teleportTarget={};
|
||||
vf2d teleportStartPosition={};
|
||||
std::pair<std::string,float> notificationDisplay={"",0.f};
|
||||
bool upperLevel=false;
|
||||
vf2d vel={0,0};
|
||||
float attack_range="Warrior.Auto Attack.Range"_F/100.f;
|
||||
Key facingDirection=DOWN;
|
||||
float swordSwingTimer=0;
|
||||
void CastSpell(Ability&ability);
|
||||
Ability*castPrepAbility;
|
||||
void PrepareCast(Ability&ability);
|
||||
vf2d precastLocation={};
|
||||
void SetVelocity(vf2d vel);
|
||||
const float RETREAT_DISTANCE=24*"Ranger.Right Click Ability.RetreatDistance"_F/100;
|
||||
float RETREAT_TIME="Ranger.Right Click Ability.RetreatTime"_F; //How long the Retreat ability takes.
|
||||
const int RETREAT_GHOST_FRAMES=8;
|
||||
const float RETREAT_GHOST_FRAME_DELAY=0.025f;
|
||||
float ghostFrameTimer=0;
|
||||
float ghostRemoveTimer=0;
|
||||
float blockTimer=0;
|
||||
float retreatTimer=0;
|
||||
std::vector<vf2d>ghostPositions;
|
||||
float rapidFireTimer=0;
|
||||
int remainingRapidFireShots=0;
|
||||
float endZoneStandTime=0;
|
||||
const float RAPID_FIRE_SHOOT_DELAY="Ranger.Ability 1.ArrowDelay"_F;
|
||||
const int RAPID_FIRE_SHOOT_AMOUNT="Ranger.Ability 1.ArrowCount"_I;
|
||||
};
|
||||
|
||||
#pragma region Warrior
|
||||
struct Warrior:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -291,7 +307,9 @@ struct Warrior:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Thief
|
||||
struct Thief:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -320,7 +338,9 @@ struct Thief:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Ranger
|
||||
struct Ranger:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -349,7 +369,9 @@ struct Ranger:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Trapper
|
||||
struct Trapper:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -378,7 +400,9 @@ struct Trapper:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Wizard
|
||||
struct Wizard:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -407,7 +431,9 @@ struct Wizard:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#pragma region Witch
|
||||
struct Witch:Player{
|
||||
static std::string name;
|
||||
static Class cl;
|
||||
@ -436,6 +462,7 @@ struct Witch:Player{
|
||||
std::string&GetIdleSAnimation()override;
|
||||
std::string&GetIdleWAnimation()override;
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#define READFROMCONFIG(class,enum) \
|
||||
class::name=#class".ClassName"_S; \
|
||||
|
||||
@ -106,12 +106,12 @@ void Monster::STRATEGY::SHOOT_AFAR(Monster&m,float fElapsedTime,std::string stra
|
||||
pathfindingDecision=movedX||movedY;
|
||||
m.canMove=movedX&&movedY;
|
||||
}
|
||||
if(!pathfindingDecision){
|
||||
if(!pathfindingDecision&&m.targetAcquireTimer==0){
|
||||
m.StartPathfinding(2.5);
|
||||
}else
|
||||
if(line.length()>=24.f*ConfigInt("Range")/100.f){
|
||||
m.SetState(State::NORMAL);
|
||||
}
|
||||
if((m.path.size()==0&&!m.canMove)||line.length()>=24.f*ConfigInt("Range")/100.f){
|
||||
m.SetState(State::NORMAL);
|
||||
}
|
||||
if(moveTowardsLine.vector().x>0){
|
||||
m.facingDirection=RIGHT;
|
||||
} else {
|
||||
|
||||
@ -240,7 +240,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
m.phase=ConfigInt("StartPhase");
|
||||
}break;
|
||||
case 1:{
|
||||
if(m.hp<=m.maxhp*ConfigFloat("Phase2.Change")/100){
|
||||
if(m.stats.A("Health")<=m.maxhp*ConfigFloat("Phase2.Change")/100){
|
||||
m.phase=2;
|
||||
m.SetSize(ConfigFloat("Phase2.Size")/100,false);
|
||||
TransitionPhase(m.phase);
|
||||
@ -273,7 +273,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}
|
||||
}break;
|
||||
case 2:{
|
||||
if(m.hp<=m.maxhp*ConfigFloat("Phase3.Change")/100){
|
||||
if(m.stats.A("Health")<=m.maxhp*ConfigFloat("Phase3.Change")/100){
|
||||
m.phase=3;
|
||||
m.SetSize(ConfigFloat("Phase3.Size")/100,false);
|
||||
TransitionPhase(m.phase);
|
||||
@ -297,7 +297,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}
|
||||
}break;
|
||||
case 3:{
|
||||
if(m.hp<=m.maxhp*ConfigFloat("Phase4.Change")/100){
|
||||
if(m.stats.A("Health")<=m.maxhp*ConfigFloat("Phase4.Change")/100){
|
||||
m.phase=4;
|
||||
m.SetSize(ConfigFloat("Phase4.Size")/100,false);
|
||||
m.AddBuff(BuffType::SLOWDOWN,99999,ConfigFloat("Phase4.MoveSpdModifier")/100);
|
||||
@ -325,7 +325,7 @@ void Monster::STRATEGY::SLIMEKING(Monster&m,float fElapsedTime,std::string strat
|
||||
}
|
||||
}break;
|
||||
case 4:{
|
||||
if(m.hp<=1){ //HP can't reach 0 when the dies normally flag is on.
|
||||
if(m.stats.A("Health")<=1){ //HP can't reach 0 when the dies normally flag is on.
|
||||
m.phase=5;
|
||||
m.F(A::IFRAME_TIME_UPON_HIT)=1;
|
||||
m.I(A::HITS_UNTIL_DEATH)=int(m.GetSizeMult()*100/ConfigFloat("Phase5.SizeLossPerHit"))-1;
|
||||
|
||||
37
Crawler/StatCalculations.txt
Normal file
37
Crawler/StatCalculations.txt
Normal file
@ -0,0 +1,37 @@
|
||||
Level Up stats:
|
||||
Sword Class: Hp 9 Atk 2
|
||||
Bow Class: Hp 7 Atk 3
|
||||
Staff Class: Hp 5 Atk 4
|
||||
|
||||
Armor Damage Reduction has 2 values. min. reduction and max. reduction.
|
||||
Every time the player takes damage the damage gets reduced by a random amount between those 2 values.
|
||||
min reduction:
|
||||
1 def = 0.05%
|
||||
max reduction:
|
||||
first 10 def = 1 def = 1%
|
||||
10%
|
||||
10-30 = 1 def = 0.5%
|
||||
10%
|
||||
30-70 = 1 def = 0.25%
|
||||
10%
|
||||
70-150 = 1 def = 0.125%
|
||||
10%
|
||||
150-310 = 1 def = 0.0625%
|
||||
10%
|
||||
310-950 = 1 def = 0.03125% (20% instead of 10% in this bracket)
|
||||
20%
|
||||
950-1000 = 1 def = 0.1%
|
||||
5%
|
||||
1000 def = cap
|
||||
|
||||
75% total
|
||||
|
||||
Damage Reduction with Armor is applied multiplicative with other Damage Reduction effects.
|
||||
Every other Damage Redcution is Additive and capped at 75%.
|
||||
(All Dmg. Reduction buffs)
|
||||
|
||||
|
||||
Max Armor Roll + capped damage reduction therefore reduce damage by 93.75 (87.5%??).
|
||||
|
||||
|
||||
Every dmg number after reduction is rounded up. dmg cant be 0.
|
||||
@ -72,10 +72,9 @@ protected:
|
||||
SetValue(game->GetPlayer()->GetStat(stat));
|
||||
}
|
||||
inline void UpdateLabel(){
|
||||
AttributeData data=ItemAttributable::GetDisplayInfo(stat);
|
||||
std::string attrStr=data.name+":\n ";
|
||||
std::string attrStr=std::string(stat.Name())+":\n ";
|
||||
attrStr+=std::to_string(value);
|
||||
if(data.displayAsPercent){
|
||||
if(stat.DisplayAsPercent()){
|
||||
attrStr+="%";
|
||||
}
|
||||
if(statChangeAmt>0){
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
January 1st
|
||||
===========
|
||||
Buff Item Script
|
||||
Buff Item Script (Implement Other Stats)
|
||||
Sell Item Merchant Screen
|
||||
Blacksmith Item Crafting Screen
|
||||
Randomized Item Stats
|
||||
|
||||
@ -39,7 +39,7 @@ All rights reserved.
|
||||
#define VERSION_MAJOR 0
|
||||
#define VERSION_MINOR 2
|
||||
#define VERSION_PATCH 1
|
||||
#define VERSION_BUILD 4574
|
||||
#define VERSION_BUILD 4643
|
||||
|
||||
#define stringify(a) stringify_(a)
|
||||
#define stringify_(a) #a
|
||||
|
||||
@ -118,7 +118,7 @@ void Warrior::InitializeClassAbilities(){
|
||||
Warrior::ability1.action=
|
||||
[](Player*p,vf2d pos={}){
|
||||
game->AddEffect(std::make_unique<Effect>(p->GetPos(),"Warrior.Ability 1.EffectLifetime"_F,"battlecry_effect.png",p->upperLevel,"Warrior.Ability 1.Range"_F/350,"Warrior.Ability 1.EffectFadetime"_F));
|
||||
p->AddBuff(BuffType::ATTACK_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F);
|
||||
p->AddBuff(BuffType::STAT_UP,"Warrior.Ability 1.AttackUpDuration"_F,"Warrior.Ability 1.AttackIncrease"_F,{"Attack %"});
|
||||
p->AddBuff(BuffType::DAMAGE_REDUCTION,"Warrior.Ability 1.DamageReductionDuration"_F,"Warrior.Ability 1.DamageReduction"_F);
|
||||
for(Monster&m:MONSTER_LIST){
|
||||
if(m.GetSizeMult()>="Warrior.Ability 1.AffectedSizeRange"_f[0]&&m.GetSizeMult()<="Warrior.Ability 1.AffectedSizeRange"_f[1]&&geom2d::overlaps(geom2d::circle<float>(p->GetPos(),12*"Warrior.Ability 1.Range"_I/100.f),geom2d::circle<float>(m.GetPos(),m.GetSizeMult()*12))){
|
||||
|
||||
@ -46,9 +46,6 @@ Warrior
|
||||
|
||||
# Percentage of player's normal movement speed while block is active.
|
||||
SlowAmt = 0.3
|
||||
|
||||
# Percentage of damage to reduce by. (1.0 = 100%)
|
||||
DamageReduction = 1.00
|
||||
}
|
||||
Ability 1
|
||||
{
|
||||
|
||||
@ -68,6 +68,9 @@ story_player_name = You
|
||||
# The path to the merchant configuration files in the game
|
||||
merchant_directory = config/shops/
|
||||
|
||||
# Item Stats Config
|
||||
item_stats_config = items/ItemStats.txt
|
||||
|
||||
# Dialog font and size.
|
||||
dialog_font_size = c64esque,12
|
||||
|
||||
|
||||
@ -21,13 +21,19 @@ ItemScript
|
||||
Buff
|
||||
{
|
||||
Attack = 0,0.0
|
||||
Attack % = 0,0.0
|
||||
Attack % = 0%,0.0
|
||||
Defense = 0,0.0
|
||||
Defense % = 0,0.0
|
||||
Defense % = 0%,0.0
|
||||
Health = 0,0.0
|
||||
Health % = 0,0.0
|
||||
Move Spd = 0,0.0
|
||||
Move Spd % = 0,0.0
|
||||
Health % = 0%,0.0
|
||||
Move Spd % = 0%,0.0
|
||||
CDR = 0%,0.0
|
||||
Crit Rate = 0%,0.0
|
||||
Crit Dmg = 0%,0.0
|
||||
HP Recovery % = 0%,0.0
|
||||
HP6 Recovery % = 0%,0.0
|
||||
HP4 Recovery % = 0%,0.0
|
||||
Damage Reduction = 0%,0.0
|
||||
}
|
||||
|
||||
# Unlike an item or ability that requires a cast to perform, this applies a buff immediately and then moving cancels the buff mid-application.
|
||||
|
||||
@ -20,7 +20,9 @@ ItemSet
|
||||
# Defense = 5 # Adds 5 defense
|
||||
# Health = 3 # Adds 3 Health
|
||||
# Attack = 7 # Adds 7 attack
|
||||
# Move Spd = 10% # Grants 10% more movement speed.
|
||||
# Defense % = 2% # Adds 2% defense
|
||||
# Attack % = 5% # Adds 5% attack
|
||||
# Move Spd % = 10% # Grants 10% more movement speed.
|
||||
# CDR = 14% # Grants 14% cooldown reduction.
|
||||
# Crit Rate = 5% # Grants 5% crit rate.
|
||||
# Crit Dmg = 25% # Grants 25% bonus crit damage.
|
||||
@ -29,6 +31,9 @@ ItemSet
|
||||
# HP4 Recovery % = 2% # Grants 2% HP recovery every 4 seconds.
|
||||
# Damage Reduction = 2% # Grants 2% direct damage reduction
|
||||
#
|
||||
#
|
||||
# All Possible Stat modifications can be found in ItemStats.txt
|
||||
#
|
||||
Leather
|
||||
{
|
||||
2
|
||||
@ -55,7 +60,7 @@ ItemSet
|
||||
{
|
||||
2
|
||||
{
|
||||
Move Spd = 5%
|
||||
Move Spd % = 5%
|
||||
}
|
||||
4
|
||||
{
|
||||
@ -78,7 +83,7 @@ ItemSet
|
||||
2
|
||||
{
|
||||
Crit Rate = 3%
|
||||
Move Spd = 5%
|
||||
Move Spd % = 5%
|
||||
Attack = 2%
|
||||
}
|
||||
4
|
||||
@ -114,7 +119,7 @@ ItemSet
|
||||
{
|
||||
2
|
||||
{
|
||||
Move Spd = 10%
|
||||
Move Spd % = 10%
|
||||
}
|
||||
4
|
||||
{
|
||||
|
||||
62
Crawler/assets/config/items/ItemStats.txt
Normal file
62
Crawler/assets/config/items/ItemStats.txt
Normal file
@ -0,0 +1,62 @@
|
||||
Stats
|
||||
{
|
||||
Defense
|
||||
{
|
||||
Percentage = False
|
||||
}
|
||||
Health
|
||||
{
|
||||
Percentage = False
|
||||
}
|
||||
Attack
|
||||
{
|
||||
Percentage = False
|
||||
}
|
||||
Defense %
|
||||
{
|
||||
Modifies = Defense
|
||||
Percentage = True
|
||||
}
|
||||
Attack %
|
||||
{
|
||||
Modifies = Attack
|
||||
Percentage = True
|
||||
}
|
||||
Health %
|
||||
{
|
||||
Modifies = Health
|
||||
Percentage = True
|
||||
}
|
||||
Move Spd %
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
CDR
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
Crit Rate
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
Crit Dmg
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
HP Recovery %
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
HP6 Recovery %
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
HP4 Recovery %
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
Damage Reduction
|
||||
{
|
||||
Percentage = True
|
||||
}
|
||||
}
|
||||
@ -114,6 +114,12 @@ namespace olc::utils
|
||||
return std::atoi(GetString(nItem).c_str());
|
||||
}
|
||||
|
||||
// Retrieves the Boolean Value of a Property (for a given index) or 0
|
||||
inline const bool GetBool(const size_t nItem = 0) const
|
||||
{
|
||||
return GetString(nItem).starts_with('T')||GetString(nItem).starts_with('t');
|
||||
}
|
||||
|
||||
// Sets the Integer Value of a Property (for a given index)
|
||||
inline void SetInt(const int32_t n, const size_t nItem = 0)
|
||||
{
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user