#include "precompiled.h" #include "TerritoryManager.h" #include "graphics/Frustum.h" #include "graphics/Camera.h" #include "graphics/GameView.h" #include "graphics/Model.h" #include "graphics/Terrain.h" #include "graphics/Unit.h" #include "lib/allocators.h" #include "lib/ogl.h" #include "lib/timer.h" #include "maths/Bound.h" #include "maths/MathUtil.h" #include "ps/Game.h" #include "ps/Player.h" #include "ps/Profile.h" #include "simulation/Entity.h" #include "simulation/EntityManager.h" #include "simulation/EntityManager.h" #include "simulation/EntityTemplate.h" #include "simulation/LOSManager.h" CTerritoryManager::CTerritoryManager() { m_TerritoryMatrix = 0; m_DelayedRecalculate = false; } CTerritoryManager::~CTerritoryManager() { if(m_TerritoryMatrix) { matrix_free( (void**) m_TerritoryMatrix ); m_TerritoryMatrix = 0; } for( size_t i=0; iGetWorld()->GetTerrain(); m_TilesPerSide = terrain->GetVerticesPerSide() - 1; m_TerritoryMatrix = (CTerritory***) matrix_alloc( m_TilesPerSide, m_TilesPerSide, sizeof(CTerritory*) ); Recalculate(); } void CTerritoryManager::Recalculate() { // Delete any territories created last time we called Recalculate() for( size_t i=0; icentre ) m_Territories[i]->centre->m_associatedTerritory = 0; delete m_Territories[i]; } m_Territories.clear(); // First, find all the units that are territory centres std::vector centres; std::vector entities; g_EntityManager.GetExtant(entities); for( size_t i=0; ientf_get(ENTF_DESTROYED) && entities[i]->m_base->m_isTerritoryCentre ) centres.push_back(entities[i]); } int mapSize = m_TilesPerSide * CELL_SIZE; // If there aren't any centre objects, create one big Gaia territory which spans the whole map if( centres.empty() ) { std::vector boundary; boundary.push_back( CVector2D(0, 0) ); boundary.push_back( CVector2D(0, mapSize) ); boundary.push_back( CVector2D(mapSize, mapSize) ); boundary.push_back( CVector2D(mapSize, 0) ); CTerritory* ter = new CTerritory( g_Game->GetPlayer(0), HEntity(), boundary ); m_Territories.push_back(ter); for( uint x=0; x boundary; CalculateBoundary( centres, i, boundary ); CTerritory* ter = new CTerritory( centres[i]->GetPlayer(), centres[i]->me, boundary ); centres[i]->m_associatedTerritory = ter; m_Territories.push_back(ter); } // For each tile, match it to the closest centre object to it. // TODO: Optimize this, for example by intersecting scanlines with the Voronoi polygons. for( uint x=0; xm_position.X, centres[i]->m_position.Z ); float squareDist = (centreLoc - tileLoc).length2(); if( squareDist < bestSquareDist ) { bestSquareDist = squareDist; m_TerritoryMatrix[x][z] = m_Territories[i]; } } } } } } void CTerritoryManager::DelayedRecalculate() { // This is useful particularly for Atlas, which wants to recalculate // the boundaries as you move an object around but which doesn't want // to waste time recalculating multiple times per frame m_DelayedRecalculate = true; } CTerritory* CTerritoryManager::GetTerritory(int x, int z) { debug_assert( (uint) x < m_TilesPerSide && (uint) z < m_TilesPerSide ); return m_TerritoryMatrix[x][z]; } CTerritory* CTerritoryManager::GetTerritory(float x, float z) { int ix, iz; CTerrain::CalcFromPosition(x, z, ix, iz); return GetTerritory(ix, iz); } // Calculate the boundary points of a given territory into the given vector void CTerritoryManager::CalculateBoundary( std::vector& centres, size_t myIndex, std::vector& boundary ) { // Start with a boundary equal to the whole map int mapSize = m_TilesPerSide * CELL_SIZE; boundary.push_back( CVector2D(0, 0) ); boundary.push_back( CVector2D(0, mapSize) ); boundary.push_back( CVector2D(mapSize, mapSize) ); boundary.push_back( CVector2D(mapSize, 0) ); // Clip this polygon against the perpendicular bisector between this centre and each other territory centre CVector2D myPos( centres[myIndex]->m_position.X, centres[myIndex]->m_position.Z ); for( size_t i=0; im_position.X, centres[i]->m_position.Z ); CVector2D midpoint = (myPos + itsPos) / 2.0f; CVector2D normal = itsPos - myPos; // Clip our polygon to the negative side of the half-space with normal "normal" // containing point "midpoint", i.e. the side of the perpendicular bisector // between myPos and itsPos that contains myPos. We do this by tracing around // the polygon looking at each vertex to decide which ones to add as follows: // - If a vertex is inside the half-space, take it. // - If a vertex is inside but the next one is outside, also take the // intersection of that edge with the perpendicular bisector. // - If a vertex is outside but the next one is inside, take the // intersection of that edge with the perpendicular bisector. std::vector newBoundary; for( size_t j=0; jGetWorld()->GetLOSManager(); CFrustum frustum = g_Game->GetView()->GetCamera()->GetFrustum(); std::vector::iterator terr=m_Territories.begin(); for ( ; terr != m_Territories.end(); ++terr ) { float r, g, b; if ( (*terr)->owner->GetPlayerID() == 0 ) { // Use a dark gray for Gaia territories since white looks a bit weird //glColor3f( 0.65f, 0.65f, 0.65f ); r = g = b = 0.65f; } else { // Use the player's colour const SPlayerColour& col = (*terr)->owner->GetColour(); //glColor3f(col.r, col.g, col.b); r = col.r; g = col.g; b = col.b; } for ( size_t edge=0; edge < (*terr)->boundary.size(); edge++ ) { const std::vector& coords = (*terr)->GetEdgeCoords(edge); CVector3D start = coords[0]; CVector3D end = coords[coords.size() - 1]; if ( !frustum.DoesSegmentIntersect(start, end) ) continue; glBegin( GL_LINE_STRIP ); for( size_t i=0; iGetStatus(coords[i].X, coords[i].Z, g_Game->GetLocalPlayer()); if( los & LOS_VISIBLE ) losScale = 1.0f; else if( los & LOS_EXPLORED ) losScale = 0.7f; glColor3f( r*losScale, g*losScale, b*losScale ); glVertex3f( coords[i].X, coords[i].Y, coords[i].Z ); } glEnd(); } } glEnable(GL_TEXTURE_2D); glDisable(GL_BLEND); glDisable(GL_LINE_SMOOTH); glLineWidth(1.0f); glColor4f(1,1,1,1); } const std::vector& CTerritory::GetEdgeCoords(size_t edge) { if ( edgeCoords.size() == 0 ) { // Edge coords have not been calculated - calculate them now edgeCoords.resize( boundary.size() ); const CTerrain* pTerrain = g_Game->GetWorld()->GetTerrain(); // Tweak the boundary to shift all edges "inwards" by 0.3 units towards the territory's centre, // so that boundaries for adjacent territories don't overlap std::vector tweakedBoundary = boundary; for ( size_t i=0; i& coords = edgeCoords[e]; CVector2D start = tweakedBoundary[e]; CVector2D end = tweakedBoundary[(e+1) % boundary.size()]; float iterf = (end - start).Length() / TERRITORY_PRECISION_STEP; for ( float i=0; i < iterf; i += TERRITORY_PRECISION_STEP ) { CVector2D pos = Interpolate( start, end, i/iterf ); coords.push_back( CVector3D( pos.x, pTerrain->GetExactGroundLevel(pos)+0.25f, pos.y ) ); } coords.push_back( CVector3D( end.x, pTerrain->GetExactGroundLevel(end)+0.25f, end.y ) ); } } return edgeCoords[edge]; } void CTerritory::ClearEdgeCache() { edgeCoords.clear(); edgeCoords.resize( boundary.size() ); }