@ -0,0 +1,8 @@ | |||
<?xml version="1.0"?> | |||
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | |||
<application xmlns="urn:schemas-microsoft-com:asm.v3"> | |||
<windowsSettings> | |||
<ms_windowsSettings:dpiAware xmlns:ms_windowsSettings="http://schemas.microsoft.com/SMI/2005/WindowsSettings" xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</ms_windowsSettings:dpiAware> | |||
</windowsSettings> | |||
</application> | |||
</assembly> |
@ -1,124 +0,0 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "level-select-state.hpp" | |||
#include "main-menu-state.hpp" | |||
#include "../application.hpp" | |||
#include "../configuration.hpp" | |||
#include "../camera-controller.hpp" | |||
LevelSelectState::LevelSelectState(Application* application): | |||
ApplicationState(application) | |||
{} | |||
LevelSelectState::~LevelSelectState() | |||
{} | |||
void LevelSelectState::enter() | |||
{ | |||
levelRotation = 0.0f; | |||
for (int i = 0; i < 5; ++i) | |||
{ | |||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||
Quaternion rotation = glm::angleAxis(levelRotation, Vector3(0, 1, 0)); | |||
surfaceInstance->setRotation(rotation); | |||
subsurfaceInstance->setRotation(rotation); | |||
application->defaultLayer->addObject(surfaceInstance); | |||
application->defaultLayer->addObject(subsurfaceInstance); | |||
} | |||
application->defaultLayer->addObject(&application->biomeFloorModelInstance); | |||
application->levelIDLabel->setVisible(true); | |||
application->levelNameLabel->setVisible(true); | |||
application->selectWorld(0); | |||
application->selectLevel(0); | |||
// Setup camera controller | |||
application->surfaceCam->setTargetFocalPoint(Vector3(0.0f)); | |||
application->surfaceCam->setTargetFocalDistance(350.0f); | |||
application->surfaceCam->setTargetElevation(glm::radians(85.0f)); | |||
application->surfaceCam->setTargetAzimuth(0.0f); | |||
application->surfaceCam->update(0.0f); | |||
} | |||
void LevelSelectState::execute() | |||
{ | |||
// Navigate menu | |||
if (application->menuLeft.isTriggered() && !application->menuLeft.wasTriggered()) | |||
{ | |||
application->selectPreviousLevel(); | |||
} | |||
else if (application->menuRight.isTriggered() && !application->menuRight.wasTriggered()) | |||
{ | |||
application->selectNextLevel(); | |||
} | |||
if (application->menuDown.isTriggered() && !application->menuDown.wasTriggered()) | |||
{ | |||
application->selectPreviousWorld(); | |||
} | |||
else if (application->menuUp.isTriggered() && !application->menuUp.wasTriggered()) | |||
{ | |||
application->selectNextWorld(); | |||
} | |||
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) | |||
{ | |||
application->enterSelectedLevel(); | |||
} | |||
else if (application->menuCancel.isTriggered() && !application->menuCancel.wasTriggered()) | |||
{ | |||
application->changeState(application->mainMenuState); | |||
} | |||
// Rotate levels | |||
levelRotation += glm::radians(5.0f) * application->dt; | |||
for (int i = 0; i < 5; ++i) | |||
{ | |||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||
Quaternion rotation = glm::angleAxis(levelRotation, Vector3(0, 1, 0)); | |||
//surfaceInstance->setRotation(rotation); | |||
//subsurfaceInstance->setRotation(rotation); | |||
} | |||
// Update camera | |||
application->surfaceCam->update(application->dt); | |||
} | |||
void LevelSelectState::exit() | |||
{ | |||
for (int i = 0; i < 5; ++i) | |||
{ | |||
ModelInstance* surfaceInstance = &application->previewLevelSurfaces[i]; | |||
ModelInstance* subsurfaceInstance = &application->previewLevelSubsurfaces[i]; | |||
application->defaultLayer->removeObject(surfaceInstance); | |||
application->defaultLayer->removeObject(subsurfaceInstance); | |||
} | |||
application->defaultLayer->removeObject(&application->biomeFloorModelInstance); | |||
application->levelIDLabel->setVisible(false); | |||
application->levelNameLabel->setVisible(false); | |||
} |
@ -1,42 +0,0 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef LEVEL_SELECT_STATE_HPP | |||
#define LEVEL_SELECT_STATE_HPP | |||
#include "../application-state.hpp" | |||
#include <emergent/emergent.hpp> | |||
using namespace Emergent; | |||
class LevelSelectState: public ApplicationState | |||
{ | |||
public: | |||
LevelSelectState(Application* application); | |||
virtual ~LevelSelectState(); | |||
virtual void enter(); | |||
virtual void execute(); | |||
virtual void exit(); | |||
private: | |||
float levelRotation; | |||
}; | |||
#endif // LEVEL_SELECT_STATE_HPP |
@ -1,299 +0,0 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "main-menu-state.hpp" | |||
#include "title-state.hpp" | |||
#include "../application.hpp" | |||
#include "../debug.hpp" | |||
#include "../camera-controller.hpp" | |||
void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft); | |||
void drawChamber(LineBatcher* lineBatcher, const Chamber* chamber) | |||
{ | |||
float helixAngle = chamber->parent->getHelixAngle(chamber->relativeDepth); | |||
float minAngle = helixAngle - chamber->centralAngle * 0.5f; | |||
float maxAngle = helixAngle + chamber->centralAngle * 0.5f; | |||
// Find position on helix | |||
Vector3 helixPosition = chamber->parent->getHelixPosition(chamber->relativeDepth); | |||
helixPosition.y = -helixPosition.y; | |||
// Move annulus toward helix by the inner radius | |||
Vector3 helixDirection = glm::normalize(Vector3(std::cos(helixAngle), 0.0f, std::sin(helixAngle))); | |||
Vector3 offset = helixPosition - helixDirection * (chamber->innerRadius - chamber->parent->shaftRadius); | |||
int stepCount = 10; | |||
float angleStep = chamber->centralAngle / (float)stepCount; | |||
for (int i = 0; i < stepCount; ++i) | |||
{ | |||
float angle0 = minAngle + angleStep * (float)i; | |||
float angle1 = minAngle + angleStep * (float)(i + 1); | |||
float x0 = std::cos(angle0); | |||
float z0 = std::sin(angle0); | |||
float x1 = std::cos(angle1); | |||
float z1 = std::sin(angle1); | |||
Vector3 innerStart; | |||
innerStart.x = x0 * chamber->innerRadius; | |||
innerStart.y = 0.0f; | |||
innerStart.z = z0 * chamber->innerRadius; | |||
Vector3 outerStart; | |||
outerStart.x = x0 * chamber->outerRadius; | |||
outerStart.y = 0.0f; | |||
outerStart.z = z0 * chamber->outerRadius; | |||
Vector3 innerEnd; | |||
innerEnd.x = x1 * chamber->innerRadius; | |||
innerEnd.y = 0.0f; | |||
innerEnd.z = z1 * chamber->innerRadius; | |||
Vector3 outerEnd; | |||
outerEnd.x = x1 * chamber->outerRadius; | |||
outerEnd.y = 0.0f; | |||
outerEnd.z = z1 * chamber->outerRadius; | |||
lineBatcher->draw(offset + innerStart, offset + innerEnd); | |||
lineBatcher->draw(offset + outerStart, offset + outerEnd); | |||
} | |||
Vector3 leftWallStart; | |||
leftWallStart.x = std::cos(minAngle) * chamber->innerRadius; | |||
leftWallStart.y = 0.0f; | |||
leftWallStart.z = std::sin(minAngle) * chamber->innerRadius; | |||
Vector3 leftWallEnd; | |||
leftWallEnd.x = std::cos(minAngle) * chamber->outerRadius; | |||
leftWallEnd.y = 0.0f; | |||
leftWallEnd.z = std::sin(minAngle) * chamber->outerRadius; | |||
Vector3 rightWallStart; | |||
rightWallStart.x = std::cos(maxAngle) * chamber->innerRadius; | |||
rightWallStart.y = 0.0f; | |||
rightWallStart.z = std::sin(maxAngle) * chamber->innerRadius; | |||
Vector3 rightWallEnd; | |||
rightWallEnd.x = std::cos(maxAngle) * chamber->outerRadius; | |||
rightWallEnd.y = 0.0f; | |||
rightWallEnd.z = std::sin(maxAngle) * chamber->outerRadius; | |||
lineBatcher->draw(offset + leftWallStart, offset + leftWallEnd); | |||
lineBatcher->draw(offset + rightWallStart, offset + rightWallEnd); | |||
if (chamber->child != nullptr) | |||
{ | |||
drawShaft(lineBatcher, chamber->child); | |||
} | |||
} | |||
void drawShaft(LineBatcher* lineBatcher, const Shaft* shaft) | |||
{ | |||
// Draw helix | |||
int stepCount = 50; | |||
float depthStep = shaft->shaftDepth / (float)stepCount; | |||
for (int i = 0; i < stepCount; ++i) | |||
{ | |||
Vector3 start = shaft->getHelixPosition((float)i * depthStep); | |||
Vector3 end = shaft->getHelixPosition((float)(i + 1) * depthStep); | |||
start.y = -start.y; | |||
end.y = -end.y; | |||
lineBatcher->draw(start, end); | |||
} | |||
// Draw children | |||
for (const Chamber* chamber: shaft->children) | |||
{ | |||
drawChamber(lineBatcher, chamber); | |||
} | |||
} | |||
void MainMenuState::generateNest() | |||
{ | |||
NestParameters params; | |||
params.randomSeed = std::rand(); | |||
params.maxShaftGeneration = 2; | |||
params.minShaftRadius = 0.0f; | |||
params.maxShaftRadius = 0.0f; | |||
params.minShaftDepth = 4.0f; | |||
params.maxShaftDepth = 6.0f; | |||
params.minShaftHelixRadius = 0.1f; | |||
params.maxShaftHelixRadius = 1.0f; | |||
params.minShaftHelixPitch = 0.25f; | |||
params.maxShaftHelixPitch = 0.75f; | |||
params.minShaftChamberCount = 1; | |||
params.maxShaftChamberCount = 5; | |||
params.minShaftChamberPitch = 0.5f; | |||
params.maxShaftChamberPitch = 2.0f; | |||
params.minChamberInnerRadius = 0.2f; | |||
params.maxChamberInnerRadius = 0.2f; | |||
params.minChamberOuterRadius = 0.5f; | |||
params.maxChamberOuterRadius = 0.5f; | |||
params.minChamberCentralAngle = glm::radians(240.0f); | |||
params.maxChamberCentralAngle = glm::radians(240.0f); | |||
nest.setParameters(params); | |||
nest.generate(); | |||
// Draw nest | |||
application->lineBatcher->setColor(Vector4(1.0f)); | |||
application->lineBatcher->setWidth(0.015f); | |||
application->lineBatcher->begin(); | |||
drawShaft(application->lineBatcher, nest.getRootShaft()); | |||
application->lineBatcher->end(); | |||
} | |||
MainMenuState::MainMenuState(Application* application): | |||
ApplicationState(application) | |||
{} | |||
MainMenuState::~MainMenuState() | |||
{} | |||
void MainMenuState::enter() | |||
{ | |||
// Open main menu | |||
application->enterMenu(0); | |||
application->menuSelectorLabel->setVisible(true); | |||
// Start fade-in | |||
application->blackoutImage->setVisible(true); | |||
application->fadeInTween->start(); | |||
application->backgroundLayer->addObject(&application->bgCamera); | |||
application->backgroundLayer->addObject(&application->bgBatch); | |||
// Add nest | |||
application->defaultLayer->addObject(&application->nestModelInstance); | |||
application->surfaceCam->setCamera(&application->camera); | |||
application->surfaceCam->setFocalPoint(Vector3(-10.0f, -13.3f, 0.0f)); | |||
application->surfaceCam->setFocalDistance(89.5f); | |||
application->surfaceCam->setElevation(glm::radians(15.0f)); | |||
application->surfaceCam->setAzimuth(glm::radians(0.0f)); | |||
application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getFocalPoint()); | |||
application->surfaceCam->setTargetFocalDistance(application->surfaceCam->getFocalDistance()); | |||
application->surfaceCam->setTargetElevation(application->surfaceCam->getElevation()); | |||
application->surfaceCam->setTargetAzimuth(application->surfaceCam->getAzimuth()); | |||
application->surfaceCam->update(0.0f); | |||
// 3D camera | |||
application->camera.setPerspective( | |||
glm::radians(25.0f), | |||
(float)application->width / (float)application->height, | |||
0.1f, | |||
1000.0f); | |||
} | |||
void MainMenuState::execute() | |||
{ | |||
// Navigate menu | |||
if (application->menuDown.isTriggered() && !application->menuDown.wasTriggered()) | |||
{ | |||
if (application->selectedMenuItemIndex < application->currentMenu->getItemCount() - 1) | |||
{ | |||
application->selectMenuItem(application->selectedMenuItemIndex + 1); | |||
} | |||
else | |||
{ | |||
application->selectMenuItem(0); | |||
} | |||
} | |||
else if (application->menuUp.isTriggered() && !application->menuUp.wasTriggered()) | |||
{ | |||
if (application->selectedMenuItemIndex > 0) | |||
{ | |||
application->selectMenuItem(application->selectedMenuItemIndex - 1); | |||
} | |||
else | |||
{ | |||
application->selectMenuItem(application->currentMenu->getItemCount() - 1); | |||
} | |||
} | |||
if (application->menuSelect.isTriggered() && !application->menuSelect.wasTriggered()) | |||
{ | |||
application->activateMenuItem(application->selectedMenuItemIndex); | |||
} | |||
else if (application->menuCancel.isTriggered() && !application->menuCancel.wasTriggered()) | |||
{ | |||
application->changeState(application->titleState); | |||
} | |||
float lineHeight = application->menuFont->getMetrics().getHeight(); | |||
const UIContainer* container = application->menuContainers[application->currentMenuIndex]; | |||
application->menuSelectorLabel->setTranslation( | |||
Vector2(container->getPosition().x - application->menuSelectorLabel->getDimensions().x * 1.5f, | |||
container->getPosition().y + lineHeight * 0.5f - application->menuSelectorLabel->getDimensions().y * 0.5f + lineHeight * application->selectedMenuItemIndex)); | |||
// Move camera | |||
Vector2 movementVector(0.0f); | |||
if (application->cameraMoveLeft.isTriggered()) | |||
movementVector.x -= application->cameraMoveLeft.getCurrentValue(); | |||
if (application->cameraMoveRight.isTriggered()) | |||
movementVector.x += application->cameraMoveRight.getCurrentValue(); | |||
if (application->cameraMoveForward.isTriggered()) | |||
movementVector.y -= application->cameraMoveForward.getCurrentValue(); | |||
if (application->cameraMoveBack.isTriggered()) | |||
movementVector.y += application->cameraMoveBack.getCurrentValue(); | |||
if (movementVector.x != 0.0f || movementVector.y != 0.0f) | |||
{ | |||
movementVector *= 0.005f * application->surfaceCam->getFocalDistance() * application->dt / (1.0f / 60.0f); | |||
application->surfaceCam->setTargetFocalPoint(application->surfaceCam->getTargetFocalPoint() + Vector3(movementVector.x, -movementVector.y, 0.0f)); | |||
Vector3 focal = application->surfaceCam->getFocalPoint(); | |||
std::cout << focal.x << ", " << focal.y << ", " << focal.z << "; " << application->surfaceCam->getFocalDistance() << std::endl; | |||
} | |||
// Zoom camera | |||
float zoomFactor = application->surfaceCam->getFocalDistance() / 20.0f * application->dt / (1.0f / 60.0f); | |||
if (application->cameraZoomIn.isTriggered()) | |||
application->surfaceCam->zoom(zoomFactor * application->cameraZoomIn.getCurrentValue()); | |||
if (application->cameraZoomOut.isTriggered()) | |||
application->surfaceCam->zoom(-zoomFactor * application->cameraZoomOut.getCurrentValue()); | |||
application->surfaceCam->update(application->dt); | |||
application->surfaceCam->setTargetAzimuth(application->surfaceCam->getTargetAzimuth() + glm::radians(2.0f) * application->dt); | |||
application->surfaceCam->update(application->dt); | |||
} | |||
void MainMenuState::exit() | |||
{ | |||
// Hide UI | |||
application->menuSelectorLabel->setVisible(false); | |||
// Clear scene | |||
application->defaultLayer->removeObject(&application->nestModelInstance); | |||
application->backgroundLayer->removeObject(&application->bgCamera); | |||
application->backgroundLayer->removeObject(&application->bgBatch); | |||
} | |||
void MainMenuState::mouseButtonPressed(int button, int x, int y) | |||
{ | |||
} | |||
void MainMenuState::mouseButtonReleased(int button, int x, int y) | |||
{ | |||
} |
@ -1,49 +0,0 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef MAIN_MENU_STATE_HPP | |||
#define MAIN_MENU_STATE_HPP | |||
#include "../application-state.hpp" | |||
#include "../input.hpp" | |||
#include "../game/nest.hpp" | |||
#include <emergent/emergent.hpp> | |||
using namespace Emergent; | |||
class MainMenuState: public ApplicationState, public MouseButtonObserver | |||
{ | |||
public: | |||
MainMenuState(Application* application); | |||
virtual ~MainMenuState(); | |||
virtual void enter(); | |||
virtual void execute(); | |||
virtual void exit(); | |||
virtual void mouseButtonPressed(int button, int x, int y); | |||
virtual void mouseButtonReleased(int button, int x, int y); | |||
private: | |||
void generateNest(); | |||
Nest nest; | |||
}; | |||
#endif // MAIN_MENU_STATE_HPP |
@ -0,0 +1,247 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#include "menu.hpp" | |||
#include "ui.hpp" | |||
#include <algorithm> | |||
MenuItem::MenuItem(Menu* parent, std::size_t index): | |||
parent(parent), | |||
index(index), | |||
selectedCallback(nullptr), | |||
deselectedCallback(nullptr), | |||
activatedCallback(nullptr) | |||
{ | |||
label = new UILabel(); | |||
label->setMouseOverCallback(std::bind(&Menu::select, parent, index)); | |||
label->setMouseMovedCallback(std::bind(&Menu::select, parent, index)); | |||
label->setMousePressedCallback(std::bind(&Menu::activate, parent)); | |||
} | |||
MenuItem::~MenuItem() | |||
{ | |||
delete label; | |||
} | |||
void MenuItem::select() | |||
{ | |||
if (selectedCallback != nullptr) | |||
{ | |||
selectedCallback(); | |||
} | |||
} | |||
void MenuItem::deselect() | |||
{ | |||
if (deselectedCallback != nullptr) | |||
{ | |||
deselectedCallback(); | |||
} | |||
} | |||
void MenuItem::activate() | |||
{ | |||
if (activatedCallback != nullptr) | |||
{ | |||
activatedCallback(); | |||
} | |||
} | |||
void MenuItem::setSelectedCallback(std::function<void()> callback) | |||
{ | |||
this->selectedCallback = callback = callback; | |||
} | |||
void MenuItem::setDeselectedCallback(std::function<void()> callback) | |||
{ | |||
this->deselectedCallback = callback; | |||
} | |||
void MenuItem::setActivatedCallback(std::function<void()> callback) | |||
{ | |||
this->activatedCallback = callback; | |||
} | |||
void MenuItem::setLabel(const std::string& text) | |||
{ | |||
label->setText(text); | |||
parent->resize(); | |||
} | |||
bool MenuItem::isSelected() const | |||
{ | |||
return (parent->getSelectedItem() == this); | |||
} | |||
Menu::Menu(): | |||
selectedItem(nullptr), | |||
enteredCallback(nullptr), | |||
exitedCallback(nullptr), | |||
font(nullptr), | |||
lineSpacing(1.0f) | |||
{ | |||
container = new UIContainer(); | |||
resize(); | |||
} | |||
Menu::~Menu() | |||
{ | |||
removeItems(); | |||
delete container; | |||
} | |||
void Menu::enter() | |||
{ | |||
if (enteredCallback != nullptr) | |||
{ | |||
enteredCallback(); | |||
} | |||
} | |||
void Menu::exit() | |||
{ | |||
if (exitedCallback != nullptr) | |||
{ | |||
exitedCallback(); | |||
} | |||
} | |||
MenuItem* Menu::addItem() | |||
{ | |||
// Allocate item and add to items | |||
MenuItem* item = new MenuItem(this, items.size()); | |||
items.push_back(item); | |||
// Set item label font | |||
item->label->setFont(font); | |||
item->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f)); | |||
// Add item label to UI container | |||
container->addChild(item->label); | |||
// Resize UI container | |||
resize(); | |||
return item; | |||
} | |||
void Menu::removeItems() | |||
{ | |||
for (MenuItem* item: items) | |||
{ | |||
// Remove label from UI container | |||
container->removeChild(item->label); | |||
delete item; | |||
} | |||
items.clear(); | |||
resize(); | |||
} | |||
void Menu::setEnteredCallback(std::function<void()> callback) | |||
{ | |||
this->enteredCallback = callback; | |||
} | |||
void Menu::setExitedCallback(std::function<void()> callback) | |||
{ | |||
this->exitedCallback = callback; | |||
} | |||
void Menu::setFont(Font* font) | |||
{ | |||
this->font = font; | |||
for (MenuItem* item: items) | |||
{ | |||
item->label->setFont(font); | |||
} | |||
resize(); | |||
} | |||
void Menu::setLineSpacing(float spacing) | |||
{ | |||
lineSpacing = spacing; | |||
resize(); | |||
} | |||
void Menu::deselect() | |||
{ | |||
if (selectedItem != nullptr) | |||
{ | |||
selectedItem->deselect(); | |||
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.35f)); | |||
selectedItem = nullptr; | |||
} | |||
} | |||
void Menu::select(std::size_t index) | |||
{ | |||
deselect(); | |||
MenuItem* item = items[index]; | |||
item->select(); | |||
selectedItem = item; | |||
selectedItem->label->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 1.0f)); | |||
} | |||
void Menu::activate() | |||
{ | |||
if (selectedItem != nullptr) | |||
{ | |||
selectedItem->activate(); | |||
} | |||
} | |||
void Menu::resize() | |||
{ | |||
if (!font) | |||
{ | |||
container->setDimensions(Vector2(0.0f)); | |||
} | |||
else | |||
{ | |||
Vector2 dimensions(0.0f); | |||
for (std::size_t i = 0; i < items.size(); ++i) | |||
{ | |||
const MenuItem* item = items[i]; | |||
item->label->setTranslation(Vector2(0.0f, static_cast<int>(font->getMetrics().getHeight() * lineSpacing * static_cast<float>(i)))); | |||
dimensions.x = std::max<float>(dimensions.x, item->label->getDimensions().x); | |||
if (!i) | |||
{ | |||
dimensions.y += font->getMetrics().getHeight(); | |||
} | |||
else | |||
{ | |||
dimensions.y += font->getMetrics().getHeight() * lineSpacing; | |||
} | |||
} | |||
container->setDimensions(dimensions); | |||
} | |||
} |
@ -0,0 +1,161 @@ | |||
/* | |||
* Copyright (C) 2017 Christopher J. Howard | |||
* | |||
* This file is part of Antkeeper Source Code. | |||
* | |||
* Antkeeper Source Code is free software: you can redistribute it and/or modify | |||
* it under the terms of the GNU General Public License as published by | |||
* the Free Software Foundation, either version 3 of the License, or | |||
* (at your option) any later version. | |||
* | |||
* Antkeeper Source Code is distributed in the hope that it will be useful, | |||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |||
* GNU General Public License for more details. | |||
* | |||
* You should have received a copy of the GNU General Public License | |||
* along with Antkeeper Source Code. If not, see <http://www.gnu.org/licenses/>. | |||
*/ | |||
#ifndef MENU_HPP | |||
#define MENU_HPP | |||
#include <emergent/emergent.hpp> | |||
using namespace Emergent; | |||
#include <functional> | |||
#include <string> | |||
#include <vector> | |||
class Menu; | |||
class UIContainer; | |||
class UILabel; | |||
class MenuItem | |||
{ | |||
public: | |||
void setSelectedCallback(std::function<void()> callback); | |||
void setDeselectedCallback(std::function<void()> callback); | |||
void setActivatedCallback(std::function<void()> callback); | |||
void setValueChangedCallback(std::function<void(std::size_t)> callback); | |||
void setLabel(const std::string& text); | |||
std::size_t getIndex() const; | |||
bool isSelected() const; | |||
private: | |||
friend class Menu; | |||
MenuItem(Menu* parent, std::size_t index); | |||
~MenuItem(); | |||
void select(); | |||
void deselect(); | |||
void activate(); | |||
Menu* parent; | |||
std::size_t index; | |||
std::function<void()> selectedCallback; | |||
std::function<void()> deselectedCallback; | |||
std::function<void()> activatedCallback; | |||
UILabel* label; | |||
}; | |||
inline std::size_t MenuItem::getIndex() const | |||
{ | |||
return index; | |||
} | |||
class Menu | |||
{ | |||
public: | |||
Menu(); | |||
~Menu(); | |||
void enter(); | |||
void exit(); | |||
MenuItem* addItem(); | |||
void removeItems(); | |||
void setEnteredCallback(std::function<void()> callback); | |||
void setExitedCallback(std::function<void()> callback); | |||
void setFont(Font* font); | |||
void setLineSpacing(float spacing); | |||
std::size_t getItemCount(); | |||
const MenuItem* getItem(std::size_t index) const; | |||
MenuItem* getItem(std::size_t index); | |||
const MenuItem* getSelectedItem() const; | |||
MenuItem* getSelectedItem(); | |||
const UIContainer* getUIContainer() const; | |||
UIContainer* getUIContainer(); | |||
/** | |||
* Deselects the currently selected item (if any) | |||
*/ | |||
void deselect(); | |||
/** | |||
* Selects the item at the specified index | |||
*/ | |||
void select(std::size_t index); | |||
/** | |||
* Activates the selected item (if any) | |||
*/ | |||
void activate(); | |||
/** | |||
* Recalculates the dimensions of the UI container according the dimensions of the menu item labels and the line spacing. | |||
*/ | |||
void resize(); | |||
private: | |||
friend class MenuItem; | |||
std::vector<MenuItem*> items; | |||
MenuItem* selectedItem; | |||
std::function<void()> enteredCallback; | |||
std::function<void()> exitedCallback; | |||
Font* font; | |||
float lineSpacing; | |||
UIContainer* container; | |||
}; | |||
inline std::size_t Menu::getItemCount() | |||
{ | |||
return items.size(); | |||
} | |||
inline const MenuItem* Menu::getItem(std::size_t index) const | |||
{ | |||
return items[index]; | |||
} | |||
inline MenuItem* Menu::getItem(std::size_t index) | |||
{ | |||
return items[index]; | |||
} | |||
inline const MenuItem* Menu::getSelectedItem() const | |||
{ | |||
return selectedItem; | |||
} | |||
inline MenuItem* Menu::getSelectedItem() | |||
{ | |||
return selectedItem; | |||
} | |||
inline const UIContainer* Menu::getUIContainer() const | |||
{ | |||
return container; | |||
} | |||
inline UIContainer* Menu::getUIContainer() | |||
{ | |||
return container; | |||
} | |||
#endif // MENU_HPP |