1
0
forked from 0ad/0ad

# Add fog-of-war.

Move Vision component to C++, for more efficient FoW processing.
Disable FoW on demo maps.

This was SVN commit r8160.
This commit is contained in:
Ykkrosh 2010-09-23 12:13:13 +00:00
parent 8886841860
commit 924d1219a7
36 changed files with 725 additions and 173 deletions

View File

@ -77,7 +77,7 @@
/>
<!-- Dev/cheat commands -->
<object name="devCommands" size="100%-155 50 100%-16 162" z="200" type="image" sprite="devCommandsBackground"
<object name="devCommands" size="100%-155 50 100%-16 178" z="200" type="image" sprite="devCommandsBackground"
hidden="true" hotkey="session.devcommands.toggle">
<action on="Press">
this.hidden = !this.hidden;
@ -113,6 +113,11 @@
<object size="100%-16 96 100% 112" type="checkbox" style="wheatCrossBox" checked="true">
<action on="Press">gameView.constrainCamera = this.checked;</action>
</object>
<object size="0 112 100%-18 128" type="text" style="devCommandsText">Reveal map</object>
<object size="100%-16 112 100% 128" type="checkbox" style="wheatCrossBox">
<action on="Press">Engine.SetRevealMap(this.checked);</action>
</object>
</object>
<!-- ================================ ================================ -->

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,22 +0,0 @@
function Vision() {}
Vision.prototype.Schema =
"<optional>" +
"<element name='Range'>" +
"<data type='positiveInteger'/>" +
"</element>" +
"</optional>" +
"<element name='RetainInFog'>" +
"<data type='boolean'/>" +
"</element>";
/*
* TODO: this all needs to be designed and implemented
*/
Vision.prototype.GetRange = function()
{
return +this.template.Range;
};
Engine.RegisterComponentType(IID_Vision, "Vision", Vision);

View File

@ -1 +0,0 @@
Engine.RegisterInterface("Vision");

View File

@ -12,6 +12,13 @@ function LoadMapSettings(settings)
cmpUnitAI.SetStance(settings.DefaultStance);
}
}
if (settings.RevealMap)
{
var cmpRangeManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_RangeManager);
if (cmpRangeManager)
cmpRangeManager.SetLosRevealAll(true);
}
}
Engine.RegisterGlobal("LoadMapSettings", LoadMapSettings);

View File

@ -5,6 +5,7 @@
<GenericName>Gaia</GenericName>
</Identity>
<Vision>
<Range>0</Range>
<RetainInFog>true</RetainInFog>
</Vision>
</Entity>

View File

@ -39,6 +39,7 @@
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpMinimap.h"
#include "simulation2/components/ICmpRangeManager.h"
bool g_GameRestarted = false;
@ -246,9 +247,7 @@ void CMiniMap::Draw()
if(m_TerrainDirty)
RebuildTerrainTexture();
// CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
// if(losMgr->m_LOSSetting != LOS_SETTING_ALL_VISIBLE)
// RebuildLOSTexture();
RebuildLOSTexture();
}
const float texCoordMax = ((float)m_MapSize - 1) / ((float)m_TextureSize);
@ -366,16 +365,24 @@ void CMiniMap::Draw()
std::vector<MinimapUnitVertex> vertexArray;
vertexArray.reserve(ents.size());
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
debug_assert(!cmpRangeManager.null());
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{
MinimapUnitVertex v;
ICmpMinimap* cmpMinimap = static_cast<ICmpMinimap*>(it->second);
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, v.x, v.y))
entity_pos_t posX, posZ;
if (cmpMinimap->GetRenderData(v.r, v.g, v.b, posX, posZ))
{
v.a = 255;
v.x = x + v.x*sx;
v.y = y - v.y*sy;
vertexArray.push_back(v);
ICmpRangeManager::ELosVisibility vis = cmpRangeManager->GetLosVisibility(it->first, g_Game->GetPlayerID());
if (vis != ICmpRangeManager::VIS_HIDDEN)
{
v.a = 255;
v.x = x + posX.ToFloat()*sx;
v.y = y - posZ.ToFloat()*sy;
vertexArray.push_back(v);
}
}
}
@ -421,7 +428,7 @@ void CMiniMap::CreateTextures()
glGenTextures(1, &m_LOSTexture);
g_Renderer.BindTexture(0, m_LOSTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA8, m_TextureSize, m_TextureSize, 0, GL_ALPHA, GL_UNSIGNED_BYTE, 0);
m_LOSData = new u8[(m_MapSize - 1) * (m_MapSize - 1)];
m_LOSData = new u8[m_MapSize * m_MapSize];
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP);
@ -493,41 +500,39 @@ void CMiniMap::RebuildTerrainTexture()
void CMiniMap::RebuildLOSTexture()
{
PROFILE_START("rebuild minimap: los");
PROFILE("rebuild minimap: los");
ssize_t x = 0;
ssize_t y = 0;
ssize_t w = m_MapSize - 1;
ssize_t h = m_MapSize - 1;
ssize_t w = m_MapSize;
ssize_t h = m_MapSize;
memset(m_LOSData, 0, w*h);
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID()))
{
memset(m_LOSData, 0, w*h);
}
else
{
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
// TODO: ought to do something like:
/*
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
CPlayer* player = g_Game->GetLocalPlayer();
u8 *dataPtr = m_LOSData;
for(ssize_t j = 0; j < h; j++)
for (ssize_t j = 0; j < h; j++)
{
u8 *dataPtr = m_LOSData + ((y + j) * (m_MapSize - 1)) + x;
for(ssize_t i = 0; i < w; i++)
for (ssize_t i = 0; i < w; i++)
{
ELOSStatus status = losMgr->GetStatus(i, j, player);
if(status == LOS_UNEXPLORED)
*dataPtr++ = 0xff;
else if(status == LOS_EXPLORED)
*dataPtr++ = (u8) (0xff * 0.3f);
else
if (los.IsVisible(i, j))
*dataPtr++ = 0;
else if (los.IsExplored(i, j))
*dataPtr++ = 76;
else
*dataPtr++ = 255;
}
}
*/
}
// Upload the texture
g_Renderer.BindTexture(0, m_LOSTexture);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize - 1, m_MapSize - 1, GL_ALPHA, GL_UNSIGNED_BYTE, m_LOSData);
PROFILE_END("rebuild minimap: los");
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_MapSize, m_MapSize, GL_ALPHA, GL_UNSIGNED_BYTE, m_LOSData);
}
void CMiniMap::Destroy()

