/*
** Command & Conquer Renegade(tm)
** Copyright 2025 Electronic Arts Inc.
**
** This program is free software: you can redistribute it and/or modify
** it under the terms of the GNU General Public License as published by
** the Free Software Foundation, either version 3 of the License, or
** (at your option) any later version.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
** GNU General Public License for more details.
**
** You should have received a copy of the GNU General Public License
** along with this program. If not, see .
*/
/***********************************************************************************************
*** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
***********************************************************************************************
* *
* Project Name : combat *
* *
* $Archive:: /Commando/Code/Combat/statemachine.h $*
* *
* Author:: Patrick Smith *
* *
* $Modtime:: 11/07/01 5:27p $*
* *
* $Revision:: 4 $*
* *
*---------------------------------------------------------------------------------------------*
* Functions: *
* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if defined(_MSC_VER)
#pragma once
#endif
#ifndef __STATEMACHINE_H
#define __STATEMACHINE_H
#include "simplevec.h"
#include "chunkio.h"
//////////////////////////////////////////////////////////////////////
// Macros
//////////////////////////////////////////////////////////////////////
//#define ADD_STATE_TO_MACHINE(machine, state) \
//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);
/*#define ADD_STATE_TO_MACHINE(machine, state, is_think, is_request_end, is_begin, is_end) \
machine.Add_State ( \
is_think ? On_##state##_Think : NULL, \
is_request_end ? On_##state##_Request_End : NULL, \
is_begin ? On_##state##_Begin : NULL, \
is_end ? On_##state##_End : NULL); */
#define ADD_STATE_TO_MACHINE(machine, state) \
machine.Add_State ( \
On_##state##_Think, \
On_##state##_Request_End, \
On_##state##_Begin, \
On_##state##_End);
//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);
/*#define BEGIN_STATE_MAP(machine) \
void Install_##machine_States (void) { \
#define ADD_STATE_HANDLER(machine, state) \
machine.Add_State (state); \
machine.
#define END_STATE_MAP() \
}*/
//#define ADD_STATE_TO_MACHINE(machine, state) \
//machine.Add_State (On_##state_Think, On_##state_Request_End, On_##state_Begin, On_##state_End);
/*#define DECLARE_STATE_HANDLER(state) \
void On_##state##_Begin (void); \
void On_##state##_End (void); \
void On_##state##_Think (void); \
bool On_##state##_Request_End (int new_state);*/
#define SM_BEGIN ;
#define SM_END ;
#define SM_THINK ;
#define SM_REQ_END ;
#define SM_NO_BEGIN {}
#define SM_NO_END {}
#define SM_NO_THINK {}
#define SM_NO_REQ_END { return true; }
#define DECLARE_STATE(state, begin, end, think, req_end) \
void On_##state##_Begin (void) begin \
void On_##state##_End (void) end \
void On_##state##_Think (void) think \
bool On_##state##_Request_End (int state) req_end
#define DECLARE_STATE_HANDLER_BEGIN(state) \
void On_##state##_Begin (void)
#define DECLARE_STATE_HANDLER_END(state) \
void On_##state##_End (void)
#define DECLARE_STATE_HANDLER_THINK(state) \
void On_##state##_Think (void)
#define DECLARE_STATE_HANDLER_REQUEST_END(state) \
bool On_##state##_Request_End (int state)
#define STATE_IMPL_BEGIN(state) \
On_##state##_Begin
#define STATE_IMPL_END(state) \
On_##state##_End
#define STATE_IMPL_THINK(state) \
On_##state##_Think
#define STATE_IMPL_REQUEST_END(state) \
On_##state##_Request_End
/*#define DECLARE_STATE_HANDLER_BEGIN(state) \
void On_##state_Begin (void);
#define DECLARE_STATE_HANDLER_END(state) \
void On_##state_End (void);
#define DECLARE_STATE_HANDLER_THINK(state) \
void On_##state_Think (void);
#define DECLARE_STATE_HANDLER_REQUEST_END(state) \
void On_##state_Request_End (void);
*/
//////////////////////////////////////////////////////////////////////
//
// StateClass
//
//////////////////////////////////////////////////////////////////////
template
class StateClass
{
public:
///////////////////////////////////////////////////////////////////
// Public constructors/destructors
///////////////////////////////////////////////////////////////////
StateClass (void) :
Think (NULL),
RequestEnd (NULL),
Begin (NULL),
End (NULL) {}
///////////////////////////////////////////////////////////////////
// Public typedefs
///////////////////////////////////////////////////////////////////
typedef void (T::*THINK_PTR) (void);
typedef bool (T::*REQUEST_END_PTR) (int new_state);
typedef void (T::*BEGIN_PTR) (void);
typedef void (T::*END_PTR) (void);
///////////////////////////////////////////////////////////////////
// Public member data
///////////////////////////////////////////////////////////////////
THINK_PTR Think;
REQUEST_END_PTR RequestEnd;
BEGIN_PTR Begin;
END_PTR End;
};
//////////////////////////////////////////////////////////////////////
//
// StateMachineClass
//
//////////////////////////////////////////////////////////////////////
template
class StateMachineClass
{
public:
///////////////////////////////////////////////////////////////////
// Public typedefs
///////////////////////////////////////////////////////////////////
typedef StateClass STATE_OBJ;
///////////////////////////////////////////////////////////////////
// Save/load constants
///////////////////////////////////////////////////////////////////
enum
{
CHUNKID_VARIABLES = 0x11070946,
VARID_CURR_STATE = 0,
VARID_IS_HALTED,
};
///////////////////////////////////////////////////////////////////
// Public constructors/destructors
///////////////////////////////////////////////////////////////////
StateMachineClass (T *object) :
CurrState (-1),
Object (object),
IsHalted (false) {}
virtual ~StateMachineClass (void) {}
///////////////////////////////////////////////////////////////////
// Public methods
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// Think
///////////////////////////////////////////////////////////////////
void Think (void)
{
if (IsHalted) {
return ;
}
//
// Call the think member function of the container
//
if (Is_Valid_State (CurrState)) {
if (StateTable[CurrState].Think != NULL) {
(Object->*(StateTable[CurrState].Think)) ();
}
}
return ;
}
///////////////////////////////////////////////////////////////////
// Add_State
///////////////////////////////////////////////////////////////////
void Add_State (StateClass &state)
{
StateTable.Add (state);
return ;
}
///////////////////////////////////////////////////////////////////
// Add_State
///////////////////////////////////////////////////////////////////
void Add_State
(
STATE_OBJ::THINK_PTR think_ptr,
STATE_OBJ::REQUEST_END_PTR request_ptr,
STATE_OBJ::BEGIN_PTR begin_ptr,
STATE_OBJ::END_PTR end_ptr
)
{
StateClass state;
state.Think = think_ptr;
state.RequestEnd = request_ptr;
state.Begin = begin_ptr;
state.End = end_ptr;
Add_State (state);
return ;
}
///////////////////////////////////////////////////////////////////
// Halt_State
///////////////////////////////////////////////////////////////////
void Halt_State (void)
{
if (IsHalted) {
return ;
}
//
// Notify the current state that it's "ending"
//
if (Is_Valid_State (CurrState)) {
if (StateTable[CurrState].End != NULL) {
(Object->*(StateTable[CurrState].End)) ();
}
}
IsHalted = true;
return ;
}
///////////////////////////////////////////////////////////////////
// Resume_State
///////////////////////////////////////////////////////////////////
void Resume_State (void)
{
if (IsHalted == false) {
return ;
}
//
// Notify the current state that it's starting again
//
if (Is_Valid_State (CurrState)) {
if (StateTable[CurrState].Begin != NULL) {
(Object->*(StateTable[CurrState].Begin)) ();
}
}
IsHalted = false;
return ;
}
///////////////////////////////////////////////////////////////////
// Get_State
///////////////////////////////////////////////////////////////////
int Get_State (void)
{
return CurrState;
}
///////////////////////////////////////////////////////////////////
// Set_State
///////////////////////////////////////////////////////////////////
void Set_State (int state_index, bool force = false)
{
if (state_index == CurrState && force == false) {
return ;
}
//
// First, check to see if we can switch states
//
if (IsHalted == false && Is_Valid_State (CurrState)) {
if (force == false && StateTable[CurrState].RequestEnd != NULL) {
if ((Object->*(StateTable[CurrState].RequestEnd)) (state_index) == false) {
return ;
}
}
//
// Now, notify the old state that it's ending
//
if (StateTable[CurrState].End != NULL) {
(Object->*(StateTable[CurrState].End)) ();
}
}
if (Is_Valid_State (state_index)) {
//
// Switch to the new state
//
CurrState = state_index;
if (IsHalted == false && StateTable[CurrState].Begin != NULL) {
(Object->*(StateTable[CurrState].Begin)) ();
}
} else {
CurrState = -1;
}
return ;
}
///////////////////////////////////////////////////////////////////
// Save
///////////////////////////////////////////////////////////////////
void Save (ChunkSaveClass &csave)
{
csave.Begin_Chunk (CHUNKID_VARIABLES);
WRITE_MICRO_CHUNK (csave, VARID_CURR_STATE, CurrState);
WRITE_MICRO_CHUNK (csave, VARID_IS_HALTED, IsHalted);
csave.End_Chunk ();
return ;
}
///////////////////////////////////////////////////////////////////
// Load
///////////////////////////////////////////////////////////////////
void Load (ChunkLoadClass &cload)
{
while (cload.Open_Chunk ())
{
switch (cload.Cur_Chunk_ID ())
{
case CHUNKID_VARIABLES:
Load_Variables (cload);
break;
default:
WWDEBUG_SAY (("Unrecognized StateMachineClass chunkID\n"));
break;
}
cload.Close_Chunk ();
}
return ;
}
///////////////////////////////////////////////////////////////////
// Load_Variables
///////////////////////////////////////////////////////////////////
void Load_Variables (ChunkLoadClass &cload)
{
while (cload.Open_Micro_Chunk ()) {
switch (cload.Cur_Micro_Chunk_ID ())
{
READ_MICRO_CHUNK (cload, VARID_CURR_STATE, CurrState);
READ_MICRO_CHUNK (cload, VARID_IS_HALTED, IsHalted);
default:
WWDEBUG_SAY (("Unrecognized StateMachineClass variable chunkID\n"));
break;
}
cload.Close_Micro_Chunk ();
}
return ;
}
protected:
///////////////////////////////////////////////////////////////////
// Protected methods
///////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
// Is_Valid_State
///////////////////////////////////////////////////////////////////
bool Is_Valid_State (int index)
{
return (index >= 0 && index < StateTable.Count ());
}
///////////////////////////////////////////////////////////////////
// Protected member data
///////////////////////////////////////////////////////////////////
SimpleDynVecClass StateTable;
T * Object;
int CurrState;
bool IsHalted;
};
#endif //__STATEMACHINE_H