diff --git a/crogine/src/core/App.cpp b/crogine/src/core/App.cpp index 437883aa9..30e087122 100644 --- a/crogine/src/core/App.cpp +++ b/crogine/src/core/App.cpp @@ -271,7 +271,9 @@ App::App(std::uint32_t styleFlags) m_instance = this; //maps the steam deck rear buttons to the controller paddles - auto mapResult = SDL_GameControllerAddMapping("03000000de2800000512000011010000,Steam Deck,platform:Linux,crc:17f6,a:b3,b:b4,x:b5,y:b6,back:b11,guide:b13,start:b12,leftstick:b14,rightstick:b15,leftshoulder:b7,rightshoulder:b8,dpup:b16,dpdown:b17,dpleft:b18,dpright:b19,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a9,righttrigger:a8,"); + //auto mapResult = SDL_GameControllerAddMapping("03000000de2800000512000011010000,Steam Deck,platform:Linux,crc:17f6,a:b3,b:b4,x:b5,y:b6,back:b11,guide:b13,start:b12,leftstick:b14,rightstick:b15,leftshoulder:b7,rightshoulder:b8,dpup:b16,dpdown:b17,dpleft:b18,dpright:b19,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,leftx:a0,lefty:a1,rightx:a2,righty:a3,lefttrigger:a9,righttrigger:a8"); + auto mapResult = SDL_GameControllerAddMapping("03000000de2800000512000011010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,misc1:b2,paddle1:b21,paddle2:b20,paddle3:b23,paddle4:b22,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux"); + //auto mapResult = SDL_GameControllerAddMapping("03000000de2800000512000010010000,Steam Deck,a:b3,b:b4,back:b11,dpdown:b17,dpleft:b18,dpright:b19,dpup:b16,guide:b13,leftshoulder:b7,leftstick:b14,lefttrigger:a9,leftx:a0,lefty:a1,rightshoulder:b8,rightstick:b15,righttrigger:a8,rightx:a2,righty:a3,start:b12,x:b5,y:b6,platform:Linux"); if (mapResult == -1) { LogE << SDL_GetError() << std::endl; diff --git a/libsocial/include/Input.hpp b/libsocial/include/Input.hpp index 3257218f3..d72eae123 100644 --- a/libsocial/include/Input.hpp +++ b/libsocial/include/Input.hpp @@ -105,17 +105,20 @@ namespace Progress file.file = SDL_RWFromFile(path.c_str(), "wb"); if (file.file) { - file.file->write(file.file, &holeIndex, sizeof(holeIndex), 1); + static constexpr std::size_t MaxBytes = 26; //size of holeIndex + 18 scores. TODO this will make files INCOMPATIBLE cross platform if size_t is defined as a different size... + + auto written = file.file->write(file.file, &holeIndex, sizeof(holeIndex), 1); const auto scoreSize = std::min(std::size_t(18), holeScores.size()); for (auto i = 0u; i < scoreSize; ++i) { - file.file->write(file.file, &holeScores[i], 1, 1); + written += file.file->write(file.file, &holeScores[i], 1, 1); } - for (auto i = scoreSize; i < 18; ++i) + //for (auto i = scoreSize; i < 18; ++i) + while (written < MaxBytes) { std::uint8_t packing = 0; - file.file->write(file.file, &packing, 1, 1); + written += file.file->write(file.file, &packing, 1, 1); } } } @@ -131,9 +134,13 @@ namespace Progress file.file = SDL_RWFromFile(path.c_str(), "rb"); if (file.file) { + /*auto size = file.file->seek(file.file, 0, RW_SEEK_END); + file.file->seek(file.file, 0, RW_SEEK_SET);*/ + std::array buffer = {}; std::size_t i = 0u; - while (file.file->read(file.file, &buffer[i], 1, 1)) + while (file.file->read(file.file, &buffer[i], 1, 1) + && i < buffer.size() - 1) //hm some existing files have 1 byte padding too many { i++; } diff --git a/libsocial/include/Social.hpp b/libsocial/include/Social.hpp index 7f12102a7..eb79e34f1 100644 --- a/libsocial/include/Social.hpp +++ b/libsocial/include/Social.hpp @@ -49,11 +49,11 @@ source distribution. //(ball started sending wind effect 1120 -> 1124) //(added night mode/weather 1141 -> 1150) //(player avatar data format changed 1153->1160) -static constexpr std::uint16_t CURRENT_VER = 1160; +static constexpr std::uint16_t CURRENT_VER = 1161; #ifdef __APPLE__ -static const std::string StringVer("1.16.0 (macOS beta)"); +static const std::string StringVer("1.16.1 (macOS beta)"); #else -static const std::string StringVer("1.16.0"); +static const std::string StringVer("1.16.1"); #endif struct HallEntry final diff --git a/samples/golf/buildnumber.h b/samples/golf/buildnumber.h index c9150065e..f80f9fb00 100644 --- a/samples/golf/buildnumber.h +++ b/samples/golf/buildnumber.h @@ -3,7 +3,7 @@ #ifndef BUILD_NUMBER_H_ #define BUILD_NUMBER_H_ -#define BUILDNUMBER 5168 -#define BUILDNUMBER_STR "5168" +#define BUILDNUMBER 5341 +#define BUILDNUMBER_STR "5341" #endif /* BUILD_NUMBER_H_ */ diff --git a/samples/golf/golf.aps b/samples/golf/golf.aps index be9df166b..bd62ffd01 100644 Binary files a/samples/golf/golf.aps and b/samples/golf/golf.aps differ diff --git a/samples/golf/golf.rc b/samples/golf/golf.rc index 6912a1b3f..1788905ab 100644 --- a/samples/golf/golf.rc +++ b/samples/golf/golf.rc @@ -61,8 +61,8 @@ IDI_ICON1 ICON "icon.ico" // VS_VERSION_INFO VERSIONINFO - FILEVERSION 1,16,0,0 - PRODUCTVERSION 1,16,0,0 + FILEVERSION 1,16,1,0 + PRODUCTVERSION 1,16,1,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -79,12 +79,12 @@ BEGIN BEGIN VALUE "CompanyName", "Trederia" VALUE "FileDescription", "Super Video Golf" - VALUE "FileVersion", "1.16.0.0" + VALUE "FileVersion", "1.16.1.0" VALUE "InternalName", "golf.exe" VALUE "LegalCopyright", "Copyright (C) 2024 Trederia Games" VALUE "OriginalFilename", "golf.exe" VALUE "ProductName", "Super Video Golf" - VALUE "ProductVersion", "1.16.0.0" + VALUE "ProductVersion", "1.16.1.0" END END BLOCK "VarFileInfo" diff --git a/samples/golf/src/GolfGame.cpp b/samples/golf/src/GolfGame.cpp index 93f813ea3..b1b146b26 100644 --- a/samples/golf/src/GolfGame.cpp +++ b/samples/golf/src/GolfGame.cpp @@ -1331,6 +1331,10 @@ void GolfGame::loadPreferences() { m_sharedData.useTTS = prop.getValue(); } + else if (name == "use_swingput") + { + m_sharedData.useSwingput = prop.getValue(); + } } } } @@ -1435,6 +1439,7 @@ void GolfGame::savePreferences() cfg.addProperty("fov").setValue(m_sharedData.fov); cfg.addProperty("vertex_snap").setValue(m_sharedData.vertexSnap); cfg.addProperty("mouse_speed").setValue(m_sharedData.mouseSpeed); + cfg.addProperty("use_swingput").setValue(m_sharedData.useSwingput); cfg.addProperty("invert_x").setValue(m_sharedData.invertX); cfg.addProperty("invert_y").setValue(m_sharedData.invertY); cfg.addProperty("show_beacon").setValue(m_sharedData.showBeacon); diff --git a/samples/golf/src/golf/CareerState.cpp b/samples/golf/src/golf/CareerState.cpp index eb62350a8..f07d8e272 100644 --- a/samples/golf/src/golf/CareerState.cpp +++ b/samples/golf/src/golf/CareerState.cpp @@ -333,8 +333,9 @@ void CareerState::buildScene() //check if we just completed a league, and if we did and it's //one less than max leagues, increment our current round id - auto currIdx = m_sharedData.leagueRoundID - 1; - if (currIdx == m_maxLeagueIndex - 1) + auto currIdx = std::max(0, m_sharedData.leagueRoundID - 1); + if (currIdx != 0 && + currIdx == m_maxLeagueIndex - 1) { if (Career::instance().getLeagueTables()[currIdx].getCurrentIteration() == 0) { diff --git a/samples/golf/src/golf/CommandIDs.hpp b/samples/golf/src/golf/CommandIDs.hpp index 85f60c4ae..d37a07762 100644 --- a/samples/golf/src/golf/CommandIDs.hpp +++ b/samples/golf/src/golf/CommandIDs.hpp @@ -115,7 +115,8 @@ struct CommandID final CourseType = 0x80000, MetricClub = 0x100000, //used in the stats viewer for club labels ImperialClub = 0x200000, - ChatHint = 0x400000 + ChatHint = 0x400000, + CourseHint = 0x800000 }; }; }; \ No newline at end of file diff --git a/samples/golf/src/golf/CommonConsts.hpp b/samples/golf/src/golf/CommonConsts.hpp index a078afa24..ba037e176 100644 --- a/samples/golf/src/golf/CommonConsts.hpp +++ b/samples/golf/src/golf/CommonConsts.hpp @@ -1,6 +1,6 @@ /*----------------------------------------------------------------------- -Matt Marchant 2021 - 2023 +Matt Marchant 2021 - 2024 http://trederia.blogspot.com Super Video Golf - zlib licence. @@ -54,8 +54,8 @@ namespace ConstVal static constexpr float MinMouseSpeed = 0.5f; static constexpr float MaxMouseSpeed = 2.f; - static constexpr float MinSwingputThresh = 0.2f; - static constexpr float MaxSwingputThresh = 10.f; + static constexpr float MinSwingputThresh = 0.1f; + static constexpr float MaxSwingputThresh = 2.f; static constexpr std::int32_t MaxProfiles = 64; static constexpr std::uint32_t MaxBalls = 64u; diff --git a/samples/golf/src/golf/DrivingState.hpp b/samples/golf/src/golf/DrivingState.hpp index 1c3348f3e..fc0519402 100644 --- a/samples/golf/src/golf/DrivingState.hpp +++ b/samples/golf/src/golf/DrivingState.hpp @@ -215,7 +215,6 @@ class DrivingState final : public cro::State, public cro::GuiClient cro::Entity m_mapCam; cro::SimpleQuad m_flagQuad; void createUI(); - void createSwingMeter(cro::Entity); void createGameOptions(); void createSummary(); void updateMinimap(); diff --git a/samples/golf/src/golf/DrivingStateUI.cpp b/samples/golf/src/golf/DrivingStateUI.cpp index ac000d3b2..698686903 100644 --- a/samples/golf/src/golf/DrivingStateUI.cpp +++ b/samples/golf/src/golf/DrivingStateUI.cpp @@ -242,7 +242,11 @@ void DrivingState::createUI() entity.addComponent(); entity.addComponent(); auto infoEnt = entity; - createSwingMeter(entity); + + entity = m_uiScene.createEntity(); + createSwingputMeter(entity, m_inputParser); + infoEnt.getComponent().addChild(entity.getComponent()); + //createSwingMeter(entity); auto& font = m_sharedData.sharedResources->fonts.get(FontID::UI); @@ -807,7 +811,7 @@ void DrivingState::createUI() cam.viewport = { 0.f, 0.f, 1.f, 1.f }; m_viewScale = glm::vec2(getViewScale()); - m_inputParser.setMouseScale(m_viewScale.x); + //m_inputParser.setMouseScale(m_viewScale.x); glm::vec2 courseScale(m_sharedData.pixelScale ? m_viewScale.x : 1.f); @@ -886,78 +890,6 @@ void DrivingState::createUI() createSummary(); } -void DrivingState::createSwingMeter(cro::Entity root) -{ - static constexpr float Width = 4.f; - static constexpr float Height = 40.f; - auto entity = m_uiScene.createEntity(); - entity.addComponent(); - entity.addComponent().setVertexData( - { - cro::Vertex2D(glm::vec2(-Width, -Height), SwingputDark), - cro::Vertex2D(glm::vec2(Width, -Height), SwingputDark), - cro::Vertex2D(glm::vec2(-Width, -0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(Width, -0.5f), SwingputDark), - - cro::Vertex2D(glm::vec2(-Width, -0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(Width, -0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(-Width, 0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(Width, 0.5f), TextNormalColour), - - cro::Vertex2D(glm::vec2(-Width, 0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(Width, 0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(-Width, Height), SwingputDark), - cro::Vertex2D(glm::vec2(Width, Height), SwingputDark), - - - cro::Vertex2D(glm::vec2(-Width, -Height), SwingputLight), - cro::Vertex2D(glm::vec2(Width, -Height), SwingputLight), - cro::Vertex2D(glm::vec2(-Width, 0.f), SwingputLight), - cro::Vertex2D(glm::vec2(Width, 0.f), SwingputLight), - }); - entity.addComponent().active = true; - entity.getComponent().setUserData(0.f); - entity.getComponent().function = - [&](cro::Entity e, float dt) - { - auto& verts = e.getComponent().getVertexData(); - float height = verts[14].position.y; - float targetAlpha = -0.01f; - - if (m_inputParser.isSwingputActive()) - { - height = m_inputParser.getSwingputPosition() * ((Height * 2.f) / MaxSwingputDistance); - targetAlpha = 1.f; - } - - auto& currentAlpha = e.getComponent().getUserData(); - const float InSpeed = dt * 6.f; - const float OutSpeed = m_inputParser.getPower() < 0.5 ? InSpeed : dt * 0.5f; - if (currentAlpha <= targetAlpha) - { - currentAlpha = std::min(1.f, currentAlpha + InSpeed); - } - else - { - currentAlpha = std::max(0.f, currentAlpha - OutSpeed); - } - - for (auto& v : verts) - { - v.colour.setAlpha(currentAlpha); - } - verts[14].position.y = height; - verts[15].position.y = height; - }; - - entity.addComponent().ID = CommandID::UI::UIElement; - entity.addComponent().depth = 0.2f; - entity.getComponent().relativePosition = { 1.f, 0.f }; - entity.getComponent().absolutePosition = { -10.f, 50.f }; - - root.getComponent().addChild(entity.getComponent()); -} - void DrivingState::createGameOptions() { const auto centreSprite = [](cro::Entity e) diff --git a/samples/golf/src/golf/GameConsts.hpp b/samples/golf/src/golf/GameConsts.hpp index b257712de..0f1cd2f12 100644 --- a/samples/golf/src/golf/GameConsts.hpp +++ b/samples/golf/src/golf/GameConsts.hpp @@ -32,6 +32,9 @@ source distribution. #include "Terrain.hpp" #include "MenuConsts.hpp" #include "SharedStateData.hpp" +#include "InputParser.hpp" +#include "MenuConsts.hpp" +#include "CommandIDs.hpp" #include "../GolfGame.hpp" #include @@ -43,7 +46,9 @@ source distribution. #include #include #include +#include #include +#include #include #include #include @@ -677,6 +682,76 @@ static inline std::vector getStrokeIndicatorVerts() }; } +static inline void createSwingputMeter(cro::Entity entity, InputParser& inputParser) +{ + static constexpr float Width = 4.f; + static constexpr float Height = 40.f; + + entity.addComponent(); + entity.addComponent().setVertexData( + { + cro::Vertex2D(glm::vec2(-Width, -Height), SwingputDark), + cro::Vertex2D(glm::vec2(Width, -Height), SwingputDark), + cro::Vertex2D(glm::vec2(-Width, -0.5f), SwingputDark), + cro::Vertex2D(glm::vec2(Width, -0.5f), SwingputDark), + + cro::Vertex2D(glm::vec2(-Width, -0.5f), TextNormalColour), + cro::Vertex2D(glm::vec2(Width, -0.5f), TextNormalColour), + cro::Vertex2D(glm::vec2(-Width, 0.5f), TextNormalColour), + cro::Vertex2D(glm::vec2(Width, 0.5f), TextNormalColour), + + cro::Vertex2D(glm::vec2(-Width, 0.5f), SwingputDark), + cro::Vertex2D(glm::vec2(Width, 0.5f), SwingputDark), + cro::Vertex2D(glm::vec2(-Width, Height), SwingputDark), + cro::Vertex2D(glm::vec2(Width, Height), SwingputDark), + + + cro::Vertex2D(glm::vec2(-Width, -Height), SwingputLight), + cro::Vertex2D(glm::vec2(Width, -Height), SwingputLight), + cro::Vertex2D(glm::vec2(-Width, 0.f), SwingputLight), + cro::Vertex2D(glm::vec2(Width, 0.f), SwingputLight), + }); + entity.addComponent().active = true; + entity.getComponent().setUserData(0.f); + entity.getComponent().function = + [&](cro::Entity e, float dt) + { + auto& verts = e.getComponent().getVertexData(); + float height = verts[14].position.y; + float targetAlpha = -0.01f; + + if (inputParser.isSwingputActive()) + { + height = inputParser.getSwingputPosition() * ((Height * 2.f) / MaxSwingputDistance); + targetAlpha = 1.f; + } + + auto& currentAlpha = e.getComponent().getUserData(); + const float InSpeed = dt * 6.f; + const float OutSpeed = inputParser.getPower() < 0.5 ? InSpeed : dt * 0.5f; + if (currentAlpha <= targetAlpha) + { + currentAlpha = std::min(1.f, currentAlpha + InSpeed); + } + else + { + currentAlpha = std::max(0.f, currentAlpha - OutSpeed); + } + + for (auto& v : verts) + { + v.colour.setAlpha(currentAlpha); + } + verts[14].position.y = height; + verts[15].position.y = height; + }; + + entity.addComponent().ID = CommandID::UI::UIElement; + entity.addComponent().depth = 0.2f; + entity.getComponent().relativePosition = { 1.f, 0.f }; + entity.getComponent().absolutePosition = { -10.f, 50.f }; +} + //applies material data loaded in a model definition such as texture info to custom materials static inline void applyMaterialData(const cro::ModelDefinition& modelDef, cro::Material::Data& dest, std::size_t matID = 0) { diff --git a/samples/golf/src/golf/GolfState.cpp b/samples/golf/src/golf/GolfState.cpp index 0c8bb0b07..51e7c5aae 100644 --- a/samples/golf/src/golf/GolfState.cpp +++ b/samples/golf/src/golf/GolfState.cpp @@ -429,12 +429,22 @@ bool GolfState::handleEvent(const cro::Event& evt) } }; + const auto toggleMiniZoom = [&]() + { + auto& [_, dir] = m_mapRoot.getComponent().getUserData>(); + dir = (dir == 0) ? 1 : 0; + m_mapRoot.getComponent().active = true; + }; + if (evt.type == SDL_KEYUP) { //hideMouse(); //TODO this should only react to current keybindings switch (evt.key.keysym.sym) { default: break; + case SDLK_KP_MULTIPLY: + toggleMiniZoom(); + break; case SDLK_2: if (!m_textChat.isVisible() && !m_holeData[m_currentHole].puttFromTee) @@ -479,10 +489,7 @@ bool GolfState::handleEvent(const cro::Event& evt) closeMessage(); break; case SDLK_F6: - //logCSV(); - /*m_activeAvatar->model.getComponent().active = true; - m_activeAvatar->model.getComponent().setHidden(false);*/ - //m_sharedData.clientConnection.netClient.sendPacket(PacketID::ServerCommand, std::uint16_t(ServerCommand::NextHole), net::NetFlag::Reliable); + toggleMiniZoom(); break; case SDLK_F8: if (evt.key.keysym.mod & KMOD_SHIFT) @@ -721,9 +728,14 @@ bool GolfState::handleEvent(const cro::Event& evt) switch (evt.cbutton.button) { default: break; - case cro::GameController::ButtonLeftStick: + case cro::GameController::ButtonRightStick: + //can't use sticks because they are used on the emote wheel //showMapOverview(); break; + case cro::GameController::ButtonTrackpad: + case cro::GameController::PaddleR5: + toggleMiniZoom(); + break; case cro::GameController::ButtonBack: if (!m_textChat.isVisible()) { @@ -744,7 +756,7 @@ bool GolfState::handleEvent(const cro::Event& evt) case cro::GameController::ButtonA: toggleQuitReady(); break; - case cro::GameController::ButtonTrackpad: + //case cro::GameController::ButtonTrackpad: case cro::GameController::PaddleR4: m_textChat.toggleWindow(true, true); break; @@ -788,7 +800,14 @@ bool GolfState::handleEvent(const cro::Event& evt) } else if (evt.type == SDL_MOUSEBUTTONDOWN) { - if (evt.button.button == SDL_BUTTON_RIGHT) + if (evt.button.button == SDL_BUTTON_LEFT) + { + toggleQuitReady(); + } + } + else if (evt.type == SDL_MOUSEBUTTONUP) + { + if (evt.button.button == SDL_BUTTON_LEFT) { closeMessage(); } @@ -1156,6 +1175,13 @@ void GolfState::handleMessage(const cro::Message& msg) setActiveCamera(data.data); } break; + case SceneEvent::RequestToggleMinimap: + { + auto& [_, dir] = m_mapRoot.getComponent().getUserData>(); + dir = (dir == 0) ? 1 : 0; + m_mapRoot.getComponent().active = true; + } + break; } } break; @@ -1166,6 +1192,9 @@ void GolfState::handleMessage(const cro::Message& msg) switch (data.type) { default: break; + case GolfEvent::NiceTiming: + Social::awardXP(10, XPStringID::NiceTiming); + break; case GolfEvent::HitBall: { hitBall(); diff --git a/samples/golf/src/golf/GolfState.hpp b/samples/golf/src/golf/GolfState.hpp index d85b1ba81..f65f53c57 100644 --- a/samples/golf/src/golf/GolfState.hpp +++ b/samples/golf/src/golf/GolfState.hpp @@ -460,7 +460,6 @@ class GolfState final : public cro::State, public cro::GuiClient, public cro::Co std::uint8_t m_readyQuitFlags; void buildUI(); - void createSwingMeter(cro::Entity); void showCountdown(std::uint8_t); void createScoreboard(); void updateScoreboard(bool updatePardiff = true); @@ -543,6 +542,7 @@ class GolfState final : public cro::State, public cro::GuiClient, public cro::Co //----------- cro::Entity m_mapCam; + cro::Entity m_mapRoot; cro::RenderTexture m_mapTexture; cro::MultiRenderTexture m_mapTextureMRT; //hack to create images for map explorer diff --git a/samples/golf/src/golf/GolfStateUI.cpp b/samples/golf/src/golf/GolfStateUI.cpp index 3adee342e..22f7922dd 100644 --- a/samples/golf/src/golf/GolfStateUI.cpp +++ b/samples/golf/src/golf/GolfStateUI.cpp @@ -288,7 +288,11 @@ void GolfState::buildUI() entity.addComponent(); entity.addComponent(); auto infoEnt = entity; - createSwingMeter(infoEnt); + + entity = m_uiScene.createEntity(); + createSwingputMeter(entity, m_inputParser); + infoEnt.getComponent().addChild(entity.getComponent()); + m_textChat.setRootNode(infoEnt); auto& font = m_sharedData.sharedResources->fonts.get(FontID::UI); @@ -997,7 +1001,7 @@ void GolfState::buildUI() float textureRatio = 1.f; //pixels per metre in the minimap texture * 2 }; entity = m_uiScene.createEntity(); - entity.addComponent().setPosition({ 0.f, 82.f }); + entity.addComponent();// .setPosition({ 0.f, 82.f }); entity.getComponent().setRotation(-90.f * cro::Util::Const::degToRad); entity.getComponent().setOrigin({ 0.f, 0.f, 0.1f }); entity.getComponent().setScale(glm::vec2(0.05f, 0.f)); @@ -1120,7 +1124,64 @@ void GolfState::buildUI() } e.getComponent().setScale(glm::vec2(1.f / ratio, newScale / ratio)); }; - infoEnt.getComponent().addChild(entity.getComponent()); + + m_mapRoot = m_uiScene.createEntity(); + m_mapRoot.addComponent().addChild(entity.getComponent()); + m_mapRoot.addComponent().setUserData>(0.f, 1); + m_mapRoot.getComponent().function = + [](cro::Entity e, float dt) + { + auto& [progress, dir] = e.getComponent().getUserData>(); + if (dir == 0) + { + //bigger + progress = std::min(1.f, progress + (dt * 8.f)); + if (progress == 1) + { + e.getComponent().active = false; + } + } + else + { + progress = std::max(0.f, progress - (dt * 8.f)); + if (progress == 0) + { + e.getComponent().active = false; + } + } + + static constexpr glm::vec2 offset(0.5299f, 0.84799f); //1/8 map size, rotated 90deg, normalised + static constexpr float l = 47.167f;// glm::length(offset); //aww no constexpr + + const auto p = cro::Util::Easing::easeOutExpo(progress); + auto o = offset * l * p; + o.x = std::round(o.x); + o.y = std::round(o.y); + + e.getComponent().setScale(glm::vec2(1.f + p)); + e.getComponent().setOrigin(o); + }; + + + /*registerWindow([&]() + { + if (ImGui::Begin("Zoom")) + { + static float scale = 1.f; + + + if (ImGui::SliderFloat("Scale", &scale, 1.f, 2.f)) + { + m_mapRoot.getComponent().setScale(glm::vec2(scale)); + m_mapRoot.getComponent().setOrigin(offset * (scale - 1.f)); + } + + + } + ImGui::End(); + });*/ + + infoEnt.getComponent().addChild(m_mapRoot.getComponent()); auto mapEnt = entity; m_minimapEnt = entity; @@ -1457,14 +1518,14 @@ void GolfState::buildUI() //callback for the UI camera when window is resized - auto updateView = [&, trophyEnt, courseEnt, infoEnt, spinEnt, windEnt, windEnt2, mapEnt, greenEnt, rootNode](cro::Camera& cam) mutable + auto updateView = [&, trophyEnt, courseEnt, infoEnt, spinEnt, windEnt, windEnt2, greenEnt, rootNode](cro::Camera& cam) mutable { auto size = glm::vec2(GolfGame::getActiveTarget()->getSize()); cam.setOrthographic(0.f, size.x, 0.f, size.y, -3.5f, 20.f); cam.viewport = { 0.f, 0.f, 1.f, 1.f }; m_viewScale = glm::vec2(getViewScale()); - m_inputParser.setMouseScale(m_viewScale.x); + //m_inputParser.setMouseScale(m_viewScale.x); glm::vec2 courseScale(m_sharedData.pixelScale ? m_viewScale.x : 1.f); @@ -1480,7 +1541,7 @@ void GolfState::buildUI() const auto uiSize = size / m_viewScale; auto mapSize = glm::vec2(MapSize / 2u); mapSize /= 2.f; - mapEnt.getComponent().setPosition({ uiSize.x - mapSize.y - 2.f, uiSize.y - mapSize.x - (UIBarHeight + 2.f), -0.05f }); //map sprite is rotated 90 + m_mapRoot.getComponent().setPosition({ uiSize.x - mapSize.y - 2.f, uiSize.y - mapSize.x - (UIBarHeight + 2.f), -0.05f }); //map sprite is rotated 90 greenEnt.getComponent().setPosition({ 2.f, uiSize.y - std::floor(MapSize.y * 0.6f) - UIBarHeight - 2.f, 0.1f }); @@ -1539,78 +1600,6 @@ void GolfState::buildUI() m_emoteWheel.build(infoEnt, m_uiScene, m_resources.textures); } -void GolfState::createSwingMeter(cro::Entity root) -{ - static constexpr float Width = 4.f; - static constexpr float Height = 40.f; - auto entity = m_uiScene.createEntity(); - entity.addComponent(); - entity.addComponent().setVertexData( - { - cro::Vertex2D(glm::vec2(-Width, -Height), SwingputDark), - cro::Vertex2D(glm::vec2(Width, -Height), SwingputDark), - cro::Vertex2D(glm::vec2(-Width, -0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(Width, -0.5f), SwingputDark), - - cro::Vertex2D(glm::vec2(-Width, -0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(Width, -0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(-Width, 0.5f), TextNormalColour), - cro::Vertex2D(glm::vec2(Width, 0.5f), TextNormalColour), - - cro::Vertex2D(glm::vec2(-Width, 0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(Width, 0.5f), SwingputDark), - cro::Vertex2D(glm::vec2(-Width, Height), SwingputDark), - cro::Vertex2D(glm::vec2(Width, Height), SwingputDark), - - - cro::Vertex2D(glm::vec2(-Width, -Height), SwingputLight), - cro::Vertex2D(glm::vec2(Width, -Height), SwingputLight), - cro::Vertex2D(glm::vec2(-Width, 0.f), SwingputLight), - cro::Vertex2D(glm::vec2(Width, 0.f), SwingputLight), - }); - entity.addComponent().active = true; - entity.getComponent().setUserData(0.f); - entity.getComponent().function = - [&](cro::Entity e, float dt) - { - auto& verts = e.getComponent().getVertexData(); - float height = verts[14].position.y; - float targetAlpha = -0.01f; - - if (m_inputParser.isSwingputActive()) - { - height = m_inputParser.getSwingputPosition() * ((Height * 2.f) / MaxSwingputDistance); - targetAlpha = 1.f; - } - - auto& currentAlpha = e.getComponent().getUserData(); - const float InSpeed = dt * 6.f; - const float OutSpeed = m_inputParser.getPower() < 0.5 ? InSpeed : dt * 0.5f; - if (currentAlpha <= targetAlpha) - { - currentAlpha = std::min(1.f, currentAlpha + InSpeed); - } - else - { - currentAlpha = std::max(0.f, currentAlpha - OutSpeed); - } - - for (auto& v : verts) - { - v.colour.setAlpha(currentAlpha); - } - verts[14].position.y = height; - verts[15].position.y = height; - }; - - entity.addComponent().ID = CommandID::UI::UIElement; - entity.addComponent().depth = 0.2f; - entity.getComponent().relativePosition = { 1.f, 0.f }; - entity.getComponent().absolutePosition = { -10.f, 50.f }; - - root.getComponent().addChild(entity.getComponent()); -} - void GolfState::showCountdown(std::uint8_t seconds) { m_roundEnded = true; @@ -4870,7 +4859,8 @@ bool GolfState::EmoteWheel::handleEvent(const cro::Event& evt) else if (evt.type == SDL_CONTROLLERBUTTONDOWN) { auto controllerID = activeControllerID(sharedData.inputBinding.playerID); - if (cro::GameController::controllerID(evt.cbutton.which) == controllerID) + if (cro::GameController::controllerID(evt.cbutton.which) == controllerID + || sharedData.localConnectionData.playerCount == 1) { switch (evt.cbutton.button) { @@ -4915,7 +4905,9 @@ bool GolfState::EmoteWheel::handleEvent(const cro::Event& evt) } else if (evt.type == SDL_CONTROLLERBUTTONUP) { - auto controllerID = activeControllerID(sharedData.inputBinding.playerID); + //the controller ID is actually used to select the play name in this case + auto controllerID = activeControllerID(sharedData.localConnectionData.playerCount == 1 ? 0 : + sharedData.inputBinding.playerID); /*if (cro::GameController::controllerID(evt.cbutton.which) == controllerID) { diff --git a/samples/golf/src/golf/InputBinding.hpp b/samples/golf/src/golf/InputBinding.hpp index 253b7100b..87ead0110 100644 --- a/samples/golf/src/golf/InputBinding.hpp +++ b/samples/golf/src/golf/InputBinding.hpp @@ -53,6 +53,9 @@ namespace InputFlag CamModifier = SpinMenu, SwitchView = EmoteWheel, + Swingput = 0x400, + MiniMap = 0x800, + All = 0xFFFF }; } diff --git a/samples/golf/src/golf/InputParser.cpp b/samples/golf/src/golf/InputParser.cpp index d568655c8..b31f92c85 100644 --- a/samples/golf/src/golf/InputParser.cpp +++ b/samples/golf/src/golf/InputParser.cpp @@ -1,6 +1,6 @@ /*----------------------------------------------------------------------- -Matt Marchant 2021 - 2023 +Matt Marchant 2021 - 2024 http://trederia.blogspot.com Super Video Golf - zlib licence. @@ -139,7 +139,7 @@ void InputParser::handleEvent(const cro::Event& evt) }; if (m_active && - !m_swingput.handleEvent(evt)) + !m_swingput.handleEvent(evt, m_inputFlags, static_cast(m_state))) { //apply to input mask if (evt.type == SDL_KEYDOWN @@ -202,6 +202,12 @@ void InputParser::handleEvent(const cro::Event& evt) { toggleDroneCam(); } + + else if (evt.key.keysym.sym == SDLK_PAGEUP) + { + m_inputFlags |= InputFlag::MiniMap; + m_minimapToggleTimer.restart(); + } } else if (evt.type == SDL_KEYUP) { @@ -246,12 +252,18 @@ void InputParser::handleEvent(const cro::Event& evt) { m_inputFlags &= ~InputFlag::SpinMenu; } + + else if (evt.key.keysym.sym == SDLK_PAGEUP) + { + m_inputFlags &= ~InputFlag::MiniMap; + } } else if (evt.type == SDL_CONTROLLERBUTTONDOWN) { auto controllerID = activeControllerID(m_inputBinding.playerID); if (!m_isCPU && - evt.cbutton.which == cro::GameController::deviceID(controllerID)) + (evt.cbutton.which == cro::GameController::deviceID(controllerID) + || m_sharedData.localConnectionData.playerCount == 1)) //allow input from any controller if only one local player { if (evt.cbutton.button == m_inputBinding.buttons[InputBinding::Action]) { @@ -298,13 +310,20 @@ void InputParser::handleEvent(const cro::Event& evt) { toggleDroneCam(); } + //people say this happens accidentally, so let's use a timer + else if (evt.cbutton.button == cro::GameController::ButtonLeftStick) + { + m_inputFlags |= InputFlag::MiniMap; + m_minimapToggleTimer.restart(); + } } } else if (evt.type == SDL_CONTROLLERBUTTONUP) { auto controllerID = activeControllerID(m_inputBinding.playerID); if (!m_isCPU && - evt.cbutton.which == cro::GameController::deviceID(controllerID)) + (evt.cbutton.which == cro::GameController::deviceID(controllerID) + || m_sharedData.localConnectionData.playerCount == 1)) { if (evt.cbutton.button == m_inputBinding.buttons[InputBinding::Action]) { @@ -351,6 +370,11 @@ void InputParser::handleEvent(const cro::Event& evt) { m_inputFlags &= ~InputFlag::Down; } + + else if (evt.cbutton.button == cro::GameController::ButtonLeftStick) + { + m_inputFlags &= ~InputFlag::MiniMap; + } } } @@ -381,7 +405,7 @@ void InputParser::handleEvent(const cro::Event& evt) }*/ } - else + else if ((m_inputFlags & InputFlag::Swingput) == 0) { m_inputFlags = 0; } @@ -649,6 +673,15 @@ void InputParser::update(float dt) } else { + //if the stick is held, toggle the mini map + if ((m_inputFlags & InputFlag::MiniMap) + && m_minimapToggleTimer.elapsed() > cro::seconds(0.5f)) + { + cro::App::postMessage(MessageID::SceneMessage)->type = SceneEvent::RequestToggleMinimap; + + m_inputFlags &= ~InputFlag::MiniMap; + } + //drone controls handle controller independently checkControllerInput(); checkMouseInput(); @@ -841,25 +874,14 @@ void InputParser::updateDistanceEstimation() void InputParser::updateStroke(float dt) { + m_swingput.assertIdled(dt, m_inputFlags, static_cast(m_state)); + //catch the inputs that where filtered by the //enable flags so we can raise their own event for them auto disabledFlags = (m_inputFlags & ~m_enableFlags); if (m_active) { - if (m_swingput.process(dt)) - { - //we took our shot - m_power = m_swingput.getPower(); - m_hook = m_swingput.getHook(); - - m_powerbarDirection = 1.f; - m_state = State::Flight; - - auto* msg = cro::App::postMessage(MessageID::GolfMessage); - msg->type = GolfEvent::HitBall; - } - m_inputFlags &= m_enableFlags; switch (m_state) @@ -972,7 +994,8 @@ void InputParser::updateStroke(float dt) m_powerbarDirection = -1.f; } - if (m_sharedData.pressHold) + if (m_sharedData.pressHold + && ((m_inputFlags & InputFlag::Swingput) == 0)) { if ((m_inputFlags & InputFlag::Action) == 0 && (m_prevFlags & InputFlag::Action)) { @@ -991,9 +1014,23 @@ void InputParser::updateStroke(float dt) { m_powerbarDirection = 1.f; - m_state = State::Stroke; - m_doubleTapClock.restart(); - beginIcon(); + if (m_inputFlags & InputFlag::Swingput) + { + //take shot immediately + m_state = State::Flight; + m_hook = m_swingput.getHook(); + + auto* msg = cro::App::postMessage(MessageID::GolfMessage); + msg->type = GolfEvent::HitBall; + + //TODO read hook value from swingput + } + else + { + m_state = State::Stroke; + m_doubleTapClock.restart(); + beginIcon(); + } } } } @@ -1074,6 +1111,13 @@ void InputParser::updateStroke(float dt) } } + //if the input is flagged as being from the swingput, + //automatically reset the flag as if the button was released + if ((m_inputFlags & InputFlag::Swingput) != 0) + { + m_inputFlags &= ~(InputFlag::Action | InputFlag::Cancel | InputFlag::Swingput); + } + m_prevDisabledFlags = disabledFlags; } diff --git a/samples/golf/src/golf/InputParser.hpp b/samples/golf/src/golf/InputParser.hpp index bf8b7f1cf..a29d9b626 100644 --- a/samples/golf/src/golf/InputParser.hpp +++ b/samples/golf/src/golf/InputParser.hpp @@ -78,8 +78,8 @@ class InputParser final bool isAiming() const { return m_state == InputParser::State::Aim; } bool isSwingputActive() const { return m_swingput.isActive() && m_state != State::Drone; } - float getSwingputPosition() const { return m_swingput.getActivePoint().y; } - void setMouseScale(float scale) { CRO_ASSERT(scale > 0, ""); m_swingput.setMouseScale(scale); } + float getSwingputPosition() const { return m_swingput.getGaugePosition(); } + //void setMouseScale(float scale) { CRO_ASSERT(scale > 0, ""); m_swingput.setMouseScale(scale); } void setMaxRotation(float); float getMaxRotation() const { return m_maxRotation; } @@ -169,6 +169,8 @@ class InputParser final void checkMouseInput(); glm::vec2 getRotationalInput(std::int32_t xAxis, std::int32_t yAxis) const; //used for drone cam and spin amount + cro::Clock m_minimapToggleTimer; + cro::Clock m_iconTimer; bool m_iconActive; void beginIcon(); diff --git a/samples/golf/src/golf/MenuAvatars.cpp b/samples/golf/src/golf/MenuAvatars.cpp index 5690be9ca..cb852dfee 100644 --- a/samples/golf/src/golf/MenuAvatars.cpp +++ b/samples/golf/src/golf/MenuAvatars.cpp @@ -942,9 +942,21 @@ void MenuState::createAvatarMenu(cro::Entity parent) { profile.avatarFlags[i] = static_cast(cro::Util::Random::value(0u, pc::PairCounts[i] - 1)); } - profile.skinID = m_sharedData.avatarInfo[cro::Util::Random::value(0u, m_sharedData.avatarInfo.size() - 1)].uid; - profile.ballID = m_sharedData.ballInfo[cro::Util::Random::value(0u, m_sharedData.ballInfo.size() - 1)].uid; - profile.hairID = m_sharedData.hairInfo[cro::Util::Random::value(0u, m_sharedData.hairInfo.size() - 1)].uid; + + //don't allow locked items duh + profile.skinID = m_cosmeticIDs.avatars[cro::Util::Random::value(0u, m_cosmeticIDs.avatars.size() - 1)]; + profile.hairID = m_cosmeticIDs.hair[cro::Util::Random::value(0u, m_cosmeticIDs.hair.size() - 1)]; + //prefer the white ball by default + if (std::find(m_cosmeticIDs.balls.begin(), m_cosmeticIDs.balls.end(), DefaultBallID) != m_cosmeticIDs.balls.end()) + { + profile.ballID = DefaultBallID; + } + else + { + profile.ballID = m_cosmeticIDs.balls[cro::Util::Random::value(0u, m_cosmeticIDs.balls.size() - 1)]; + + } + profile.flipped = cro::Util::Random::value(0, 1) == 0 ? false : true; profile.saveProfile(); m_profileData.activeProfileIndex = m_profileData.playerProfiles.size() - 1; diff --git a/samples/golf/src/golf/MenuConsts.hpp b/samples/golf/src/golf/MenuConsts.hpp index 26c1ea90f..8d93f663e 100644 --- a/samples/golf/src/golf/MenuConsts.hpp +++ b/samples/golf/src/golf/MenuConsts.hpp @@ -129,6 +129,8 @@ static constexpr std::array EmotePositions = glm::vec3(24.f, -24.f, 0.15f) }; +static constexpr std::uint32_t DefaultBallID = 1714267008; + static constexpr std::uint32_t LargeTextSize = 64; static constexpr std::uint32_t MediumTextSize = 32; static constexpr std::uint32_t SmallTextSize = 16; diff --git a/samples/golf/src/golf/MenuCreation.cpp b/samples/golf/src/golf/MenuCreation.cpp index 618cde857..26353e2db 100644 --- a/samples/golf/src/golf/MenuCreation.cpp +++ b/samples/golf/src/golf/MenuCreation.cpp @@ -1743,6 +1743,134 @@ void MenuState::createLobbyMenu(cro::Entity parent, std::uint32_t mouseEnter, st auto bgEnt = entity; auto bgBounds = bounds; + + //shows a button hint for switching courses if hosting + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ (bgBounds.width / 2.f) - 180.f, bgBounds.height - 15.f, -0.2f }); + entity.addComponent(); + entity.addComponent() = spriteSheetV2.getSprite("button_hint"); + entity.addComponent().active = true; + entity.getComponent().function = + [&](cro::Entity e, float dt) + { + static constexpr float HideOffset = 16.f; + float target = HideOffset; + if (m_sharedData.hosting) + { + //hmm, seems a funny way to tell which page we're on + //but it does the job I guess + if (m_lobbyWindowEntities[LobbyEntityID::HoleSelection].getComponent().getScale().x != 0) + { + target = 0.f; + } + } + + const float Speed = 56.f * dt; + auto origin = e.getComponent().getOrigin(); + if (target > origin.y) + { + origin.y = std::min(target, origin.y + Speed); + } + else + { + origin.y = std::max(target, origin.y - Speed); + } + e.getComponent().setOrigin(origin); + }; + bgEnt.getComponent().addChild(entity.getComponent()); + auto buttonEnt = entity; + + //button icons + struct IconCallback final + { + void operator()(cro::Entity e, float) + { + if (cro::GameController::isConnected(0)) + { + if (cro::GameController::hasPSLayout(0)) + { + e.getComponent().play(1); + } + else + { + e.getComponent().play(0); + } + } + e.getComponent().active = false; + } + }; + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ 67.f, -1.f, 0.1f }); + entity.addComponent().setFacing(cro::Drawable2D::Facing::Back); + entity.addComponent() = spriteSheetV2.getSprite("lb"); + entity.addComponent(); + entity.addComponent().active = false; //set true but the UI command + entity.getComponent().function = IconCallback(); + entity.addComponent().ID = CommandID::Menu::CourseHint; + buttonEnt.getComponent().addChild(entity.getComponent()); + + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ 273.f, -1.f, 0.1f }); + entity.addComponent().setFacing(cro::Drawable2D::Facing::Back); + entity.addComponent() = spriteSheetV2.getSprite("rb"); + entity.addComponent(); + entity.addComponent().active = false; + entity.getComponent().function = IconCallback(); + entity.addComponent().ID = CommandID::Menu::CourseHint; + buttonEnt.getComponent().addChild(entity.getComponent()); + + + //button texts + struct TextCallback final + { + std::int32_t keybind = InputBinding::PrevClub; + const SharedStateData& sharedData; + explicit TextCallback(std::int32_t kb, const SharedStateData& sd) : keybind(kb), sharedData(sd) {} + + void operator()(cro::Entity e, float) + { + cro::String s; + if (keybind == InputBinding::PrevClub) + { + s = "< " + cro::Keyboard::keyString(sharedData.inputBinding.keys[keybind]); + } + else + { + s = cro::Keyboard::keyString(sharedData.inputBinding.keys[keybind]) + " >"; + } + e.getComponent().setString(s); + e.getComponent().active = false; + } + }; + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ 75.f, 9.f, 0.1f }); + entity.addComponent(); + entity.addComponent(font).setString("X"); + entity.getComponent().setFillColour(TextNormalColour); + entity.getComponent().setShadowColour(LeaderboardTextDark); + entity.getComponent().setShadowOffset({ 1.f, -1.f }); + entity.getComponent().setCharacterSize(UITextSize); + entity.getComponent().setAlignment(cro::Text::Alignment::Centre); + entity.addComponent().active = false; //command sets this to true so we only update on switch + entity.getComponent().function = TextCallback(InputBinding::PrevClub, m_sharedData); + entity.addComponent().ID = CommandID::Menu::CourseHint; + buttonEnt.getComponent().addChild(entity.getComponent()); + + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({ 285.f, 9.f, 0.1f }); + entity.addComponent(); + entity.addComponent(font).setString("Y"); + entity.getComponent().setFillColour(TextNormalColour); + entity.getComponent().setShadowColour(LeaderboardTextDark); + entity.getComponent().setShadowOffset({ 1.f, -1.f }); + entity.getComponent().setCharacterSize(UITextSize); + entity.getComponent().setAlignment(cro::Text::Alignment::Centre); + entity.addComponent().active = false; + entity.getComponent().function = TextCallback(InputBinding::NextClub, m_sharedData); + entity.addComponent().ID = CommandID::Menu::CourseHint; + buttonEnt.getComponent().addChild(entity.getComponent()); + + #ifdef USE_GNS //scrolls info about the selected course auto& labelFont = m_sharedData.sharedResources->fonts.get(FontID::Label); @@ -3917,8 +4045,8 @@ void MenuState::addCourseSelectButtons() buttonEnt.addComponent().absolutePosition = { 6.f, 101.f }; buttonEnt.getComponent().depth = 0.01f; bounds = buttonEnt.getComponent().getTextureBounds(); - bounds.bottom -= 6.f; - bounds.height += 12.f; + bounds.bottom -= 16.f; + bounds.height += 32.f; buttonEnt.addComponent().area = bounds; buttonEnt.getComponent().setGroup(MenuID::Lobby); buttonEnt.getComponent().setSelectionIndex(CoursePrev); @@ -3947,8 +4075,8 @@ void MenuState::addCourseSelectButtons() buttonEnt.addComponent().absolutePosition = { 159.f, 101.f }; buttonEnt.getComponent().depth = 0.01f; bounds = buttonEnt.getComponent().getTextureBounds(); - bounds.bottom -= 6.f; - bounds.height += 12.f; + bounds.bottom -= 16.f; + bounds.height += 32.f; buttonEnt.addComponent().area = bounds; buttonEnt.getComponent().setGroup(MenuID::Lobby); buttonEnt.getComponent().setSelectionIndex(CourseNext); diff --git a/samples/golf/src/golf/MenuCustomisation.cpp b/samples/golf/src/golf/MenuCustomisation.cpp index 4fc322d17..1f6def81f 100644 --- a/samples/golf/src/golf/MenuCustomisation.cpp +++ b/samples/golf/src/golf/MenuCustomisation.cpp @@ -522,6 +522,29 @@ void MenuState::createBallScene() }), m_sharedData.ballInfo.end()); } + //put the default ball at the front - this doesn't work??? + //if (const auto b = std::find_if(m_sharedData.ballInfo.cbegin(), m_sharedData.ballInfo.cend(), [](const auto& inf) {return inf.uid == DefaultBallID;}); + // b != m_sharedData.ballInfo.cend()) + //{ + // //std::swap(m_sharedData.ballInfo.begin(), b); + // const auto pos = std::distance(m_sharedData.ballInfo.cbegin(), b); + // const auto old = m_sharedData.ballInfo[0]; + // m_sharedData.ballInfo[0] = m_sharedData.ballInfo[pos]; + // m_sharedData.ballInfo[pos] = old; + + // const auto oldModel = m_ballModels[0]; + // m_ballModels[0] = m_ballModels[pos]; + // m_ballModels[pos] = oldModel; + //} + + //store valid/unlocked IDs so the new profile randomiser can pick one + for (const auto& i : m_sharedData.ballInfo) + { + if (!i.locked) + { + m_cosmeticIDs.balls.push_back(i.uid); + } + } } std::int32_t MenuState::indexFromBallID(std::uint32_t ballID) @@ -631,6 +654,14 @@ void MenuState::parseAvatarDirectory() } + //store the unlocked UIDs so the new profile can pick only unlocked avatars + for (const auto& i : m_sharedData.avatarInfo) + { + if (!i.locked) + { + m_cosmeticIDs.avatars.push_back(i.uid); + } + } //load hair models @@ -750,8 +781,14 @@ void MenuState::parseAvatarDirectory() } } - - + //honestly these are probably already sorted above, but let's just get this done + for (const auto& i : m_sharedData.hairInfo) + { + if (!i.locked) + { + m_cosmeticIDs.hair.push_back(i.uid); + } + } //these are just used in the player preview window diff --git a/samples/golf/src/golf/MenuState.cpp b/samples/golf/src/golf/MenuState.cpp index cf086541b..c09c234e8 100644 --- a/samples/golf/src/golf/MenuState.cpp +++ b/samples/golf/src/golf/MenuState.cpp @@ -580,6 +580,38 @@ MenuState::~MenuState() //public bool MenuState::handleEvent(const cro::Event& evt) { + const auto doNext = [&]() + { + if (m_sharedData.hosting + && m_currentMenu == MenuID::Lobby + && m_lobbyWindowEntities[LobbyEntityID::HoleSelection].getComponent().getScale().x != 0) + { + nextCourse(); + } + else if (m_currentMenu == MenuID::Avatar) + { + auto i = m_rosterMenu.profileIndices[m_rosterMenu.activeIndex]; + i = (i + 1) % m_profileData.playerProfiles.size(); + setProfileIndex(i); + } + }; + + const auto doPrev = [&]() + { + if (m_sharedData.hosting + && m_currentMenu == MenuID::Lobby + && m_lobbyWindowEntities[LobbyEntityID::HoleSelection].getComponent().getScale().x != 0) + { + prevCourse(); + } + else if (m_currentMenu == MenuID::Avatar) + { + auto i = m_rosterMenu.profileIndices[m_rosterMenu.activeIndex]; + i = (i + (m_profileData.playerProfiles.size() - 1)) % m_profileData.playerProfiles.size(); + setProfileIndex(i); + } + }; + const auto showOptions = [&]() { if (m_currentMenu == MenuID::Lobby) @@ -607,7 +639,7 @@ bool MenuState::handleEvent(const cro::Event& evt) } }; - const auto setChatHint = + const auto setChatHint = //also sets the hint icons for switching courses [&](bool controller, std::int32_t joyID) { cro::Command cmd; @@ -621,7 +653,7 @@ bool MenuState::handleEvent(const cro::Event& evt) { e.getComponent().setScale(glm::vec2(1.f)); m_uiScene.getActiveCamera().getComponent().active = true; - if (cro::GameController::hasPSLayout(/*cro::GameController::controllerID(joyID)*/0)) + if (cro::GameController::hasPSLayout(0)) { e.getComponent().play(1); } @@ -652,6 +684,40 @@ bool MenuState::handleEvent(const cro::Event& evt) }; } m_uiScene.getSystem()->sendCommand(cmd); + + //update the hint about switching courses + cmd.targetFlags = CommandID::Menu::CourseHint; + if (controller) + { + cmd.action = [](cro::Entity e, float) + { + if (e.hasComponent()) + { + e.getComponent().setFacing(cro::Drawable2D::Facing::Front); + e.getComponent().active = true; //updates the animation + } + else + { + e.getComponent().setFacing(cro::Drawable2D::Facing::Back); + } + }; + } + else + { + cmd.action = [](cro::Entity e, float) + { + if (e.hasComponent()) + { + e.getComponent().setFacing(cro::Drawable2D::Facing::Back); + } + else + { + e.getComponent().setFacing(cro::Drawable2D::Facing::Front); + e.getComponent().active = true; //updates the string + } + }; + } + m_uiScene.getSystem()->sendCommand(cmd); }; const auto quitMenu = @@ -743,6 +809,17 @@ bool MenuState::handleEvent(const cro::Event& evt) if (evt.type == SDL_KEYUP) { setChatHint(false, 0); + + if (evt.key.keysym.sym == m_sharedData.inputBinding.keys[InputBinding::PrevClub]) + { + doPrev(); + } + else if (evt.key.keysym.sym == m_sharedData.inputBinding.keys[InputBinding::NextClub]) + { + doNext(); + } + + switch (evt.key.keysym.sym) { default: break; @@ -915,32 +992,10 @@ bool MenuState::handleEvent(const cro::Event& evt) quitMenu(); break; case cro::GameController::ButtonRightShoulder: - if (m_sharedData.hosting - && m_currentMenu == MenuID::Lobby - && m_lobbyWindowEntities[LobbyEntityID::HoleSelection].getComponent().getScale().x != 0) - { - nextCourse(); - } - else if (m_currentMenu == MenuID::Avatar) - { - auto i = m_rosterMenu.profileIndices[m_rosterMenu.activeIndex]; - i = (i + 1) % m_profileData.playerProfiles.size(); - setProfileIndex(i); - } + doNext(); break; case cro::GameController::ButtonLeftShoulder: - if (m_sharedData.hosting - && m_currentMenu == MenuID::Lobby - && m_lobbyWindowEntities[LobbyEntityID::HoleSelection].getComponent().getScale().x != 0) - { - prevCourse(); - } - else if (m_currentMenu == MenuID::Avatar) - { - auto i = m_rosterMenu.profileIndices[m_rosterMenu.activeIndex]; - i = (i + (m_profileData.playerProfiles.size() - 1)) % m_profileData.playerProfiles.size(); - setProfileIndex(i); - } + doPrev(); break; case cro::GameController::ButtonGuide: togglePreviousScoreCard(); @@ -991,6 +1046,7 @@ bool MenuState::handleEvent(const cro::Event& evt) else if (evt.type == SDL_MOUSEMOTION) { cro::App::getWindow().setMouseCaptured(false); + setChatHint(false, 0); } else if (evt.type == SDL_CONTROLLERAXISMOTION) { diff --git a/samples/golf/src/golf/MenuState.hpp b/samples/golf/src/golf/MenuState.hpp index 3478187be..b9aad55d3 100644 --- a/samples/golf/src/golf/MenuState.hpp +++ b/samples/golf/src/golf/MenuState.hpp @@ -108,6 +108,13 @@ class MenuState final : public cro::State, public cro::GuiClient, public cro::Co std::int32_t m_connectedClientCount; std::int32_t m_connectedPlayerCount; + struct CosmeticID final + { + std::vector balls; + std::vector hair; + std::vector avatars; + }m_cosmeticIDs; + TextChat m_textChat; MatchMaking m_matchMaking; cro::ResourceCollection m_resources; diff --git a/samples/golf/src/golf/MessageIDs.hpp b/samples/golf/src/golf/MessageIDs.hpp index 35b42ec9a..3853e6bc6 100644 --- a/samples/golf/src/golf/MessageIDs.hpp +++ b/samples/golf/src/golf/MessageIDs.hpp @@ -65,6 +65,7 @@ struct GolfEvent final SlicedBall, BallLanded, Scored, + NiceTiming, NiceShot, PowerShot, DriveComplete, @@ -103,6 +104,7 @@ struct SceneEvent TransitionStart, TransitionComplete, RequestSwitchCamera, + RequestToggleMinimap, PlayerIdle, PlayerRotate, PlayerBad, diff --git a/samples/golf/src/golf/OptionsEnum.inl b/samples/golf/src/golf/OptionsEnum.inl index 3f69cb32e..2538d960c 100644 --- a/samples/golf/src/golf/OptionsEnum.inl +++ b/samples/golf/src/golf/OptionsEnum.inl @@ -83,6 +83,7 @@ enum OptionsIndex CtrlInvY, CtrlVib, CtrlAltPower, + CtrlSwg, CtrlReset, CtrlDown, CtrlLeft, diff --git a/samples/golf/src/golf/OptionsState.cpp b/samples/golf/src/golf/OptionsState.cpp index 10c12278c..6c6cd4f43 100644 --- a/samples/golf/src/golf/OptionsState.cpp +++ b/samples/golf/src/golf/OptionsState.cpp @@ -2855,7 +2855,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& entity = createHighlight(Positions[Highlight::AimRight], InputBinding::Right); entity.getComponent().setSelectionIndex(CtrlRight); entity.getComponent().setNextIndex(CtrlA, CtrlDown); - entity.getComponent().setPrevIndex(CtrlReset, CtrlRight); + entity.getComponent().setPrevIndex(CtrlAltPower, CtrlRight); entity.getComponent().callbacks[cro::UIInput::Selected] = uiSystem.addCallback( [&,infoEnt, buttonChangeEnt](cro::Entity e) mutable { @@ -2904,7 +2904,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& entity = createHighlight(Positions[Highlight::LowerCam], InputBinding::Down); entity.getComponent().setSelectionIndex(CtrlDown); entity.getComponent().setNextIndex(CtrlA, TabAchievements); - entity.getComponent().setPrevIndex(CtrlReset, CtrlRight); + entity.getComponent().setPrevIndex(CtrlSwg, CtrlRight); entity.getComponent().callbacks[cro::UIInput::Selected] = uiSystem.addCallback( [&, infoEnt, buttonChangeEnt](cro::Entity e) mutable { @@ -3149,20 +3149,21 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& return e; }; - std::stringstream ss; + /*std::stringstream ss; ss.precision(2); - ss << "Swingput Threshold " << m_sharedData.swingputThreshold; - auto swingputText = createText(glm::vec2(20.f, 124.f), ss.str()); + ss << "Swingput Mouse Sensitivity " << m_sharedData.swingputThreshold; + auto swingputText = createText(glm::vec2(20.f, 124.f), ss.str());*/ std::stringstream st; st.precision(2); st << "Look Speed (Billiards) " << m_sharedData.mouseSpeed; - auto mouseText = createText(glm::vec2(20.f, 92.f), st.str()); - createText(glm::vec2(32.f, 63.f), "Invert X"); - createText(glm::vec2(32.f, 47.f), "Invert Y"); - createText(glm::vec2(118.f, 63.f), "Use Vibration"); - createText(glm::vec2(118.f, 47.f), "Hold For Power"); + auto mouseText = createText(glm::vec2(20.f, 124.f), st.str()); + createText(glm::vec2(32.f, 79.f), "Invert X"); + createText(glm::vec2(32.f, 63.f), "Invert Y"); + createText(glm::vec2(118.f, 79.f), "Use Vibration"); + createText(glm::vec2(118.f, 63.f), "Hold For Power"); + createText(glm::vec2(118.f, 47.f), "Enable Swingput"); //TODO don't duplicate these as they already exist in the AV menu auto selectedID = uiSystem.addCallback([infoEnt](cro::Entity e) mutable @@ -3235,7 +3236,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& return entity; }; - auto swingputSlider = createSlider({ 35.f, 109.f }); + /*auto swingputSlider = createSlider({ 35.f, 109.f }); swingputSlider.getComponent().getUserData().onActivate = [&, swingputText](float distance) mutable { @@ -3243,7 +3244,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& std::stringstream ss; ss.precision(2); - ss << "Swingput Threshold " << m_sharedData.swingputThreshold; + ss << "Swingput Mouse Sensitivity " << m_sharedData.swingputThreshold; swingputText.getComponent().setString(ss.str()); }; swingputSlider.getComponent().function = @@ -3253,10 +3254,10 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& float amount = (m_sharedData.swingputThreshold - ConstVal::MinSwingputThresh) / (ConstVal::MaxSwingputThresh - ConstVal::MinSwingputThresh); e.getComponent().setPosition({ pos.x + (width * amount), pos.y }); - }; + };*/ //mouse speed slider - createSlider(glm::vec2(35.f, 77.f)); + createSlider(glm::vec2(35.f, 109.f));//77 auto createSquareHighlight = [&](glm::vec2 pos) @@ -3283,88 +3284,92 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& }; //swingput down - entity = createSquareHighlight(glm::vec2(17.f, 103.f)); - entity.getComponent().setSelectionIndex(CtrlThreshL); - entity.getComponent().setNextIndex(CtrlThreshR, CtrlLookL); - entity.getComponent().setPrevIndex(CtrlLB, TabAV); - entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( - [&, swingputText](cro::Entity, cro::ButtonEvent evt) mutable - { - if (activated(evt)) - { - std::stringstream ss; - ss.precision(2); - ss << "Swingput Threshold " << m_sharedData.swingputThreshold; - swingputText.getComponent().setString(ss.str()); + //entity = createSquareHighlight(glm::vec2(17.f, 103.f)); + //entity.getComponent().setSelectionIndex(CtrlThreshL); + //entity.getComponent().setNextIndex(CtrlThreshR, CtrlLookL); + //entity.getComponent().setPrevIndex(CtrlLB, TabAV); + //entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( + // [&, swingputText](cro::Entity, cro::ButtonEvent evt) mutable + // { + // if (activated(evt)) + // { + // m_sharedData.swingputThreshold = std::max(ConstVal::MinSwingputThresh, m_sharedData.swingputThreshold - 0.1f); + + // std::stringstream ss; + // ss.precision(2); + // ss << "Swingput Mouse Sensitivity " << m_sharedData.swingputThreshold; + // swingputText.getComponent().setString(ss.str()); + + // m_audioEnts[AudioID::Accept].getComponent().play(); + // } + // }); + + ////swingput up + //entity = createSquareHighlight(glm::vec2(184.f, 103.f)); + //entity.getComponent().setSelectionIndex(CtrlThreshR); + //entity.getComponent().setNextIndex(CtrlRB, CtrlLookR); + //entity.getComponent().setPrevIndex(CtrlThreshL, TabAchievements); + //entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( + // [&, swingputText](cro::Entity, cro::ButtonEvent evt) mutable + // { + // if (activated(evt)) + // { + // m_sharedData.swingputThreshold = std::min(ConstVal::MaxSwingputThresh, m_sharedData.swingputThreshold + 0.1f); - m_sharedData.swingputThreshold = std::max(ConstVal::MinSwingputThresh, m_sharedData.swingputThreshold - 0.6f); - m_audioEnts[AudioID::Accept].getComponent().play(); - } - }); + // std::stringstream ss; + // ss.precision(2); + // ss << "Swingput Mouse Sensitivity " << m_sharedData.swingputThreshold; + // swingputText.getComponent().setString(ss.str()); - //swingput up - entity = createSquareHighlight(glm::vec2(184.f, 103.f)); - entity.getComponent().setSelectionIndex(CtrlThreshR); - entity.getComponent().setNextIndex(CtrlRB, CtrlLookR); - entity.getComponent().setPrevIndex(CtrlThreshL, TabAchievements); - entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( - [&, swingputText](cro::Entity, cro::ButtonEvent evt) mutable - { - if (activated(evt)) - { - std::stringstream ss; - ss.precision(2); - ss << "Swingput Threshold " << m_sharedData.swingputThreshold; - swingputText.getComponent().setString(ss.str()); - - m_sharedData.swingputThreshold = std::min(ConstVal::MaxSwingputThresh, m_sharedData.swingputThreshold + 0.6f); - m_audioEnts[AudioID::Back].getComponent().play(); - } - }); + // m_audioEnts[AudioID::Back].getComponent().play(); + // } + // }); //mouse speed down - entity = createSquareHighlight(glm::vec2(17.f, 71.f)); + entity = createSquareHighlight(glm::vec2(17.f, 103.f));//71 entity.getComponent().setSelectionIndex(CtrlLookL); entity.getComponent().setNextIndex(CtrlLookR, CtrlInvX); - entity.getComponent().setPrevIndex(CtrlLB, CtrlThreshL); + entity.getComponent().setPrevIndex(CtrlLB, /*CtrlThreshL*/TabAV); entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( [&, mouseText](cro::Entity, cro::ButtonEvent evt) mutable { if (activated(evt)) { + m_sharedData.mouseSpeed = std::max(ConstVal::MinMouseSpeed, m_sharedData.mouseSpeed - 0.1f); + std::stringstream st; st.precision(2); st << "Look Speed (Billiards) " << m_sharedData.mouseSpeed; mouseText.getComponent().setString(st.str()); - m_sharedData.mouseSpeed = std::max(ConstVal::MinMouseSpeed, m_sharedData.mouseSpeed - 0.1f); m_audioEnts[AudioID::Accept].getComponent().play(); } }); //mouse speed up - entity = createSquareHighlight(glm::vec2(184.f, 71.f)); + entity = createSquareHighlight(glm::vec2(184.f, 103.f));//71 entity.getComponent().setSelectionIndex(CtrlLookR); entity.getComponent().setNextIndex(CtrlUp, CtrlVib); - entity.getComponent().setPrevIndex(CtrlLookL, CtrlThreshR); + entity.getComponent().setPrevIndex(CtrlLookL, /*CtrlThreshR*/TabAchievements); entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( [&, mouseText](cro::Entity, cro::ButtonEvent evt) mutable { if (activated(evt)) { + m_sharedData.mouseSpeed = std::min(ConstVal::MaxMouseSpeed, m_sharedData.mouseSpeed + 0.1f); + std::stringstream st; st.precision(2); st << "Look Speed (Billiards) " << m_sharedData.mouseSpeed; mouseText.getComponent().setString(st.str()); - m_sharedData.mouseSpeed = std::min(ConstVal::MaxMouseSpeed, m_sharedData.mouseSpeed + 0.1f); m_audioEnts[AudioID::Back].getComponent().play(); } }); //invert X - entity = createSquareHighlight(glm::vec2(17.f, 54.f)); + entity = createSquareHighlight(glm::vec2(17.f, 70.f)); entity.setLabel("Invert the controller X axis when playing Billiards"); entity.getComponent().setSelectionIndex(CtrlInvX); entity.getComponent().setNextIndex(CtrlVib, CtrlInvY); @@ -3383,7 +3388,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& //centre entity = m_scene.createEntity(); - entity.addComponent().setPosition(glm::vec3(19.f, 56.f, HighlightOffset)); + entity.addComponent().setPosition(glm::vec3(19.f, 72.f, HighlightOffset)); entity.addComponent().getVertexData() = { cro::Vertex2D(glm::vec2(0.f, 7.f), TextGoldColour), @@ -3403,10 +3408,10 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& //rumble enable - entity = createSquareHighlight(glm::vec2(103.f, 54.f)); + entity = createSquareHighlight(glm::vec2(103.f, 70.f)); entity.setLabel("Enable or disable controller vibration"); entity.getComponent().setSelectionIndex(CtrlVib); - entity.getComponent().setNextIndex(CtrlLeft, CtrlAltPower); + entity.getComponent().setNextIndex(CtrlUp, CtrlAltPower); entity.getComponent().setPrevIndex(CtrlInvX, CtrlLookR); entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( [&](cro::Entity, cro::ButtonEvent evt) mutable @@ -3421,7 +3426,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& }); entity = m_scene.createEntity(); - entity.addComponent().setPosition(glm::vec3(105.f, 56.f, HighlightOffset)); + entity.addComponent().setPosition(glm::vec3(105.f, 72.f, HighlightOffset)); entity.addComponent().getVertexData() = { cro::Vertex2D(glm::vec2(0.f, 7.f), TextGoldColour), @@ -3441,10 +3446,10 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& //alt power input - entity = createSquareHighlight(glm::vec2(103.f, 38.f)); + entity = createSquareHighlight(glm::vec2(103.f, 54.f)); entity.setLabel("When enabled press and hold Action to select stroke power\nelse use the default 2-tap method when disabled"); entity.getComponent().setSelectionIndex(CtrlAltPower); - entity.getComponent().setNextIndex(CtrlLeft, CtrlReset); + entity.getComponent().setNextIndex(CtrlLeft, CtrlSwg); entity.getComponent().setPrevIndex(CtrlInvY, CtrlVib); entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( [&](cro::Entity, cro::ButtonEvent evt) mutable @@ -3459,7 +3464,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& }); //y'know if we defined these first we could capture them and update them in the button callback... entity = m_scene.createEntity(); - entity.addComponent().setPosition(glm::vec3(105.f, 40.f, HighlightOffset)); + entity.addComponent().setPosition(glm::vec3(105.f, 56.f, HighlightOffset)); entity.addComponent().getVertexData() = { cro::Vertex2D(glm::vec2(0.f, 7.f), TextGoldColour), @@ -3478,12 +3483,48 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& parent.getComponent().addChild(entity.getComponent()); + //swingput enable + entity = createSquareHighlight(glm::vec2(103.f, 38.f)); + entity.setLabel("With either trigger held, pull back on a thumbstick to charge the power.\nPush forward on the stick to make your shot. Timing is important!"); + entity.getComponent().setSelectionIndex(CtrlSwg); + entity.getComponent().setNextIndex(CtrlRight, WindowAdvanced); + entity.getComponent().setPrevIndex(CtrlReset, CtrlAltPower); + entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( + [&](cro::Entity, cro::ButtonEvent evt) mutable + { + if (activated(evt)) + { + m_sharedData.useSwingput = !m_sharedData.useSwingput; + m_audioEnts[AudioID::Back].getComponent().play(); + + m_scene.getActiveCamera().getComponent().active = true; + } + }); + entity = m_scene.createEntity(); + entity.addComponent().setPosition(glm::vec3(105.f, 40.f, HighlightOffset)); + entity.addComponent().getVertexData() = + { + cro::Vertex2D(glm::vec2(0.f, 7.f), TextGoldColour), + cro::Vertex2D(glm::vec2(0.f), TextGoldColour), + cro::Vertex2D(glm::vec2(7.f), TextGoldColour), + cro::Vertex2D(glm::vec2(7.f, 0.f), TextGoldColour) + }; + entity.getComponent().updateLocalBounds(); + entity.addComponent().active = true; + entity.getComponent().function = + [&](cro::Entity e, float) + { + float scale = m_sharedData.useSwingput ? 1.f : 0.f; + e.getComponent().setScale(glm::vec2(scale)); + }; + parent.getComponent().addChild(entity.getComponent()); + //invert Y - entity = createSquareHighlight(glm::vec2(17.f, 38.f)); + entity = createSquareHighlight(glm::vec2(17.f, 54.f)); entity.setLabel("Invert the controller Y axis when playing Billiards"); entity.getComponent().setSelectionIndex(CtrlInvY); - entity.getComponent().setNextIndex(CtrlRight, WindowAdvanced); + entity.getComponent().setNextIndex(CtrlAltPower, CtrlReset); entity.getComponent().setPrevIndex(CtrlB, CtrlInvX); entity.getComponent().callbacks[cro::UIInput::ButtonDown] = uiSystem.addCallback( [&](cro::Entity e, cro::ButtonEvent evt) mutable @@ -3498,7 +3539,7 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& }); entity = m_scene.createEntity(); - entity.addComponent().setPosition(glm::vec3(19.f, 40.f, HighlightOffset)); + entity.addComponent().setPosition(glm::vec3(19.f, 56.f, HighlightOffset)); entity.addComponent().getVertexData() = { cro::Vertex2D(glm::vec2(0.f, 7.f), TextGoldColour), @@ -3519,15 +3560,15 @@ void OptionsState::buildControlMenu(cro::Entity parent, const cro::SpriteSheet& //reset to defaults entity = m_scene.createEntity(); - entity.addComponent().setPosition(glm::vec3(83.f, 11.f, HighlightOffset)); + entity.addComponent().setPosition(glm::vec3(38.f, 11.f, HighlightOffset)); entity.addComponent() = m_menuSounds.getEmitter("switch"); entity.addComponent(); entity.addComponent() = spriteSheet.getSprite("small_highlight"); entity.getComponent().setColour(cro::Colour::Transparent); entity.addComponent().setGroup(MenuID::Controls); entity.getComponent().setSelectionIndex(CtrlReset); - entity.getComponent().setNextIndex(CtrlDown, WindowCredits); - entity.getComponent().setPrevIndex(CtrlA, CtrlVib); + entity.getComponent().setNextIndex(CtrlSwg, WindowCredits); + entity.getComponent().setPrevIndex(CtrlA, CtrlInvY); auto bounds = entity.getComponent().getTextureBounds(); entity.getComponent().area = bounds; entity.getComponent().callbacks[cro::UIInput::Selected] = uiSystem.addCallback( @@ -4088,7 +4129,7 @@ void OptionsState::createButtons(cro::Entity parent, std::int32_t menuID, std::u downLeftB = TabAV; downRightA = TabAchievements; downRightB = TabStats; - upLeftA = CtrlInvY; + upLeftA = CtrlReset; upLeftB = CtrlReset; upRightA = CtrlA; upRightB = CtrlA; diff --git a/samples/golf/src/golf/PlayerColours.hpp b/samples/golf/src/golf/PlayerColours.hpp index b2804585f..5d5243ab6 100644 --- a/samples/golf/src/golf/PlayerColours.hpp +++ b/samples/golf/src/golf/PlayerColours.hpp @@ -67,7 +67,7 @@ namespace pc cro::Colour(0xadd9b7ff), cro::Colour(0x6eb39dff), cro::Colour(0x30555bff), - cro::Colour(0x1a1e2dff), + cro::Colour(0x1a1e3fff), cro::Colour(0x284e43ff), cro::Colour(0x467e3eff), cro::Colour(0x93ab52ff), diff --git a/samples/golf/src/golf/ProfileState.cpp b/samples/golf/src/golf/ProfileState.cpp index af73136cd..a1baffdec 100644 --- a/samples/golf/src/golf/ProfileState.cpp +++ b/samples/golf/src/golf/ProfileState.cpp @@ -815,8 +815,22 @@ void ProfileState::buildScene() e.getComponent().setColour(cro::Colour::White); e.getComponent().active = true; m_audioEnts[AudioID::Accept].getComponent().play(); + + const auto& str = e.getLabel(); + if (str.empty()) + { + m_menuEntities[EntityID::TipText].getComponent().setString(" "); + } + else + { + m_menuEntities[EntityID::TipText].getComponent().setString(str); + } + }); + auto unselected = uiSystem.addCallback([&](cro::Entity e) + { + e.getComponent().setColour(cro::Colour::Transparent); + m_menuEntities[EntityID::TipText].getComponent().setString(" "); }); - auto unselected = uiSystem.addCallback([](cro::Entity e) {e.getComponent().setColour(cro::Colour::Transparent); }); const auto createButton = [&](const std::string& spriteID, glm::vec2 position, std::int32_t selectionIndex) { @@ -860,6 +874,7 @@ void ProfileState::buildScene() entity.getComponent().setNextIndex(ButtonHairBrowse, ButtonHairColour); entity.getComponent().setPrevIndex(ButtonName, ButtonRandomise); + entity.setLabel("Open Steam Workshop In Overlay"); #endif //colour swatch - this has its own update function @@ -1026,6 +1041,7 @@ void ProfileState::buildScene() //hat select button entity = m_uiScene.createEntity(); + entity.setLabel("Open Headwear Browser"); entity.addComponent().setPosition({163.f, 214.f, 0.5f}); entity.addComponent(); entity.addComponent() = spriteSheet.getSprite("hat_select"); @@ -1044,6 +1060,8 @@ void ProfileState::buildScene() { e.getComponent().setTextureRect(rect); m_audioEnts[AudioID::Select].getComponent().play(); + + m_menuEntities[EntityID::TipText].getComponent().setString(e.getLabel()); }); entity.getComponent().callbacks[cro::UIInput::ButtonUp] = uiSystem.addCallback([&](cro::Entity, const cro::ButtonEvent& evt) @@ -1061,8 +1079,8 @@ void ProfileState::buildScene() [](cro::Entity e) { auto bounds = e.getComponent().area; - bounds.bottom -= 8.f; - bounds.height += 16.f; + bounds.bottom -= 16.f; + bounds.height += 32.f; e.getComponent().area = bounds; }; @@ -1081,6 +1099,7 @@ void ProfileState::buildScene() }); hairLeft.getComponent().setNextIndex(ButtonHairBrowse, ButtonPrevBody); hairLeft.getComponent().setPrevIndex(ButtonHairColour, ButtonPrevBody); + hairLeft.setLabel("Previous Headwear"); expandHitbox(hairLeft); auto hairRight = createButton("arrow_right", glm::vec2(234.f, 156.f), ButtonNextHair); @@ -1096,6 +1115,7 @@ void ProfileState::buildScene() }); hairRight.getComponent().setNextIndex(ButtonPrevBall, ButtonNextBody); hairRight.getComponent().setPrevIndex(ButtonHairBrowse, ButtonNextBody); + hairRight.setLabel("Next Headwear"); expandHitbox(hairRight); auto avatarLeft = createButton("arrow_left", glm::vec2(87.f, 110.f), ButtonPrevBody); @@ -1110,6 +1130,7 @@ void ProfileState::buildScene() }); avatarLeft.getComponent().setNextIndex(ButtonNextBody, ButtonPrevHair); avatarLeft.getComponent().setPrevIndex(ButtonTopDark, ButtonPrevHair); + avatarLeft.setLabel("Previous Avatar"); expandHitbox(avatarLeft); auto avatarRight = createButton("arrow_right", glm::vec2(234.f, 110.f), ButtonNextBody); @@ -1124,9 +1145,10 @@ void ProfileState::buildScene() }); avatarRight.getComponent().setNextIndex(ButtonPrevBall, ButtonNextHair); avatarRight.getComponent().setPrevIndex(ButtonPrevBody, ButtonNextHair); + avatarRight.setLabel("Next Avatar"); expandHitbox(avatarRight); - //checkbox + //southpaw checkbox auto southPaw = createButton("check_highlight", glm::vec2(17.f, 50.f), ButtonSouthPaw); southPaw.getComponent().callbacks[cro::UIInput::ButtonUp] = uiSystem.addCallback([&](cro::Entity, const cro::ButtonEvent& evt) @@ -1139,6 +1161,8 @@ void ProfileState::buildScene() }); southPaw.getComponent().setNextIndex(ButtonSaveClose, ButtonRandomise); southPaw.getComponent().setPrevIndex(ButtonSaveClose, ButtonBottomLight); + southPaw.getComponent().area.width *= 7.f; + southPaw.setLabel("Use Left Handed Avatar"); auto innerEnt = m_uiScene.createEntity(); innerEnt.addComponent().setPosition(glm::vec3(19.f, 52.f, 0.1f)); @@ -1171,7 +1195,7 @@ void ProfileState::buildScene() if (activated(evt)) { //randomise hair - setHairIndex(cro::Util::Random::value(0u, m_sharedData.hairInfo.size() - 1)); + setHairIndex(cro::Util::Random::value(0u, m_avatarHairModels.size() - 1)); //randomise avatar setAvatarIndex(cro::Util::Random::value(0u, m_sharedData.avatarInfo.size() - 1)); @@ -1261,6 +1285,7 @@ void ProfileState::buildScene() }); ballLeft.getComponent().setNextIndex(ButtonBallSelect, ButtonBallColour); ballLeft.getComponent().setPrevIndex(ButtonNextHair, ButtonName); + ballLeft.setLabel("Previous Ball"); expandHitbox(ballLeft); //toggles the ball browser @@ -1281,6 +1306,7 @@ void ProfileState::buildScene() }); ballThumb.getComponent().setNextIndex(ButtonNextBall, ButtonBallColour); ballThumb.getComponent().setPrevIndex(ButtonPrevBall, ButtonName); + ballThumb.setLabel("Open Ball Browser"); auto ballRight = createButton("arrow_right", glm::vec2(382.f, 144.f), ButtonNextBall); ballRight.getComponent().callbacks[cro::UIInput::ButtonUp] = @@ -1295,6 +1321,7 @@ void ProfileState::buildScene() }); ballRight.getComponent().setNextIndex(ButtonDescUp, ButtonBallColourReset); ballRight.getComponent().setPrevIndex(ButtonBallSelect, ButtonName); + ballRight.setLabel("Next Ball"); expandHitbox(ballRight); @@ -1326,9 +1353,9 @@ void ProfileState::buildScene() m_audioEnts[AudioID::Accept].getComponent().play(); m_lastSelected = e.getComponent().getSelectionIndex(); - LogI << "Last selected set to " << m_lastSelected << std::endl; } }); + ballColour.setLabel("Choose Ball Tint"); entity = m_uiScene.createEntity(); entity.addComponent().setPosition({ 316.f, 78.f, 0.1f }); @@ -1371,6 +1398,7 @@ void ProfileState::buildScene() m_audioEnts[AudioID::Back].getComponent().play(); } }); + ballColourReset.setLabel("Reset Ball Tint"); //updates the profile icon @@ -1778,7 +1806,7 @@ void ProfileState::buildScene() //help string bounds = bgEnt.getComponent().getTextureBounds(); entity = m_uiScene.createEntity(); - entity.addComponent().setPosition({bounds.width / 2.f, 14.f, 0.1f}); + entity.addComponent().setPosition({/*bounds.width / 2.f*/164.f, 14.f, 0.1f}); entity.addComponent(); entity.addComponent(smallFont); entity.getComponent().setFillColour(TextNormalColour); @@ -1789,6 +1817,21 @@ void ProfileState::buildScene() m_menuEntities[EntityID::HelpText] = entity; + //button help string + entity = m_uiScene.createEntity(); + entity.addComponent().setPosition({bounds.width - 126.f, 14.f, 0.1f }); + entity.addComponent(); + entity.addComponent(smallFont); + entity.getComponent().setFillColour(TextNormalColour); + entity.getComponent().setShadowColour(LeaderboardTextDark); + entity.getComponent().setShadowOffset({ 1.f, -1.f }); + entity.getComponent().setCharacterSize(InfoTextSize); + entity.getComponent().setAlignment(cro::Text::Alignment::Centre); + //entity.getComponent().setString("This is some test text. Hello!"); + bgEnt.getComponent().addChild(entity.getComponent()); + m_menuEntities[EntityID::TipText] = entity; + + CallbackContext ctx; ctx.spriteSheet.loadFromFile("assets/golf/sprites/avatar_browser.spt", m_resources.textures); ctx.createArrow = diff --git a/samples/golf/src/golf/ProfileState.hpp b/samples/golf/src/golf/ProfileState.hpp index 2d0710d8b..12e02bca7 100644 --- a/samples/golf/src/golf/ProfileState.hpp +++ b/samples/golf/src/golf/ProfileState.hpp @@ -117,7 +117,7 @@ class ProfileState final : public cro::State, public cro::GuiClient Root, HelpText, Mugshot, NameText, Swatch, AvatarPreview, - BioText, + BioText, TipText, BallBrowser, HairBrowser, diff --git a/samples/golf/src/golf/SharedStateData.hpp b/samples/golf/src/golf/SharedStateData.hpp index 1700a6b1f..966635e33 100644 --- a/samples/golf/src/golf/SharedStateData.hpp +++ b/samples/golf/src/golf/SharedStateData.hpp @@ -241,7 +241,8 @@ struct SharedStateData final float fov = MinFOV; bool vertexSnap = false; float mouseSpeed = 1.f; - float swingputThreshold = 0.1f; + float swingputThreshold = 1.f; + bool useSwingput = false; bool invertX = false; bool invertY = false; bool showBeacon = true; diff --git a/samples/golf/src/golf/Swingput.cpp b/samples/golf/src/golf/Swingput.cpp index 4751fd909..e773f261e 100644 --- a/samples/golf/src/golf/Swingput.cpp +++ b/samples/golf/src/golf/Swingput.cpp @@ -1,6 +1,6 @@ /*----------------------------------------------------------------------- -Matt Marchant 2022 +Matt Marchant 2022 - 2024 http://trederia.blogspot.com Super Video Golf - zlib licence. @@ -28,8 +28,11 @@ source distribution. -----------------------------------------------------------------------*/ #include "Swingput.hpp" +#include "Clubs.hpp" +#include "InputBinding.hpp" #include "GameConsts.hpp" #include "SharedStateData.hpp" +#include "MessageIDs.hpp" #include #include @@ -46,29 +49,38 @@ namespace }debugOutput; #endif - constexpr float MaxControllerVelocity = 3000.f; - constexpr float MaxMouseVelocity = 1700.f; - constexpr float MaxAccuracy = 20.f; - //constexpr float CommitDistance = 4.f;// 0.01f; //TODO make this user variable - - constexpr float ControllerAxisRange = 32767.f; constexpr std::int16_t MinTriggerMove = 16000; - constexpr std::int16_t MinStickMove = 8000; + + constexpr float MaxMouseDraw = 20.f; + constexpr float MaxMouseSwing = -20.f; + constexpr float MaxMouseHook = 120.f; + + constexpr std::int16_t MaxControllerDraw = (std::numeric_limits::max() / 3) * 2; + constexpr std::int16_t MaxControllerSwing = -std::numeric_limits::max() / 2; + + //horrible hack to match up with InputParser::State + struct StateID final + { + enum + { + Aim, Power + }; + }; } Swingput::Swingput(const SharedStateData& sd) - : m_sharedData (sd), - m_enabled (false), - m_backPoint (0.f), - m_activePoint (0.f), - m_frontPoint (0.f), - m_power (0.f), - m_hook (0.f), - m_maxVelocity (1.f), - m_mouseScale (1.f), - m_lastLT (0), - m_lastRT (0), - m_elapsedTime (0.f) + : m_sharedData (sd), + m_enabled (-1), + m_mouseMovement (0), + m_hook (0.f), + m_gaugePosition (0.f), + m_lastLT (0), + m_lastRT (0), + m_strokeStartPosition (0), + m_activeStick (SDL_CONTROLLER_AXIS_INVALID), + m_lastAxisposition (0), + m_cancelTimer (0.f), + m_inCancelZone (false) { #ifdef CRO_DEBUG_ //registerWindow([&]() @@ -95,27 +107,25 @@ Swingput::Swingput(const SharedStateData& sd) } //public -bool Swingput::handleEvent(const cro::Event& evt) +bool Swingput::handleEvent(const cro::Event& evt, std::uint16_t& inputFlags, std::int32_t state) { - if (m_enabled == -1) + if (m_enabled == -1 + || !m_sharedData.useSwingput) { return false; } - const auto startStroke = [&](float maxVelocity) + const auto startStroke = [&]() { if (m_state == State::Inactive && (m_lastLT < MinTriggerMove) && (m_lastRT < MinTriggerMove)) { m_state = State::Swing; - m_backPoint = { 0.f, 0.f }; - m_activePoint = { 0.f, 0.f }; - m_frontPoint = { 0.f, 0.f }; - m_power = 0.f; - m_hook = 0.f; - - m_maxVelocity = maxVelocity; + m_mouseMovement = { 0.f,0.f }; + m_hook = 0.5f; + m_gaugePosition = 0.f; + m_strokeStartPosition = 0; cro::App::getWindow().setMouseCaptured(true); } @@ -124,20 +134,28 @@ bool Swingput::handleEvent(const cro::Event& evt) const auto endStroke = [&]() { m_state = State::Inactive; - m_activePoint = { 0.f, 0.f }; + m_activeStick = SDL_CONTROLLER_AXIS_INVALID; + + if (state == StateID::Power) + { + //cancel the shot + inputFlags |= (InputFlag::Cancel | InputFlag::Swingput); + } }; switch (evt.type) { default: return isActive(); case SDL_MOUSEBUTTONDOWN: + return false; //disable for now if (evt.button.button == SDL_BUTTON_RIGHT) { - startStroke(MaxMouseVelocity); + startStroke(); return true; } return false; case SDL_MOUSEBUTTONUP: + return false; //disable for now if (evt.button.button == SDL_BUTTON_RIGHT) { endStroke(); @@ -145,145 +163,205 @@ bool Swingput::handleEvent(const cro::Event& evt) } return false; case SDL_MOUSEMOTION: + return false; //TODO we need to scale this down relative to the game buffer size - switch (m_state) + if (m_state == State::Swing) { - default: break; - case State::Swing: - m_activePoint.x = std::clamp(m_activePoint.x + (static_cast(evt.motion.xrel) / 10.f/* / m_mouseScale*/), -MaxAccuracy, MaxAccuracy); - m_activePoint.y = std::clamp(m_activePoint.y - (static_cast(evt.motion.yrel)/* / m_mouseScale*/), -(MaxSwingputDistance / 2.f), MaxSwingputDistance / 2.f); - return true; + switch (state) + { + default: break; + case StateID::Aim: + if (evt.motion.yrel > 1) + { + //measure this then trigger action + //once a certain distance is met + m_mouseMovement.y += static_cast(evt.motion.yrel) * m_sharedData.swingputThreshold; + if (m_mouseMovement.y > MaxMouseDraw) + { + inputFlags |= (InputFlag::Action | InputFlag::Swingput); + } + + setGaugeFromMouse(); + return true; + } + break; + case StateID::Power: + if (evt.motion.yrel < -4) + { + //measure the distance as it's travelled + //as well as the X axis for accuracy + m_mouseMovement.x += static_cast(evt.motion.xrel); //hmm this is going to be dependent on screen resolution + m_mouseMovement.y += static_cast(evt.motion.yrel) * m_sharedData.swingputThreshold; + + if (m_mouseMovement.y < MaxMouseSwing) + { + inputFlags |= (InputFlag::Action | InputFlag::Swingput); + m_hook = std::clamp(m_mouseMovement.x / MaxMouseHook, -1.f, 1.f); + m_hook += 1.f; + m_hook /= 2.f; + + m_state = State::Inactive; + } + + setGaugeFromMouse(); + return true; + } + break; + } } return false; + case SDL_CONTROLLERBUTTONDOWN: + if (cro::GameController::controllerID(evt.cbutton.which) == activeControllerID(m_enabled)) + { + if (evt.cbutton.button == cro::GameController::ButtonB + && m_state == State::Swing) + { + endStroke(); + } + } + return false; //we allow either trigger or either stick //to aid handedness of players case SDL_CONTROLLERAXISMOTION: - //if (cro::GameController::controllerID(evt.caxis.which) == activeControllerID(m_enabled)) - //{ - // switch (evt.caxis.axis) - // { - // default: break; - // case SDL_CONTROLLER_AXIS_TRIGGERLEFT: - // case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: - // if (evt.caxis.value > MinTriggerMove) - // { - // startStroke(MaxControllerVelocity); - // } - // else - // { - // endStroke(); - // } - - // if (evt.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) - // { - // m_lastLT = evt.caxis.value; - // } - // else - // { - // m_lastRT = evt.caxis.value; - // } - - // return (evt.caxis.value > MinTriggerMove); - // case SDL_CONTROLLER_AXIS_LEFTY: - // case SDL_CONTROLLER_AXIS_RIGHTY: - - // if (std::abs(evt.caxis.value) > MinStickMove) - // { - // if (m_state == State::Swing) - // { - // m_activePoint.y = std::pow((static_cast(-evt.caxis.value) / ControllerAxisRange), 7.f) * (MaxSwingputDistance / 2.f); - // return true; - // } - // } - // return false; - // case SDL_CONTROLLER_AXIS_LEFTX: - // case SDL_CONTROLLER_AXIS_RIGHTX: - // //just set this and we'll have - // //whichever value was present when - // //the swing is finished - - // if (std::abs(evt.caxis.value) > MinStickMove - // && m_state == State::Swing) - // { - // m_activePoint.x = (static_cast(evt.caxis.value) / ControllerAxisRange) * MaxAccuracy; - // return true; - // } - // return false; - // } - //} + if (cro::GameController::controllerID(evt.caxis.which) == activeControllerID(m_enabled)) + { + switch (evt.caxis.axis) + { + default: break; + case SDL_CONTROLLER_AXIS_TRIGGERLEFT: + case SDL_CONTROLLER_AXIS_TRIGGERRIGHT: + if (evt.caxis.value > MinTriggerMove) + { + startStroke(); + } + else if (evt.caxis.value < 6000) //some arbitrary deadzone + { + endStroke(); + } + + if (evt.caxis.axis == SDL_CONTROLLER_AXIS_TRIGGERLEFT) + { + m_lastLT = evt.caxis.value; + } + else + { + m_lastRT = evt.caxis.value; + } + + return (evt.caxis.value > MinTriggerMove); + case SDL_CONTROLLER_AXIS_LEFTY: + case SDL_CONTROLLER_AXIS_RIGHTY: + if (m_state == State::Swing) + { + //one of the triggers is held + if (state == StateID::Aim) + { + //pulling back should create a button press - as this + //should more or less immediately change state to power + //we *shouldn't* get multiple presses. InputParser will + //reset these flags for us automatically. + if (evt.caxis.value > MaxControllerDraw) + { + inputFlags |= (InputFlag::Action | InputFlag::Swingput); + m_strokeStartPosition = evt.caxis.value; + m_activeStick = evt.caxis.axis; + } + + if (evt.caxis.value > cro::GameController::RightThumbDeadZone + || evt.caxis.value < -cro::GameController::RightThumbDeadZone) + { + setGaugeFromController(evt.caxis.value); + } + return true; + } + else if (state == StateID::Power) + { + //pushing forward rapidly should create a button press + //the swingput flag should tell the InputParser to skip + //the accuracy stage... + if (evt.caxis.value < MaxControllerSwing) + { + inputFlags |= (InputFlag::Action | InputFlag::Swingput); + m_state = State::Inactive; + + const float t = m_tempoTimer.restart(); + m_hook = 0.5f + ((0.033f - std::min(t, 0.066f)) / 3.f); + + const auto timing = std::floor(t * 1000.f); + if (timing == 33.f) + { + auto* msg = cro::App::postMessage(cl::MessageID::GolfMessage); + msg->type = GolfEvent::NiceTiming; + } + + auto x = cro::GameController::getAxisPosition(activeControllerID(m_enabled), + evt.caxis.axis == cro::GameController::AxisLeftY ? cro::GameController::AxisLeftX : cro::GameController::AxisRightX); + //higher level club sets require better accuracy + //https://www.desmos.com/calculator/u8hmy5q3mz + static constexpr std::array LevelMultipliers = { 19.f, 11.f, 7.f }; + const float xAmount = std::pow(std::clamp(static_cast(x) / 22000.f, -1.f, 1.f), LevelMultipliers[Club::getClubLevel()]); + + m_hook += (xAmount * 0.122f); + } + + //see if we started moving back after beginning the power mode + if (evt.caxis.value < m_strokeStartPosition) + { + m_tempoTimer.restart(); + + //prevent this triggering again + m_strokeStartPosition = std::numeric_limits::min(); + } + + + //check if this is the active axis and if it's in the cancel + //zone - ie the user let go of the stick before swinging + if (evt.caxis.axis == m_activeStick) + { + m_inCancelZone = (evt.caxis.value < cro::GameController::RightThumbDeadZone && evt.caxis.value > -cro::GameController::RightThumbDeadZone); + + //reset the timer if we entered the zone for the first time + if (m_inCancelZone && m_lastAxisposition > cro::GameController::RightThumbDeadZone) + { + //TODO do we need to check if it was below the deadzone? We should only be travelling in one dir + m_cancelTimer = 0.f; + } + m_lastAxisposition = evt.caxis.value; + setGaugeFromController(evt.caxis.value); + } + + return true; + } + } + + return false; + } + } return isActive(); } return isActive(); } -bool Swingput::process(float) +void Swingput::assertIdled(float dt, std::uint16_t& inputFlags, std::int32_t state) { - switch (m_state) - { - default: break; - case State::Swing: + if (state == StateID::Power + && m_activeStick != SDL_CONTROLLER_AXIS_INVALID) { - //moving down - if (m_activePoint.y < m_frontPoint.y) - { - m_backPoint = m_activePoint; - } - - //started moving back up so time the ascent - if (m_activePoint.y > m_backPoint.y - && m_frontPoint.y == m_backPoint.y) + if (m_inCancelZone) { - m_timer.restart(); + static constexpr float Timeout = 0.08f; + m_cancelTimer += dt; + + if (m_cancelTimer > Timeout) + { + inputFlags |= (InputFlag::Cancel | InputFlag::Swingput); + //m_state = State::Inactive; + m_activeStick = SDL_CONTROLLER_AXIS_INVALID; + } } - - //we've done full stroke. - if (m_activePoint.y > (MaxSwingputDistance / 2.f) - /*CommitDistance*/m_sharedData.swingputThreshold) - { - m_state = State::Summarise; - m_elapsedTime = m_timer.restart(); - } - - m_frontPoint = m_activePoint; - } - return false; - case State::Summarise: - m_state = State::Inactive; - -#ifdef CRO_DEBUG_ - debugOutput.distance = (m_frontPoint.y - m_backPoint.y); - debugOutput.velocity = debugOutput.distance / (m_elapsedTime + 0.0001f); //potential NaN - debugOutput.accuracy = (m_frontPoint.x - m_backPoint.x); - m_power = std::clamp(debugOutput.velocity / m_maxVelocity, 0.f, 1.f); - m_hook = std::clamp(debugOutput.accuracy / MaxAccuracy, -1.f, 1.f); - - //travelling a shorter distance doesn't imply lower - //velocity so we need to create a distance based modifier - float multiplier = debugOutput.distance / MaxSwingputDistance; - -#else - float distance = (m_frontPoint.y - m_backPoint.y); - float velocity = distance / (m_elapsedTime + 0.0001f); //potential NaN - float accuracy = (m_frontPoint.x - m_backPoint.x); - m_power = std::clamp(velocity / m_maxVelocity, 0.f, 1.f); - m_hook = std::clamp(accuracy / MaxAccuracy, -1.f, 1.f); - - //travelling a shorter distance doesn't imply lower - //velocity so we need to create a distance based modifier - float multiplier = distance / MaxSwingputDistance; -#endif - - //hmm have to double convert this because the input parser - //actually calcs it to 0-1 and converts back to -1 1 on return - m_hook += 1.f; - m_hook /= 2.f; - - m_power *= multiplier; - - return true; } - return false; } void Swingput::setEnabled(std::int32_t enabled) @@ -291,4 +369,29 @@ void Swingput::setEnabled(std::int32_t enabled) m_enabled = enabled; m_lastLT = 0; m_lastRT = 0; +} + +//private +void Swingput::setGaugeFromMouse() +{ + //gauge is +/- MaxSwingputDistance / 2 + //but also INVERTED from the actual values *sigh* + float norm = 0.f; + if (m_mouseMovement.y > 0) + { + //draw + norm = std::min(1.f, m_mouseMovement.y / MaxMouseDraw) * -1.f; + } + else + { + //swing + norm = std::min(1.f, m_mouseMovement.y / MaxMouseSwing); + } + m_gaugePosition = (MaxSwingputDistance / 2.f) * norm; +} + +void Swingput::setGaugeFromController(std::int16_t position) +{ + const float norm = (static_cast(position) / std::numeric_limits::max()) * -1.f; + m_gaugePosition = (MaxSwingputDistance / 2.f) * norm; } \ No newline at end of file diff --git a/samples/golf/src/golf/Swingput.hpp b/samples/golf/src/golf/Swingput.hpp index 11fa5fa19..385f34180 100644 --- a/samples/golf/src/golf/Swingput.hpp +++ b/samples/golf/src/golf/Swingput.hpp @@ -1,6 +1,6 @@ /*----------------------------------------------------------------------- -Matt Marchant 2022 +Matt Marchant 2022 - 2024 http://trederia.blogspot.com Super Video Golf - zlib licence. @@ -44,24 +44,19 @@ class Swingput final : public cro::GuiClient explicit Swingput(const SharedStateData&); //returns true if this consumed the event - bool handleEvent(const cro::Event&); - - //returns true if the shot was taken and result - //is ready in power/hook - bool process(float); - + bool handleEvent(const cro::Event&, std::uint16_t& flags, std::int32_t state); bool isActive() const { return m_state != State::Inactive; } + //checksto see if an active swing was cancelled by letting go of + //the thumbstick must be called by input parser at top of processing + void assertIdled(float, std::uint16_t& flags, std::int32_t state); + //set to -1 to deactivate, else current player ID void setEnabled(std::int32_t enabled); std::int32_t getEnabled() const { return m_enabled; } - float getPower() const { return m_power; } float getHook() const { return m_hook; } - - glm::vec2 getBackPoint() const { return m_backPoint; } - glm::vec2 getActivePoint() const { return m_activePoint; } - glm::vec2 getFrontPoint() const { return m_frontPoint; } + float getGaugePosition() const { return m_gaugePosition; } struct State final { @@ -76,24 +71,25 @@ class Swingput final : public cro::GuiClient }; std::int32_t getState() const { return m_state; } - void setMouseScale(float scale) { m_mouseScale = scale; } - private: const SharedStateData& m_sharedData; std::int32_t m_enabled; - glm::vec2 m_backPoint; - glm::vec2 m_activePoint; - glm::vec2 m_frontPoint; + glm::vec2 m_mouseMovement; + cro::HiResTimer m_tempoTimer; - float m_power; float m_hook; - - float m_maxVelocity; //depends on controller or mouse input - float m_mouseScale; + float m_gaugePosition; std::int16_t m_lastLT; std::int16_t m_lastRT; + std::int16_t m_strokeStartPosition; + + //used to track if we should cancel from letting go of the stick + std::int32_t m_activeStick; + std::int16_t m_lastAxisposition; + float m_cancelTimer; + bool m_inCancelZone; std::int32_t m_state = State::Inactive; const std::array StateStrings = @@ -101,6 +97,6 @@ class Swingput final : public cro::GuiClient "Inactive", "Swing", "Summarise" }; - cro::HiResTimer m_timer; - float m_elapsedTime; + void setGaugeFromMouse(); + void setGaugeFromController(std::int16_t); }; \ No newline at end of file diff --git a/samples/golf/src/golf/XPAwardStrings.hpp b/samples/golf/src/golf/XPAwardStrings.hpp index 5e0840904..811e13bb7 100644 --- a/samples/golf/src/golf/XPAwardStrings.hpp +++ b/samples/golf/src/golf/XPAwardStrings.hpp @@ -39,6 +39,7 @@ struct XPStringID final NiceOn, NearMiss, GreatAccuracy, + NiceTiming, OnTheFairway, DroneHit, CourseComplete, @@ -79,6 +80,7 @@ static inline const std::array XPStrings = std::string("Nice On!"), "Near Miss", "Great Accuracy", + "Nice Timing!", "Fairway", "Drone Hit!", "Course Complete",