View File

@ -36,6 +36,7 @@
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpCommandQueue.h"
#include "simulation2/components/ICmpGuiInterface.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTemplateManager.h"
#include "simulation2/helpers/Selection.h"
@ -119,7 +120,7 @@ void PostNetworkCommand(void* cbdata, CScriptVal cmd)
std::vector<entity_id_t> PickEntitiesAtPoint(void* UNUSED(cbdata), int x, int y)
{
return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y);
return EntitySelection::PickEntitiesAtPoint(*g_Game->GetSimulation2(), *g_Game->GetView()->GetCamera(), x, y, g_Game->GetPlayerID());
}
std::vector<entity_id_t> PickFriendlyEntitiesInRect(void* UNUSED(cbdata), int x0, int y0, int x1, int y1, int player)
@ -304,6 +305,18 @@ CScriptVal LoadMapData(void* cbdata, std::wstring name)
return reader.GetScriptData(guiManager->GetScriptInterface()).get();
}
void SetRevealMap(void* UNUSED(cbdata), bool enabled)
{
if (!g_Game)
return;
CSimulation2* sim = g_Game->GetSimulation2();
debug_assert(sim);
CmpPtr<ICmpRangeManager> cmpRangeManager(*sim, SYSTEM_ENTITY);
if (!cmpRangeManager.null())
cmpRangeManager->SetLosRevealAll(enabled);
}
} // namespace
void GuiScriptingInit(ScriptInterface& scriptInterface)
@ -343,4 +356,5 @@ void GuiScriptingInit(ScriptInterface& scriptInterface)
scriptInterface.RegisterFunction<void, &RestartInAtlas>("RestartInAtlas");
scriptInterface.RegisterFunction<bool, &AtlasIsAvailable>("AtlasIsAvailable");
scriptInterface.RegisterFunction<CScriptVal, std::wstring, &LoadMapData>("LoadMapData");
scriptInterface.RegisterFunction<void, bool, &SetRevealMap>("SetRevealMap");
}

View File

@ -176,6 +176,11 @@ public:
return value >> fract_bits;
}
int ToInt_RoundToNearest() const // (ties to infinity)
{
return (value + fract_pow2/2) >> fract_bits;
}
/// Returns the shortest string such that FromString will parse to the correct value.
CStr8 ToString() const;
@ -280,6 +285,14 @@ public:
return CFixed((T)t);
}
/**
* Multiply the value by itself. Might overflow.
*/
CFixed Square() const
{
return (*this).Multiply(*this);
}
/**
* Compute this*m/d. Must not have d == 0. Won't overflow if the result can be represented as a CFixed.
*/

View File

@ -189,6 +189,22 @@ public:
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToNegInfinity(), -3);
}
void test_RoundToNearest()
{
TS_ASSERT_EQUALS(fixed::FromFloat(10.f).ToInt_RoundToNearest(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.1f).ToInt_RoundToNearest(), 10);
TS_ASSERT_EQUALS(fixed::FromFloat(10.5f).ToInt_RoundToNearest(), 11);
TS_ASSERT_EQUALS(fixed::FromFloat(10.99f).ToInt_RoundToNearest(), 11);
TS_ASSERT_EQUALS(fixed::FromFloat(0.1f).ToInt_RoundToNearest(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(0.0f).ToInt_RoundToNearest(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.1f).ToInt_RoundToNearest(), 0);
TS_ASSERT_EQUALS(fixed::FromFloat(-0.99f).ToInt_RoundToNearest(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-1.0f).ToInt_RoundToNearest(), -1);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.0f).ToInt_RoundToNearest(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.5f).ToInt_RoundToNearest(), -2);
TS_ASSERT_EQUALS(fixed::FromFloat(-2.99f).ToInt_RoundToNearest(), -3);
}
// TODO: test all the arithmetic operators
void test_Mod()

View File

@ -33,6 +33,7 @@
#include "graphics/Patch.h"
#include "graphics/Terrain.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpRangeManager.h"
const ssize_t BlendOffsets[8][2] = {
{ 0, -1 },
@ -431,47 +432,11 @@ void CPatchRData::Update()
ssize_t pz=m_Patch->m_Z;
CTerrain* terrain=m_Patch->m_Parent;
ssize_t mapSize=terrain->GetVerticesPerSide();
ssize_t vsize=PATCH_SIZE+1;
SColor4ub baseColour = terrain->GetBaseColour();
/*
if (g_Game) // XXX: need to implement this for new sim system
{
CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
// this is very similar to BuildVertices(), but just for color
for (ssize_t j=0;j<vsize;j++) {
for (ssize_t i=0;i<vsize;i++) {
ssize_t ix=px*PATCH_SIZE+i;
ssize_t iz=pz*PATCH_SIZE+j;
ssize_t v=(j*vsize)+i;
const ssize_t DX[] = {1,1,0,0};
const ssize_t DZ[] = {0,1,1,0};
SColor4ub losMod = baseColour;
for(size_t k=0; k<4; k++)
{
ssize_t tx = ix - DX[k];
ssize_t tz = iz - DZ[k];
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
{
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
if(s==LOS_EXPLORED && losMod.R > 178)
losMod = SColor4ub(178, 178, 178, 255);
else if(s==LOS_UNEXPLORED && losMod.R > 0)
losMod = SColor4ub(0, 0, 0, 255);
}
}
m_Vertices[v].m_LOSColor = losMod;
}
}
}
else
*/
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (cmpRangeManager.null() || cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID()))
{
for (ssize_t j = 0; j < vsize; ++j)
{
@ -481,7 +446,31 @@ void CPatchRData::Update()
m_Vertices[v].m_LOSColor = baseColour;
}
}
}
else
{
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
// this is very similar to BuildVertices(), but just for color
for (ssize_t j = 0; j < vsize; j++)
{
for (ssize_t i = 0; i < vsize; i++)
{
ssize_t ix = px * PATCH_SIZE + i;
ssize_t iz = pz * PATCH_SIZE + j;
ssize_t v = (j * vsize) + i;
SColor4ub losMod;
if (los.IsVisible(ix, iz))
losMod = baseColour;
else if (los.IsExplored(ix, iz))
losMod = SColor4ub(178, 178, 178, 255);
else
losMod = SColor4ub(0, 0, 0, 255);
m_Vertices[v].m_LOSColor = losMod;
}
}
}
// upload base vertices into their vertex buffer

