thug/Code/Sk/ParkEditor2/clipboard.cpp
2016-02-14 08:39:12 +11:00

998 lines
26 KiB
C++

#include <core/defines.h>
#include <gel/mainloop.h>
#include <gel/objtrack.h>
#include <gel/event.h>
#include <gel/scripting/checksum.h>
#include <gel/scripting/script.h>
#include <gel/scripting/struct.h>
#include <gel/scripting/array.h>
#include <gel/scripting/symboltable.h>
#include <gel/scripting/utils.h>
#include <sk/ParkEditor2/ParkEd.h>
#include <sk/scripting/cfuncs.h>
#include <sk/modules/FrontEnd/FrontEnd.h>
#include <gfx/nxviewman.h>
#include <gfx/2D/ScreenElemMan.h>
#include <gfx/2D/TextElement.h>
#include <sk/ParkEditor2/clipboard.h>
#include <sk/modules/skate/skate.h>
#include <sk/components/RailEditorComponent.h>
#include <gfx/nx.h>
DefinePoolableClass(Ed::CClipboardEntry)
DefinePoolableClass(Ed::CClipboard)
namespace Ed
{
static void s_rotate_map_vector(int *p_x, int *p_z, Mth::ERot90 rotation)
{
int in_x=*p_x;
int in_z=*p_z;
switch (rotation)
{
case Mth::ROT_0:
break;
case Mth::ROT_90:
*p_x=in_z;
*p_z=-in_x;
break;
case Mth::ROT_180:
*p_x=-in_x;
*p_z=-in_z;
break;
case Mth::ROT_270:
*p_x=-in_z;
*p_z=in_x;
break;
default:
Dbg_MsgAssert(0, ("Wacky rotation"));
break;
}
}
CClipboardEntry::CClipboardEntry()
{
mpMeta=NULL;
mpConcreteMeta=NULL;
mX=mY=mZ=0;
mWidth=0;
mLength=0;
m_rot=Mth::ROT_0;
mpNext=NULL;
}
CClipboardEntry::~CClipboardEntry()
{
DestroyMeta();
}
void CClipboardEntry::DestroyMeta()
{
if (mpConcreteMeta)
{
mpConcreteMeta->MarkUnlocked();
CParkManager::sInstance()->DestroyConcreteMeta(mpConcreteMeta, CParkManager::mDONT_DESTROY_PIECES_ABOVE);
mpConcreteMeta=NULL;
}
}
// This function takes the vector from the centre to the piece, rotates it according to
// the cursor's current rotation, then adds that vector to the cursor's position and places the result in
// p_mapCoords
void CClipboardEntry::CalculateMapCoords(GridDims *p_mapCoords, uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
GridDims meta_area;
if (mpMeta)
{
meta_area=mpMeta->GetArea();
}
else
{
// If there is no meta then the clipboard entry is defining a height for one cell, so
// just set the w and l to 1.
meta_area.SetWHL(1,1,1);
}
Mth::ERot90 total_rotation=Mth::ERot90((p_cursor->GetRotation()+m_rot)&3);
switch ( total_rotation )
{
case Mth::ROT_0:
case Mth::ROT_180:
p_mapCoords->SetWHL(meta_area.GetW(),1,meta_area.GetL());
break;
case Mth::ROT_90:
case Mth::ROT_270:
p_mapCoords->SetWHL(meta_area.GetL(),1,meta_area.GetW());
break;
default:
Dbg_MsgAssert(0, ("Wacky rotation"));
break;
}
// Get the vector from the centre of the selection area to the piece.
int vector_x=mX-centre_x;
int vector_z=mZ-centre_z;
// Rotate the vector according to the cursor's current rotation
s_rotate_map_vector(&vector_x, &vector_z, p_cursor->GetRotation());
// A frig is needed after rotation because the piece always appears to be
// displayed with the coords referring to the top-left corner, even when the
// piece is rotated.
switch ( p_cursor->GetRotation() )
{
case Mth::ROT_0:
break;
case Mth::ROT_90:
vector_z-=mWidth-1;
break;
case Mth::ROT_180:
vector_x-=mWidth-1;
vector_z-=mLength-1;
break;
case Mth::ROT_270:
vector_x-=mLength-1;
break;
default:
Dbg_MsgAssert(0, ("Wacky rotation"));
break;
}
// Get the new map coords of the meta such that it is positioned relative to the cursor.
GridDims cursor_position=p_cursor->GetPosition();
p_mapCoords->SetXYZ(cursor_position.GetX()+vector_x, mY, cursor_position.GetZ()+vector_z);
}
bool CClipboardEntry::CanPaste(uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
GridDims map_coords;
CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
// Make sure the y is set to zero because the GetMetaPiecesAt function that gets called in
// CanPlacePiecesIn will think there is something in the cell if the y is -1
map_coords.SetXYZ(map_coords.GetX(),0,map_coords.GetZ());
CParkManager *p_manager=CParkManager::sInstance();
return p_manager->CanPlacePiecesIn(map_coords, true); // The true means ignore floor height problems
}
// centre_x,centre_z are the map coords of the centre of the original selection area.
// The coords of the entry (mX,mZ) are also in map coords.
void CClipboardEntry::Paste(uint8 centre_x, int raiseAmount, uint8 centre_z, CCursor *p_cursor)
{
if (mpMeta && mpMeta->IsRiser())
{
return;
}
GridDims map_coords;
CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
int new_y=mY+raiseAmount;
if (new_y >= CParkManager::MAX_HEIGHT)
{
new_y=CParkManager::MAX_HEIGHT-1;
}
map_coords.SetXYZ(map_coords.GetX(),new_y,map_coords.GetZ());
CParkManager *p_manager=CParkManager::sInstance();
// Adjust the floor height to match that of the piece
int count=0;
while (true)
{
bool uniform_height=false;
int current_height = p_manager->GetFloorHeight(map_coords, &uniform_height);
if (current_height > new_y)
{
break;
}
if (count>40)
{
Dbg_MsgAssert(0,("Too many recursions of ChangeFloorHeight in CClipboardEntry::Paste"));
break;
}
if (uniform_height && current_height==new_y)
{
break;
}
p_manager->ChangeFloorHeight(map_coords,1); // 1 is the step amount, 1 upwards
++count;
}
if (mpMeta)
{
// Place the piece
CConcreteMetaPiece *p_concrete = p_manager->CreateConcreteMeta(mpMeta);
p_concrete->SetRot(Mth::ERot90((m_rot+p_cursor->GetRotation())&3));
p_manager->AddMetaPieceToPark(map_coords, p_concrete);
}
}
int CClipboardEntry::GetFloorHeight(uint8 centre_x, uint8 centre_z, CCursor *p_cursor)
{
GridDims map_coords;
CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
bool uniform_height;
return CParkManager::sInstance()->GetFloorHeight(map_coords, &uniform_height);
}
void CClipboardEntry::HighlightIntersectingMetas(uint8 centre_x, uint8 centre_z, CCursor *p_cursor, bool on)
{
GridDims map_coords;
CalculateMapCoords(&map_coords,centre_x,centre_z,p_cursor);
// Make sure the y is set to zero because the GetMetaPiecesAt function that gets called in
// CanPlacePiecesIn will think there is something in the cell if the y is -1
map_coords.SetXYZ(map_coords.GetX(),0,map_coords.GetZ());
CParkManager *p_manager=CParkManager::sInstance();
CMapListTemp metas_at_pos = p_manager->GetAreaMetaPieces(map_coords);
if (metas_at_pos.IsEmpty())
{
return;
}
// Iterate over the list of metapieces we just found, and highlight each one
CMapListNode *p_entry = metas_at_pos.GetList();
while(p_entry)
{
CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
if (!p_meta->IsRiser())
{
p_meta->Highlight(on);
}
p_entry = p_entry->GetNext();
}
}
// This gets called when the CCursor is in paste mode, and needs to display the clipboard contents at
// the cursor position.
// pos and rot are the world position and rotation in degrees of the cursor.
// They may not be cell aligned, and the rotation may not be a multiple of 90 degrees.
// This function will position the meta such that the centre of the clipboard selection is at pos.
void CClipboardEntry::ShowMeta(uint8 centre_x, int raiseAmount, uint8 centre_z, Mth::Vector pos, float rot)
{
if (!mpMeta)
{
// No meta to display, meaning the clipboard entry is defining a cell height
return;
}
// Get the vector (in cell units) from the centre of the selection area to the piece.
float vector_x=mX-centre_x;
float vector_z=mZ-centre_z;
// Calculate the vector, in world coords, from the centre of the selection to the centre of the piece.
Mth::Vector p(0.0f,0.0f,0.0f);
p[X]=vector_x*CParkGenerator::CELL_WIDTH+(mWidth-1)*CParkGenerator::CELL_WIDTH/2.0f;
p[Z]=vector_z*CParkGenerator::CELL_LENGTH+(mLength-1)*CParkGenerator::CELL_LENGTH/2.0f;
// Rotate by rot degrees
float rad=rot*3.141592654f/180.0f;
float s=sinf(rad);
float c=cosf(rad);
Mth::Vector rotated;
rotated[X]=c*p[X]+s*p[Z];
rotated[Y]=0.0f;
rotated[Z]=c*p[Z]-s*p[X];
// Add the passed pos to get the final world position of the piece.
Mth::Vector world_pos=pos+rotated;
// Add in the cell height
world_pos[Y]+=mY*CParkGenerator::CELL_HEIGHT;
if (mpMeta->IsRiser())
{
world_pos[Y]-=CParkGenerator::CELL_HEIGHT;
}
// Create the concrete meta if it does not exist already.
if (!mpConcreteMeta)
{
Dbg_MsgAssert(mpMeta,("NULL mpMeta ?"));
mpConcreteMeta=CParkManager::sInstance()->CreateConcreteMeta(mpMeta,true);
mpConcreteMeta->MarkLocked();
}
// Position the piece, adding in the pieces local rotation value.
mpConcreteMeta->SetSoftRot(world_pos,rot+90.0f*m_rot);
}
bool CClipboardEntry::CreateGapFillerPieces()
{
if (!mpMeta)
{
//printf("y=%d\n",mY);
return true;
}
//printf("y=%d Meta=%s\n",mY,Script::FindChecksumName(mpMeta->GetNameChecksum()));
switch (mpMeta->GetNameChecksum())
{
case CRCC(0x66a8fedb,"floor_block1"):
case CRCC(0xffa1af61,"floor_block2"):
case CRCC(0x88a69ff7,"floor_block3"):
case CRCC(0x16c20a54,"floor_block4"):
case CRCC(0x61c53ac2,"floor_block5"):
case CRCC(0xf8cc6b78,"floor_block6"):
case CRCC(0x8fcb5bee,"floor_block7"):
case CRCC(0x1f74467f,"floor_block8"):
break;
default:
return true;
break;
}
//printf("y=%d Meta=%s\n",mY,Script::FindChecksumName(mpMeta->GetNameChecksum()));
bool ran_out_of_memory=false;
CClipboardEntry *p_new_list=NULL;
CClipboardEntry *p_last_in_list=NULL;
for (int y=mY; y>0; --y)
{
if (Mem::CPoolable<CClipboardEntry>::SGetNumUsedItems()==
Mem::CPoolable<CClipboardEntry>::SGetTotalItems())
{
// Not enough space to create a new CClipboardEntry !
ran_out_of_memory=true;
break;
}
CClipboardEntry *p_new_entry=new CClipboardEntry;
if (!p_last_in_list)
{
p_last_in_list=p_new_entry;
}
if (y==mY)
{
p_new_entry->mpMeta = CParkManager::sInstance()->GetAbstractMeta(CRCD(0x2c5b0277,"floor_wall_block3"));
}
else
{
p_new_entry->mpMeta = CParkManager::sInstance()->GetAbstractMeta(CRCD(0x72372c50,"wall_block1"));
}
p_new_entry->mX=mX;
p_new_entry->mY=y;
p_new_entry->mZ=mZ;
p_new_entry->mWidth=1;
p_new_entry->mLength=1;
p_new_entry->m_rot=m_rot;
// Insert into the list.
p_new_entry->mpNext=p_new_list;
p_new_list=p_new_entry;
}
if (p_new_list)
{
Dbg_MsgAssert(p_last_in_list,("Eh?"));
p_last_in_list->mpNext=mpNext;
mpNext=p_new_list;
}
if (ran_out_of_memory)
{
return false;
}
return true;
}
CClipboard::CClipboard()
{
mp_entries=NULL;
m_min_x=m_min_z=255;
m_max_x=m_max_z=0;
mpNext=NULL;
m_area.SetXYZ(0,0,0);
m_area.SetWHL(0,0,0);
mp_rails=NULL;
}
CClipboard::~CClipboard()
{
delete_entries();
}
void CClipboard::delete_entries()
{
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
CClipboardEntry *p_next=p_entry->mpNext;
delete p_entry;
p_entry=p_next;
}
mp_entries=NULL;
m_min_x=m_min_z=255;
m_max_x=m_max_z=0;
m_area.SetXYZ(0,0,0);
m_area.SetWHL(0,0,0);
// Call UpdateSuperSectors to refresh the collision before deleting the rails otherwise the
// collision code will assert when the rail sectors are deleted. (TT6874)
// UpdateSuperSectors isn;t normally called on the rails displayed on the cursor because
// they don't need collision.
Nx::CScene *p_cloned_scene=Ed::CParkManager::sInstance()->GetGenerator()->GetClonedScene();
p_cloned_scene->UpdateSuperSectors();
Obj::CEditedRail *p_rail=mp_rails;
while (p_rail)
{
Obj::CEditedRail *p_next=p_rail->mpNext;
delete p_rail;
p_rail=p_next;
}
mp_rails=NULL;
}
void CClipboard::update_extents(uint8 x, uint8 z)
{
if (x < m_min_x)
{
m_min_x=x;
}
if (x > m_max_x)
{
m_max_x=x;
}
if (z < m_min_z)
{
m_min_z=z;
}
if (z > m_max_z)
{
m_max_z=z;
}
}
// Returns false if it fails due to running out of space on the CClipboardEntry pool
bool CClipboard::AddMeta(CConcreteMetaPiece *p_meta, int raiseAmount)
{
Dbg_MsgAssert(p_meta,("NULL p_meta"));
GridDims dims=p_meta->GetArea();
dims.SetXYZ(dims.GetX(),dims.GetY()+raiseAmount,dims.GetZ());
//printf("AddMeta y=%d raiseAmount=%d, \t%s Riser=%d\n",dims.GetY(),raiseAmount,Script::FindChecksumName(p_meta->GetNameChecksum()),p_meta->IsRiser());
if (p_meta->IsRiser())
{
dims.SetXYZ(dims.GetX(),dims.GetY()+1,dims.GetZ());
//printf("Riser: %d,%d %d\n",dims.GetX(),dims.GetZ(),dims.GetY());
if (dims.GetY())
{
if (!AddHeight(dims.GetX(),dims.GetY(),dims.GetZ()))
{
return false;
}
}
// Zero height riser pieces (ie, floor pieces) still get added so that they
// are displayed on the cursor.
}
CAbstractMetaPiece *p_abstract_meta=CParkManager::sInstance()->GetAbstractMeta(p_meta->GetNameChecksum());
// Don't add meta if it is in the list already
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
if (p_entry->mpMeta==p_abstract_meta &&
p_entry->mX==dims.GetX() &&
p_entry->mY==dims.GetY() &&
p_entry->mZ==dims.GetZ() &&
p_entry->mWidth==dims.GetW() &&
p_entry->mLength==dims.GetL() &&
p_entry->m_rot==p_meta->GetRot())
{
return true;
}
p_entry=p_entry->mpNext;
}
if (Mem::CPoolable<CClipboardEntry>::SGetNumUsedItems()==
Mem::CPoolable<CClipboardEntry>::SGetTotalItems())
{
// Not enough space to create a new CClipboardEntry !
return false;
}
CClipboardEntry *p_new_entry=new CClipboardEntry;
// Store a pointer to the abstract meta so that a copy can be made when the time comes to paste.
// Mustn't store a pointer to the concrete meta because the user may delete that piece later.
p_new_entry->mpMeta = p_abstract_meta;
// Store the grid coords of the concrete meta because the abstract meta's x,y,z will be zero.
p_new_entry->mX=dims.GetX();
p_new_entry->mY=dims.GetY();
p_new_entry->mZ=dims.GetZ();
// Also store the width and length, needed for calculating the world pos of the centre when
// displaying the piece on the cursor.
// Note that the width and height will already take into account any rotation that the concrete meta has.
p_new_entry->mWidth=dims.GetW();
p_new_entry->mLength=dims.GetL();
// Update the min & max x & z for the clipboard selection for calculation of the centre later.
update_extents(dims.GetX(), dims.GetZ());
update_extents(dims.GetX()+dims.GetW()-1, dims.GetZ()+dims.GetL()-1);
p_new_entry->m_rot=p_meta->GetRot();
// Insert into the list.
p_new_entry->mpNext=mp_entries;
mp_entries=p_new_entry;
return true;
}
// Returns false if it fails due to running out of space on the CClipboardEntry pool
bool CClipboard::AddHeight(int x, int y, int z)
{
// If there is a piece covering this position already then do not add
// a new height, to prevent unnecessary column slides.
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
if (p_entry->mpMeta && !p_entry->mpMeta->IsRiser() &&
p_entry->mX <= x && x < p_entry->mX+p_entry->mWidth &&
p_entry->mZ <= z && z < p_entry->mZ+p_entry->mLength)
{
return true;
}
p_entry=p_entry->mpNext;
}
// If there is a height defined for x,z already then just update it's y
// if the passed y is bigger.
p_entry=mp_entries;
while (p_entry)
{
if (p_entry->mpMeta==NULL &&
p_entry->mX <= x && x < p_entry->mX+p_entry->mWidth &&
p_entry->mZ <= z && z < p_entry->mZ+p_entry->mLength)
{
if (y >= p_entry->mY)
{
p_entry->mY=y;
}
return true;
}
p_entry=p_entry->mpNext;
}
if (Mem::CPoolable<CClipboardEntry>::SGetNumUsedItems()==
Mem::CPoolable<CClipboardEntry>::SGetTotalItems())
{
// Not enough space to create a new CClipboardEntry !
return false;
}
CClipboardEntry *p_new_entry=new CClipboardEntry;
p_new_entry->mpMeta = NULL;
p_new_entry->mX=x;
p_new_entry->mY=y;
p_new_entry->mZ=z;
p_new_entry->mWidth=1;
p_new_entry->mLength=1;
// Update the min & max x & z for the clipboard selection for calculation of the centre later.
update_extents(x,z);
// Insert into the list.
p_new_entry->mpNext=mp_entries;
mp_entries=p_new_entry;
return true;
}
// Creates clipboard entries for all the things in the passed area.
// A clipboard entry will be created if there is a piece in a cell, or if
// the cell has a non-zero height.
// Returns false if it fails due to running out of space on the CClipboardEntry pool.
// If that happens, it will delete any CClipboardEntry's that it already allocated.
bool CClipboard::CopySelectionToClipboard(GridDims area)
{
m_area=area;
CParkManager *p_manager=CParkManager::sInstance();
// First, figure out how much the selection needs to be raised up so that
// all the pieces are above ground level.
int most_negative_y=0;
for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
{
for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
{
CMapListNode *p_entry=p_manager->GetMapListNode(x,z);
while (p_entry)
{
CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
//printf("Meta name = %s\n",Script::FindChecksumName(p_meta->GetNameChecksum()));
int y=p_meta->GetArea().GetY();
switch (p_meta->GetNameChecksum())
{
case CRCC(0x66a8fedb,"floor_block1"):
case CRCC(0xffa1af61,"floor_block2"):
case CRCC(0x88a69ff7,"floor_block3"):
case CRCC(0x16c20a54,"floor_block4"):
case CRCC(0x61c53ac2,"floor_block5"):
case CRCC(0xf8cc6b78,"floor_block6"):
case CRCC(0x8fcb5bee,"floor_block7"):
case CRCC(0x1f74467f,"floor_block8"):
// With floor blocks, the ground position is 1 unit higher up than
// their stored y, because their stored y is the y of their base.
++y;
break;
default:
break;
}
if (y < most_negative_y)
{
most_negative_y=y;
}
//printf("y=%d %s\n",p_meta->GetArea().GetY(),Script::FindChecksumName(p_meta->GetNameChecksum()));
p_entry = p_entry->GetNext();
}
}
}
int raise_amount=-most_negative_y;
for (int x = area.GetX(); x < area.GetX() + area.GetW(); x++)
{
for (int z = area.GetZ(); z < area.GetZ() + area.GetL(); z++)
{
CMapListNode *p_entry=p_manager->GetMapListNode(x,z);
while (p_entry)
{
CConcreteMetaPiece *p_meta=p_entry->GetConcreteMeta();
if (p_meta->IsRestartOrFlag())
{
// Don't copy restarts or flags (TT7793)
}
else if (!p_meta->IsCompletelyWithinArea(area))
{
}
else
{
if (!AddMeta(p_meta,raise_amount))
{
// Delete the existing CClipboardEntry's so that another attempt
// at calling CopySelectionToClipboard can be made later after the
// calling code has freed up some memory.
delete_entries();
return false;
}
}
p_entry = p_entry->GetNext();
}
}
}
// Now one last ugly bit. Raising the selection may have moved some simple ground pieces
// to above the ground, in which case they won't have pieces filling the gap, which looks odd.
// So create these pieces if needed.
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
if (!p_entry->CreateGapFillerPieces())
{
// Could not create the pieces, due to low memory.
delete_entries();
return false;
}
p_entry=p_entry->mpNext;
}
Dbg_MsgAssert(mp_rails==NULL,("Expected mp_rails=NULL"));
Mth::Vector corner_pos;
Mth::Vector area_dims;
corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(area,&area_dims);
mp_rails=Obj::GetRailEditor()->GenerateDuplicateRails(corner_pos[X], corner_pos[Z],
corner_pos[X]+area_dims[X], corner_pos[Z]+area_dims[Z]);
return true;
}
bool CClipboard::IsEmpty()
{
if (mp_entries)
{
return false;
}
if (mp_rails)
{
return false;
}
return true;
}
// Pastes the contents of the clipboard into the position that the cursor is at,
// using the cursor's rotation value.
bool CClipboard::Paste(CCursor *p_cursor)
{
Spt::SingletonPtr<CParkEditor> p_editor;
if (!p_editor->RoomToCopyOrPaste(m_max_x-m_min_x+1, m_max_z-m_min_z+1))
{
return false;
}
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
// Bail out if any entry cannot be pasted
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
if (!p_entry->CanPaste(centre_x,centre_z,p_cursor))
{
return false;
}
p_entry=p_entry->mpNext;
}
// Bail out if there are not enough free rail points to create the edited rails
int num_rail_points_to_paste=0;
Obj::CEditedRail *p_rail=mp_rails;
while (p_rail)
{
num_rail_points_to_paste += p_rail->CountPoints();
p_rail=p_rail->mpNext;
}
if (num_rail_points_to_paste > Obj::GetRailEditor()->GetNumFreePoints() )
{
return false;
}
// OK to proceed, so paste the entries
p_entry=mp_entries;
while (p_entry)
{
p_entry->Paste(centre_x,p_cursor->GetClipboardY(),centre_z,p_cursor);
p_entry=p_entry->mpNext;
}
// and paste the rails
Mth::Vector clipboard_pos=GetClipboardWorldPos();
Mth::Vector cursor_pos=p_editor->GetCursorPos();
p_rail=mp_rails;
while (p_rail)
{
Obj::CEditedRail *p_new_rail=Obj::GetRailEditor()->NewRail();
p_new_rail->CopyRail(p_rail);
p_new_rail->RotateAndTranslate(clipboard_pos, cursor_pos, p_cursor->GetRotation()*90.0f);
p_new_rail->UpdateRailGeometry();
p_new_rail->UpdatePostGeometry();
p_rail=p_rail->mpNext;
}
return true;
}
int CClipboard::GetFloorHeight(CCursor *p_cursor)
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
int max_height=-1000;
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
int height=p_entry->GetFloorHeight(centre_x,centre_z,p_cursor);
if (height > max_height)
{
max_height=height;
}
p_entry=p_entry->mpNext;
}
if (max_height==-1000)
{
return 0;
}
return max_height;
}
int CClipboard::FindMaxFloorHeight(CCursor *p_cursor)
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
int max_floor_height=-1000;
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
int height=p_entry->GetFloorHeight(centre_x,centre_z,p_cursor);
if (height > max_floor_height)
{
max_floor_height=height;
}
p_entry=p_entry->mpNext;
}
if (max_floor_height != -1000)
{
return max_floor_height;
}
return 0;
}
void CClipboard::HighlightIntersectingMetas(CCursor *p_cursor, bool on)
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
p_entry->HighlightIntersectingMetas(centre_x,centre_z,p_cursor,on);
p_entry=p_entry->mpNext;
}
}
int CClipboard::GetWidth()
{
return m_max_x-m_min_x+1;
}
int CClipboard::GetLength()
{
return m_max_z-m_min_z+1;
}
void CClipboard::GetArea(CCursor *p_cursor, GridDims *p_area)
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
int idx=m_min_x-centre_x;
int idz=m_min_z-centre_z;
int dx=0;
int dz=0;
switch ( p_cursor->GetRotation() )
{
case Mth::ROT_0:
dx=idx;
dz=idz;
p_area->SetWHL(m_area.GetW(),1,m_area.GetL());
break;
case Mth::ROT_270:
dx=-idz;
dz=idx;
p_area->SetWHL(m_area.GetL(),1,m_area.GetW());
// Shift dx,dz so that they refer to the top-left point again
dx=dx-m_area.GetL()+1;
break;
case Mth::ROT_180:
dx=-idx;
dz=-idz;
p_area->SetWHL(m_area.GetW(),1,m_area.GetL());
// Shift dx,dz so that they refer to the top-left point again
dx=dx-m_area.GetW()+1;
dz=dz-m_area.GetL()+1;
break;
case Mth::ROT_90:
dx=idz;
dz=-idx;
p_area->SetWHL(m_area.GetL(),1,m_area.GetW());
// Shift dx,dz so that they refer to the top-left point again
dz=dz-m_area.GetW()+1;
break;
default:
break;
}
GridDims cursor_position=p_cursor->GetPosition();
p_area->SetXYZ(cursor_position.GetX()+dx,0,cursor_position.GetZ()+dz);
}
Mth::Vector CClipboard::GetClipboardWorldPos()
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
GridDims centre_cell;
centre_cell.SetXYZ(centre_x,m_area.GetY(),centre_z);
centre_cell.SetWHL(1,1,1);
Mth::Vector corner_pos;
Mth::Vector area_dims;
corner_pos=Ed::CParkManager::Instance()->GridCoordinatesToWorld(centre_cell,&area_dims);
// corner_pos is the world pos of the top left corner of the centre cell
Mth::Vector clipboard_pos=corner_pos;
clipboard_pos[X]+=area_dims[X]/2.0f;
clipboard_pos[Z]+=area_dims[Z]/2.0f;
// clipboard_pos is the world pos of the centre of the cell
return clipboard_pos;
}
// This gets called when the CCursor is in paste mode, and needs to display the clipboard contents at
// the cursor position.
void CClipboard::ShowMetas(Mth::Vector pos, float rot, int clipboardY)
{
// Get the map coords of the centre cell of the set of meta's
uint8 centre_x=(m_min_x+m_max_x)/2;
uint8 centre_z=(m_min_z+m_max_z)/2;
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
p_entry->ShowMeta(centre_x,clipboardY,centre_z,pos,rot);
p_entry=p_entry->mpNext;
}
Mth::Vector clipboard_pos=GetClipboardWorldPos();
Obj::CEditedRail *p_rail=mp_rails;
while (p_rail)
{
p_rail->UpdateRailGeometry(clipboard_pos,pos,rot);
p_rail->UpdatePostGeometry(clipboard_pos,pos,rot);
p_rail=p_rail->mpNext;
}
}
void CClipboard::DestroyMetas()
{
CClipboardEntry *p_entry=mp_entries;
while (p_entry)
{
p_entry->DestroyMeta();
p_entry=p_entry->mpNext;
}
Obj::CEditedRail *p_rail=mp_rails;
while (p_rail)
{
p_rail->DestroyPostGeometry();
p_rail->DestroyRailGeometry();
p_rail=p_rail->mpNext;
}
}
} // namespace Ed