#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include DefinePoolableClass(Ed::CMapListNode); #ifdef __PLAT_NGC__ #define _16(a) (((a>>8)&0x00ff)|((a<<8)&0xff00)) #define _32(a) (((a>>24)&0x000000ff)|((a>>8)&0x0000ff00)|((a<<8)&0x00ff0000)|((a<<24)&0xff000000)) #else #define _16(a) a #define _32(a) a #endif // __PLAT_NGC__ #if 1 #ifdef __PLAT_NGPS__ #define __USE_EXTERNAL_BUFFER__ #include #include #define __EXTERNAL_BUFFER__ NxPs2::dma::pRuntimeBuffer #define ACQUIRE_BUFFER Nx::CEngine::sFinishRendering(); #endif #endif namespace Ed { DefineSingletonClass(CParkManager, "Park Manager"); CParkManager *CParkManager::sp_instance = NULL; GridDims::GridDims(uint8 x, sint8 y, uint8 z, uint8 w, uint8 h, uint8 l) { m_dims[0] = x; m_dims[1] = (uint8) y; m_dims[2] = z; m_dims[3] = w; m_dims[4] = h; m_dims[5] = l; } uint8 &GridDims::operator [](sint i) { Dbg_Assert(i < 6); return m_dims[i]; } // Effectively gives the area an H of infinity void GridDims::MakeInfinitelyHigh() { m_dims[4] = 127 - (sint8) m_dims[1]; } // Effectively gives the area an H of infinity void GridDims::MakeInfiniteOnY() { m_dims[1] = (uint8) -100; m_dims[4] = 200; } void GridDims::PrintContents() const { printf("pos=(%d,%d,%d) dims=(%d,%d,%d)\n", m_dims[0], (sint8) m_dims[1], m_dims[2], m_dims[3], m_dims[4], m_dims[5]); } CMetaPiece::CMetaPiece() { m_flags = EFlags(0); mp_additional_desc_tab = NULL; m_total_entries = 0; m_num_used_entries = 0; } CMetaPiece::~CMetaPiece() { if (mp_additional_desc_tab) delete mp_additional_desc_tab; } CConcreteMetaPiece *CMetaPiece::CastToConcreteMeta() { Dbg_MsgAssert(this == NULL || (m_flags & mCONCRETE_META), ("not a concrete metapiece")); return (CConcreteMetaPiece *) this; } CAbstractMetaPiece *CMetaPiece::CastToAbstractMeta() { Dbg_MsgAssert(this == NULL || (m_flags & mABSTRACT_META), ("not an abstract metapiece")); return (CAbstractMetaPiece *) this; } bool CMetaPiece::IsRestartOrFlag() { switch (m_name_checksum) { case CRCC(0xdc09a6a4,"Sk3Ed_RS_1p"): case CRCC(0xca07b4fc,"Sk3Ed_RS_Mp"): case CRCC(0x3a784d4c,"Sk3Ed_Rs_Ho"): case CRCC(0x4e2efd2a,"Sk3Ed_Rs_KOTH"): case CRCC(0x613c7766,"Sk4Ed_Team_Yellow"): case CRCC(0xec82433c,"Sk4Ed_Team_Green"): case CRCC(0xd7eb62b5,"Sk4Ed_Team_Red"): case CRCC(0x8a1576b6,"Sk4Ed_Team_Blue"): return true; break; default: break; } return false; } bool CMetaPiece::IsCompletelyWithinArea(const GridDims &area) { if (m_cell_area.GetX() >= area.GetX() && m_cell_area.GetX()+m_cell_area.GetW() <= area.GetX()+area.GetW() && m_cell_area.GetZ() >= area.GetZ() && m_cell_area.GetZ()+m_cell_area.GetL() <= area.GetZ()+area.GetL()) { return true; } return false; } /* Use to set rotation of abstract metapiece for testing purposes (in GetMetaPiecesAt()), or to set rotation of concrete metapiece before adding to park. */ void CMetaPiece::SetRot(Mth::ERot90 newRot) { Dbg_MsgAssert(!(m_flags & CMetaPiece::mIN_PARK), ("metapiece already in park -- can't change rotation")); // just want to change relative to current rotation Mth::ERot90 rot = Mth::ERot90((newRot + 4 - m_rot) & 3); m_rot = newRot; if (rot & 1) { // switch W and L m_cell_area.SetWHL(m_cell_area.GetL(), m_cell_area.GetH(), m_cell_area.GetW()); } SMetaDescriptor temp_new_first[USUAL_NUM_DESCRIPTORS]; // Create a new table, which will soon replace the old one SMetaDescriptor *p_meta_tab = NULL; if (mp_additional_desc_tab) { Mem::Manager::sHandle().PushContext(CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap()); p_meta_tab = new SMetaDescriptor[m_total_entries-USUAL_NUM_DESCRIPTORS]; Mem::Manager::sHandle().PopContext(); } for (uint i = 0; i < m_num_used_entries; i++) { // "dims" contains position of upper-left, after rotation SMetaDescriptor &out = (i < USUAL_NUM_DESCRIPTORS) ? temp_new_first[i] : p_meta_tab[i-USUAL_NUM_DESCRIPTORS]; SMetaDescriptor &in = (i < USUAL_NUM_DESCRIPTORS) ? m_first_desc_tab[i] : mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS]; out = in; // dimensions with current rotation (not new) // OOG! GridDims in_dims(0, 0, 0); if (m_flags & mABSTRACT_META) { CAbstractMetaPiece *p_meta = CParkManager::sInstance()->GetAbstractMeta(in.mPieceName); in_dims = p_meta->GetArea(); if ((in.mRot & 1)) { // for a concrete metapiece, the retrieved cell dimensions will already reflect // the current rotation, so no need to enter this block in_dims.SetWHL(in_dims.GetL(), in_dims.GetH(), in_dims.GetW()); // switch W & L } } else { in.mpPiece->GetCellDims(&in_dims); } switch(rot) { case Mth::ROT_0: out.mX = in.mX; out.mZ = in.mZ; break; case Mth::ROT_90: out.mX = in.mZ; out.mZ = m_cell_area.GetL() - in.mX - in_dims.GetW(); break; case Mth::ROT_180: out.mX = m_cell_area.GetW() - in.mX - in_dims.GetW(); out.mZ = m_cell_area.GetL() - in.mZ - in_dims.GetL(); break; case Mth::ROT_270: out.mX = m_cell_area.GetW() - in.mZ - in_dims.GetL(); out.mZ = in.mX; break; default: Dbg_MsgAssert(0, ("ahhh-oooop?")); break; } out.mY = in.mY; out.mRot = Mth::ERot90((in.mRot + rot) & 3); // this will also copy in.mPieceName, if this is an abstract metapiece out.mpPiece = in.mpPiece; } // end for if (mp_additional_desc_tab) delete mp_additional_desc_tab; mp_additional_desc_tab = p_meta_tab; for (int i = 0; i < USUAL_NUM_DESCRIPTORS; i++) { m_first_desc_tab[i] = temp_new_first[i]; //printf("desc at (%d,%d,%d)\n", m_first_desc_tab[i].mX, m_first_desc_tab[i].mY, m_first_desc_tab[i].mZ); } } // returns postion of piece's center, relative to center of metapiece Mth::Vector CMetaPiece::GetRelativePosOfContainedPiece(int index) { // center_pos in world coordinates Mth::Vector center_pos; center_pos[X] = (float) m_cell_area.GetW() * CParkGenerator::CELL_WIDTH / 2.0f; center_pos[Y] = 0; center_pos[Z] = (float) m_cell_area.GetL() * CParkGenerator::CELL_LENGTH / 2.0f; SMetaDescriptor &desc = get_desc_at_index(index); CPiece *p_piece = GetContainedPiece(index); Mth::Vector piece_pos(desc.mX * CParkGenerator::CELL_WIDTH + p_piece->GetDimsWithRot(Mth::ERot90(desc.mRot)).GetX() / 2.0f, desc.mY * CParkGenerator::CELL_HEIGHT, desc.mZ * CParkGenerator::CELL_LENGTH + p_piece->GetDimsWithRot(Mth::ERot90(desc.mRot)).GetZ() / 2.0f); return piece_pos - center_pos; } void CMetaPiece::BuildElement3dSectorsArray(Script::CArray *pArray) { pArray->SetSizeAndType(m_num_used_entries, ESYMBOLTYPE_STRUCTURE); for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); Script::CStruct *p_struct = new Script::CStruct(); p_struct->AddChecksum(NONAME, desc.mPieceName); Mth::Vector pos = GetRelativePosOfContainedPiece(i); p_struct->AddVector(NONAME, pos.GetX(), pos.GetY(), pos.GetZ()); pArray->SetStructure(i, p_struct); } } /* Creates new table of SMetaDescriptors. Might copy old one. */ void CMetaPiece::initialize_desc_table(int numEntries) { SMetaDescriptor *p_new_tab = NULL; if (numEntries > USUAL_NUM_DESCRIPTORS) { Mem::Manager::sHandle().PushContext(CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap()); p_new_tab = new SMetaDescriptor[numEntries-USUAL_NUM_DESCRIPTORS]; Mem::Manager::sHandle().PopContext(); } // Copy old table, if any. Only copied if there are to be at least as many entries as // there were before. if (p_new_tab && mp_additional_desc_tab && m_total_entries <= (uint) numEntries) { for (uint i = USUAL_NUM_DESCRIPTORS; i < m_total_entries; i++) p_new_tab[i-USUAL_NUM_DESCRIPTORS] = mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS]; } if (mp_additional_desc_tab) delete mp_additional_desc_tab; mp_additional_desc_tab = p_new_tab; m_total_entries = numEntries; } /* Returns specified descriptor. */ SMetaDescriptor &CMetaPiece::get_desc_at_index(int i) { if (i < USUAL_NUM_DESCRIPTORS) return m_first_desc_tab[i]; Dbg_MsgAssert(mp_additional_desc_tab && (uint) i < m_num_used_entries, ("descriptor entry doesn't exist")); return mp_additional_desc_tab[i-USUAL_NUM_DESCRIPTORS]; } CConcreteMetaPiece::CConcreteMetaPiece() { m_flags = EFlags(m_flags | mCONCRETE_META); m_rot = 0; } int CConcreteMetaPiece::CountContainedPieces() { return m_num_used_entries; } CPiece *CConcreteMetaPiece::GetContainedPiece(int index) { if ((uint) index >= m_num_used_entries || index < 0) return NULL; return get_desc_at_index(index).mpPiece; } /* Used for the cursor piece, allowing any position and rotation, not just cell-aligned with 90 degree increments. Doesn't affect "hard" position, rotation. */ void CConcreteMetaPiece::SetSoftRot(Mth::Vector pos, float rot) { //Ryan("moving metapiece to (%.2f,%.2f,%.2f)\n", pos[X], pos[Y], pos[Z]); Mth::Vector center(m_cell_area.GetW() * CParkGenerator::CELL_WIDTH / 2.0f, 0.0f, m_cell_area.GetL() * CParkGenerator::CELL_LENGTH / 2.0f); for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); // BAKU Mth::Vector piece_dims = desc.mpPiece->GetDims(); Mth::Vector piece_pos(desc.mX * CParkGenerator::CELL_WIDTH + piece_dims[X] / 2.0f - center[X], desc.mY * CParkGenerator::CELL_HEIGHT - center[Y], desc.mZ * CParkGenerator::CELL_LENGTH + piece_dims[Z] / 2.0f - center[Z]); Mth::Matrix rot_mat; rot_mat.Ident(); rot_mat.SetPos(piece_pos); rot_mat.RotateY(Mth::DegToRad(rot)); rot_mat.Translate(pos); //Ryan(" sub-piece position (%d) (%.2f,%.2f,%.2f)\n", desc.mY, rot_mat[Mth::POS][X], rot_mat[Mth::POS][Y], rot_mat[Mth::POS][Z]); desc.mpPiece->CastToCClonedPiece()->SetDesiredPos(rot_mat[Mth::POS], CClonedPiece::CHANGE_SECTOR); //desc.mpPiece->CastToCClonedPiece()->SetDesiredRot(Mth::ROT_0, CClonedPiece::CHANGE_SECTOR); float soft_rot = 90.0f * (float) desc.mRot + rot; desc.mpPiece->CastToCClonedPiece()->SetSoftRot(soft_rot); } } // Turn on or off the highlight effect for this concrete meta piece // which just turns the effect on or off for the cloned pieces that make it up void CConcreteMetaPiece::Highlight(bool on, bool gapHighlight) { for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); if (desc.mpPiece->GetFlags() & CPiece::mCLONED_PIECE) { desc.mpPiece->CastToCClonedPiece()->Highlight(on, gapHighlight); } } } void CConcreteMetaPiece::SetVisibility(bool visible) { for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); desc.mpPiece->CastToCClonedPiece()->SetActive(visible); } } /* Use after calls to add_piece() have been made. Once metapiece has been locked, it can be added to the world or used as a cursor */ void CConcreteMetaPiece::lock() { // work out dimensions of metapiece from contained pieces Mth::Vector min_pos, max_pos; get_max_min_world_coords(max_pos, min_pos); Mth::Vector my_dims; my_dims.Set(max_pos.GetX() - min_pos.GetX(), max_pos.GetY() - min_pos.GetY(), max_pos.GetZ() - min_pos.GetZ()); m_cell_area.SetWHL((uint8) ((my_dims.GetX() + 2.0f) / CParkGenerator::CELL_WIDTH), (uint8) ((my_dims.GetY() + 2.0f) / CParkGenerator::CELL_HEIGHT), (uint8) ((my_dims.GetZ() + 2.0f) / CParkGenerator::CELL_LENGTH)); if (m_cell_area[H] < 1) m_cell_area[H] = 1; //Dbg_Assert(m_cell_area.mW > 0 && m_cell_area.mL > 0); // reposition all contained pieces relative to center bottom for (uint i = 0; i < m_num_used_entries; i++) { CClonedPiece *p_piece = get_desc_at_index(i).mpPiece->CastToCClonedPiece(); //Mth::Vector piece_dims = p_piece->GetDims(); p_piece->m_pos[X] = p_piece->m_pos.GetX() - min_pos.GetX() - my_dims.GetX() / 2.0f; p_piece->m_pos[Z] = p_piece->m_pos.GetZ() - min_pos.GetZ() - my_dims.GetZ() / 2.0f; p_piece->m_pos[Y] = p_piece->m_pos.GetY(); // - min_pos.GetY(); } } /* Called by lock() */ void CConcreteMetaPiece::get_max_min_world_coords(Mth::Vector &max, Mth::Vector &min) { min.Set(100000.0f, 100000.0f, 100000.0f, 0.0f); max.Set(-100000.0f, -100000.0f, -100000.0f, 0.0f); for (uint i = 0; i < m_num_used_entries; i++) { CClonedPiece *p_piece = get_desc_at_index(i).mpPiece->CastToCClonedPiece(); Mth::Vector piece_dims = p_piece->GetDims(); Mth::Vector piece_pos = p_piece->GetPos(); if (piece_pos.GetX() - piece_dims.GetX() / 2.0f < min.GetX()) min[X] = piece_pos.GetX() - piece_dims.GetX() / 2.0f; if (piece_pos.GetX() + piece_dims.GetX() / 2.0f > max.GetX()) max[X] = piece_pos.GetX() + piece_dims.GetX() / 2.0f; if (piece_pos.GetZ() - piece_dims.GetZ() / 2.0f < min.GetZ()) min[Z] = piece_pos.GetZ() - piece_dims.GetZ() / 2.0f; if (piece_pos.GetZ() + piece_dims.GetZ() / 2.0f > max.GetZ()) max[Z] = piece_pos.GetZ() + piece_dims.GetZ() / 2.0f; if (piece_pos.GetY() < min.GetY()) min[Y] = piece_pos.GetY(); if (piece_pos.GetY() + piece_dims.GetY() > max.GetY()) max[Y] = piece_pos.GetY() + piece_dims.GetY(); } //Dbg_Assert(max[X] - min[X] > 1.0f && max[Z] - min[Z] > 1.0f); } /* Determines if overlap between specified area of park and this metapiece. If excludeRisers set, then we ignore overlaps with contained CPieces that are risers. */ bool CConcreteMetaPiece::test_for_conflict(GridDims area, bool excludeRisers) { // for now, we simply see if this metapiece overlaps with the area // later, we may add code that tests against contained pieces Dbg_Assert(m_cell_area.GetW() > 0 && m_cell_area.GetL() > 0 && m_cell_area.GetH() > 0); Dbg_MsgAssert(area.GetW() > 0 && area.GetL() > 0 && area.GetH() > 0, ("invalid dimensions (%d,%d,%d)", area.GetW(), area.GetW(), area.GetL())); // do quick test if (m_cell_area.GetX() + m_cell_area.GetW() <= area.GetX()) return false; if (m_cell_area.GetX() >= area.GetX() + area.GetW()) return false; if (m_cell_area.GetY() + m_cell_area.GetH() <= area.GetY()) return false; if (m_cell_area.GetY() >= area.GetY() + area.GetH()) return false; if (m_cell_area.GetZ() + m_cell_area.GetL() <= area.GetZ()) return false; if (m_cell_area.GetZ() >= area.GetZ() + area.GetL()) return false; bool contains_risers = false; if (excludeRisers) { for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); if (desc.mRiser) contains_risers = true; } } // '|| if !excludeRisers' is implicit if (!contains_risers) return true; for (uint i = 0; i < m_num_used_entries; i++) { SMetaDescriptor &desc = get_desc_at_index(i); CClonedPiece *p_piece = GetContainedPiece(i)->CastToCClonedPiece(); GridDims piece_area; p_piece->GetCellDims(&piece_area); piece_area[X] = m_cell_area.GetX() + desc.mX; piece_area[Y] = m_cell_area.GetY() + desc.mY; piece_area[Z] = m_cell_area.GetZ() + desc.mZ; if (!desc.mRiser && piece_area.GetX() + piece_area.GetW() > area.GetX() && piece_area.GetY() + piece_area.GetH() > area.GetY() && piece_area.GetZ() + piece_area.GetL() > area.GetZ() && piece_area.GetX() < area.GetX() + area.GetW() && piece_area.GetY() < area.GetY() + area.GetH() && piece_area.GetZ() < area.GetZ() + area.GetL()) { return true; } } return false; } /* See comments with original declaration. It is fine if pPiece has been rotated already. */ void CConcreteMetaPiece::add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot, bool isRiser) { Dbg_Assert(pPiece); Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mIN_WORLD), ("piece already in world")); CClonedPiece *p_cloned = pPiece->CastToCClonedPiece(); #ifdef __NOPT_ASSERT__ if (m_num_used_entries > 0) { if (get_desc_at_index(0).mpPiece->GetFlags() & CPiece::mSOFT_PIECE) { Dbg_MsgAssert(pPiece->GetFlags() & CPiece::mSOFT_PIECE, ("must be a soft piece")); } else { Dbg_MsgAssert(!(pPiece->GetFlags() & CPiece::mSOFT_PIECE), ("must be a hard piece")); } } #endif Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries")); SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++); // Set position relative to northwest bottom of piece. For now. Mth::Vector piece_dims = p_cloned->GetDims(); Mth::Vector desired_pos; desired_pos.Set(pPos->GetX() * CParkGenerator::CELL_WIDTH + piece_dims.GetX() / 2.0f, pPos->GetY() * CParkGenerator::CELL_HEIGHT, // + piece_dims.GetY() / 2.0f, pPos->GetZ() * CParkGenerator::CELL_LENGTH + piece_dims.GetZ() / 2.0f); p_cloned->SetDesiredPos(desired_pos, CClonedPiece::MARKER_ONLY); desc.mpPiece = p_cloned; desc.mX = pPos->GetX(); desc.mY = pPos->GetY(); desc.mZ = pPos->GetZ(); desc.mRot = rot; desc.mRiser = isRiser ? 1 : 0; /* if (isRiser) { printf("hoogaboog 2 (%d,%d,%d), desc.mRiser is %d, desc.mRiser at 0x%x, index %d\n", desc.mX, desc.mY, desc.mZ, desc.mRiser, &desc.mRiser, m_num_used_entries-1); } */ } CAbstractMetaPiece::CAbstractMetaPiece() { m_flags = EFlags(m_flags | mABSTRACT_META); } CPiece *CAbstractMetaPiece::GetContainedPiece(int index) { if ((uint) index >= m_num_used_entries || index < 0) return NULL; CPiece *p_piece = CParkManager::sInstance()->GetGenerator()->GetMasterPiece(get_desc_at_index(index).mPieceName); Dbg_Assert(p_piece); return p_piece; } /* See comments with original declaration. It is fine if pPiece has been rotated already. Automatically changes stored cell WHL dimensions of metapiece to include new addition. */ void CAbstractMetaPiece::add_piece(CPiece *pPiece, GridDims *pPos, Mth::ERot90 rot, bool isRiser) { Dbg_Assert(pPiece); CSourcePiece *p_source = NULL; if (pPiece->GetFlags() & CPiece::mSOURCE_PIECE) p_source = pPiece->CastToCSourcePiece(); if (!p_source) Dbg_MsgAssert(0, ("not supported yet")); Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries")); SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++); if (p_source) { desc.mX = pPos->GetX(); desc.mY = pPos->GetY(); desc.mZ = pPos->GetZ(); desc.mRot = rot; desc.mRiser = (uint8) isRiser; desc.mPieceName = p_source->GetType(); int highest_x = pPos->GetX() + pPos->GetW(); int highest_y = pPos->GetY() + pPos->GetH(); int highest_z = pPos->GetZ() + pPos->GetL(); uint8 current_w = m_cell_area.GetW(); uint8 current_h = m_cell_area.GetH(); uint8 current_l = m_cell_area.GetL(); if (highest_x > current_w) current_w = highest_x; if (highest_y > current_h) current_h = highest_y; if (highest_z > current_l) current_l = highest_z; m_cell_area.SetWHL(current_w, current_h, current_l); if (m_cell_area[H] < 1) m_cell_area[H] = 1; } /* if (isRiser) { printf("hoogaboog 1 (%d,%d,%d), index %d\n", desc.mX, desc.mY, desc.mZ, m_num_used_entries-1); } */ } void CAbstractMetaPiece::add_piece_dumb(CAbstractMetaPiece *pMeta, GridDims *pPos, Mth::ERot90 rot, bool isRiser) { Dbg_MsgAssert(m_num_used_entries < m_total_entries, ("out of entries")); SMetaDescriptor &desc = get_desc_at_index(m_num_used_entries++); if (pMeta) { desc.mX = pPos->GetX(); desc.mY = pPos->GetY(); desc.mZ = pPos->GetZ(); desc.mRot = rot; desc.mRiser = (uint8) isRiser; desc.mPieceName = pMeta->GetNameChecksum(); int highest_x = pPos->GetX() + pPos->GetW(); int highest_y = pPos->GetY() + pPos->GetH(); int highest_z = pPos->GetZ() + pPos->GetL(); uint8 current_w = m_cell_area.GetW(); uint8 current_h = m_cell_area.GetH(); uint8 current_l = m_cell_area.GetL(); if (highest_x > current_w) current_w = highest_x; if (highest_y > current_h) current_h = highest_y; if (highest_z > current_l) current_l = highest_z; m_cell_area.SetWHL(current_w, current_h, current_l); } /* if (isRiser) { printf("hoogaboog 1 (%d,%d,%d), index %d\n", desc.mX, desc.mY, desc.mZ, m_num_used_entries-1); } */ } SMetaDescriptor CAbstractMetaPiece::get_desc_with_expansion(int index) { #if 1 int count = 0; for (uint j = 0; j < m_num_used_entries; j++) { SMetaDescriptor &outer_desc = get_desc_at_index(j); CAbstractMetaPiece *p_contained_meta = CParkManager::sInstance()->GetAbstractMeta(outer_desc.mPieceName); if (p_contained_meta->m_flags & CMetaPiece::mSINGULAR) { if (count == index) return outer_desc; count++; } else { for (uint i = 0; i < p_contained_meta->m_num_used_entries; i++) { SMetaDescriptor &inner_desc = p_contained_meta->get_desc_at_index(i); if (count == index) { SMetaDescriptor ret_desc = inner_desc; ret_desc.mPieceName = inner_desc.mPieceName; ret_desc.mRot = inner_desc.mRot + outer_desc.mRot; GridDims outer_dims = GetArea(); GridDims inner_dims = p_contained_meta->GetArea(); ret_desc.mX = outer_desc.mX; ret_desc.mZ = outer_desc.mZ; ret_desc.mY = outer_desc.mY + inner_desc.mY; switch(outer_desc.mRot) { case Mth::ROT_0: ret_desc.mX += inner_desc.mX; ret_desc.mZ += inner_desc.mZ; break; case Mth::ROT_90: ret_desc.mX += inner_desc.mZ; ret_desc.mZ += outer_dims.GetW() - inner_desc.mX - inner_dims.GetW(); break; case Mth::ROT_180: ret_desc.mX += outer_dims.GetW() - inner_desc.mX - inner_dims.GetW(); ret_desc.mZ += outer_dims.GetL() - inner_desc.mZ - inner_dims.GetL(); break; case Mth::ROT_270: ret_desc.mX += outer_dims.GetL() - inner_desc.mZ - inner_dims.GetL(); ret_desc.mZ += inner_desc.mX; break; } return ret_desc; } count++; } } } Dbg_MsgAssert(0, ("what the bloody 'ell?")); SMetaDescriptor dumb; return dumb; #else return get_desc_at_index(index); #endif } int CAbstractMetaPiece::count_descriptors_expanded() { #if 1 int count = 0; for (uint j = 0; j < m_num_used_entries; j++) { SMetaDescriptor &outer_desc = get_desc_at_index(j); CAbstractMetaPiece *p_contained_meta = CParkManager::sInstance()->GetAbstractMeta(outer_desc.mPieceName); Dbg_MsgAssert(p_contained_meta, ("couldn't find metapiece %s", Script::FindChecksumName(outer_desc.mPieceName))); if (p_contained_meta->m_flags & CMetaPiece::mSINGULAR) { count++; } else { for (uint i = 0; i < p_contained_meta->m_num_used_entries; i++) { count++; } } } return count; #else return m_num_used_entries; #endif } CMetaPiece *CMapListNode::GetMeta() { Dbg_Assert(this); Dbg_MsgAssert(((uint32)this) != 0x03030303,("Bad CMapListNode pointer ! (0x03030303)")); Dbg_MsgAssert(((uint32)this) != 0x55555555,("Bad CMapListNode pointer ! (0x55555555)")); Dbg_Assert(mp_meta); return mp_meta; } void CMapListNode::DestroyList() { CMapListNode *p_node = this; while (p_node) { CMapListNode *p_next_node = p_node->mp_next; delete p_node; p_node = p_next_node; } } void CMapListNode::VerifyIntegrity() { CMapListNode *p_outer_node = this; while (p_outer_node) { CMapListNode *p_inner_node = p_outer_node->mp_next; while (p_inner_node) { Dbg_Assert(p_inner_node->GetMeta() != p_outer_node->GetMeta()); p_inner_node = p_inner_node->mp_next; } p_outer_node = p_outer_node->mp_next; } } CMapListTemp::CMapListTemp(CMapListNode *p_list) { mp_list = p_list; } CMapListTemp::~CMapListTemp() { if (mp_list) mp_list->DestroyList(); } CMapListNode *CMapListTemp::GetList() { return mp_list; } void CMapListTemp::PrintContents() { printf("contents of map list:\n"); CMapListNode *p_node = mp_list; while(p_node) { printf(" address=%p area=", p_node->GetMeta()); p_node->GetMeta()->GetArea().PrintContents(); p_node = p_node->GetNext(); } } CParkManager::CParkManager() { mp_generator = new CParkGenerator(); mp_concrete_metapiece_list = NULL; mp_abstract_metapiece_list = NULL; m_num_concrete_metapieces = 0; m_num_general_concrete_metapieces = 0; m_dma_piece_count = 0; m_num_abstract_metapieces = 0; m_floor_height_map = NULL; for (int i = 0; i < FLOOR_MAX_WIDTH; i++) for (int j = 0; j < FLOOR_MAX_LENGTH; j++) mp_bucket_list[i][j] = NULL; m_state_on = false; sp_instance = this; mp_compressed_map_buffer = new uint8[COMPRESSED_MAP_SIZE]; m_compressed_map_flags = mNO_FLAGS; m_park_is_valid = false; mp_gap_manager = NULL; for (int i = 0; i < NUM_RISER_INFO_SLOTS; i++) { char name0[128]; sprintf(name0, "floor_wall_block%d", i+1); m_riser_piece_checksum[i][0] = Script::GenerateCRC(name0); char name1[128]; sprintf(name1, "wall_block%d", i+1); m_riser_piece_checksum[i][1] = Script::GenerateCRC(name1); char name2[128]; sprintf(name2, "floor_block%d", i+1); m_riser_piece_checksum[i][2] = Script::GenerateCRC(name2); } m_theme = 0; // Mick: Create a compressed map buffer, equivalent to an empty park fake_compressed_map_buffer(); mp_column_slide_list = NULL; mp_park_name[0]=0; #ifdef USE_BUILD_LIST m_build_list_size=0; mp_build_list_entry=NULL; #endif } CParkManager::~CParkManager() { #ifdef USE_BUILD_LIST if (mp_build_list_entry) { #ifndef __USE_EXTERNAL_BUFFER__ Mem::Free(mp_build_list_entry); #endif mp_build_list_entry=NULL; } #endif delete mp_generator; delete mp_compressed_map_buffer; sp_instance = NULL; } /* Boots up park editor. -Loads up files needed -Initializes buffers -Initializes CParkGenerator module -Calls Rebuild(), which: -Clears map -Generates riser geometry -Creates piece set database */ void CParkManager::Initialize() { ParkEd("CParkManager::Initialize()"); if (m_state_on) return; m_state_on = true; Mem::Manager::sHandle().PushContext(Mem::Manager::sHandle().TopDownHeap()); Mem::CPoolable::SCreatePool(16000, "CMapListNode"); Mem::CPoolable::SPrintInfo(); Mem::CPoolable::SCreatePool(MAX_CLIPBOARD_METAS, "CClipboardEntry"); Mem::CPoolable::SCreatePool(MAX_CLIPBOARDS, "CClipboard"); Mem::Manager::sHandle().PopContext(); mp_generator->InitializeMasterPieces(MAX_WIDTH, 16, MAX_LENGTH, GetTheme()); mp_generator->ReadInRailInfo(); create_abstract_metapieces(); setup_road_mask(); Dbg_MsgAssert(!m_floor_height_map, ("bad monkey!!!")); m_floor_height_map = new FloorHeightEntry*[FLOOR_MAX_WIDTH]; for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { m_floor_height_map[i] = new FloorHeightEntry[FLOOR_MAX_LENGTH]; for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { m_floor_height_map[i][j].mMarkAsSlid = false; m_floor_height_map[i][j].mSlideAmount = 0; } } setup_default_dimensions(); mp_gap_manager = new CGapManager(this); RebuildWholePark(false); Script::CArray *p_set_array = Script::GetArray("Ed_piece_sets", Script::ASSERT); m_total_sets = p_set_array->GetSize(); Dbg_Assert(m_total_sets <= MAX_SETS); Script::CArray *p_piece_array = Script::GetArray("Ed_standard_metapieces", Script::ASSERT); int last_piece_index=p_piece_array->GetSize(); int last_set_index=-1; for (int s = m_total_sets-1; s >=0 ; --s) { Script::CStruct *p_set = p_set_array->GetStructure(s); m_palette_set[s].mpName = NULL; p_set->GetText("name", &m_palette_set[s].mpName, Script::ASSERT); m_palette_set[s].mNameCrc = Script::GenerateCRC(m_palette_set[s].mpName); m_palette_set[s].mSelectedEntry = 0; m_palette_set[s].mTotalEntries=0; uint32 first_checksum; if (p_set->GetChecksum("first", &first_checksum)) { int first_index=0; if (!GetAbstractMeta(first_checksum, &first_index)) { Dbg_MsgAssert(0, ("couldn't find first-in-set entry")); } // make sure index in previous first-in_set_entry is less than this one if (last_set_index>=0) { Dbg_MsgAssert(first_index < last_piece_index, ("sets %s and %s are out of order in EdPieces2.q", m_palette_set[s].mpName, m_palette_set[last_set_index].mpName)); } else { Dbg_MsgAssert(first_index < last_piece_index, ("set %s has a bad first piece in EdPieces2.q", m_palette_set[s].mpName)); } m_palette_set[s].mTotalEntries=last_piece_index-first_index; last_piece_index=first_index; last_set_index=s; // Now that we know the number of entries, create the set. int entry=first_index; for (int i=0; iGetNameChecksum(); Script::CStruct *p_piece_entry = p_piece_array->GetStructure(entry); if (!p_piece_entry->GetLocalString("text_name", &m_palette_set[s].mEntryTab[i].mpName)) { m_palette_set[s].mEntryTab[i].mpName = NULL; } ++entry; } } m_palette_set[s].mIsClipboardSet=false; if (p_set->ContainsFlag(CRCD(0x45c6c158,"clipboard_set"))) { m_palette_set[s].mIsClipboardSet=true; m_palette_set[s].mTotalEntries=MAX_CLIPBOARDS; const char *p_clipboard_title=Script::GetString(CRCD(0x4e677220,"ClipboardTitle")); for (int i=0; iContainsFlag("hidden"); #ifdef __PLAT_NGC__ if (p_set->ContainsFlag("no_gamecube")) m_palette_set[s].mVisible = false; #endif } } float CParkManager::GetClipboardProportionUsed() { float proportion_clipboard_entries=((float)Mem::CPoolable::SGetNumUsedItems())/Mem::CPoolable::SGetTotalItems(); float proportion_clipboard_slots=((float)Mem::CPoolable::SGetNumUsedItems())/Mem::CPoolable::SGetTotalItems(); if (proportion_clipboard_entries > proportion_clipboard_slots) { return proportion_clipboard_entries; } return proportion_clipboard_slots; } // This is just a public version of the private function // needed so the parks can be saved from within the park editor // This just updates the contents of the map buffer that will be used // by the enxt two public functions void CParkManager::WriteCompressedMapBuffer() { if (m_state_on) write_compressed_map_buffer(); } uint8* CParkManager::GetCompressedMapBuffer(bool markSaved) { /* The compressed map buffer is valid if: -The park manager is active AND (in sync with built park OR newer than built park) OR -The park manager is not active */ Dbg_MsgAssert((m_compressed_map_flags & mIS_VALID), ("compressed map buffer not valid")); if (m_state_on) { Dbg_MsgAssert((m_compressed_map_flags & mIN_SYNC_WITH_PARK) || (m_compressed_map_flags & mIS_NEWER_THAN_PARK), ("park has changed, compressed map buffer out of sync")); } #ifdef __NOPT_ASSERT__ CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; Dbg_Printf( "************ NUM METAS: %d\n", p_header->mNumMetas ); #endif // __NOPT_ASSERT__ if (markSaved) m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL); return mp_compressed_map_buffer; } void CParkManager::SetCompressedMapBuffer( uint8* src_buffer, bool markNotSaved ) { ParkEd("SetCompressedMapBuffer()"); memcpy( mp_compressed_map_buffer, src_buffer, COMPRESSED_MAP_SIZE ); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_VALID); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_NEWER_THAN_PARK); if (markNotSaved) m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL); #ifdef __NOPT_ASSERT__ CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; //uint32 checksum=Crc::GenerateCRCCaseSensitive((const char*)mp_compressed_map_buffer+4,COMPRESSED_MAP_SIZE-4); //Dbg_MsgAssert(checksum==p_header->mChecksum,("Park editor buffer checksum mismatch !")); Dbg_Printf( "************ NUM METAS: %d\n", p_header->mNumMetas ); #endif // __NOPT_ASSERT__ } bool CParkManager::IsMapSavedLocally() { if ((!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK)) || (m_compressed_map_flags & mNOT_SAVED_LOCAL)) return false; return true; } void CParkManager::WriteIntoStructure(Script::CStruct *p_struct) { WriteCompressedMapBuffer(); // Ensure map buffer is correct uint32 *p_map_buffer = (uint32*) GetCompressedMapBuffer(true); int size = COMPRESSED_MAP_SIZE ; // create the array Script::CArray *p_map=new Script::CArray; p_map->SetArrayType(size/4,ESYMBOLTYPE_INTEGER); // copy the park editor into it for (int i=0;iSetInteger(i,p_map_buffer[i]); } // append it to p_struct p_struct->AddArrayPointer(CRCD(0x337c5289,"Park_editor_map"),p_map); // Now also add in any created goals, so that the goals do not have to saved out to a separate file. Script::CStruct *p_goals_struct=new Script::CStruct; Obj::CGoalEditorComponent *p_goal_editor=Obj::GetGoalEditor(); Dbg_MsgAssert(p_goal_editor,("No goal editor component ???")); p_goal_editor->WriteIntoStructure(CRCD(0xe8b4b836,"Load_Sk5Ed"),p_goals_struct); p_struct->AddStructurePointer(CRCD(0xd8eb825e,"Park_editor_goals"),p_goals_struct); // Add in the created rails. Obj::GetRailEditor()->WriteIntoStructure(p_struct); p_struct->AddInteger(CRCD(0xb7e39b53,"MaxPlayers"),GetGenerator()->GetMaxPlayers()); } #ifdef __PLAT_NGC__ void CParkManager::ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load, bool preMadePark) #else void CParkManager::ReadFromStructure(Script::CStruct *p_struct, bool do_post_mem_card_load) #endif { Obj::CGoalEditorComponent *p_goal_editor=Obj::GetGoalEditor(); Dbg_MsgAssert(p_goal_editor,("No goal editor component ???")); p_goal_editor->ClearOnlyParkGoals(); // Read in any created goals. Script::CStruct *p_goals_struct=NULL; if (p_struct->GetStructure(CRCD(0xd8eb825e,"Park_editor_goals"),&p_goals_struct)) { p_goal_editor->ReadFromStructure(p_goals_struct,Obj::CGoalEditorComponent::LOADING_PARK_GOALS); } // Read in the created rails Spt::SingletonPtr p_park_ed; if (!p_park_ed->EditingCustomPark()) { // Do not call UpdateSuperSectors after deleting any existing rails if playing a park, // otherwise the entire park will disappear from under the skater due to all the sectors // having been flagged for deletion. Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=false; } Obj::GetRailEditor()->ReadFromStructure(p_struct); Obj::CRailEditorComponent::sUpdateSuperSectorsAfterDeletingRailSectors=true; int max_players=2; p_struct->GetInteger(CRCD(0xb7e39b53,"MaxPlayers"),&max_players); Ed::CParkManager::Instance()->GetGenerator()->SetMaxPlayers(max_players); int old_theme = GetTheme(); WriteCompressedMapBuffer(); uint32 *p_map_buffer = (uint32*) GetCompressedMapBuffer(); int size = Ed::CParkManager::COMPRESSED_MAP_SIZE ; // get the array Script::CArray *p_map=NULL; if (p_struct->GetArray("Park_editor_map",&p_map,true)) { // copy the park editor map from structure to buffer for (int i=0;iGetInteger(i); p_map_buffer[i] = ((v>>24) | (((v>>16)&0xff)<<8) | (((v>>8)&0xff)<<16) | ((v&0xff)<<24)); } else { p_map_buffer[i] = p_map->GetInteger(i); } #else p_map_buffer[i] = p_map->GetInteger(i); #endif } #ifdef __PLAT_NGC__ if (preMadePark) { // If loading a pre-made park, it will need the map buffer endianness swapped since // it was saved from the PS2. SwapMapBufferEndianness(); } #endif if (do_post_mem_card_load) { Spt::SingletonPtr p_park_ed; p_park_ed->PostMemoryCardLoad((uint8*) p_map_buffer, old_theme); } } } int CParkManager::GetTheme() { if (m_compressed_map_flags & mIS_VALID) { int theme = (((CompressedMapHeader *) mp_compressed_map_buffer)->mTheme); Dbg_MsgAssert(theme >= 0 && theme <= MAX_THEMES, ("nonsense theme %d", theme)); return theme; } return 0; } void CParkManager::SetTheme(int theme) { Dbg_MsgAssert(theme >= 0 && theme < MAX_THEMES , ("nonsense theme %d", theme)); m_theme = theme; write_compressed_map_buffer(); } const char *CParkManager::GetParkName() { if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION)) return ((CompressedMapHeader *) mp_compressed_map_buffer)->mParkName; else return NULL; } uint32 CParkManager::GetParkChecksum() { if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION)) return ((CompressedMapHeader *) mp_compressed_map_buffer)->mChecksum; else return 0; } int CParkManager::GetCompressedParkWidth() { if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION)) return ((CompressedMapHeader *) mp_compressed_map_buffer)->mW; else return 0; } int CParkManager::GetCompressedParkLength() { if (mp_compressed_map_buffer && ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion == (VERSION)) return ((CompressedMapHeader *) mp_compressed_map_buffer)->mL; else return 0; } void CParkManager::SetParkName(const char *pName) { if (!mp_compressed_map_buffer || ((CompressedMapHeader *) mp_compressed_map_buffer)->mVersion != (VERSION)) return; Dbg_Assert(pName); Dbg_MsgAssert(strlen(pName) < 64, ("park name too long")); strcpy(((CompressedMapHeader *) mp_compressed_map_buffer)->mParkName, pName); strcpy(mp_park_name, pName); } void CParkManager::AccessDisk(bool save, int fileSlot) { char fullname[64]; sprintf(fullname, "CustomParks\\custom%d.prk", fileSlot); if (save) { // Ryan("I am saving park %s\n", fullname); // printf("I am saving park %s\n", fullname); Script::CStruct *p_struct=new Script::CStruct; WriteIntoStructure(p_struct); uint32 size=Script::CalculateBufferSize(p_struct); uint8 *p_buffer=(uint8*)Mem::Malloc(size); Script::WriteToBuffer(p_struct,p_buffer,size); void *fp = File::Open(fullname, "wb"); Dbg_MsgAssert(fp, ("failed to open file %s for write", fullname)); File::Write(p_buffer, size, 1, fp); File::Close(fp); Mem::Free(p_buffer); delete p_struct; } else { // Ryan("I am loading park %s\n", fullname); // printf("I am loading park %s\n", fullname); void *fp = File::Open(fullname, "rb"); Dbg_MsgAssert(fp, ("failed to open file %s for read", fullname)); int size=File::GetFileSize(fp); uint8 *p_buffer=(uint8*)Mem::Malloc(size); File::Read(p_buffer, size, 1, fp); File::Close(fp); Script::CStruct *p_struct=new Script::CStruct; Script::ReadFromBuffer(p_struct,p_buffer); Mem::Free(p_buffer); #ifdef __PLAT_NGC__ ReadFromStructure(p_struct,false,true); #else ReadFromStructure(p_struct,false); #endif delete p_struct; m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIS_NEWER_THAN_PARK + mIS_VALID)); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIN_SYNC_WITH_PARK); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL); } } /* Destroys everything, concrete metas, abstract metas, floor map. */ // K: Added type so that DESTROY_ONLY_PIECES can be passed when this function is used to clean // up the park editor heap for deletion when playing a park. void CParkManager::Destroy(CParkGenerator::EDestroyType type) { ParkEd("CParkManager::Destroy(), m_compressed_map_flags=%08x", m_compressed_map_flags); if (!m_state_on) return; if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK)) { write_compressed_map_buffer(); } Dbg_Assert(mp_gap_manager); mp_gap_manager->RemoveAllGaps(); // remove all abstract metapieces CMapListNode *p_node = NULL; while((p_node = mp_abstract_metapiece_list)) { CMetaPiece *p_meta = p_node->GetMeta(); remove_metapiece_from_node_list(p_meta, &mp_abstract_metapiece_list); delete p_meta; } m_num_abstract_metapieces = 0; destroy_concrete_metapieces(type); mp_generator->UnloadMasterPieces(); mp_generator->DestroyRailInfo(); // info from node array (mostly rails) Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!")); for (int i = 0; i < FLOOR_MAX_WIDTH; i++) delete [] m_floor_height_map[i]; delete [] m_floor_height_map; m_floor_height_map = NULL; m_state_on = false; // since park is destroyed, buffer is out of sync and newer than park m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIN_SYNC_WITH_PARK); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIS_NEWER_THAN_PARK); m_park_is_valid = false; Dbg_Assert(mp_gap_manager); delete mp_gap_manager; mp_gap_manager = NULL; if (mp_column_slide_list) mp_column_slide_list->DestroyList(); mp_column_slide_list = NULL; Mem::CPoolable::SRemovePool(); Mem::CPoolable::SRemovePool(); Mem::CPoolable::SRemovePool(); } void CParkManager::RebuildWholePark(bool clearPark) { ParkEd("CParkManager::RebuildWholePark() 0x%x", m_compressed_map_flags); setup_lighting(); // ensure light arrays are correct for current theme Dbg_Assert(m_state_on); if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK) && m_park_is_valid) { //Ryan("writing compressed map buffer\n"); write_compressed_map_buffer(); } Dbg_Printf( "************* Rebuilding whole park : %d\n", (int) clearPark ); destroy_concrete_metapieces(CParkGenerator::DESTROY_PIECES_AND_SECTORS); mp_generator->GenerateCollisionInfo(false); if (clearPark || !(m_compressed_map_flags & mIS_VALID)) { //Ryan("clearing park\n"); Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!")); for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { int x = i; int z = j; m_floor_height_map[x][z].mHeight = 0; //Mth::Rnd(4); m_floor_height_map[x][z].mEnclosedFloor = 0; m_floor_height_map[x][z].mMarkAsSlid = false; m_floor_height_map[i][j].mSlideAmount = 0; } } write_compressed_map_buffer(); SetParkName("unnamed park"); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mNOT_SAVED_LOCAL); clearPark = true; } else { // use compressed map read_from_compressed_map_buffer(); } RebuildInnerShell(m_park_near_bounds); generate_floor_pieces_from_height_map(clearPark, false); mp_generator->GenerateCollisionInfo(); // mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list); m_park_is_valid = true; if (clearPark) { m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK); } // K: Now that the park geometry has been built and collision info generated, // update the created rails. Obj::GetRailEditor()->RefreshGeometry(); } /* -Might clear map -Generates riser geometry (from height map) The only place we call this function with firstTestMemory true is from CCursor::ChangeFloorHeight() */ bool CParkManager::RebuildFloor(bool firstTestMemory) { ParkEd("CParkManager::RebuildFloor()"); Dbg_Assert(m_state_on); //Dbg_Printf( "************* Rebuilding floor\n" ); if (firstTestMemory) { if (!generate_floor_pieces_from_height_map(false, true)) return false; } generate_floor_pieces_from_height_map(false, false); //CConcreteMetaPiece *p_out = CreateConcreteMeta(GetAbstractMeta(Script::GenerateCRC("gloopy"))); //GridDims dims(m_park_near_bounds.GetX(), 0, m_park_near_bounds.GetZ()); //AddMetaPieceToPark(dims, p_out); mp_generator->GenerateCollisionInfo(); return true; } void CParkManager::RebuildNodeArray() { ParkEd("CParkManager::RebuildNodeArray()"); // clear any pre-exisiting automatic restarts // so we have slots free to add new ones mp_generator->ClearAutomaticRestarts(); // add default restarts // (they won't be added if player-placed ones already exist) // there are 18 points // 1p, 2p, 4 flags, 6 horse, 6 KOTH for (int p = 0; p < 18; p++) { CParkGenerator::RestartType restart_type = CParkGenerator::vONE_PLAYER; GridDims restart_area; restart_area = GetParkNearBounds(); // returns the rectangle enclosed by the fence restart_area.SetXYZ(restart_area.GetX()+restart_area.GetW()/2, 0, restart_area.GetZ()+restart_area.GetL()/2); // sets xyz to the midpoint restart_area.SetWHL(1,1,1); // needed for when we get the floor height bool find_ground = true; // most restarts need to find the ground, except copied horse points int need = 1; // Most restarts only need one. Horse/Multi being the current exception if (p == 1) { restart_type = CParkGenerator::vMULTIPLAYER; restart_area.SetXYZ(restart_area.GetX()+1, 0, restart_area.GetZ()); } if (p == 2) { restart_type = CParkGenerator::vRED_FLAG; restart_area.SetXYZ(restart_area.GetX()-2, 0, restart_area.GetZ()+2); } if (p == 3) { restart_type = CParkGenerator::vGREEN_FLAG; restart_area.SetXYZ(restart_area.GetX()+0, 0, restart_area.GetZ()+2); } if (p == 4) { restart_type = CParkGenerator::vBLUE_FLAG; restart_area.SetXYZ(restart_area.GetX()+2, 0, restart_area.GetZ()+2); } if (p == 5) { restart_type = CParkGenerator::vYELLOW_FLAG; restart_area.SetXYZ(restart_area.GetX()+4, 0, restart_area.GetZ()+2); } // all the dummy horse/multi restart points go in a line at Z-2 if (p >= 6 && p <=11) // 6,7,8,9,10,11 { need = 6; restart_type = CParkGenerator::vHORSE; #if 0 // Code to make horse points be evenly distributed between 1p/2p nodes // but it's not usable, as the players will start at the same point // either all multi points need to be different, of the code needs // to check in 2P (and multi) for same node positions // either are too risky to implement right now static GridDims horse_points[6]; // needs to be static, so I can include it in this conditional GridDims dims; if (p==6) { // about to try the first horse point // so fill the array with appropriate values int num_actual_horse = mp_generator->NumRestartsOfType(restart_type); // printf ("There are %d actual horse points\n",num_actual_horse); if (num_actual_horse == 0) { // printf ("No horse points, using 1p/2p\n"); // not even got one, so fill the array with 1p,2p,1p,2p,1p,2p for (int i = 0;i<6;i+=2) { dims = mp_generator->GetRestartDims(CParkGenerator::vONE_PLAYER,0); horse_points[i] = restart_area; horse_points[i].SetXYZ(dims.GetX(),0,dims.GetZ()); dims = mp_generator->GetRestartDims(CParkGenerator::vMULTIPLAYER,0); horse_points[i+1] = restart_area; horse_points[i+1].SetXYZ(dims.GetX(),0,dims.GetZ()); } } else { // we have one or more real horse points, so find out where they are // and copy those values for any automatic horse points, if needed for (int i = 0;i<6;i++) { if (i < num_actual_horse) { // copy the n initial values into the array horse_points[i] = mp_generator->GetRestartDims(CParkGenerator::vHORSE,i); } else { // and duplicate these through the rest of the array // so we will get an even distribution // n = 1 gives [ 1 1 1 1 1 1 ] // n = 2 gives [ 1 2 1 2 1 2 ] // n = 3 gives [ 1 2 3 1 2 3 ] // n = 4 gives [ 1 2 3 4 1 2 ] // n = 5 gives [ 1 2 3 4 5 1 ] // n = 6 gives [ 1 2 3 4 5 6 ] horse_points[i] = horse_points[i % num_actual_horse]; } // printf ("Horse %d: (%d,%d,%d)\n",horse_points[i].GetX(),horse_points[i].GetY(),horse_points[i].GetX()); } } } // copy the appropiate value from the array restart_area = horse_points[p-6]; find_ground = false; // Don't find the ground when copying existing restarts, as we will be raised up by 1p/2p/horse pieces //printf ("Using Restart Area(%d,%d,%d)\n",restart_area.GetX(), restart_area.GetY(), restart_area.GetZ()); #else restart_area.SetXYZ(restart_area.GetX()-3 + (p-6), 0, restart_area.GetZ()-2); //printf ("Using Restart Area(%d,%d,%d)\n",restart_area.GetX(), restart_area.GetY(), restart_area.GetZ()); #endif } bool auto_copy = false; if ( p >= 12 && p <= 17 ) // 12,13,14,15,16,17 { auto_copy = true; // copy any existing restart point position, if there is one need = 6; restart_type = CParkGenerator::vKING_OF_HILL; restart_area.SetXYZ(restart_area.GetX()-3 + (p-12), 0, restart_area.GetZ()-3); } if (mp_generator->NotEnoughRestartsOfType(restart_type, need)) { if (find_ground) { restart_area.SetXYZ(restart_area.GetX(),GetFloorHeight(restart_area),restart_area.GetZ()); CMapListTemp meta_list = GetMetaPiecesAt(restart_area); if (!meta_list.IsEmpty()) { restart_area[Y] = restart_area.GetY() + meta_list.GetList()->GetConcreteMeta()->GetArea().GetH(); } } Mth::Vector restart_pos = GridCoordinatesToWorld(restart_area); restart_pos[X] = restart_pos[X] + CParkGenerator::CELL_WIDTH / 2.0f; restart_pos[Z] = restart_pos[Z] + CParkGenerator::CELL_LENGTH / 2.0f; // Fix to TT12221, where the flags would be floating if over a pool. CFeeler feeler; feeler.SetStart(restart_pos+Mth::Vector(0.0f, 10000.0f, 0.0f)); feeler.SetEnd(restart_pos+Mth::Vector(0.0f, -1000.0f, 0.0f)); if (feeler.GetCollision()) { restart_pos[Y]=feeler.GetPoint().GetY(); } // printf("restart point %d at: cell=(%d,%d,%d) world=(%.4f,%.4f,%.4f)\n", p, // restart_area.GetX(), restart_area.GetY(), restart_area.GetZ(), // restart_pos[X], restart_pos[Y], restart_pos[Z]); mp_generator->AddRestart(restart_pos, 0, restart_area, restart_type, true, auto_copy); } } // end for p mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list); } uint32 CParkManager::GetShellSceneID() { char p_shell_name[40]; if (GetTheme()) { sprintf(p_shell_name,"sk5ed%d_shell",GetTheme()+1); } else { strcpy(p_shell_name,"sk5ed_shell"); } return Script::GenerateCRC(p_shell_name); } void CParkManager::RebuildInnerShell(GridDims newNearBounds) { ParkEd("CParkManager::RebuildInnerShell()"); Dbg_Printf( "************* Rebuilding inner shell\n" ); #if 1 ParkEd("old shell bounds: (%d,%d,%d)-(%d,%d,%d)\n", m_park_near_bounds.GetX(), m_park_near_bounds.GetY(), m_park_near_bounds.GetZ(), m_park_near_bounds.GetW(), m_park_near_bounds.GetH(), m_park_near_bounds.GetL()); #endif m_park_near_bounds = newNearBounds; #if 1 ParkEd("new shell bounds: (%d,%d,%d)-(%d,%d,%d)\n", newNearBounds.GetX(), newNearBounds.GetY(), newNearBounds.GetZ(), newNearBounds.GetW(), newNearBounds.GetH(), newNearBounds.GetL()); #endif // west m_road_mask_tab[0][X] = m_park_near_bounds.GetX() - 1; m_road_mask_tab[0][Z] = m_park_near_bounds.GetZ() - 1; m_road_mask_tab[0][W] = 1; m_road_mask_tab[0][L] = m_park_near_bounds.GetL() + 2; // east m_road_mask_tab[1][X] = m_park_near_bounds.GetX() + m_park_near_bounds.GetW(); m_road_mask_tab[1][Z] = m_park_near_bounds.GetZ() - 1; m_road_mask_tab[1][W] = 1; m_road_mask_tab[1][L] = m_park_near_bounds.GetL() + 2; // north m_road_mask_tab[2][X] = m_park_near_bounds.GetX() - 1; m_road_mask_tab[2][Z] = m_park_near_bounds.GetZ() - 1; m_road_mask_tab[2][W] = m_park_near_bounds.GetW() + 2; m_road_mask_tab[2][L] = 1; // south m_road_mask_tab[3][X] = m_park_near_bounds.GetX() - 1; m_road_mask_tab[3][Z] = m_park_near_bounds.GetZ() + m_park_near_bounds.GetL(); m_road_mask_tab[3][W] = m_park_near_bounds.GetW() + 2; m_road_mask_tab[3][L] = 1; //#if DEBUG_THIS_DAMN_THING //int destroy_type2_count = 0; //#endif // destroy all existing inner shell pieces // also: kill everything that's outside the near bounds CMapListNode *p_node = mp_concrete_metapiece_list; while(p_node) { CMapListNode *p_next_node = p_node->GetNext(); GridDims meta_area = p_node->GetConcreteMeta()->GetArea(); if (p_node->GetConcreteMeta()->IsInnerShell()) { DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE); } #if 1 else if ( !p_node->GetConcreteMeta()->IsRiser() && p_node->GetConcreteMeta()->IsInPark() && (meta_area.GetX() < m_park_near_bounds.GetX() || meta_area.GetZ() < m_park_near_bounds.GetZ() || meta_area.GetX() + meta_area.GetW() > m_park_near_bounds.GetX() + m_park_near_bounds.GetW() || meta_area.GetZ() + meta_area.GetL() > m_park_near_bounds.GetZ() + m_park_near_bounds.GetL())) { DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE); //#if DEBUG_THIS_DAMN_THING //destroy_type2_count++; //#endif } #endif p_node = p_next_node; } uint32 fence_meta_name_crc = Script::GenerateCRC("Sk4Ed_Fence_20x20"); CAbstractMetaPiece *p_abstract_fence_meta = GetAbstractMeta(fence_meta_name_crc); for (int x = m_park_near_bounds.GetX(); x < m_park_near_bounds.GetX() + m_park_near_bounds.GetW(); x += 2) { GridDims road_test_area; road_test_area.SetWHL(2, 1, 1); // north GridDims pos(x, 0, m_park_near_bounds.GetZ() - 1); road_test_area.SetXYZ(pos.GetX(), 0, pos.GetZ() + 1); if (!IsOccupiedByRoad(road_test_area)) { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta); p_meta->SetRot(Mth::ROT_0); p_meta->MarkAsInnerShell(); AddMetaPieceToPark(pos, p_meta); } // south pos.SetXYZ(x, 0, m_park_near_bounds.GetZ() + m_park_near_bounds.GetL()); road_test_area.SetXYZ(pos.GetX(), 0, pos.GetZ() - 1); if (!IsOccupiedByRoad(road_test_area)) { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta); p_meta->SetRot(Mth::ROT_180); p_meta->MarkAsInnerShell(); AddMetaPieceToPark(pos, p_meta); } } for (int z = m_park_near_bounds.GetZ(); z < m_park_near_bounds.GetZ() + m_park_near_bounds.GetL(); z += 2) { GridDims road_test_area; road_test_area.SetWHL(1, 1, 2); // west GridDims pos(m_park_near_bounds.GetX() - 1, 0, z); road_test_area.SetXYZ(pos.GetX() + 1, 0, pos.GetZ()); if (!IsOccupiedByRoad(road_test_area)) { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta); p_meta->SetRot(Mth::ROT_90); p_meta->MarkAsInnerShell(); AddMetaPieceToPark(pos, p_meta); } // east pos.SetXYZ(m_park_near_bounds.GetX() + m_park_near_bounds.GetW(), 0, z); road_test_area.SetXYZ(pos.GetX() - 1, 0, pos.GetZ()); if (!IsOccupiedByRoad(road_test_area)) { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_abstract_fence_meta); p_meta->SetRot(Mth::ROT_270); p_meta->MarkAsInnerShell(); AddMetaPieceToPark(pos, p_meta); } } //ParkEd("destroyed %d non-riser pieces\n", destroy_type2_count); for (int x = 0; x < MAX_WIDTH; x++) { for (int z = 0; z < MAX_LENGTH; z++) { if (x < m_park_near_bounds.GetX() || z < m_park_near_bounds.GetZ() || x >= m_park_near_bounds.GetX() + m_park_near_bounds.GetW() || z >= m_park_near_bounds.GetZ() + m_park_near_bounds.GetL()) { m_floor_height_map[x][z].mHeight = 0; } } } mp_generator->RemoveOuterShellPieces(GetTheme()); mp_generator->GenerateCollisionInfo(); // mp_generator->GenerateNodeInfo(mp_concrete_metapiece_list); ParkEd("end RebuildInnerShell()\n"); } /* Retrieves the abstract meta with the specified name checksum. Returns its index. */ CAbstractMetaPiece *CParkManager::GetAbstractMeta(uint32 type, int *pRetIndex) { Dbg_Assert(m_state_on); CMapListNode *p_node = mp_abstract_metapiece_list; int index = m_num_abstract_metapieces-1; while(p_node) { CAbstractMetaPiece *p_meta = p_node->GetAbstractMeta(); Dbg_Assert(p_meta); if (p_meta->m_name_checksum == type) { if (pRetIndex) *pRetIndex = index; return p_meta; } index--; p_node = p_node->GetNext(); } return NULL; } /* Retrieves the abstract meta with the specified index. */ CAbstractMetaPiece *CParkManager::GetAbstractMetaByIndex(int index) { Dbg_Assert(m_state_on); CMapListNode *p_node = mp_abstract_metapiece_list; int i = m_num_abstract_metapieces-1; while(p_node) { CAbstractMetaPiece *p_meta = p_node->GetAbstractMeta(); Dbg_Assert(p_meta); if (i == index) return p_meta; i--; p_node = p_node->GetNext(); } Dbg_MsgAssert(0, ("index %d out of range", i)); return NULL; } /* Creates a concrete copy of the specified abstract meta. */ CConcreteMetaPiece *CParkManager::CreateConcreteMeta(CAbstractMetaPiece *pSource, bool isSoft) { Dbg_Assert(m_state_on); Dbg_Assert(pSource); Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CConcreteMetaPiece *p_meta = new CConcreteMetaPiece(); Mem::Manager::sHandle().PopContext(); int num_entries = pSource->count_descriptors_expanded(); p_meta->initialize_desc_table(num_entries); p_meta->m_name_checksum = pSource->m_name_checksum; CPiece::EFlags clone_flags = (isSoft) ? CPiece::mSOFT_PIECE : CPiece::mNO_FLAGS; if (pSource->IsRiser()) { p_meta->set_flag(CMetaPiece::mIS_RISER); clone_flags = CPiece::EFlags(clone_flags | CPiece::mFLOOR); } if (pSource->NoRails()) { p_meta->set_flag(CMetaPiece::mNO_RAILS); clone_flags = CPiece::EFlags(clone_flags | CPiece::mNO_RAILS); } if (pSource->m_flags & CMetaPiece::mCONTAINS_RISERS) { p_meta->set_flag(CMetaPiece::mCONTAINS_RISERS); } // pick through source piece, for each descriptor entry, make new cloned piece for (int i = 0; i < num_entries; i++) { SMetaDescriptor desc = pSource->get_desc_with_expansion(i); CClonedPiece *p_piece = mp_generator->ClonePiece(CParkManager::sInstance()->GetGenerator()->GetMasterPiece(desc.mPieceName), clone_flags); p_piece->SetDesiredRot(Mth::ERot90(desc.mRot), CClonedPiece::MARKER_ONLY); GridDims dims(desc.mX, desc.mY, desc.mZ); p_meta->add_piece(p_piece, &dims, Mth::ERot90(desc.mRot), (desc.mRiser != 0)); #if 0 // Ryan: this call is unnecessary if (!isSoft) { mp_generator->CalculateLighting(p_piece); } #endif #if 0 if (pSource->GetNameChecksum() == Script::GenerateCRC("Low_Short_Deck")) { printf(" rotation: %d\n", desc.mRot); } #endif } p_meta->lock(); #if 1 #ifdef __NOPT_ASSERT__ GridDims test_area = p_meta->GetArea(); Dbg_MsgAssert(test_area.GetW() > 0 && test_area.GetH() > 0 && test_area.GetL() > 0, ("metapiece %s has invalid dimensions (%d,%d,%d)", Script::FindChecksumName(p_meta->GetNameChecksum()), test_area.GetW(), test_area.GetH(), test_area.GetL())); #if 0 if (1 || p_meta->GetNameChecksum() == Script::GenerateCRC("Sk4Ed_Fence_10x10")) printf("OY! metapiece %s has dimensions (%d,%d,%d)\n", Script::FindChecksumName(p_meta->GetNameChecksum()), test_area.GetW(), test_area.GetH(), test_area.GetL()); #endif #endif #endif Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_node = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_node->mp_meta = p_meta; #if 0 p_node->mp_next = mp_concrete_metapiece_list; mp_concrete_metapiece_list = p_node; #else p_node->mp_next = NULL; if (!mp_concrete_metapiece_list) { mp_concrete_metapiece_list = p_node; } else { CMapListNode *p_add = mp_concrete_metapiece_list; while( p_add->mp_next) { p_add = p_add->mp_next; } p_add->mp_next = p_node; } #endif m_num_concrete_metapieces++; return p_meta; } void CParkManager::DestroyConcreteMeta(CConcreteMetaPiece *pMeta, EDestroyFlags flags) { Dbg_Assert(m_state_on); if (pMeta->m_flags & CMetaPiece::mLOCK_AGAINST_DELETE) return; Dbg_MsgAssert(!(pMeta->m_flags & CMetaPiece::mLOCK_AGAINST_DELETE), ("Metapiece is locked, can't deleted")); if (CCursor::sInstance(false)) CCursor::sInstance()->InformOfMetapieceDeletion(pMeta); // remove any gaps attached to this piece RemoveGap(pMeta); if (!pMeta->IsInnerShell() && pMeta->IsInPark()) { decrement_meta_count(pMeta->GetNameChecksum()); if (!pMeta->IsRiser()) { m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK)); } } // destroy all metas that are ABOVE this one if (!pMeta->IsRiser() && (flags & mDESTROY_PIECES_ABOVE)) { GridDims test_area = pMeta->GetArea(); test_area[Y] = test_area.GetY() + test_area.GetH(); test_area.MakeInfinitelyHigh(); // XXX Ryan("die now: "); //test_area.PrintContents(); DestroyMetasInArea(test_area, EDestroyFlags(mEXCLUDE_RISERS | mDESTROY_PIECES_ABOVE)); } apply_meta_contained_risers_to_floor(pMeta, false); debucketify_metapiece(pMeta); remove_metapiece_from_node_list(pMeta, &mp_concrete_metapiece_list); #if 0 printf("concrete metapiece %s destroyed: cell dims (%d,%d,%d)-(%d,%d,%d), flags=0x%x\n", Script::FindChecksumName(pMeta->GetNameChecksum()), pMeta->m_cell_area.GetX(), pMeta->m_cell_area.GetY(), pMeta->m_cell_area.GetZ(), pMeta->m_cell_area.GetW(), pMeta->m_cell_area.GetH(), pMeta->m_cell_area.GetL(), pMeta->m_flags); #endif CParkGenerator::RestartType restart_type = IsRestartPiece(pMeta->GetNameChecksum()); if (restart_type != CParkGenerator::vEMPTY) { mp_generator->RemoveRestart(pMeta->GetArea(),restart_type); } // take out metapiece for (uint i = 0; i < pMeta->m_num_used_entries; i++) { CClonedPiece *p_piece = pMeta->get_desc_at_index(i).mpPiece->CastToCClonedPiece(); Dbg_Assert(p_piece); mp_generator->DestroyClonedPiece(p_piece); } delete pMeta; m_num_concrete_metapieces--; } void CParkManager::HighlightMetasInArea(GridDims area, bool on) { CMapListTemp list = GetAreaMetaPieces(area); CMapListNode *p_entry = list.GetList(); while(p_entry) { CMetaPiece *p_meta=p_entry->GetMeta(); if (p_meta) { if (p_meta->IsConcrete() && p_meta->IsCompletelyWithinArea(area)) { p_entry->GetConcreteMeta()->Highlight(on); } } p_entry = p_entry->GetNext(); } } /* GridDims CParkManager::FindBoundingArea(GridDims area) { int min_x=1000000; int min_z=1000000; int max_x=-1000000; int max_z=-1000000; CMapListTemp list = GetAreaMetaPieces(area); CMapListNode *p_entry = list.GetList(); while(p_entry) { CMetaPiece *p_meta=p_entry->GetMeta(); if (p_meta && p_meta->IsConcrete()) { GridDims meta_area = p_entry->GetConcreteMeta()->GetArea(); int x1=meta_area.GetX(); int z1=meta_area.GetZ(); int x2=x1+meta_area.GetW()-1; int z2=z1+meta_area.GetL()-1; if (x1 < min_x) min_x=x1; if (x2 > max_x) max_x=x2; if (z1 < min_z) min_z=z1; if (z2 > max_z) max_z=z2; } p_entry = p_entry->GetNext(); } GridDims bounding_area=area; if (min_x <= max_x && min_z <= max_z) { bounding_area.SetXYZ(min_x, 0, min_z); bounding_area.SetWHL(max_x-min_x+1, 1, max_z-min_z+1); } return bounding_area; } */ // K: Does the same as CCursor::AttemptRemove() but for any area bool CParkManager::DestroyMetasInArea(GridDims area, bool doNotDestroyRestartsOrFlags, bool onlyIfWithinArea) { bool destroyed_something=false; CMapListTemp metas_at_pos = GetAreaMetaPieces(area); CMapListNode *p_entry = metas_at_pos.GetList(); while (p_entry) { CConcreteMetaPiece *p_meta = p_entry->GetConcreteMeta(); if (p_meta->IsRiser() || (doNotDestroyRestartsOrFlags && p_meta->IsRestartOrFlag()) || (onlyIfWithinArea && !p_meta->IsCompletelyWithinArea(area))) { } else { DestroyConcreteMeta(p_meta, CParkManager::mDESTROY_PIECES_ABOVE); destroyed_something=true; } p_entry = p_entry->GetNext(); } // Also destroy any rail points in the area. Mth::Vector corner_pos; Mth::Vector area_dims; corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(area,&area_dims); Obj::GetRailEditor()->ClipRails(corner_pos[X], corner_pos[Z], corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z], Obj::CRailEditorComponent::DELETE_POINTS_INSIDE); if (destroyed_something) { RebuildFloor(); return true; } return false; } /* Destroys concrete metapieces in specified area, according to flags. Flags: -mDESTROY_PIECES_ABOVE: if set, whenever a meta is destroyed, recursively destroy all metas above it Return value: upper 16 bits: number metas killed lower 16 bits: number pieces killed */ uint32 CParkManager::DestroyMetasInArea(GridDims area, EDestroyFlags flags) { uint16 metas_killed = 0; uint16 pieces_killed = 0; CMapListNode *p_list = GetMetaPiecesAt(area, Mth::ROT_0, NULL, (flags & mEXCLUDE_RISERS)); CMapListNode *p_node = p_list; while (p_node) { if (!(flags & mEXCLUDE_PIECES_MARKED_AS_SLID) || !(p_node->GetConcreteMeta()->m_flags & CMetaPiece::mMARK_AS_SLID)) { metas_killed++; pieces_killed += p_node->GetConcreteMeta()->m_num_used_entries; DestroyConcreteMeta(p_node->GetConcreteMeta(), flags); } p_node = p_node->GetNext(); } p_list->DestroyList(); return ((metas_killed<<16) | pieces_killed); } int CParkManager::get_dma_usage(uint32 pieceChecksum) { Script::CStruct *p_dma_usage=Script::GetStructure(CRCD(0x9547da8d,"DMA_Usage"),Script::ASSERT); int usage=1; p_dma_usage->GetInteger(pieceChecksum, &usage); return usage; } void CParkManager::increment_meta_count(uint32 pieceChecksum) { ++m_num_general_concrete_metapieces; m_dma_piece_count += get_dma_usage(pieceChecksum); //printf("IncrementMetaCount '%s', %d DMA=%d\n",Script::FindChecksumName(pieceChecksum),m_num_general_concrete_metapieces,m_dma_piece_count); } void CParkManager::decrement_meta_count(uint32 pieceChecksum) { --m_num_general_concrete_metapieces; m_dma_piece_count -= get_dma_usage(pieceChecksum); //printf("DecrementMetaCount '%s' %d DMA=%d\n",Script::FindChecksumName(pieceChecksum),m_num_general_concrete_metapieces,m_dma_piece_count); Dbg_MsgAssert(m_dma_piece_count >= 0,("Negative m_dma_piece_count of %d\n",m_dma_piece_count)); } /* Position is in cell coordinates, relative to northwest corner of 256X256 grid. Y coordinate is relative to "ground level", can be + or -. Rotation is 0-3. */ void CParkManager::AddMetaPieceToPark(GridDims pos, CConcreteMetaPiece *pMetaPiece) { Dbg_Assert(m_state_on); Dbg_MsgAssert(!(pMetaPiece->m_flags & CMetaPiece::mIN_PARK), ("metapiece already in park")); pMetaPiece->set_flag(CMetaPiece::mIN_PARK); pMetaPiece->m_cell_area.SetXYZ(pos.GetX(), pos.GetY(), pos.GetZ()); // center_pos in world coordinates Mth::Vector center_pos; center_pos[X] = ((float) pos.GetX() + (float) pMetaPiece->m_cell_area.GetW() / 2.0f) * CParkGenerator::CELL_WIDTH; center_pos[X] -= (CParkGenerator::CELL_WIDTH * (float) CParkManager::FLOOR_MAX_WIDTH) / 2.0f; center_pos[Z] = ((float) pos.GetZ() + (float) pMetaPiece->m_cell_area.GetL() / 2.0f) * CParkGenerator::CELL_LENGTH; center_pos[Z] -= (CParkGenerator::CELL_LENGTH * (float) CParkManager::FLOOR_MAX_LENGTH) / 2.0f; center_pos[Y] = pos.GetY() * CParkGenerator::CELL_HEIGHT; // XXX #if 0 Dbg_Printf("adding piece to park: (%d,%d,%d), (%.2f,%.2f,%.2f)\n", pos.GetX(), pos.GetY(), pos.GetZ(), center_pos[X], center_pos[Y], center_pos[Z]); #endif for (uint i = 0; i < pMetaPiece->m_num_used_entries; i++) { Mth::Vector rel_pos = pMetaPiece->GetRelativePosOfContainedPiece(i); CClonedPiece *p_piece = pMetaPiece->GetContainedPiece(i)->CastToCClonedPiece(); // reposition piece from metapiece-relative coordinates to world-relative Mth::Vector abs_pos; abs_pos[X] = center_pos[X] + rel_pos[X]; abs_pos[Z] = center_pos[Z] + rel_pos[Z]; abs_pos[Y] = center_pos[Y] + rel_pos[Y]; p_piece->SetDesiredPos(abs_pos, CClonedPiece::CHANGE_SECTOR); p_piece->SetDesiredRot(Mth::ERot90((p_piece->GetRot() + pMetaPiece->m_rot) & 3), CClonedPiece::CHANGE_SECTOR); mp_generator->AddClonedPieceToWorld(p_piece); mp_generator->CalculateLighting(p_piece); } apply_meta_contained_risers_to_floor(pMetaPiece, true); bucketify_metapiece(pMetaPiece); // If it's a restart point, then add that to the list of restart points CParkGenerator::RestartType restart_type = IsRestartPiece(pMetaPiece->GetNameChecksum()); if (restart_type != CParkGenerator::vEMPTY) { mp_generator->AddRestart(center_pos,pMetaPiece->m_rot,pos,restart_type,false); } if (!pMetaPiece->IsInnerShell()) { if (!pMetaPiece->IsRiser()) { m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK)); } increment_meta_count(pMetaPiece->GetNameChecksum()); } } /* "Moves" a metapiece from one position to another. Really, the old metapiece is destroyed, and a new copy created, so be careful to replace any pointers to old one. */ CConcreteMetaPiece *CParkManager::RelocateMetaPiece(CConcreteMetaPiece *pMeta, int changeX, int changeY, int changeZ) { GridDims area = pMeta->GetArea(); uint32 name = pMeta->GetNameChecksum(); Mth::ERot90 rot = pMeta->GetRot(); CMetaPiece::EFlags old_flags = pMeta->m_flags; // get gap associated with metapiece int gap_half; bool gap_exists = false; CGapManager::GapDescriptor *p_gap_desc = GetGapDescriptor(area, &gap_half); CGapManager::GapDescriptor new_gap_desc; if (p_gap_desc) { gap_exists = true; new_gap_desc = *p_gap_desc; // remove it RemoveGap(*p_gap_desc); // shift gap descriptor to new position new_gap_desc.loc[gap_half][X] = new_gap_desc.loc[gap_half].GetX() + changeX; new_gap_desc.loc[gap_half][Y] = new_gap_desc.loc[gap_half].GetY() + changeY; new_gap_desc.loc[gap_half][Z] = new_gap_desc.loc[gap_half].GetZ() + changeZ; } DestroyConcreteMeta(pMeta, mDONT_DESTROY_PIECES_ABOVE); area[X] += changeX; area[Y] = area.GetY() + changeY; area[Z] += changeZ; CConcreteMetaPiece *p_new_meta = CreateConcreteMeta(GetAbstractMeta(name), false); p_new_meta->SetRot(rot); p_new_meta->m_flags = old_flags; p_new_meta->clear_flag(CMetaPiece::mIN_PARK); AddMetaPieceToPark(area, p_new_meta); if (gap_exists) { AddGap(new_gap_desc); } return p_new_meta; } /* Returns list of concrete metapieces in specified area. Modes: pPiece == NULL, grab all pieces in area specified by 'dims', excluding risers and riser-containing metas if desired. pPiece != NULL, does pretty much the same thing right now. */ CMapListNode *CParkManager::GetMetaPiecesAt(GridDims dims, Mth::ERot90 rot, CAbstractMetaPiece *pPiece, bool excludeRisers) { Dbg_Assert(m_state_on); if (!pPiece) { CMapListNode *p_out_list = NULL; // need to be scanning an area bigger than 0 //if (dims.mW == 0) dims.mW++; //if (dims.mL == 0) dims.mL++; // no piece supplied, just scan through and grab all metapieces int end_x = dims.GetX() + dims.GetW(); for (int x = dims.GetX(); x < end_x; x++) { int end_z = dims.GetZ() + dims.GetL(); for (int z = dims.GetZ(); z < end_z; z++) { CMapListNode *p_node = mp_bucket_list[x][z]; while(p_node) { //Ryan("boogah %d\n", x); if ((!excludeRisers || !(p_node->GetMeta()->IsRiser())) && p_node->GetConcreteMeta()->test_for_conflict(dims, excludeRisers)) { add_metapiece_to_node_list(p_node->GetMeta(), &p_out_list); } p_node = p_node->GetNext(); } } } p_out_list->VerifyIntegrity(); return p_out_list; } else { Dbg_MsgAssert(pPiece->GetRot() == Mth::ROT_0, ("abstract metapiece needs 0 rotation")); // build a descriptor table rotated and shifted to correct world coordinates // rotate the abstract meta piece to the desired rotation pPiece->SetRot(rot); dims[W] = pPiece->m_cell_area.GetW(); dims[H] = pPiece->m_cell_area.GetH(); dims[L] = pPiece->m_cell_area.GetL(); CMapListNode *p_out_list = NULL; // grab list of all concrete metas CMapListTemp metas_in_park = GetMetaPiecesAt(dims); // for each contained piece for (uint i = 0; i < pPiece->m_num_used_entries; i++) { SMetaDescriptor &desc = pPiece->get_desc_at_index(i); GridDims test_area(desc.mX, desc.mY, desc.mZ); test_area[X] += dims.GetX(); test_area[Y] += dims.GetY(); test_area[Z] += dims.GetZ(); CMapListNode *p_in_park_entry = metas_in_park.GetList(); while (p_in_park_entry) { if (p_in_park_entry->GetConcreteMeta()->test_for_conflict(test_area, false)) add_metapiece_to_node_list(p_in_park_entry->GetMeta(), &p_out_list); p_in_park_entry = p_in_park_entry->GetNext(); } } // return abstract meta to a zero rotation pPiece->SetRot(Mth::ROT_0); p_out_list->VerifyIntegrity(); return p_out_list; } return NULL; } // This is just a slightly faster version of GetMetaPiecesAt, which gets all CMapListNode *CParkManager::GetAreaMetaPieces(GridDims dims) { Dbg_Assert(m_state_on); CMapListNode *p_out_list = NULL; int end_x = dims.GetX() + dims.GetW(); for (int x = dims.GetX(); x < end_x; x++) { int end_z = dims.GetZ() + dims.GetL(); for (int z = dims.GetZ(); z < end_z; z++) { CMapListNode *p_node = mp_bucket_list[x][z]; while(p_node) { add_metapiece_to_node_list(p_node->GetMeta(), &p_out_list); p_node = p_node->GetNext(); } } } return p_out_list; } // K: These two functions used by CClipboard::CopySelectionToClipboard CMapListNode *CParkManager::GetMapListNode(int x, int z) { return mp_bucket_list[x][z]; } int CParkManager::GetFloorHeight(int x, int z) { return m_floor_height_map[x][z].mHeight; } /* Used to determine if resizing park will destroy existing metas. */ bool CParkManager::AreMetasOutsideBounds(GridDims new_dims) { //Ryan("New Park Bounds are: (%d,%d,%d)-(%d,%d,%d)\n", // new_dims.GetX(), new_dims.GetY(), new_dims.GetZ(), // new_dims.GetW(), new_dims.GetH(), new_dims.GetL()); CMapListNode *p_node = mp_concrete_metapiece_list; while(p_node) { CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta(); GridDims meta_area = p_meta->GetArea(); //Ryan("Checking if meta in bounds: is_riser=%d, inner_shell=%d (%d,%d,%d)-(%d,%d,%d)\n", // p_meta->IsRiser(), p_meta->IsInnerShell(), // meta_area.GetX(), meta_area.GetY(), meta_area.GetZ(), // meta_area.GetW(), meta_area.GetH(), meta_area.GetL()); if ((meta_area.GetX() < new_dims.GetX() || meta_area.GetZ() < new_dims.GetZ() || meta_area.GetX() + meta_area.GetW() > new_dims.GetX() + new_dims.GetW() || meta_area.GetZ() + meta_area.GetL() > new_dims.GetZ() + new_dims.GetL()) && !p_meta->IsRiser() && !p_meta->IsInnerShell() && (p_meta->m_flags & CMetaPiece::mIN_PARK)) return true; p_node = p_node->GetNext(); } return false; } /* Used to determine if resizing park will destroy existing metas. */ bool CParkManager::EnoughMemoryToResize(GridDims new_dims) { int current_num_tiles = m_park_near_bounds.GetW() * m_park_near_bounds.GetL(); int new_num_tiles = new_dims.GetW() * new_dims.GetL(); if (new_num_tiles - current_num_tiles > GetGenerator()->GetResourceSize("max_dma_pieces") - GetDMAPieceCount()) { return false; } int park_add_amount = (new_num_tiles - current_num_tiles) * mp_generator->GetResourceSize("floor_piece_size_park"); int main_add_amount = (new_num_tiles - current_num_tiles) * mp_generator->GetResourceSize("floor_piece_size_main"); CParkGenerator::MemUsageInfo usage_info = mp_generator->GetResourceUsageInfo(); if (usage_info.mParkHeapFree < park_add_amount) { Ryan("not enough memory for resize on park heap, need %d, have %d\n", park_add_amount, usage_info.mParkHeapFree); return false; } if (usage_info.mMainHeapFree < main_add_amount) { Ryan("not enough memory for resize on main heap, need %d, have %d\n", main_add_amount, usage_info.mMainHeapFree); return false; } #ifdef __PLAT_NGC__ // NGC has problems resizing if the top down heap is low due to UpdateSuperSectors requiring some temporary space. int required_top_down_heap=TOP_DOWN_REQUIRED_MARGIN; // If increasing the size of the park, then in the worse case the TD heap may reduce by main_add_amount, // if none of the fragments got used. // We want there to be more than TOP_DOWN_REQUIRED_MARGIN after increasing the size, so that the user // will be able to reduce the size afterwards, so add in main_add_amount to the requirement. if (main_add_amount > 0) { required_top_down_heap += main_add_amount; } Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap(); // Check if there is enough TD heap at the moment if (p_top_down_heap->mp_region->MemAvailable() < required_top_down_heap) { return false; } #endif return true; } /* Returns max floor height within specified */ int CParkManager::GetFloorHeight(GridDims dims, bool *pRetUniformHeight) { Dbg_Assert(m_state_on); Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!")); int max_height = -10000; if (pRetUniformHeight) *pRetUniformHeight = true; int corner_height = m_floor_height_map[dims.GetX()][dims.GetZ()].mHeight; for (int x = dims.GetX(); x < dims.GetX() + dims.GetW(); x++) { for (int z = dims.GetZ(); z < dims.GetZ() + dims.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); if (pRetUniformHeight && m_floor_height_map[x][z].mHeight != corner_height) *pRetUniformHeight = false; if (m_floor_height_map[x][z].mHeight > max_height) max_height = m_floor_height_map[x][z].mHeight; } } return max_height; } #if 0 /* Changes the floor height stored in the height map, A subsequent call to generate_floor_pieces_from_height_map() will make the geometry. */ void CParkManager::SetFloorHeight(GridDims area, int newHeight, bool justDropHighest) { for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++) { for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); GridDims test_area(x, 0, z, 1, 1, 1); if (IsOccupiedByRoad(test_area)) { m_floor_height_map[x][z].mHeight = 0; } if (justDropHighest) { if (m_floor_height_map[x][z].mHeight > newHeight) m_floor_height_map[x][z].mHeight = newHeight; } else m_floor_height_map[x][z].mHeight = newHeight; } } m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK)); } #endif void CParkManager::ResetFloorHeights(GridDims area) { // First move all the pieces in the area such that their y's are zero. CMapListTemp metas_at_pos = GetAreaMetaPieces(area); CMapListNode *p_entry = metas_at_pos.GetList(); while (p_entry) { CConcreteMetaPiece *p_meta = p_entry->GetConcreteMeta(); if (!p_meta->IsRiser()) { GridDims meta_area=p_meta->GetArea(); RelocateMetaPiece(p_meta,0,-meta_area.GetY(),0); // Set the floor heights under the piece to zero. Need to do this as well // as zeroing the heights for the whole area, in case the piece sticks out // of the area. for (int x = meta_area.GetX(); x < meta_area.GetX() + meta_area.GetW(); x++) { for (int z = meta_area.GetZ(); z < meta_area.GetZ() + meta_area.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); m_floor_height_map[x][z].mHeight=0; } } } p_entry = p_entry->GetNext(); } // Then set all the floor heights to zero for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++) { for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); m_floor_height_map[x][z].mHeight=0; } } // K: Do I need this? m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~(mIN_SYNC_WITH_PARK | mIS_NEWER_THAN_PARK)); // Then rebuild the floor so that the above changes take effect. RebuildFloor(); } /* A "column" is a 3D region defined by the XZ rectangle of 'area'. Its bottom is at height 'y'. All the metapieces within the column will change y position by the amount specified. Floor heights within the column will change as well. Returns true if success. */ bool CParkManager::SlideColumn(GridDims area, int bottom, int top, bool up, bool uneven) { ESlideColumnFlags flags = ESlideColumnFlags(mFIRST_RECURSE); if (up) flags = ESlideColumnFlags(flags | mUP); if (uneven) flags = ESlideColumnFlags(flags | mUNEVEN); /* Remove bookkeeping left from last SlideColumn */ finish_slide_column(); if (mp_column_slide_list) mp_column_slide_list->DestroyList(); mp_column_slide_list = NULL; m_num_metas_killed_in_slide = 0; m_num_pieces_killed_in_slide = 0; #if 1 if (uneven && intersection_with_riser_containing_metas(area)) return false; #endif if (!slide_column_part_one(area, bottom, top, flags, &mp_column_slide_list)) { fix_slide_column_part_one_failure(); return false; } slide_column_part_two(mp_column_slide_list); if (!up) kill_pieces_in_layer_under_area(area); return true; } /* Undoes the results of a successful SlideColumn() */ void CParkManager::UndoSlideColumn() { fix_slide_column_part_one_failure(); CMapListNode *p_node = mp_column_slide_list; while(p_node) { p_node->mp_meta = RelocateMetaPiece(p_node->GetConcreteMeta(), 0, -p_node->mSlideAmount, 0); p_node = p_node->GetNext(); } } // K: This is copied from CCursor::ChangeFloorHeight // Modified to take a GridDims parameter so that it can be used by the clipboard pasting code. bool CParkManager::ChangeFloorHeight(GridDims dims, int dir) { /* Uneven floor: If going up, raise to height of topmost If going down, only lower ones on same level */ bool uniform_height; int height = GetFloorHeight(dims, &uniform_height); /* Now, */ bool success = true; if (SlideColumn(dims, dims.GetY(), height, (dir > 0), !uniform_height)) { //Ryan("Slide column, dir = %d, dims=", dir); //dims.PrintContents(); // K: This was required in the CCursor version of ChangeFloorHeight, can't // call it here though cos it is a member function of CCursor ... //change_cell_pos(0, 0); Spt::SingletonPtr p_editor; if (RebuildFloor(p_editor->IsParkFull())) { HighlightMetasWithGaps(false, NULL); } else { // out of memory, so reverse slide UndoSlideColumn(); RebuildFloor(); success = false; } } else success = false; return success; } /* Converts a cell based XYZ coordinate to its world coordinate equivalent. */ Mth::Vector CParkManager::GridCoordinatesToWorld(const GridDims &cellDims, Mth::Vector *pRetDims) { Mth::Vector world_pos(((int) cellDims.GetX() - (int) CParkManager::FLOOR_MAX_WIDTH / 2) * CParkGenerator::CELL_WIDTH, (int) cellDims.GetY() * CParkGenerator::CELL_HEIGHT, ((int) cellDims.GetZ() - (int) CParkManager::FLOOR_MAX_LENGTH / 2) * CParkGenerator::CELL_LENGTH); if (pRetDims) { (*pRetDims)[X] = cellDims.GetW() * CParkGenerator::CELL_WIDTH; (*pRetDims)[Y] = cellDims.GetH() * CParkGenerator::CELL_HEIGHT; (*pRetDims)[Z] = cellDims.GetL() * CParkGenerator::CELL_LENGTH; #if 0 printf("dims: (%d,%d,%d)-(%d,%d,%d)\n", cellDims.GetW(), cellDims.GetH(), cellDims.GetL(), (int) cellDims.GetX() - (int) CParkManager::FLOOR_MAX_WIDTH / 2, (int) cellDims.GetY(), (int) cellDims.GetZ() - (int) CParkManager::FLOOR_MAX_LENGTH / 2); #endif } return world_pos; } CParkManager *CParkManager::sInstance() { Dbg_Assert(sp_instance); return sp_instance; } /* The manager keeps track of a piece database that matches the selection set the user sees at the top of the screen. */ CParkManager::CPieceSet &CParkManager::GetPieceSet(int setNumber, int *pMenuSetNumber) { Dbg_Assert(setNumber >= 0 && setNumber < MAX_SETS); int menu_set_number = 0; for (int s = 0; s < setNumber; s++) { if (m_palette_set[s].mVisible) menu_set_number++; } if (pMenuSetNumber) *pMenuSetNumber = menu_set_number; return m_palette_set[setNumber]; } /* y coordinates, dimensions of 'area' not used. */ bool CParkManager::IsOccupiedByRoad(GridDims area) { for (uint i = 0; i < m_road_mask_tab_size; i++) { bool overlap = true; if (area.GetX() + area.GetW() <= m_road_mask_tab[i].GetX()) overlap = false; else if (area.GetX() >= m_road_mask_tab[i].GetX() + m_road_mask_tab[i].GetW()) overlap = false; else if (area.GetZ() + area.GetL() <= m_road_mask_tab[i].GetZ()) overlap = false; else if (area.GetZ() >= m_road_mask_tab[i].GetZ() + m_road_mask_tab[i].GetL()) overlap = false; if (overlap) return true; } return false; } bool CParkManager::CanPlacePiecesIn(GridDims cellDims, bool ignoreFloorHeight) { Spt::SingletonPtr p_editor; if (p_editor->IsParkFull()) { ParkEd("p_editor->IsParkFull()"); return false; } // Check if there are any pieces in the area already. // Not using GetMetaPiecesAt because it does not detect pieces in lowered cells for some reason. CMapListTemp metas_at_pos = GetAreaMetaPieces(cellDims); CMapListNode *p_entry = metas_at_pos.GetList(); while (p_entry) { CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta(); if (!p_meta->IsRiser()) { return false; } p_entry = p_entry->GetNext(); } if (!ignoreFloorHeight) { bool uniform_floor; int floor_height = GetFloorHeight(cellDims, &uniform_floor); if (floor_height != cellDims.GetY() || !uniform_floor) { ParkEd("floor_height != cellDims.GetY() || !uniform_floor"); return false; } } if (IsOccupiedByRoad(cellDims)) { ParkEd("IsOccupiedByRoad(cellDims)"); return false; } return true; } // pHalf returns the half number associated with the piece, if any gap found CGapManager::GapDescriptor *CParkManager::GetGapDescriptor(GridDims &area, int *pHalf) { area.SetWHL(1,1,1); CMapListTemp meta_list = GetMetaPiecesAt(area); if (meta_list.IsEmpty()) return NULL; CConcreteMetaPiece *p_meta = meta_list.GetList()->GetConcreteMeta(); int tab_index; Dbg_Assert(mp_gap_manager); if (!mp_gap_manager->IsGapAttached(p_meta, &tab_index)) return NULL; CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(tab_index, pHalf); Dbg_MsgAssert(pDesc, ("couldn't find gap descriptor")); return pDesc; } /* Call from: read_from_compressed_map_buffer(), CParkEditor, gap adjustment code, gap name changer -see if meta at cursor loc -if user working on gap already, fail -if no prexisiting, remove gap-in-progress -build descriptor -add gap */ bool CParkManager::AddGap(CGapManager::GapDescriptor &descriptor) { Dbg_Assert(mp_gap_manager); int gap_tab_index = mp_gap_manager->GetFreeGapIndex(); if (gap_tab_index == -1) return false; for (int i = 0; i < descriptor.numCompleteHalves; i++) { // fetch piece associated with this gap half descriptor.loc[i].SetWHL(1,1,1); CMapListTemp meta_list = GetMetaPiecesAt(descriptor.loc[i]); if (meta_list.IsEmpty()) return false; Dbg_MsgAssert(!meta_list.IsEmpty(), ("am expecting a metapiece here")); CConcreteMetaPiece *p_meta = meta_list.GetList()->GetConcreteMeta(); Dbg_MsgAssert(p_meta, ("no pieces found at (%d,%d,%d)", descriptor.loc[i].GetX(), descriptor.loc[i].GetY(), descriptor.loc[i].GetZ())); Dbg_MsgAssert(!mp_gap_manager->IsGapAttached(p_meta, NULL), ("gap is already attached")); p_meta->set_flag(CMetaPiece::mGAP_ATTACHED); descriptor.loc[i] = p_meta->GetArea(); #if 0 Ryan(" Placing gap half %d, loc=(%d,%d,%d-%d,%d,%d), rot=%d, left=%d, right=%d\n", i, descriptor.loc[i].GetX(), descriptor.loc[i].GetY(), descriptor.loc[i].GetZ(), descriptor.loc[i].GetW(), descriptor.loc[i].GetH(), descriptor.loc[i].GetL(), descriptor.rot[i], descriptor.leftExtent[i], descriptor.rightExtent[i]); #endif GridDims area = p_meta->GetArea(); #if 1 // work out gap positioning Mth::Vector gap_pos; float gap_length = 0.0f; int gap_rot = 0; switch(descriptor.rot[i]) { case 0: // east gap_length = (area.GetL() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH; gap_pos[X] = (area.GetX() + area.GetW()) * CParkGenerator::CELL_WIDTH - CParkGenerator::CELL_WIDTH / 2.0f - 1.0f; gap_pos[Z] = (area.GetZ() - descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH + gap_length / 2.0f - CParkGenerator::CELL_LENGTH; gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT; gap_rot = 1; break; case 1: // north gap_length = (area.GetW() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH; gap_pos[X] = (area.GetX() - descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH + gap_length / 2.0f - CParkGenerator::CELL_WIDTH; gap_pos[Z] = area.GetZ() * CParkGenerator::CELL_LENGTH - CParkGenerator::CELL_LENGTH / 2.0f + 1.0f; gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT; gap_rot = 0; break; case 2: // west gap_length = (area.GetL() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_LENGTH; gap_pos[X] = area.GetX() * CParkGenerator::CELL_WIDTH - CParkGenerator::CELL_WIDTH / 2.0f + 1.0f; gap_pos[Z] = (area.GetZ() - descriptor.leftExtent[i]) * CParkGenerator::CELL_LENGTH + gap_length / 2.0f - CParkGenerator::CELL_LENGTH; gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT; gap_rot = 1; break; case 3: // south gap_length = (area.GetW() + descriptor.leftExtent[i] + descriptor.rightExtent[i]) * CParkGenerator::CELL_WIDTH; gap_pos[X] = (area.GetX() - descriptor.leftExtent[i]) * CParkGenerator::CELL_WIDTH + gap_length / 2.0f - CParkGenerator::CELL_WIDTH; gap_pos[Z] = (area.GetZ() + area.GetL()) * CParkGenerator::CELL_LENGTH - CParkGenerator::CELL_LENGTH / 2.0f - 1.0f; gap_pos[Y] = area.GetY() * CParkGenerator::CELL_HEIGHT; gap_rot = 0; break; default: Dbg_MsgAssert(0, ("not a valid gap rotation")); break; } #endif gap_pos[X] -= MAX_WIDTH * CParkGenerator::CELL_WIDTH / 2.0f; gap_pos[Z] -= MAX_LENGTH * CParkGenerator::CELL_LENGTH / 2.0f; CClonedPiece *p_gap_piece = mp_generator->CreateGapPiece(gap_pos, gap_length, gap_rot); p_gap_piece->SetScaleX(gap_length / CParkGenerator::CELL_WIDTH); // create gap if (i == 0) mp_gap_manager->StartGap(p_gap_piece, p_meta, gap_tab_index); else mp_gap_manager->EndGap(p_gap_piece, p_meta, gap_tab_index); } descriptor.tabIndex = gap_tab_index; mp_gap_manager->SetGapInfo(gap_tab_index, descriptor); return true; } /* Called from: change cursor, cleanup, gap placement, gap adjustment, name change */ void CParkManager::RemoveGap(CGapManager::GapDescriptor &descriptor) { #if 1 // XXX for (int half = 0; half < 2; half++) Ryan(" removing gap half %d (%d,%d,%d),(%d,%d,%d) left=%d right=%d, index=%d\n", half, descriptor.loc[half].GetX(), descriptor.loc[half].GetY(), descriptor.loc[half].GetZ(), descriptor.loc[half].GetW(), descriptor.loc[half].GetH(), descriptor.loc[half].GetL(), descriptor.leftExtent[half], descriptor.rightExtent[half], descriptor.tabIndex); #endif CConcreteMetaPiece *p_meta[2]; for (int half = 0; half < descriptor.numCompleteHalves; half++) { CMapListTemp meta_list = GetMetaPiecesAt(descriptor.loc[half]); Dbg_MsgAssert(!meta_list.IsEmpty(), ("no pieces found at (%d,%d,%d)", descriptor.loc[half].GetX(), descriptor.loc[half].GetY(), descriptor.loc[half].GetZ())); #if 1 if (!meta_list.IsSingular()) { // printf("failed singularity, half %d\n", half); meta_list.PrintContents(); } #endif // go through list, find metapiece matching location above // there is only SUPPOSED to be one metapiece in list, but sometimes there are more // and I have no idea why, so fuck it CMapListNode *p_node = meta_list.GetList(); while(p_node) { p_meta[half] = p_node->GetConcreteMeta(); GridDims area = p_meta[half]->GetArea(); if (area.GetX() == descriptor.loc[half].GetX() && area.GetY() == descriptor.loc[half].GetY() && area.GetZ() == descriptor.loc[half].GetZ()) break; p_node = p_node->GetNext(); } //p_meta[half] = meta_list.GetList()->GetConcreteMeta(); p_meta[half]->Highlight(false); p_meta[half]->clear_flag(CMetaPiece::mGAP_ATTACHED); } int tab_index; Dbg_Assert(mp_gap_manager); if (mp_gap_manager->IsGapAttached(p_meta[0], &tab_index)) { mp_gap_manager->RemoveGap(tab_index); } descriptor.tabIndex = -1; // Reset the cancel flags to the default, to fix TT9493 descriptor.mCancelFlags=Script::GetInteger(CRCD(0x9a27e74d,"Cancel_Ground")); } void CParkManager::RemoveGap(CConcreteMetaPiece *pMeta) { int tab_index; Dbg_Assert(mp_gap_manager); if (!mp_gap_manager->IsGapAttached(pMeta, &tab_index)) return; int half; CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(tab_index, &half); Dbg_MsgAssert(pDesc, ("couldn't find gap descriptor")); RemoveGap(*pDesc); } void CParkManager::HighlightMetasWithGaps(bool highlightOn, CGapManager::GapDescriptor *pDesc) { CMapListNode *p_node = mp_concrete_metapiece_list; while (p_node) { if (p_node->GetConcreteMeta()->m_flags & CMetaPiece::mGAP_ATTACHED) p_node->GetConcreteMeta()->Highlight(highlightOn, true); p_node = p_node->GetNext(); } mp_generator->SetGapPiecesVisible(false); if (highlightOn && pDesc && pDesc->tabIndex >= 0) { mp_gap_manager->MakeGapWireframe(pDesc->tabIndex); } else if (!highlightOn) { mp_generator->SetGapPiecesVisible(false); } } // Added for use by s_generate_summary_info in mcfunc.cpp void CParkManager::GetSummaryInfoFromBuffer(uint8 *p_buffer, int *p_numGaps, int *p_numMetas, uint16 *p_theme, uint32 *p_todScript, int *p_width, int *p_length) { CompressedMapHeader *p_header=(CompressedMapHeader*)p_buffer; *p_width=p_header->mW; *p_length=p_header->mL; *p_numGaps=p_header->mNumGaps; *p_numMetas=p_header->mNumMetas; *p_theme=p_header->mTheme; if (p_header->mTODScript) { *p_todScript=p_header->mTODScript; } else { *p_todScript=CRCD(0x1ca1ff20,"default"); } } /* Creates a "stack" of risers within the specified vertical column. If !simpleBuild, will keep exisiting riser metapieces if they do the job. 'top' refers to cell position of top of topmost riser */ void CParkManager::output_riser_stack(BuildFloorParams ¶ms) { Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!")); Dbg_MsgAssert(params.top >= -16 && params.top <= 15, ("faulty floor height %d, at (%d,%d)", params.top, params.x, params.z)); int lowest_neighbor = 10000; // work out lowest neighbor if (params.x > m_park_far_bounds.GetX()) { if (m_floor_height_map[params.x-1][params.z].mHeight < lowest_neighbor) lowest_neighbor = m_floor_height_map[params.x-1][params.z].mHeight; } else lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor; if (params.x < m_park_far_bounds.GetX() + m_park_far_bounds.GetW() - 1) { if (m_floor_height_map[params.x+1][params.z].mHeight < lowest_neighbor) lowest_neighbor = m_floor_height_map[params.x+1][params.z].mHeight; } else lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor; if (params.z > m_park_far_bounds.GetZ()) { if (m_floor_height_map[params.x][params.z-1].mHeight < lowest_neighbor) lowest_neighbor = m_floor_height_map[params.x][params.z-1].mHeight; } else lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor; if (params.z < m_park_far_bounds.GetZ() + m_park_far_bounds.GetL() - 1) { if (m_floor_height_map[params.x][params.z+1].mHeight < lowest_neighbor) lowest_neighbor = m_floor_height_map[params.x][params.z+1].mHeight; } else lowest_neighbor = (lowest_neighbor > 0) ? 0 : lowest_neighbor; EFloorFlags flags = mHAS_BOTTOM; GridDims road_test_area(params.x, 0, params.z, 1, 1, 1); if (IsOccupiedByRoad(road_test_area)) { flags = EFloorFlags(flags | mNO_COVER); params.top = 0; } if (lowest_neighbor < params.top) { params.bottom = lowest_neighbor; } else { params.bottom = params.top-1; flags = EFloorFlags(flags & ~mHAS_BOTTOM); } params.flags = flags; for (int y = params.bottom; y < params.top; y++) { if (y == params.top-1 && !(flags & mNO_COVER)) params.flags = EFloorFlags(params.flags | mHAS_TOP); if (y == -1) { if (y <= lowest_neighbor) params.heightSlotOffset = 3; // group 4 -- bottom level, just below surface else params.heightSlotOffset = 2; // group 3 -- not bottom level, just below surface } else if (y < -1) { if (y <= lowest_neighbor) params.heightSlotOffset = 0; // group 1 -- bottom level, not touching surface else params.heightSlotOffset = 1; // group 2 -- not bottom level, not touching surface } else { if (y == params.top-1) { if (y == 0) params.heightSlotOffset = 4; // group 5 -- top level risers sitting on surface else params.heightSlotOffset = 7; // group 8 -- risers sitting above surface, top level } else if (y == 0) params.heightSlotOffset = 5; // group 6 -- risers sitting on surface, but not top level else params.heightSlotOffset = 6; // group 7 -- risers sitting above surface, not top level } params.y = y; if (params.simpleBuild) build_add_floor_piece_simple(params); else build_add_floor_piece(params); } } /* Creates a riser metapiece, if one is needed. */ void CParkManager::build_add_floor_piece(BuildFloorParams ¶ms) { uint32 type = TYPE_EMPTY_BLOCK; if (params.flags & mHAS_BOTTOM) { if (params.flags & mHAS_TOP) type = m_riser_piece_checksum[params.heightSlotOffset][0]; else type = m_riser_piece_checksum[params.heightSlotOffset][1]; } else if (params.flags & mHAS_TOP) { type = m_riser_piece_checksum[params.heightSlotOffset][2]; } else return; // see if floor already included in metapiece at this position int floor_height = params.y + 1; Dbg_MsgAssert(floor_height >= -16 && floor_height <= 15, ("faulty floor height %d, at (%d,%d,%d)", floor_height, params.x, params.y, params.z)); uint32 floor_bit = (1<<(floor_height+16)); if (m_floor_height_map[params.x][params.z].mEnclosedFloor & floor_bit) { #if 0 printf("preexisting riser at %d (%d,%d)!\n", floor_height, params.x, params.z); #endif return; } // see if floor piece is already there and if it has desired type GridDims test_dims(params.x, params.y, params.z, 1, 1, 1); CMapListNode *p_node = GetMetaPiecesAt(test_dims); if (p_node) { if (p_node->GetMeta()->m_name_checksum == type) { /* if (type == TYPE_FLOOR_BLOCK && 0) printf("preserving floor block at %d,%d,%d\n", p_node->GetMeta()->GetArea().GetX(), p_node->GetMeta()->GetArea().GetY(), p_node->GetMeta()->GetArea().GetZ()); */ // undo mark for kill p_node->GetMeta()->clear_flag(CMetaPiece::mMARK_FOR_KILL); p_node->DestroyList(); return; } else p_node->DestroyList(); } if (!params.dryRun) { #ifdef USE_BUILD_LIST enum { MAX_BUILD_LIST_SIZE=10000, }; Dbg_MsgAssert(m_build_list_sizem_num_used_entries; } params.addCount++; } /* Creates a riser metapiece, period. */ void CParkManager::build_add_floor_piece_simple(BuildFloorParams ¶ms) { uint32 type = TYPE_EMPTY_BLOCK; if (params.flags & mHAS_BOTTOM) { if (params.flags & mHAS_TOP) type = m_riser_piece_checksum[params.heightSlotOffset][0]; else type = m_riser_piece_checksum[params.heightSlotOffset][1]; } else if (params.flags & mHAS_TOP) { type = m_riser_piece_checksum[params.heightSlotOffset][2]; } else return; CConcreteMetaPiece *p_out = CreateConcreteMeta(GetAbstractMeta(type)); GridDims pos(params.x, params.y, params.z); AddMetaPieceToPark(pos, p_out); params.addCount++; } /* Some metapieces contain risers. */ void CParkManager::apply_meta_contained_risers_to_floor(CConcreteMetaPiece *pMetaPiece, bool add) { for (uint i = 0; i < pMetaPiece->m_num_used_entries; i++) { CClonedPiece *p_piece = pMetaPiece->GetContainedPiece(i)->CastToCClonedPiece(); // see if this piece affects floor height SMetaDescriptor &desc = pMetaPiece->get_desc_at_index(i); if (desc.mRiser) { GridDims riser_area; p_piece->GetCellDims(&riser_area); riser_area[X] = pMetaPiece->m_cell_area.GetX() + desc.mX; riser_area[Y] = pMetaPiece->m_cell_area.GetY() + desc.mY; riser_area[Z] = pMetaPiece->m_cell_area.GetZ() + desc.mZ; int start_x = riser_area.GetX(); int end_x = start_x + riser_area.GetW(); int start_z = riser_area.GetZ(); int end_z = start_z + riser_area.GetL(); #if 0 printf("raising floor at: (%d,%d,%d)+(%d,%d,%d), desc.mRiser is %d at 0x%x, index %d\n", pMetaPiece->m_cell_area.GetX(), pMetaPiece->m_cell_area.GetY(), pMetaPiece->m_cell_area.GetZ(), desc.mX, desc.mY, desc.mZ, desc.mRiser, &desc.mRiser, i); #endif for (int x = start_x; x < end_x; x++) { for (int z = start_z; z < end_z; z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); if (add) { if (m_floor_height_map[x][z].mHeight < riser_area.GetY() + riser_area.GetH()) m_floor_height_map[x][z].mHeight = riser_area.GetY() + riser_area.GetH(); if (m_floor_height_map[x][z].mHeight > 15) m_floor_height_map[x][z].mHeight = 15; if (m_floor_height_map[x][z].mHeight < -16) m_floor_height_map[x][z].mHeight = -16; } else { // Are there non-riser metapieces above this one? GridDims area_above = riser_area; area_above[Y] = riser_area.GetY() + riser_area.GetH(); area_above.MakeInfinitelyHigh(); CMapListTemp meta_list = GetMetaPiecesAt(area_above, Mth::ROT_0, NULL, true); if (meta_list.IsEmpty()) { if (m_floor_height_map[x][z].mHeight > riser_area.GetY()) m_floor_height_map[x][z].mHeight = riser_area.GetY(); } } for (int y = riser_area.GetY(); y < riser_area.GetY() + riser_area.GetH(); y++) { if (add) m_floor_height_map[x][z].mEnclosedFloor |= (1<<(y+16+1)); else m_floor_height_map[x][z].mEnclosedFloor &= ~(1<<(y+16+1)); } #if 0 printf(" floor change: (%d,%d) to %d, flags 0x%x, bottom %d, top %d\n", x, z, m_floor_height_map[x][z].mHeight, m_floor_height_map[x][z].mEnclosedFloor, riser_area.GetY(), riser_area.GetY() + riser_area.GetH()); #endif } } } } } bool CParkManager::intersection_with_riser_containing_metas(GridDims area) { area.MakeInfiniteOnY(); CMapListTemp list = GetMetaPiecesAt(area, Mth::ROT_0, NULL, true); CMapListNode *p_node = list.GetList(); while (p_node) { if (p_node->GetConcreteMeta()->m_flags & CMetaPiece::mCONTAINS_RISERS) return true; p_node = p_node->GetNext(); } return false; } /* Change map height. Add metapieces in column to move list. Repeat this for every metapiece in move list. No metapiece will end up in move list twice, though. Good LUCK figuring this out. :) */ bool CParkManager::slide_column_part_one(GridDims area, int bottom, int top, ESlideColumnFlags flags, CMapListNode **ppMoveList) { GridDims test_area = area; test_area[Y] = (uint8) bottom; if (flags & mFIRST_RECURSE) test_area.MakeInfiniteOnY(); else test_area.MakeInfinitelyHigh(); for (int x = test_area.GetX(); x < test_area.GetX() + test_area.GetW(); x++) { for (int z = test_area.GetZ(); z < test_area.GetZ() + test_area.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); if (!m_floor_height_map[x][z].mMarkAsSlid) { int change_amount = 0; if (flags & mUNEVEN) { if (flags & mUP) change_amount = top - m_floor_height_map[x][z].mHeight; else change_amount = (m_floor_height_map[x][z].mHeight == top) ? -1 : 0; } else { if (flags & mUP) change_amount = 1; else change_amount = -1; } m_floor_height_map[x][z].mHeight += change_amount; m_floor_height_map[x][z].mSlideAmount = change_amount; if (m_floor_height_map[x][z].mHeight < -16 || m_floor_height_map[x][z].mHeight > 15) { return false; } } m_floor_height_map[x][z].mMarkAsSlid = true; } } CMapListTemp list = GetMetaPiecesAt(test_area, Mth::ROT_0, NULL, true); CMapListNode *p_node = list.GetList(); while (p_node) { CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta(); GridDims meta_area = p_meta->GetArea(); if (!p_meta->IsRiser() && !(p_meta->m_flags & CMetaPiece::mMARK_AS_SLID)) { /* Are we actually going to move this meta? Even surface: -yes Uneven -up: only if lower than top -down */ p_meta->set_flag(CMetaPiece::mMARK_AS_SLID); Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_new_node = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_new_node->mp_next = *ppMoveList; p_new_node->mp_meta = p_meta; *ppMoveList = p_new_node; #if 0 Ryan("rising meta at: "); p_meta->GetArea().PrintContents(); #endif p_new_node->mSlideAmount = 0; if (flags & mUNEVEN) { if (flags & mUP) p_new_node->mSlideAmount = top - meta_area.GetY(); else p_new_node->mSlideAmount = (meta_area.GetY() == top) ? -1 : 0; } else { if (flags & mUP) p_new_node->mSlideAmount = 1; else p_new_node->mSlideAmount = -1; } ESlideColumnFlags recurse_flags = ESlideColumnFlags(flags & ~mFIRST_RECURSE); if (!slide_column_part_one(meta_area, meta_area.GetY(), top, recurse_flags, ppMoveList)) return false; } p_node = p_node->GetNext(); } return true; } void CParkManager::fix_slide_column_part_one_failure() { for (int x = 0; x < FLOOR_MAX_WIDTH; x++) { for (int z = 0; z < FLOOR_MAX_LENGTH; z++) { if (m_floor_height_map[x][z].mMarkAsSlid) { m_floor_height_map[x][z].mMarkAsSlid = false; m_floor_height_map[x][z].mHeight -= m_floor_height_map[x][z].mSlideAmount; } if (m_floor_height_map[x][z].mHeight > 15) m_floor_height_map[x][z].mHeight = 15; if (m_floor_height_map[x][z].mHeight < -16) m_floor_height_map[x][z].mHeight = -16; } } } /* Go through move list, relocate each metapiece */ void CParkManager::slide_column_part_two(CMapListNode *pMoveList) { //Mem::Heap *p_top_down_heap = Mem::Manager::sHandle().TopDownHeap(); CMapListNode *p_node = pMoveList; while (p_node) { CConcreteMetaPiece *p_meta = p_node->GetConcreteMeta(); if (p_node->mSlideAmount < 0) { kill_pieces_in_layer_under_area(p_meta->GetArea()); } p_node->mp_meta = RelocateMetaPiece(p_meta, 0, p_node->mSlideAmount, 0); //if (p_top_down_heap->mp_region->MemAvailable() < 200000) //{ // GetGenerator()->GetClonedScene()->UpdateSuperSectors(); //} p_node = p_node->GetNext(); } } void CParkManager::kill_pieces_in_layer_under_area(GridDims area) { GridDims kill_area = area; kill_area[Y] = area.GetY() - 1; kill_area[H] = 1; #if 0 printf("killing metas in area: (%d,%d,%d)-(%d,%d,%d)\n", kill_area.GetX(), kill_area.GetY(), kill_area.GetZ(), kill_area.GetW(), kill_area.GetH(), kill_area.GetL()); #endif uint32 killed_result = DestroyMetasInArea(kill_area, mEXCLUDE_PIECES_MARKED_AS_SLID); m_num_metas_killed_in_slide += killed_result>>16; m_num_pieces_killed_in_slide += killed_result & 0xFFFF; } /* Undo 'slid' markers in floor map array. Turn off MARK_AS_SLID flags on metapieces. */ void CParkManager::finish_slide_column() { for (int x = m_park_far_bounds.GetX(); x < m_park_far_bounds.GetX() + m_park_far_bounds.GetW(); x++) { for (int z = m_park_far_bounds.GetZ(); z < m_park_far_bounds.GetZ() + m_park_far_bounds.GetL(); z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); m_floor_height_map[x][z].mMarkAsSlid = false; m_floor_height_map[x][z].mSlideAmount = 0; } } CMapListNode *p_node = mp_concrete_metapiece_list; while(p_node) { p_node->GetConcreteMeta()->clear_flag(CMetaPiece::mMARK_AS_SLID); p_node = p_node->GetNext(); } } #ifdef USE_BUILD_LIST void CParkManager::create_metas_in_build_list() { for (int i=0; iGetMeta(); if (p_meta->IsRiser()) { p_meta->set_flag(CMetaPiece::mMARK_FOR_KILL); mark_count++; } p_node = p_node->GetNext(); } mark_count += m_num_metas_killed_in_slide; #ifdef USE_BUILD_LIST m_build_list_size=0; Dbg_MsgAssert(mp_build_list_entry==NULL,("Non-NULL mp_build_list_entry")); #endif Dbg_MsgAssert(m_floor_height_map, ("bad bad monkey!!!")); BuildFloorParams riser_build_params; riser_build_params.simpleBuild = simpleBuild; riser_build_params.dryRun = dryRun; riser_build_params.addCount = 0; // counts all risers added by the output_riser_stack() calls riser_build_params.addCount2 = 0; for (int x = m_park_near_bounds.GetX() - 1; x < m_park_near_bounds.GetX() + m_park_near_bounds.GetW() + 1; x++) { for (int z = m_park_near_bounds.GetZ() - 1; z < m_park_near_bounds.GetZ() + m_park_near_bounds.GetL() + 1; z++) { Dbg_MsgAssert(x >= 0 && z >= 0 && x < FLOOR_MAX_WIDTH && z < FLOOR_MAX_LENGTH, ("out of bounds (%d,%d)", x, z)); riser_build_params.x = x; riser_build_params.z = z; riser_build_params.top = m_floor_height_map[x][z].mHeight; riser_build_params.bottom = m_park_far_bounds.GetY(); output_riser_stack(riser_build_params); } #ifdef __PLAT_XBOX__ // On the XBox the large amount of CPU time taken up by rebuilding the park causes a glitch when // wma music is playing. So make sure Pcm::Update gets called often whilst in this inner loop. DirectSoundDoWork(); Pcm::Update(); #endif // __PLAT_XBOX__ #ifdef __PLAT_NGC__ // Same for the NGC ... Pcm::Update(); #endif // __PLAT_NGC__ } // remove all marked floor piece int kill_count = 0; // counts risers killed int kill_count2 = 0; // counts contained pieces int left_count = 0; // counts risers not killed plus those added p_node = mp_concrete_metapiece_list; while(p_node) { CMapListNode *p_nextNode = p_node->GetNext(); // get next node now, as DestroyConcretMeta, below, can delete current node CMetaPiece *p_meta = p_node->GetMeta(); if (p_meta->m_flags & CMetaPiece::mMARK_FOR_KILL) { kill_count++; kill_count2 += p_meta->m_num_used_entries; if (!dryRun) { //p_node->GetConcreteMeta()->GetArea().PrintContents(); DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE); } } else if (p_meta->IsRiser()) left_count++; p_node = p_nextNode; } kill_count += m_num_metas_killed_in_slide; kill_count2 += m_num_pieces_killed_in_slide; #ifdef USE_BUILD_LIST mp_generator->GetClonedScene()->UpdateSuperSectors(); if (!dryRun) { left_count+=m_build_list_size; create_metas_in_build_list(); } Dbg_MsgAssert(mp_build_list_entry==NULL,("Non-NULL mp_build_list_entry")); #endif // risers preexisting + risers added - risers killed == risers left Dbg_Assert(dryRun || riser_build_params.addCount == left_count + kill_count - mark_count); //printf("riser_build_params.addCount = %d kill_count = %d, kill_count2 = %d\n",riser_build_params.addCount,kill_count,kill_count2); //if (dryRun) // printf("DRYRUN: "); //printf("marked %d pieces, killed %d, not killed %d, left %d, added %d\n", // mark_count, kill_count, mark_count - kill_count, left_count, riser_build_params.addCount); //printf("addCount2 = %d, killCount2 = %d\n", riser_build_params.addCount2, kill_count2); if (dryRun && (riser_build_params.addCount > kill_count || riser_build_params.addCount2 > kill_count2)) { return false; } //if (dryRun && (riser_build_params.addCount - kill_count > 200)) //{ // // Don't allow height changes that will result in too many pieces being created, to prevent // // running out of memory. // return false; //} return true; } /* Create new list if none */ void CParkManager::add_metapiece_to_node_list(CMetaPiece *pMetaToAdd, CMapListNode **ppList) { CMapListNode *p_test_node = *ppList; while(p_test_node) { if (p_test_node->GetMeta() == pMetaToAdd) return; // it's in there p_test_node = p_test_node->GetNext(); } // not in list already, so add it Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_new_node = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_new_node->mp_meta = pMetaToAdd; p_new_node->mp_next = *ppList; *ppList = p_new_node; } /* Opposite of above */ void CParkManager::remove_metapiece_from_node_list(CMetaPiece *pMetaToRemove, CMapListNode **ppList) { CMapListNode *p_node = *ppList; CMapListNode *p_prev = NULL; while(p_node) { if (p_node->GetMeta() == pMetaToRemove) { if (p_prev) p_prev->mp_next = p_node->GetNext(); else *ppList = p_node->GetNext(); delete p_node; return; } p_prev = p_node; p_node = p_node->GetNext(); } } void CParkManager::bucketify_metapiece(CConcreteMetaPiece *pMeta) { for (int x = 0; x < pMeta->m_cell_area.GetW(); x++) { for (int z = 0; z < pMeta->m_cell_area.GetL(); z++) { Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_new_entry = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_new_entry->mp_meta = pMeta; p_new_entry->mp_next = mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()]; mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()] = p_new_entry; } } } void CParkManager::debucketify_metapiece(CConcreteMetaPiece *pMeta) { for (int x = 0; x < pMeta->m_cell_area.GetW(); x++) { for (int z = 0; z < pMeta->m_cell_area.GetL(); z++) { CMapListNode *p_prev = NULL; CMapListNode *p_entry = mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()]; while(p_entry) { // piece should occur only once per list if (p_entry->mp_meta == pMeta) { if (p_prev) p_prev->mp_next = p_entry->mp_next; else mp_bucket_list[x + pMeta->m_cell_area.GetX()][z + pMeta->m_cell_area.GetZ()] = p_entry->mp_next; delete p_entry; break; } p_prev = p_entry; p_entry = p_entry->mp_next; } } // end for z } // end for x } void CParkManager::create_abstract_metapieces() { ParkEd("CParkManager::create_abstract_metapieces()"); /* This add_later stuff is so we can create singular metapieces that weren't defined in Ed_standard_metapieces. Every CPiece that exists must have a corresponding singular metapiece, at least an abstract one. */ uint32 add_later_tab[1000]; uint add_later_count = 0; Script::CArray *p_metapiece_array = Script::GetArray("Ed_standard_metapieces", Script::ASSERT); // this first loop makes sure that metapieces named in EdPieces2.q actually exist for (uint i = 0; i < p_metapiece_array->GetSize(); i++) { Script::CStruct *p_entry = p_metapiece_array->GetStructure(i); // see if singular or multiple metapiece uint32 single_crc = 0; if (p_entry->GetChecksum("single", &single_crc)) { mp_generator->GetMasterPiece(single_crc, false); } } // end for // now, build abstract metapieces from Ed_standard_metapieces array for (uint i = 0; i < p_metapiece_array->GetSize(); i++) { Script::CStruct *p_entry = p_metapiece_array->GetStructure(i); Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CAbstractMetaPiece *p_meta = new CAbstractMetaPiece(); Mem::Manager::sHandle().PopContext(); // see if singular or multiple metapiece uint32 single_crc = 0; Script::CArray *p_multiple_array = NULL; if (p_entry->GetChecksum("single", &single_crc)) { p_meta->initialize_desc_table(1); CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(single_crc, true); GridDims area(0, 0, 0); p_source_piece->GetCellDims(&area); Script::CArray *p_pos_array = NULL; if (p_entry->GetArray("pos", &p_pos_array)) { area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2)); } p_meta->add_piece(p_source_piece, &area); // if just a singular meta, use the name checksum already associated with single piece p_meta->m_name_checksum = p_source_piece->GetType(); Dbg_Assert(add_later_count < 1000); add_later_tab[add_later_count++] = p_meta->m_name_checksum; p_meta->set_flag(CMetaPiece::mSINGULAR); // Finally, Check to see if it is a special type // and add it to the table of special types: uint32 special_type; if (p_entry->GetChecksum(CRCD(0xf176a9fd,"special_type"), &special_type)) { // gap piece detection here ----- need to reinstate this when we do gaps if (special_type == CRCD(0x2813ab8a,"gap_placement")) p_meta->set_flag(CMetaPiece::mGAP_PLACEMENT); if (special_type == CRCD(0xfc26b6f4,"area_selection")) p_meta->set_flag(CMetaPiece::mAREA_SELECTION); if (special_type == CRCD(0xffd81c08,"rail_placement")) p_meta->set_flag(CMetaPiece::mRAIL_PLACEMENT); if (special_type == CRCD(0x48fa9c7b,"restart_1")) SetRestartTypeId(CParkGenerator::vONE_PLAYER, single_crc); if (special_type == CRCD(0x419183b2,"restart_multi")) SetRestartTypeId(CParkGenerator::vMULTIPLAYER, single_crc); if (special_type == CRCD(0xe69aefaf,"restart_horse")) SetRestartTypeId(CParkGenerator::vHORSE, single_crc); if (special_type == CRCD(0xa091ce25,"king_of_hill")) SetRestartTypeId(CParkGenerator::vKING_OF_HILL, single_crc); if (special_type == CRCD(0x74b0f4e9,"red_flag")) { SetRestartTypeId(CParkGenerator::vRED_FLAG, single_crc); p_meta->set_flag(CMetaPiece::mNOT_ON_GC); } if (special_type == CRCD(0xc893acf8,"green_flag")) { SetRestartTypeId(CParkGenerator::vGREEN_FLAG, single_crc); p_meta->set_flag(CMetaPiece::mNOT_ON_GC); } if (special_type == CRCD(0x40bdfa7e,"blue_flag")) { SetRestartTypeId(CParkGenerator::vBLUE_FLAG, single_crc); p_meta->set_flag(CMetaPiece::mNOT_ON_GC); } if (special_type == CRCD(0x3ae047fc,"yellow_flag")) { SetRestartTypeId(CParkGenerator::vYELLOW_FLAG, single_crc); p_meta->set_flag(CMetaPiece::mNOT_ON_GC); } } } else if (p_entry->GetArray("multiple", &p_multiple_array)) { bool contains_risers = false; p_meta->initialize_desc_table(p_multiple_array->GetSize()); for (uint j = 0; j < p_multiple_array->GetSize(); j++) { Script::CStruct *p_name_pos = p_multiple_array->GetStructure(j); Script::CComponent *p_name_crc_comp = p_name_pos->GetNextComponent(NULL); Dbg_Assert(p_name_crc_comp->mType == ESYMBOLTYPE_NAME); uint32 name_crc = p_name_crc_comp->mChecksum; Script::CArray *p_pos_array = NULL; p_name_pos->GetArray(NONAME, &p_pos_array, Script::ASSERT); CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(name_crc, true); Dbg_Assert(add_later_count < 1000); add_later_tab[add_later_count++] = name_crc; GridDims area; p_source_piece->GetCellDims(&area); area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2)); Mth::ERot90 rot = Mth::ROT_0; if (p_pos_array->GetSize() > 3) rot = (Mth::ERot90) p_pos_array->GetInteger(3); if (p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser"))) contains_risers = true; p_meta->add_piece(p_source_piece, &area, rot, p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser"))); } // end for if (contains_risers) p_meta->set_flag(CMetaPiece::mCONTAINS_RISERS); } else if (p_entry->GetArray("ack", &p_multiple_array)) { p_meta->initialize_desc_table(p_multiple_array->GetSize()); for (uint j = 0; j < p_multiple_array->GetSize(); j++) { Script::CStruct *p_name_pos = p_multiple_array->GetStructure(j); Script::CComponent *p_name_crc_comp = p_name_pos->GetNextComponent(NULL); Dbg_Assert(p_name_crc_comp->mType == ESYMBOLTYPE_NAME); uint32 name_crc = p_name_crc_comp->mChecksum; Script::CArray *p_pos_array = NULL; p_name_pos->GetArray(NONAME, &p_pos_array, Script::ASSERT); CAbstractMetaPiece *p_source_meta = GetAbstractMeta(name_crc); GridDims area = p_source_meta->GetArea(); area.SetXYZ(p_pos_array->GetInteger(0), p_pos_array->GetInteger(1), p_pos_array->GetInteger(2)); Mth::ERot90 rot = Mth::ROT_0; if (p_pos_array->GetSize() > 3) rot = (Mth::ERot90) p_pos_array->GetInteger(3); p_meta->add_piece_dumb(p_source_meta, &area, rot, p_name_pos->ContainsFlag(CRCD(0xcd75a3f,"riser"))); } // end for } else { Dbg_MsgAssert(0, ("screwy entry %d in Ed_standard_metapieces", i)); } #if 1 #ifdef __NOPT_ASSERT__ GridDims test_area = p_meta->GetArea(); Dbg_MsgAssert(test_area.GetW() > 0 && test_area.GetH() > 0 && test_area.GetL() > 0, ("metapiece %s has invalid dimensions (%d,%d,%d)", Script::FindChecksumName(p_meta->GetNameChecksum()), test_area.GetW(), test_area.GetH(), test_area.GetL())); #if 0 if (p_meta->GetNameChecksum() == Script::GenerateCRC("Sk4Ed_Fence_10x10")) printf("OY! metapiece %s has dimensions (%d,%d,%d)\n", Script::FindChecksumName(p_meta->GetNameChecksum()), test_area.GetW(), test_area.GetH(), test_area.GetL()); #endif #endif #endif // the name provided in EdPieces.q, if any, takes precedence if (p_entry->GetChecksum("name", &p_meta->m_name_checksum)) { // this metapiece has been renamed, so we must treat it as a non-singular piece p_meta->clear_flag(CMetaPiece::mSINGULAR); } // make sure no piece with checksum currently exists Dbg_MsgAssert(!GetAbstractMeta(p_meta->m_name_checksum), ("name checksum already used for metapiece")); if (p_entry->ContainsFlag("is_riser")) p_meta->set_flag(CMetaPiece::mIS_RISER); if (p_entry->ContainsFlag("no_rails")) { # ifdef __NOPT_ASSERT__ Ryan("No rails for %s\n", Script::FindChecksumName(p_meta->m_name_checksum)); # endif p_meta->set_flag(CMetaPiece::mNO_RAILS); } Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_node = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_node->mp_meta = p_meta; p_node->mp_next = mp_abstract_metapiece_list; mp_abstract_metapiece_list = p_node; m_num_abstract_metapieces++; } // end for for (uint i = 0; i < add_later_count; i++) { if (GetAbstractMeta(add_later_tab[i])) continue; Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CAbstractMetaPiece *p_meta = new CAbstractMetaPiece(); Mem::Manager::sHandle().PopContext(); p_meta->initialize_desc_table(1); CSourcePiece *p_source_piece = mp_generator->GetMasterPiece(add_later_tab[i], true); GridDims area(0, 0, 0); p_source_piece->GetCellDims(&area); p_meta->add_piece(p_source_piece, &area); p_meta->m_name_checksum = add_later_tab[i]; p_meta->set_flag(CMetaPiece::mSINGULAR); Mem::Manager::sHandle().PushContext(mp_generator->GetParkEditorHeap()); CMapListNode *p_node = new CMapListNode(); Mem::Manager::sHandle().PopContext(); p_node->mp_meta = p_meta; p_node->mp_next = mp_abstract_metapiece_list; mp_abstract_metapiece_list = p_node; m_num_abstract_metapieces++; } } void CParkManager::SetRestartTypeId(CParkGenerator::RestartType type, uint32 id) { // Mick // printf ("SetRestartTypeId(%d,%x)\n",type,id); if (type > CParkGenerator::vEMPTY && type < CParkGenerator::vNUM_RESTART_TYPES) { m_restartPieceIdTab[type] = id; } /* switch(type) { case CParkGenerator::vONE_PLAYER: // printf ("Found vONE_PLAYER\n"); m_restartPieceIdTab[0] = id; break; case CParkGenerator::vMULTIPLAYER: // printf ("Found vMULTIPLAYER\n"); m_restartPieceIdTab[1] = id; break; case CParkGenerator::vHORSE: // printf ("Found vHORSE\n"); m_restartPieceIdTab[2] = id; break; case CParkGenerator::vKING_OF_HILL: // printf ("Found vKING_OF_HILL\n"); m_restartPieceIdTab[3] = id; break; default: break; } */ } // returns the type of restart piece this is, or vEMPTY is it is not a restart piece // (vEMPTY is 0, so the return value can be treated as true/false) CParkGenerator::RestartType CParkManager::IsRestartPiece(uint32 id) { for (int type = CParkGenerator::vONE_PLAYER; type < CParkGenerator::vNUM_RESTART_TYPES; type++) { if (id == m_restartPieceIdTab[type]) { return (CParkGenerator::RestartType) type; } } return CParkGenerator::vEMPTY; /* if (id == m_restartPieceIdTab[0]) return CParkGenerator::vONE_PLAYER; if (id == m_restartPieceIdTab[1]) return CParkGenerator::vMULTIPLAYER; if (id == m_restartPieceIdTab[2]) return CParkGenerator::vHORSE; if (id == m_restartPieceIdTab[3]) return CParkGenerator::vKING_OF_HILL; return CParkGenerator::vEMPTY; */ } /* Commented out cos I just discovered Destroy() does pretty much the same thing! // K: This is for freeing up as much memory as possible when playing a park, so that // the number of players can be increased. Each player requires 830K. void CParkManager::FreeUpMemoryForPlayingPark() { // Delete everything in the park editor heap, so that it can be removed, freeing // up 900K. // First delete the cursor. Ed::CParkEditor::Instance()->DeleteCursor(); printf("After deleting cursor ...\n"); printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems()); MemView_AnalyzeHeap(mp_generator->GetParkEditorHeap()); mp_generator->DeleteSourceAndClonedPieces(); // Delete the concrete metapieces CMapListNode *p_node = mp_concrete_metapiece_list; while(p_node) { CMapListNode *p_next_node = p_node->GetNext(); delete p_node->GetConcreteMeta(); delete p_node; p_node = p_next_node; } m_num_concrete_metapieces=0; mp_concrete_metapiece_list=NULL; printf("After deleting CParkManager::mp_concrete_metapiece_list ...\n"); printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems()); MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap()); // Free up the abstract metapieces. p_node = mp_abstract_metapiece_list; while(p_node) { CMapListNode *p_next_node = p_node->GetNext(); delete p_node->GetAbstractMeta(); delete p_node; p_node = p_next_node; } m_num_abstract_metapieces=0; mp_abstract_metapiece_list=NULL; printf("After deleting CParkManager::mp_abstract_metapiece_list ...\n"); printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems()); MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap()); // The park editor heap is now totally empty! // So delete it. mp_generator->DeleteParkEditorHeap(); printf("After deleting stuff ...\n"); printf("Num CMapListNode's = %d\n",Ed::CMapListNode::SGetNumUsedItems()); MemView_AnalyzeHeap(Ed::CParkManager::sInstance()->GetGenerator()->GetParkEditorHeap()); } */ // K: Added type so that DESTROY_ONLY_PIECES can be passed when this function is used to clean // up the park editor heap for deletion when playing a park. void CParkManager::destroy_concrete_metapieces(CParkGenerator::EDestroyType type) { ParkEd("CParkManager::destroy_concrete_metapieces()"); Dbg_Assert(mp_gap_manager); mp_gap_manager->RemoveAllGaps(); // Commented these out because destroy_concrete_metapieces gets called via // ScriptFreeUpMemoryForPlayingPark, which must not delete the edited rails. // (Fix to TT2125) //Obj::GetRailEditor()->DestroyRailGeometry(); //Obj::GetRailEditor()->DestroyPostGeometry(); // remove all concrete metapieces, except locked ones CMapListNode *p_node = mp_concrete_metapiece_list; while(p_node) { CMapListNode *p_next_node = p_node->GetNext(); DestroyConcreteMeta(p_node->GetConcreteMeta(), mDONT_DESTROY_PIECES_ABOVE); p_node = p_next_node; } mp_generator->DestroyAllClonedPieces(type); // if any are left mp_generator->KillRestarts(); // This should have been taken care of by destrying the actual concrete metas, but just to be safe.... } #ifdef __PLAT_NGC__ void CParkManager::SwapMapBufferEndianness() { CompressedMapHeader *p_header = (CompressedMapHeader *)mp_compressed_map_buffer; p_header->mChecksum=_32(p_header->mChecksum); p_header->mSizeInBytes=_16(p_header->mSizeInBytes); p_header->mVersion=_16(p_header->mVersion); p_header->mTheme=_16(p_header->mTheme); p_header->mParkSize=_16(p_header->mParkSize); p_header->mNumMetas=_16(p_header->mNumMetas); p_header->mNumGaps=_16(p_header->mNumGaps); p_header->mTODScript=_32(p_header->mTODScript); uint32 *p_metas=(uint32*)((uint8*)(p_header+1) + FLOOR_MAX_WIDTH * FLOOR_MAX_LENGTH); for (int i=0; imNumMetas; ++i) { p_metas[0]=_32(p_metas[0]); p_metas[1]=_32(p_metas[1]); p_metas+=2; } CompressedGap *p_gap=(CompressedGap*)p_metas; for (int i=0; imNumGaps; ++i) { p_gap->score=_16(p_gap->score); p_gap->mCancelFlags=_32(p_gap->mCancelFlags); ++p_gap; } } #endif /* Park editor map file format: Header -size -version -theme -X, Z, W, L -number of metas Floor height array, each entry: -height -texture (included risers taken care of by placed metapieces) Meta entries: -10 bits: index -8 bits: X -8 bits: Z -6 bits: padding -2 bits: rot -5 bits: Y -1 bit: padding (total: 40) Gap info User-defined stamps -number contained -source meta -8 bits: X -8 bits: Z -6 bits: Y -2 bits: rot */ void CParkManager::write_compressed_map_buffer() { Dbg_Assert(m_state_on); ParkEd("CParkManager::write_compressed_map_buffer()"); //DumpUnwindStack( 20, 0 ); if (m_compressed_map_flags & mIS_NEWER_THAN_PARK) // we just loaded this park, so don't touch it return; for (int i=0; iGetSize(); CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; p_header->mVersion = (VERSION); p_header->mTheme = (m_theme); p_header->mParkSize = (0); p_header->mX = m_park_near_bounds.GetX(); p_header->mZ = m_park_near_bounds.GetZ(); p_header->mW = m_park_near_bounds.GetW(); p_header->mL = m_park_near_bounds.GetL(); p_header->mTODScript = Ed::CParkEditor::Instance()->GetTimeOfDayScript(); p_header->mMaxPlayers=GetGenerator()->GetMaxPlayers(); Dbg_MsgAssert(strlen(mp_park_name) < 64,("Park name '%s' too long",mp_park_name)); strcpy(p_header->mParkName,mp_park_name); CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1); for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { p_floor_entry->mHeight = (sint8) m_floor_height_map[i][j].mHeight; p_floor_entry++; } } uint8 *p_meta_entry = (uint8 *) p_floor_entry; int num_metas_outputted = 0; CMapListNode *p_meta_node = mp_concrete_metapiece_list; while(p_meta_node) { CConcreteMetaPiece *p_meta = p_meta_node->GetConcreteMeta(); if (!p_meta->IsRiser() && !p_meta->IsInnerShell() && p_meta->IsInPark()) { uint32 *p_meta_entry_first = (uint32 *) p_meta_entry; uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4); GridDims meta_area = p_meta->GetArea(); uint32 metapiece_index = 0; uint32 metapiece_name_checksum = p_meta->GetNameChecksum(); for (; metapiece_index < save_map_array_size; metapiece_index++) { if (p_save_map_array->GetChecksum(metapiece_index) == metapiece_name_checksum) break; } if (metapiece_index < save_map_array_size) { *p_meta_entry_first = metapiece_index & 1023; *p_meta_entry_first |= meta_area.GetX() << 10; *p_meta_entry_first |= meta_area.GetZ() << 18; *p_meta_entry_second = p_meta->GetRot() & 3; *p_meta_entry_second |= ((meta_area.GetY() + 16) & 31) << 2; *p_meta_entry_first = (*p_meta_entry_first); *p_meta_entry_second = (*p_meta_entry_second); //Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second); p_meta_entry += 8; num_metas_outputted++; } } p_meta_node = p_meta_node->GetNext(); } int buffer_used_size = (uint32) p_meta_entry - (uint32) p_header; Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size)); p_header->mNumMetas = (num_metas_outputted); /* Gap Section */ // count number of gaps CGapManager::GapDescriptor *p_desc_tab[CGapManager::vMAX_GAPS]; int num_gaps = 0; int gap; for (gap = 0; gap < CGapManager::vMAX_GAPS; gap++) { int half = 0; CGapManager::GapDescriptor *pDesc = mp_gap_manager->GetGapDescriptor(gap, &half); if (pDesc && half == 0 && pDesc->numCompleteHalves == 2) p_desc_tab[num_gaps++] = pDesc; } p_header->mNumGaps = (num_gaps); // write gaps CompressedGap *p_out_gap = (CompressedGap *) p_meta_entry; for (gap = 0; gap < num_gaps; gap++) { p_out_gap->rot = 0; for (int half = 0; half < 2; half++) { p_out_gap->x[half] = p_desc_tab[gap]->loc[half].GetX(); p_out_gap->y[half] = p_desc_tab[gap]->loc[half].GetY() + (MAX_HEIGHT>>1); p_out_gap->z[half] = p_desc_tab[gap]->loc[half].GetZ(); p_out_gap->rot |= p_desc_tab[gap]->rot[half]<<(half*4); p_out_gap->extent[half] = (uint8) ((p_desc_tab[gap]->leftExtent[half] & 15) << 4); p_out_gap->extent[half] |= (uint8) (p_desc_tab[gap]->rightExtent[half] & 15); //Ryan("adding gap at (%d,%d,%d)\n", p_out_gap->x[half], p_out_gap->y[half], p_out_gap->z[half]); } Dbg_MsgAssert(strlen(p_desc_tab[gap]->text) <= 31, ("too many characters in gap name %s", p_desc_tab[gap]->text)); strcpy(p_out_gap->text, p_desc_tab[gap]->text); p_out_gap->score = ((uint16) p_desc_tab[gap]->score); p_out_gap->mCancelFlags=p_desc_tab[gap]->mCancelFlags; p_out_gap++; } buffer_used_size = (uint32) p_out_gap - (uint32) p_header; Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size)); p_header->mSizeInBytes = buffer_used_size; /* Flag section */ if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK)) m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIN_SYNC_WITH_PARK + mIS_VALID)); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK); p_header->mChecksum = (Crc::GenerateCRCCaseSensitive((const char *) mp_compressed_map_buffer + 4, COMPRESSED_MAP_SIZE-4)); // XXX ParkEd("end CParkManager::write_compressed_map_buffer()"); } // Create a minimal compressed map buffer void CParkManager::fake_compressed_map_buffer() { ParkEd("CParkManager::fake_compressed_map_buffer()"); // Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT); // uint save_map_array_size = p_save_map_array->GetSize(); setup_default_dimensions(); CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; p_header->mVersion = (VERSION); p_header->mTheme = (m_theme); p_header->mParkSize = (0); p_header->mX = m_park_near_bounds.GetX(); p_header->mZ = m_park_near_bounds.GetZ(); p_header->mW = m_park_near_bounds.GetW(); p_header->mL = m_park_near_bounds.GetL(); Ed::CParkEditor *p_editor=Ed::CParkEditor::Instance(); if (p_editor) { p_header->mTODScript = p_editor->GetTimeOfDayScript(); } else { p_header->mTODScript=0; } p_header->mMaxPlayers=2; strcpy(p_header->mParkName, "unnamed park"); CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1); for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { p_floor_entry->mHeight = 0; // (sint8) m_floor_height_map[i][j].mHeight; p_floor_entry++; } } uint8 *p_meta_entry = (uint8 *) p_floor_entry; int num_metas_outputted = 0; int buffer_used_size = (uint32) p_meta_entry - (uint32) p_header; p_header->mNumMetas = num_metas_outputted; p_header->mChecksum = Crc::GenerateCRCCaseSensitive((const char *) mp_compressed_map_buffer + 4, buffer_used_size - 4); int num_gaps = 0; p_header->mNumGaps = num_gaps; // write gaps CompressedGap *p_out_gap = (CompressedGap *) p_meta_entry; buffer_used_size = (uint32) p_out_gap - (uint32) p_header; Dbg_MsgAssert(buffer_used_size <= COMPRESSED_MAP_SIZE, ("compressed map buffer (%d) needs to be bigger than (%d)",COMPRESSED_MAP_SIZE,buffer_used_size)); p_header->mSizeInBytes = buffer_used_size; /* Flag section */ if (!(m_compressed_map_flags & mIN_SYNC_WITH_PARK) && !(m_compressed_map_flags & mIS_NEWER_THAN_PARK)) m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mNOT_SAVED_LOCAL); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | (mIN_SYNC_WITH_PARK + mIS_VALID)); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK); // XXX ParkEd("end CParkManager::fake_compressed_map_buffer()"); } void CParkManager::read_from_compressed_map_buffer() { Dbg_Assert(m_state_on); // XXX ParkEd("CParkManager::read_from_compressed_map_buffer()"); Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT); uint32 dead_piece_checksum = Script::GenerateCRC("Sk4Ed_Dead"); Dbg_MsgAssert(m_compressed_map_flags & mIS_VALID, ("compressed map is invalid")); CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; // make sure latest version if (p_header->mVersion != VERSION) { read_from_compressed_map_buffer_old(); GetGenerator()->SetMaxPlayers(1); return; } m_park_near_bounds[X] = p_header->mX; m_park_near_bounds[Z] = p_header->mZ; m_park_near_bounds[W] = p_header->mW; m_park_near_bounds[L] = p_header->mL; m_theme = p_header->mTheme; Ed::CParkEditor::Instance()->SetTimeOfDayScript(p_header->mTODScript); #ifdef __PLAT_NGC__ p_header->mMaxPlayers=2; #endif GetGenerator()->SetMaxPlayers(p_header->mMaxPlayers); // SG: Was setting to the incorrect number (8 when it should have been 2). I think // it is redundant anyway as max players is set elsewhere already //GameNet::Manager::Instance()->SetMaxPlayers(p_header->mMaxPlayers); CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1); for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { m_floor_height_map[i][j].mHeight = (int) p_floor_entry->mHeight; m_floor_height_map[i][j].mEnclosedFloor = 0; p_floor_entry++; } } uint8 *p_meta_entry = (uint8 *) p_floor_entry; Dbg_Printf( "****** NUM METAS WHILE READING FROM BUFFER : %d\n", p_header->mNumMetas ); for (int i = 0; i < p_header->mNumMetas; i++) { uint32 *p_meta_entry_first = (uint32 *) p_meta_entry; uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4); uint32 meta1 = *p_meta_entry_first; uint32 meta2 = *p_meta_entry_second; //Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second); uint32 metapiece_index = meta1 & 1023; uint32 metapiece_checksum = p_save_map_array->GetChecksum(metapiece_index); if (metapiece_checksum != dead_piece_checksum) { CAbstractMetaPiece *p_source_meta = GetAbstractMeta(metapiece_checksum); Dbg_MsgAssert(p_source_meta, ("couldn't find abstract meta %s", Script::FindChecksumName(metapiece_checksum))); #ifdef __PLAT_NGC__ if (!(p_source_meta->m_flags & CMetaPiece::mNOT_ON_GC)) #endif { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_source_meta); GridDims pos; pos[X] = (meta1 >> 10) & 255; pos[Z] = (meta1 >> 18) & 255; pos[Y] = ((meta2 >> 2) & 31) - 16; int rot = meta2 & 3; p_meta->SetRot(Mth::ERot90(rot)); AddMetaPieceToPark(pos, p_meta); } } p_meta_entry += 8; } /* Gap section */ CompressedGap *p_in_gap = (CompressedGap *) p_meta_entry; for (int g = 0; g < p_header->mNumGaps; g++) { CGapManager::GapDescriptor desc; for (int half = 0; half < 2; half++) { desc.loc[half][X] = p_in_gap->x[half]; desc.loc[half][Y] = p_in_gap->y[half] - (MAX_HEIGHT>>1); desc.loc[half][Z] = p_in_gap->z[half]; desc.rot[half] = (p_in_gap->rot>>(half*4)) & 15; desc.leftExtent[half] = (p_in_gap->extent[half]>>4) & 15; desc.rightExtent[half] = (p_in_gap->extent[half]) & 15; //Ryan("adding gap at (%d,%d,%d)\n", p_in_gap->x[half], p_in_gap->y[half], p_in_gap->z[half]); } Dbg_Assert(strlen(p_in_gap->text) < 32); strcpy(desc.text, p_in_gap->text); desc.score = (int) p_in_gap->score; desc.numCompleteHalves = 2; desc.mCancelFlags=p_in_gap->mCancelFlags; AddGap(desc); p_in_gap++; } m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK); // XXX ParkEd("end CParkManager::read_from_compressed_map_buffer()"); } void CParkManager::read_from_compressed_map_buffer_old() { Dbg_Assert(m_state_on); // XXX ParkEd("CParkManager::read_from_compressed_map_buffer()"); Script::CArray *p_save_map_array = Script::GetArray("Ed_Save_Map", Script::ASSERT); uint32 dead_piece_checksum = Script::GenerateCRC("Sk4Ed_Dead"); Dbg_MsgAssert(m_compressed_map_flags & mIS_VALID, ("compressed map is invalid")); CompressedMapHeader *p_header = (CompressedMapHeader *) mp_compressed_map_buffer; // make sure proper version Dbg_MsgAssert((p_header->mVersion) == VERSION-1, ("can't read park with this version #: %d", (p_header->mVersion))); // XXX // for (int zzz = 0; zzz < 16; zzz++) // printf("OUT OF DATE PARK FORMAT -- SAVE NOW. OUT OF DATE PARK FORMAT -- SAVE NOW. OUT OF DATE PARK FORMAT -- SAVE NOW.\n"); m_park_near_bounds[X] = p_header->mX; m_park_near_bounds[Z] = p_header->mZ; m_park_near_bounds[W] = p_header->mW; m_park_near_bounds[L] = p_header->mL; m_theme = (p_header->mTheme); Ed::CParkEditor::Instance()->SetTimeOfDayScript(p_header->mTODScript); GetGenerator()->SetMaxPlayers(p_header->mMaxPlayers); // SG: Was setting to the incorrect number (8 when it should have been 2). I think // it is redundant anyway as max players is set elsewhere already //GameNet::Manager::Instance()->SetMaxPlayers(p_header->mMaxPlayers); CompressedFloorEntry *p_floor_entry = (CompressedFloorEntry *) (p_header + 1); for (int i = 0; i < FLOOR_MAX_WIDTH; i++) { for (int j = 0; j < FLOOR_MAX_LENGTH; j++) { m_floor_height_map[i][j].mHeight = (int) p_floor_entry->mHeight; m_floor_height_map[i][j].mEnclosedFloor = 0; p_floor_entry++; } } uint8 *p_meta_entry = (uint8 *) p_floor_entry; Dbg_Printf( "****** NUM METAS WHILE READING FROM BUFFER : %d\n", p_header->mNumMetas ); for (int i = 0; i < (p_header->mNumMetas); i++) { uint32 *p_meta_entry_first = (uint32 *) p_meta_entry; uint32 *p_meta_entry_second = (uint32 *) (p_meta_entry + 4); uint32 meta1 = (*p_meta_entry_first); uint32 meta2 = (*p_meta_entry_second); //Ryan("compressed piece: 0x%x 0x%x\n", *p_meta_entry_first, *p_meta_entry_second); uint32 metapiece_index = meta1 & 1023; uint32 metapiece_checksum = p_save_map_array->GetChecksum(metapiece_index); if (metapiece_checksum != dead_piece_checksum) { CAbstractMetaPiece *p_source_meta = GetAbstractMeta(metapiece_checksum); Dbg_MsgAssert(p_source_meta, ("couldn't find abstract meta %s", Script::FindChecksumName(metapiece_checksum))); #ifdef __PLAT_NGC__ if (!(p_source_meta->m_flags & CMetaPiece::mNOT_ON_GC)) #endif { CConcreteMetaPiece *p_meta = CreateConcreteMeta(p_source_meta); GridDims pos; pos[X] = (meta1 >> 10) & 255; pos[Z] = (meta1 >> 18) & 255; pos[Y] = ((meta2 >> 2) & 31) - 16; int rot = meta2 & 3; p_meta->SetRot(Mth::ERot90(rot)); AddMetaPieceToPark(pos, p_meta); } } p_meta_entry += 8; } // Gap section CompressedGapOld *p_in_gap = (CompressedGapOld *) p_meta_entry; for (int g = 0; g < (p_header->mNumGaps); g++) { CGapManager::GapDescriptor desc; for (int half = 0; half < 2; half++) { desc.loc[half][X] = p_in_gap->x[half]; desc.loc[half][Y] = p_in_gap->y[half] - (MAX_HEIGHT>>1); desc.loc[half][Z] = p_in_gap->z[half]; desc.rot[half] = (p_in_gap->rot>>(half*4)) & 15; desc.leftExtent[half] = (p_in_gap->extent[half]>>4) & 15; desc.rightExtent[half] = (p_in_gap->extent[half]) & 15; //Ryan("adding gap at (%d,%d,%d)\n", p_in_gap->x[half], p_in_gap->y[half], p_in_gap->z[half]); } Dbg_Assert(strlen(p_in_gap->text) < 32); strcpy(desc.text, p_in_gap->text); desc.score = (int) (p_in_gap->score); desc.numCompleteHalves = 2; AddGap(desc); p_in_gap++; } m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags | mIN_SYNC_WITH_PARK); m_compressed_map_flags = ECompressedMapFlags(m_compressed_map_flags & ~mIS_NEWER_THAN_PARK); // XXX ParkEd("end CParkManager::read_from_compressed_map_buffer_old()"); } void CParkManager::setup_default_dimensions() { int default_inner_dim = Script::GetInt("ed_default_inner_dim", Script::ASSERT); Dbg_Assert(default_inner_dim <= MAX_WIDTH && default_inner_dim <= MAX_LENGTH); m_park_far_bounds.SetXYZ(1, -4, 1); m_park_far_bounds.SetWHL(MAX_WIDTH, 8, MAX_LENGTH); m_park_near_bounds.SetXYZ(1 + (MAX_WIDTH - default_inner_dim) / 2, -4, 1 + (MAX_LENGTH - default_inner_dim) / 2); m_park_near_bounds.SetWHL(default_inner_dim, 8, default_inner_dim); } void CParkManager::setup_road_mask() { /* The first four entries in m_road_mask_tab will be set up in RebuildInnerShell() */ Script::CArray *p_road_mask_array = Script::GetArray("Ed_Roadmask", Script::ASSERT); m_road_mask_tab_size = p_road_mask_array->GetSize(); for (uint i = 4; i < 4 + m_road_mask_tab_size; i++) { Dbg_Assert(i < NUM_ROAD_PIECES); Script::CArray *p_entry = p_road_mask_array->GetArray(i); m_road_mask_tab[i][X] = p_entry->GetInt(0); m_road_mask_tab[i][Z] = p_entry->GetInt(1); m_road_mask_tab[i][W] = p_entry->GetInt(2); m_road_mask_tab[i][L] = p_entry->GetInt(3); m_road_mask_tab[i][Y] = 0; m_road_mask_tab[i].MakeInfinitelyHigh(); } m_road_mask_tab_size += 4; } void CParkManager::setup_lighting() { Script::CArray *pThemeArray = Script::GetArray("Editor_Light_Info"); // Script::CArray *pSizeArray = pThemeArray->GetArray(m_currentThemeIndex); // Script::CScriptStructure *pLightInfo = pSizeArray->GetStructure(m_currentShellSizeIndex); Script::CArray *pSizeArray = pThemeArray->GetArray(0); Script::CScriptStructure *pLightInfo = pSizeArray->GetStructure(0); float amb_const; pLightInfo->GetFloat("ambient_const", &amb_const, true); Mth::Vector ambient_rgb; pLightInfo->GetVector("ambient_rgb", &ambient_rgb, true); float falloff_const; pLightInfo->GetFloat("falloff_const", &falloff_const, true); Mth::Vector falloff_rgb; pLightInfo->GetVector("falloff_rgb", &falloff_rgb, true); float cursor_ambience; pLightInfo->GetFloat("cursor_ambience", &cursor_ambience, true); Script::CArray *pLightArray; pLightInfo->GetArray("pos", &pLightArray, true); mp_generator->SetLightProps(pLightArray->GetSize(), amb_const * ambient_rgb.GetX(), amb_const * ambient_rgb.GetY(), amb_const * ambient_rgb.GetZ(), falloff_const * falloff_rgb.GetX(), falloff_const * falloff_rgb.GetY(), falloff_const * falloff_rgb.GetZ(), cursor_ambience); for (int i = 0; i < (int)pLightArray->GetSize(); i++) { Script::CVector *pInPos = pLightArray->GetVector(i); Mth::Vector outPos; outPos.Set(pInPos->mX * CParkGenerator::CELL_WIDTH, pInPos->mY * CParkGenerator::CELL_HEIGHT, pInPos->mZ * CParkGenerator::CELL_LENGTH); mp_generator->SetLight(outPos, i); } } }