Files
RP412/MUNGA/CONTROLS.h
T
CydandClaude Opus 4.8 4abbf8879f Initial import of Red Planet v4.10 Win32 source
Imports the current Win32 source for the pod-racing game 'Red Planet',
built on the MUNGA engine and its L4 (Win32/DirectX) platform layer:

- MUNGA / MUNGA_L4: cross-platform engine core and Win32 backend
- RP / RP_L4: Red Planet game logic and Win32 application
- DivLoader, Setup1: asset loader and installer project
- lib, MUNGA_L4/openal, MUNGA_L4/sos: third-party audio dependencies

Removed stale Subversion metadata and added .gitignore/.gitattributes.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-30 07:59:51 -05:00

911 lines
20 KiB
C++

#pragma once
#include "style.h"
class Controls;
class ControlsInstance;
class ControlsMappingGroup;
struct ControlsMapping;
class ControlsOwner;
class ControlsManager;
#include "slot.h"
#include "chain.h"
#include "vector2d.h"
#include "receiver.h"
#include "simulate.h"
#include "mode.h"
//########################################################################
//######################## Basic control typedefs ########################
//########################################################################
typedef Scalar ControlsScalar;
typedef Vector2DOf<Scalar> ControlsJoystick;
typedef int ControlsKey;
typedef int ControlsButton; // negative for release
typedef Vector2DOf<int> ControlsMouse;
//#########################################################################
//######################### ControlsInstance ##############################
//#########################################################################
// The ControlsInstance object exists to serve the ControlsMappingGroup
// object. It maintains one instance of a mapping from a physical
// input device to either an entity event message or a value.
class ControlsInstance :
public Node
{
friend class ControlsMappingGroup;
friend class ControlsManager; // required for testing
friend class L4MappableButtonManager;
public:
ControlsInstance(
ClassID class_id,
ModeMask mode_mask,
Plug *dependant
);
~ControlsInstance();
ModeMask
modeMask; // lots of objects (and templates) need access to this
protected:
virtual void Update(void *data_source);
SlotOf<Plug*>
dependantPlug;
void
ReleaseLinkHandler(
Socket *socket,
Plug *plug
);
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ControlsInstanceDirectOf ~~~~~~~~~~~~~~~~~~
template <class T> class ControlsInstanceDirectOf:
public ControlsInstance
{
friend class ControlsMappingGroup;
friend class ControlsManager; // required for testing
public:
ControlsInstanceDirectOf(
ModeMask mode_mask,
T* data_pointer,
Plug *dependant
);
ControlsInstanceDirectOf(
ControlsInstanceDirectOf<T> *original
);
~ControlsInstanceDirectOf();
Logical
TestInstance() const;
// protected:
void Update(void *data_source);
T *dataPointer;
};
template <class T> ControlsInstanceDirectOf<T>::ControlsInstanceDirectOf(
ModeMask mode_mask,
T* data,
Plug *dependant
):
ControlsInstance(
DirectControlsInstanceClassID,
mode_mask,
dependant
),
dataPointer(data)
{}
template <class T> ControlsInstanceDirectOf<T>::ControlsInstanceDirectOf(
ControlsInstanceDirectOf<T> *original
):
ControlsInstance(
DirectControlsInstanceClassID,
original->modeMask,
original->dependantPlug.GetCurrent()
),
dataPointer(original->dataPointer)
{}
template <class T> ControlsInstanceDirectOf<T>::~ControlsInstanceDirectOf()
{}
template <class T> void
ControlsInstanceDirectOf<T>::Update(void *data_source)
{
Check(this);
Check_Pointer(data_source);
Check_Pointer(dataPointer);
*dataPointer = *(T*)data_source;
Verify(*dataPointer == *(T*)data_source);
Check_Fpu();
}
template <class T> Logical
ControlsInstanceDirectOf<T>::TestInstance() const
{
Check_Pointer(this);
Check_Pointer(dataPointer);
Check_Fpu();
return Plug::TestInstance();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ControlsInstanceEventOf ~~~~~~~~~~~~~~~~~~
template <class T> class ControlsInstanceEventOf:
public ControlsInstance
{
friend class ControlsMappingGroup;
friend class ControlsManager; // required for testing
public:
ControlsInstanceEventOf(
ModeMask mode_mask,
Receiver *receiver,
Receiver::MessageID message_id,
Plug *dependant
);
ControlsInstanceEventOf(
ControlsInstanceEventOf<T> *original
);
~ControlsInstanceEventOf();
Logical
TestInstance() const;
// protected:
void Update(void *data_source);
Receiver *receiverPointer;
Receiver::MessageID messageID;
};
template <class T> ControlsInstanceEventOf<T>::ControlsInstanceEventOf(
ModeMask mode_mask,
Receiver *receiver,
Receiver::MessageID message_id,
Plug *dependant
):
ControlsInstance(
EventControlsInstanceClassID,
mode_mask,
dependant
),
receiverPointer(receiver),
messageID(message_id)
{}
template <class T> ControlsInstanceEventOf<T>::ControlsInstanceEventOf(
ControlsInstanceEventOf<T> *original
):
ControlsInstance(
EventControlsInstanceClassID,
original->modeMask,
original->dependantPlug.GetCurrent()
),
receiverPointer(original->receiverPointer),
messageID(original->messageID)
{}
template <class T> ControlsInstanceEventOf<T>::~ControlsInstanceEventOf()
{}
template <class T> void
ControlsInstanceEventOf<T>::Update(void *data_source)
{
Check(this);
Check_Pointer(data_source);
Check(application);
ReceiverDataMessageOf<T>
update_message(
messageID,
sizeof(ReceiverDataMessageOf<T>),
*(T*)data_source
);
application->Post(
ControlsEventPriority,
receiverPointer,
&update_message
);
}
template <class T> Logical
ControlsInstanceEventOf<T>::TestInstance() const
{
Check_Pointer(this);
Check(receiverPointer);
Check_Fpu();
return Plug::TestInstance();
}
//#########################################################################
//######################### ControlsMappingGroup ##########################
//#########################################################################
// The ControlsMappingGroup object maintains a group of ControlInstances.
//
// ControlsMappingGroup.Update() will call ControlsInstance.Update()
// for each instance in the group.
class ControlsMappingGroup :
public Node
{
friend class ControlsManager; // required for testing
public:
ControlsMappingGroup();
~ControlsMappingGroup();
void
Add(ControlsInstance *mapping)
{
Check(this);
Check(mapping);
instanceList.Add(mapping);
}
virtual void
Remove(ModeMask mode_mask);
virtual void
Update(void *value, ModeMask mode_mask);
Logical // returns true if at least one button active
CurrentFilterStatus(ModeMask mode_mask);
protected:
ChainOf<ControlsInstance*>
instanceList;
};
//#########################################################################
//########################## ControlsUpdateManager ########################
//#########################################################################
template <class T> class ControlsUpdateManager :
public ControlsMappingGroup
{
public:
ControlsUpdateManager();
~ControlsUpdateManager();
Logical
TestInstance() const;
virtual ControlsInstance*
Add(
ModeMask mode_mask,
Receiver *receiver_pointer,
Receiver::MessageID id,
Plug *dependant
);
virtual ControlsInstance*
Add(
ModeMask mode_mask,
T *destination,
Plug *dependant
);
virtual ControlsInstance*
AddOrErase(
ModeMask mode_mask,
Receiver *target,
Receiver::MessageID message_ID,
Plug *dependant
);
virtual ControlsInstance*
AddOrErase(
ModeMask mode_mask,
T *destination,
Plug *dependant
);
enum
{
unmapped = 0,
mappedByOthers = 1,
mappedByMe = 2
};
virtual int
GetMapState(
void *direct_destination,
Receiver *event_receiver,
Receiver::MessageID message_id,
ModeMask mode_mask
);
virtual void
Update(void *sourcePointer, ModeMask mode_mask);
void
ForceUpdate(T *sourcePointer, ModeMask mode_mask);
T
previousValue;
};
template <class T> Logical
ControlsUpdateManager<T>::TestInstance() const
{
return True;
}
template <class T>
ControlsUpdateManager<T>::ControlsUpdateManager()
: ControlsMappingGroup()
{
}
template <class T> ControlsUpdateManager<T>::~ControlsUpdateManager()
{
}
template <class T> void
ControlsUpdateManager<T>::Update(
void * source_pointer,
ModeMask mode_mask
)
{
Check(this);
Check_Pointer(source_pointer);
if (previousValue != *(T*) source_pointer)
{
ForceUpdate((T*)source_pointer, mode_mask);
}
Check_Fpu();
}
template <class T> void
ControlsUpdateManager<T>::ForceUpdate(
T* source_pointer,
ModeMask mode_mask
)
{
Check(this);
Check_Pointer(source_pointer);
previousValue = *source_pointer;
ControlsMappingGroup::Update(source_pointer, mode_mask);
Check_Fpu();
}
template <class T> ControlsInstance*
ControlsUpdateManager<T>::Add(
ModeMask mode_mask,
Receiver *receiver_pointer,
Receiver::MessageID message_id,
Plug *dependant
)
{
Check(this);
Check(receiver_pointer);
ControlsInstanceEventOf<T> *instance =
new ControlsInstanceEventOf<T>(
mode_mask,
receiver_pointer,
message_id,
dependant
);
Register_Object(instance);
ControlsMappingGroup::Add(instance);
Check_Fpu();
return instance;
}
template <class T> ControlsInstance*
ControlsUpdateManager<T>::Add(
ModeMask mode_mask,
T *destination,
Plug *dependant
)
{
Check(this);
Check_Pointer(destination);
ControlsInstanceDirectOf<T> *instance =
new ControlsInstanceDirectOf<T>(
mode_mask,
destination,
dependant
);
Register_Object(instance);
ControlsMappingGroup::Add(instance);
Check_Fpu();
return instance;
}
template <class T> ControlsInstance*
ControlsUpdateManager<T>::AddOrErase(
ModeMask mode_mask,
Receiver *receiver_pointer,
Receiver::MessageID message_id,
Plug *dependant
)
{
Check(this);
Check(receiver_pointer);
ChainIteratorOf<ControlsInstance*>
i(instanceList);
ControlsInstance
*controls_instance;
ControlsInstanceEventOf<T>
*event_mapping;
//-----------------------------------------------
// Search through all mappings
//-----------------------------------------------
while ((controls_instance=i.ReadAndNext()) != NULL)
{
Check(controls_instance);
//-----------------------------------------------
// Process only the event mappings
//-----------------------------------------------
if (controls_instance->GetClassID() == EventControlsInstanceClassID)
{
event_mapping = (ControlsInstanceEventOf<T> *)controls_instance;
Check(event_mapping);
//-----------------------------------------------
// Process only if the target is correct, AND
// if the mapping is allowed under the given
// mode mask
//-----------------------------------------------
if (
(event_mapping->receiverPointer == receiver_pointer) &&
(event_mapping->messageID == message_id) &&
(event_mapping->modeMask & mode_mask)
)
{
//-----------------------------------------------
// Found! Remove mode enable bit(s)
//-----------------------------------------------
event_mapping->modeMask &= ~mode_mask;
//-----------------------------------------------
// If this mapping no longer matches ANY mode
// (i.e., all bits cleared), delete it, 'cuz
// it doesn't do anything anymore.
//-----------------------------------------------
if (event_mapping->modeMask == (ModeMask)0)
{
Unregister_Object(event_mapping);
delete event_mapping;
}
//-----------------------------------------------
// We found the match, break out of the loop
//-----------------------------------------------
break;
}
}
}
//----------------------------------------------------------
// If the mapping wasn't found (controls_instance == NULL),
// then create one.
//----------------------------------------------------------
if (controls_instance == NULL)
{
event_mapping =
new ControlsInstanceEventOf<T>(
mode_mask,
receiver_pointer,
message_id,
dependant
);
Register_Object(event_mapping);
ControlsMappingGroup::Add(event_mapping);
Check_Fpu();
return event_mapping;
}
Check_Fpu();
return NULL;
}
template <class T> ControlsInstance*
ControlsUpdateManager<T>::AddOrErase(
ModeMask mode_mask,
T *destination,
Plug *dependant
)
{
Check(this);
Check_Pointer(destination);
ChainIteratorOf<ControlsInstance*>
i(instanceList);
ControlsInstance
*controls_instance;
ControlsInstanceDirectOf<T>
*direct_mapping;
//-----------------------------------------------
// Search through all mappings
//-----------------------------------------------
while ((controls_instance=i.ReadAndNext()) != NULL)
{
Check(controls_instance);
//-----------------------------------------------
// Process only the direct mappings
//-----------------------------------------------
if (controls_instance->GetClassID() == DirectControlsInstanceClassID)
{
direct_mapping = (ControlsInstanceDirectOf<T> *)controls_instance;
Check(direct_mapping);
//-----------------------------------------------
// Process only if the target is correct, AND
// if the mapping is allowed under the given
// mode mask
//-----------------------------------------------
if (
(direct_mapping->dataPointer == destination) &&
(direct_mapping->modeMask & mode_mask)
)
{
//-----------------------------------------------
// Found! Remove mode enable bit(s)
//-----------------------------------------------
direct_mapping->modeMask &= ~mode_mask;
//-----------------------------------------------
// If this mapping no longer matches ANY mode
// (i.e., all bits cleared), delete it, 'cuz
// it doesn't do anything anymore.
//-----------------------------------------------
if (direct_mapping->modeMask == (ModeMask)0)
{
Unregister_Object(direct_mapping);
delete direct_mapping;
}
//-----------------------------------------------
// We found the match, break out of the loop
//-----------------------------------------------
break;
}
}
}
//----------------------------------------------------------
// If the mapping wasn't found (controls_instance == NULL),
// then create one.
//----------------------------------------------------------
if (controls_instance == NULL)
{
direct_mapping =
new ControlsInstanceDirectOf<T>(
mode_mask,
destination,
dependant
);
Register_Object(direct_mapping);
ControlsMappingGroup::Add(direct_mapping);
Check_Fpu();
return direct_mapping;
}
Check_Fpu();
return NULL;
}
template <class T> int
ControlsUpdateManager<T>::GetMapState(
void *direct_destination,
Receiver *event_receiver,
Receiver::MessageID message_id,
ModeMask mode_mask
)
{
Check(this);
ChainIteratorOf<ControlsInstance*>
i(instanceList);
ControlsInstance
*controls_instance;
int
map_state = 0;
//-----------------------------------------------
// Search through all mappings
//-----------------------------------------------
while ((controls_instance=i.ReadAndNext()) != NULL)
{
Check(controls_instance);
//------------------------------------
// Process if allowed mode
//------------------------------------
if (controls_instance->modeMask & mode_mask)
{
//------------------------------------
// Process direct mappings
//------------------------------------
if (controls_instance->GetClassID() ==
DirectControlsInstanceClassID
)
{
ControlsInstanceDirectOf<T>
*direct_mapping = (ControlsInstanceDirectOf<T> *)
controls_instance;
Check(direct_mapping);
if (direct_mapping->dataPointer == direct_destination)
{
map_state |= mappedByMe;
}
else
{
map_state |= mappedByOthers;
}
}
//------------------------------------
// Process event mappings
//------------------------------------
else
{
Verify(controls_instance->GetClassID() ==
EventControlsInstanceClassID
);
ControlsInstanceEventOf<T>
*event_mapping = (ControlsInstanceEventOf<T> *)
controls_instance;
Check(event_mapping);
if (
(event_mapping->receiverPointer == event_receiver) &&
(event_mapping->messageID == message_id)
)
{
map_state |= mappedByMe;
}
else
{
map_state |= mappedByOthers;
}
}
}
}
Check_Fpu();
return map_state;
}
//#########################################################################
//############################ ControlsManager ############################
//#########################################################################
class ControlsManager:
public Receiver
{
friend class ControlsMappingGroup; // for access to groupEnable
//-------------------------------------------------------------------
// Shared data support
//-------------------------------------------------------------------
public:
static Derivation *GetClassDerivations();
// static SharedData DefaultData;
//-------------------------------------------------------------------
// Construction/destruction/verification
//-------------------------------------------------------------------
protected:
ControlsManager(
ClassID class_ID = ControlsManagerClassID,
SharedData &shared_data = ControlsManager::DefaultData
);
public:
~ControlsManager();
unsigned int
UniqueOwnerID()
{
if ((nextOwnerID+1)==0) { ++nextOwnerID; }
Check_Fpu();
return (++nextOwnerID);
}
virtual void
Remove(ModeMask mode_mask);
virtual void
Execute();
static Logical
TestClass();
Logical
TestInstance() const;
static void
Shutdown();
virtual void
LocalShutdown();
//-------------------------------------------------------------------
// Mode control
//-------------------------------------------------------------------
virtual void
StartMappableButtonsConfigure(
void *active_direct_target,
Receiver *target,
Plug *dependant,
Receiver::MessageID active_message_id,
ModeMask remove_mode_mask,
ModeMask add_mode_mask
);
virtual void
StopMappableButtonsConfigure(
ModeMask remove_mode_mask,
ModeMask add_mode_mask
);
virtual void
AddOrEraseMappableButton(
int button_number,
ModeMask mode_mask
);
//-------------------------------------------------------------------
// Gauge support
//-------------------------------------------------------------------
void
*activeDestination;
Receiver
*activeReceiver;
Receiver::MessageID
activeMessageID;
Plug
*activeDependant;
private:
static ControlsManager
*headControlsManager;
ControlsManager
*nextControlsManager;
static unsigned int
nextOwnerID;
};
//#########################################################################
//############################ ControlsMapping ############################
//#########################################################################
// Used by tools for building/reading control map streams
struct ControlsMapping
{
public:
enum {
DirectMapping=0,
EventMapping=1
};
Enumeration
controlsGroup,
controlsElement,
mappingType,
subsystemID;
ModeMask
modeMask;
union
{
Simulation::AttributeID attributeID;
Receiver::MessageID messageID;
};
};
//#########################################################################
//############################ ControlsString #############################
//#########################################################################
typedef int ControlsStringID;
class ControlsString :
public Node
{
friend class ControlsStringManager;
protected:
enum
{
maxStringLength=16
};
ControlsString(
const char *new_string,
int keypad_unit_number,
Receiver *receiver_pointer,
Receiver::MessageID message_id,
int user_code
);
~ControlsString();
Logical
TestInstance() const;
void
Update(int keypad_unit_number, ControlsKey new_key);
ControlsStringID
id;
const char
*matchStringPointer;
char
string[maxStringLength+1];
int
stringLength,
keypadUnitNumber,
userCode;
Receiver
*receiverPointer;
Receiver::MessageID
messageID;
static ControlsStringID
nextID;
};
//#########################################################################
//######################## ControlsStringMessage ##########################
//#########################################################################
// This is sent by the controlsString object when a match is detected
class ControlsStringMessage:
public Receiver::Message
{
public:
ControlsStringMessage(
Receiver::MessageID message_ID,
int user_code
):
Receiver::Message(message_ID, sizeof(ControlsStringMessage)),
userCode(user_code)
{}
int
userCode;
};
//#########################################################################
//######################## ControlsStringManager ##########################
//#########################################################################
// This class matches strings from a keypad. When the given string
// is matched, it sends an event to the given receiver.
class ControlsStringManager :
public Node
{
public:
ControlsStringManager();
~ControlsStringManager();
Logical
TestInstance() const;
ControlsStringID
Add(
const char *new_string,
int keypad_unit_number,
Receiver *receiver_pointer,
Receiver::MessageID id,
int user_code = 0
);
void
Remove(ControlsStringID string_id);
void
Update(int keypad_unit_number, ControlsKey new_key);
ChainOf<ControlsString*>
instanceList;
};