View File

@ -1,4 +1,4 @@
/* Copyright (C) 2009 Wildfire Games.
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
@ -38,6 +38,7 @@
#include "ps/World.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "renderer/PatchRData.h"
#include "renderer/Renderer.h"
@ -434,9 +435,11 @@ void TerrainRenderer::RenderWater()
}
}
CTerrain* terrain = g_Game->GetWorld()->GetTerrain();
// int mapSize = terrain->GetVerticesPerSide();
// CLOSManager* losMgr = g_Game->GetWorld()->GetLOSManager();
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
debug_assert(!cmpRangeManager.null());
ICmpRangeManager::CLosQuerier los = cmpRangeManager->GetLosQuerier(g_Game->GetPlayerID());
bool losRevealAll = cmpRangeManager->GetLosRevealAll(g_Game->GetPlayerID());
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
@ -566,50 +569,35 @@ void TerrainRenderer::RenderWater()
// is any corner of the tile below the water height? if not, no point rendering it
bool shouldRender = false;
for(int j=0; j<4; j++)
for (int j = 0; j < 4; j++)
{
float terrainHeight = terrain->GetVertexGroundLevel(x + DX[j], z + DZ[j]);
if( terrainHeight < WaterMgr->m_WaterHeight )
if (terrainHeight < WaterMgr->m_WaterHeight)
{
shouldRender = true;
break;
}
}
if(!shouldRender)
{
if (!shouldRender)
continue;
}
for (int j=0; j<4; j++)
{
int ix = x + DX[j];
int iz = z + DZ[j];
ssize_t ix = x + DX[j];
ssize_t iz = z + DZ[j];
float vertX = ix * CELL_SIZE;
float vertZ = iz * CELL_SIZE;
float terrainHeight = terrain->GetVertexGroundLevel(ix, iz);
float losMod = 1.0f;
/*
if (false) // XXX: need to implement this for new sim system
{
for(size_t k=0; k<4; k++)
{
ssize_t tx = ix - DX[k];
ssize_t tz = iz - DZ[k];
if(tx >= 0 && tz >= 0 && tx <= mapSize-2 && tz <= mapSize-2)
{
ELOSStatus s = losMgr->GetStatus(tx, tz, g_Game->GetLocalPlayer());
if(s == LOS_EXPLORED && losMod > 0.7f)
losMod = 0.7f;
else if(s==LOS_UNEXPLORED && losMod > 0.0f)
losMod = 0.0f;
}
}
}
*/
float losMod;
if (losRevealAll || los.IsVisible(ix, iz))
losMod = 1.0f;
else if (los.IsExplored(ix, iz))
losMod = 0.7f;
else
losMod = 0.0f;
if (fancy)
{
@ -672,5 +660,3 @@ void TerrainRenderer::RenderWater()
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
}

View File

@ -115,6 +115,9 @@ public:
fixed turnLength;
};
/**
* Prepare for rendering a new frame (set up model positions etc).
*/
class CMessageInterpolate : public CMessage
{
public:
@ -129,6 +132,10 @@ public:
float offset; // range [0, 1] (inclusive); fractional time of current frame between previous/next simulation turns
};
/**
* Add renderable objects to the scene collector.
* Called after CMessageInterpolate.
*/
class CMessageRenderSubmit : public CMessage
{
public:

View File

@ -118,6 +118,9 @@ COMPONENT(Terrain)
INTERFACE(UnitMotion)
COMPONENT(UnitMotion) // must be after Obstruction
INTERFACE(Vision)
COMPONENT(Vision)
INTERFACE(Visual)
COMPONENT(VisualActor)

View File

@ -160,7 +160,7 @@ public:
}
}
virtual bool GetRenderData(u8& r, u8& g, u8& b, float& x, float& z)
virtual bool GetRenderData(u8& r, u8& g, u8& b, entity_pos_t& x, entity_pos_t& z)
{
if (!m_Active)
return false;
@ -168,8 +168,8 @@ public:
r = m_R;
g = m_G;
b = m_B;
x = m_X.ToFloat();
z = m_Z.ToFloat();
x = m_X;
z = m_Z;
return true;
}
};

View File

