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>
448 lines
11 KiB
C++
448 lines
11 KiB
C++
#include "munga.h"
|
|
#pragma hdrstop
|
|
|
|
#include "dropzone.h"
|
|
#include "random.h"
|
|
#include "app.h"
|
|
#include "hostmgr.h"
|
|
#include "nttmgr.h"
|
|
#include "notation.h"
|
|
#include "namelist.h"
|
|
#include "player.h"
|
|
|
|
#define DOWN_TIME 5.0f
|
|
|
|
//#############################################################################
|
|
//############################### DropZone #################################
|
|
//#############################################################################
|
|
|
|
//#############################################################################
|
|
// Shared Data Support
|
|
//
|
|
Derivation* DropZone::GetClassDerivations()
|
|
{
|
|
static Derivation classDerivations(Entity::GetClassDerivations(),"DropZone");
|
|
return &classDerivations;
|
|
}
|
|
|
|
DropZone::SharedData
|
|
DropZone::DefaultData(
|
|
DropZone::GetClassDerivations(),
|
|
DropZone::GetMessageHandlers(),
|
|
DropZone::GetAttributeIndex(),
|
|
DropZone::StateCount,
|
|
(Entity::MakeHandler)DropZone::Make
|
|
);
|
|
|
|
//#############################################################################
|
|
// Message Support
|
|
//
|
|
const Receiver::HandlerEntry
|
|
DropZone::MessageHandlerEntries[]=
|
|
{
|
|
MESSAGE_ENTRY(DropZone, AssignDropZone)
|
|
};
|
|
|
|
Receiver::MessageHandlerSet& DropZone::GetMessageHandlers()
|
|
{
|
|
static Receiver::MessageHandlerSet messageHandlers(ELEMENTS(DropZone::MessageHandlerEntries), DropZone::MessageHandlerEntries, Entity::GetMessageHandlers());
|
|
return messageHandlers;
|
|
}
|
|
|
|
//#############################################################################
|
|
// Construction and Destruction
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DropZone::DropZone(
|
|
DropZone::MakeMessage *creation_message,
|
|
DropZone::SharedData &virtual_data
|
|
):
|
|
Entity(creation_message, virtual_data)
|
|
{
|
|
Check_Pointer(this);
|
|
Check_Pointer(creation_message);
|
|
|
|
SetValidFlag();
|
|
dropZoneCount = 0;
|
|
dropZones = NULL;
|
|
|
|
//
|
|
//--------------------------------------------------------------
|
|
// Name the drop zone, and enter the zone in the drop zone group
|
|
//--------------------------------------------------------------
|
|
//
|
|
Str_Copy(dropZoneName, creation_message->dropZoneName, sizeof(dropZoneName));
|
|
EntityManager *entity_manager = application->GetEntityManager();
|
|
Check(entity_manager);
|
|
EntityGroup* drop_zones = entity_manager->UseGroup("DropZones");
|
|
Check(drop_zones);
|
|
drop_zones->Add(this);
|
|
|
|
//
|
|
//-----------------------------------------------------------------------
|
|
// Now, interpret the data to create the appropriate number of drop zones
|
|
//-----------------------------------------------------------------------
|
|
//
|
|
MemoryStream
|
|
stream(
|
|
creation_message,
|
|
creation_message->messageLength,
|
|
sizeof(*creation_message)
|
|
);
|
|
dropZoneCount = creation_message->dropZoneCount;
|
|
Verify(dropZoneCount > 0);
|
|
dropZones = new Origin[dropZoneCount];
|
|
Register_Pointer(dropZones);
|
|
lastUsageTime = new Time[dropZoneCount];
|
|
Register_Pointer(lastUsageTime);
|
|
lastUsedBy = new EntityID[dropZoneCount];
|
|
Register_Pointer(lastUsedBy);
|
|
lastDeathCount = new int[dropZoneCount];
|
|
Register_Pointer(lastDeathCount);
|
|
|
|
for (int i=0; i<dropZoneCount; ++i)
|
|
{
|
|
dropZones[i] = *(Origin*)stream.GetPointer();
|
|
lastUsageTime[i] = Time::Null;
|
|
lastUsedBy[i] = EntityID::Null;
|
|
lastDeathCount[i] = -3;
|
|
stream.AdvancePointer(sizeof(Origin));
|
|
}
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DropZone*
|
|
DropZone::Make(DropZone::MakeMessage *creation_message)
|
|
{
|
|
return new DropZone(creation_message);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
DropZone::~DropZone()
|
|
{
|
|
if (dropZones)
|
|
{
|
|
Unregister_Pointer(dropZones);
|
|
delete[] dropZones;
|
|
Unregister_Pointer(lastUsageTime);
|
|
delete[] lastUsageTime;
|
|
Unregister_Pointer(lastUsedBy);
|
|
delete[] lastUsedBy;
|
|
Unregister_Pointer(lastDeathCount);
|
|
delete[] lastDeathCount;
|
|
}
|
|
}
|
|
|
|
//#############################################################################
|
|
// Dropzone assignment
|
|
//
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
DropZone::IsAvailable(int index)
|
|
{
|
|
Check(this);
|
|
Verify(index >= 0 && index < dropZoneCount);
|
|
Check(application);
|
|
|
|
return
|
|
!lastUsageTime[index].ticks
|
|
|| Now() - lastUsageTime[index] > DOWN_TIME
|
|
&& application->GetApplicationState() == Application::RunningMission;
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
void
|
|
DropZone::AssignDropZoneMessageHandler(AssignDropZoneMessage* message)
|
|
{
|
|
Check(this);
|
|
Check_Pointer(message);
|
|
|
|
Check(application);
|
|
HostManager *host = application->GetHostManager();
|
|
Check(host);
|
|
Entity *entity = host->GetEntityPointer(message->requestingEntity);
|
|
Check(entity);
|
|
|
|
//
|
|
//---------------------------------------------------------------------
|
|
// If we have allocated a dropzone within the last ten seconds for this
|
|
// player, resend the data
|
|
//---------------------------------------------------------------------
|
|
//
|
|
Origin *drop_zone = NULL;
|
|
int
|
|
lowest, highest, remaining, i;
|
|
for (i=0; i<dropZoneCount; ++i)
|
|
{
|
|
if (
|
|
!IsAvailable(i)
|
|
&& lastUsedBy[i] == message->requestingEntity
|
|
&& lastDeathCount[i] == message->deathCount
|
|
)
|
|
{
|
|
lastUsageTime[i] = Now();
|
|
drop_zone = &dropZones[i];
|
|
goto Found_One;
|
|
}
|
|
}
|
|
|
|
//
|
|
//------------------------------------------------------------------------
|
|
// Figure out how many drop zones are available. If none are, repost this
|
|
// message to ourself
|
|
//------------------------------------------------------------------------
|
|
//
|
|
lowest = dropZoneCount;
|
|
highest = -1;
|
|
remaining = 0;
|
|
for (i=0; i<dropZoneCount; ++i)
|
|
{
|
|
if (IsAvailable(i))
|
|
{
|
|
++remaining;
|
|
if (i < lowest)
|
|
{
|
|
lowest = i;
|
|
}
|
|
if (i > highest)
|
|
{
|
|
highest = i;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//-----------------------------------------------------
|
|
// Now, randomly look about until we find one available
|
|
//-----------------------------------------------------
|
|
//
|
|
highest = highest - lowest + 1;
|
|
while (remaining)
|
|
{
|
|
i = lowest + Random(highest);
|
|
Verify(i < dropZoneCount && i >= 0);
|
|
if (IsAvailable(i))
|
|
{
|
|
//
|
|
//-------------------------------------------------------------------
|
|
// Check to see if vehicle attached to a player is within 2 meters of
|
|
// this drop zone. If so, mark it unavailable for 3 seconds
|
|
//-------------------------------------------------------------------
|
|
//
|
|
EntityManager *entity_mgr = application->GetEntityManager();
|
|
Check(entity_mgr);
|
|
EntityGroup *player_group = entity_mgr->FindGroup("Players");
|
|
if (
|
|
player_group
|
|
&& application->GetApplicationState()
|
|
== Application::RunningMission
|
|
)
|
|
{
|
|
Player *player;
|
|
ChainIteratorOf<Node*> iterator(player_group->groupMembers);
|
|
while ((player = (Player*)iterator.ReadAndNext()) != NULL)
|
|
{
|
|
Check(player);
|
|
Entity *vehicle = player->GetPlayerVehicle();
|
|
if (vehicle && player != entity)
|
|
{
|
|
Check(vehicle);
|
|
Vector3D distance;
|
|
distance.Subtract(
|
|
vehicle->localOrigin.linearPosition,
|
|
dropZones[i].linearPosition
|
|
);
|
|
if (distance.LengthSquared() <= 4.0f)
|
|
{
|
|
lastUsedBy[i] = NULL;
|
|
lastUsageTime[i] = Now();
|
|
lastUsageTime[i] -= DOWN_TIME - 1.0f;
|
|
--remaining;
|
|
if (i == lowest)
|
|
{
|
|
++lowest;
|
|
--highest;
|
|
}
|
|
else if (i == lowest + highest - 1)
|
|
{
|
|
--highest;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
//----------------------------------------------------------
|
|
// If someone was in this spot, keep looking for another one
|
|
//----------------------------------------------------------
|
|
//
|
|
if (player)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
lastUsageTime[i] = Now();
|
|
drop_zone = &dropZones[i];
|
|
lastUsedBy[i] = message->requestingEntity;
|
|
lastDeathCount[i] = message->deathCount;
|
|
remaining = 0;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
//----------------------------------------------------------------
|
|
// If no drop_zone could be found, repost the message to ourselves
|
|
//----------------------------------------------------------------
|
|
//
|
|
if (!drop_zone)
|
|
{
|
|
Time when=Now();
|
|
when += 0.1f;
|
|
application->Post(MaxEventPriority, this, message, when);
|
|
return;
|
|
}
|
|
|
|
//
|
|
//--------------------------------------------------------------------
|
|
// Send a message back to the requesting object what sent this message
|
|
//--------------------------------------------------------------------
|
|
//
|
|
Found_One:
|
|
ReplyMessage
|
|
reply(
|
|
message->replyMessageID,
|
|
sizeof(ReplyMessage),
|
|
*drop_zone,
|
|
message->deathCount
|
|
);
|
|
entity->Dispatch(&reply);
|
|
}
|
|
|
|
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
//
|
|
Logical
|
|
DropZone::CreateMakeMessage(
|
|
MakeMessage *creation_message,
|
|
const char* model_name,
|
|
const char* resource_name,
|
|
NotationFile *map_file
|
|
)
|
|
{
|
|
Check(creation_message);
|
|
Check(map_file);
|
|
Check_Pointer(model_name);
|
|
Check_Pointer(resource_name);
|
|
|
|
if (!Entity::CreateMakeMessage(creation_message, map_file, NULL))
|
|
{
|
|
return False;
|
|
}
|
|
|
|
creation_message->classToCreate = RegisteredClass::DropZoneClassID;
|
|
creation_message->instanceFlags = MapFlag|TrappedFlag;
|
|
|
|
//
|
|
//-----------------------------------------------
|
|
// Count up the number of drop zones in our model
|
|
//-----------------------------------------------
|
|
//
|
|
Enumeration save_modes = map_file->GetModes();
|
|
map_file->SetMode(NotationFile::CleanListMode);
|
|
|
|
NameList *zone_list =
|
|
map_file->MakeEntryList(model_name, "dropzone");
|
|
Register_Object(zone_list);
|
|
creation_message->dropZoneCount = zone_list->EntryCount();
|
|
creation_message->messageLength =
|
|
sizeof(*creation_message)
|
|
+ creation_message->dropZoneCount*sizeof(Origin);
|
|
if (!creation_message->dropZoneCount)
|
|
{
|
|
std::cout << "Error: " << model_name << " hasn't specified any drop zones!\n";
|
|
Dump_And_Die:
|
|
Unregister_Object(zone_list);
|
|
delete zone_list;
|
|
map_file->PutModes(save_modes);
|
|
return False;
|
|
}
|
|
|
|
//
|
|
//---------------------------------------------
|
|
// Allocate a buffer to hold the drop zone data
|
|
//---------------------------------------------
|
|
//
|
|
MemoryStream
|
|
stream(
|
|
creation_message,
|
|
creation_message->messageLength,
|
|
sizeof(*creation_message)
|
|
);
|
|
|
|
//
|
|
//----------------------------
|
|
// zero out the drop zone name
|
|
//----------------------------
|
|
//
|
|
for (int ii=0; ii<sizeof(creation_message->dropZoneName); ii++)
|
|
{
|
|
creation_message->dropZoneName[ii] = '\0';
|
|
}
|
|
|
|
Str_Copy(
|
|
creation_message->dropZoneName,
|
|
resource_name,
|
|
sizeof(creation_message->dropZoneName)
|
|
);
|
|
|
|
//
|
|
//-----------------
|
|
// Read in the data
|
|
//-----------------
|
|
//
|
|
NameList::Entry *drop_zone = zone_list->GetFirstEntry();
|
|
while (drop_zone)
|
|
{
|
|
Point3D translation;
|
|
EulerAngles tmp_rotation;
|
|
|
|
const char* zone_data = drop_zone->GetChar();
|
|
sscanf(
|
|
zone_data,
|
|
"%f %f %f %f %f %f",
|
|
&translation.x,
|
|
&translation.y,
|
|
&translation.z,
|
|
&tmp_rotation.pitch,
|
|
&tmp_rotation.yaw,
|
|
&tmp_rotation.roll
|
|
);
|
|
|
|
((Origin*)stream.GetPointer())->linearPosition = translation;
|
|
((Origin*)stream.GetPointer())->angularPosition = tmp_rotation;
|
|
stream.AdvancePointer(sizeof(Origin));
|
|
drop_zone = drop_zone->GetNextEntry();
|
|
}
|
|
|
|
Unregister_Object(zone_list);
|
|
delete zone_list;
|
|
map_file->PutModes(save_modes);
|
|
|
|
return True;
|
|
}
|
|
|
|
Logical
|
|
DropZone::TestInstance() const
|
|
{
|
|
return IsDerivedFrom(*GetClassDerivations());
|
|
}
|