/* * Copyright (c) Contributors to the Open 3D Engine Project. * For complete copyright and license terms please see the LICENSE at the root of this distribution. * * SPDX-License-Identifier: Apache-2.0 OR MIT * */ #pragma once #include #include #include //////////////////////////////////////////////////////////////////////////////////////////////////// namespace GameState { //////////////////////////////////////////////////////////////////////////////////////////////// //! Alias for verbose function pointer type using GameStateFactory = AZStd::function()>; //////////////////////////////////////////////////////////////////////////////////////////////// //! EBus interface used to submit requests related to game state management class GameStateRequests : public AZ::EBusTraits { public: //////////////////////////////////////////////////////////////////////////////////////////// //! EBus Trait: requests can only be sent to and addressed by a single instance (singleton) ///@{ static const AZ::EBusAddressPolicy AddressPolicy = AZ::EBusAddressPolicy::Single; static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; ///@} //////////////////////////////////////////////////////////////////////////////////////////// //! Create a new game state //! \tparam GameStateType The game state type to create //! \param[in] checkForOverrides True if we should check for an override, false otherwise //! \return A shared pointer to the new game state that was created template static AZStd::shared_ptr CreateNewOverridableGameStateOfType(bool checkForOverride = true); //////////////////////////////////////////////////////////////////////////////////////////// //! Create a new game state and push it onto the stack to make it the active game state. //! New game states are created and stored in the stack using a shared_ptr, so they will //! be destroyed automatically once they are popped off the stack (assuming that nothing //! else retains a reference, say via GameStateNotifications::OnActiveGameStateChanged). //! \tparam GameStateType The game state type to create and activate //! \param[in] checkForOverrides True if we should check for an override, false otherwise template static void CreateAndPushNewOverridableGameStateOfType(bool checkForOverride = true); //////////////////////////////////////////////////////////////////////////////////////////// //! Pop game states from the stack until the active game state is of the specified type. If //! no game state of the specified type exists in the game state stack it will be left empty. //! \tparam GameStateType The game state type in the stack that we want to be active //! \return True if the active game state is now of the specified type, false otherwise template static bool PopActiveGameStateUntilOfType(); //////////////////////////////////////////////////////////////////////////////////////////// //! Query whether the active game state is of the specified type //! \tparam GameStateType The game state type to check whether is active //! \return True if the active game state is of the specified type, false otherwise template static bool IsActiveGameStateOfType(); //////////////////////////////////////////////////////////////////////////////////////////// //! Query whether the game state stack contains a game state of the specified type //! \tparam GameStateType The game state type to check whether is in the stack //! \return True if the stack contains a game state of the specified type, false otherwise template static bool DoesStackContainGameStateOfType(); //////////////////////////////////////////////////////////////////////////////////////////// //! Add a game state override so that a request to push a new game state of a certain type //! will result in pushing a new game state of a different type instead. This is useful for //! situations where we want to use a set of default game states but override some (or all) //! of them with custom versions which satisfy the requirements of a specific game project. //! \tparam GameStateType The original game state type to be overridden //! \param[in] factory The factory function that will create the game state override //! \return True if the game state override was successfully added, false otherwise template static bool AddGameStateFactoryOverrideForType(GameStateFactory factory); //////////////////////////////////////////////////////////////////////////////////////////// //! Remove a game state override that was added using AddGameStateFactoryOverrideForType. //! \tparam GameStateType The original game state type that was overridden //! \return True if the game state override was successfully removed, false otherwise template static bool RemoveGameStateFactoryOverrideForType(); //////////////////////////////////////////////////////////////////////////////////////////// //! Retrieve a game state override that was added using AddGameStateFactoryOverrideForType. //! \tparam GameStateType The original game state type that was overridden //! \return The factory function used to create the game state override, nullptr otherwise template static GameStateFactory GetGameStateFactoryOverrideForType(); //////////////////////////////////////////////////////////////////////////////////////////// //! Update the active game state. This is called during the AZ::ComponentTickBus::TICK_GAME //! priority update of the AZ::TickBus, but can be called independently any time if needed. virtual void UpdateActiveGameState() = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Request the active game state (if any) //! \return A shared pointer to the active game state (will be empty if there is none) virtual AZStd::shared_ptr GetActiveGameState() = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Push a game state onto the stack, which will result in it becoming the active game state. //! If newGameState is already found in the stack this will fail and return false, however //! it is possible for multiple instances of the same game state type to occupy the stack. //! \param[in] newGameState The new game state to push onto the stack //! \return True if the game state was successfully pushed onto the stack, false otherwise virtual bool PushGameState(AZStd::shared_ptr newGameState) = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Pop the active game state from the stack, which will result in it being deactivated and //! the game state below it in the stack (if any) becoming the active game state again. //! \return True if the active game state was successfully popped, false otherwise virtual bool PopActiveGameState() = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Pop all game states from the stack, leaving it empty. virtual void PopAllGameStates() = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Replace the active game state with another game state that will become the active state. //! If the stack is currently empty, newGameState will be pushed to become the active state. //! If newGameState is already found in the stack this will fail and return false, however //! it is possible for multiple instances of the same game state type to occupy the stack. //! This differs from calling PopActiveGameState followed by PushGameState(newGameState), //! which would result in the state below the currently active state being activated then //! immediately deactivated when newGameState is pushed onto the stack; calling this will //! the state below the currently active state unchanged. //! \param[in] newGameState The new game state with which to replace the active game state //! \return True if the active game state was successfully replaced, false otherwise virtual bool ReplaceActiveGameState(AZStd::shared_ptr newGameState) = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Query whether the game state stack contains a game state of the specified type //! \param[in] gameStateTypeId The game state type to check whether is in the stack //! \return True if the stack contains a game state of the specified type, false otherwise virtual bool DoesStackContainGameStateOfTypeId(const AZ::TypeId& gameStateTypeId) = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Add a game state override so that a request to push a new game state of a certain type //! will result in pushing a new game state of a derived type instead. This is useful for //! situations where we want to use a set of default game states but override some (or all) //! of them with custom versions which satisfy the requirements of a specific game project. //! \param[in] gameStateTypeId The original game state type id to be overridden //! \param[in] factory The factory function that will create the game state override //! \return True if the game state override was successfully added, false otherwise virtual bool AddGameStateFactoryOverrideForTypeId(const AZ::TypeId& gameStateTypeId, GameStateFactory factory) = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Remove a game state override that was added using AddGameStateFactoryOverrideForTypeId. //! \param[in] gameStateTypeId The original game state type id that was overridden //! \return True if the game state override was successfully removed, false otherwise virtual bool RemoveGameStateFactoryOverrideForTypeId(const AZ::TypeId& gameStateTypeId) = 0; //////////////////////////////////////////////////////////////////////////////////////////// //! Retrieve a game state override that was added using AddGameStateFactoryOverrideForTypeId. //! \param[in] gameStateTypeId The original game state type id that was overridden //! \return The factory function used to create the game state override, nullptr otherwise virtual GameStateFactory GetGameStateFactoryOverrideForTypeId(const AZ::TypeId& gameStateTypeId) = 0; }; using GameStateRequestBus = AZ::EBus; //////////////////////////////////////////////////////////////////////////////////////////////// template inline AZStd::shared_ptr GameStateRequests::CreateNewOverridableGameStateOfType(bool checkForOverride) { AZStd::shared_ptr newGameState; if (checkForOverride) { auto factoryFunction = GetGameStateFactoryOverrideForType(); if (factoryFunction) { newGameState = factoryFunction(); if (!azrtti_istypeof(newGameState.get())) { AZ_Warning("GameStateSystemComponent", false, "Trying to override a game state type with one that doesn't derive from it."); newGameState.reset(); } } } return newGameState ? newGameState : AZStd::make_shared(); } //////////////////////////////////////////////////////////////////////////////////////////////// template inline void GameStateRequests::CreateAndPushNewOverridableGameStateOfType(bool checkForOverride) { AZStd::shared_ptr newGameState = CreateNewOverridableGameStateOfType(checkForOverride); AZ_Assert(newGameState, "Failed to create new game state"); bool result = false; GameStateRequestBus::BroadcastResult(result, &GameStateRequests::PushGameState, newGameState); AZ_Assert(result, "Failed to push new game state"); } //////////////////////////////////////////////////////////////////////////////////////////////// template inline bool GameStateRequests::PopActiveGameStateUntilOfType() { AZStd::shared_ptr activeGameState; GameStateRequestBus::BroadcastResult(activeGameState, &GameStateRequests::GetActiveGameState); while (activeGameState && !azrtti_istypeof(activeGameState.get())) { GameStateRequestBus::Broadcast(&GameStateRequests::PopActiveGameState); GameStateRequestBus::BroadcastResult(activeGameState, &GameStateRequests::GetActiveGameState); } return activeGameState != nullptr; } //////////////////////////////////////////////////////////////////////////////////////////////// template inline bool GameStateRequests::IsActiveGameStateOfType() { AZStd::shared_ptr activeGameState; GameStateRequestBus::BroadcastResult(activeGameState, &GameStateRequests::GetActiveGameState); return activeGameState ? azrtti_istypeof(activeGameState.get()) : false; } //////////////////////////////////////////////////////////////////////////////////////////////// template inline bool GameStateRequests::DoesStackContainGameStateOfType() { bool doesStackContainGameStateOfType = false; GameStateRequestBus::BroadcastResult(doesStackContainGameStateOfType, &GameStateRequests::DoesStackContainGameStateOfTypeId, azrtti_typeid()); return doesStackContainGameStateOfType; } //////////////////////////////////////////////////////////////////////////////////////////////// template inline bool GameStateRequests::AddGameStateFactoryOverrideForType(GameStateFactory factory) { bool overrideAdded = false; GameStateRequestBus::BroadcastResult(overrideAdded, &GameStateRequests::AddGameStateFactoryOverrideForTypeId, azrtti_typeid(), factory); return overrideAdded; } //////////////////////////////////////////////////////////////////////////////////////////////// template inline bool GameStateRequests::RemoveGameStateFactoryOverrideForType() { bool overrideRemoved = false; GameStateRequestBus::BroadcastResult(overrideRemoved, &GameStateRequests::RemoveGameStateFactoryOverrideForTypeId, azrtti_typeid()); return overrideRemoved; } //////////////////////////////////////////////////////////////////////////////////////////////// template inline GameStateFactory GameStateRequests::GetGameStateFactoryOverrideForType() { GameStateFactory overrideFactoryFunction; GameStateRequestBus::BroadcastResult(overrideFactoryFunction, &GameStateRequests::GetGameStateFactoryOverrideForTypeId, azrtti_typeid()); return overrideFactoryFunction; } } // namespace GameState