@ -21,6 +21,7 @@
#include "ICmpRangeManager.h"
#include "ICmpPosition.h"
#include "ICmpVision.h"
#include "simulation2/MessageTypes.h"
#include "simulation2/helpers/Render.h"
#include "simulation2/helpers/Spatial.h"
@ -49,11 +50,11 @@ struct Query
/**
* Convert an owner ID (-1 = unowned, 0 = gaia, 1..30 = players)
* into a 31-bit mask for quick set-membership tests.
* into a 32-bit mask for quick set-membership tests.
*/
static u32 CalcOwnerMask(i32 owner)
{
if (owner >= -1 && owner < 30)
if (owner >= -1 && owner < 31)
return 1 << (1+owner);
else
return 0; // owner was invalid
@ -64,13 +65,15 @@ static u32 CalcOwnerMask(i32 owner)
*/
struct EntityData
{
EntityData() : ownerMask(CalcOwnerMask(-1)), inWorld(0) { }
EntityData() : retainInFog(0), owner(-1), inWorld(0) { }
entity_pos_t x, z;
u32 ownerMask : 31;
u32 inWorld : 1;
entity_pos_t visionRange;
u8 retainInFog; // boolean
i8 owner;
u8 inWorld; // boolean
};
cassert(sizeof(EntityData) == 12);
cassert(sizeof(EntityData) == 16);
/**
* Functor for sorting entities by distance from a source point.
@ -101,12 +104,13 @@ private:
};
/**
* Basic range manager implementation.
* Range manager implementation.
* Maintains a list of all entities (and their positions and owners), which is used for
* queries.
*
* TODO: Ideally this would use a quadtree or something for more efficient spatial queries,
* since it's about O(n^2) in the total number of entities on the map.
* LOS implementation is based on the model described in GPG2.
* (TODO: would be nice to make it cleverer, so e.g. mountains and walls
* can block vision)
*/
class CCmpRangeManager : public ICmpRangeManager
{
@ -131,10 +135,27 @@ public:
SpatialSubdivision<entity_id_t> m_Subdivision;
// Range query state:
tag_t m_QueryNext; // next allocated id
std::map<tag_t, Query> m_Queries;
std::map<entity_id_t, EntityData> m_EntityData;
// LOS state:
bool m_LosRevealAll;
ssize_t m_TerrainVerticesPerSide;
// Counts of units seeing vertex, per vertex, per player (starting with player 0).
// Use u16 to avoid overflows when we have very large (but not infeasibly large) numbers
// of units in a very small area.
// (Note we use vertexes, not tiles, to better match the renderer.)
// Lazily constructed when it's needed, to save memory in smaller games.
std::vector<std::vector<u16> > m_LosPlayerCounts;
// 2-bit ELosState per player, starting with player 1 (not 0!) up to player MAX_LOS_PLAYER_ID (inclusive)
std::vector<u32> m_LosState;
static const int MAX_LOS_PLAYER_ID = 16;
static std::string GetSchema()
{
return "<a:component type='system'/><empty/>";
@ -150,6 +171,9 @@ public:
// Initialise with bogus values (these will get replaced when
// SetBounds is called)
ResetSubdivisions(entity_pos_t::FromInt(1), entity_pos_t::FromInt(1));
m_LosRevealAll = false;
m_TerrainVerticesPerSide = 0;
}
virtual void Deinit(const CSimContext& UNUSED(context))
@ -189,6 +213,14 @@ public:
// use the default-constructed EntityData here
EntityData entdata;
// Store the LOS data, if any
CmpPtr<ICmpVision> cmpVision(GetSimContext(), ent);
if (!cmpVision.null())
{
entdata.visionRange = cmpVision->GetRange();
entdata.retainInFog = (cmpVision->GetRetainInFog() ? 1 : 0);
}
// Remember this entity
m_EntityData.insert(std::make_pair(ent, entdata));
@ -208,9 +240,18 @@ public:
if (msgData.inWorld)
{
if (it->second.inWorld)
m_Subdivision.Move(ent, CFixedVector2D(it->second.x, it->second.z), CFixedVector2D(msgData.x, msgData.z));
{
CFixedVector2D from(it->second.x, it->second.z);
CFixedVector2D to(msgData.x, msgData.z);
m_Subdivision.Move(ent, from, to);
LosMove(it->second.owner, it->second.visionRange, from, to);
}
else
m_Subdivision.Add(ent, CFixedVector2D(msgData.x, msgData.z));
{
CFixedVector2D to(msgData.x, msgData.z);
m_Subdivision.Add(ent, to);
LosAdd(it->second.owner, it->second.visionRange, to);
}
it->second.inWorld = 1;
it->second.x = msgData.x;
@ -219,7 +260,11 @@ public:
else
{
if (it->second.inWorld)
m_Subdivision.Remove(ent, CFixedVector2D(it->second.x, it->second.z));
{
CFixedVector2D from(it->second.x, it->second.z);
m_Subdivision.Remove(ent, from);
LosRemove(it->second.owner, it->second.visionRange, from);
}
it->second.inWorld = 0;
it->second.x = entity_pos_t::Zero();
@ -239,7 +284,14 @@ public:
if (it == m_EntityData.end())
break;
it->second.ownerMask = CalcOwnerMask(msgData.to);
if (it->second.inWorld)
{
CFixedVector2D pos(it->second.x, it->second.z);
LosRemove(it->second.owner, it->second.visionRange, pos);
LosAdd(msgData.to, it->second.visionRange, pos);
}
it->second.owner = msgData.to;
break;
}
@ -276,10 +328,19 @@ public:
}
}
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1)
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices)
{
debug_assert(x0.IsZero() && z0.IsZero()); // don't bother implementing non-zero offsets yet
ResetSubdivisions(x1, z1);
m_TerrainVerticesPerSide = vertices;
m_LosPlayerCounts.clear();
m_LosPlayerCounts.resize(MAX_LOS_PLAYER_ID+1);
m_LosState.clear();
m_LosState.resize(vertices*vertices);
for (std::map<u32, EntityData>::iterator it = m_EntityData.begin(); it != m_EntityData.end(); ++it)
LosAdd(it->second.owner, it->second.visionRange, CFixedVector2D(it->second.x, it->second.z));
}
void ResetSubdivisions(entity_pos_t x1, entity_pos_t z1)
@ -482,7 +543,7 @@ private:
debug_assert(it != m_EntityData.end());
// Quick filter to ignore entities with the wrong owner
if (!(it->second.ownerMask & q.ownersMask))
if (!(CalcOwnerMask(it->second.owner) & q.ownersMask))
continue;
// Restrict based on precise location
@ -573,6 +634,158 @@ private:
for (size_t i = 0; i < m_DebugOverlayLines.size(); ++i)
collector.Submit(&m_DebugOverlayLines[i]);
}
// LOS implementation:
virtual CLosQuerier GetLosQuerier(int player)
{
return CLosQuerier(player, m_LosState, m_TerrainVerticesPerSide);
}
virtual ELosVisibility GetLosVisibility(entity_id_t ent, int player)
{
// (We can't use m_EntityData since this needs to handle LOCAL entities too)
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), ent);
if (cmpPosition.null() || !cmpPosition->IsInWorld())
return VIS_HIDDEN;
if (m_LosRevealAll)
return VIS_VISIBLE;
CFixedVector2D pos = cmpPosition->GetPosition2D();
CLosQuerier los(player, m_LosState, m_TerrainVerticesPerSide);
int i = (pos.X / (int)CELL_SIZE).ToInt_RoundToNearest();
int j = (pos.Y / (int)CELL_SIZE).ToInt_RoundToNearest();
if (los.IsVisible(i, j))
return VIS_VISIBLE;
if (los.IsExplored(i, j))
{
CmpPtr<ICmpVision> cmpVision(GetSimContext(), ent);
if (!cmpVision.null() && cmpVision->GetRetainInFog())
return VIS_FOGGED;
}
return VIS_HIDDEN;
}
virtual void SetLosRevealAll(bool enabled)
{
// Eventually we might want this to be a per-player flag (which is why
// GetLosRevealAll takes a player argument), but currently it's just a
// global setting since I can't quite work out where per-player would be useful
m_LosRevealAll = enabled;
}
virtual bool GetLosRevealAll(int UNUSED(player))
{
return m_LosRevealAll;
}
/**
* Update the LOS state of tiles within a given horizontal strip (i0,j) to (i1,j) (inclusive).
* amount is +1 or -1.
*/
inline void LosUpdateStripHelper(u8 owner, ssize_t i0, ssize_t i1, ssize_t j, int amount, std::vector<u16>& counts)
{
for (ssize_t i = i0; i <= i1; ++i)
{
ssize_t idx = j*m_TerrainVerticesPerSide + i;
// Increasing from zero to non-zero - move from unexplored/explored to visible+explored
if (counts[idx] == 0 && amount > 0)
{
m_LosState[idx] |= ((LOS_VISIBLE | LOS_EXPLORED) << (2*(owner-1)));
}
counts[idx] += amount;
// Decreasing from non-zero to zero - move from visible+explored to explored
if (counts[idx] == 0 && amount < 0)
{
m_LosState[idx] &= ~(LOS_VISIBLE << (2*(owner-1)));
}
}
}
/**
* Update the LOS state of tiles within a given circular range.
* Assumes owner is in the valid range.
*/
inline void LosUpdateHelper(u8 owner, entity_pos_t visionRange, CFixedVector2D pos, int amount)
{
if (m_TerrainVerticesPerSide == 0) // do nothing if not initialised yet
return;
PROFILE("LosUpdateHelper");
std::vector<u16>& counts = m_LosPlayerCounts.at(owner);
// Lazy initialisation of counts:
if (counts.empty())
counts.resize(m_TerrainVerticesPerSide*m_TerrainVerticesPerSide);
// Compute the circular region as a series of strips.
// Rather than quantise pos to vertexes, we do more precise sub-tile computations
// to get smoother behaviour as a unit moves rather than jumping a whole tile
// at once.
// Compute top/bottom coordinates, and clamp to exclude the 1-tile border around the map
// (so that we never render the sharp edge of the map)
ssize_t j0 = ((pos.Y - visionRange)/(int)CELL_SIZE).ToInt_RoundToInfinity();
ssize_t j1 = ((pos.Y + visionRange)/(int)CELL_SIZE).ToInt_RoundToNegInfinity();
ssize_t j0clamp = std::max(j0, (ssize_t)1);
ssize_t j1clamp = std::min(j1, m_TerrainVerticesPerSide-2);
entity_pos_t xscale = pos.X / (int)CELL_SIZE;
entity_pos_t yscale = pos.Y / (int)CELL_SIZE;
entity_pos_t rsquared = (visionRange / (int)CELL_SIZE).Square();
for (ssize_t j = j0clamp; j <= j1clamp; ++j)
{
// Compute values such that (i - x)^2 + (j - y)^2 <= r^2
// (TODO: is this sqrt slow? can we optimise it?)
entity_pos_t di = (rsquared - (entity_pos_t::FromInt(j) - yscale).Square()).Sqrt();
ssize_t i0 = (xscale - di).ToInt_RoundToInfinity();
ssize_t i1 = (xscale + di).ToInt_RoundToNegInfinity();
ssize_t i0clamp = std::max(i0, (ssize_t)1);
ssize_t i1clamp = std::min(i1, m_TerrainVerticesPerSide-2);
LosUpdateStripHelper(owner, i0clamp, i1clamp, j, amount, counts);
}
}
void LosAdd(i8 owner, entity_pos_t visionRange, CFixedVector2D pos)
{
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
return;
LosUpdateHelper(owner, visionRange, pos, 1);
}
void LosRemove(i8 owner, entity_pos_t visionRange, CFixedVector2D pos)
{
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
return;
LosUpdateHelper(owner, visionRange, pos, -1);
}
void LosMove(i8 owner, entity_pos_t visionRange, CFixedVector2D from, CFixedVector2D to)
{
if (visionRange.IsZero() || owner <= 0 || owner > MAX_LOS_PLAYER_ID)
return;
// TODO: we could optimise this by only modifying tiles that changed
LosRemove(owner, visionRange, from);
LosAdd(owner, visionRange, to);
}
};
REGISTER_COMPONENT_TYPE(RangeManager)

