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.
 
 
 
 
 
 
AdventuresInLestoria/Crawler/Menu.cpp

237 lines
7.0 KiB

#include "Crawler.h"
#include "Menu.h"
#include "DEFINES.h"
#include "safemap.h"
bool Menu::MOUSE_NAVIGATION=true;
std::vector<Menu*>Menu::stack;
std::map<MenuType,Menu>Menu::menus;
INCLUDE_GFX
Menu::Menu(){}
Menu::Menu(vf2d size)
:size(size){}
void Menu::InitializeMenus(){
stack.reserve(32);
menus[TEST]=InitializeTestMenu();
menus[TEST_2]=InitializeTestSubMenu();
for(MenuType type=TEST;type<MenuType::ENUM_END;type=MenuType(int(type+1))){
if(menus.count(type)==0){
std::cout<<"WARNING! Menu Type "<<type<<" does not exist!"<<std::endl;
throw;
}
}
}
void Menu::AddComponent(MenuComponent*button){
buttons[button->rect.pos.y].push_back(button);
}
void Menu::MenuSelect(Crawler*game){
if(selection==vi2d{-1,-1})return;
buttons[selection.y][selection.x]->onClick(*this,game);
if(buttons[selection.y][selection.x]->menuDest!=MenuType::ENUM_END){
if(stack.size()<32){
stack.push_back(&menus[buttons[selection.y][selection.x]->menuDest]);//Navigate to the next menu.
}else{
std::cout<<"WARNING! Exceeded menu stack size limit!"<<std::endl;
throw;
}
}
}
void Menu::Update(Crawler*game){
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
for(auto&key:buttons){
for(auto&button:key.second){
button->hovered=false;
}
}
if(!MOUSE_NAVIGATION){
if(selection!=vi2d{-1,-1})buttons[selection.y][selection.x]->hovered=true;
}else{
for(auto&key:buttons){
for(auto&button:key.second){
if(geom2d::overlaps(geom2d::rect<float>{button->rect.pos+upperLeftPos,button->rect.size},game->GetMousePos())){
button->hovered=true;
}
}
}
}
KeyboardButtonNavigation(game,upperLeftPos);
for(auto&key:buttons){
for(auto&button:key.second){
button->Update(game);
}
}
};
void Menu::Draw(Crawler*game){
vf2d upperLeftPos=game->GetScreenSize()/2-size/2;
vf2d patchSize=vf2d{float("Interface.9PatchSize"_i[0]),float("Interface.9PatchSize"_i[1])};
//Upper-Left
game->DrawPartialDecal(upperLeftPos-patchSize,patchSize,GFX["9patch.png"].Decal(),{patchSize.x*0,patchSize.y*0},patchSize);
//Upper-Right
game->DrawPartialDecal(upperLeftPos+vf2d{size.x,-patchSize.y},patchSize,GFX["9patch.png"].Decal(),{patchSize.x*2,patchSize.y*0},patchSize);
//Bottom-Left
game->DrawPartialDecal(upperLeftPos+vf2d{-patchSize.x,size.y},patchSize,GFX["9patch.png"].Decal(),{patchSize.x*0,patchSize.y*2},patchSize);
//Bottom-Right
game->DrawPartialDecal(upperLeftPos+vf2d{size.x,size.y},patchSize,GFX["9patch.png"].Decal(),{patchSize.x*2,patchSize.y*2},patchSize);
//Top
game->DrawPartialDecal(upperLeftPos+vf2d{0,-patchSize.y},patchSize+vf2d{size.x,0},GFX["9patch.png"].Decal(),{patchSize.x*1,patchSize.y*0},patchSize);
//Left
game->DrawPartialDecal(upperLeftPos+vf2d{-patchSize.x,0},patchSize+vf2d{0,size.y},GFX["9patch.png"].Decal(),{patchSize.x*0,patchSize.y*1},patchSize);
//Right
game->DrawPartialDecal(upperLeftPos+vf2d{size.x,0},patchSize+vf2d{0,size.y},GFX["9patch.png"].Decal(),{patchSize.x*2,patchSize.y*1},patchSize);
//Bottom
game->DrawPartialDecal(upperLeftPos+vf2d{0,size.y},patchSize+vf2d{size.x,0},GFX["9patch.png"].Decal(),{patchSize.x*1,patchSize.y*2},patchSize);
//Center
game->DrawPartialDecal(upperLeftPos,patchSize+size,GFX["9patch.png"].Decal(),{patchSize.x*1,patchSize.y*1},patchSize);
for(auto&key:buttons){
for(auto&button:key.second){
button->Draw(game,upperLeftPos);
}
}
};
void Menu::OpenMenu(MenuType menu){
stack.clear();
stack.push_back(&(menus[menu]));
}
void Menu::KeyboardButtonNavigation(Crawler*game,vf2d menuPos){
if(game->GetKey(RIGHT).bPressed){
if(selection==vi2d{-1,-1})return;
MOUSE_NAVIGATION=false;
selection.x=(selection.x+1)%buttons[selection.y].size();
}
if(game->GetKey(LEFT).bPressed){
if(selection==vi2d{-1,-1})return;
selection.x--;
if(selection.x<0)selection.x+=buttons[selection.y].size();
}
if(game->GetKey(DOWN).bPressed||game->GetKey(UP).bPressed){
if(game->GetKey(DOWN).bPressed){
MOUSE_NAVIGATION=false;
bool found=false;
bool selectedItem=false;
if(selection==vi2d{-1,-1}){
//Highlight first item.
for(auto&key:buttons){
selection.y=key.first;
break;
}
}else{
for(auto&key:buttons){
if(found){ //Once we discover the previous element, the next element becomes our next selection.
int previousButtonX=buttons[selection.y][selection.x]->rect.pos.x;
selection.y=key.first;
int index=0;
for(auto&button:key.second){ //Try to match a button in the same column as this button first.
if(previousButtonX==button->rect.pos.x){
selection.x=index;
break;
}
index++;
}
selectedItem=true;
break;
}
if(key.first==selection.y){
found=true;
}
}
if(!selectedItem){ //This means we need to loop around instead and pick the first one.
for(auto&key:buttons){
selection.y=key.first;
break;
}
}
}
}
if(game->GetKey(UP).bPressed){
MOUSE_NAVIGATION=false;
if(selection==vi2d{-1,-1}){
//Highlight last item.
for(auto&key:buttons){
selection.y=key.first;
}
}else{
int prevInd=-1;
for(auto&key:buttons){
if(key.first==selection.y){
break;
}
prevInd=key.first;
}
if(prevInd!=-1){
int previousButtonX=buttons[selection.y][selection.x]->rect.pos.x;
selection.y=prevInd;
int index=0;
for(auto&button:buttons[prevInd]){ //Try to match a button in the same column as this button first.
if(previousButtonX==button->rect.pos.x){
selection.x=index;
break;
}
index++;
}
}else{ //Since we didn't find it, it means we're at the top of the list or the list is empty. Go to the last element and use that one.
int lastInd=-1;
for(auto&key:buttons){
lastInd=key.first;
}
selection.y=lastInd;
}
}
}
//In both cases, we should clamp the X index to make sure it's still valid.
if(selection.y!=-1){
selection.x=std::clamp(selection.x,0,int(buttons[selection.y].size())-1);
}else{
selection.x=-1;
}
}
if(game->GetMouse(0).bPressed||game->GetKey(ENTER).bPressed||game->GetKey(SPACE).bPressed){
MOUSE_NAVIGATION=game->GetMouse(0).bPressed; //If a click occurs we use mouse controls.
if(!MOUSE_NAVIGATION){
MenuSelect(game);
//Key presses automatically highlight the first button if it's not highlighted.
if(selection==vi2d{-1,-1}&&buttons.size()>0){
//Find the first possible button entry in the map...
int firstInd=-1;
for(auto&key:buttons){
if(buttons[key.first].size()>0){
firstInd=key.first;
break;
}
}
if(firstInd!=-1){ //This means we found a valid menu item. If we didn't find one don't highlight any menu item...
selection={0,firstInd};
}
}
}else{//Mouse click.
selection={-1,-1};
for(auto&key:buttons){
int index=0;
for(auto&button:key.second){
if(geom2d::overlaps(geom2d::rect<float>{button->rect.pos+menuPos,button->rect.size},game->GetMousePos())){
selection={index,key.first};
break;
}
index++;
}
}
MenuSelect(game);
}
}
}