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>
578 lines
14 KiB
C++
578 lines
14 KiB
C++
#include "rp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "scorzone.h"
|
|
#include "..\munga\fileutil.h"
|
|
#include "..\munga\nttmgr.h"
|
|
#include "..\munga\app.h"
|
|
#include "..\munga\hostmgr.h"
|
|
#include "..\munga\notation.h"
|
|
|
|
//#############################################################################
|
|
//############################### ScoreZone #############################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
|
|
Derivation* ScoreZone::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(Entity::GetClassDerivations(), "ScoreZone");
|
|
return &classDerivations;
|
|
}
|
|
|
|
ScoreZone::SharedData
|
|
ScoreZone::DefaultData(
|
|
ScoreZone::GetClassDerivations(),
|
|
ScoreZone::MessageHandlers,
|
|
ScoreZone::GetAttributeIndex(),
|
|
ScoreZone::StateCount,
|
|
(Entity::MakeHandler)ScoreZone::Make
|
|
);
|
|
//#############################################################################
|
|
// Attribute Support
|
|
//
|
|
|
|
const ScoreZone::IndexEntry
|
|
ScoreZone::AttributePointers[]=
|
|
{
|
|
ATTRIBUTE_ENTRY(ScoreZone, ScoreZoneType, scoreZoneType),
|
|
ATTRIBUTE_ENTRY(ScoreZone, PointValue, pointValue)
|
|
};
|
|
|
|
ScoreZone::AttributeIndexSet& ScoreZone::GetAttributeIndex()
|
|
{
|
|
static ScoreZone::AttributeIndexSet attributeIndex(ELEMENTS(ScoreZone::AttributePointers),
|
|
ScoreZone::AttributePointers,
|
|
Entity::GetAttributeIndex()
|
|
);
|
|
return attributeIndex;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Message Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
ScoreZone::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(ScoreZone, ProcessScoreZoneRequest)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet
|
|
ScoreZone::MessageHandlers(
|
|
ELEMENTS(ScoreZone::MessageHandlerEntries),
|
|
ScoreZone::MessageHandlerEntries,
|
|
Entity::GetMessageHandlers()
|
|
);
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
|
|
void
|
|
ScoreZone::ProcessScoreZoneRequestMessageHandler(
|
|
ProcessScoreZoneRequestMessage *message
|
|
)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(message);
|
|
|
|
//
|
|
//----------------------------------------
|
|
// Make sure that the score zone is active
|
|
//----------------------------------------
|
|
//
|
|
EntityGroup *score_zone_group;
|
|
if (GetSimulationState() == Active)
|
|
{
|
|
//
|
|
//-----------------------------------------------------------
|
|
// If Sequential, hunt for ourselves in the sequential group
|
|
//-----------------------------------------------------------
|
|
//
|
|
Check(application);
|
|
EntityManager *entity_mgr = application->GetEntityManager();
|
|
Check(entity_mgr);
|
|
if (
|
|
scoreZoneType == LocalSequential
|
|
|| scoreZoneType == GlobalSequential
|
|
)
|
|
{
|
|
int new_zone = sequenceNumber + 1;
|
|
score_zone_group = entity_mgr->FindGroup("SequentialScoreZones");
|
|
Check(score_zone_group);
|
|
ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
|
|
ScoreZone
|
|
*score_zone,
|
|
*first_score_zone = NULL;
|
|
|
|
//
|
|
//------------------------------------------------------------------
|
|
// Store the first score zone in case we run off the end of the list
|
|
//------------------------------------------------------------------
|
|
//
|
|
while ((score_zone = (ScoreZone*)iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(score_zone);
|
|
if (score_zone->sequenceNumber == 1)
|
|
{
|
|
first_score_zone = score_zone;
|
|
}
|
|
if (score_zone->sequenceNumber == new_zone)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
if (!score_zone)
|
|
{
|
|
score_zone = first_score_zone;
|
|
}
|
|
|
|
if (score_zone->GetSimulationState() == InActive)
|
|
{
|
|
score_zone->SetSimulationState(Active);
|
|
score_zone_group = entity_mgr->FindGroup("ActiveScoreZones");
|
|
Check(score_zone_group);
|
|
score_zone_group->Add(score_zone);
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// Set the zone as inactive, remove ourselves from the active list
|
|
//----------------------------------------------------------------
|
|
//
|
|
SetSimulationState(InActive);
|
|
score_zone_group = entity_mgr->FindGroup("ActiveScoreZones");
|
|
Check(score_zone_group);
|
|
ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
|
|
ScoreZone *score_zone;
|
|
while ((score_zone = (ScoreZone*)iterator.GetCurrent()) != NULL)
|
|
{
|
|
if (score_zone == this)
|
|
{
|
|
iterator.Remove();
|
|
break;
|
|
}
|
|
iterator.Next();
|
|
}
|
|
|
|
//
|
|
//-----------------------------------
|
|
// Send the points back to the player
|
|
//-----------------------------------
|
|
//
|
|
ReplyMessage
|
|
reply(
|
|
message->replyMessageID,
|
|
sizeof(ReplyMessage),
|
|
pointValue
|
|
);
|
|
Check(application);
|
|
HostManager *host = application->GetHostManager();
|
|
Check(host);
|
|
Entity *entity = host->GetEntityPointer(message->requestingEntity);
|
|
Check(entity);
|
|
entity->Dispatch(&reply);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
ScoreZone::Dispatch(Receiver::Message *message)
|
|
{
|
|
Check(this);
|
|
Check(message);
|
|
|
|
ProcessScoreZoneRequestMessage *score_zone_message =
|
|
(ProcessScoreZoneRequestMessage*) message;
|
|
if (score_zone_message->messageID == ProcessScoreZoneRequestMessageID)
|
|
{
|
|
if (!extentBox.Contains(score_zone_message->linearPosition))
|
|
{
|
|
Check_Fpu();
|
|
return;
|
|
}
|
|
}
|
|
Entity::Dispatch(score_zone_message);
|
|
Check_Fpu();
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ScoreZone::ScoreZone(
|
|
ScoreZone::MakeMessage *creation_message,
|
|
ScoreZone::SharedData &virtual_data
|
|
):
|
|
Entity(creation_message, virtual_data)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Pointer(creation_message);
|
|
|
|
SetValidFlag();
|
|
|
|
//-------------------------------------------------
|
|
// Find the drop zone resource in the resource file
|
|
//-------------------------------------------------
|
|
ResourceFile
|
|
*res_file = application->GetResourceFile();
|
|
Check(res_file);
|
|
ResourceDescription *res =
|
|
res_file->SearchList(
|
|
resourceID,
|
|
ResourceDescription::GameModelResourceType
|
|
);
|
|
Check(res);
|
|
res->Lock();
|
|
|
|
//
|
|
// Get ScoreZone Info
|
|
//
|
|
sequenceNumber = creation_message->sequenceNumber;
|
|
|
|
ModelResource
|
|
*score_res = (ModelResource*)res->resourceAddress;
|
|
|
|
scoreZoneType = score_res->scoreZoneType;
|
|
pointValue = score_res->pointValue;
|
|
extentBox = score_res->extentBox;
|
|
|
|
extentBox.minX += localOrigin.linearPosition.x;
|
|
extentBox.minY += localOrigin.linearPosition.y;
|
|
extentBox.minZ += localOrigin.linearPosition.z;
|
|
|
|
extentBox.maxX += localOrigin.linearPosition.x;
|
|
extentBox.maxY += localOrigin.linearPosition.y;
|
|
extentBox.maxZ += localOrigin.linearPosition.z;
|
|
|
|
SetSimulationState(InActive);
|
|
|
|
res->Unlock();
|
|
//---------------------------------------------------------------------
|
|
// Make the sequential score zone list. Only make the first sequential
|
|
// score zone active
|
|
//---------------------------------------------------------------------
|
|
EntityManager *entity_manager = application->GetEntityManager();
|
|
Check(entity_manager);
|
|
EntityGroup *score_zones;
|
|
|
|
if (scoreZoneType == LocalSequential || scoreZoneType == GlobalSequential)
|
|
{
|
|
score_zones = entity_manager->UseGroup("SequentialScoreZones");
|
|
Check(score_zones);
|
|
score_zones->Add(this);
|
|
if (sequenceNumber == 1)
|
|
{
|
|
goto Active_Zone;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------
|
|
// If it isn't a sequential, always make it active
|
|
//------------------------------------------------
|
|
//
|
|
else
|
|
{
|
|
Active_Zone:
|
|
score_zones = entity_manager->UseGroup("ActiveScoreZones");
|
|
Check(score_zones);
|
|
SetSimulationState(Active);
|
|
score_zones->Add(this);
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ScoreZone*
|
|
ScoreZone::Make(ScoreZone::MakeMessage *creation_message)
|
|
{
|
|
return new ScoreZone(creation_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
ScoreZone::CreateMakeMessage(
|
|
MakeMessage *creation_message,
|
|
NotationFile *model_file,
|
|
const ResourceDirectories *directories
|
|
)
|
|
{
|
|
Check(creation_message);
|
|
Check(model_file);
|
|
|
|
if (!Entity::CreateMakeMessage(creation_message, model_file, directories))
|
|
{
|
|
Check_Fpu();
|
|
return False;
|
|
}
|
|
|
|
creation_message->classToCreate = RegisteredClass::ScoreZoneClassID;
|
|
creation_message->messageLength = sizeof(*creation_message);
|
|
creation_message->instanceFlags =
|
|
IndependantInstance|DynamicFlag|MapFlag|TrappedFlag;
|
|
|
|
char *p;
|
|
creation_message->sequenceNumber = -1;
|
|
if ((p = strtok(NULL, " ")) != NULL)
|
|
{
|
|
creation_message->sequenceNumber = atoi(p);
|
|
}
|
|
|
|
Check_Fpu();
|
|
return True;
|
|
}
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ScoreZone::~ScoreZone()
|
|
{
|
|
Check(this);
|
|
|
|
if (scoreZoneType == LocalSequential || scoreZoneType == GlobalSequential)
|
|
{
|
|
Check(application);
|
|
EntityManager *entity_mgr = application->GetEntityManager();
|
|
Check(entity_mgr);
|
|
EntityGroup *score_zone_group =
|
|
entity_mgr->FindGroup("SequentialScoreZones");
|
|
if (score_zone_group)
|
|
{
|
|
ChainIteratorOf<Node*> iterator(score_zone_group->groupMembers);
|
|
ScoreZone *score_zone = (ScoreZone*)iterator.GetCurrent();
|
|
if (score_zone == this)
|
|
{
|
|
iterator.Remove();
|
|
if (!iterator.GetCurrent())
|
|
{
|
|
entity_mgr->RemoveGroup(score_zone_group);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Check_Fpu();
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
ResourceDescription::ResourceID
|
|
ScoreZone::CreateModelResource(
|
|
ResourceFile *resource_file,
|
|
const char* model_name,
|
|
NotationFile * model_file,
|
|
const ResourceDirectories *directories,
|
|
ModelResource *model
|
|
)
|
|
{
|
|
Check(resource_file);
|
|
Check_Pointer(model_name);
|
|
Check_Pointer(directories);
|
|
|
|
//
|
|
// If we were not provided a buffer to write the model data into, we must
|
|
// create it ourselves. Then make sure that the model stuff is read in
|
|
//
|
|
ModelResource *local_model = model;
|
|
if (!local_model)
|
|
{
|
|
local_model = new ModelResource;
|
|
Register_Pointer(local_model);
|
|
}
|
|
|
|
|
|
//
|
|
// Get PointValue assigned to this ScoreZone
|
|
//
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"PointValue",
|
|
&local_model->pointValue
|
|
)
|
|
)
|
|
{
|
|
std::cerr << model_name << "Missing PointValue" << std::endl;
|
|
Dump_And_Die:
|
|
if (!model)
|
|
{
|
|
Unregister_Pointer(local_model);
|
|
delete local_model;
|
|
}
|
|
Check_Fpu();
|
|
return -1;
|
|
}
|
|
//
|
|
// Get The ScoreZone Type
|
|
//
|
|
const char* score_zone_type;
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"ScoreZoneType",
|
|
&score_zone_type
|
|
)
|
|
)
|
|
{
|
|
std::cerr << model_name << "Missing ScoreZoneType" << std::endl;
|
|
goto Dump_And_Die;
|
|
}
|
|
else
|
|
{
|
|
if(strcmp(score_zone_type,"GlobalOneTime") == 0)
|
|
{
|
|
local_model->scoreZoneType = GlobalOneTime;
|
|
}
|
|
else
|
|
if(strcmp(score_zone_type,"LocalOneTime") == 0)
|
|
{
|
|
local_model->scoreZoneType = LocalOneTime;
|
|
}
|
|
else
|
|
if(strcmp(score_zone_type,"GlobalSequential") == 0)
|
|
{
|
|
local_model->scoreZoneType = GlobalSequential;
|
|
}
|
|
else
|
|
if(strcmp(score_zone_type,"LocalSequential") == 0)
|
|
{
|
|
local_model->scoreZoneType = LocalSequential;
|
|
}
|
|
}
|
|
//
|
|
// Read In the Extent Box from the solid file determined by the
|
|
// collision data
|
|
//
|
|
const char* sld_entry;
|
|
if(
|
|
!model_file->GetEntry(
|
|
"gamedata",
|
|
"Extents",
|
|
&sld_entry
|
|
)
|
|
)
|
|
{
|
|
std::cerr << model_name << "Missing .sld Specifications " << std::endl;
|
|
goto Dump_And_Die;
|
|
}
|
|
else
|
|
{
|
|
char *sld_filename =
|
|
MakePathedFilename(directories->collisionDirectory, sld_entry);
|
|
Register_Pointer(sld_filename);
|
|
|
|
NotationFile *sld_file = new NotationFile(sld_filename);
|
|
Register_Object(sld_file);
|
|
if(sld_file->PageCount() == 0)
|
|
{
|
|
std::cerr <<sld_filename<<" is has no Pages"<<std::endl;
|
|
Unregister_Pointer(sld_filename);
|
|
delete sld_filename;
|
|
Unregister_Object(sld_file);
|
|
delete sld_file;
|
|
goto Dump_And_Die;
|
|
}
|
|
|
|
NameList *sld_namelist = sld_file->MakeEntryList("volume 0","extm");
|
|
Register_Object(sld_namelist);
|
|
if (sld_namelist->EntryCount() == 0)
|
|
{
|
|
Dump_And_Die_2:
|
|
Unregister_Pointer(sld_filename);
|
|
delete sld_filename;
|
|
Unregister_Object(sld_file);
|
|
delete sld_file;
|
|
Unregister_Object(sld_namelist);
|
|
delete sld_namelist;
|
|
goto Dump_And_Die;
|
|
}
|
|
NameList::Entry *extent_entry = sld_namelist->GetFirstEntry();
|
|
while(extent_entry)
|
|
{
|
|
Scalar value = extent_entry->GetAtof();
|
|
if (strcmp(extent_entry->GetName(),"extminX") == 0)
|
|
{
|
|
local_model->extentBox.minX = value;
|
|
}
|
|
else
|
|
if (strcmp(extent_entry->GetName(),"extminY") == 0)
|
|
{
|
|
local_model->extentBox.minY = value;
|
|
}
|
|
else
|
|
if (strcmp(extent_entry->GetName(),"extminZ") == 0)
|
|
{
|
|
local_model->extentBox.minZ = value;
|
|
}
|
|
else
|
|
if (strcmp(extent_entry->GetName(),"extmaxX") == 0)
|
|
{
|
|
local_model->extentBox.maxX = value;
|
|
}
|
|
else
|
|
if (strcmp(extent_entry->GetName(),"extmaxY") == 0)
|
|
{
|
|
local_model->extentBox.maxY = value;
|
|
}
|
|
else
|
|
if (strcmp(extent_entry->GetName(),"extmaxZ") == 0)
|
|
{
|
|
local_model->extentBox.maxZ = value;
|
|
}
|
|
else
|
|
{
|
|
std::cerr << sld_filename << "Improper Extent Format" << std::endl;
|
|
goto Dump_And_Die_2;
|
|
}
|
|
extent_entry = extent_entry->GetNextEntry();
|
|
}
|
|
Unregister_Pointer(sld_filename);
|
|
delete sld_filename;
|
|
Unregister_Object(sld_file);
|
|
delete sld_file;
|
|
Unregister_Object(sld_namelist);
|
|
delete sld_namelist;
|
|
}
|
|
//
|
|
//---------------------------
|
|
// Write out the new resource
|
|
//---------------------------
|
|
//
|
|
if (!model)
|
|
{
|
|
ResourceDescription *new_res =
|
|
resource_file->AddResource(
|
|
model_name,
|
|
ResourceDescription::GameModelResourceType,
|
|
1,
|
|
ResourceDescription::Preload,
|
|
local_model,
|
|
sizeof(*local_model)
|
|
);
|
|
Unregister_Pointer(local_model);
|
|
delete local_model;
|
|
Check(new_res);
|
|
Check_Fpu();
|
|
return new_res->resourceID;
|
|
}
|
|
else
|
|
{
|
|
Check_Fpu();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
ScoreZone::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|