View File

@ -433,9 +433,15 @@ void CCmpTemplateManager::CopyPreviewSubset(CParamNode& out, const CParamNode& i
if (out.GetChild("Entity").GetChild("Obstruction").IsOk())
CParamNode::LoadXMLString(out, "<Entity><Obstruction><Inactive/></Obstruction></Entity>");
// Corpses should include decay components and un-inactivate them
if (!corpse)
{
// Previews should always be visible in fog-of-war
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision></Entity>");
}
if (corpse)
{
// Corpses should include decay components and un-inactivate them
if (out.GetChild("Entity").GetChild("Decay").IsOk())
CParamNode::LoadXMLString(out, "<Entity><Decay><Inactive disable=''/></Decay></Entity>");
}
@ -459,6 +465,7 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
permittedComponentTypes.insert("Decay");
permittedComponentTypes.insert("Cost");
permittedComponentTypes.insert("Sound");
permittedComponentTypes.insert("Vision");
CParamNode::LoadXMLString(out, "<Entity/>");
out.CopyFilteredChildrenOfChild(in, "Entity", permittedComponentTypes);
@ -478,4 +485,9 @@ void CCmpTemplateManager::CopyFoundationSubset(CParamNode& out, const CParamNode
// Don't provide population bonuses yet (but still do take up population cost)
if (out.GetChild("Entity").GetChild("Cost").IsOk())
CParamNode::LoadXMLString(out, "<Entity><Cost><PopulationBonus>0</PopulationBonus></Cost></Entity>");
// Foundations should be visible themselves in fog-of-war if their base template is,
// but shouldn't have any vision range
if (out.GetChild("Entity").GetChild("Vision").IsOk())
CParamNode::LoadXMLString(out, "<Entity><Vision><Range>0</Range></Vision></Entity>");
}

View File

@ -94,7 +94,8 @@ public:
{
cmpRangeManager->SetBounds(entity_pos_t::Zero(), entity_pos_t::Zero(),
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE),
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE));
entity_pos_t::FromInt(m_Terrain->GetTilesPerSide()*CELL_SIZE),
m_Terrain->GetVerticesPerSide());
}
}

