Esempio n. 1
0
	virtual void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize)
	{
		Init(paramNode);

		u32 oldSeed = GetActorSeed();

		SerializeCommon(deserialize);

		// If we serialized a different seed, reload actor
		if (oldSeed != GetActorSeed())
			ReloadActor();

		fixed repeattime = m_AnimSyncRepeatTime; // save because SelectAnimation overwrites it

		if (m_AnimRunThreshold.IsZero())
			SelectAnimation(m_AnimName, m_AnimOnce, m_AnimSpeed, m_SoundGroup);
		else
			SelectMovementAnimation(m_AnimRunThreshold);

		SetAnimationSyncRepeat(repeattime);

		if (m_Unit)
		{
			CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), GetEntityId());
			if (cmpOwnership)
				m_Unit->GetModel().SetPlayerID(cmpOwnership->GetOwner());
		}
	}
Esempio n. 2
0
std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, player_id_t owner, bool allowEditorSelectables)
{
	PROFILE2("PickEntitiesInRect");
	// Make sure sx0 <= sx1, and sy0 <= sy1
	if (sx0 > sx1)
		std::swap(sx0, sx1);
	if (sy0 > sy1)
		std::swap(sy0, sy1);

	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
	ENSURE(cmpRangeManager);

	std::vector<entity_id_t> hitEnts;

	const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
	for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
	{
		entity_id_t ent = it->first;
		CEntityHandle handle = it->second->GetEntityHandle();

		// Check if this entity is only selectable in Atlas
		if (static_cast<ICmpSelectable*>(it->second)->IsEditorOnly() && !allowEditorSelectables)
			continue;

		// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
		if (cmpRangeManager->GetLosVisibility(handle, owner) == ICmpRangeManager::VIS_HIDDEN)
			continue;

		// Ignore entities not owned by 'owner'
		CmpPtr<ICmpOwnership> cmpOwnership(handle);
		if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner))
			continue;

		// Find the current interpolated model position.
		// (We just use the centre position and not the whole bounding box, because maybe
		// that's better for users trying to select objects in busy areas)

		CmpPtr<ICmpVisual> cmpVisual(handle);
		if (!cmpVisual)
			continue;

		CVector3D position = cmpVisual->GetPosition();

		// Reject if it's not on-screen (e.g. it's behind the camera)
		if (!camera.GetFrustum().IsPointVisible(position))
			continue;

		// Compare screen-space coordinates
		float x, y;
		camera.GetScreenCoordinates(position, x, y);
		int ix = (int)x;
		int iy = (int)y;
		if (sx0 <= ix && ix <= sx1 && sy0 <= iy && iy <= sy1)
			hitEnts.push_back(ent);
	}

	return hitEnts;
}
Esempio n. 3
0
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, const std::string& templateName, int owner, bool includeOffScreen, bool matchRank)
{
	CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);

	std::vector<entity_id_t> hitEnts;

 	const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
	for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
	{
 		entity_id_t ent = it->first;

		if (matchRank)
		{
			// Exact template name matching
			if (cmpTemplateManager->GetCurrentTemplateName(ent) != templateName)
				continue;
		}

		// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
		// In this case, the checking is done to avoid selecting garrisoned units
		if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
			continue;

 		// Ignore entities not owned by 'owner'
 		CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
 		if (!cmpOwnership || cmpOwnership->GetOwner() != owner)
 			continue;

		// Ignore off screen entities
 		if (!includeOffScreen)
 		{
 			// Find the current interpolated model position.
			CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
			if (!cmpVisual)
				continue;
			CVector3D position = cmpVisual->GetPosition();

			// Reject if it's not on-screen (e.g. it's behind the camera)
			if (!camera.GetFrustum().IsPointVisible(position))
				continue;
 		}

		if (!matchRank)
		{
			// Match by selection group name
			// (This is relatively expensive since it involves script calls, so do it after all other tests)
			CmpPtr<ICmpIdentity> cmpIdentity(simulation.GetSimContext(), ent);
			if (!cmpIdentity || cmpIdentity->GetSelectionGroupName() != templateName)
				continue;
		}

 		hitEnts.push_back(ent);
 	}

 	return hitEnts;
}
Esempio n. 4
0
std::vector<entity_id_t> EntitySelection::PickEntitiesInRect(CSimulation2& simulation, const CCamera& camera, int sx0, int sy0, int sx1, int sy1, int owner)
{
    // Make sure sx0 <= sx1, and sy0 <= sy1
    if (sx0 > sx1)
        std::swap(sx0, sx1);
    if (sy0 > sy1)
        std::swap(sy0, sy1);

    CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);
    ENSURE(!cmpRangeManager.null());

    std::vector<entity_id_t> hitEnts;

    const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
    for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
    {
        entity_id_t ent = it->first;

        // Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
        if (cmpRangeManager->GetLosVisibility(ent, owner) == ICmpRangeManager::VIS_HIDDEN)
            continue;

        // Ignore entities not owned by 'owner'
        CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
        if (cmpOwnership.null() || cmpOwnership->GetOwner() != owner)
            continue;

        // Find the current interpolated model position.
        // (We just use the centre position and not the whole bounding box, because maybe
        // that's better for users trying to select objects in busy areas)

        CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
        if (cmpVisual.null())
            continue;

        CVector3D position = cmpVisual->GetPosition();

        // Reject if it's not on-screen (e.g. it's behind the camera)

        if (!camera.GetFrustum().IsPointVisible(position))
            continue;

        // Compare screen-space coordinates
        float x, y;
        camera.GetScreenCoordinates(position, x, y);
        int ix = (int)x;
        int iy = (int)y;
        if (sx0 <= ix && ix <= sx1 && sy0 <= iy && iy <= sy1)
            hitEnts.push_back(ent);
    }

    return hitEnts;
}
Esempio n. 5
0
	virtual void HandleMessage(const CMessage& msg, bool UNUSED(global))
	{
		switch (msg.GetType())
		{
		case MT_TechnologyModification:
		{
			const CMessageTechnologyModification& msgData = static_cast<const CMessageTechnologyModification&> (msg);
			if (msgData.component == L"Vision")
			{
				CmpPtr<ICmpOwnership> cmpOwnership(GetSimContext(), GetEntityId());
				if (cmpOwnership)
				{
					player_id_t owner = cmpOwnership->GetOwner();
					if (owner != INVALID_PLAYER && owner == msgData.player)
					{
						CmpPtr<ICmpPlayerManager> cmpPlayerManager(GetSimContext(), SYSTEM_ENTITY);
						entity_id_t playerEnt = cmpPlayerManager->GetPlayerByID(owner);
						CmpPtr<ICmpTechnologyManager> cmpTechnologyManager(GetSimContext(), playerEnt);
						if (playerEnt != INVALID_ENTITY && cmpTechnologyManager)
						{
							entity_pos_t newRange = cmpTechnologyManager->ApplyModifications(L"Vision/Range", m_BaseRange, GetEntityId());
							if (newRange != m_Range)
							{
								// Update our vision range and broadcast message
								entity_pos_t oldRange = m_Range;
								m_Range = newRange;
								CMessageVisionRangeChanged msg(GetEntityId(), oldRange, newRange);
								GetSimContext().GetComponentManager().BroadcastMessage(msg);
							}
						}
					}
				}
			}
			break;
		}
		}
	}
