thug/Code/Sk/ParkEditor2/ParkGen.cpp

2960 lines
87 KiB
C++
Raw Normal View History

2016-02-13 21:39:12 +00:00
#include <core/defines.h>
#include <sk/ParkEditor2/ParkGen.h>
#include <sk/ParkEditor2/EdMap.h> // Mick: Added as we need to iterate over the ConcreteMetaPieces
#include <core/math.h>
#include <gel/collision/collision.h>
#include <gel/collision/colltridata.h>
#include <gel/scripting/script.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/symboltable.h>
#include <gel/scripting/struct.h>
#include <gel/scripting/array.h>
#include <sk/scripting/nodearray.h>
#include <sk/scripting/cfuncs.h>
#include <sk/scripting/gs_file.h>
#include <sk/gamenet/gamenet.h>
#include <sys/replay/replay.h> // Needed to get the size of the replay buffer, for adjusting available memory
#include <gel/object/compositeobjectmanager.h>
#include <sk/components/raileditorcomponent.h>
#include <gfx/nx.h>
#include <gfx/NxSector.h>
#include <gfx/NxGeom.h>
#include <gfx/NxScene.h>
#include <sk/heap_sizes.h>
//#define DEBUG_RESTARTS
#ifdef __PLAT_NGC__
#include <gfx/ngc/p_nxscene.h>
#endif // __PLAT_NGC__
#ifdef __PLAT_NGPS__
#define PRINT_RAW_RESULTS 0
#if PRINT_RAW_RESULTS
#include <core/crc.h>
#include <gfx/ngps/p_nxgeom.h>
#include <gfx/ngps/nx/geomnode.h>
#include <gfx/ngps/nx/dma.h>
namespace Nx
{
extern void find_geom_leaves( NxPs2::CGeomNode *p_node, NxPs2::CGeomNode **leaf_array, int & num_nodes);
}
#endif // PRINT_RAW_RESULTS
#endif
namespace Ed
{
const float CParkGenerator::CELL_WIDTH = 120.0f;
const float CParkGenerator::CELL_HEIGHT = 48.0f;
const float CParkGenerator::CELL_LENGTH = 120.0f;
// the first range of ids are used for regular objects and object nodes
// the second for rail nodes
const uint32 CParkGenerator::vFIRST_ID_FOR_OBJECTS = 2000;
const uint32 CParkGenerator::vMAX_ID_FOR_OBJECTS = 2000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_RAILS = 2000001;
const uint32 CParkGenerator::vMAX_ID_FOR_RAILS = 4000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_CREATED_RAILS = 4000001;
const uint32 CParkGenerator::vMAX_ID_FOR_CREATED_RAILS = 6000000;
const uint32 CParkGenerator::vFIRST_ID_FOR_RESTARTS = 6000001;
CPiece::CPiece()
{
m_flags = EFlags(0);
//mp_sector = NULL;
m_sector_checksum = 0;
mp_next_in_list = NULL;
}
CPiece::~CPiece()
{
}
void CPiece::GetCellDims(GridDims *pDims)
{
Mth::Vector dims = GetDims();
pDims->SetWHL((uint8) ((dims.GetX() + 2.0f) / CParkGenerator::CELL_WIDTH),
(uint8) ((dims.GetY() + 2.0f) / CParkGenerator::CELL_HEIGHT),
(uint8) ((dims.GetZ() + 2.0f) / CParkGenerator::CELL_LENGTH));
/*
if (m_sector_checksum == Script::GenerateCRC("Sk3Ed_MTra_02"))
{
Ryan("Sk3Ed_MTra_02 cells dims are (%d,%d,%d), world (%.4f,%.4f,%.4f)\n",
pDims->GetW(), pDims->GetH(), pDims->GetL(),
dims.GetX(), dims.GetY(), dims.GetZ());
}
*/
}
CSourcePiece *CPiece::CastToCSourcePiece()
{
Dbg_MsgAssert(this == NULL || (m_flags & mSOURCE_PIECE), ("not a source piece!"));
return (CSourcePiece *) this;
}
CClonedPiece *CPiece::CastToCClonedPiece()
{
Dbg_MsgAssert(this == NULL || (m_flags & mCLONED_PIECE), ("not a cloned piece!"));
return (CClonedPiece *) this;
}
Mth::Vector CPiece::GetDimsWithRot(Mth::ERot90 rot)
{
CSourcePiece *p_source_piece;
if (m_flags & mSOURCE_PIECE)
p_source_piece = CastToCSourcePiece();
else
p_source_piece = CastToCClonedPiece()->GetSourcePiece();
Dbg_Assert(p_source_piece);
Mth::Vector result;
if (rot & 1)
{
result[X] = p_source_piece->GetDims().GetZ();
result[Z] = p_source_piece->GetDims().GetX();
}
else
{
result[X] = p_source_piece->GetDims().GetX();
result[Z] = p_source_piece->GetDims().GetZ();
}
result[Y] = p_source_piece->GetDims().GetY();
return result;
}
CSourcePiece::CSourcePiece()
{
set_flag(CPiece::mSOURCE_PIECE);
m_domain = mREGULAR;
m_triggerScriptId = 0;
}
CSourcePiece::~CSourcePiece()
{
//printf("killed CSourcePiece\n");
}
uint32 CSourcePiece::GetType()
{
return m_sector_checksum;
}
CClonedPiece::CClonedPiece()
{
m_id = 0;
set_flag(CPiece::mCLONED_PIECE);
mp_source_piece = NULL;
}
uint32 CClonedPiece::GetID()
{
return m_id;
}
/*
Instant function. Piece arrives at new location right away.
*/
void CClonedPiece::SetDesiredPos(const Mth::Vector & pos, ESectorEffect sectorEffect)
{
//Dbg_MsgAssert(!(m_flags & CPiece::mIN_WORLD), ("can't change position, piece already in world"));
m_pos = pos;
if (sectorEffect == CHANGE_SECTOR)
{
#if 0
//printf("cloned piece at (%.2f,%.2f,%.2f), dims=(%.2f,%.2f,%.2f)\n",
//if (mp_source_piece->GetType() == Script::GenerateCRC("Sk3Ed_Rd1b_10x10x4"))
// printf(" cloned piece at (%.3f,%.3f,%.3f), dims=(%.3f,%.3f,%.3f)\n",
// pos.GetX(), pos.GetY(), pos.GetZ(),
// mp_source_piece->m_dims.GetX(), mp_source_piece->m_dims.GetY(), mp_source_piece->m_dims.GetZ());
#endif
/*
if (pos.GetX() == -1140.0f && pos.GetZ() == -1140.0f)
printf(" add (%f,%f,%f), sector 0x%x\n",
pos.GetX(), pos.GetY(), pos.GetZ(), m_sector_checksum);
*/
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
p_sector->SetWorldPosition(pos);
}
}
/*
Instant function. Piece arrives at new rotation right away.
*/
void CClonedPiece::SetDesiredRot(Mth::ERot90 rot, ESectorEffect sectorEffect)
{
//Dbg_MsgAssert(!(m_flags & CPiece::mIN_WORLD), ("can't change rotation, piece already in world"));
m_rot = rot;
if (sectorEffect == CHANGE_SECTOR)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
//Dbg_Message("Rotating piece with to %d", (int) rot * 90);
p_sector->SetYRotation(rot);
}
}
void CClonedPiece::SetSoftRot(float rot)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
Nx::CGeom *p_geom = p_sector->GetGeom();
if (p_geom)
{
Mth::Matrix rot_mat;
rot_mat.Ident();
rot_mat.RotateY(Mth::DegToRad(rot));
p_geom->SetOrientation(rot_mat);
}
}
void CClonedPiece::SetScaleX(float scaleX)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
Mth::Vector scale_vect;
if (m_rot & 1)
{
scale_vect[X] = 1.0f;
scale_vect[Z] = scaleX;
}
else
{
scale_vect[X] = scaleX;
scale_vect[Z] = 1.0f;
}
scale_vect[Y] = 1.0f;
p_sector->SetScale(scale_vect);
}
// Turn on or off the highlight effect for the geometry that makes up a cloned piece
void CClonedPiece::Highlight(bool on, bool makePurple)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
if (!p_sector)
{
return;
}
//Dbg_Assert(p_sector);
Nx::CGeom *p_geom = p_sector->GetGeom();
if (p_geom)
{
// This effect is somewhat arbitary. I just want to get SOME effect
if (on)
{
if (makePurple)
p_geom->SetColor(Image::RGBA(160,100,160,0));
else
p_geom->SetColor(Image::RGBA(160,100,100,0));
//Ryan("set color for sector=%p\n", p_sector);
}
else
{
// ClearColor flags the geom as being NOT COLORED, rather than setting the color to neutral.
p_geom->ClearColor();
//Ryan("clear color for sector=%p\n", p_sector);
}
}
}
void CClonedPiece::SetActive(bool active)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
p_sector->SetActive(active);
}
void CClonedPiece::SetVisibility(bool visible)
{
Nx::CSector *p_sector = Nx::CEngine::sGetSector(m_sector_checksum);
Dbg_Assert(p_sector);
uint32 mask = visible ? 0xFF : 0;
p_sector->SetVisibility(mask);
}
Mth::Vector CClonedPiece::GetDims()
{
Dbg_Assert(mp_source_piece);
Mth::Vector result;
if (m_rot & 1)
{
result[X] = mp_source_piece->m_dims.GetZ();
result[Z] = mp_source_piece->m_dims.GetX();
}
else
{
result[X] = mp_source_piece->m_dims.GetX();
result[Z] = mp_source_piece->m_dims.GetZ();
}
result[Y] = mp_source_piece->m_dims.GetY();
return result;
}
CParkGenerator::CParkGenerator()
{
m_flags = EFlags(0);
mp_cloned_piece_list = NULL;
mp_source_piece_list = NULL;
mp_cloned_scene = NULL;
mp_source_scene = NULL;
m_num_cloned_pieces = 0;
m_num_source_pieces = 0;
m_next_id = 32;
mp_mem_region = NULL;
mp_mem_heap = NULL;
m_mem_usage_info.mMainHeapFree = 0;
m_mem_usage_info.mMainHeapUsed = 0;
m_mem_usage_info.mParkHeapFree = 0;
m_mem_usage_info.mParkHeapUsed = 0;
m_mem_usage_info.mLastMainUsed = 0;
m_total_rail_points = 0;
m_total_rail_linked_points = 0;
m_max_players=2;
}
CParkGenerator::~CParkGenerator()
{
// Clean up scenes (do this last)
if (mp_cloned_scene)
{
delete mp_cloned_scene;
}
if (mp_source_scene)
{
delete mp_source_scene;
}
}
int CParkGenerator::GetResourceSize(char *name)
{
Script::CStruct *p_resource_struct = Script::GetStructure("Ed_Resources_Info");
int size;
if (strcmp(name, "main_heap_base") == 0)
{
Script::CArray *p_values;
p_resource_struct->GetArray(name, &p_values);
#ifdef __PLAT_NGPS__
size = p_values->GetInteger(0);
#else
#ifdef __PLAT_NGC__
size = 4654800+((2 * 1024 * 1024)-900000); //p_values->GetInteger(1);
#else
size = p_values->GetInteger(2);
#endif
#endif
}
else if(strcmp(name, "main_heap_pad") == 0)
{
Script::CArray *p_values;
p_resource_struct->GetArray(name, &p_values);
# if defined( __PLAT_NGPS__ )
size = p_values->GetInteger(0);
# elif defined( __PLAT_NGC__ )
size = -2294188; //p_values->GetInteger(1);
# else
size = p_values->GetInteger(2);
# endif
}
else if (strcmp(name, "theme_pad") == 0)
{
int theme=CParkManager::sInstance()->GetTheme();
#ifdef __PLAT_NGC__
int theme_size[5] =
{
0,
179296,
822784,
482944,
444544
};
size = theme_size[theme];
#else
#ifdef __PLAT_XBOX__
int theme_size[5] =
{
0,
253728,
564688,
161344,
441856
};
size = theme_size[theme];
#else
Script::CArray *p_values;
p_resource_struct->GetArray(name, &p_values);
size = p_values->GetInteger(theme);
#endif
#endif // __PLAT_NGC__
}
#ifdef __PLAT_NGC__
else if(strcmp(name, "floor_piece_size_main") == 0)
{
size = 1000;
}
#endif
else
{
if (!p_resource_struct->GetInteger(name, &size))
{
Dbg_MsgAssert(0, ("couldn't get resource size '%s'", name));
return 0;
}
}
return size;
}
Mem::Heap *CParkGenerator::GetParkEditorHeap()
{
Dbg_MsgAssert(mp_mem_heap, ("no park editor heap"));
return mp_mem_heap;
}
void CParkGenerator::SetMaxPlayers(int maxPlayers)
{
Dbg_MsgAssert(maxPlayers>1 && maxPlayers<=GameNet::vMAX_PLAYERS,("Bad maxPlayers of %d",maxPlayers));
m_max_players=maxPlayers;
}
CParkGenerator::MemUsageInfo CParkGenerator::GetResourceUsageInfo(bool printInfo)
{
Dbg_Assert(mp_mem_heap);
Dbg_Assert(this);
//int base_park_heap = 0;
//int max_base_park_heap = 0;
//int padding_size = GetResourceSize("park_heap_pad");
//int used_park_heap = mp_mem_heap->mUsedMem.m_count;
//int free_park_heap = mp_mem_heap->mFreeMem.m_count + mp_mem_region->MemAvailable();
// allowed to be used, that is
//int allowed_park_heap_mem = free_park_heap + used_park_heap - padding_size;
//float park_heap_pct = (float) (used_park_heap - base_park_heap) / (float) (allowed_park_heap_mem - max_base_park_heap);
//float actual_park_heap_pct = (float) (used_park_heap) / (float) (allowed_park_heap_mem);
/*
if (largest_free_block < padding_size)
{
// there is no guarantee we can safetly fit the next piece
// placed, so make the memory meter full
park_heap_pct = actual_park_heap_pct = 1.0001f;
}
*/
Mem::Heap *p_main_heap = Mem::Manager::sHandle().BottomUpHeap();
Mem::Region *p_main_region = p_main_heap->mp_region;
Dbg_Assert(p_main_region);
if (m_mem_usage_info.mMainHeapUsed != p_main_heap->mUsedMem.m_count)
{
m_mem_usage_info.mLastMainUsed = m_mem_usage_info.mMainHeapUsed;
}
// Mick, the replay buffer is allocated in the park editor
// but we want to ignore it in our calculations
// so we just subtract it from the main_heap_pad
#ifdef __PLAT_NGC__
int main_padding_size = GetResourceSize("main_heap_pad");
#else
#if __USE_REPLAYS__
int main_padding_size = GetResourceSize("main_heap_pad") - Replay::GetBufferSize();
#else
int main_padding_size = GetResourceSize("main_heap_pad");
#endif
#endif
main_padding_size+=(m_max_players-1)*(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE);
int total_mem_available=p_main_heap->mFreeMem.m_count + p_main_region->MemAvailable();
if (main_padding_size > total_mem_available)
{
//Dbg_MsgAssert(0,("main_padding_size > total_mem_available !! (%d > %d)",main_padding_size,total_mem_available));
#ifdef __NOPT_ASSERT__
//printf("main_padding_size > total_mem_available !! (%d > %d)\n",main_padding_size,total_mem_available);
#endif
//ParkEd("main_padding_size > total_mem_available !! (%d > %d)\n",main_padding_size,total_mem_available);
}
m_mem_usage_info.mMainHeapUsed = p_main_heap->mUsedMem.m_count;
m_mem_usage_info.mMainHeapFree = p_main_heap->mFreeMem.m_count + p_main_region->MemAvailable() - main_padding_size;
//printf("p_main_heap->mFreeMem.m_count=%d\np_main_region->MemAvailable()=%d\nmain_padding_size=%d\n",p_main_heap->mFreeMem.m_count,p_main_region->MemAvailable(),main_padding_size);
int park_padding_size = GetResourceSize("park_heap_pad");
m_mem_usage_info.mParkHeapUsed = mp_mem_heap->mUsedMem.m_count;
m_mem_usage_info.mParkHeapFree = mp_mem_heap->mFreeMem.m_count + mp_mem_region->MemAvailable() - park_padding_size;
if (p_main_heap->LargestFreeBlock() < m_mem_usage_info.mMainHeapFree && // heap definitely fragmented
p_main_heap->LargestFreeBlock() < main_padding_size && // no block big enough for single piece
m_mem_usage_info.mMainHeapFree >= main_padding_size) // there is enough memory for single piece
{
// XXX
Ryan("===================== DEFRAG ===========================\n");
Ryan("largest: %d, free: %d, padding %d\n",
p_main_heap->LargestFreeBlock(), m_mem_usage_info.mMainHeapFree, main_padding_size);
Ryan("========================================================\n");
m_mem_usage_info.mIsFragmented = true;
int false_free_amt = m_mem_usage_info.mMainHeapFree - p_main_heap->LargestFreeBlock();
m_mem_usage_info.mMainHeapFree -= false_free_amt;
m_mem_usage_info.mMainHeapUsed += false_free_amt;
}
else
m_mem_usage_info.mIsFragmented = false;
#if 0
if ( p_main_heap->mFreeMem.m_count > 200000)
{
m_mem_usage_info.mIsFragmented = true;
}
#endif
m_mem_usage_info.mTotalClonedPieces = m_num_cloned_pieces;
m_mem_usage_info.mTotalRailPoints = m_total_rail_points;
m_mem_usage_info.mTotalLinkedRailPoints = m_total_rail_linked_points;
// subtract theme specific padding
if (CParkManager::sInstance()->GetTheme() > 0)
{
m_mem_usage_info.mMainHeapFree -= GetResourceSize("theme_pad");
}
if (printInfo)
{
Ryan("=====================================================\nParkGen Usage Stats:\n\n");
Ryan("largest free block: %d\n", mp_mem_heap->LargestFreeBlock());
Ryan("used park heap: %d\n", m_mem_usage_info.mParkHeapUsed);
Ryan("free park heap: %d\n", m_mem_usage_info.mParkHeapFree);
Ryan("main heap used: %d\n", m_mem_usage_info.mMainHeapUsed);
Ryan("main heap available: %d\n", m_mem_usage_info.mMainHeapFree);
}
return m_mem_usage_info;
}
int CParkGenerator::GetMaxPlayersPossible()
{
CParkGenerator::MemUsageInfo usage_info = GetResourceUsageInfo();
// GetResourceUsageInfo has already subtracted the memory used by the currently set max players,
// so add that back in so that the max max can be calculated.
usage_info.mMainHeapFree += (m_max_players-1)*(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE);
// The +1 is because at least 1 skater is assumed.
return usage_info.mMainHeapFree/(SKATER_HEAP_SIZE+SKATER_GEOM_HEAP_SIZE)+1;
}
/*
Given checksum of piece name, returns pointer to source piece.
*/
CSourcePiece *CParkGenerator::GetMasterPiece(uint32 pieceType, bool assert)
{
CPiece *p_piece = mp_source_piece_list;
while(p_piece)
{
if (p_piece->CastToCSourcePiece()->m_sector_checksum == pieceType)
return p_piece->CastToCSourcePiece();
p_piece = p_piece->mp_next_in_list;
}
if (assert)
{
Dbg_MsgAssert(0, ("couldn't not find master piece %s", Script::FindChecksumName(pieceType)));
}
else
printf("couldn't not find master piece %s\n", Script::FindChecksumName(pieceType));
return NULL;
}
CSourcePiece *CParkGenerator::GetNextMasterPiece(CSourcePiece *pLast)
{
if (!pLast)
return mp_source_piece_list->CastToCSourcePiece();
else
return pLast->mp_next_in_list->CastToCSourcePiece();
}
/*
Makes a copy of a source piece. The copy can then be added to a metapiece, or to the world.
A soft piece can be smoothly rotated, like a piece used in a cursor.
*/
CClonedPiece *CParkGenerator::ClonePiece(CPiece *pMasterPiece, CPiece::EFlags flags)
{
// make sure flags make sense
flags = CPiece::EFlags(flags & (CPiece::mSOFT_PIECE | CPiece::mFLOOR | CPiece::mNO_RAILS));
Dbg_Assert(pMasterPiece);
CSourcePiece *pSource;
if (pMasterPiece->m_flags & CPiece::mSOURCE_PIECE)
pSource = pMasterPiece->CastToCSourcePiece();
else
pSource = pMasterPiece->CastToCClonedPiece()->mp_source_piece;
Dbg_Assert(pSource);
Dbg_Assert(mp_source_scene);
Dbg_Assert(mp_cloned_scene);
// putting cloned sectors on main heap seems best right now
//Mem::Manager::sHandle().PushContext(mp_mem_heap);
uint32 new_sector_checksum;
if (flags & CPiece::mSOFT_PIECE)
{
// create fresh soft piece (hmm, that sounds kind of disgusting)
new_sector_checksum = mp_source_scene->CloneSector(pSource->m_sector_checksum, mp_cloned_scene, true, false); // no instance for now (should be instance)
//Dbg_Message("************************* New soft checksum %x", new_sector_checksum);
}
else
{
// create fresh hard piece (no, this sounds worse!)
new_sector_checksum = mp_source_scene->CloneSector(pSource->m_sector_checksum, mp_cloned_scene, false, true); // no instance
if (!(flags & CPiece::mNO_RAILS))
{
m_total_rail_points += pSource->m_num_rail_points;
m_total_rail_linked_points += pSource->m_num_linked_rail_points;
}
}
//Mem::Manager::sHandle().PopContext();
// Create cloned piece, will soon live in world or as animated piece
Mem::Manager::sHandle().PushContext(mp_mem_heap);
CClonedPiece *p_cloned_piece = new CClonedPiece();
Mem::Manager::sHandle().PopContext();
p_cloned_piece->m_sector_checksum = new_sector_checksum;
p_cloned_piece->m_id = m_next_id++;
p_cloned_piece->mp_source_piece = pSource;
p_cloned_piece->SetDesiredRot(Mth::ERot90(0), CClonedPiece::MARKER_ONLY);
m_num_cloned_pieces++;
p_cloned_piece->set_flag(flags);
set_flag(mSECTORS_GENERATED);
return p_cloned_piece;
}
/*
Must be called to finalized cloned piece's presence in world.
*/
void CParkGenerator::AddClonedPieceToWorld(CClonedPiece *pPiece)
{
Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mIN_WORLD), ("piece already in world"));
pPiece->set_flag(CPiece::mIN_WORLD);
pPiece->mp_next_in_list = mp_cloned_piece_list;
mp_cloned_piece_list = pPiece;
set_flag(mPIECES_IN_WORLD);
}
void CParkGenerator::DestroyClonedPiece(CClonedPiece *pPiece)
{
destroy_piece_impl(pPiece, DESTROY_PIECES_AND_SECTORS);
}
#ifdef __PLAT_NGPS__
#if PRINT_RAW_RESULTS
void print_ps2_data(NxPs2::CGeomNode *p_node)
{
uint8 *p_dma = p_node->GetDma();
int num_verts = NxPs2::dma::GetNumVertices(p_dma);
if( num_verts >= 3 )
{
bool short_xyz = (NxPs2::dma::GetBitLengthXYZ(p_dma) == 16);
Mth::Vector mesh_center = p_node->GetBoundingSphere();
// Get DMA arrays
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
sint32 *p_vert_array = new sint32[4 * num_verts];
sint32 *p_uv_array = new sint32[2 * num_verts];
uint32 *p_rgb_array = new uint32[num_verts];
Mem::Manager::sHandle().PopContext();
// Copy DMA data
NxPs2::dma::ExtractXYZs(p_dma, (uint8 *) p_vert_array);
NxPs2::dma::ExtractSTs(p_dma, (uint8 *) p_uv_array);
NxPs2::dma::ExtractRGBAs(p_dma, (uint8 *) p_rgb_array);
Mth::Vector v0;
float u, v;
Image::RGBA col;
Dbg_Message("Number of verts: %d", num_verts);
for (int idx = 0; idx < num_verts; idx++)
{
if (short_xyz)
{
NxPs2::dma::ConvertXYZToFloat(v0, &(p_vert_array[idx * 4]), mesh_center);
} else {
NxPs2::dma::ConvertXYZToFloat(v0, &(p_vert_array[idx * 4]));
}
NxPs2::dma::ConvertSTToFloat(u, v, &(p_uv_array[idx * 2]));
col = *((Image::RGBA *) &(p_rgb_array[idx]));
Dbg_Message("Vert %d", idx);
Dbg_Message("Raw Pos (%x, %x, %x, %x)", p_vert_array[idx * 4 + 0], p_vert_array[idx * 4 + 1], p_vert_array[idx * 4 + 2], p_vert_array[idx * 4 + 3]);
Dbg_Message("Pos (%f, %f, %f, %f)", v0[X], v0[Y], v0[Z], v0[W]);
Dbg_Message("UV (%f, %f)", u, v);
Dbg_Message("Color (%d, %d, %d, %d)", col.r, col.g, col.b, col.a);
}
// Free DMA buffers
delete [] p_vert_array;
delete [] p_uv_array;
delete [] p_rgb_array;
}
}
#endif // PRINT_RAW_RESULTS
#endif
/*
Creates the set of source pieces. The data will have been loaded by this point.
*/
void CParkGenerator::InitializeMasterPieces(int parkW, int parkH, int parkL, int theme)
{
if (flag_on(mMASTER_STUFF_LOADED))
UnloadMasterPieces();
set_flag(mMASTER_STUFF_LOADED);
// When we reload the master piece, we reset the id of the generate pieces
// so that net games will be syncronized
m_next_id = 32;
// set up park editor heap
Dbg_MsgAssert(!mp_mem_region && !mp_mem_heap,( "park editor heap already exists"));
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
#ifdef __PLAT_NGC__
// Added 200K to the TOP_DOWN_REQUIRED_MARGIN for safety, and cancelled that by removing
// 200K from the park editor heap. (Normally 900K)
mp_mem_region = new Mem::AllocRegion(700000);
#else
mp_mem_region = new Mem::AllocRegion(GetResourceSize("heap"));
#endif
Mem::Manager::sHandle().PopContext();
Ryan(" allocated park editor region at %p\n", mp_mem_region);
mp_mem_heap = Mem::Manager::sHandle().CreateHeap( mp_mem_region, Mem::Allocator::vBOTTOM_UP, "Park Editor" );
// fetch scene
const char *p_scene_name="sk5ed";
mp_source_scene = Nx::CEngine::sGetScene(p_scene_name);
// set up all the source pieces
Dbg_MsgAssert(mp_source_scene, ("couldn't get scene %s", p_scene_name));
Lst::HashTable<Nx::CSector> *p_sector_list = mp_source_scene->GetSectorList();
Nx::CSector *p_sector;
// printf("Setting up park editor source pieces:\n");
p_sector_list->IterateStart();
while ((p_sector = p_sector_list->IterateNext()))
{
Mem::Manager::sHandle().PushContext(mp_mem_heap);
CSourcePiece *p_new_piece = new CSourcePiece();
Mem::Manager::sHandle().PopContext();
p_new_piece->mp_next_in_list = mp_source_piece_list;
mp_source_piece_list = p_new_piece;
//printf ("Piece with checksum 0x%x\n",p_sector->GetChecksum());
#ifdef __PLAT_NGPS__
#if PRINT_RAW_RESULTS
if (p_sector->GetChecksum() == Crc::GenerateCRCFromString("Sk3Ed_F_10x10"))
{
Dbg_Message("******* Found the target piece");
Mth::CBBox piece_bbox(p_sector->GetBoundingBox());
Dbg_Message("Bounding Box (%.8f, %.8f, %.8f) - (%.8f, %.8f, %.8f)",
piece_bbox.GetMin()[X], piece_bbox.GetMin()[Y], piece_bbox.GetMin()[Z],
piece_bbox.GetMax()[X], piece_bbox.GetMax()[Y], piece_bbox.GetMax()[Z]);
Nx::CPs2Geom *p_geom = static_cast<Nx::CPs2Geom *>(p_sector->GetGeom());
NxPs2::CGeomNode *p_geom_node = p_geom->GetEngineObject();
Dbg_Assert(p_geom_node);
// Put all the meshes in an array
int num_meshes = 0;
NxPs2::CGeomNode *mesh_array[10];
Nx::find_geom_leaves(p_geom_node, mesh_array, num_meshes);
Dbg_Assert(num_meshes == 1);
p_geom_node = mesh_array[0];
print_ps2_data(p_geom_node);
}
#endif
#endif
p_new_piece->m_sector_checksum = p_sector->GetChecksum();
// Init dimensions
Mth::CBBox piece_bbox(p_sector->GetBoundingBox());
int cell_w = (int) ((piece_bbox.GetMax()[X] - piece_bbox.GetMin()[X] + 2.0f) / CELL_WIDTH);
if (cell_w < 1) cell_w = 1;
p_new_piece->m_dims[X] = cell_w * CELL_WIDTH;
int cell_l = (int) ((piece_bbox.GetMax()[Z] - piece_bbox.GetMin()[Z] + 2.0f) / CELL_LENGTH);
if (cell_l < 1) cell_l = 1;
p_new_piece->m_dims[Z] = cell_l * CELL_LENGTH;
p_new_piece->m_dims[Y] = (piece_bbox.GetMax()[Y] - piece_bbox.GetMin()[Y]);
// piece cannot have any dimension less the that of a park editor cell
if (p_new_piece->m_dims[Y] < CParkGenerator::CELL_HEIGHT && p_new_piece->m_dims[Y] > 0.0f)
p_new_piece->m_dims[Y] = CParkGenerator::CELL_HEIGHT;
m_num_source_pieces++;
#if 0
//printf(" %s\n [%f,%f,%f]-[%f,%f,%f]\n", Script::FindChecksumName(p_new_piece->m_sector_checksum),
// piece_bbox.GetMin()[X], piece_bbox.GetMin()[Y], piece_bbox.GetMin()[Z],
// piece_bbox.GetMax()[X], piece_bbox.GetMax()[Y], piece_bbox.GetMax()[Z]);
printf(" %s [%.2f,%.2f,%.2f]\n", Script::FindChecksumName(p_new_piece->m_sector_checksum),
p_new_piece->m_dims[X], p_new_piece->m_dims[Y], p_new_piece->m_dims[Z]);
#endif
}
mp_cloned_scene = Nx::CEngine::sCreateScene("cloned", mp_source_scene->GetTexDict(), true);
#ifdef __PLAT_NGC__
// Need to copy scene data here, so stuff added to the cloned scene has geometry pools.
Nx::CNgcScene *p_Ngc_scene = static_cast<Nx::CNgcScene*>( mp_cloned_scene );
Nx::CNgcScene *p_source_scene = static_cast<Nx::CNgcScene*>( mp_source_scene );
NxNgc::sScene *p_scene = p_Ngc_scene->GetEngineScene();
memcpy( p_scene, p_source_scene->GetEngineScene(), sizeof( NxNgc::sScene ) );
p_scene->m_num_meshes = 0; // No meshes as yet.
p_scene->m_num_filled_meshes = 0;
p_scene->mpp_mesh_list = NULL;
p_scene->mp_hierarchyObjects = NULL;
p_scene->m_numHierarchyObjects = 0;
p_scene->mp_hierarchyObjects = NULL;
p_scene->m_numHierarchyObjects = 0;
// Make it renderable, and make sure it doesn't try to double-delete the scene data.
p_scene->m_is_dictionary = false;
p_scene->m_flags = SCENE_FLAG_CLONED_GEOM;
#endif // __PLAT_NGC__
Mth::CBBox b_box;
Mth::Vector vmin(-((float) parkW * CELL_WIDTH) / 2.0f, -((float) parkH * CELL_HEIGHT) / 2.0f, -((float) parkL * CELL_LENGTH) / 2.0f);
b_box.AddPoint(vmin);
Mth::Vector vmax(((float) parkW * CELL_WIDTH) / 2.0f, ((float) parkH * CELL_HEIGHT) / 2.0f, ((float) parkL * CELL_LENGTH) / 2.0f);
b_box.AddPoint(vmax);
mp_cloned_scene->CreateCollision(b_box);
KillRestarts();
}
/*
// K: Added to allow cleanup of the park editor heap during play
void CParkGenerator::DeleteSourceAndClonedPieces()
{
CPiece *p_piece = mp_source_piece_list;
while (p_piece)
{
CPiece *p_next = p_piece->mp_next_in_list;
delete p_piece;
p_piece = p_next;
}
mp_source_piece_list = NULL;
m_num_source_pieces=0;
printf("After deleting CParkGenerator::mp_source_piece_list ...\n");
printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
p_piece = mp_cloned_piece_list;
while(p_piece)
{
CPiece *p_next = p_piece->mp_next_in_list;
delete p_piece;
p_piece = p_next;
}
m_num_cloned_pieces=0;
mp_cloned_piece_list=NULL;
printf("After deleting CParkGenerator::mp_cloned_piece_list ...\n");
printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems());
MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap());
}
*/
/*
Destroys the set of source pieces.
*/
void CParkGenerator::UnloadMasterPieces()
{
if (flag_on(mPIECES_IN_WORLD))
DestroyAllClonedPieces(DESTROY_PIECES_AND_SECTORS);
// GARRETT: not sure if you need to do anything here
CPiece *p_piece = mp_source_piece_list;
while (p_piece)
{
CPiece *p_next = p_piece->mp_next_in_list;
delete p_piece;
p_piece = p_next;
}
mp_source_piece_list = NULL;
m_num_source_pieces--;
//m_num_source_pieces=0;
clear_flag(mMASTER_STUFF_LOADED);
Mem::Manager::sHandle().RemoveHeap(mp_mem_heap);
delete mp_mem_region;
mp_mem_heap = NULL;
mp_mem_region = NULL;
}
/*
void CParkGenerator::DeleteParkEditorHeap()
{
if (mp_mem_heap)
{
Mem::Manager::sHandle().RemoveHeap(mp_mem_heap);
delete mp_mem_region;
mp_mem_heap = NULL;
mp_mem_region = NULL;
}
}
*/
/*
Should be called after adding/removing sectors
*/
void CParkGenerator::PostGenerate()
{
Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
}
/*
Should be called after adding a group of cloned pieces to world.
*/
void CParkGenerator::GenerateCollisionInfo(bool assert)
{
// if this flag IS on, then we need to call UpdateSuperSectors() to free the memory
if (flag_on(mSECTOR_MEMORY_NEEDS_FREE))
{
}
else if (!assert)
{
if (!flag_on(mPIECES_IN_WORLD) || !flag_on(mSECTORS_GENERATED))
return;
}
else
Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
// GARRETT
Dbg_Assert(mp_cloned_scene);
mp_cloned_scene->UpdateSuperSectors();
//Dbg_Message("Adding collision to scene %x", mp_cloned_scene->GetID());
// XXX
//printf("there are %d source pieces, %d cloned pieces\n", m_num_source_pieces, m_num_cloned_pieces);
set_flag(mCOLLISION_INFO_GENERATED);
clear_flag(mSECTOR_MEMORY_NEEDS_FREE);
}
void CParkGenerator::RemoveOuterShellPieces(int theme)
{
uint32 shell_id=CParkManager::Instance()->GetShellSceneID();
Nx::CScene *p_scene = Nx::CEngine::sGetScene(shell_id);
if (p_scene)
{
// Turn everything on simply by using a huge bounding box
Mth::CBBox a(Mth::Vector(-200000,-200000,-200000),Mth::Vector(200000,200000,200000));
p_scene->SetActiveInBox(a,true,0.0f);
// Calculate the bounding box of the park up to the edges
GridDims fence;
fence = CParkManager::Instance()->GetParkNearBounds(); // returns the rectangle enclosed by the fence
Mth::Vector start = CParkManager::Instance()->GridCoordinatesToWorld(fence); // top left cornder
fence.SetXYZ(fence.GetX()+fence.GetW()-1,fence.GetY(),fence.GetZ()+fence.GetL()-1);
Mth::Vector end = CParkManager::Instance()->GridCoordinatesToWorld(fence); // bot right corner, in one square
// offset into the park by half a piece
start[X] += CParkGenerator::CELL_WIDTH/2;
start[Z] += CParkGenerator::CELL_LENGTH/2;
end[X] += CParkGenerator::CELL_WIDTH/2;
end[Z] += CParkGenerator::CELL_LENGTH/2;
// The Y of the world box calculated above is not correct.... so just make it bigh enough to encompass everything
start[Y] = -100000;
end[Y] = 100000;
Mth::CBBox b(start,end);
// Note: the above bounding box is based on the centers of cells
// so it misses a half cell boundry around the inside of the fence
// however, this is actually close to what I want than the true bounding box
// as I want to provide a little slack so we don't accidentally kill
// things that just come inside the fence by an inch or two.
// (remember the shell collision is not active)
// and turn off everything that intersects the bounding box we just calculated
p_scene->SetActiveInBox(b,false,0.0f);
}
else
{
Dbg_MsgAssert(0, ("Where is my scene?! %s", Script::FindChecksumName(shell_id)));
}
}
void CParkGenerator::CleanUpOutRailSet()
{
m_out_rail_set.Destroy();
m_out_rail_set.FreeAllocators();
}
// K: Factored this code out of GenerateNodeInfo because it is required by FindNearestRailPoint
void CParkGenerator::GenerateOutRailSet(CMapListNode * p_concrete_metapiece_list)
{
CleanUpOutRailSet();
Mdl::Skate * p_skate_mod = Mdl::Skate::Instance();
if (p_skate_mod->m_cur_level == CRCD(0xe8b4b836,"load_sk5ed"))
{
// K: Not using the top down heap whilst in the editor to avoid running out of memory,
// since this causes a 192K spike in memory usage when doing a test play.
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().FrontEndHeap());
}
else
{
Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap());
}
m_out_rail_set.SetupAllocators(GetResourceSize("out_railpoint_pool"), GetResourceSize("out_railstring_pool"), false);
Mem::Manager::sHandle().PopContext();
// Need to iterate over the pieces here, meaning we'd iterate over the meta pieces
// and then find the pieces they are composed of.....
// should be similar to rebuilding the park (in terms of iterating over the pieces)
// We have been passed in the list of concrete metapiece
// (the pieces that have actually been placed in the level
CMapListNode *p_node = p_concrete_metapiece_list;
while(p_node)
{
CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta();
if (p_meta->IsInPark() && !p_meta->NoRails())
{
// Iterate over the pieces in the metapiece
int pieces = p_meta->CountContainedPieces();
for (int i=0;i<pieces;i++)
{
CPiece * p_base_piece = p_meta->GetContainedPiece(i);
CClonedPiece *pPiece = p_base_piece->CastToCClonedPiece();
// if (!(pPiece->m_type & CPiece::vNO_RAILS))
{
// go through all rail strings associated with source object
for (int n = 0; ;n++)
{
uint32 piece_checksum = pPiece->mp_source_piece->m_sector_checksum;
RailString *pSrcString = m_in_rail_set.GetString(piece_checksum, n);
if (pSrcString)
{
// printf("creating rail string %d for %s, meta=%s\n",
// n, Script::FindChecksumName(piece_checksum), Script::FindChecksumName(p_meta->GetNameChecksum()));
RailString *pOutString = m_out_rail_set.CreateRailString();
uint32 trickob_id = pPiece->GetID();
if (p_meta->IsRiser())
{
// objects that are JUST risers cannot be trickobs
trickob_id = 0;
}
pOutString->CopyOffsetAndRot(pSrcString, pPiece->m_pos, pPiece->m_rot, trickob_id);
m_out_rail_set.AddString(pOutString);
}
else
{
//printf ("No rails here for %x: %s\n", piece_checksum, Script::FindChecksumName(piece_checksum));
break;
}
}
} // end if (!(pPiece->m_type & Piece::vNO_RAILS))
}
}
p_node = p_node->GetNext();
}
}
/*
For rail generation, etc.
*/
void CParkGenerator::GenerateNodeInfo(CMapListNode * p_concrete_metapiece_list)
{
Dbg_MsgAssert(flag_on(mPIECES_IN_WORLD) && flag_on(mSECTORS_GENERATED), ("no pieces or sectors available"));
int total_nodes = 0;
/*
========================================================
Begin Rail Generation
========================================================
*/
printf("Making rail objects\n");
GenerateOutRailSet(p_concrete_metapiece_list);
total_nodes += m_out_rail_set.CountPoints();
/*
========================================================
End Rail Generation
========================================================
*/
#if 1
printf("Generating node array\n");
// count up regular object nodes
int object_nodes = 0;
CPiece *p_piece = mp_cloned_piece_list;
while (p_piece)
{
if (!(p_piece->m_flags & CPiece::mFLOOR))
object_nodes++;
p_piece = p_piece->mp_next_in_list;
}
// count up object nodes
total_nodes += object_nodes;
#endif
#if 1
/*
========================================================
Do Some Restart Stuff
========================================================
*/
// count up restart nodes
for (int res = 0; res < vNUM_RESTARTS; res++)
{
if (m_restart_tab[res].type != vEMPTY)
total_nodes++;
// one player restarts are repeated as multiplayer
if (m_restart_tab[res].type == vONE_PLAYER)
total_nodes++;
// HORSE restarts are repeated as multiplayer
if (m_restart_tab[res].type == vHORSE)
total_nodes++;
// flag restart nodes will generate 3 extra nodes in addition to a restart
if (
m_restart_tab[res].type == vRED_FLAG
|| m_restart_tab[res].type == vGREEN_FLAG
|| m_restart_tab[res].type == vBLUE_FLAG
|| m_restart_tab[res].type == vYELLOW_FLAG
)
{
total_nodes += 3;
}
}
#endif
// Add in the number of nodes needed for the created rails.
int num_nodes_required=Obj::GetRailEditor()->GetTotalRailNodesRequired();
Dbg_MsgAssert((uint32)num_nodes_required <= vMAX_ID_FOR_CREATED_RAILS-vFIRST_ID_FOR_CREATED_RAILS+1,("Too many created-rail nodes"));
total_nodes+=num_nodes_required;
/*
========================================================
Actually Create Node Array
========================================================
*/
SkateScript::CreateNodeArray(total_nodes, false);
Script::CArray *pNodeArray = Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
int current_node_num = 0;
#if 1
set_up_object_nodes(pNodeArray, &current_node_num);
#endif
set_up_rail_nodes(pNodeArray, &current_node_num);
Obj::GetRailEditor()->SetUpRailNodes(pNodeArray, &current_node_num, vFIRST_ID_FOR_CREATED_RAILS);
#if 1
set_up_restart_nodes(pNodeArray, &current_node_num);
#endif
CleanUpOutRailSet();
printf("Calling CreateNodeNameHashTable()\n");
SkateScript::CreateNodeNameHashTable();
printf("Calling ScriptParseNodeArray()\n");
CFuncs::ScriptParseNodeArray(NULL, NULL);
// m_node_array_is_set_up = true;
set_flag(mNODE_INFO_GENERATED);
}
/*
Can use 'type' parameter only to remove only CPieces, but not underlying sector. This is useful
for freeing up memory no longer needed once park is generated.
*/
void CParkGenerator::DestroyAllClonedPieces(EDestroyType type)
{
if (type == DESTROY_PIECES_AND_SECTORS)
{
Obj::GetRailEditor()->DestroyRailGeometry();
Obj::GetRailEditor()->DestroyPostGeometry();
}
CPiece *p_entry = mp_cloned_piece_list;
while(p_entry)
{
CPiece *p_next_entry = p_entry->mp_next_in_list;
destroy_piece_impl(p_entry->CastToCClonedPiece(), type);
p_entry = p_next_entry;
}
if (type == DESTROY_PIECES_AND_SECTORS)
clear_flag(mSECTORS_GENERATED);
clear_flag(mPIECES_IN_WORLD);
if (type == DESTROY_PIECES_AND_SECTORS)
{
// CHUNKY
Dbg_Assert(mp_cloned_scene);
mp_cloned_scene->ClearSuperSectors();
}
}
void CParkGenerator::HighlightAllPieces(bool highlight)
{
CPiece *p_entry = mp_cloned_piece_list;
while(p_entry)
{
CPiece *p_next_entry = p_entry->mp_next_in_list;
p_entry->CastToCClonedPiece()->Highlight(highlight);
p_entry = p_next_entry;
}
}
void CParkGenerator::SetGapPiecesVisible(bool visible)
{
#if 1
// Nothing doing here yet, eventually will turn off the wireframe pieces
CPiece *p_entry = mp_cloned_piece_list;
while(p_entry)
{
CPiece *p_next_entry = p_entry->mp_next_in_list;
if (p_entry->CastToCClonedPiece()->GetFlags() & CPiece::mGAP)
{
//Dbg_Assert(0);
p_entry->CastToCClonedPiece()->SetVisibility(visible);
}
p_entry = p_next_entry;
}
#endif
}
void CParkGenerator::SetRestartPiecesVisible(bool visible)
{
Ryan("the stars go in and the stars go out %d\n", visible);
CPiece *p_entry = mp_cloned_piece_list;
while(p_entry)
{
CPiece *p_next_entry = p_entry->mp_next_in_list;
uint32 id = p_entry->CastToCClonedPiece()->mp_source_piece->GetType();
if (CParkManager::Instance()->IsRestartPiece(id))
{
p_entry->CastToCClonedPiece()->SetActive(visible);
}
p_entry = p_next_entry;
}
}
void CParkGenerator::SetLightProps(int num_lights,
float amb_const_r, float amb_const_g, float amb_const_b,
float falloff_const_r, float falloff_const_g, float falloff_const_b,
float cursor_ambience)
{
Dbg_MsgAssert(num_lights <= vMAX_LIGHTS, ("too many lights"));
m_numLights = num_lights;
m_ambientLightIntensity.r = amb_const_r;
m_ambientLightIntensity.g = amb_const_g;
m_ambientLightIntensity.b = amb_const_b;
m_falloffLightIntensity.r = falloff_const_r;
m_falloffLightIntensity.g = falloff_const_g;
m_falloffLightIntensity.b = falloff_const_b;
m_cursorAmbience = cursor_ambience;
}
void CParkGenerator::SetLight(Mth::Vector &light_pos, int light_num)
{
Dbg_MsgAssert(light_num < m_numLights, ("out of light slots"));
m_lightTab[light_num] = light_pos;
}
// Given the id of a piece in the world, apply the lights to it
void CParkGenerator::CalculateLighting(CClonedPiece *p_piece)
{
Nx::CSector *p_sector= Nx::CEngine::sGetSector(p_piece->m_sector_checksum);
Dbg_MsgAssert(p_sector,("Trying to Calculate lighting on non-existant sector"))
Nx::CGeom *p_geom = p_sector->GetGeom();
Dbg_MsgAssert(p_geom,("sector does not have geometry"))
Nx::CSector *p_source_sector= Nx::CEngine::sGetSector(p_piece->GetSourcePiece()->GetType());
Dbg_MsgAssert(p_source_sector,("Trying to Calculate lighting on sector with no source sector"))
Nx::CGeom *p_source_geom = p_source_sector->GetGeom();
Dbg_MsgAssert(p_source_geom,("source sector does not have geometry"))
//
// Do renderable geometry
int verts = p_geom->GetNumRenderVerts();
Dbg_MsgAssert(verts == p_source_geom->GetNumRenderVerts(),("source and clone have differing no of verts (%d, %d)\n",verts,p_source_geom->GetNumRenderVerts()));
if (verts)
{
Mth::Vector *p_verts = new Mth::Vector[verts];
Image::RGBA *p_colors = new Image::RGBA[verts];
p_geom->GetRenderVerts(p_verts);
p_source_geom->GetRenderColors(p_colors);
Image::RGBA *p_color = p_colors;
Mth::Vector *p_vert = p_verts;
for (int i = 0; i < verts; i++)
{
CalculateVertexLighting(*p_vert, *p_color);
p_color++;
p_vert++;
} // end for
// Set the colors on the actual new geom, not on the source...
p_geom->SetRenderColors(p_colors);
delete [] p_verts;
delete [] p_colors;
} // end if
else
{
// debuggery
//p_geom->SetColor(Image::RGBA(0,0,100,0));
}
//
// Do collision geometry
Nx::CCollObjTriData *p_coll_tri_data = p_geom->GetCollTriData();
Nx::CCollObjTriData *p_source_coll_tri_data = p_source_geom->GetCollTriData();
Dbg_MsgAssert(p_coll_tri_data, ("sector does not have collision"));
Dbg_MsgAssert(p_source_coll_tri_data, ("source sector does not have collision"));
#ifdef __PLAT_NGC__
int faces = p_coll_tri_data->GetNumFaces();
Dbg_MsgAssert(faces == p_source_coll_tri_data->GetNumFaces(), ("source and clone have differing no of collision faces (%d, %d)\n",verts,p_source_coll_tri_data->GetNumFaces()));
Image::RGBA color;
unsigned char intensity;
unsigned int avg_color;
for (int f = 0; f < faces; f++)
{
for ( int c = 0; c < 3; c++ )
{
intensity = p_source_coll_tri_data->GetVertexIntensity(f, c);
color.r = intensity;
color.g = intensity;
color.b = intensity;
color.a = 255;
CalculateVertexLighting(p_coll_tri_data->GetRawVertexPos(p_coll_tri_data->GetFaceVertIndex(f, c)), color);
avg_color = (color.r + color.g + color.b) / 3;
p_coll_tri_data->SetVertexIntensity(f, c, avg_color);
}
}
#else
verts = p_coll_tri_data->GetNumVerts();
Dbg_MsgAssert(verts == p_source_coll_tri_data->GetNumVerts(), ("source and clone have differing no of collision verts (%d, %d)\n",verts,p_source_coll_tri_data->GetNumVerts()));
Image::RGBA color;
unsigned char intensity;
unsigned int avg_color;
for (int i = 0; i < verts; i++)
{
intensity = p_source_coll_tri_data->GetVertexIntensity(i);
color.r = intensity;
color.g = intensity;
color.b = intensity;
color.a = 255;
CalculateVertexLighting(p_coll_tri_data->GetRawVertexPos(i), color);
avg_color = (color.r + color.g + color.b) / 3;
p_coll_tri_data->SetVertexIntensity(i, avg_color);
}
#endif // __PLAT_NGC__
}
// apply lights to a vert
void CParkGenerator::CalculateVertexLighting(const Mth::Vector & vert, Image::RGBA & color)
{
LightIntensity i_amb;
i_amb.r = (float) color.r * m_ambientLightIntensity.r;
i_amb.g = (float) color.g * m_ambientLightIntensity.g;
i_amb.b = (float) color.b * m_ambientLightIntensity.b;
float i_spot_light = 0.0f;
Mth::Vector *pLightTabEntry = m_lightTab;
for (int l = 0; l < m_numLights; l++)
{
float dist_x = vert.GetX() - pLightTabEntry->GetX();
//float dist_y = vert.GetY() - pLightTabEntry->GetY();
float dist_y = 400; // Mick: Patched to ignore height, otherwise we have burningly bright spots on high walls
// you can decrease this number make the lighhing more intense
// (note, this means no verticle variation in brightness in the park editor
float dist_z = vert.GetZ() - pLightTabEntry->GetZ();
float dist_squared = dist_x * dist_x + dist_y * dist_y + dist_z * dist_z;
i_spot_light += 1.0f / dist_squared;
pLightTabEntry++;
}
i_spot_light *= 255.0f;
i_amb.r += i_spot_light * m_falloffLightIntensity.r;
if (i_amb.r > 255.0)
color.r = 255;
else
color.r = (uint8) (i_amb.r);
i_amb.g += i_spot_light * m_falloffLightIntensity.g;
if (i_amb.g > 255.0)
color.g = 255;
else
color.g = (uint8) (i_amb.g);
i_amb.b += i_spot_light * m_falloffLightIntensity.b;
if (i_amb.b > 255.0)
color.b = 255;
else
color.b = (uint8) (i_amb.b);
}
/*
See comments in DestroyAllClonedPieces() for 'destroyType'
*/
void CParkGenerator::destroy_piece_impl(CClonedPiece *pPiece, EDestroyType destroyType)
{
// XXX
if (pPiece->m_flags & CPiece::mGAP)
Ryan("@@@ destroying gap piece 0x%x\n", pPiece);
if (!(pPiece->m_flags & CClonedPiece::mSOFT_PIECE))
{
// remove from list first
CPiece *p_entry = mp_cloned_piece_list;
CPiece *p_prev = NULL;
while(p_entry)
{
if (p_entry == pPiece)
{
if (p_prev)
p_prev->mp_next_in_list = p_entry->mp_next_in_list;
else
mp_cloned_piece_list = p_entry->mp_next_in_list;
break;
}
p_prev = p_entry;
p_entry = p_entry->mp_next_in_list;
}
Dbg_MsgAssert(p_entry, ("piece not found"));
if (!(pPiece->m_flags & CPiece::mNO_RAILS))
{
m_total_rail_points -= pPiece->mp_source_piece->m_num_rail_points;
m_total_rail_linked_points -= pPiece->mp_source_piece->m_num_linked_rail_points;
}
}
// now actually destroy piece
if (destroyType == DESTROY_PIECES_AND_SECTORS)
{
#if 0
printf(" Gott mit uns (%f,%f,%f)\n",
pPiece->GetPos().GetX(), pPiece->GetPos().GetY(), pPiece->GetPos().GetZ());
#endif
// destroy underlying sector
mp_cloned_scene->DeleteSector(pPiece->m_sector_checksum);
#ifdef __PLAT_NGC__
//printf("Called DeleteSector\n");
Nx::CEngine::sFinishRendering();
#endif
}
m_num_cloned_pieces--;
delete pPiece;
if (!mp_cloned_piece_list)
clear_flag(mPIECES_IN_WORLD);
set_flag(mSECTOR_MEMORY_NEEDS_FREE);
}
// Read in the node array containing the rail information
// and parse this into the park editor format....
// called from CParkManager::Initialize()
void CParkGenerator::ReadInRailInfo()
{
const uint32 crc_cluster = 0x1a3a966b;
const uint32 crc_class = 0x12b4e660;
const uint32 crc_links = 0x2e7d5ee7;
printf ("Starting ReadInRailInfo()\n");
// Mick - Not sure where this should be set up
m_in_rail_set.SetupAllocators(GetResourceSize("in_railpoint_pool"), GetResourceSize("in_railstring_pool"), true);
// Ken: This function has gone now, since the new ability to unload a qb has made it redundant.
//Script::RemoveOldTriggerScripts();
// Remove any spawned scripts that might have been left over from the previous level.
Script::DeleteSpawnedScripts();
// Load the QB
// SkateScript::LoadQB(PIECE_SET_QB[m_currentThemeNumber]);
// SkateScript::LoadQB("levels\\sk4ed\\sk4ed.qb"); // assume it has already been loaded (NOTE: If not, then you will have problems with PRE files, as the .QBs do not go in the pre file)
// Ensure it has a node array in it
Script::CArray *pNodeArray=Script::GetArray(CRCD(0xc472ecc5,"NodeArray"));
Dbg_MsgAssert(pNodeArray,("Park editor node array not found"));
// this will load up sounds
Script::RunScript("LoadTerrain");
Ryan("\n*** Reading in rail info ***\n");
m_in_rail_set.Destroy();
// BAD MEM
m_processed_node_tab = new int[256];
m_processed_node_tab_entries = 0;
m_temp_node_tab = new TempNodeInfo[2048];
int index = 0;
for ( ; index < (int)pNodeArray->GetSize(); index++)
{
Script::CScriptStructure *pStruct = pNodeArray->GetStructure(index);
m_temp_node_tab[index].classCrc = 0;
pStruct->GetChecksum(crc_class, &m_temp_node_tab[index].classCrc);
m_temp_node_tab[index].cluster = 0;
pStruct->GetChecksum(crc_cluster, &m_temp_node_tab[index].cluster);
m_temp_node_tab[index].pLinkArray = NULL;
pStruct->GetArray(crc_links, &m_temp_node_tab[index].pLinkArray);
}
const bool debug_rail_read = true;
// XXX
Ryan("number of node entries is %d\n", pNodeArray->GetSize());
index = 0;
// loop through all LevelGeometry objects in node array.
// for each one, locate associated rail nodes (if any) and create RailString.
while(1)
{
// get cluster checksum of LevelGeometry object, will be used to
// look up rail nodes
uint32 cluster_crc = scan_for_cluster(pNodeArray, index);
if (index >= (int)pNodeArray->GetSize())
{
printf ("No more clusters, exiting\n");
break;
}
// printf("source piece %s for cluster \n", Script::FindChecksumName(cluster_crc));
//debug_rail_read = (cluster_crc == 0x21997899);
#ifdef __NOPT_ASSERT__
CPiece *pMasterPiece = GetMasterPiece(cluster_crc);
CSourcePiece *pSourcePiece = pMasterPiece->CastToCSourcePiece();
Dbg_MsgAssert(pSourcePiece, ("no source piece %s found", Script::FindChecksumName(cluster_crc)));
#endif // __NOPT_ASSERT__
if (debug_rail_read)
Ryan("found cluster %s at index %d\n", Script::FindChecksumName(cluster_crc), index-1);
Mth::Vector v;
RailPoint::RailType type;
// find all rail strings associated with current LevelGeometry object
// m_processed_node_tab[] keeps track of strings already processed
m_processed_node_tab_entries = 0;
while (1)
{
bool string_is_loop = false;
// try to locate a rail node that matches the cluster and has no links
// if it fails to find one, it will try to return one that matches the cluster and does have links
int last_node_in_set = scan_for_rail_node(pNodeArray, cluster_crc, -1, &v, &string_is_loop, &type);
if (last_node_in_set == -1)
break;
int current_node = last_node_in_set;
RailString *pCurrentString = m_in_rail_set.CreateRailString();
pCurrentString->m_id = cluster_crc;
pCurrentString->m_isLoop = string_is_loop;
RailPoint *pPoint = m_in_rail_set.CreateRailPoint();
//pPoint->m_pos.Set(v.GetX() - pSourcePiece->m_pos.GetX(), v.GetY() - pSourcePiece->m_pos.GetY(), v.GetZ() + pSourcePiece->m_pos.GetZ());
pPoint->m_pos.Set(v.GetX(), v.GetY(), v.GetZ());
pPoint->m_type = type;
if (debug_rail_read)
Ryan("* [%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
pCurrentString->AddPoint(pPoint);
// go through all points in the string (after the first)
while (1)
{
current_node = scan_for_rail_node(pNodeArray, cluster_crc, current_node, &v, &string_is_loop, &type);
if (current_node == -1)
{
if (debug_rail_read && pCurrentString->m_isLoop)
Ryan(" (loop)");
break;
}
pPoint = m_in_rail_set.CreateRailPoint();
//pPoint->m_pos.Set(v.GetX() - pSourcePiece->m_pos.GetX(), v.GetY() - pSourcePiece->m_pos.GetY(), v.GetZ() + pSourcePiece->m_pos.GetZ());
pPoint->m_pos.Set(v.GetX(), v.GetY(), v.GetZ());
pPoint->m_type = type;
if (debug_rail_read)
Ryan("-[%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
pCurrentString->AddPoint(pPoint);
}
if (debug_rail_read)
Ryan("\n");
m_in_rail_set.AddString(pCurrentString);
} // end while
} // end while
delete m_processed_node_tab;
delete m_temp_node_tab;
scan_in_trigger_info(pNodeArray);
//
// m_nodeArrayIsSetup = true;
// DestroyCollisionDataAndNodeArray();
// now that rail info is set up, it's safe to do this next block of code
CPiece *p_piece = mp_source_piece_list;
while(p_piece)
{
CSourcePiece *p_source_piece = p_piece->CastToCSourcePiece();
p_source_piece->m_num_rail_points = 0;
p_source_piece->m_num_linked_rail_points = 0;
int string_num = 0;
while(1)
{
RailString *p_rail_string = m_in_rail_set.GetString(p_source_piece->GetType(), string_num++);
if (!p_rail_string)
break;
p_source_piece->m_num_rail_points += p_rail_string->Count();
p_source_piece->m_num_linked_rail_points += p_rail_string->CountLinkedPoints();
}
p_piece = p_piece->mp_next_in_list;
}
}
// called from CParkManager::Destroy()
void CParkGenerator::DestroyRailInfo()
{
m_in_rail_set.Destroy();
m_in_rail_set.FreeAllocators();
}
// returns checksum of first found cluster, increases index appropriately
// called from ReadInRailInfo()
uint32 CParkGenerator::scan_for_cluster(Script::CArray *pNodeArray, int &index)
{
const uint32 crc_class = 0x12b4e660;
const uint32 crc_levelgeometry = 0xbf4d3536; //0xdabd3086;
const uint32 crc_name = 0xa1dc81f9;
int total_entries = pNodeArray->GetSize();
/*
Given a node like...
{
// Node 471
Position = (-0.002991,1200.000000,0.000854)
Angles = (0.000000,0.000000,0.000000)
Name = Sk3Ed_Shell_Col_16x16_01
Class = levelgeometry
CreatedAtStart
}
... make sure it's level geometry, then find the value
of 'Name'
*/
while (index < total_entries)
{
Script::CStruct *pStruct = pNodeArray->GetStructure(index);
uint32 node_class = 0;
pStruct->GetChecksum(crc_class, &node_class);
if (node_class == crc_levelgeometry)
{
uint32 name;
if (pStruct->GetChecksum(crc_name, &name))
{
index++;
return name;
}
}
index++;
}
return 0;
}
/*
returns number of node
3 ways of calling:
-link set: find rail node with that link
-cluster set, link -1: find rail node with that cluster, but no link
-cluster set, link -2: find rail node with that cluster, can have a link
Called from ReadInRailInfo()
*/
int CParkGenerator::scan_for_rail_node(Script::CArray *pNodeArray, uint32 cluster, int link, Mth::Vector *pVector, bool *pHasLinks, RailPoint::RailType *pType)
{
// const uint32 crc_position = 0xb9d31b0a;
//const uint32 crc_links = 0x2e7d5ee7;
//const uint32 crc_cluster = 0x1a3a966b;
const uint32 crc_terraintype = 0x54cf8532;
// const uint32 crc_terrain_wood = 0x39075ea5;
//const uint32 crc_class = 0x12b4e660;
const uint32 crc_railnode = 0x8e6b02ad;
int found_node_with_link_index = -1;
int found_node_without_link_index = -1;
/*
A little optimization:
We first call this function with link<0, so at that time
store the index of the first node belonging to the requested
cluster.
Several following calls to this function will start the node
search from this point
*/
static int first_node_in_cluster;
if (link < 0)
first_node_in_cluster = 0;
int total_entries = pNodeArray->GetSize();
for (int n = first_node_in_cluster; n < total_entries; n++)
{
//Script::CScriptStructure *pStruct = pNodeArray->GetStructure(n);
uint32 node_class = m_temp_node_tab[n].classCrc;
if (node_class == crc_railnode)
{
// find link
int found_link = -1;
Script::CArray *pLinkArray = m_temp_node_tab[n].pLinkArray;
if (pLinkArray)
{
Dbg_MsgAssert(pLinkArray->GetSize() == 1, ("Rail node %d has more than one link (%d)",n,pLinkArray->GetSize()));
found_link = pLinkArray->GetInt(0);
}
uint32 found_cluster = m_temp_node_tab[n].cluster;
//Ryan(" cluster is %s, link is %d\n", Script::FindChecksumName(found_cluster), found_link);
if (link >= 0)
{
if (found_link == link)
{
found_node_with_link_index = n;
for (int i = 0; i < m_processed_node_tab_entries; i++)
{
if (m_processed_node_tab[i] == found_node_with_link_index)
// we've already processed this node
found_node_with_link_index = -1;
}
if (found_node_with_link_index != -1)
// in this case, we have all the info we need, so exit loop
break;
}
}
else if (cluster == found_cluster)
{
if (!first_node_in_cluster)
first_node_in_cluster = n;
if (found_link == -1)
{
found_node_without_link_index = n;
for (int i = 0; i < m_processed_node_tab_entries; i++)
{
if (m_processed_node_tab[i] == found_node_without_link_index)
// we've already processed this node
found_node_without_link_index = -1;
}
if (found_node_without_link_index != -1)
// this is the node we're looking for, so exit loop
break;
}
else if (found_node_with_link_index == -1) // && found_link >= 0
{
found_node_with_link_index = n;
for (int i = 0; i < m_processed_node_tab_entries; i++)
{
if (m_processed_node_tab[i] == found_node_with_link_index)
// we've already processed this node
found_node_with_link_index = -1;
}
// even if not already processed, this node is secondary priority,
// so we will not be exiting the loop
}
} // end else if
} // if (node_class == crc_railnode)
}
// the node without a link has greater priority
int match_index = -1;
if (found_node_with_link_index != -1)
{
match_index = found_node_with_link_index;
*pHasLinks = true;
}
if (found_node_without_link_index != -1)
{
match_index = found_node_without_link_index;
*pHasLinks = false;
}
if (match_index == -1) return -1;
// store this node so we won't use it twice
m_processed_node_tab[m_processed_node_tab_entries++] = match_index;
// fetch position info (will be returned to caller)
Script::CScriptStructure *pFound = pNodeArray->GetStructure(match_index);
pFound->GetVector(CRCD(0x7f261953,"Pos"), pVector, true);
// GJ: The following was incorrect, because the Z component should have been negated,
// however, the function that was supposed to use this data should have negated it again,
// but didn't, meaning that the 2 bugs ended up cancelling each other out.
// The new-style 'pos' vector is already correct and needs no negation.
// pFound->GetVector(CRCD(0xb9d31b0a,"Position"), pVector, false);
uint32 terrain_type; // terrain_wood
pFound->GetChecksum(crc_terraintype, &terrain_type, true);
// if (terrain_type == crc_terrain_wood)
// *pType = RailPoint::vWOOD;
// else
// *pType = RailPoint::vMETAL;
*pType = terrain_type;
return match_index;
}
// Should be called from ReadInRailInfo(), but isn't
void CParkGenerator::scan_in_trigger_info(Script::CArray *pNodeArray)
{
//const uint32 crc_position = 0xb9d31b0a;
const uint32 crc_class = 0x12b4e660;
const uint32 crc_levelgeometry = 0xbf4d3536; //0xdabd3086;
const uint32 crc_name = 0xa1dc81f9;
const uint32 crc_triggerscript = 0x2ca8a299;
for (uint32 n = 0; n < pNodeArray->GetSize(); n++)
{
Script::CScriptStructure *pStruct = pNodeArray->GetStructure(n);
uint32 node_class = 0;
pStruct->GetChecksum(crc_class, &node_class);
if (node_class == crc_levelgeometry)
{
uint32 piece_name = 0;
pStruct->GetChecksum(crc_name, &piece_name);
uint32 trigger_name = 0;
pStruct->GetChecksum(crc_triggerscript, &trigger_name);
if (piece_name && trigger_name)
{
// XXX
Ryan("Setting up trigger script: piece=%s script=%s\n", Script::FindChecksumName(piece_name), Script::FindChecksumName(trigger_name));
CPiece *pPiece = GetMasterPiece(piece_name, true);
CSourcePiece *pSourcePiece = pPiece->CastToCSourcePiece();
pSourcePiece->m_triggerScriptId = trigger_name;
}
}
}
Ryan("done with all rail reading\n");
}
void CParkGenerator::set_up_object_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{
Ryan("set_up_object_nodes: %d/%d\n", Mem::CPoolable<Script::CComponent>::SGetNumUsedItems(), Mem::CPoolable<Script::CComponent>::SGetTotalItems());
Ryan("CVector use: %d/%d\n", Mem::CPoolable<Script::CVector>::SGetNumUsedItems(), Mem::CPoolable<Script::CVector>::SGetTotalItems());
#if 1
// uint32 crc_position = Script::GenerateCRC("position");
uint32 crc_angles = Script::GenerateCRC("angles");
uint32 crc_name = Script::GenerateCRC("name");
uint32 crc_class = Script::GenerateCRC("class");
uint32 crc_levelgeometry = Script::GenerateCRC("LevelGeometry");
uint32 crc_createdatstart = Script::GenerateCRC("CreatedAtStart");
uint32 crc_triggerscript = Script::GenerateCRC("TriggerScript");
uint32 crc_trickobject = Script::GenerateCRC("TrickObject");
uint32 crc_cluster = Script::GenerateCRC("Cluster");
// 7
CPiece *p_piece = mp_cloned_piece_list;
while (p_piece)
{
CClonedPiece *p_cloned = p_piece->CastToCClonedPiece();
if (!(p_cloned->GetFlags() & CPiece::mFLOOR) &&
!(p_cloned->GetFlags() & CPiece::mRESTART))
{
Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);
Mth::Vector pos = p_cloned->GetPos();
pNode->AddComponent(CRCD(0x7f261953,"pos"), pos.GetX(), pos.GetY(), pos.GetZ());
// GJ: The following was incorrect, and the Z component should have been negated,
// but no one ever really used it, so we never noticed that it was a problem...
// The new-style 'pos' vector is already correct and needs no negation.
// pNode->AddComponent(CRCD(0xb9d31b0a,"position"), pos.GetX(), pos.GetY(), pos.GetZ());
pNode->AddComponent(crc_angles, 0.0f, 0.0f, 0.0f);
// hopefully safe
// XXX
Ryan("id is 0x%x\n", p_cloned->m_id);
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) p_cloned->m_sector_checksum);
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_levelgeometry);
pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);
pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_trickobject);
// Mick: use the cloned pieces ID, but EOR with with 0xffffffff so it's different to the original name
pNode->AddComponent(crc_cluster, ESYMBOLTYPE_NAME, (int) p_cloned->GetID() ^ 0xffffffff);
// trigger script
if (p_cloned->GetFlags() & CPiece::mGAP)
{
Dbg_Assert(CGapManager::sInstance());
pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, (int) CGapManager::sInstance()->GetGapTriggerScript(p_cloned));
}
else if (p_cloned->GetSourcePiece()->m_triggerScriptId)
pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, (int) p_cloned->GetSourcePiece()->m_triggerScriptId);
(*pNodeNum)++;
}
p_piece = p_piece->mp_next_in_list;
}
#endif
}
// Used by the rail editor for snapping to the nearest rail point.
bool CParkGenerator::FindNearestRailPoint(Mth::Vector &pos, Mth::Vector *p_nearest_pos, float *p_dist_squared)
{
float min_dist_squared=100000000.0f;
bool found_point=false;
RailString *p_string = m_out_rail_set.GetList();
while (p_string)
{
RailPoint *p_point = p_string->GetList();
while (p_point)
{
Mth::Vector diff=p_point->m_pos-pos;
float dd=diff.LengthSqr();
if (dd < min_dist_squared)
{
min_dist_squared=dd;
*p_nearest_pos=p_point->m_pos;
*p_dist_squared=min_dist_squared;
found_point=true;
}
p_point = p_point->GetNext();
}
p_string = p_string->GetNext();
}
return found_point;
}
// called from GenerateNodeInfo()
void CParkGenerator::set_up_rail_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{
Ryan("set_up_rail_nodes: %d/%d\n", Mem::CPoolable<Script::CComponent>::SGetNumUsedItems(), Mem::CPoolable<Script::CComponent>::SGetTotalItems());
#if 1
const bool debug_rail_out = false;
uint32 crc_angles = Script::GenerateCRC("angles");
uint32 crc_name = Script::GenerateCRC("name");
uint32 crc_class = Script::GenerateCRC("class");
uint32 crc_railnode = Script::GenerateCRC("railnode");
uint32 crc_createdatstart = Script::GenerateCRC("CreatedAtStart");
uint32 crc_links = Script::GenerateCRC("links");
// uint32 crc_type = Script::GenerateCRC("type");
// uint32 crc_metal = Script::GenerateCRC("metal");
// uint32 crc_wood = Script::GenerateCRC("wood");
uint32 crc_terraintype = Script::GenerateCRC("terraintype");
// uint32 crc_terrain_wood = Script::GenerateCRC("terrain_wood");
// uint32 crc_terrain_metal = Script::GenerateCRC("terrain_metal");
uint32 crc_trickobject = Script::GenerateCRC("TrickObject");
uint32 crc_cluster = Script::GenerateCRC("Cluster");
/*
Position = (-1619.978027,-336.000031,-6870.965332)
Angles = (0.000000,-3.141589,0.000000)
Name = TRG_Conc_Park_Rail0
Class = RailNode
Type = Concrete
CreatedAtStart
TerrainType = TERRAIN_CONCSMOOTH
TrickObject Cluster = ParkingLotSpine1
TriggerScript = CanTRG_Conc_Park_Rail0Script
Links=[1]
*/
int num_points = m_out_rail_set.CountPoints();
if (!num_points) return;
RailString *pString = m_out_rail_set.GetList();
RailPoint *pPoint = pString->GetList();
if (debug_rail_out)
Ryan("String %s: ", Script::FindChecksumName(pString->m_id));
int first_node_in_loop = *pNodeNum;
// 10
uint32 rail_point_id = vFIRST_ID_FOR_RAILS;
int stop_node_num = *pNodeNum + num_points;
for (; *pNodeNum < stop_node_num; (*pNodeNum)++)
{
Dbg_Assert(pPoint);
Script::CStruct *pNode = pNodeArray->GetStructure(*pNodeNum);
pNode->AddComponent(CRCD(0x7f261953,"pos"), pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
if (debug_rail_out)
Ryan("[%.1f,%.1f,%.1f]", pPoint->m_pos.GetX(), pPoint->m_pos.GetY(), pPoint->m_pos.GetZ());
pNode->AddComponent(crc_angles, 0.0f, 0.0f, 0.0f);
Dbg_MsgAssert(rail_point_id < vMAX_ID_FOR_RAILS, ("out of rail ids"));
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) rail_point_id++);
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_railnode);
pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);
/*
uint32 type, terrain_type;
if (pPoint->m_type == RailPoint::vWOOD)
{
type = crc_wood;
terrain_type = crc_terrain_wood;
}
else
{
type = crc_metal;
terrain_type = crc_terrain_metal;
}
*/
// Don't need type?
// pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, (int) type);
pNode->AddComponent(crc_terraintype, ESYMBOLTYPE_NAME, (int) pPoint->m_type);
// Mick: m_objectId will be zero if this rail is not part of a trickob
if (pPoint->m_objectId)
{
pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_trickobject);
pNode->AddComponent(crc_cluster, ESYMBOLTYPE_NAME, (int) pPoint->m_objectId ^ 0xffffffff);
}
if (pPoint->GetNext() != NULL || pString->m_isLoop)
{
int link_num = *pNodeNum + 1;
if (pPoint->GetNext() == NULL && pString->m_isLoop)
link_num = first_node_in_loop;
Script::CArray *pLinks = new Script::CArray();
pLinks->SetSizeAndType(1,ESYMBOLTYPE_INTEGER);
pLinks->SetInteger(0, link_num);
pNode->AddComponent(crc_links, pLinks);
delete pLinks;
}
pPoint = pPoint->GetNext();
if (pPoint)
{
if (debug_rail_out)
Ryan("-");
}
else
{
if (debug_rail_out)
{
if (pString->m_isLoop) Ryan(" (loop)");
Ryan("\n");
}
pString = pString->GetNext();
if (pString)
{
if (debug_rail_out)
Ryan("String %s: ", Script::FindChecksumName(pString->m_id));
pPoint = pString->GetList();
first_node_in_loop = *pNodeNum + 1;
}
}
}
#endif
}
// called from GenerateNodeInfo()
void CParkGenerator::set_up_restart_nodes(Script::CArray *pNodeArray, int *pNodeNum)
{
uint32 crc_angles = Script::GenerateCRC("angles");
uint32 crc_name = Script::GenerateCRC("name");
uint32 crc_class = Script::GenerateCRC("class");
uint32 crc_type = Script::GenerateCRC("type");
uint32 crc_restart = Script::GenerateCRC("restart");
uint32 crc_genericnode = Script::GenerateCRC("GenericNode");
uint32 crc_createdatstart = Script::GenerateCRC("CreatedAtStart");
uint32 crc_restartname = Script::GenerateCRC("RestartName");
uint32 crc_restart_types = Script::GenerateCRC("restart_types");
uint32 crc_model = Script::GenerateCRC("model");
uint32 crc_triggerscript = Script::GenerateCRC("TriggerScript");
uint32 restart_id = vFIRST_ID_FOR_RESTARTS;
for (int i = 0; i < vNUM_RESTARTS; i++)
{
if (m_restart_tab[i].type != vEMPTY)
{
//Ryan("yo, restart at (%.2f,%.2f,%.2f)\n", m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
// if restart is ONE_PLAYER or HORSE type, it will also be repeated
// as a multiplayer
int repeat_num = 1;
if (m_restart_tab[i].type == vHORSE)
repeat_num = 2;
if (m_restart_tab[i].type == vONE_PLAYER)
repeat_num = 2;
for (int n = 0; n < repeat_num; n++)
{
/*
{
// Node 1965
Position = (-476.991364,-70.263161,-5703.516602)
Angles = (0.000000,0.000000,0.000000)
Name = TRG_Crown01
Class = GenericNode
Type = Crown
CreatedAtStart
}
{
// Node 2912
Position = (2940.254639,-1368.771729,-753.127625)
Angles = (0.000000,2.687807,0.000000)
Name = TRG_CTF_Restart_Red
Class = Restart
Type = CTF
CreatedAtStart
CollisionMode = BoundingBox
RestartName = "Team: CTF"
restart_types = [ CTF ]
}
*/
int restart_name = restart_id++; // use generic name for most restarts
uint32 type_checksum = 0;
if (m_restart_tab[i].type == vONE_PLAYER)
{
type_checksum = Script::GenerateCRC("Player1");
}
if (m_restart_tab[i].type == vHORSE)
{
type_checksum = Script::GenerateCRC("Horse");
}
if (m_restart_tab[i].type == vMULTIPLAYER || n == 1)
{
type_checksum = Script::GenerateCRC("Multiplayer");
}
if (m_restart_tab[i].type == vKING_OF_HILL)
{
type_checksum = Script::GenerateCRC("Crown");
}
if (
m_restart_tab[i].type == vRED_FLAG
|| m_restart_tab[i].type == vGREEN_FLAG
|| m_restart_tab[i].type == vBLUE_FLAG
|| m_restart_tab[i].type == vYELLOW_FLAG
)
{
type_checksum = Script::GenerateCRC("CTF");
switch (m_restart_tab[i].type)
{
case vRED_FLAG:
restart_name = Script::GenerateCRC("TRG_CTF_Restart_Red");
break;
case vGREEN_FLAG:
restart_name = Script::GenerateCRC("TRG_CTF_Restart_Green");
break;
case vBLUE_FLAG:
restart_name = Script::GenerateCRC("TRG_CTF_Restart_Blue");
break;
case vYELLOW_FLAG:
restart_name = Script::GenerateCRC("TRG_CTF_Restart_Yellow");
break;
default:
Dbg_MsgAssert(0,("Impossible!"));
break;
}
}
if (type_checksum != 0)
{
Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);
if (m_restart_tab[i].type == vKING_OF_HILL)
{
pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, m_restart_tab[i].pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, -m_restart_tab[i].pos.GetZ());
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_genericnode);
}
else
{
pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY() + 50.0f, m_restart_tab[i].pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, (int) crc_restart);
}
pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, (int) restart_name);
pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, (int) type_checksum);
pNode->AddComponent(NONAME, ESYMBOLTYPE_NAME, (int) crc_createdatstart);
if (m_restart_tab[i].type != vKING_OF_HILL)
{
char *p_name = "Multi Restart";
if (n == 0)
{
switch (m_restart_tab[i].type)
{
case vONE_PLAYER:
p_name = "Player 1 Restart";
break;
case vMULTIPLAYER:
p_name = "Player 2 Restart";
break;
case vHORSE:
p_name = "Horse Restart";
break;
case vRED_FLAG:
case vGREEN_FLAG:
case vBLUE_FLAG:
case vYELLOW_FLAG:
p_name = "Team: CTF";
break;
default:
break;
}
}
pNode->AddComponent(crc_restartname, ESYMBOLTYPE_STRING, p_name);
Script::CArray *pRestartTypes = new Script::CArray();
pRestartTypes->SetSizeAndType(1, ESYMBOLTYPE_NAME);
pRestartTypes->SetChecksum(0, type_checksum);
pNode->AddComponent(crc_restart_types, pRestartTypes);
delete pRestartTypes;
}
//Script::PrintContents(pNode);
(*pNodeNum)++;
}
// Flags have the 3 additional nodes for the team flags, bases, CTF flag
if (
m_restart_tab[i].type == vRED_FLAG
|| m_restart_tab[i].type == vGREEN_FLAG
|| m_restart_tab[i].type == vBLUE_FLAG
|| m_restart_tab[i].type == vYELLOW_FLAG
)
{
// Not a normal restart point, so must be a flag
// so need to generate the flag, the CTF base and the CTF restart all in the same place
/*
// team flag is like:
{
// Node 2678
Position = (864.618835,-408.157806,-3771.989014)
Angles = (0.000000,2.268928,0.000000)
Name = TRG_Flag_Red
Class = GameObject
Type = Flag_Red
Model = "gameobjects\flags\flag_red\flag_red.mdl"
SuspendDistance = 0
lod_dist1 = 800
lod_dist2 = 801
TriggerScript = TRG_Flag_RedScript
}
then pretty much the same for CTF flags, just a different name
{
// Node 3360
Position = (4060.947998,408.885132,1926.812012)
Angles = (0.000000,0.000000,0.000000)
Name = TRG_CTF_Blue <<<<<<<<<<<<<< Different name
Class = GameObject
Type = Flag_Blue
Model = "gameobjects\flags\flag_blue\flag_blue.mdl"
SuspendDistance = 0
lod_dist1 = 800
lod_dist2 = 801
TriggerScript = TRG_CTF_BlueScript <<<<<<<<<<<<<< script can actually stay the same
}
and then the base
{
// Node 3252
Position = (-2448.872314,-373.527405,5749.742188)
Angles = (0.000000,-2.792527,0.000000)
Name = TRG_CTF_Yellow_Base <<<<<<<<<<<<<<<<<<<<<<<<<<<< different name
Class = GameObject
Type = Flag_Yellow_Base <<<<<<<<<<<<<<<<<<<<<<<<<<< different type
Model = "gameobjects\flags\flag_yellow_base\flag_yellow_base.mdl" <<<<<< differnt model
SuspendDistance = 0
lod_dist1 = 800
lod_dist2 = 801
<<<<<<<<<<<<<<< no script
}
*/
Script::CScriptStructure *pNode = pNodeArray->GetStructure(*pNodeNum);
const char *color = NULL;
if (m_restart_tab[i].type == vRED_FLAG) color = "red";
if (m_restart_tab[i].type == vGREEN_FLAG) color = "green";
if (m_restart_tab[i].type == vBLUE_FLAG) color = "blue";
if (m_restart_tab[i].type == vYELLOW_FLAG) color = "yellow";
char name_string[256];
char type_string[256];
char model_string[256];
char triggerscript_string[256];
sprintf(type_string,"Flag_%s",color);
sprintf(model_string,"gameobjects\\flags\\flag_%s\\flag_%s.mdl",color,color);
sprintf(triggerscript_string,"TRG_Flag_%sScript_Parked",color);
sprintf(name_string,"TRG_Flag_%s",color);
pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);
pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, Script::GenerateCRC(triggerscript_string));
pNode->AddInteger("nodeIndex", *pNodeNum);
pNode->AddInteger("SuspendDistance", 0);
pNode->AddInteger("lod_dist1", 800);
pNode->AddInteger("lod_dist2", 801);
// Script::PrintContents(pNode);
(*pNodeNum)++;
// then repeat exactly for the CTF node, with different name
pNode = pNodeArray->GetStructure(*pNodeNum);
sprintf(name_string,"TRG_CTF_%s",color);
pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);
pNode->AddComponent(crc_triggerscript, ESYMBOLTYPE_NAME, Script::GenerateCRC(triggerscript_string));
pNode->AddInteger("nodeIndex", *pNodeNum);
pNode->AddInteger("SuspendDistance", 0);
pNode->AddInteger("lod_dist1", 800);
pNode->AddInteger("lod_dist2", 801);
// Script::PrintContents(pNode);
(*pNodeNum)++;
// different for base (note, no triggerscript)
pNode = pNodeArray->GetStructure(*pNodeNum);
sprintf(name_string,"TRG_CTF_%s_Base",color);
sprintf(type_string,"Flag_%s_Base",color);
sprintf(model_string,"gameobjects\\flags\\flag_%s_base\\flag_%s_base.mdl",color,color);
pNode->AddComponent(CRCD(0x7f261953,"pos"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), m_restart_tab[i].pos.GetZ());
// pNode->AddComponent(CRCD(0xb9d31b0a,"Position"), m_restart_tab[i].pos.GetX(), m_restart_tab[i].pos.GetY(), -m_restart_tab[i].pos.GetZ());
pNode->AddComponent(crc_angles, 0.0f, Mth::PI * 0.5f * ((float) m_restart_tab[i].dir + 2), 0.0f);
pNode->AddComponent(crc_name, ESYMBOLTYPE_NAME, Script::GenerateCRC(name_string));
pNode->AddComponent(crc_class, ESYMBOLTYPE_NAME, Script::GenerateCRC("GameObject"));
pNode->AddComponent(crc_type, ESYMBOLTYPE_NAME, Script::GenerateCRC(type_string));
pNode->AddComponent(crc_model, ESYMBOLTYPE_STRING, model_string);
pNode->AddInteger("nodeIndex", *pNodeNum);
pNode->AddInteger("SuspendDistance", 0);
pNode->AddInteger("lod_dist1", 800);
pNode->AddInteger("lod_dist2", 801);
// Script::PrintContents(pNode);
(*pNodeNum)++;
}
} // end for n
} // end if
}
/*
Position = (2456.617188,-218.955109,-6663.688477)
Angles = (0.741766,-1.570794,-0.000002)
Name = TRG_Restart_Parking_Lot04
Class = Restart
Type = Player1
CreatedAtStart
RestartName = "P1: Restart"
restart_types =
[
Player1
]
*/
}
////////////////////////////////////////////////////////////////////////////
// Restart stuff
/*
Should be called from
-CParkManager::RebuildNodeArray() (was in Map::GenerateWorld())
-AddMetaPieceToPark::AddMetaPieceToPark() (was in Map::AddPiece())
*/
// Add a restart point if space
// Non-automatic restart points will overwrite automatic restart points
// (but not if they find an empty slot first)
bool CParkGenerator::AddRestart(Mth::Vector &pos, int dir, GridDims &dims, RestartType type, bool automatic, bool auto_copy)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
Mth::Vector use_pos = pos;
GridDims use_dims = dims;
// When "auto_copy" is set
// If this is an automatic point, then copy the position
// from the last non-automatic point we find
if (automatic && auto_copy)
{
for (int i = first_slot; i <= last_slot; i++)
{
if (m_restart_tab[i].type == type && !m_restart_tab[i].automatic)
{
use_pos = m_restart_tab[i].pos;
use_dims = m_restart_tab[i].dims;
#ifdef DEBUG_RESTARTS
printf ("%d: restart copying position from slot %d",__LINE__,i);
#endif
}
}
}
for (int i = first_slot; i <= last_slot; i++)
{
// add the restart to any empty slot, or overwrite an "automatic" slot if this is not automatic
if (m_restart_tab[i].type == vEMPTY || (m_restart_tab[i].automatic && !automatic))
{
m_restart_tab[i].pos = use_pos;
m_restart_tab[i].dir = dir;
m_restart_tab[i].dims = use_dims;
m_restart_tab[i].type = type;
m_restart_tab[i].automatic = automatic;
#ifdef DEBUG_RESTARTS
printf ("%d: restart (%.2f,%.2f,%.2f) set in slot %d (type = %d, auto = %d\n)",__LINE__,use_pos[X],use_pos[Y],use_pos[Z],i,type,automatic);
#endif
return true;
}
}
return false;
}
int CParkGenerator::NumRestartsOfType(RestartType type)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
int got = 0; // count of number of actual restarts
for (int i = first_slot; i <= last_slot; i++)
{
if (m_restart_tab[i].type != vEMPTY) // got some kind of restart
{
got++; // counting an actual restart
}
}
return got;
}
// should be called from CParkManager::RebuildNodeArray() (was in Map::GenerateWorld())
// returns true if there are not enough restart points (ie, true if we need more)
// Note: we include "automatic" restart points now.
bool CParkGenerator::NotEnoughRestartsOfType(RestartType type, int need)
{
return NumRestartsOfType(type) < need; // if got less than we need, return true, (we need more)
}
Mth::Vector CParkGenerator::GetRestartPos(RestartType type, int index)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
for (int i = first_slot; i <= last_slot; i++)
{
if (m_restart_tab[i].type != vEMPTY) // got some kind of restart
{
index--;
if (index < 0)
{
return m_restart_tab[i].pos;
}
}
}
Dbg_MsgAssert(0,("could not find restart pos"));
return Mth::Vector(0,0,0);
}
GridDims CParkGenerator::GetRestartDims(RestartType type, int index)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
for (int i = first_slot; i <= last_slot; i++)
{
if (m_restart_tab[i].type != vEMPTY) // got some kind of restart
{
index--;
if (index < 0)
{
return m_restart_tab[i].dims;
}
}
}
Dbg_MsgAssert(0,("could not find restart dims"));
return GridDims();
}
// called by various restart functions in this class
void CParkGenerator::get_restart_slots(RestartType type, int *pFirstSlot, int *pLastSlot)
{
switch (type)
{
case vONE_PLAYER:
*pFirstSlot = 1;
*pLastSlot = 1;
break;
case vMULTIPLAYER:
*pFirstSlot = 0;
*pLastSlot = 0;
break;
case vHORSE:
*pFirstSlot = 2;
*pLastSlot = 7;
break;
case vKING_OF_HILL:
*pFirstSlot = 8;
*pLastSlot = 13;
break;
case vRED_FLAG:
*pFirstSlot = 14;
*pLastSlot = 14;
break;
case vGREEN_FLAG:
*pFirstSlot = 15;
*pLastSlot = 15;
break;
case vBLUE_FLAG:
*pFirstSlot = 16;
*pLastSlot = 16;
break;
case vYELLOW_FLAG:
*pFirstSlot = 17;
*pLastSlot = 17;
break;
default:
Dbg_MsgAssert(0,("Unhandled restart type %d",type));
break;
}
}
// should be called by CParkManager::DestroyConcreteMeta() (was Map::RemovePiece())
void CParkGenerator::RemoveRestart(const GridDims &dims, RestartType type)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
for (int i = first_slot; i <= last_slot; i++)
{
// if (m_restart_tab[i].area.x == area.x &&
// m_restart_tab[i].area.y == area.y &&
// m_restart_tab[i].area.z == area.z)
if (
m_restart_tab[i].dims.GetX() == dims.GetX()
&& m_restart_tab[i].dims.GetY() == dims.GetY()
&& m_restart_tab[i].dims.GetZ() == dims.GetZ()
)
{
m_restart_tab[i].type = vEMPTY;
}
}
}
// should be called from CCursor::AttemptStamp() (was Cursor::AttemptPlacePiece())
bool CParkGenerator::FreeRestartExists(RestartType type)
{
int first_slot = 0, last_slot = 0;
get_restart_slots(type, &first_slot, &last_slot);
for (int i = first_slot; i <= last_slot; i++)
if (m_restart_tab[i].type == vEMPTY || m_restart_tab[i].automatic)
{
// m_restart_tab[i].type = vEMPTY;
return true;
}
return false;
}
/*
should be called from:
-CParkGenerator::InitializeMasterPieces() (was World::CreateParkHeap())
-CParkGenerator::DestroyAllPieces() or CParkManager::destroy_concrete_metapieces() (was World::DestroyAllPieces())
*/
void CParkGenerator::KillRestarts()
{
#ifdef DEBUG_RESTARTS
printf ("%d: KillRestarts called, all restarts set to vEMPTY",__LINE__);
#endif
for (int i = 0; i < vNUM_RESTARTS; i++)
m_restart_tab[i].type = vEMPTY;
}
void CParkGenerator::ClearAutomaticRestarts()
{
#ifdef DEBUG_RESTARTS
printf ("%d: ClearAutomaticRestarts called, all automatic restarts set to vEMPTY",__LINE__);
#endif
for (int i = 0; i < vNUM_RESTARTS; i++)
{
if (m_restart_tab[i].automatic)
{
m_restart_tab[i].type = vEMPTY;
}
}
}
/*
Left edge of gap is at position pos. Bottom edge sticks out in direction
indicated by rot, for length units.
*/
CClonedPiece *CParkGenerator::CreateGapPiece(Mth::Vector &pos, float length, int rot)
{
// maken der gapen piece
CClonedPiece *p_gap_piece = ClonePiece(GetMasterPiece(Script::GenerateCRC("Sk4Ed_Gap_10x10"), true), CPiece::mNO_FLAGS);
p_gap_piece->SetDesiredPos(pos, CClonedPiece::CHANGE_SECTOR);
p_gap_piece->SetDesiredRot(Mth::ERot90(rot), CClonedPiece::CHANGE_SECTOR);
p_gap_piece->set_flag(CPiece::mGAP);
AddClonedPieceToWorld(p_gap_piece);
// XXX
Ryan("@@@ made gap piece 0x%x\n", p_gap_piece);
return p_gap_piece;
}
}