View File

@ -0,0 +1,76 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "simulation2/system/Component.h"
#include "ICmpVision.h"
class CCmpVision: public ICmpVision
{
public:
static void ClassInit(CComponentManager& componentManager)
{
}
DEFAULT_COMPONENT_ALLOCATOR(Vision)
entity_pos_t m_Range;
bool m_RetainInFog;
static std::string GetSchema()
{
return
"<element name='Range'>"
"<data type='nonNegativeInteger'/>"
"</element>"
"<element name='RetainInFog'>"
"<data type='boolean'/>"
"</element>";
}
virtual void Init(const CSimContext& UNUSED(context), const CParamNode& paramNode)
{
m_Range = paramNode.GetChild("Range").ToFixed();
m_RetainInFog = paramNode.GetChild("RetainInFog").ToBool();
}
virtual void Deinit(const CSimContext& UNUSED(context))
{
}
virtual void Serialize(ISerializer& UNUSED(serialize))
{
}
virtual void Deserialize(const CSimContext& context, const CParamNode& paramNode, IDeserializer& UNUSED(deserialize))
{
Init(context, paramNode);
}
virtual entity_pos_t GetRange()
{
return m_Range;
}
virtual bool GetRetainInFog()
{
return m_RetainInFog;
}
};
REGISTER_COMPONENT_TYPE(Vision)

View File

@ -21,6 +21,8 @@
#include "ICmpVisual.h"
#include "ICmpPosition.h"
#include "ICmpRangeManager.h"
#include "ICmpVision.h"
#include "simulation2/MessageTypes.h"
#include "graphics/Frustum.h"
@ -52,7 +54,7 @@ public:
std::wstring m_ActorName;
CUnit* m_Unit;
bool m_Hidden; // only valid between Interpolate and RenderSubmit
ICmpRangeManager::ELosVisibility m_Visibility; // only valid between Interpolate and RenderSubmit
// Current animation state
float m_AnimRunThreshold; // if non-zero this is the special walk/run mode
@ -358,11 +360,15 @@ void CCmpVisualActor::Interpolate(float frameTime, float frameOffset)
// Disable rendering of the unit if it has no position
if (!cmpPosition->IsInWorld())
{
m_Hidden = true;
m_Visibility = ICmpRangeManager::VIS_HIDDEN;
return;
}
m_Hidden = false;
CmpPtr<ICmpRangeManager> cmpRangeManager(GetSimContext(), SYSTEM_ENTITY);
m_Visibility = cmpRangeManager->GetLosVisibility(GetEntityId(), GetSimContext().GetCurrentDisplayedPlayer());
// Even if HIDDEN due to LOS, we need to set up the transforms
// so that projectiles will be launched from the right place
bool floating = m_Unit->GetObject().m_Base->m_Properties.m_FloatOnWater;
@ -377,11 +383,9 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
if (m_Unit == NULL)
return;
if (m_Hidden)
if (m_Visibility == ICmpRangeManager::VIS_HIDDEN)
return;
// TODO: need to think about things like LOS here
CModel& model = m_Unit->GetModel();
model.ValidatePosition();
@ -389,7 +393,10 @@ void CCmpVisualActor::RenderSubmit(SceneCollector& collector, const CFrustum& fr
if (culling && !frustum.IsBoxVisible(CVector3D(0, 0, 0), model.GetBounds()))
return;
// TODO: model->SetShadingColor(CColor(1.0f, 1.0f, 1.0f, 1.0f) if visible, CColor(0.7f, 0.7f, 0.7f, 1.0f) if hidden in FOW)
if (m_Visibility == ICmpRangeManager::VIS_FOGGED)
model.SetShadingColor(CColor(0.7f, 0.7f, 0.7f, 1.0f));
else
model.SetShadingColor(CColor(1.0f, 1.0f, 1.0f, 1.0f));
collector.SubmitRecursive(&model);
}

View File

@ -20,6 +20,8 @@
#include "simulation2/system/Interface.h"
#include "simulation2/helpers/Position.h"
/**
* Per-unit minimap data.
*/
@ -31,7 +33,7 @@ public:
* If it should not be drawn, returns false; otherwise the arguments are set
* to the colour and world position.
*/
virtual bool GetRenderData(u8& r, u8& g, u8& b, float& x, float& z) = 0;
virtual bool GetRenderData(u8& r, u8& g, u8& b, entity_pos_t& x, entity_pos_t& z) = 0;
DECLARE_INTERFACE_TYPE(Minimap)
};

