The open source repository for the action RPG game in development by Sig Productions titled 'Adventures in Lestoria'!
https://forums.lestoria.net
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
264 lines
13 KiB
264 lines
13 KiB
#pragma region License
|
|
/*
|
|
License (OLC-3)
|
|
~~~~~~~~~~~~~~~
|
|
|
|
Copyright 2018 - 2023 OneLoneCoder.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 © 2023 The FreeType
|
|
Project (www.freetype.org). Please see LICENSE_FT.txt for more information.
|
|
All rights reserved.
|
|
*/
|
|
#pragma endregion
|
|
#include "Menu.h"
|
|
#include "MenuComponent.h"
|
|
#include "PopupMenuLabel.h"
|
|
#include "StatLabel.h"
|
|
#include "CharacterRotatingDisplay.h"
|
|
#include "Crawler.h"
|
|
#include "ClassInfo.h"
|
|
#include "MenuItemItemButton.h"
|
|
#include "EquipSlotButton.h"
|
|
#include "Item.h"
|
|
#include "ScrollableWindowComponent.h"
|
|
|
|
INCLUDE_game
|
|
INCLUDE_GFX
|
|
|
|
void Menu::InitializeCharacterMenuWindow(){
|
|
static bool equipmentWindowOpened=false;
|
|
|
|
vf2d windowSize=game->GetScreenSize()-vf2d{52,52};
|
|
Menu*characterMenuWindow=CreateMenu(CHARACTER_MENU,CENTERED,windowSize);
|
|
|
|
MenuLabel*characterLabel=NEW MenuLabel(CHARACTER_MENU,{{0,-4},{float(windowSize.x)-1,24}},"Character",2,ComponentAttr::SHADOW|ComponentAttr::OUTLINE|ComponentAttr::BACKGROUND);
|
|
characterLabel->decal=true;
|
|
|
|
MenuComponent*equipSlotOutline=NEW MenuComponent(CHARACTER_MENU,{{0,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE);
|
|
CharacterRotatingDisplay*charDisplay=NEW CharacterRotatingDisplay(CHARACTER_MENU,{{135,28},{90,windowSize.y-37}},GFX[classutils::GetClassInfo(game->GetPlayer()->GetClassName()).classFullImgName].Decal());
|
|
|
|
characterMenuWindow->AddComponent("Character Label",characterLabel);
|
|
characterMenuWindow->AddComponent("Equip Slot Outline",equipSlotOutline);
|
|
characterMenuWindow->AddComponent("Character Rotating Display",charDisplay);
|
|
|
|
const static std::array<ItemAttribute,7>displayAttrs{
|
|
ItemAttribute::health,
|
|
ItemAttribute::attack,
|
|
ItemAttribute::defense,
|
|
ItemAttribute::moveSpdPct,
|
|
ItemAttribute::cdrPct,
|
|
ItemAttribute::critPct,
|
|
ItemAttribute::critDmgPct,
|
|
};
|
|
|
|
MenuComponent*equipSelectionOutline=NEW MenuComponent(CHARACTER_MENU,{{123,28},{120,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE);
|
|
ScrollableWindowComponent*equipmentList=NEW ScrollableWindowComponent(CHARACTER_MENU,{{123,28},{120,windowSize.y-37-24}});
|
|
MenuComponent*equipSelectionBottomOutline=NEW MenuComponent(CHARACTER_MENU,{{123,28+(windowSize.y-37-24)},{120,24}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE);
|
|
MenuComponent*equipSelectionSelectButton=NEW MenuComponent(CHARACTER_MENU,{{123+12,28+(windowSize.y-37-24)+6},{96,12}},"Select",[](MenuFuncData data){
|
|
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(false);
|
|
Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List")->Enable(false);
|
|
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");
|
|
statDisplayLabel->SetStatChangeAmt(0);
|
|
}
|
|
equipmentWindowOpened=false;
|
|
return true;
|
|
});
|
|
equipSelectionSelectButton->decal=true;
|
|
|
|
equipSelectionOutline->Enable(false);
|
|
equipmentList->Enable(false);
|
|
equipSelectionBottomOutline->Enable(false);
|
|
equipSelectionSelectButton->Enable(false);
|
|
|
|
characterMenuWindow->AddComponent("Equip Selection Outline",equipSelectionOutline);
|
|
characterMenuWindow->AddComponent("Equip List",equipmentList,-1);
|
|
characterMenuWindow->AddComponent("Equip Selection Bottom Outline",equipSelectionBottomOutline);
|
|
characterMenuWindow->AddComponent("Equip Selection Select Button",equipSelectionSelectButton);
|
|
|
|
const static auto GetLabelText=[](ItemAttribute attribute){
|
|
AttributeData data=ItemAttributable::GetDisplayInfo(attribute);
|
|
std::string attrStr=data.name+":\n ";
|
|
attrStr+=std::to_string(game->GetPlayer()->GetStat(attribute));
|
|
if(data.displayAsPercent){
|
|
attrStr+="%";
|
|
}
|
|
return attrStr;
|
|
};
|
|
|
|
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 static std::array<std::string,8>slotNames{"Helmet","Weapon","Armor","Gloves","Pants","Shoes","Ring 1","Ring 2"};
|
|
EquipSlot slot=EquipSlot(equipSlot);
|
|
|
|
EquipSlotButton*equipmentSlot=NEW EquipSlotButton(CHARACTER_MENU,{{x,y+28},{24,24}},slot,MenuType::ENUM_END,
|
|
[&](MenuFuncData data){
|
|
EquipSlot slot=EquipSlot(data.component->I(Attribute::EQUIP_TYPE));
|
|
|
|
std::vector<Item>&equips=Inventory::get("Equipment");
|
|
std::vector<Item>&accessories=Inventory::get("Accessories");
|
|
std::vector<Item>availableEquipment;
|
|
std::copy_if(equips.begin(),equips.end(),std::back_inserter(availableEquipment),[&](Item&it){
|
|
return it.GetEquipSlot()&slot;
|
|
});
|
|
std::copy_if(accessories.begin(),accessories.end(),std::back_inserter(availableEquipment),[&](Item&it){
|
|
return it.GetEquipSlot()&slot;
|
|
});
|
|
|
|
ScrollableWindowComponent*equipList=Component<ScrollableWindowComponent>(data.component->parentMenu,"Equip List");
|
|
equipList->RemoveAllComponents();
|
|
for(int counter=0;Item&it:availableEquipment){
|
|
float xOffset=(counter%3)*26;
|
|
Item&itemInvRef=Inventory::GetItem(it.Name());
|
|
MenuItemItemButton*equip=NEW MenuItemItemButton(CHARACTER_MENU,{{2+xOffset,2},{24,24}},itemInvRef,MenuType::ENUM_END,[](MenuFuncData data){
|
|
MenuItemItemButton*comp=(MenuItemItemButton*)data.component;
|
|
Inventory::EquipItem(comp->GetItem(),EquipSlot(comp->I(Attribute::EQUIP_TYPE)));
|
|
for(MenuComponent*button:((ScrollableWindowComponent*)data.parentComponent)->GetComponents()){
|
|
MenuItemItemButton*comp=(MenuItemItemButton*)button;
|
|
comp->SetSelected(false);
|
|
}
|
|
comp->SetSelected(true);
|
|
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
|
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
|
statDisplayLabel->SetStatChangeAmt(0);
|
|
}
|
|
MenuItemItemButton*equipButton=Component<MenuItemItemButton>(CHARACTER_MENU,"Equip Slot "+slotNames[data.parentComponent->I(A::INDEXED_THEME)]);
|
|
equipButton->SetItem(comp->GetItem());
|
|
return true;
|
|
},[&](MenuFuncData data){
|
|
MenuItemItemButton*button=(MenuItemItemButton*)data.component;
|
|
Item&buttonItem=button->GetItem();
|
|
std::vector<int>statsBeforeEquip;
|
|
EquipSlot slot=EquipSlot(button->I(Attribute::EQUIP_TYPE));
|
|
for(ItemAttribute attribute:displayAttrs){
|
|
statsBeforeEquip.push_back(game->GetPlayer()->GetStat(attribute));
|
|
}
|
|
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");
|
|
int statChangeAmt=game->GetPlayer()->GetStat(attribute)-statsBeforeEquip[counter];
|
|
statDisplayLabel->SetStatChangeAmt(statChangeAmt);
|
|
counter++;
|
|
}
|
|
Inventory::UnequipItem(slot);
|
|
if(*equippedItem!=Item::BLANK){
|
|
Inventory::EquipItem(*equippedItem,slot);
|
|
}
|
|
return true;
|
|
},[](MenuFuncData data){
|
|
for(int counter=0;ItemAttribute attribute:displayAttrs){
|
|
StatLabel*statDisplayLabel=Component<StatLabel>(CHARACTER_MENU,"Attribute "+ItemAttributable::GetDisplayInfo(attribute).name+" Label");
|
|
statDisplayLabel->SetStatChangeAmt(0);
|
|
counter++;
|
|
}
|
|
return true;
|
|
},"Item Name","Item Description");
|
|
equip->I(Attribute::EQUIP_TYPE)=int(slot);
|
|
if(Inventory::GetEquip(slot)==&itemInvRef){
|
|
equip->SetSelected(true);
|
|
}
|
|
equip->SetCompactDescriptions(false);
|
|
equipList->AddComponent(Menu::menus[CHARACTER_MENU],"Equip Item "+std::to_string(counter),equip);
|
|
counter++;
|
|
}
|
|
equipList->I(Attribute::INDEXED_THEME)=data.component->I(Attribute::INDEXED_THEME);
|
|
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Outline")->Enable(true);
|
|
equipList->Enable(true);
|
|
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Bottom Outline")->Enable(true);
|
|
Component<MenuComponent>(data.component->parentMenu,"Equip Selection Select Button")->Enable(true);
|
|
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(false);
|
|
equipmentWindowOpened=true;
|
|
return true;
|
|
},[](MenuFuncData data){//On Mouse Hover
|
|
if(Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->GetLabel()!=""){
|
|
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(false);
|
|
}
|
|
return true;
|
|
},[](MenuFuncData data){//On Mouse Out
|
|
if(Component<MenuLabel>(data.component->parentMenu,"Item Equip Description")->GetLabel()!=""&&!equipmentWindowOpened){
|
|
Component<CharacterRotatingDisplay>(data.component->parentMenu,"Character Rotating Display")->Enable(true);
|
|
}
|
|
return true;
|
|
},"Item Equip Name","Item Equip Description");
|
|
PopupMenuLabel*equipmentLabel=NEW PopupMenuLabel(CHARACTER_MENU,{{labelX,labelY},{29,16}},slotNames[i],{0.5,1},ComponentAttr::SHADOW);
|
|
equipmentSlot->I(Attribute::EQUIP_TYPE)=int(slot);
|
|
equipmentSlot->I(Attribute::INDEXED_THEME)=i;
|
|
equipmentSlot->SetShowQuantity(false);
|
|
equipmentSlot->SetCompactDescriptions(false);
|
|
equipSlot<<=1;
|
|
characterMenuWindow->AddComponent("Equip Slot "+slotNames[i],equipmentSlot);
|
|
characterMenuWindow->AddComponent("Equip Label "+slotNames[i],equipmentLabel);
|
|
Menu::AddEquipStatListener(equipmentSlot);
|
|
}
|
|
|
|
MenuComponent*statDisplayOutline=NEW MenuComponent(CHARACTER_MENU,{{245,28},{62,windowSize.y-37}},"",DO_NOTHING,ButtonAttr::UNSELECTABLE);
|
|
|
|
characterMenuWindow->AddComponent("Stat Display Outline",statDisplayOutline);
|
|
|
|
int yOffset=0;
|
|
for(ItemAttribute attribute:displayAttrs){
|
|
std::string attrStr=GetLabelText(attribute);
|
|
StatLabel*attrLabel=NEW StatLabel(CHARACTER_MENU,{{245,28+2+float(yOffset)},{62,18}},attribute,1,ComponentAttr::SHADOW|ComponentAttr::LEFT_ALIGN);
|
|
yOffset+=20;
|
|
AttributeData data=ItemAttributable::GetDisplayInfo(attribute);
|
|
characterMenuWindow->AddComponent("Attribute "+data.name+" Label",attrLabel);
|
|
Menu::AddEquipStatListener(attrLabel);
|
|
}
|
|
|
|
MenuComponent*backButton=NEW MenuComponent(CHARACTER_MENU,{{windowSize.x/2-64,windowSize.y},{128,12}},"Back",[](MenuFuncData data){Menu::stack.pop_back();return true;});
|
|
backButton->decal=true;
|
|
characterMenuWindow->AddComponent("Back button",backButton);
|
|
|
|
MenuLabel*itemNameDisplay=NEW MenuLabel(CHARACTER_MENU,{{0,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW);
|
|
MenuLabel*itemDescriptionDisplay=NEW MenuLabel(CHARACTER_MENU,{{0,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW);
|
|
|
|
itemNameDisplay->decal=true;
|
|
itemDescriptionDisplay->decal=true;
|
|
itemNameDisplay->Enable(false);
|
|
itemDescriptionDisplay->Enable(false);
|
|
|
|
MenuLabel*itemNameDisplayEquip=NEW MenuLabel(CHARACTER_MENU,{{123,28},{120,12}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW);
|
|
MenuLabel*itemDescriptionDisplayEquip=NEW MenuLabel(CHARACTER_MENU,{{123,40},{120,windowSize.y-49}},"",1,ComponentAttr::BACKGROUND|ComponentAttr::LEFT_ALIGN|ComponentAttr::OUTLINE|ComponentAttr::SHADOW);
|
|
|
|
characterMenuWindow->AddComponent("Item Name",itemNameDisplay);
|
|
characterMenuWindow->AddComponent("Item Description",itemDescriptionDisplay);
|
|
characterMenuWindow->AddComponent("Item Equip Name",itemNameDisplayEquip);
|
|
characterMenuWindow->AddComponent("Item Equip Description",itemDescriptionDisplayEquip);
|
|
} |