diff --git a/Crawler/AttributableStat.h b/Crawler/AttributableStat.h index 5372d352..66110b7b 100644 --- a/Crawler/AttributableStat.h +++ b/Crawler/AttributableStat.h @@ -45,7 +45,7 @@ enum class ItemAttribute{ /*////////////////////////////////////////////*/ /*//////*/ENUM_START,/*///////////////////////*/ /*////////////////////////////////////////////*/ - +////////////// defense, health, attack, @@ -56,18 +56,49 @@ enum class ItemAttribute{ healthPct, //Percentage of health boost healthPctRecoveryPer6sec, //Percentage of health recovered every 6 seconds. +/////////NOTE: When adding a new item stat, provide its display name in the map below. /*////////////////////////////////////////////*/ /*//////*/ENUM_END/*//////////////////////////*/ /*////////////////////////////////////////////*/ }; +namespace DisplayType{ + enum DisplayType{ + DISPLAY_AS_NUMBER=0, + DISPLAY_AS_PERCENT=1, + }; +} +struct AttributeData{ + std::string name; + DisplayType::DisplayType displayAsPercent; +}; + class ItemAttributable{ std::mapattributes; +private: + inline static std::mapdata{ + {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,{"HP Recovery",DisplayType::DISPLAY_AS_NUMBER}} //Percentage of health recovered every 6 seconds.} + }; public: + //Returns a copy of all the attributes to be passed to a new instance easily / to sync values between both. + inline void copyTo(ItemAttributable&target){ + target.attributes=attributes; + } inline int&get(ItemAttribute a){ return attributes[a]; } inline const int get_readOnly(ItemAttribute a)const{ return attributes.at(a); } + inline static AttributeData GetDisplayInfo(ItemAttribute a){ + return data[a]; + } }; \ No newline at end of file diff --git a/Crawler/BitwiseEnum.h b/Crawler/BitwiseEnum.h index ef1c6e9c..a27b416d 100644 --- a/Crawler/BitwiseEnum.h +++ b/Crawler/BitwiseEnum.h @@ -47,4 +47,18 @@ template constexpr auto operator&(T attribute,T attribute2) noexcept { return static_cast>(attribute)&static_cast>(attribute2); +} + +template +constexpr auto operator|=(T&lhs,const T&rhs) noexcept +{ + lhs=T(static_cast>(lhs)|static_cast>(rhs)); + return lhs; +} + +template +constexpr auto operator&=(T&lhs,const T&rhs) noexcept +{ + lhs=T(static_cast>(lhs)&static_cast>(rhs)); + return lhs; } \ No newline at end of file diff --git a/Crawler/CharacterMenuWindow.cpp b/Crawler/CharacterMenuWindow.cpp index 50c7d41d..ad65ecc6 100644 --- a/Crawler/CharacterMenuWindow.cpp +++ b/Crawler/CharacterMenuWindow.cpp @@ -41,6 +41,10 @@ All rights reserved. #include "CharacterRotatingDisplay.h" #include "Crawler.h" #include "ClassInfo.h" +#include "MenuItemItemButton.h" +#include "Item.h" +#include "PopupMenuLabel.h" +#include "ScrollableWindowComponent.h" INCLUDE_game INCLUDE_GFX @@ -57,4 +61,72 @@ void Menu::InitializeCharacterMenuWindow(){ characterMenuWindow->AddComponent("Character Label",characterLabel); characterMenuWindow->AddComponent("Equip Slot Outline",equipSlotOutline); characterMenuWindow->AddComponent("Character Rotating Display",charDisplay); + + MenuComponent*equipSelectionOutline=NEW MenuComponent(CHARACTER_MENU,{{123,36},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE); + ScrollableWindowComponent*equipmentList=NEW ScrollableWindowComponent(CHARACTER_MENU,{{123,36},{120,windowSize.y-37-24}}); + MenuComponent*equipSelectionBottomOutline=NEW MenuComponent(CHARACTER_MENU,{{123,36+(windowSize.y-37-24)},{120,24}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE); + MenuComponent*equipSelectionSelectButton=NEW MenuComponent(CHARACTER_MENU,{{123+12,36+(windowSize.y-37-24)+6},{96,12}},"Select",DO_NOTHING); + + equipSelectionOutline->Enable(false); + equipmentList->Enable(false); + equipSelectionBottomOutline->Enable(false); + equipSelectionSelectButton->Enable(false); + + characterMenuWindow->AddComponent("Equip Selection Outline",equipSelectionOutline); + characterMenuWindow->AddComponent("Equip List",equipmentList); + characterMenuWindow->AddComponent("Equip Selection Bottom Outline",equipSelectionBottomOutline); + characterMenuWindow->AddComponent("Equip Selection Select Button",equipSelectionSelectButton); + + int equipSlot=1; + for(int i=0;i<8;i++){ + float x=31+(i%2)*33; + float y=24+(i/2)*28; + float labelX=2+(i%2)*90; + float labelY=24+(i/2)*28+36; + if(i<6){ + y-=8; + labelY-=8; + } + const std::arrayslotNames{"Helmet","Weapon","Armor","Gloves","Pants","Shoes","Ring 1","Ring 2"}; + EquipSlot slot=EquipSlot(equipSlot); + MenuItemItemButton*equipmentSlot=NEW MenuItemItemButton(CHARACTER_MENU,{{x,y+36},{24,24}},Item::BLANK,MenuType::ENUM_END, + [](MenuFuncData data){ + Component(data.component->parentMenu,"Equip Selection Outline")->Enable(true); + Component(data.component->parentMenu,"Equip List")->Enable(true); + Component(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(true); + Component(data.component->parentMenu,"Equip Selection Select Button")->Enable(true); + Component(data.component->parentMenu,"Character Rotating Display")->Enable(false); + return true; + },MenuType::ENUM_END,"",""); + PopupMenuLabel*equipmentLabel=NEW PopupMenuLabel(CHARACTER_MENU,{{labelX,labelY},{29,24}},slotNames[i],{0.5,1},ComponentAttr::SHADOW); + equipSlot<<=1; + characterMenuWindow->AddComponent("Equip Slot "+slotNames[i],equipmentSlot); + characterMenuWindow->AddComponent("Equip Label "+slotNames[i],equipmentLabel); + } + + MenuComponent*statDisplayOutline=NEW MenuComponent(CHARACTER_MENU,{{245,36},{62,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE); + const std::arraydisplayAttrs{ + ItemAttribute::health, + ItemAttribute::attack, + ItemAttribute::defense, + ItemAttribute::moveSpdPct, + ItemAttribute::cdrPct, + ItemAttribute::critPct, + ItemAttribute::critDmgPct, + }; + + characterMenuWindow->AddComponent("Stat Display Outline",statDisplayOutline); + + int yOffset=0; + for(ItemAttribute attribute:displayAttrs){ + AttributeData data=ItemAttributable::GetDisplayInfo(attribute); + std::string attrStr=data.name+":\n "; + attrStr+=std::to_string(game->GetPlayer()->A(attribute)); + if(data.displayAsPercent){ + attrStr+="%"; + } + MenuLabel*attrLabel=NEW MenuLabel(CHARACTER_MENU,{{245,36+2+float(yOffset)},{62,18}},attrStr,1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN); + yOffset+=20; + characterMenuWindow->AddComponent("Attribute "+data.name+" Label",attrLabel); + } } \ No newline at end of file diff --git a/Crawler/Crawler.cpp b/Crawler/Crawler.cpp index 7349d3a1..d801edfd 100644 --- a/Crawler/Crawler.cpp +++ b/Crawler/Crawler.cpp @@ -1702,8 +1702,8 @@ void Crawler::ChangePlayerClass(Class cl){ player.reset(NEW Witch(player.get())); }break; } - player->hp=player->maxhp=DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt(); - player->atk=DATA.GetProperty(player->GetClassName()+".BaseAtk").GetInt(); + player->hp=player->A(ItemAttribute::health)=DATA.GetProperty(player->GetClassName()+".BaseHealth").GetInt(); + player->A(ItemAttribute::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()); sig::Animation::SetupPlayerAnimations(); diff --git a/Crawler/Item.cpp b/Crawler/Item.cpp index 671c02ed..e46d0f0c 100644 --- a/Crawler/Item.cpp +++ b/Crawler/Item.cpp @@ -131,7 +131,7 @@ void ItemInfo::InitializeItems(){ if(slot.size()>0){ for(std::string&s:slot){ if(!nameToEquipSlot.count(s))ERR("WARNING! Tried to add item "<(itemDescriptionMenu,itemNameLabelName)->label=itemRef.Name(); Component(itemDescriptionMenu,itemDescriptionLabelName)->label=itemRef.Description(); } }else{ icon=nullptr; - if(hovered){ + if(hovered&&itemDescriptionMenu!=MenuType::ENUM_END){ Component(itemDescriptionMenu,itemNameLabelName)->label=""; Component(itemDescriptionMenu,itemDescriptionLabelName)->label=""; } diff --git a/Crawler/Player.cpp b/Crawler/Player.cpp index 628dbd9c..1c22dea6 100644 --- a/Crawler/Player.cpp +++ b/Crawler/Player.cpp @@ -76,11 +76,21 @@ Player::Player() Player::Player(Player*player) :pos(player->GetPos()),vel(player->GetVelocity()),iframe_time(player->iframe_time),lastReleasedMovementKey(DOWN), facingDirection(DOWN),state(State::NORMAL){ + player->copyTo(*this); Initialize(); } void Player::Initialize(){ Player::GROUND_SLAM_SPIN_TIME="Warrior.Ability 2.SpinTime"_F; + A(ItemAttribute::health)=hp; + A(ItemAttribute::defense)=0; + A(ItemAttribute::attack)="Warrior.BaseAtk"_I; + A(ItemAttribute::moveSpdPct)=100; + A(ItemAttribute::cdrPct)=0; + A(ItemAttribute::critPct)=0; + A(ItemAttribute::critDmgPct)=0; + A(ItemAttribute::healthPct)=0; + A(ItemAttribute::healthPctRecoveryPer6sec)=0; } bool Player::SetX(float x){ @@ -169,7 +179,7 @@ int Player::GetHealth(){ } int Player::GetMaxHealth(){ - return maxhp; + return A(ItemAttribute::health); } int Player::GetMana(){ @@ -181,15 +191,15 @@ int Player::GetMaxMana() { } int Player::GetAttack(){ - float mod_atk=float(atk); + float mod_atk=float(A(ItemAttribute::attack)); for(Buff&b:GetBuffs(BuffType::ATTACK_UP)){ - mod_atk+=atk*b.intensity; + mod_atk+=A(ItemAttribute::attack)*b.intensity; } return int(mod_atk); } float Player::GetMoveSpdMult(){ - float mod_moveSpd=moveSpd; + float mod_moveSpd=A(ItemAttribute::moveSpdPct)/100.f; for(Buff&b:GetBuffs(BuffType::SLOWDOWN)){ mod_moveSpd-=mod_moveSpd*b.intensity; } @@ -779,7 +789,7 @@ void Player::SetIframes(float duration){ } bool Player::Heal(int damage){ - hp=std::clamp(hp+damage,0,maxhp); + hp=std::clamp(hp+damage,0,A(ItemAttribute::health)); if(damage>0){ DAMAGENUMBER_LIST.push_back(std::make_shared(GetPos(),damage,true,HEALTH_GAIN)); } diff --git a/Crawler/Player.h b/Crawler/Player.h index a95e9c63..d974e55f 100644 --- a/Crawler/Player.h +++ b/Crawler/Player.h @@ -47,6 +47,7 @@ All rights reserved. #include "Key.h" #include "Class.h" #include "Item.h" +#include "AttributableStat.h" #undef GetClassName struct DamageNumber; @@ -58,7 +59,7 @@ struct CastInfo{ vf2d castPos; }; -struct Player{ +struct Player:ItemAttributable{ friend class Crawler; friend class sig::Animation; friend struct Warrior; @@ -69,14 +70,12 @@ struct Player{ friend struct Witch; friend class State_GameRun; private: - int hp="Warrior.BaseHealth"_I,maxhp=hp; + int hp="Warrior.BaseHealth"_I; int mana="Player.BaseMana"_I,maxmana=mana; - int atk="Warrior.BaseAtk"_I; float hpGrowthRate="Warrior.HealthGrowthRate"_F; float atkGrowthRate="Warrior.AtkGrowthRate"_F; vf2d pos; float z=0; - float moveSpd=1.0f; float size=1.0f; float spin_attack_timer=0; float spin_spd=0; diff --git a/Crawler/PopupMenuLabel.h b/Crawler/PopupMenuLabel.h index ea0b81d7..0c60e8c6 100644 --- a/Crawler/PopupMenuLabel.h +++ b/Crawler/PopupMenuLabel.h @@ -45,10 +45,13 @@ INCLUDE_game class PopupMenuLabel:public MenuLabel{ private: - float scale; + vf2d scale; public: inline PopupMenuLabel(MenuType parent,geom2d::rectrect,std::string label,float scale=1,ComponentAttr attributes=ComponentAttr::NONE) - :MenuLabel(parent,rect,label,int(scale),attributes),scale(scale){ + :MenuLabel(parent,rect,label,1,attributes),scale({scale,scale}){ + } + inline PopupMenuLabel(MenuType parent,geom2d::rectrect,std::string label,vf2d scale={1,1},ComponentAttr attributes=ComponentAttr::NONE) + :MenuLabel(parent,rect,label,1,attributes),scale(scale){ } protected: virtual void inline Update(Crawler*game)override{ @@ -58,8 +61,8 @@ protected: virtual void inline DrawDecal(Crawler*game,vf2d parentPos,bool focused)override{ if(label.length()>0){ MenuLabel::DrawDecal(game,parentPos,focused); - std::string wrappedText=util::WrapText(game,label,int(rect.size.x-1),true,{scale,scale}); - vf2d drawPos=Menu::menus.at(parentMenu)->pos+parentPos+rect.middle()-vf2d{game->GetTextSizeProp(wrappedText)}*float(scale)/2; //Assume centered. + std::string wrappedText=util::WrapText(game,label,int(rect.size.x-1),true,scale); + vf2d drawPos=Menu::menus.at(parentMenu)->pos+parentPos+rect.middle()-vf2d{game->GetTextSizeProp(wrappedText)}*scale/2; //Assume centered. if(!centered){ drawPos=Menu::menus.at(parentMenu)->pos+vf2d{rect.pos.x+2,rect.middle().y-game->GetTextSizeProp(wrappedText).y/2}+parentPos; //We should at least vertically align here. } @@ -73,9 +76,9 @@ protected: game->DrawStringPropDecal(Menu::menus.at(parentMenu)->pos+rect.pos+parentPos+rect.size/2-game->GetTextSizeProp(label)/2,label); } if(shadow){ - game->DrawShadowStringPropDecal(drawPos,wrappedText,WHITE,BLACK,{scale,scale}); + game->DrawShadowStringPropDecal(drawPos,wrappedText,WHITE,BLACK,scale); }else{ - game->DrawStringPropDecal(drawPos,wrappedText,WHITE,{scale,scale}); + game->DrawStringPropDecal(drawPos,wrappedText,WHITE,scale); } } } diff --git a/Crawler/ScrollableWindowComponent.h b/Crawler/ScrollableWindowComponent.h index b8260141..09cd8aba 100644 --- a/Crawler/ScrollableWindowComponent.h +++ b/Crawler/ScrollableWindowComponent.h @@ -64,9 +64,6 @@ public: background=attributes&ComponentAttr::BACKGROUND; border=attributes&ComponentAttr::OUTLINE; r.Create(uint32_t(rect.size.x),uint32_t(rect.size.y)); - } -protected: - virtual inline void AfterCreate()override{ upButton=NEW MenuComponent(parentMenu,{rect.pos+vf2d{rect.size.x-12,0},{12,12}},"^",[&](MenuFuncData dat){V(A::SCROLL_OFFSET).y+="ThemeGlobal.MenuButtonScrollSpeed"_I;return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD); downButton=NEW MenuComponent(parentMenu,{rect.pos+rect.size-vf2d{12,12},{12,12}},"v",[&](MenuFuncData dat){V(A::SCROLL_OFFSET).y-="ThemeGlobal.MenuButtonScrollSpeed"_I;return true;},ButtonAttr::UNSELECTABLE_VIA_KEYBOARD); //Let's use the internal name of this component to add unique names for sub-components. @@ -236,4 +233,9 @@ public: inline std::vector&GetComponents(){ return components; } + virtual inline void Enable(bool enabled)override final{ + disabled=!enabled; + upButton->Enable(enabled); + downButton->Enable(enabled); + }; }; \ No newline at end of file diff --git a/Crawler/Version.h b/Crawler/Version.h index 8ec37cf2..af2fca7b 100644 --- a/Crawler/Version.h +++ b/Crawler/Version.h @@ -39,7 +39,7 @@ All rights reserved. #define VERSION_MAJOR 0 #define VERSION_MINOR 2 #define VERSION_PATCH 1 -#define VERSION_BUILD 3365 +#define VERSION_BUILD 3441 #define stringify(a) stringify_(a) #define stringify_(a) #a