ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool onlyCenterPoint) { // Check unit obstruction CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR; if (cmpObstructionManager->TestStaticShape(filter, x, z, a, w, h, NULL)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION; // Test against terrain: UpdateGrid(); ICmpObstructionManager::ObstructionSquare square; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id); if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; if (onlyCenterPoint) { u16 i, j; NearestTile(x, z, i, j); if (IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } // Expand bounds by 1/sqrt(2) tile (multiply by TERRAIN_TILE_SIZE since we want world coordinates) entity_pos_t expand = entity_pos_t::FromInt(2).Sqrt().Multiply(entity_pos_t::FromInt(TERRAIN_TILE_SIZE / 2)); CFixedVector2D halfSize(square.hw + expand, square.hh + expand); CFixedVector2D halfBound = Geometry::GetHalfBoundingBox(square.u, square.v, halfSize); u16 i0, j0, i1, j1; NearestTile(square.x - halfBound.X, square.z - halfBound.Y, i0, j0); NearestTile(square.x + halfBound.X, square.z + halfBound.Y, i1, j1); for (u16 j = j0; j <= j1; ++j) { for (u16 i = i0; i <= i1; ++i) { entity_pos_t x, z; TileCenter(i, j, x, z); if (Geometry::PointIsInSquare(CFixedVector2D(x - square.x, z - square.z), square.u, square.v, halfSize) && !IS_TERRAIN_PASSABLE(m_Grid->get(i,j), passClass)) { return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } } } return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; }
ICmpObstruction::EFoundationCheck CCmpPathfinder::CheckBuildingPlacement(const IObstructionTestFilter& filter, entity_pos_t x, entity_pos_t z, entity_pos_t a, entity_pos_t w, entity_pos_t h, entity_id_t id, pass_class_t passClass, bool UNUSED(onlyCenterPoint)) { // Check unit obstruction CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return ICmpObstruction::FOUNDATION_CHECK_FAIL_ERROR; if (cmpObstructionManager->TestStaticShape(filter, x, z, a, w, h, NULL)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_OBSTRUCTS_FOUNDATION; // Test against terrain: ICmpObstructionManager::ObstructionSquare square; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), id); if (!cmpObstruction || !cmpObstruction->GetObstructionSquare(square)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_NO_OBSTRUCTION; entity_pos_t expand; const PathfinderPassability* passability = GetPassabilityFromMask(passClass); if (passability) expand = passability->m_Clearance; SimRasterize::Spans spans; SimRasterize::RasterizeRectWithClearance(spans, square, expand, Pathfinding::NAVCELL_SIZE); for (SimRasterize::Span& span : spans) { i16 i0 = span.i0; i16 i1 = span.i1; i16 j = span.j; // Fail if any span extends outside the grid if (i0 < 0 || i1 > m_TerrainOnlyGrid->m_W || j < 0 || j > m_TerrainOnlyGrid->m_H) return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; // Fail if any span includes an impassable tile for (i16 i = i0; i < i1; ++i) if (!IS_PASSABLE(m_TerrainOnlyGrid->get(i, j), passClass)) return ICmpObstruction::FOUNDATION_CHECK_FAIL_TERRAIN_CLASS; } return ICmpObstruction::FOUNDATION_CHECK_SUCCESS; }
virtual void Init(const CParamNode& paramNode) { m_FormationController = paramNode.GetChild("FormationController").ToBool(); m_WalkSpeed = paramNode.GetChild("WalkSpeed").ToFixed(); m_Speed = m_WalkSpeed; if (paramNode.GetChild("Run").IsOk()) { m_RunSpeed = paramNode.GetChild("Run").GetChild("Speed").ToFixed(); } else { m_RunSpeed = m_WalkSpeed; } CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (!cmpPathfinder.null()) { m_PassClass = cmpPathfinder->GetPassabilityClass(paramNode.GetChild("PassabilityClass").ToUTF8()); m_CostClass = cmpPathfinder->GetCostClass(paramNode.GetChild("CostClass").ToUTF8()); } CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), GetEntityId()); if (!cmpObstruction.null()) m_Radius = cmpObstruction->GetUnitRadius(); m_State = STATE_IDLE; m_PathState = PATHSTATE_NONE; m_ExpectedPathTicket = 0; m_TargetEntity = INVALID_ENTITY; m_FinalGoal.type = ICmpPathfinder::Goal::POINT; m_DebugOverlayEnabled = false; }
void ResolveFoundationCollisions() { if (m_Type == UNIT) return; CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); if (!cmpObstructionManager) return; CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); if (!cmpPosition) return; // error if (!cmpPosition->IsInWorld()) return; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); // Ignore collisions within the same control group, or with other non-foundation-blocking shapes. // Note that, since the control group for each entity defaults to the entity's ID, this is typically // equivalent to only ignoring the entity's own shape and other non-foundation-blocking shapes. SkipControlGroupsRequireFlagObstructionFilter filter(m_ControlGroup, m_ControlGroup2, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); std::vector<entity_id_t> collisions; if (cmpObstructionManager->TestStaticShape(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, &collisions)) { std::vector<entity_id_t> persistentEnts, normalEnts; if (m_ControlPersist) persistentEnts.push_back(m_ControlGroup); else normalEnts.push_back(GetEntityId()); for (std::vector<entity_id_t>::iterator it = collisions.begin(); it != collisions.end(); ++it) { entity_id_t ent = *it; if (ent == INVALID_ENTITY) continue; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); if (!cmpObstruction->IsControlPersistent()) normalEnts.push_back(ent); else persistentEnts.push_back(cmpObstruction->GetControlGroup()); } // The collision can't be resolved without usable persistent control groups. if (persistentEnts.empty()) return; // Attempt to replace colliding entities' control groups with a persistent one. for (std::vector<entity_id_t>::iterator it = normalEnts.begin(); it != normalEnts.end(); ++it) { entity_id_t ent = *it; CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), ent); for (std::vector<entity_id_t>::iterator it = persistentEnts.begin(); it != persistentEnts.end(); ++it) { entity_id_t persistent = *it; entity_id_t group = cmpObstruction->GetControlGroup(); // Only clobber 'default' control groups. if (group == ent) cmpObstruction->SetControlGroup(persistent); else if (cmpObstruction->GetControlGroup2() == INVALID_ENTITY && group != persistent) cmpObstruction->SetControlGroup2(persistent); } } } }
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()); }