Esempio n. 6
0
	virtual void PlaySoundGroup(const std::wstring& name, entity_id_t source)
	{
		if ( ! g_SoundManager || (source == INVALID_ENTITY) )
			return;

		CmpPtr<ICmpRangeManager> cmpRangeManager(GetSystemEntity());
		int currentPlayer = GetSimContext().GetCurrentDisplayedPlayer();

		if ( !cmpRangeManager || 
				( cmpRangeManager->GetLosVisibility(source, currentPlayer) != ICmpRangeManager::VIS_VISIBLE ) )
			return;

		CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), source);
		if (cmpPosition && cmpPosition->IsInWorld())
		{
			bool playerOwned = false;
			CmpPtr<ICmpOwnership> cmpOwnership( GetSimContext(), source);
			if (cmpOwnership)
				playerOwned = cmpOwnership->GetOwner() == currentPlayer;

			CVector3D sourcePos = CVector3D(cmpPosition->GetPosition());
			g_SoundManager->PlayAsGroup(name, sourcePos, source, playerOwned);
		}
	}
Esempio n. 7
0
void CMapWriter::WriteXML(const VfsPath& filename,
						  WaterManager* pWaterMan, SkyManager* pSkyMan,
						  CLightEnv* pLightEnv, CCamera* pCamera, CCinemaManager* pCinema,
						  CPostprocManager* pPostproc,
						  CSimulation2* pSimulation2)
{
	XML_Start();

	{
		XML_Element("Scenario");
		XML_Attribute("version", (int)FILE_VERSION);

		ENSURE(pSimulation2);
		CSimulation2& sim = *pSimulation2;

		if (!sim.GetStartupScript().empty())
		{
			XML_Element("Script");
			XML_CDATA(sim.GetStartupScript().c_str());
		}

		{
			XML_Element("Environment");

			XML_Setting("SkySet", pSkyMan->GetSkySet());
			{
				XML_Element("SunColor");
				XML_Attribute("r", pLightEnv->m_SunColor.X); // yes, it's X/Y/Z...
				XML_Attribute("g", pLightEnv->m_SunColor.Y);
				XML_Attribute("b", pLightEnv->m_SunColor.Z);
			}
			{
				XML_Element("SunElevation");
				XML_Attribute("angle", pLightEnv->m_Elevation);
			}
			{
				XML_Element("SunRotation");
				XML_Attribute("angle", pLightEnv->m_Rotation);
			}
			{
				XML_Element("TerrainAmbientColor");
				XML_Attribute("r", pLightEnv->m_TerrainAmbientColor.X);
				XML_Attribute("g", pLightEnv->m_TerrainAmbientColor.Y);
				XML_Attribute("b", pLightEnv->m_TerrainAmbientColor.Z);
			}
			{
				XML_Element("UnitsAmbientColor");
				XML_Attribute("r", pLightEnv->m_UnitsAmbientColor.X);
				XML_Attribute("g", pLightEnv->m_UnitsAmbientColor.Y);
				XML_Attribute("b", pLightEnv->m_UnitsAmbientColor.Z);
			}
			{
				XML_Element("Fog");
				XML_Setting("FogFactor", pLightEnv->m_FogFactor);
				XML_Setting("FogThickness", pLightEnv->m_FogMax);
				{
					XML_Element("FogColor");
					XML_Attribute("r", pLightEnv->m_FogColor.X);
					XML_Attribute("g", pLightEnv->m_FogColor.Y);
					XML_Attribute("b", pLightEnv->m_FogColor.Z);
				}
			}

			{
				XML_Element("Water");
				{
					XML_Element("WaterBody");
					CmpPtr<ICmpWaterManager> cmpWaterManager(sim, SYSTEM_ENTITY);
					ENSURE(cmpWaterManager);
					XML_Setting("Type", pWaterMan->m_WaterType);
					{
						XML_Element("Color");
						XML_Attribute("r", pWaterMan->m_WaterColor.r);
						XML_Attribute("g", pWaterMan->m_WaterColor.g);
						XML_Attribute("b", pWaterMan->m_WaterColor.b);
					}
					{
						XML_Element("Tint");
						XML_Attribute("r", pWaterMan->m_WaterTint.r);
						XML_Attribute("g", pWaterMan->m_WaterTint.g);
						XML_Attribute("b", pWaterMan->m_WaterTint.b);
					}
					XML_Setting("Height", cmpWaterManager->GetExactWaterLevel(0, 0));
					XML_Setting("Waviness", pWaterMan->m_Waviness);
					XML_Setting("Murkiness", pWaterMan->m_Murkiness);
					XML_Setting("WindAngle", pWaterMan->m_WindAngle);
				}
			}

			{
				XML_Element("Postproc");
				{
					XML_Setting("Brightness", pLightEnv->m_Brightness);
					XML_Setting("Contrast", pLightEnv->m_Contrast);
					XML_Setting("Saturation", pLightEnv->m_Saturation);
					XML_Setting("Bloom", pLightEnv->m_Bloom);
					XML_Setting("PostEffect", pPostproc->GetPostEffect());
				}
			}
		}

		{
			XML_Element("Camera");

			{
				XML_Element("Position");
				CVector3D pos = pCamera->m_Orientation.GetTranslation();
				XML_Attribute("x", pos.X);
				XML_Attribute("y", pos.Y);
				XML_Attribute("z", pos.Z);
			}

			CVector3D in = pCamera->m_Orientation.GetIn();
			// Convert to spherical coordinates
			float rotation = atan2(in.X, in.Z);
			float declination = atan2(sqrt(in.X*in.X + in.Z*in.Z), in.Y) - (float)M_PI/2;

			{
				XML_Element("Rotation");
				XML_Attribute("angle", rotation);
			}
			{
				XML_Element("Declination");
				XML_Attribute("angle", declination);
			}
		}

		{
			std::string settings = sim.GetMapSettingsString();
			if (!settings.empty())
			{
				XML_Element("ScriptSettings");
				XML_CDATA(("\n" + settings + "\n").c_str());
			}
		}

		{
			XML_Element("Entities");

			CmpPtr<ICmpTemplateManager> cmpTemplateManager(sim, SYSTEM_ENTITY);
			ENSURE(cmpTemplateManager);

			// This will probably need to be changed in the future, but for now we'll
			// just save all entities that have a position
			CSimulation2::InterfaceList ents = sim.GetEntitiesWithInterface(IID_Position);
			for (CSimulation2::InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
			{
				entity_id_t ent = it->first;

				// Don't save local entities (placement previews etc)
				if (ENTITY_IS_LOCAL(ent))
					continue;

				XML_Element("Entity");
				XML_Attribute("uid", ent);

				XML_Setting("Template", cmpTemplateManager->GetCurrentTemplateName(ent));

				CmpPtr<ICmpOwnership> cmpOwnership(sim, ent);
				if (cmpOwnership)
					XML_Setting("Player", (int)cmpOwnership->GetOwner());

				CmpPtr<ICmpPosition> cmpPosition(sim, ent);
				if (cmpPosition)
				{
					CFixedVector3D pos;
					if (cmpPosition->IsInWorld())
						pos = cmpPosition->GetPosition();

					CFixedVector3D rot = cmpPosition->GetRotation();
					{
						XML_Element("Position");
						XML_Attribute("x", pos.X);
						XML_Attribute("z", pos.Z);
						// TODO: height offset etc
					}
					{
						XML_Element("Orientation");
						XML_Attribute("y", rot.Y);
						// TODO: X, Z maybe
					}
				}

				CmpPtr<ICmpObstruction> cmpObstruction(sim, ent);
				if (cmpObstruction)
				{
					// TODO: Currently only necessary because Atlas
					// does not set up control groups for its walls.
					cmpObstruction->ResolveFoundationCollisions();

					entity_id_t group = cmpObstruction->GetControlGroup();
					entity_id_t group2 = cmpObstruction->GetControlGroup2();

					// Don't waste space writing the default control groups.
					if (group != ent || group2 != INVALID_ENTITY)
					{
						XML_Element("Obstruction");
						if (group != ent)
							XML_Attribute("group", group);
						if (group2 != INVALID_ENTITY)
							XML_Attribute("group2", group2);
					}
				}

				CmpPtr<ICmpVisual> cmpVisual(sim, ent);
				if (cmpVisual)
				{
					u32 seed = cmpVisual->GetActorSeed();
					if (seed != (u32)ent)
					{
						XML_Element("Actor");
						XML_Attribute("seed", seed);
					}
					// TODO: variation/selection strings
				}
			}
		}

		const std::map<CStrW, CCinemaPath>& paths = pCinema->GetAllPaths();
		std::map<CStrW, CCinemaPath>::const_iterator it = paths.begin();

		{
			XML_Element("Paths");

			for ( ; it != paths.end(); ++it )
			{
				fixed timescale = it->second.GetTimescale();
				const std::vector<SplineData>& nodes = it->second.GetAllNodes();
				const std::vector<SplineData>& target_nodes = it->second.GetTargetSpline().GetAllNodes();
				const CCinemaData* data = it->second.GetData();
				
				XML_Element("Path");
				XML_Attribute("name", data->m_Name);
				XML_Attribute("timescale", timescale);
				XML_Attribute("orientation", data->m_Orientation);
				XML_Attribute("mode", data->m_Mode);
				XML_Attribute("style", data->m_Style);

				fixed last_target = fixed::Zero();
				for (size_t i = 0, j = 0; i < nodes.size(); ++i)
				{
					XML_Element("Node");
					fixed distance = i > 0 ? nodes[i - 1].Distance : fixed::Zero();
					last_target += distance;

					XML_Attribute("deltatime", distance);

					{
						XML_Element("Position");
						XML_Attribute("x", nodes[i].Position.X);
						XML_Attribute("y", nodes[i].Position.Y);
						XML_Attribute("z", nodes[i].Position.Z);
					}

					{
						XML_Element("Rotation");
						XML_Attribute("x", nodes[i].Rotation.X);
						XML_Attribute("y", nodes[i].Rotation.Y);
						XML_Attribute("z", nodes[i].Rotation.Z);
					}

					if (j >= target_nodes.size())
						continue;

					fixed target_distance = j > 0 ? target_nodes[j - 1].Distance : fixed::Zero();

					if (target_distance > last_target)
						continue;

					{
						XML_Element("Target");
						XML_Attribute("x", target_nodes[j].Position.X);
						XML_Attribute("y", target_nodes[j].Position.Y);
						XML_Attribute("z", target_nodes[j].Position.Z);
					}

					last_target = fixed::Zero();
					++j;
				}
			}
		}
	}
	if (!XML_StoreVFS(g_VFS, filename))
		LOGERROR("Failed to write map '%s'", filename.string8());
}
Esempio n. 8
0
void ActorViewer::SetActor(const CStrW& name, const CStrW& animation, player_id_t playerID)
{
	bool needsAnimReload = false;

	CStrW id = name;

	// Recreate the entity, if we don't have one or if the new one is different
	if (m.Entity == INVALID_ENTITY || id != m.CurrentUnitID)
	{
		// Delete the old entity (if any)
		if (m.Entity != INVALID_ENTITY)
		{
			m.Simulation2.DestroyEntity(m.Entity);
			m.Simulation2.FlushDestroyedEntities();
			m.Entity = INVALID_ENTITY;
		}

		// Clear particles associated with deleted entity
		g_Renderer.GetParticleManager().ClearUnattachedEmitters();

		// If there's no actor to display, return with nothing loaded
		if (id.empty())
			return;

		m.Entity = m.Simulation2.AddEntity(L"preview|" + id);
		if (m.Entity == INVALID_ENTITY)
			return;

		CmpPtr<ICmpPosition> cmpPosition(m.Simulation2, m.Entity);
		if (cmpPosition)
		{
			ssize_t c = TERRAIN_TILE_SIZE * m.Terrain.GetPatchesPerSide()*PATCH_SIZE/2;
			cmpPosition->JumpTo(entity_pos_t::FromInt(c), entity_pos_t::FromInt(c));
			cmpPosition->SetYRotation(entity_angle_t::Pi());
		}

		CmpPtr<ICmpOwnership> cmpOwnership(m.Simulation2, m.Entity);
		if (cmpOwnership)
			cmpOwnership->SetOwner(playerID);

		needsAnimReload = true;
	}

	if (animation != m.CurrentUnitAnim)
		needsAnimReload = true;

	if (needsAnimReload)
	{
		CStr anim = animation.ToUTF8().LowerCase();

		// Emulate the typical simulation animation behaviour
		float speed;
		float repeattime = 0.f;
		if (anim == "walk")
		{
			CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity);
			if (cmpUnitMotion)
				speed = cmpUnitMotion->GetWalkSpeed().ToFloat();
			else
				speed = 7.f; // typical unit speed

			m.CurrentSpeed = speed;
		}
		else if (anim == "run")
		{
			CmpPtr<ICmpUnitMotion> cmpUnitMotion(m.Simulation2, m.Entity);
			if (cmpUnitMotion)
				speed = cmpUnitMotion->GetRunSpeed().ToFloat();
			else
				speed = 12.f; // typical unit speed

			m.CurrentSpeed = speed;
		}
		else if (anim == "melee")
		{
			speed = 1.f; // speed will be ignored if we have a repeattime
			m.CurrentSpeed = 0.f;

			CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Attack); " +
				"if (cmp) cmp.GetTimers(cmp.GetBestAttack()).repeat; else 0;";
			m.Simulation2.GetScriptInterface().Eval(code.c_str(), repeattime);
		}
		else
		{
			// Play the animation at normal speed, but movement speed is zero
			speed = 1.f;
			m.CurrentSpeed = 0.f;
		}

		CStr sound;
		if (anim == "melee")
			sound = "attack";
		else if (anim == "build")
			sound = "build";
		else if (anim.Find("gather_") == 0)
			sound = anim;

		std::wstring soundgroup;
		if (!sound.empty())
		{
			CStr code = "var cmp = Engine.QueryInterface("+CStr::FromUInt(m.Entity)+", IID_Sound); " +
				"if (cmp) cmp.GetSoundGroup('"+sound+"'); else '';";
			m.Simulation2.GetScriptInterface().Eval(code.c_str(), soundgroup);
		}

		CmpPtr<ICmpVisual> cmpVisual(m.Simulation2, m.Entity);
		if (cmpVisual)
		{
			// TODO: SetEntitySelection(anim)
			cmpVisual->SelectAnimation(anim, false, fixed::FromFloat(speed), soundgroup);
			if (repeattime)
				cmpVisual->SetAnimationSyncRepeat(fixed::FromFloat(repeattime));
		}

		// update prop list for new entity/animation (relies on needsAnimReload also getting called for entire entity changes)
		m.UpdatePropList();
	}

	m.CurrentUnitID = id;
	m.CurrentUnitAnim = animation;
}
Esempio n. 9
0
std::vector<entity_id_t> EntitySelection::PickSimilarEntities(CSimulation2& simulation, const CCamera& camera, 
	const std::string& templateName, player_id_t owner, bool includeOffScreen, bool matchRank,
	bool allowEditorSelectables, bool allowFoundations)
{
	PROFILE2("PickSimilarEntities");
	CmpPtr<ICmpTemplateManager> cmpTemplateManager(simulation, SYSTEM_ENTITY);
	CmpPtr<ICmpRangeManager> cmpRangeManager(simulation, SYSTEM_ENTITY);

	std::vector<entity_id_t> hitEnts;

 	const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable);
	for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
	{
 		entity_id_t ent = it->first;
		CEntityHandle handle = it->second->GetEntityHandle();

		// Check if this entity is only selectable in Atlas
		if (static_cast<ICmpSelectable*>(it->second)->IsEditorOnly() && !allowEditorSelectables)
			continue;

		if (matchRank)
		{
			// Exact template name matching, optionally also allowing foundations
			std::string curTemplateName = cmpTemplateManager->GetCurrentTemplateName(ent);
			bool matches = (curTemplateName == templateName ||
			                (allowFoundations && curTemplateName.substr(0, 11) == "foundation|" && curTemplateName.substr(11) == templateName));
			if (!matches)
				continue;
		}

		// Ignore entities hidden by LOS (or otherwise hidden, e.g. when not IsInWorld)
		// In this case, the checking is done to avoid selecting garrisoned units
		if (cmpRangeManager->GetLosVisibility(handle, owner) == ICmpRangeManager::VIS_HIDDEN)
			continue;

		// Ignore entities not owned by 'owner'
		CmpPtr<ICmpOwnership> cmpOwnership(simulation.GetSimContext(), ent);
		if (owner != INVALID_PLAYER && (!cmpOwnership || cmpOwnership->GetOwner() != owner))
			continue;

		// Ignore off screen entities
		if (!includeOffScreen)
		{
 			// Find the current interpolated model position.
			CmpPtr<ICmpVisual> cmpVisual(simulation.GetSimContext(), ent);
			if (!cmpVisual)
				continue;
			CVector3D position = cmpVisual->GetPosition();

			// Reject if it's not on-screen (e.g. it's behind the camera)
			if (!camera.GetFrustum().IsPointVisible(position))
				continue;
		}

		if (!matchRank)
		{
			// Match by selection group name
			// (This is relatively expensive since it involves script calls, so do it after all other tests)
			CmpPtr<ICmpIdentity> cmpIdentity(simulation.GetSimContext(), ent);
			if (!cmpIdentity || cmpIdentity->GetSelectionGroupName() != templateName)
				continue;
		}

 		hitEnts.push_back(ent);
 	}

 	return hitEnts;
}