View File

@ -29,4 +29,5 @@ DEFINE_INTERFACE_METHOD_1("EnableActiveQuery", void, ICmpRangeManager, EnableAct
DEFINE_INTERFACE_METHOD_1("DisableActiveQuery", void, ICmpRangeManager, DisableActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("ResetActiveQuery", std::vector<entity_id_t>, ICmpRangeManager, ResetActiveQuery, ICmpRangeManager::tag_t)
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpRangeManager, SetDebugOverlay, bool)
DEFINE_INTERFACE_METHOD_1("SetLosRevealAll", void, ICmpRangeManager, SetLosRevealAll, bool)
END_INTERFACE_WRAPPER(RangeManager)

View File

@ -22,8 +22,14 @@
#include "simulation2/helpers/Position.h"
#include "graphics/Terrain.h" // for CELL_SIZE
/**
* Provides efficient range-based queries of the game world.
* Provides efficient range-based queries of the game world,
* and also LOS-based effects (fog of war).
*
* (These are somewhat distinct concepts but they share a lot of the implementation,
* so for efficiency they're combined into this class.)
*
* Possible use cases:
* - combat units need to detect targetable enemies entering LOS, so they can choose
@ -67,8 +73,9 @@ public:
* Set the bounds of the world.
* Entities should not be outside the bounds (else efficiency will suffer).
* @param x0,z0,x1,z1 Coordinates of the corners of the world
* @param vertices Number of terrain vertices per side
*/
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1) = 0;
virtual void SetBounds(entity_pos_t x0, entity_pos_t z0, entity_pos_t x1, entity_pos_t z1, ssize_t vertices) = 0;
/**
* Execute a passive query.
@ -123,6 +130,98 @@ public:
*/
virtual void SetDebugOverlay(bool enabled) = 0;
// LOS interface:
enum ELosState
{
LOS_UNEXPLORED = 0,
LOS_EXPLORED = 1,
LOS_VISIBLE = 2,
LOS_MASK = 3
};
enum ELosVisibility
{
VIS_HIDDEN,
VIS_FOGGED,
VIS_VISIBLE
};
/**
* Object providing efficient abstracted access to the LOS state.
* This depends on some implementation details of CCmpRangeManager.
*
* This *ignores* the GetLosRevealAll flag - callers should check that explicitly.
*/
class CLosQuerier
{
private:
friend class CCmpRangeManager;
CLosQuerier(int player, const std::vector<u32>& data, ssize_t verticesPerSide) :
m_Data(data), m_VerticesPerSide(verticesPerSide)
{
if (player > 0 && player <= 16)
m_PlayerMask = LOS_MASK << (2*(player-1));
else
m_PlayerMask = 0;
}
public:
/**
* Returns whether the given vertex is visible (i.e. is within a unit's LOS).
* i and j must be in the range [0, verticesPerSide).
*/
inline bool IsVisible(ssize_t i, ssize_t j)
{
#ifndef NDEBUG
debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide);
#endif
// Check high bit of each bit-pair
return (m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0xAAAAAAAAu;
}
/**
* Returns whether the given vertex is explored (i.e. was (or still is) within a unit's LOS).
* i and j must be in the range [0, verticesPerSide).
*/
inline bool IsExplored(ssize_t i, ssize_t j)
{
#ifndef NDEBUG
debug_assert(i >= 0 && j >= 0 && i < m_VerticesPerSide && j < m_VerticesPerSide);
#endif
// Check low bit of each bit-pair
return (m_Data.at(j*m_VerticesPerSide + i) & m_PlayerMask) & 0x55555555u;
}
private:
u32 m_PlayerMask;
const std::vector<u32>& m_Data;
ssize_t m_VerticesPerSide;
};
/**
* Returns a CLosQuerier for checking whether vertex positions are visible to the given player.
*/
virtual CLosQuerier GetLosQuerier(int player) = 0;
/**
* Returns the visibility status of the given entity, with respect to the given player.
* Returns VIS_HIDDEN if the entity doesn't exist or is not in the world.
* This respects the GetLosRevealAll flag.
*/
virtual ELosVisibility GetLosVisibility(entity_id_t ent, int player) = 0;
/**
* Set globally whether the whole map should be made visible.
*/
virtual void SetLosRevealAll(bool enabled) = 0;
/**
* Returns whether the whole map has been made visible to the given player.
*/
virtual bool GetLosRevealAll(int player) = 0;
DECLARE_INTERFACE_TYPE(RangeManager)
};

View File

@ -0,0 +1,27 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ICmpVision.h"
#include "simulation2/system/InterfaceScripted.h"
BEGIN_INTERFACE_WRAPPER(Vision)
DEFINE_INTERFACE_METHOD_0("GetRange", entity_pos_t, ICmpVision, GetRange)
DEFINE_INTERFACE_METHOD_0("GetRetainInFog", bool, ICmpVision, GetRetainInFog)
END_INTERFACE_WRAPPER(Vision)

View File

@ -0,0 +1,38 @@
/* Copyright (C) 2010 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_ICMPVISION
#define INCLUDED_ICMPVISION
#include "simulation2/system/Interface.h"
#include "simulation2/helpers/Position.h"
/**
* Vision (LOS etc) interface (typically implemented by a scripted component).
*/
class ICmpVision : public IComponent
{
public:
virtual entity_pos_t GetRange() = 0;
virtual bool GetRetainInFog() = 0;
DECLARE_INTERFACE_TYPE(Vision)
};
#endif // INCLUDED_ICMPVISION

View File

