diff --git a/src/game-states.hpp b/src/game-states.hpp new file mode 100644 index 0000000..d8d3991 --- /dev/null +++ b/src/game-states.hpp @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#ifndef GAME_STATES_HPP +#define GAME_STATES_HPP + +#include + + + +#endif // GAME_STATES_HPP + diff --git a/src/game.cpp b/src/game.cpp index 56724a3..033c5ab 100644 --- a/src/game.cpp +++ b/src/game.cpp @@ -18,9 +18,9 @@ */ #include "game.hpp" +#include "game-states.hpp" #include "resources/csv-table.hpp" #include "states/game-state.hpp" -#include "states/splash-state.hpp" #include "states/sandbox-state.hpp" #include "filesystem.hpp" #include "timestamp.hpp" @@ -56,6 +56,7 @@ #include "entity/systems/particle-system.hpp" #include "entity/systems/terrain-system.hpp" #include "stb/stb_image_write.h" +#include "menu.hpp" #include #include #include @@ -202,7 +203,10 @@ Game::Game(int argc, char* argv[]): resourceManager->include(configPath); resourceManager->include(dataPath); - splashState = new SplashState(this); + // Subscribe the game to scheduled function events + eventDispatcher.subscribe(this); + toggleFullscreenDisabled = false; + sandboxState = new SandboxState(this); } @@ -228,14 +232,14 @@ void Game::changeState(GameState* state) } } -std::string Game::getString(std::size_t languageIndex, const std::string& name) const +std::string Game::getString(const std::string& name) const { std::string value; auto it = stringMap.find(name); if (it != stringMap.end()) { - value = (*stringTable)[it->second][languageIndex + 1]; + value = (*stringTable)[it->second][languageIndex + 2]; if (value.empty()) { value = std::string("# EMPTY STRING: ") + name + std::string(" #"); @@ -249,19 +253,235 @@ std::string Game::getString(std::size_t languageIndex, const std::string& name) return value; } -void Game::changeLanguage(std::size_t languageIndex) +void Game::changeLanguage(std::size_t nextLanguageIndex) { - this->languageIndex = languageIndex; - window->setTitle(getString(getLanguageIndex(), "title").c_str()); + // Get names of fonts + std::string menuFontFilename = getString("menu-font-filename"); + + // Unload fonts + delete menuFont; + resourceManager->unload(menuFontFilename); + + // Change current language index + languageIndex = nextLanguageIndex; + + // Reload fonts + loadFonts(); + // Set window title + window->setTitle(getString("title").c_str()); + + // Repopulate UI element strings restringUI(); + + // Resize the UI resizeUI(w, h); + + // Reselect menu item + if (currentMenuItem) + { + menuSelectorSlideAnimation.stop(); + selectMenuItem(menuItemIndex, false); + + uiRootElement->update(); + currentMenu->getContainer()->resetTweens(); + } +} + +void Game::nextLanguage() +{ + changeLanguage((getLanguageIndex() + 1) % getLanguageCount()); +} + +void Game::openMenu(Menu* menu, int selectedItemIndex) +{ + if (currentMenu) + { + closeCurrentMenu(); + } + + currentMenu = menu; + uiRootElement->addChild(currentMenu->getContainer()); + currentMenu->getContainer()->addChild(menuSelectorImage); + currentMenu->getContainer()->setTintColor(Vector4(1.0f)); + + for (MenuItem* item: *currentMenu->getItems()) + { + item->getContainer()->setTintColor(menuItemInactiveColor); + } + + selectMenuItem(selectedItemIndex, false); + + uiRootElement->update(); + currentMenu->getContainer()->resetTweens(); +} + +void Game::closeCurrentMenu() +{ + uiRootElement->removeChild(currentMenu->getContainer()); + currentMenu->getContainer()->removeChild(menuSelectorImage); + currentMenu->getContainer()->setTintColor(Vector4(1.0f)); + for (MenuItem* item: *currentMenu->getItems()) + { + item->getContainer()->setTintColor(menuItemInactiveColor); + } + + currentMenu = nullptr; + currentMenuItem = nullptr; + menuItemIndex = -1; + + menuFadeAnimation.stop(); + menuSelectorSlideAnimation.stop(); + menuItemSelectAnimation.stop(); + menuItemDeselectAnimation.stop(); + + previousMenu = currentMenu; + currentMenu = nullptr; +} + +void Game::selectMenuItem(int index, bool tween) +{ + bool reselected = false; + + if (index != menuItemIndex) + { + if (menuItemSelectAnimation.isPlaying()) + { + menuItemSelectAnimation.stop(); + currentMenuItem->getContainer()->setTintColor(menuItemActiveColor); + } + + if (menuItemDeselectAnimation.isPlaying()) + { + menuItemDeselectAnimation.stop(); + previousMenuItem->getContainer()->setTintColor(menuItemInactiveColor); + } + + // Save previous menu item + previousMenuItem = currentMenuItem; + + // Determine current menu item + menuItemIndex = index; + currentMenuItem = (*(currentMenu->getItems()))[index]; + } + else + { + reselected = true; + } + + // Determine target position of menu item selector + Vector2 itemTranslation = currentMenuItem->getContainer()->getTranslation(); + Vector2 itemDimensions = currentMenuItem->getContainer()->getDimensions(); + float spacing = currentMenuItem->getNameLabel()->getFont()->getWidth("A"); + Vector2 translation; + translation.x = itemTranslation.x - menuSelectorImage->getDimensions().x - spacing; + translation.y = itemTranslation.y + itemDimensions.y * 0.5f - menuSelectorImage->getDimensions().y * 0.5; + + // Create tween animations + if (!reselected && tween && previousMenuItem != nullptr) + { + float tweenDuration = 0.2f; + + Vector2 oldTranslation = menuSelectorImage->getTranslation(); + Vector2 newTranslation = translation; + + // Slide animation + { + menuSelectorSlideClip.removeChannels(); + AnimationChannel* channel = menuSelectorSlideClip.addChannel(0); + channel->insertKeyframe(0.0f, oldTranslation.y); + channel->insertKeyframe(tweenDuration, newTranslation.y); + menuSelectorSlideAnimation.setTimeFrame(menuSelectorSlideClip.getTimeFrame()); + menuSelectorSlideAnimation.rewind(); + menuSelectorSlideAnimation.play(); + } + + // Color animations + { + menuItemSelectClip.removeChannels(); + AnimationChannel* channel = menuItemSelectClip.addChannel(0); + channel->insertKeyframe(0.0f, menuItemInactiveColor); + channel->insertKeyframe(tweenDuration, menuItemActiveColor); + menuItemSelectAnimation.setTimeFrame(menuItemSelectClip.getTimeFrame()); + menuItemSelectAnimation.rewind(); + menuItemSelectAnimation.play(); + + if (previousMenuItem) + { + menuItemDeselectClip.removeChannels(); + channel = menuItemDeselectClip.addChannel(0); + channel->insertKeyframe(0.0f, menuItemActiveColor); + channel->insertKeyframe(tweenDuration, menuItemInactiveColor); + menuItemDeselectAnimation.setTimeFrame(menuItemDeselectClip.getTimeFrame()); + menuItemDeselectAnimation.rewind(); + menuItemDeselectAnimation.play(); + } + } + + menuSelectorImage->setTranslation(Vector2(newTranslation.x, oldTranslation.y)); + } + else if (!tween) + { + menuSelectorImage->setTranslation(translation); + currentMenuItem->getContainer()->setTintColor(menuItemActiveColor); + + if (previousMenuItem) + { + previousMenuItem->getContainer()->setTintColor(menuItemInactiveColor); + } + } +} + +void Game::selectNextMenuItem() +{ + int index = (menuItemIndex + 1) % currentMenu->getItems()->size(); + selectMenuItem(index, true); +} + +void Game::selectPreviousMenuItem() +{ + int index = (menuItemIndex + (currentMenu->getItems()->size() - 1)) % currentMenu->getItems()->size(); + selectMenuItem(index, true); +} + +void Game::activateMenuItem() +{ + currentMenuItem->activate(); +} + +void Game::activateLastMenuItem() +{ + if (currentMenu) + { + (*currentMenu->getItems())[currentMenu->getItems()->size() - 1]->activate(); + } } void Game::toggleFullscreen() { - fullscreen = !fullscreen; - window->setFullscreen(fullscreen); + if (!toggleFullscreenDisabled) + { + fullscreen = !fullscreen; + window->setFullscreen(fullscreen); + restringUI(); + + // Disable fullscreen toggles for 500ms + toggleFullscreenDisabled = true; + ScheduledFunctionEvent event; + event.caller = static_cast(this); + event.function = [this]() + { + toggleFullscreenDisabled = false; + }; + eventDispatcher.schedule(event, time + 0.5f); + } +} + +void Game::toggleVSync() +{ + vsync = !vsync; + window->setVSync(vsync); + restringUI(); } void Game::setUpdateRate(double frequency) @@ -276,14 +496,10 @@ void Game::setup() setupLocalization(); setupWindow(); setupGraphics(); - setupUI(); setupControls(); + setupUI(); setupGameplay(); - #if defined(DEBUG) - toggleWireframe(); - #endif // DEBUG - screenshotQueued = false; @@ -315,13 +531,7 @@ void Game::setup() worldScene->addObject(lens->getSpotlight()); lens->setSunDirection(-sunlightCamera.getForward()); - ModelInstance* modelInstance = lens->getModelInstance(); - for (std::size_t i = 0; i < modelInstance->getModel()->getGroupCount(); ++i) - { - Material* material = modelInstance->getModel()->getGroup(i)->material->clone(); - material->setFlags(material->getFlags() | 256); - modelInstance->setMaterialSlot(i, material); - } + // Forceps forceps = new Forceps(forcepsModel, &animator); @@ -361,7 +571,6 @@ void Game::setup() particleSystem->getBillboardBatch()->setAlignment(&camera, BillboardAlignmentMode::SPHERICAL); worldScene->addObject(particleSystem->getBillboardBatch()); - // Initialize system manager systemManager = new SystemManager(); systemManager->addSystem(soundSystem); @@ -375,21 +584,6 @@ void Game::setup() systemManager->addSystem(cameraSystem); systemManager->addSystem(renderSystem); - /* - EntityID sidewalkPanel; - sidewalkPanel = createInstanceOf("sidewalk-panel"); - - EntityID antHill = createInstanceOf("ant-hill"); - setTranslation(antHill, Vector3(20, 0, 40)); - - EntityID antNest = createInstanceOf("ant-nest"); - setTranslation(antNest, Vector3(20, 0, 40)); - - EntityID lollipop = createInstanceOf("lollipop"); - setTranslation(lollipop, Vector3(30.0f, 3.5f * 0.5f, -30.0f)); - setRotation(lollipop, glm::angleAxis(glm::radians(8.85f), Vector3(1.0f, 0.0f, 0.0f))); - */ - // Load navmesh TriangleMesh* navmesh = resourceManager->load("sidewalk.mesh"); @@ -467,6 +661,31 @@ void Game::setup() } } + // Setup state machine states + splashState = + { + std::bind(&Game::enterSplashState, this), + std::bind(&Game::exitSplashState, this) + }; + loadingState = + { + std::bind(&Game::enterLoadingState, this), + std::bind(&Game::exitLoadingState, this) + }; + titleState = + { + std::bind(&Game::enterTitleState, this), + std::bind(&Game::exitTitleState, this) + }; + playState = + { + std::bind(&Game::enterPlayState, this), + std::bind(&Game::exitPlayState, this) + }; + + // Initialize state machine + StateMachine::changeState(&titleState); + changeState(sandboxState); } @@ -547,6 +766,8 @@ void Game::handleEvent(const WindowResizedEvent& event) toolSystem->setPickingViewport(Vector4(0, 0, w, h)); resizeUI(event.width, event.height); + + skipSplash(); } void Game::handleEvent(const GamepadConnectedEvent& event) @@ -555,12 +776,20 @@ void Game::handleEvent(const GamepadConnectedEvent& event) inputRouter->reset(); // Reload control profile - loadControlProfile(); + loadControlProfile(controlProfileName); } void Game::handleEvent(const GamepadDisconnectedEvent& event) {} +void Game::handleEvent(const ScheduledFunctionEvent& event) +{ + if (event.caller == static_cast(this)) + { + event.function(); + } +} + void Game::setupDebugging() { // Setup performance sampling @@ -576,16 +805,16 @@ void Game::setupLocalization() loadStrings(); // Determine number of available languages - languageCount = (*stringTable)[0].size() - 1; + languageCount = (*stringTable)[0].size() - 2; // Match language code with language index languageIndex = 0; - CSVRow* languageCodes = &(*stringTable)[0]; - for (std::size_t i = 1; i < languageCodes->size(); ++i) + CSVRow* languageCodes = &(*stringTable)[1]; + for (std::size_t i = 2; i < languageCodes->size(); ++i) { if (language == (*languageCodes)[i]) { - languageIndex = i - 1; + languageIndex = i - 2; break; } } @@ -614,7 +843,7 @@ void Game::setupWindow() int y = std::get<1>(display->getPosition()) + displayHeight / 2 - h / 2; // Read title string - std::string title = getString(getLanguageIndex(), "title"); + std::string title = getString("title"); // Create window window = windowManager->createWindow(title.c_str(), x, y, w, h, fullscreen, WindowFlag::RESIZABLE); @@ -625,6 +854,11 @@ void Game::setupWindow() // Set v-sync mode window->setVSync(vsync); + + debugTypeface = nullptr; + debugFont = nullptr; + menuTypeface = nullptr; + menuFont = nullptr; } void Game::setupGraphics() @@ -779,20 +1013,8 @@ void Game::setupUI() dpi = display->getDPI(); fontSizePX = fontSizePT * (1.0f / 72.0f) * dpi; - // Load label typeface - labelTypeface = resourceManager->load("caveat-bold.ttf"); - labelFont = labelTypeface->createFont(fontSizePX); - - // Load debugging typeface - debugTypeface = resourceManager->load("inconsolata-bold.ttf"); - debugFont = debugTypeface->createFont(fontSizePX); - debugTypeface->loadCharset(debugFont, UnicodeRange::BASIC_LATIN); - - // Character set test - std::set charset; - charset.emplace(U'A'); - labelTypeface->loadCharset(labelFont, UnicodeRange::BASIC_LATIN); - labelTypeface->loadCharset(labelFont, charset); + // Load fonts + loadFonts(); // Load splash screen texture splashTexture = resourceManager->load("epigraph.png"); @@ -1060,8 +1282,8 @@ void Game::setupUI() antLabelContainer->addChild(antLabelCR); antLabel = new UILabel(); - antLabel->setFont(labelFont); - antLabel->setText("Boggy B."); + antLabel->setFont(nullptr); + antLabel->setText(""); antLabel->setTintColor(Vector4(Vector3(0.0f), 1.0f)); antLabel->setLayerOffset(1); antLabelContainer->addChild(antLabel); @@ -1136,6 +1358,7 @@ void Game::setupUI() cameraGridContainer->setVisible(false); uiRootElement->addChild(cameraGridContainer); + cameraFlashImage = new UIImage(); cameraFlashImage->setLayerOffset(99); cameraFlashImage->setTintColor(Vector4(1.0f)); @@ -1148,9 +1371,230 @@ void Game::setupUI() blackoutImage->setVisible(false); uiRootElement->addChild(blackoutImage); + menuItemActiveColor = Vector4(Vector3(0.2f), 1.0f); + menuItemInactiveColor = Vector4(Vector3(0.2f), 0.5f); + + menuItemIndex = -1; + currentMenu = nullptr; + currentMenuItem = nullptr; + previousMenuItem = nullptr; + previousMenu = nullptr; + + + + menuSelectorImage = new UIImage(); + menuSelectorImage->setAnchor(Anchor::TOP_LEFT); + menuSelectorImage->setTexture(hudSpriteSheetTexture); + menuSelectorImage->setTextureBounds(normalizeTextureBounds(hudTextureAtlas.getBounds("menu-selector"), hudTextureAtlasBounds)); + menuSelectorImage->setTintColor(menuItemActiveColor); + + // Build main menu + mainMenu = new Menu(); + mainMenuContinueItem = mainMenu->addItem(); + mainMenuNewGameItem = mainMenu->addItem(); + mainMenuColoniesItem = mainMenu->addItem(); + mainMenuSettingsItem = mainMenu->addItem(); + mainMenuQuitItem = mainMenu->addItem(); + + // Build settings menu + settingsMenu = new Menu(); + settingsMenuControlsItem = settingsMenu->addItem(); + settingsMenuFullscreenItem = settingsMenu->addItem(); + settingsMenuVSyncItem = settingsMenu->addItem(); + settingsMenuLanguageItem = settingsMenu->addItem(); + settingsMenuBackItem = settingsMenu->addItem(); + + // Build controls menu + controlsMenu = new Menu(); + controlsMenuMoveForwardItem = controlsMenu->addItem(); + controlsMenuMoveLeftItem = controlsMenu->addItem(); + controlsMenuMoveBackItem = controlsMenu->addItem(); + controlsMenuMoveRightItem = controlsMenu->addItem(); + controlsMenuChangeToolItem = controlsMenu->addItem(); + controlsMenuUseToolItem = controlsMenu->addItem(); + controlsMenuAdjustCameraItem = controlsMenu->addItem(); + controlsMenuToggleFullscreenItem = controlsMenu->addItem(); + controlsMenuTakeScreenshotItem = controlsMenu->addItem(); + controlsMenuResetToDefaultItem = controlsMenu->addItem(); + controlsMenuBackItem = controlsMenu->addItem(); + + // Setup main menu callbacks + mainMenuSettingsItem->setActivatedCallback(std::bind(&Game::openMenu, this, settingsMenu, 0)); + mainMenuQuitItem->setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); + + // Setup settings menu callbacks + settingsMenuControlsItem->setActivatedCallback(std::bind(&Game::openMenu, this, controlsMenu, 0)); + settingsMenuFullscreenItem->setActivatedCallback(std::bind(&Game::toggleFullscreen, this)); + settingsMenuVSyncItem->setActivatedCallback(std::bind(&Game::toggleVSync, this)); + settingsMenuLanguageItem->setActivatedCallback(std::bind(&Game::nextLanguage, this)); + settingsMenuBackItem->setActivatedCallback(std::bind(&Game::openMenu, this, mainMenu, 3)); + + // Setup controls menu callbacks + controlsMenuMoveForwardItem->setActivatedCallback(std::bind(&Game::remapControl, this, &moveForwardControl)); + + controlsMenuMoveLeftItem->setActivatedCallback(std::bind(&Game::remapControl, this, &moveLeftControl)); + controlsMenuMoveBackItem->setActivatedCallback(std::bind(&Game::remapControl, this, &moveBackControl)); + controlsMenuMoveRightItem->setActivatedCallback(std::bind(&Game::remapControl, this, &moveRightControl)); + controlsMenuChangeToolItem->setActivatedCallback(std::bind(&Game::remapControl, this, &changeToolControl)); + controlsMenuUseToolItem->setActivatedCallback(std::bind(&Game::remapControl, this, &useToolControl)); + controlsMenuAdjustCameraItem->setActivatedCallback(std::bind(&Game::remapControl, this, &adjustCameraControl)); + controlsMenuToggleFullscreenItem->setActivatedCallback(std::bind(&Game::remapControl, this, &toggleFullscreenControl)); + controlsMenuTakeScreenshotItem->setActivatedCallback(std::bind(&Game::remapControl, this, &takeScreenshotControl)); + controlsMenuResetToDefaultItem->setActivatedCallback(std::bind(&Game::resetControls, this)); + controlsMenuBackItem->setActivatedCallback(std::bind(&Game::openMenu, this, settingsMenu, 0)); + + // Setup standard callbacks for all menu items + for (std::size_t i = 0; i < mainMenu->getItems()->size(); ++i) + { + MenuItem* item = (*mainMenu->getItems())[i]; + item->getContainer()->setTintColor(menuItemInactiveColor); + item->getContainer()->setMouseOverCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMouseMovedCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMousePressedCallback(std::bind(&Game::activateMenuItem, this)); + } + + for (std::size_t i = 0; i < settingsMenu->getItems()->size(); ++i) + { + MenuItem* item = (*settingsMenu->getItems())[i]; + item->getContainer()->setTintColor(menuItemInactiveColor); + item->getContainer()->setMouseOverCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMouseMovedCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMousePressedCallback(std::bind(&Game::activateMenuItem, this)); + } + + for (std::size_t i = 0; i < controlsMenu->getItems()->size(); ++i) + { + MenuItem* item = (*controlsMenu->getItems())[i]; + item->getContainer()->setTintColor(menuItemInactiveColor); + item->getContainer()->setMouseOverCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMouseMovedCallback(std::bind(&Game::selectMenuItem, this, i, true)); + item->getContainer()->setMousePressedCallback(std::bind(&Game::activateMenuItem, this)); + } + + // Set fonts for all menus + mainMenu->setFonts(menuFont); + settingsMenu->setFonts(menuFont); + controlsMenu->setFonts(menuFont); + + AnimationChannel* channel; + + // Setup splash fade-in animation + splashFadeInClip.setInterpolator(easeOutCubic); + channel = splashFadeInClip.addChannel(0); + channel->insertKeyframe(0.0f, 0.0f); + channel->insertKeyframe(1.0f, 1.0f); + channel->insertKeyframe(3.0f, 1.0f); + splashFadeInAnimation.setClip(&splashFadeInClip); + splashFadeInAnimation.setTimeFrame(splashFadeInClip.getTimeFrame()); + splashFadeInAnimation.setAnimateCallback + ( + [this](std::size_t id, float opacity) + { + Vector3 color = Vector3(splashImage->getTintColor()); + splashImage->setTintColor(Vector4(color, opacity)); + } + ); + splashFadeInAnimation.setEndCallback + ( + [this]() + { + splashFadeOutAnimation.rewind(); + splashFadeOutAnimation.play(); + } + ); + + // Setup splash fade-out animation + splashFadeOutClip.setInterpolator(easeOutCubic); + channel = splashFadeOutClip.addChannel(0); + channel->insertKeyframe(0.0f, 1.0f); + channel->insertKeyframe(1.0f, 0.0f); + channel->insertKeyframe(1.5f, 0.0f); + splashFadeOutAnimation.setClip(&splashFadeOutClip); + splashFadeOutAnimation.setTimeFrame(splashFadeOutClip.getTimeFrame()); + splashFadeOutAnimation.setAnimateCallback + ( + [this](std::size_t id, float opacity) + { + Vector3 color = Vector3(splashImage->getTintColor()); + splashImage->setTintColor(Vector4(color, opacity)); + } + ); + splashFadeOutAnimation.setEndCallback(std::bind(&StateMachine::changeState, this, &titleState)); + + // Ant-hill zoom animation + antHillZoomClip.setInterpolator(easeOutCubic); + channel = antHillZoomClip.addChannel(0); + channel->insertKeyframe(0.0f, 0.0f); + channel->insertKeyframe(3.0f, 40.0f); + antHillZoomAnimation.setClip(&antHillZoomClip); + antHillZoomAnimation.setTimeFrame(antHillZoomClip.getTimeFrame()); + antHillZoomAnimation.setAnimateCallback + ( + [this](std::size_t id, float distance) + { + orbitCam->setFocalDistance(distance); + orbitCam->setTargetFocalDistance(distance); + } + ); + + // Menu fade animation + menuFadeInClip.setInterpolator(easeOutCubic); + channel = menuFadeInClip.addChannel(0); + channel->insertKeyframe(0.0f, 0.0f); + channel->insertKeyframe(3.0f, 0.0f); + channel->insertKeyframe(5.0f, 1.0f); + menuFadeAnimation.setClip(&menuFadeInClip); + menuFadeAnimation.setTimeFrame(menuFadeInClip.getTimeFrame()); + menuFadeAnimation.setAnimateCallback + ( + [this](std::size_t id, float opacity) + { + mainMenu->getContainer()->setTintColor(Vector4(opacity)); + } + ); + + // Menu selector animation + menuSelectorSlideClip.setInterpolator(easeOutCubic); + menuSelectorSlideAnimation.setClip(&menuSelectorSlideClip); + menuSelectorSlideAnimation.setAnimateCallback + ( + [this](std::size_t id, float offset) + { + Vector2 translation = menuSelectorImage->getTranslation(); + translation.y = offset; + menuSelectorImage->setTranslation(translation); + } + ); + + animator.addAnimation(&menuSelectorSlideAnimation); + + // Menu item select animation + menuItemSelectClip.setInterpolator(easeOutCubic); + menuItemSelectAnimation.setClip(&menuItemSelectClip); + menuItemSelectAnimation.setAnimateCallback + ( + [this](std::size_t id, const Vector4& color) + { + currentMenuItem->getContainer()->setTintColor(color); + } + ); + + // Menu item deselect animation + menuItemDeselectClip.setInterpolator(easeOutCubic); + menuItemDeselectAnimation.setClip(&menuItemDeselectClip); + menuItemDeselectAnimation.setAnimateCallback + ( + [this](std::size_t id, const Vector4& color) + { + previousMenuItem->getContainer()->setTintColor(color); + } + ); + + animator.addAnimation(&menuItemSelectAnimation); + animator.addAnimation(&menuItemDeselectAnimation); + // Construct fade-in animation clip fadeInClip.setInterpolator(easeOutCubic); - AnimationChannel* channel; channel = fadeInClip.addChannel(0); channel->insertKeyframe(0.0f, 1.0f); channel->insertKeyframe(1.0f, 0.0f); @@ -1268,11 +1712,12 @@ void Game::setupControls() // Build the master control set controls.addControl(&exitControl); controls.addControl(&toggleFullscreenControl); - controls.addControl(&screenshotControl); + controls.addControl(&takeScreenshotControl); controls.addControl(&menuUpControl); controls.addControl(&menuDownControl); controls.addControl(&menuLeftControl); controls.addControl(&menuRightControl); + controls.addControl(&menuActivateControl); controls.addControl(&menuBackControl); controls.addControl(&moveForwardControl); controls.addControl(&moveBackControl); @@ -1284,7 +1729,7 @@ void Game::setupControls() controls.addControl(&orbitCWControl); controls.addControl(&adjustCameraControl); controls.addControl(&dragCameraControl); - controls.addControl(&openToolMenuControl); + controls.addControl(&changeToolControl); controls.addControl(&useToolControl); controls.addControl(&toggleEditModeControl); controls.addControl(&toggleWireframeControl); @@ -1292,14 +1737,14 @@ void Game::setupControls() // Build the system control set systemControls.addControl(&exitControl); systemControls.addControl(&toggleFullscreenControl); - systemControls.addControl(&screenshotControl); + systemControls.addControl(&takeScreenshotControl); // Build the menu control set menuControls.addControl(&menuUpControl); menuControls.addControl(&menuDownControl); menuControls.addControl(&menuLeftControl); menuControls.addControl(&menuRightControl); - menuControls.addControl(&menuSelectControl); + menuControls.addControl(&menuActivateControl); menuControls.addControl(&menuBackControl); // Build the camera control set @@ -1315,26 +1760,31 @@ void Game::setupControls() cameraControls.addControl(&dragCameraControl); // Build the tool control set - toolControls.addControl(&openToolMenuControl); + toolControls.addControl(&changeToolControl); toolControls.addControl(&useToolControl); // Build the editor control set editorControls.addControl(&toggleEditModeControl); // Setup control callbacks + menuDownControl.setActivatedCallback(std::bind(&Game::selectNextMenuItem, this)); + menuUpControl.setActivatedCallback(std::bind(&Game::selectPreviousMenuItem, this)); + menuActivateControl.setActivatedCallback(std::bind(&Game::activateMenuItem, this)); + menuBackControl.setActivatedCallback(std::bind(&Game::activateLastMenuItem, this)); exitControl.setActivatedCallback(std::bind(&Application::close, this, EXIT_SUCCESS)); toggleFullscreenControl.setActivatedCallback(std::bind(&Game::toggleFullscreen, this)); - screenshotControl.setActivatedCallback(std::bind(&Game::queueScreenshot, this)); + takeScreenshotControl.setActivatedCallback(std::bind(&Game::queueScreenshot, this)); toggleWireframeControl.setActivatedCallback(std::bind(&Game::toggleWireframe, this)); // Build map of control names controlNameMap["exit"] = &exitControl; controlNameMap["toggle-fullscreen"] = &toggleFullscreenControl; - controlNameMap["screenshot"] = &screenshotControl; + controlNameMap["take-screenshot"] = &takeScreenshotControl; controlNameMap["menu-up"] = &menuUpControl; controlNameMap["menu-down"] = &menuDownControl; controlNameMap["menu-left"] = &menuLeftControl; controlNameMap["menu-right"] = &menuRightControl; + controlNameMap["menu-activate"] = &menuActivateControl; controlNameMap["menu-back"] = &menuBackControl; controlNameMap["move-forward"] = &moveForwardControl; controlNameMap["move-back"] = &moveBackControl; @@ -1346,13 +1796,21 @@ void Game::setupControls() controlNameMap["orbit-cw"] = &orbitCWControl; controlNameMap["adjust-camera"] = &adjustCameraControl; controlNameMap["drag-camera"] = &dragCameraControl; - controlNameMap["open-tool-menu"] = &openToolMenuControl; + controlNameMap["change-tool"] = &changeToolControl; controlNameMap["use-tool"] = &useToolControl; controlNameMap["toggle-edit-mode"] = &toggleEditModeControl; controlNameMap["toggle-wireframe"] = &toggleWireframeControl; // Load control profile - loadControlProfile(); + if (pathExists(controlsPath + controlProfileName + ".csv")) + { + loadControlProfile(controlProfileName); + } + else + { + loadControlProfile("default-controls"); + saveControlProfile(controlProfileName); + } // Setup input mapper inputMapper = new InputMapper(&eventDispatcher); @@ -1402,8 +1860,8 @@ void Game::resetSettings() // Set default font size fontSizePT = 14.0f; - // Set default control profile name - controlProfileName = "default-controls"; + // Set control profile name + controlProfileName = "controls"; } void Game::loadSettings() @@ -1453,10 +1911,56 @@ void Game::loadStrings() } } -void Game::loadControlProfile() +void Game::loadFonts() +{ + // Get filenames of fonts + std::string menuFontFilename = getString("menu-font-filename"); + std::string debugFontFilename = "inconsolata-bold.ttf"; + + // Load debugging font + if (!debugFont) + { + debugTypeface = resourceManager->load(debugFontFilename); + debugFont = debugTypeface->createFont(fontSizePX); + debugTypeface->loadCharset(debugFont, UnicodeRange::BASIC_LATIN); + } + + // Load menu typeface + menuTypeface = resourceManager->load(menuFontFilename); + menuFont = menuTypeface->createFont(fontSizePX * 1.5f); + menuTypeface->loadCharset(menuFont, UnicodeRange::BASIC_LATIN); + + // Load menu font typeface + menuTypeface = resourceManager->load(menuFontFilename); + + // Create menu font + menuFont = menuTypeface->createFont(fontSizePX * 1.5f); + + // Load basic latin character set + menuTypeface->loadCharset(menuFont, UnicodeRange::BASIC_LATIN); + + // Build character set for all strings in current language + std::set characterSet; + for (const CSVRow& row: *stringTable) + { + // Convert to UTF-8 string to UTF-32 + std::u32string string = toUTF32(row[languageIndex + 2]); + + // Add each character in the string to the charater set + for (char32_t charcode: string) + { + characterSet.emplace(charcode); + } + } + + // Load custom character set + menuTypeface->loadCharset(menuFont, characterSet); +} + +void Game::loadControlProfile(const std::string& profileName) { // Load control profile - std::string controlProfilePath = controlProfileName + ".csv"; + std::string controlProfilePath = profileName + ".csv"; CSVTable* controlProfile = resourceManager->load(controlProfilePath); for (const CSVRow& row: *controlProfile) @@ -1634,7 +2138,7 @@ void Game::loadControlProfile() } } -void Game::saveControlProfile() +void Game::saveControlProfile(const std::string& profileName) { // Build control profile CSV table CSVTable* table = new CSVTable(); @@ -1795,7 +2299,7 @@ void Game::saveControlProfile() } // Form full path to control profile file - std::string controlProfilePath = controlsPath + controlProfileName + ".csv"; + std::string controlProfilePath = controlsPath + profileName + ".csv"; // Save control profile resourceManager->save(table, controlProfilePath); @@ -1804,6 +2308,155 @@ void Game::saveControlProfile() delete table; } +std::array Game::getInputMappingStrings(const InputMapping* mapping) +{ + std::string deviceString; + std::string typeString; + std::string eventString; + + switch (mapping->getType()) + { + case InputMappingType::KEY: + { + const KeyMapping* keyMapping = static_cast(mapping); + deviceString = "keyboard"; + typeString = "key"; + eventString = std::string(Keyboard::getScancodeName(keyMapping->scancode)); + break; + } + + case InputMappingType::MOUSE_MOTION: + { + const MouseMotionMapping* mouseMotionMapping = static_cast(mapping); + deviceString = "mouse"; + eventString = "motion"; + + if (mouseMotionMapping->axis == MouseMotionAxis::POSITIVE_X) + { + eventString = "+x"; + } + else if (mouseMotionMapping->axis == MouseMotionAxis::NEGATIVE_X) + { + eventString = "-x"; + } + else if (mouseMotionMapping->axis == MouseMotionAxis::POSITIVE_Y) + { + eventString = "+y"; + } + else + { + eventString = "-y"; + } + + break; + } + + case InputMappingType::MOUSE_WHEEL: + { + const MouseWheelMapping* mouseWheelMapping = static_cast(mapping); + + deviceString = "mouse"; + typeString = "wheel"; + + if (mouseWheelMapping->axis == MouseWheelAxis::POSITIVE_X) + { + eventString = "+x"; + } + else if (mouseWheelMapping->axis == MouseWheelAxis::NEGATIVE_X) + { + eventString = "-x"; + } + else if (mouseWheelMapping->axis == MouseWheelAxis::POSITIVE_Y) + { + eventString = "+y"; + } + else + { + eventString = "-y"; + } + + break; + } + + case InputMappingType::MOUSE_BUTTON: + { + const MouseButtonMapping* mouseButtonMapping = static_cast(mapping); + deviceString = "mouse"; + typeString = "button"; + + std::stringstream stream; + stream << static_cast(mouseButtonMapping->button); + stream >> eventString; + break; + } + + case InputMappingType::GAMEPAD_AXIS: + { + const GamepadAxisMapping* gamepadAxisMapping = static_cast(mapping); + deviceString = "gamepad"; + typeString = "axis"; + + std::stringstream stream; + if (gamepadAxisMapping->negative) + { + stream << "-"; + } + else + { + stream << "+"; + } + stream << gamepadAxisMapping->axis; + + stream >> eventString; + break; + } + + case InputMappingType::GAMEPAD_BUTTON: + { + const GamepadButtonMapping* gamepadButtonMapping = static_cast(mapping); + deviceString = "gamepad"; + typeString = "button"; + + std::stringstream stream; + stream << static_cast(gamepadButtonMapping->button); + stream >> eventString; + break; + } + + default: + break; + } + + return {deviceString, typeString, eventString}; +} + +void Game::remapControl(Control* control) +{ + // Remove previously set input mappings for the control + inputRouter->removeMappings(control); + + // Start mapping new input + inputMapper->setControl(control); + inputMapper->setEnabled(true); + + // Restring UI to show control mappings have been removed. + restringUI(); + + // Disable UI callbacks + uiRootElement->setCallbacksEnabled(false); + + // Disable menu control callbacks + menuControls.setCallbacksEnabled(false); +} + +void Game::resetControls() +{ + inputRouter->reset(); + loadControlProfile("default-controls"); + saveControlProfile(controlProfileName); + restringUI(); +} + void Game::resizeUI(int w, int h) { // Adjust root element dimensions @@ -1980,6 +2633,9 @@ void Game::resizeUI(int w, int h) cameraGridX1Image->setTranslation(Vector2(0)); cameraReticleImage->setTranslation(Vector2(0)); + Rect menuSelectorBounds = hudTextureAtlas.getBounds("menu-selector"); + menuSelectorImage->setDimensions(Vector2(menuSelectorBounds.getWidth(), menuSelectorBounds.getHeight())); + UIImage* icons[] = { toolIconBrushImage, @@ -2007,11 +2663,168 @@ void Game::resizeUI(int w, int h) icons[i]->setTranslation(translation); } } + + // Main menu size + float mainMenuWidth = 0.0f; + float mainMenuHeight = 0.0f; + float mainMenuSpacing = 0.5f * fontSizePX; + float mainMenuPadding = fontSizePX * 4.0f; + + for (const MenuItem* item: *mainMenu->getItems()) + { + + mainMenuHeight += item->getNameLabel()->getFont()->getMetrics().getHeight(); + mainMenuHeight += mainMenuSpacing; + mainMenuWidth = std::max(mainMenuWidth, item->getNameLabel()->getDimensions().x); + } + mainMenuHeight -= mainMenuSpacing; + mainMenu->getContainer()->setAnchor(Anchor::BOTTOM_RIGHT); + mainMenu->resize(mainMenuWidth, mainMenuHeight); + mainMenu->getContainer()->setTranslation(Vector2(-mainMenuPadding)); + + // Settings menu size + float settingsMenuWidth = 0.0f; + float settingsMenuHeight = 0.0f; + float settingsMenuSpacing = 0.5f * fontSizePX; + float settingsMenuPadding = fontSizePX * 4.0f; + float settingsMenuValueMargin = fontSizePX * 4.0f; + + for (const MenuItem* item: *settingsMenu->getItems()) + { + settingsMenuHeight += item->getNameLabel()->getFont()->getMetrics().getHeight(); + settingsMenuHeight += settingsMenuSpacing; + + float itemWidth = item->getNameLabel()->getDimensions().x; + if (!item->getValueLabel()->getText().empty()) + { + itemWidth += item->getValueLabel()->getDimensions().x + settingsMenuValueMargin; + } + + settingsMenuWidth = std::max(settingsMenuWidth, itemWidth); + } + + settingsMenuHeight -= settingsMenuSpacing; + + settingsMenu->getContainer()->setAnchor(Anchor::BOTTOM_RIGHT); + settingsMenu->resize(settingsMenuWidth, settingsMenuHeight); + settingsMenu->getContainer()->setTranslation(Vector2(-settingsMenuPadding)); + + // Controls menu size + float controlsMenuWidth = 0.0f; + float controlsMenuHeight = 0.0f; + float controlsMenuSpacing = 0.5f * fontSizePX; + float controlsMenuPadding = fontSizePX * 4.0f; + float controlsMenuValueMargin = fontSizePX * 4.0f; + + for (const MenuItem* item: *controlsMenu->getItems()) + { + controlsMenuHeight += item->getNameLabel()->getFont()->getMetrics().getHeight(); + controlsMenuHeight += controlsMenuSpacing; + + float itemWidth = item->getNameLabel()->getDimensions().x; + if (!item->getValueLabel()->getText().empty()) + { + itemWidth += item->getValueLabel()->getDimensions().x + controlsMenuValueMargin; + } + + controlsMenuWidth = std::max(controlsMenuWidth, itemWidth); + } + + controlsMenuWidth += controlsMenuValueMargin; + controlsMenuHeight -= controlsMenuSpacing; + + controlsMenu->getContainer()->setAnchor(Anchor::BOTTOM_RIGHT); + controlsMenu->resize(controlsMenuWidth, controlsMenuHeight); + controlsMenu->getContainer()->setTranslation(Vector2(-controlsMenuPadding)); } void Game::restringUI() { + // Reset fonts + mainMenu->setFonts(menuFont); + settingsMenu->setFonts(menuFont); + controlsMenu->setFonts(menuFont); + + // Get common strings + std::string offString = getString("off"); + std::string onString = getString("on"); + std::string backString = getString("back"); + + // Main menu strings + mainMenuContinueItem->setName(getString("continue")); + mainMenuNewGameItem->setName(getString("new-game")); + mainMenuColoniesItem->setName(getString("colonies")); + mainMenuSettingsItem->setName(getString("settings")); + mainMenuQuitItem->setName(getString("quit")); + + // Settings menu strings + settingsMenuControlsItem->setName(getString("controls")); + settingsMenuControlsItem->setValue(getString("dotdotdot")); + settingsMenuFullscreenItem->setName(getString("fullscreen")); + settingsMenuFullscreenItem->setValue((fullscreen) ? onString : offString); + settingsMenuVSyncItem->setName(getString("v-sync")); + settingsMenuVSyncItem->setValue((vsync) ? onString : offString); + settingsMenuLanguageItem->setName(getString("language")); + settingsMenuLanguageItem->setValue(getString("language-name")); + settingsMenuBackItem->setName(backString); + + // Controls menu strings + restringControlMenuItem(controlsMenuMoveForwardItem, "move-forward"); + restringControlMenuItem(controlsMenuMoveLeftItem, "move-left"); + restringControlMenuItem(controlsMenuMoveBackItem, "move-back"); + restringControlMenuItem(controlsMenuMoveRightItem, "move-right"); + restringControlMenuItem(controlsMenuChangeToolItem, "change-tool"); + restringControlMenuItem(controlsMenuUseToolItem, "use-tool"); + restringControlMenuItem(controlsMenuAdjustCameraItem, "adjust-camera"); + restringControlMenuItem(controlsMenuToggleFullscreenItem, "toggle-fullscreen"); + restringControlMenuItem(controlsMenuTakeScreenshotItem, "take-screenshot"); + controlsMenuResetToDefaultItem->setName(getString("reset-to-default")); + controlsMenuBackItem->setName(backString); + + // Reset menu tweens + uiRootElement->update(); + mainMenu->getContainer()->resetTweens(); + settingsMenu->getContainer()->resetTweens(); + controlsMenu->getContainer()->resetTweens(); +} + +void Game::restringControlMenuItem(MenuItem* item, const std::string& name) +{ + item->setName(getString(name)); + + Control* control = controlNameMap.find(name)->second; + + std::string value; + const std::list* mappings = inputRouter->getMappings(control); + if (mappings != nullptr) + { + std::size_t i = 0; + for (const InputMapping* mapping: *mappings) + { + std::array mappingStrings = getInputMappingStrings(mapping); + + // keyboard-key, mouse-button, gamepad-axis, etc. + std::string typeName = mappingStrings[0] + "-" + mappingStrings[1]; + std::string type = getString(typeName); + if (mapping->getType() != InputMappingType::KEY) + { + value += type; + value += " "; + } + + value += mappingStrings[2]; + + if (i < mappings->size() - 1) + { + value += ", "; + } + + ++i; + } + } + + item->setValue(value); } void Game::setTimeOfDay(float time) @@ -2075,7 +2888,7 @@ void Game::screenshot() glReadPixels(0, 0, w, h, GL_RGB, GL_UNSIGNED_BYTE, pixels); // Get game title in current language - std::string title = getString(getLanguageIndex(), "title"); + std::string title = getString("title"); // Convert title to lowercase std::transform(title.begin(), title.end(), title.begin(), ::tolower); @@ -2127,6 +2940,120 @@ void Game::mapInput(const InputMapping& mapping) // Disable input mapping generation inputMapper->setControl(nullptr); inputMapper->setEnabled(false); + + // Restring UI + restringUI(); + + // Schedule callbacks to be enabled in 100ms + ScheduledFunctionEvent event; + event.caller = static_cast(this); + event.function = [this]() + { + // Re-enable UI callbacks + uiRootElement->setCallbacksEnabled(true); + + // Re-enable menu controls + menuControls.setCallbacksEnabled(true); + }; + eventDispatcher.schedule(event, time + 0.1f); + + // Save control profile + saveControlProfile(controlProfileName); +} + +void Game::enterSplashState() +{ + // Show splash screen + splashBackgroundImage->setVisible(true); + splashImage->setVisible(true); + splashImage->setTintColor({1, 1, 1, 0}); + splashBackgroundImage->setTintColor({0, 0, 0, 1}); + splashImage->resetTweens(); + splashBackgroundImage->resetTweens(); + uiRootElement->update(); + + // Add splash animations to animator + animator.addAnimation(&splashFadeInAnimation); + animator.addAnimation(&splashFadeOutAnimation); + + // Play splash fade-in animation + splashFadeInAnimation.rewind(); + splashFadeInAnimation.play(); +} + +void Game::exitSplashState() +{ + // Hide splash screen + splashImage->setVisible(false); + splashBackgroundImage->setVisible(false); + uiRootElement->update(); + + // Remove splash animations from animator + animator.removeAnimation(&splashFadeInAnimation); + animator.removeAnimation(&splashFadeOutAnimation); +} + +void Game::enterLoadingState() +{} + +void Game::exitLoadingState() +{} + +void Game::enterTitleState() +{ + // Setup scene + Vector3 antHillTranslation = {0, 0, 0}; + EntityID antHill = createInstanceOf("ant-hill"); + setTranslation(antHill, antHillTranslation); + + // Setup camera + cameraRig = orbitCam; + orbitCam->setTargetFocalPoint(antHillTranslation); + orbitCam->setTargetFocalDistance(0.0f); + orbitCam->setTargetElevation(glm::radians(80.0f)); + orbitCam->setTargetAzimuth(0.0f); + orbitCam->setFocalPoint(orbitCam->getTargetFocalPoint()); + orbitCam->setFocalDistance(orbitCam->getTargetFocalDistance()); + orbitCam->setElevation(orbitCam->getTargetElevation()); + orbitCam->setAzimuth(orbitCam->getTargetAzimuth()); + + float fov = glm::radians(30.0f); + orbitCam->getCamera()->setPerspective(fov, (float)w / (float)h, 1.0f, 1000.0f); + + // Begin fade-in + fadeIn(6.0f, {0, 0, 0}, nullptr); + + // + animator.addAnimation(&antHillZoomAnimation); + antHillZoomAnimation.rewind(); + antHillZoomAnimation.play(); + + animator.addAnimation(&menuFadeAnimation); + menuFadeAnimation.rewind(); + menuFadeAnimation.play(); + + // Select the first menu item + openMenu(mainMenu, 0); +} + +void Game::exitTitleState() +{ + animator.removeAnimation(&antHillZoomAnimation); + animator.removeAnimation(&menuFadeAnimation); +} + +void Game::enterPlayState() +{} + +void Game::exitPlayState() +{} + +void Game::skipSplash() +{ + if (StateMachine::getCurrentState() == &splashState) + { + StateMachine::changeState(&titleState); + } } void Game::boxSelect(float x, float y, float w, float h) @@ -2185,6 +3112,14 @@ void Game::fadeOut(float duration, const Vector3& color, std::function c uiRootElement->update(); } +void Game::stopFade() +{ + fadeInAnimation.stop(); + fadeOutAnimation.stop(); + blackoutImage->setVisible(false); + uiRootElement->update(); +} + void Game::selectTool(int toolIndex) { Tool* tools[] = diff --git a/src/game.hpp b/src/game.hpp index a52cf1e..e640f45 100644 --- a/src/game.hpp +++ b/src/game.hpp @@ -23,7 +23,10 @@ #include using namespace Emergent; +#include "state-machine.hpp" #include "entity/entity-id.hpp" +#include "scheduled-function-event.hpp" +#include #include #include #include @@ -65,11 +68,15 @@ class SteeringSystem; class LocomotionSystem; class TerrainSystem; class ComponentBase; +class Menu; +class MenuItem; enum class ComponentType; typedef std::vector> CSVTable; class Game: - public Application + public Application, + public StateMachine, + public EventHandler { public: /** @@ -84,20 +91,21 @@ public: virtual ~Game(); /** - * Gets a string in the specified language. + * Gets a string in the current language. * - * @param languageIndex Index of a language. * @param name Name of the string. - * @return String in the specified language. + * @return String in the current language. */ - std::string getString(std::size_t languageIndex, const std::string& name) const; + std::string getString(const std::string& name) const; /** * Changes the current language. * * @param languageIndex Index of the language to use. */ - void changeLanguage(std::size_t languageIndex); + void changeLanguage(std::size_t nextLanguageIndex); + + void nextLanguage(); /// Returns the number of available languages. std::size_t getLanguageCount() const; @@ -105,7 +113,16 @@ public: /// Returns the index of the current language. std::size_t getLanguageIndex() const; + void openMenu(Menu* menu, int selectedItemIndex); + void closeCurrentMenu(); + void selectMenuItem(int index, bool tween); + void selectNextMenuItem(); + void selectPreviousMenuItem(); + void activateMenuItem(); + void activateLastMenuItem(); + void toggleFullscreen(); + void toggleVSync(); void setUpdateRate(double frequency); /** @@ -123,6 +140,7 @@ public: void fadeIn(float duration, const Vector3& color, std::function callback); void fadeOut(float duration, const Vector3& color, std::function callback); + void stopFade(); void selectTool(int toolIndex); @@ -135,6 +153,7 @@ private: virtual void handleEvent(const WindowResizedEvent& event); virtual void handleEvent(const GamepadConnectedEvent& event); virtual void handleEvent(const GamepadDisconnectedEvent& event); + virtual void handleEvent(const ScheduledFunctionEvent& event); void setupDebugging(); void setupLocalization(); @@ -148,11 +167,16 @@ private: void loadSettings(); void saveSettings(); void loadStrings(); - void loadControlProfile(); - void saveControlProfile(); + void loadFonts(); + void loadControlProfile(const std::string& profileName); + void saveControlProfile(const std::string& profileName); + std::array getInputMappingStrings(const InputMapping* mapping); + void remapControl(Control* control); + void resetControls(); void resizeUI(int w, int h); void restringUI(); + void restringControlMenuItem(MenuItem* item, const std::string& name); void resizeRenderTargets(); void setTimeOfDay(float time); @@ -160,9 +184,21 @@ private: void queueScreenshot(); void screenshot(); + // Callback for the input mapper void mapInput(const InputMapping& mapping); + // State functions + void enterSplashState(); + void exitSplashState(); + void enterLoadingState(); + void exitLoadingState(); + void enterTitleState(); + void exitTitleState(); + void enterPlayState(); + void exitPlayState(); + + void skipSplash(); public: EntityID createInstance(); @@ -182,9 +218,13 @@ public: public: - // States + // Game states + StateMachine::State splashState; + StateMachine::State loadingState; + StateMachine::State titleState; + StateMachine::State playState; + GameState* currentState; - SplashState* splashState; SandboxState* sandboxState; // Paths @@ -219,7 +259,7 @@ public: ControlSet systemControls; Control exitControl; Control toggleFullscreenControl; - Control screenshotControl; + Control takeScreenshotControl; // Menu control set ControlSet menuControls; @@ -227,14 +267,14 @@ public: Control menuDownControl; Control menuLeftControl; Control menuRightControl; - Control menuSelectControl; + Control menuActivateControl; Control menuBackControl; // Camera control set ControlSet cameraControls; Control moveForwardControl; - Control moveBackControl; Control moveLeftControl; + Control moveBackControl; Control moveRightControl; Control zoomInControl; Control zoomOutControl; @@ -245,7 +285,7 @@ public: // Tool control set ControlSet toolControls; - Control openToolMenuControl; + Control changeToolControl; Control useToolControl; // Editor control set @@ -267,10 +307,10 @@ public: float timestep; // UI - Typeface* labelTypeface; - Font* labelFont; Typeface* debugTypeface; Font* debugFont; + Typeface* menuTypeface; + Font* menuFont; BillboardBatch* uiBatch; UIBatcher* uiBatcher; UIContainer* uiRootElement; @@ -333,6 +373,49 @@ public: Vector4 cameraGridColor; Vector4 cameraReticleColor; + + + + // Menu selection + UIImage* menuSelectorImage; + Menu* currentMenu; + Menu* previousMenu; + MenuItem* currentMenuItem; + MenuItem* previousMenuItem; + int menuItemIndex; + Vector4 menuItemActiveColor; + Vector4 menuItemInactiveColor; + + // Main menu + Menu* mainMenu; + MenuItem* mainMenuContinueItem; + MenuItem* mainMenuNewGameItem; + MenuItem* mainMenuColoniesItem; + MenuItem* mainMenuSettingsItem; + MenuItem* mainMenuQuitItem; + + // Settings menu + Menu* settingsMenu; + MenuItem* settingsMenuControlsItem; + MenuItem* settingsMenuFullscreenItem; + MenuItem* settingsMenuVSyncItem; + MenuItem* settingsMenuLanguageItem; + MenuItem* settingsMenuBackItem; + + // Controls menu + Menu* controlsMenu; + MenuItem* controlsMenuMoveForwardItem; + MenuItem* controlsMenuMoveLeftItem; + MenuItem* controlsMenuMoveBackItem; + MenuItem* controlsMenuMoveRightItem; + MenuItem* controlsMenuChangeToolItem; + MenuItem* controlsMenuUseToolItem; + MenuItem* controlsMenuAdjustCameraItem; + MenuItem* controlsMenuToggleFullscreenItem; + MenuItem* controlsMenuTakeScreenshotItem; + MenuItem* controlsMenuResetToDefaultItem; + MenuItem* controlsMenuBackItem; + // Rendering Renderer renderer; RenderTarget defaultRenderTarget; @@ -364,6 +447,17 @@ public: // Animation Animator animator; + + Animation antHillZoomAnimation; + AnimationClip antHillZoomClip; + + Animation menuFadeAnimation; + AnimationClip menuFadeInClip; + + Animation splashFadeInAnimation; + Animation splashFadeOutAnimation; + AnimationClip splashFadeInClip; + AnimationClip splashFadeOutClip; Animation fadeInAnimation; Animation fadeOutAnimation; AnimationClip fadeInClip; @@ -372,6 +466,14 @@ public: std::function fadeOutEndCallback; Animation cameraFlashAnimation; AnimationClip cameraFlashClip; + Animation menuSelectorSlideAnimation; + AnimationClip menuSelectorSlideClip; + Animation menuItemSelectAnimation; + AnimationClip menuItemSelectClip; + Animation menuItemDeselectAnimation; + AnimationClip menuItemDeselectClip; + + // Assets ResourceManager* resourceManager; @@ -417,6 +519,7 @@ public: bool vsync; float fontSizePT; std::string controlProfileName; + bool toggleFullscreenDisabled; // Debugging std::ofstream logFileStream; diff --git a/src/menu.cpp b/src/menu.cpp new file mode 100644 index 0000000..60c09dd --- /dev/null +++ b/src/menu.cpp @@ -0,0 +1,126 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#include "menu.hpp" + +MenuItem::MenuItem(): + activatedCallback(nullptr) +{ + container = new UIContainer(); + container->setAnchor(Anchor::TOP_LEFT); + + nameLabel = new UILabel(); + nameLabel->setAnchor(Anchor::TOP_LEFT); + + valueLabel = new UILabel(); + valueLabel->setAnchor(Anchor::TOP_RIGHT); + + container->addChild(nameLabel); + container->addChild(valueLabel); +} + +MenuItem::~MenuItem() +{ + delete container; + delete nameLabel; + delete valueLabel; +} + +void MenuItem::setFont(Font* font) +{ + nameLabel->setFont(font); + valueLabel->setFont(font); +} + +void MenuItem::setName(const std::string& name) +{ + nameLabel->setText(name); +} + +void MenuItem::setValue(const std::string& value) +{ + valueLabel->setText(value); +} + +void MenuItem::setActivatedCallback(std::function callback) +{ + activatedCallback = callback; +} + +void MenuItem::activate() +{ + if (activatedCallback) + { + activatedCallback(); + } +} + +Menu::Menu() +{ + container = new UIContainer(); +} + +Menu::~Menu() +{ + removeItems(); + delete container; +} + +MenuItem* Menu::addItem() +{ + MenuItem* item = new MenuItem(); + container->addChild(item->container); + items.push_back(item); + + return item; +} + +void Menu::removeItems() +{ + for (MenuItem* item: items) + { + container->removeChild(item->container); + delete item; + } + items.clear(); +} + +void Menu::setFonts(Font* font) +{ + for (MenuItem* item: items) + { + item->setFont(font); + } +} + +void Menu::resize(int w, int h) +{ + container->setDimensions(Vector2(w, h)); + + int spacing = h / items.size(); + int offset = 0; + + for (MenuItem* item: items) + { + item->container->setTranslation(Vector2(0, offset)); + item->container->setDimensions(Vector2(w, item->getNameLabel()->getFont()->getMetrics().getHeight())); + offset += spacing; + } +} + diff --git a/src/menu.hpp b/src/menu.hpp new file mode 100644 index 0000000..12f7850 --- /dev/null +++ b/src/menu.hpp @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#ifndef MENU_HPP +#define MENU_HPP + +#include +using namespace Emergent; + +#include "ui/ui.hpp" +#include + +class Menu; + +class MenuItem +{ +public: + MenuItem(); + ~MenuItem(); + + void setFont(Font* font); + + void setName(const std::string& name); + + void setValue(const std::string& value); + + void setActivatedCallback(std::function callback); + + void activate(); + + UIContainer* getContainer() const; + UILabel* getNameLabel() const; + UILabel* getValueLabel() const; + +private: + friend class Menu; + + UIContainer* container; + UILabel* nameLabel; + UILabel* valueLabel; + std::function activatedCallback; +}; + +inline UIContainer* MenuItem::getContainer() const +{ + return container; +} + +inline UILabel* MenuItem::getNameLabel() const +{ + return nameLabel; +} + +inline UILabel* MenuItem::getValueLabel() const +{ + return valueLabel; +} + +class Menu +{ +public: + /// Creates a menu. + Menu(); + + /// Destroys a menu. + ~Menu(); + + /** + * Adds a new item to the menu. + */ + MenuItem* addItem(); + + /** + * Removes an item from the menu. + */ + void removeItem(MenuItem* item); + + /** + * Removes all items from the menu. + */ + void removeItems(); + + /** + * Sets the font for all menu items in the menu. + */ + void setFonts(Font* font); + + const std::vector* getItems() const; + + UIContainer* getContainer() const; + + void resize(int w, int h); + +private: + + UIContainer* container; + std::vector items; +}; + +inline const std::vector* Menu::getItems() const +{ + return &items; +} + +inline UIContainer* Menu::getContainer() const +{ + return container; +} + +#endif // MENU_HPP + diff --git a/src/resources/csv-table-loader.cpp b/src/resources/csv-table-loader.cpp index da9580e..404119e 100644 --- a/src/resources/csv-table-loader.cpp +++ b/src/resources/csv-table-loader.cpp @@ -120,7 +120,7 @@ void ResourceLoader::save(ResourceManager* resourceManager, std::ostre for (std::size_t j = 0; j < row.size(); ++j) { - const CSVColumn& column = row[j]; + const CSVEntry& column = row[j]; (*os) << column; diff --git a/src/resources/csv-table.hpp b/src/resources/csv-table.hpp index 6b5fd0a..e041e03 100644 --- a/src/resources/csv-table.hpp +++ b/src/resources/csv-table.hpp @@ -23,8 +23,8 @@ #include #include -typedef std::string CSVColumn; -typedef std::vector CSVRow; +typedef std::string CSVEntry; +typedef std::vector CSVRow; typedef std::vector CSVTable; #endif // CSV_TABLE_HPP diff --git a/src/scheduled-function-event.cpp b/src/scheduled-function-event.cpp new file mode 100644 index 0000000..4a1db3b --- /dev/null +++ b/src/scheduled-function-event.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#include "scheduled-function-event.hpp" + +EventBase* ScheduledFunctionEvent::clone() const +{ + ScheduledFunctionEvent* event = new ScheduledFunctionEvent(); + event->caller = caller; + event->function = function; + return event; +} + diff --git a/src/states/splash-state.hpp b/src/scheduled-function-event.hpp old mode 100755 new mode 100644 similarity index 53% rename from src/states/splash-state.hpp rename to src/scheduled-function-event.hpp index 94cf572..41e05b8 --- a/src/states/splash-state.hpp +++ b/src/scheduled-function-event.hpp @@ -17,39 +17,25 @@ * along with Antkeeper Source Code. If not, see . */ -#ifndef SPLASH_STATE_HPP -#define SPLASH_STATE_HPP - -#include "game-state.hpp" +#ifndef SCHEDULED_FUNCTION_EVENT_HPP +#define SCHEDULED_FUNCTION_EVENT_HPP #include using namespace Emergent; /** - * Displays the splash screen. + * Event which asks a caller to execute a function when the event is handled. + * + * @ingroup input */ -class SplashState: - public GameState, - public EventHandler, - public EventHandler, - public EventHandler +class ScheduledFunctionEvent: public Event { public: - SplashState(Game* game); - virtual ~SplashState(); - virtual void enter(); - virtual void execute(); - virtual void exit(); + virtual EventBase* clone() const; -private: - virtual void handleEvent(const KeyPressedEvent& event); - virtual void handleEvent(const MouseButtonPressedEvent& event); - virtual void handleEvent(const GamepadButtonPressedEvent& event); - void skip(); - - Animation fadeAnimation; - AnimationClip fadeInClip; - AnimationClip fadeOutClip; + void* caller; + std::function function; }; -#endif // SPLASH_STATE_HPP +#endif // SCHEDULED_FUNCTION_EVENT_HPP + diff --git a/src/state-machine.cpp b/src/state-machine.cpp new file mode 100644 index 0000000..131e457 --- /dev/null +++ b/src/state-machine.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#include "state-machine.hpp" + +StateMachine::StateMachine(): + previousState(nullptr), + currentState(nullptr) +{} + +void StateMachine::changeState(const State* nextState) +{ + // Call exit function of current state + if (currentState && currentState->back()) + { + currentState->back()(); + } + + // Change state + previousState = currentState; + currentState = nextState; + + // Call enter function of next state + if (currentState && currentState->front()) + { + currentState->front()(); + } +} + diff --git a/src/state-machine.hpp b/src/state-machine.hpp new file mode 100644 index 0000000..ef8e846 --- /dev/null +++ b/src/state-machine.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2017-2019 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 . + */ + +#ifndef STATE_MACHINE_HPP +#define STATE_MACHINE_HPP + +#include +#include + +/** + * Extremely lightweight finite-state machine. + */ +class StateMachine +{ +public: + /** + * A state is a fixed-size array of function pointers with the first and second elements referring to the state's enter and exit functions, respectively. + */ + typedef std::array, 2> State; + + /** + * Creates a state machine, setting the initial state to nullptr. + */ + StateMachine(); + + /** + * Changes the current state. + */ + void changeState(const State* nextState); + + /** + * Returns the previous state. + */ + const State* getPreviousState() const; + + /** + * Returns the current state. + */ + const State* getCurrentState() const; + +private: + const State* previousState; + const State* currentState; +}; + +inline const StateMachine::State* StateMachine::getPreviousState() const +{ + return previousState; +} + +inline const StateMachine::State* StateMachine::getCurrentState() const +{ + return currentState; +} + +#endif // STATE_MACHINE_HPP + diff --git a/src/states/sandbox-state.cpp b/src/states/sandbox-state.cpp index 36f3e86..21b4f29 100755 --- a/src/states/sandbox-state.cpp +++ b/src/states/sandbox-state.cpp @@ -47,7 +47,7 @@ void SandboxState::enter() game->radialMenuContainer->setVisible(false); // Show mouse - game->mouse->setVisible(false); + //game->mouse->setVisible(false); game->fadeIn(1.0f, Vector3(0.0f), nullptr); @@ -56,22 +56,8 @@ void SandboxState::enter() float elevation = glm::radians(30.0f); float azimuth = glm::radians(-45.0f); - game->cameraRig = game->orbitCam; - game->orbitCam->setFocalPoint(focalPoint); - game->orbitCam->setTargetFocalPoint(focalPoint); - game->orbitCam->setFocalDistance(focalDistance); - game->orbitCam->setTargetFocalDistance(focalDistance); - game->orbitCam->setElevation(elevation); - game->orbitCam->setTargetElevation(elevation); - game->orbitCam->setAzimuth(azimuth); - game->orbitCam->setTargetAzimuth(azimuth); - - game->freeCam->setTranslation(Vector3(-5, 5.0f, -5.0f)); - //game->cameraRig = game->freeCam; - //game->mouse->setRelativeMode(true); - toolIndex = 0; - game->selectTool(toolIndex); + //game->selectTool(toolIndex); //game->currentTool->setActive(false); game->mouse->warp(game->window, game->w / 2, game->h / 2); @@ -86,7 +72,7 @@ void SandboxState::execute() game->lightingPass->setTime(game->time); bool menuClosed = false; - if (game->openToolMenuControl.isActive() && !game->openToolMenuControl.wasActive()) + if (game->changeToolControl.isActive() && !game->changeToolControl.wasActive()) { game->radialMenuContainer->setVisible(true); game->hudContainer->setVisible(false); @@ -95,7 +81,7 @@ void SandboxState::execute() selectorVector = Vector2(0.0f); game->mouse->setRelativeMode(true); } - else if (!game->openToolMenuControl.isActive() && game->openToolMenuControl.wasActive()) + else if (!game->changeToolControl.isActive() && game->changeToolControl.wasActive()) { game->radialMenuContainer->setVisible(false); //game->hudContainer->setVisible(true); @@ -186,12 +172,14 @@ void SandboxState::execute() float logMovementSpeed = lerp(logMinMovementSpeed, logMaxMovementSpeed, 1.0f - zoom); float movementSpeed = std::exp(logMovementSpeed); + /* game->orbitCam->setTargetFocalDistance(focalDistance); game->orbitCam->getCamera()->setPerspective(fov, (float)game->w / (float)game->h, clipNear, clipFar); game->orbitCam->move(direction * movementSpeed); game->orbitCam->setFocalPoint(game->orbitCam->getTargetFocalPoint()); //game->orbitCam->setTargetElevation(elevation); // + */ if (game->cameraRig == game->freeCam) { diff --git a/src/states/splash-state.cpp b/src/states/splash-state.cpp deleted file mode 100755 index caad285..0000000 --- a/src/states/splash-state.cpp +++ /dev/null @@ -1,151 +0,0 @@ -/* - * Copyright (C) 2017-2019 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 . - */ - -#include "splash-state.hpp" -#include "game.hpp" -#include "sandbox-state.hpp" -#include "ui/ui.hpp" - -SplashState::SplashState(Game* game): - GameState(game) -{} - -SplashState::~SplashState() -{} - -void SplashState::enter() -{ - AnimationChannel* channel; - - // Construct fade-in animation clip - fadeInClip.setInterpolator(lerp); - channel = fadeInClip.addChannel(0); - channel->insertKeyframe(0.0f, 0.0f); - channel->insertKeyframe(0.5f, 0.0f); - channel->insertKeyframe(1.25f, 1.0f); - channel->insertKeyframe(5.25f, 1.0f); - - // Construct fade-out animation clip - fadeOutClip.setInterpolator(lerp); - channel = fadeOutClip.addChannel(0); - channel->insertKeyframe(0.0f, 1.0f); - channel->insertKeyframe(0.75f, 0.0f); - channel->insertKeyframe(1.25f, 0.0f); - - // Setup animate callback to change splash screen opacity - fadeAnimation.setAnimateCallback - ( - [this](std::size_t id, float opacity) - { - this->game->splashImage->setTintColor(Vector4(1.0f, 1.0f, 1.0f, opacity)); - } - ); - - // Setup end callback to fade-out after fading in - fadeAnimation.setEndCallback - ( - [this]() - { - // Change clip to fade-out - this->fadeAnimation.setClip(&fadeOutClip); - this->fadeAnimation.setTimeFrame(this->fadeOutClip.getTimeFrame()); - this->fadeAnimation.rewind(); - this->fadeAnimation.play(); - - // Setup end callback to change to title state after fading out - this->fadeAnimation.setEndCallback - ( - [this]() - { - //this->game->changeState(this->game->titleState); - this->game->changeState(this->game->sandboxState); - } - ); - } - ); - - // Setup fade animation to fade-in - fadeAnimation.setSpeed(1.0f); - fadeAnimation.setLoop(false); - fadeAnimation.setClip(&fadeInClip); - fadeAnimation.setTimeFrame(fadeInClip.getTimeFrame()); - - // Play the fade animation - fadeAnimation.play(); - - // Add fade animation to the animator - game->getAnimator()->addAnimation(&fadeAnimation); - - // Subscribe this state to input events - game->getEventDispatcher()->subscribe(this); - game->getEventDispatcher()->subscribe(this); - game->getEventDispatcher()->subscribe(this); - - // Make splash screen visible - game->splashBackgroundImage->setVisible(true); - game->splashImage->setVisible(true); - game->splashImage->setTintColor(Vector4(1.0f, 1.0f, 1.0f, 0.0f)); - game->splashBackgroundImage->setTintColor(Vector4(0.0f, 0.0f, 0.0f, 1.0f)); - game->splashImage->resetTweens(); - game->splashBackgroundImage->resetTweens(); - game->uiRootElement->update(); - - // Hide mouse - game->mouse->setVisible(false); -} - -void SplashState::execute() -{} - -void SplashState::exit() -{ - // Remove fade animation from the animator - game->getAnimator()->removeAnimation(&fadeAnimation); - - // Unsubscribe this state from input events - game->getEventDispatcher()->unsubscribe(this); - game->getEventDispatcher()->unsubscribe(this); - game->getEventDispatcher()->unsubscribe(this); - - // Make splash screen invisible - game->splashBackgroundImage->setVisible(false); - game->splashImage->setVisible(false); -} - -void SplashState::handleEvent(const KeyPressedEvent& event) -{ - skip(); -} - -void SplashState::handleEvent(const MouseButtonPressedEvent& event) -{ - skip(); -} - -void SplashState::handleEvent(const GamepadButtonPressedEvent& event) -{ - skip(); -} - -void SplashState::skip() -{ - game->splashImage->setVisible(false); - game->changeState(game->sandboxState); -} - diff --git a/src/ui/ui.cpp b/src/ui/ui.cpp index f1780ea..20b219a 100755 --- a/src/ui/ui.cpp +++ b/src/ui/ui.cpp @@ -39,7 +39,7 @@ UIElement::UIElement(): tintColor(1.0f), color(tintColor), visible(true), - active(true), + callbacksEnabled(true), mouseOver(false), mouseOverCallback(nullptr), mouseOutCallback(nullptr), @@ -56,7 +56,8 @@ UIElement::UIElement(): rotationTween(&rotation, lerp), dimensionsTween(&dimensions, lerp), positionTween(&position, lerp), - tintColorTween(&tintColor, lerp) + tintColorTween(&tintColor, lerp), + colorTween(&color, lerp) {} UIElement::~UIElement() @@ -142,7 +143,7 @@ void UIElement::setMouseReleasedCallback(std::function call void UIElement::handleEvent(const MouseMovedEvent& event) { - if (!active) + if (!callbacksEnabled) { return; } @@ -180,7 +181,7 @@ void UIElement::handleEvent(const MouseMovedEvent& event) void UIElement::handleEvent(const MouseButtonPressedEvent& event) { - if (!active) + if (!callbacksEnabled) { return; } @@ -201,7 +202,7 @@ void UIElement::handleEvent(const MouseButtonPressedEvent& event) void UIElement::handleEvent(const MouseButtonReleasedEvent& event) { - if (!active) + if (!callbacksEnabled) { return; } @@ -228,6 +229,7 @@ void UIElement::interpolate(float dt) dimensionsTween.interpolate(dt); positionTween.interpolate(dt); tintColorTween.interpolate(dt); + colorTween.interpolate(dt); for (UIElement* child: children) { @@ -243,6 +245,12 @@ void UIElement::resetTweens() dimensionsTween.reset(); positionTween.reset(); tintColorTween.reset(); + colorTween.reset(); + + for (UIElement* child: children) + { + child->resetTweens(); + } } UILabel::UILabel(): @@ -401,7 +409,7 @@ void UIBatcher::batchLabel(BillboardBatch* result, const UILabel* label) const Font* font = label->getFont(); std::size_t index = range->start + range->length; std::size_t count = 0; - font->puts(result, origin, label->getText(), label->getColor(), index, &count); + font->puts(result, origin, label->getText(), label->getColorTween()->getSubstate(), index, &count); for (std::size_t i = index; i < index + count; ++i) { @@ -443,7 +451,7 @@ void UIBatcher::batchImage(BillboardBatch* result, const UIImage* image) } billboard->setTextureCoordinates(image->getTextureBounds().getMin(), image->getTextureBounds().getMax()); - billboard->setTintColor(image->getTintColorTween()->getSubstate()); + billboard->setTintColor(image->getColorTween()->getSubstate()); billboard->resetTweens(); // Increment range length diff --git a/src/ui/ui.hpp b/src/ui/ui.hpp index 8956162..30a5387 100755 --- a/src/ui/ui.hpp +++ b/src/ui/ui.hpp @@ -86,7 +86,7 @@ public: void setVisible(bool visible); /// Enables or disables callbacks - void setActive(bool active); + void setCallbacksEnabled(bool enabled); /// Returns the type of this element virtual UIElement::Type getElementType() const = 0; @@ -148,8 +148,8 @@ public: /// Returns the visibility of this element bool isVisible() const; - /// Returns `true` if the element is active (callbacks enabled) - bool isActive() const; + /// Returns `true` if the element callbacks are enabled + bool areCallbacksEnabled() const; /// Calculates the world-space position and bounds of this element and its children virtual void update(); @@ -185,6 +185,9 @@ public: Tween* getPositionTween(); const Tween* getTintColorTween() const; Tween* getTintColorTween(); + const Tween* getColorTween() const; + Tween* getColorTween(); + protected: UIMaterial material; @@ -199,7 +202,7 @@ private: Vector4 tintColor; Vector4 color; bool visible; - bool active; + bool callbacksEnabled; bool mouseOver; std::function mouseOverCallback; std::function mouseOutCallback; @@ -219,6 +222,7 @@ private: Tween dimensionsTween; Tween positionTween; Tween tintColorTween; + Tween colorTween; }; inline void UIElement::setAnchor(const Vector2& anchor) @@ -261,9 +265,9 @@ inline void UIElement::setVisible(bool visible) this->visible = visible; } -inline void UIElement::setActive(bool active) +inline void UIElement::setCallbacksEnabled(bool enabled) { - this->active = active; + this->callbacksEnabled = enabled; } inline const UIElement* UIElement::getParent() const @@ -361,9 +365,9 @@ inline bool UIElement::isVisible() const return visible; } -inline bool UIElement::isActive() const +inline bool UIElement::areCallbacksEnabled() const { - return active; + return callbacksEnabled; } inline const Tween* UIElement::getOriginTween() const @@ -426,6 +430,16 @@ inline Tween* UIElement::getTintColorTween() return &tintColorTween; } +inline const Tween* UIElement::getColorTween() const +{ + return &colorTween; +} + +inline Tween* UIElement::getColorTween() +{ + return &colorTween; +} + class UIContainer: public UIElement { public: