mirror of
https://github.com/alliedmodders/hl2sdk.git
synced 2025-01-12 11:42:10 +08:00
354 lines
16 KiB
C++
354 lines
16 KiB
C++
//============ Copyright (c) Valve Corporation, All rights reserved. ============
|
|
//
|
|
// Declarations for the rule- and state-based level generation system.
|
|
//
|
|
//===============================================================================
|
|
|
|
#ifndef TILEGEN_LAYOUT_SYSTEM_H
|
|
#define TILEGEN_LAYOUT_SYSTEM_H
|
|
|
|
#if defined( COMPILER_MSVC )
|
|
#pragma once
|
|
#endif
|
|
|
|
#include "utlvector.h"
|
|
#include "vstdlib/random.h"
|
|
#include "tilegen_class_interfaces.h"
|
|
#include "tilegen_expressions.h"
|
|
#include "RoomTemplate.h"
|
|
|
|
class CMapLayout;
|
|
class CTilegenState;
|
|
class CLayoutSystem;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Adds all known tilegen listeners to the given layout system.
|
|
//-----------------------------------------------------------------------------
|
|
void AddListeners( CLayoutSystem *pLayoutSystem );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A list of states in a state layout system state machine.
|
|
// Each state may contain a nested state list.
|
|
// A state list is owned either by a state or by the root-level layout system.
|
|
//-----------------------------------------------------------------------------
|
|
class CTilegenStateList
|
|
{
|
|
public:
|
|
explicit CTilegenStateList( CLayoutSystem *pLayoutSystem = NULL ) : m_pOwnerState( NULL ), m_pLayoutSystem( pLayoutSystem ) { }
|
|
~CTilegenStateList() { m_States.PurgeAndDeleteElements(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Finds a state with the given name (either in this class or in one of
|
|
// its children). The search is depth-first.
|
|
// Returns NULL if not found.
|
|
//-----------------------------------------------------------------------------
|
|
CTilegenState *FindState( const char *pStateName );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the next state in the list after the specified state.
|
|
//-----------------------------------------------------------------------------
|
|
CTilegenState *GetNextState( CTilegenState *pState );
|
|
|
|
int GetStateCount() const { return m_States.Count(); }
|
|
CTilegenState *GetState( int i ) { return m_States[i]; }
|
|
|
|
void AddState( CTilegenState *pState ) { m_States.AddToTail( pState ); }
|
|
|
|
CLayoutSystem *GetLayoutSystem() { return m_pLayoutSystem; }
|
|
void SetLayoutSystem( CLayoutSystem *pLayoutSystem ) { m_pLayoutSystem = pLayoutSystem; }
|
|
CTilegenStateList *GetParentStateList();
|
|
void SetOwnerState( CTilegenState *pParent ) { m_pOwnerState = pParent; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called when beginning generation of a new map.
|
|
//-----------------------------------------------------------------------------
|
|
void OnBeginGeneration();
|
|
|
|
private:
|
|
// A list of contained states.
|
|
CUtlVector< CTilegenState * > m_States;
|
|
|
|
// This is NULL if the state list is owned by the layout system.
|
|
CTilegenState *m_pOwnerState;
|
|
|
|
CLayoutSystem *m_pLayoutSystem;
|
|
};
|
|
|
|
struct ActionConditionPair_t
|
|
{
|
|
ActionConditionPair_t( ITilegenAction *pAction, ITilegenExpression< bool > *pCondition ) :
|
|
m_pCondition( pCondition ),
|
|
m_pAction( pAction)
|
|
{
|
|
}
|
|
|
|
ITilegenAction *m_pAction;
|
|
ITilegenExpression< bool > *m_pCondition;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A node in the layout system state machine which contains a list of
|
|
// actions (and optional conditions) to execute in linear order.
|
|
// States may contain nested state lists.
|
|
//-----------------------------------------------------------------------------
|
|
class CTilegenState
|
|
{
|
|
public:
|
|
CTilegenState( CLayoutSystem *pLayoutSystem, CTilegenState *pParentState ) :
|
|
m_ChildStates( pLayoutSystem ),
|
|
m_pParentState( pParentState )
|
|
{
|
|
m_StateName[0] = '\0';
|
|
m_ChildStates.SetOwnerState( this );
|
|
}
|
|
|
|
~CTilegenState();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parses a state from KeyValues data.
|
|
//-----------------------------------------------------------------------------
|
|
bool LoadFromKeyValues( KeyValues *pKeyValues );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Executes a single iteration of logic in this state of the state machine.
|
|
//-----------------------------------------------------------------------------
|
|
void ExecuteIteration( CLayoutSystem *pLayoutSystem );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called when beginning generation of a new map.
|
|
//-----------------------------------------------------------------------------
|
|
void OnBeginGeneration( CLayoutSystem *pLayoutSystem );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Called when changing to this state.
|
|
//-----------------------------------------------------------------------------
|
|
void OnStateChanged( CLayoutSystem *pLayoutSystem );
|
|
|
|
void AddAction( ITilegenAction *pAction, ITilegenExpression< bool > *pCondition = NULL ) { m_Actions.AddToTail( ActionConditionPair_t( pAction, pCondition ) ); }
|
|
int GetActionCount() { return m_Actions.Count(); }
|
|
|
|
CTilegenStateList *GetStateList() { return &m_ChildStates; }
|
|
const char *GetStateName() const { return m_StateName; }
|
|
CTilegenState *GetNextState() { return m_ChildStates.GetParentStateList()->GetNextState( this ); }
|
|
|
|
CTilegenState *GetParentState() const { return m_pParentState; }
|
|
CLayoutSystem *GetLayoutSystem() { return m_ChildStates.GetLayoutSystem(); }
|
|
|
|
private:
|
|
bool ParseAction( KeyValues *pKeyValues );
|
|
|
|
// A list of conditional actions in execution-order (e.g. m_Actions[0] runs first)
|
|
CUtlVector< ActionConditionPair_t > m_Actions;
|
|
|
|
CTilegenStateList m_ChildStates;
|
|
|
|
// The parent state, or NULL if this is owned directly by the layout system.
|
|
CTilegenState *m_pParentState;
|
|
|
|
char m_StateName[MAX_TILEGEN_IDENTIFIER_LENGTH];
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// A state machine which iteratively executes actions in states
|
|
// in order to populate a map layout (CMapLayout).
|
|
//-----------------------------------------------------------------------------
|
|
class CLayoutSystem
|
|
{
|
|
public:
|
|
CLayoutSystem();
|
|
~CLayoutSystem();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Parses a full state machine from KeyValues data.
|
|
//-----------------------------------------------------------------------------
|
|
bool LoadFromKeyValues( KeyValues *pKeyValues );
|
|
|
|
CMapLayout *GetMapLayout() { return m_pMapLayout; }
|
|
const CMapLayout *GetMapLayout() const { return m_pMapLayout; }
|
|
CUtlVector< CRoomCandidate > *GetRoomCandidateList() const { return m_CurrentIterationState.m_pRoomCandidateList; }
|
|
CTilegenStateList *GetStateList() { return &m_States; }
|
|
CTilegenState *GetCurrentState() const { return m_pCurrentState; }
|
|
CTilegenState *GetGlobalActionState() const { return m_pGlobalActionState; }
|
|
|
|
void AddListener( ITilegenListener *pListener ) { m_TilegenListeners.AddToTail( pListener ); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets a list of currently open (un-matched) exits.
|
|
// This pointer should never be cached; map layout changes may invalidate it.
|
|
//-----------------------------------------------------------------------------
|
|
const CExit * GetOpenExits() const { return m_OpenExits.Base(); }
|
|
int GetNumOpenExits() const { return m_OpenExits.Count(); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the dictionary of variable values.
|
|
//-----------------------------------------------------------------------------
|
|
CFreeVariableMap *GetFreeVariables() { return &m_FreeVariables; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Instructs the system to no longer process further actions this iteration.
|
|
//-----------------------------------------------------------------------------
|
|
void StopProcessingActions() { m_CurrentIterationState.m_bStopIteration = true; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets a value indicating whether to continue processing further
|
|
// actions this iteration.
|
|
//-----------------------------------------------------------------------------
|
|
bool ShouldStopProcessingActions() const { return m_CurrentIterationState.m_bStopIteration || m_bLayoutError; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets a value indicating whether the layout system is in the process of
|
|
// generating a level or not.
|
|
//-----------------------------------------------------------------------------
|
|
bool IsGenerating() const { return m_bGenerating; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets a value indicating whether an error occurred during the layout process.
|
|
//-----------------------------------------------------------------------------
|
|
bool GenerationErrorOccurred() const { return m_bLayoutError; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets a value indicating whether this map is randomly generated or
|
|
// fixed to a particular seed.
|
|
// A seed of 0 implies that a new seed is chosen for each generation while
|
|
// a non-zero seed implies that the same seed should be chosen each time.
|
|
//-----------------------------------------------------------------------------
|
|
bool IsRandomlyGenerated() const { return m_nRandomSeed == 0; }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Attempts to place a room in the map layout.
|
|
//
|
|
// Returns true on success, false on failure.
|
|
//
|
|
// Reasons for failure include: room out of bounds, room overlapping
|
|
// with existing room, room exit mismatch.
|
|
//-----------------------------------------------------------------------------
|
|
bool TryPlaceRoom( const CRoomCandidate *pRoomCandidate );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Generates a random integer value X in the range [nMin, nMax], inclusive.
|
|
//
|
|
// All actions or rules which require randomness must use CLayoutSystem
|
|
// random functions so that "randomness" is reproducible from a given seed.
|
|
//-----------------------------------------------------------------------------
|
|
int GetRandomInt( int nMin, int nMax ) { return m_Random.RandomInt( nMin, nMax ); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Generates a random float value X in the range [nMin, nMax], inclusive.
|
|
//
|
|
// All actions or rules which require randomness must use CLayoutSystem
|
|
// random functions so that "randomness" is reproducible from a given seed.
|
|
//-----------------------------------------------------------------------------
|
|
float GetRandomFloat( float flMin, float flMax ) { return m_Random.RandomFloat( flMin, flMax ); }
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Changes the current state to the state with the given name and
|
|
// stops processing further actions.
|
|
//-----------------------------------------------------------------------------
|
|
void TransitionToState( const char *pStateName );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Changes the current state to the specified state object and stops
|
|
// processing further actions.
|
|
//-----------------------------------------------------------------------------
|
|
void TransitionToState( CTilegenState *pState );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Notifies the system taht an error occurred during level generation
|
|
// to signal a failure and stop further processing.
|
|
//-----------------------------------------------------------------------------
|
|
void OnError();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Notifies the system that level generation is complete and stops
|
|
// further processing.
|
|
//-----------------------------------------------------------------------------
|
|
void OnFinished();
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Executes the specified action with a given condition.
|
|
// If pCondition is NULL, the action is always executed.
|
|
//-----------------------------------------------------------------------------
|
|
void ExecuteAction( ITilegenAction *pAction, ITilegenExpression< bool > *pCondition );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Resets the layout system and begins generating a new map.
|
|
// Once this is called, the system must be ticked by calling ExecuteIteration
|
|
// until IsGeneration() returns false.
|
|
//-----------------------------------------------------------------------------
|
|
void BeginGeneration( CMapLayout *pMapLayout );
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Executes a single pass through the actions in the global & current state.
|
|
// Executing an iteration may have a number of side-effects on the layout
|
|
// system such as placing rooms, setting variables, changing states, etc.
|
|
//-----------------------------------------------------------------------------
|
|
void ExecuteIteration();
|
|
|
|
private:
|
|
void OnActionExecuted( const ITilegenAction *pAction );
|
|
void OnRoomPlaced();
|
|
void OnStateChanged( const CTilegenState *pOldState );
|
|
|
|
void AddOpenExitsFromRoom( CRoom *pRoom );
|
|
void AddOpenExit( CRoom *pSourceRoom, int nX, int nY, ExitDirection_t exitDirection, const char *pExitTag, bool bChokepointGrowSource );
|
|
|
|
// A random number generator which must be used by any actions/conditions which need randomness.
|
|
CUniformRandomStream m_Random;
|
|
// Starting random seed of the map. If set to 0, pick a completely arbitrary one using the global random generator.
|
|
int m_nRandomSeed;
|
|
|
|
// A set of top-level states.
|
|
CTilegenStateList m_States;
|
|
|
|
// A special/sentinel state which contains global actions that get executed on every iteration.
|
|
CTilegenState *m_pGlobalActionState;
|
|
|
|
// Current state in the state machine.
|
|
// Points to a member of m_States or a nested state contained within.
|
|
CTilegenState *m_pCurrentState;
|
|
|
|
// Exits which are currently open.
|
|
CUtlVector< CExit > m_OpenExits;
|
|
|
|
// Stores a name-value map of data which can be accessed by expressions
|
|
CFreeVariableMap m_FreeVariables;
|
|
|
|
// A list of objects listening for tilegen events which can affect free variable state.
|
|
CUtlVector< ITilegenListener * > m_TilegenListeners;
|
|
|
|
// The map layout to be built over time (not owned by this class)
|
|
CMapLayout *m_pMapLayout;
|
|
|
|
struct ActionData_t
|
|
{
|
|
int m_nNumTimesExecuted;
|
|
};
|
|
|
|
CUtlMap< ITilegenAction *, ActionData_t > m_ActionData;
|
|
|
|
bool m_bLayoutError;
|
|
bool m_bGenerating;
|
|
|
|
// Number of iterations since beginning level generation.
|
|
int m_nIterations;
|
|
|
|
// State for the current iteration
|
|
struct CurrentIterationState_t
|
|
{
|
|
CurrentIterationState_t() :
|
|
m_bStopIteration( false ),
|
|
m_pRoomCandidateList( NULL )
|
|
{
|
|
}
|
|
|
|
// True if iteration should stop after execution of the current rule, false otherwise.
|
|
bool m_bStopIteration;
|
|
|
|
// A list of all valid combinations of room placement.
|
|
// This is essentially the outer product of the set of all possible room
|
|
// choices and the set of all open exits, filtered down to valid combinations.
|
|
CUtlVector< CRoomCandidate > *m_pRoomCandidateList;
|
|
} m_CurrentIterationState;
|
|
};
|
|
|
|
#endif // TILEGEN_LAYOUT_SYSTEM_H
|