@ -22,14 +22,18 @@
#include "graphics/Camera.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpOwnership.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpSelectable.h"
#include "simulation2/components/ICmpVisual.h"
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY)
std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player)
{
CVector3D origin, dir;
camera.BuildCameraRay(screenX, screenY, origin, dir);
CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
debug_assert(!cmpRangeManager.null());
std::vector<std::pair<float, entity_id_t> > hits; // (dist^2, entity) pairs
const CSimulation2::InterfaceList& ents = simulation.GetEntitiesWithInterface(IID_Selectable);
@ -37,7 +41,9 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesAtPoint(CSimulation2& simu
{
entity_id_t ent = it->first;
// TODO: ought to filter out units hidden by current player's LOS
// Ignore entities hidden by LOS
if (cmpRangeManager->GetLosVisibility(ent, player) == ICmpRangeManager::VIS_HIDDEN)
continue;
CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
if (cmpVisual.null())
@ -81,6 +87,9 @@ std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simul
std::vector<entity_id_t> hitEnts;
// (We don't bother doing LOS checks for these units, since we assume 'owner'
// will be the current local player and all their own units will always be visible)
const CSimulation2::InterfaceList& ents = simulation.GetEntitiesWithInterface(IID_Selectable);
for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
{

View File

@ -36,8 +36,9 @@ namespace EntitySelection
/**
* Finds all selectable entities under the given screen coordinates.
* Returns list ordered by closeness of picking, closest first.
* Restricted to entities in the LOS of @p player, but with any owner.
*/
std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY);
std::vector<entity_id_t> PickEntitiesAtPoint(CSimulation2& simulation, const CCamera& camera, int screenX, int screenY, int player);
/**
* Finds all selectable entities within the given screen coordinate rectangle,

View File

@ -21,6 +21,8 @@
#include "ComponentManager.h"
#include "ps/Game.h"
CSimContext::CSimContext() :
m_ComponentManager(NULL), m_UnitManager(NULL), m_Terrain(NULL)
{
@ -63,3 +65,10 @@ ScriptInterface& CSimContext::GetScriptInterface() const
{
return GetComponentManager().GetScriptInterface();
}
int CSimContext::GetCurrentDisplayedPlayer() const
{
if (!g_Game)
return -1;
return g_Game->GetPlayerID();
}

View File

@ -43,6 +43,12 @@ public:
ScriptInterface& GetScriptInterface() const;
/**
* Returns the player ID that the current display is being rendered for.
* Currently relies on g_Game being initialised (evil globals...)
*/
int GetCurrentDisplayedPlayer() const;
private:
CComponentManager* m_ComponentManager;
CUnitManager* m_UnitManager;

View File

@ -81,15 +81,26 @@ public:
const CParamNode* preview = tempMan->LoadTemplate(ent2, "preview|unit", -1);
TS_ASSERT(preview != NULL);
TS_ASSERT_WSTR_EQUALS(preview->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
TS_ASSERT_WSTR_EQUALS(preview->ToXML(),
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
L"<VisualActor><Actor>example</Actor></VisualActor>");
const CParamNode* previewobstruct = tempMan->LoadTemplate(ent2, "preview|unitobstruct", -1);
TS_ASSERT(previewobstruct != NULL);
TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(), L"<Footprint><Circle radius=\"4\"></Circle><Height>1.0</Height></Footprint><Obstruction><Inactive></Inactive><Unit radius=\"4\"></Unit></Obstruction><Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example</Actor></VisualActor>");
TS_ASSERT_WSTR_EQUALS(previewobstruct->ToXML(),
L"<Footprint><Circle radius=\"4\"></Circle><Height>1.0</Height></Footprint>"
L"<Obstruction><Inactive></Inactive><Unit radius=\"4\"></Unit></Obstruction>"
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
L"<VisualActor><Actor>example</Actor></VisualActor>");
const CParamNode* previewactor = tempMan->LoadTemplate(ent2, "preview|actor|example2", -1);
TS_ASSERT(previewactor != NULL);
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(), L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position><VisualActor><Actor>example2</Actor></VisualActor>");
TS_ASSERT_WSTR_EQUALS(previewactor->ToXML(),
L"<Position><Altitude>0</Altitude><Anchor>upright</Anchor><Floating>false</Floating></Position>"
L"<Vision><Range>0</Range><RetainInFog>true</RetainInFog></Vision>"
L"<VisualActor><Actor>example2</Actor></VisualActor>");
}
void test_LoadTemplate_scriptcache()

View File

@ -40,6 +40,8 @@
#include "scriptinterface/ScriptInterface.h"
#include "simulation2/Simulation2.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
#include "simulation2/components/ICmpTerrain.h"
#include "simulation2/components/ICmpUnitMotion.h"
#include "simulation2/components/ICmpVisual.h"
@ -116,6 +118,15 @@ ActorViewer::ActorViewer()
// Start the simulation
m.Simulation2.LoadDefaultScripts();
m.Simulation2.ResetState();
// Tell the simulation we've already loaded the terrain
CmpPtr<ICmpTerrain> cmpTerrain(m.Simulation2, SYSTEM_ENTITY);
if (!cmpTerrain.null())
cmpTerrain->ReloadTerrain();
CmpPtr<ICmpRangeManager> cmpRangeManager(m.Simulation2, SYSTEM_ENTITY);
if (!cmpRangeManager.null())
cmpRangeManager->SetLosRevealAll(true);
}
ActorViewer::~ActorViewer()

View File

@ -37,6 +37,7 @@
#include "simulation2/components/ICmpPlayer.h"
#include "simulation2/components/ICmpPlayerManager.h"
#include "simulation2/components/ICmpPosition.h"
#include "simulation2/components/ICmpRangeManager.h"
namespace
{
@ -66,6 +67,11 @@ namespace
LDR_NonprogressiveLoad();
PSRETURN ret = g_Game->ReallyStartGame();
debug_assert(ret == PSRETURN_OK);
// Disable fog-of-war
CmpPtr<ICmpRangeManager> cmpRangeManager(*g_Game->GetSimulation2(), SYSTEM_ENTITY);
if (!cmpRangeManager.null())
cmpRangeManager->SetLosRevealAll(true);
}
}