virtual bool CheckFoundation(std::string className) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (cmpPosition.null()) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (cmpPathfinder.null()) return false; // error // Get passability class ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className); // Ignore collisions with self, or with non-foundation-blocking obstructions SkipTagFlagsObstructionFilter filter(m_Tag, ICmpObstructionManager::FLAG_BLOCK_FOUNDATION); if (m_Type == STATIC) return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass); else return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Size0, passClass); }
void AtlasViewGame::Render() { SViewPort vp = { 0, 0, g_xres, g_yres }; CCamera& camera = GetCamera(); camera.SetViewPort(vp); camera.SetProjection(g_Game->GetView()->GetNear(), g_Game->GetView()->GetFar(), g_Game->GetView()->GetFOV()); camera.UpdateFrustum(); // Update the pathfinder display if necessary if (!m_DisplayPassability.empty()) { CmpPtr<ICmpObstructionManager> cmpObstructionManager(*GetSimulation2(), SYSTEM_ENTITY); if (cmpObstructionManager) { cmpObstructionManager->SetDebugOverlay(true); } CmpPtr<ICmpPathfinder> cmpPathfinder(*GetSimulation2(), SYSTEM_ENTITY); if (cmpPathfinder) { cmpPathfinder->SetDebugOverlay(true); // Kind of a hack to make it update the terrain grid PathGoal goal = { PathGoal::POINT, fixed::Zero(), fixed::Zero() }; pass_class_t passClass = cmpPathfinder->GetPassabilityClass(m_DisplayPassability); cmpPathfinder->SetDebugPath(fixed::Zero(), fixed::Zero(), goal, passClass); } } ::Render(); Atlas_GLSwapBuffers((void*)g_AtlasGameLoop->glCanvas); }
void CSimulation2Impl::UpdateComponents(CSimContext& simContext, fixed turnLengthFixed, const std::vector<SimulationCommand>& commands) { // TODO: the update process is pretty ugly, with lots of messages and dependencies // between different components. Ought to work out a nicer way to do this. CComponentManager& componentManager = simContext.GetComponentManager(); CMessageTurnStart msgTurnStart; componentManager.BroadcastMessage(msgTurnStart); CmpPtr<ICmpPathfinder> cmpPathfinder(simContext, SYSTEM_ENTITY); if (cmpPathfinder) { cmpPathfinder->UpdateGrid(); cmpPathfinder->FinishAsyncRequests(); } // Push AI commands onto the queue before we use them CmpPtr<ICmpAIManager> cmpAIManager(simContext, SYSTEM_ENTITY); if (cmpAIManager) cmpAIManager->PushCommands(); CmpPtr<ICmpCommandQueue> cmpCommandQueue(simContext, SYSTEM_ENTITY); if (cmpCommandQueue) cmpCommandQueue->FlushTurn(commands); // Process newly generated move commands so the UI feels snappy if (cmpPathfinder) cmpPathfinder->ProcessSameTurnMoves(); // Send all the update phases { CMessageUpdate msgUpdate(turnLengthFixed); componentManager.BroadcastMessage(msgUpdate); } { CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed); componentManager.BroadcastMessage(msgUpdate); } // Process move commands for formations (group proxy) if (cmpPathfinder) cmpPathfinder->ProcessSameTurnMoves(); { CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed); componentManager.BroadcastMessage(msgUpdate); } { CMessageUpdate_Final msgUpdate(turnLengthFixed); componentManager.BroadcastMessage(msgUpdate); } // Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others if (cmpPathfinder) cmpPathfinder->ProcessSameTurnMoves(); // Clean up any entities destroyed during the simulation update componentManager.FlushDestroyedComponents(); }
void AtlasViewGame::SetParam(const std::wstring& name, const std::wstring& value) { if (name == L"passability") { m_DisplayPassability = CStrW(value).ToUTF8(); CmpPtr<ICmpObstructionManager> cmpObstructionManager(*GetSimulation2(), SYSTEM_ENTITY); if (cmpObstructionManager) cmpObstructionManager->SetDebugOverlay(!value.empty()); CmpPtr<ICmpPathfinder> cmpPathfinder(*GetSimulation2(), SYSTEM_ENTITY); if (cmpPathfinder) cmpPathfinder->SetDebugOverlay(!value.empty()); } else if (name == L"renderpath") { g_Renderer.SetRenderPath(g_Renderer.GetRenderPathByName(CStrW(value).ToUTF8())); } }
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; }
virtual bool CheckFoundation(std::string className) { CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition) return false; // error if (!cmpPosition->IsInWorld()) return false; // no obstruction CFixedVector2D pos = cmpPosition->GetPosition2D(); CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (!cmpPathfinder) return false; // error // required precondition to use SkipControlGroupsRequireFlagObstructionFilter if (m_ControlGroup == INVALID_ENTITY) { LOGERROR(L"[CmpObstruction] Cannot test for foundation obstructions; primary control group must be valid"); return false; } // Get passability class ICmpPathfinder::pass_class_t passClass = cmpPathfinder->GetPassabilityClass(className); // 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); if (m_Type == STATIC) return cmpPathfinder->CheckBuildingPlacement(filter, pos.X, pos.Y, cmpPosition->GetRotation().Y, m_Size0, m_Size1, GetEntityId(), passClass); else return cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, m_Size0, passClass); }
bool CSimulation2Impl::Update(int turnLength, const std::vector<SimulationCommand>& commands) { fixed turnLengthFixed = fixed::FromInt(turnLength) / 1000; // TODO: the update process is pretty ugly, with lots of messages and dependencies // between different components. Ought to work out a nicer way to do this. CMessageTurnStart msgTurnStart; m_ComponentManager.BroadcastMessage(msgTurnStart); CmpPtr<ICmpPathfinder> cmpPathfinder(m_SimContext, SYSTEM_ENTITY); if (!cmpPathfinder.null()) cmpPathfinder->FinishAsyncRequests(); // Push AI commands onto the queue before we use them CmpPtr<ICmpAIManager> cmpAIManager(m_SimContext, SYSTEM_ENTITY); if (!cmpAIManager.null()) cmpAIManager->PushCommands(); CmpPtr<ICmpCommandQueue> cmpCommandQueue(m_SimContext, SYSTEM_ENTITY); if (!cmpCommandQueue.null()) cmpCommandQueue->FlushTurn(commands); // Process newly generated move commands so the UI feels snappy if (!cmpPathfinder.null()) cmpPathfinder->ProcessSameTurnMoves(); // Send all the update phases { CMessageUpdate msgUpdate(turnLengthFixed); m_ComponentManager.BroadcastMessage(msgUpdate); } { CMessageUpdate_MotionFormation msgUpdate(turnLengthFixed); m_ComponentManager.BroadcastMessage(msgUpdate); } // Process move commands for formations (group proxy) if (!cmpPathfinder.null()) cmpPathfinder->ProcessSameTurnMoves(); { CMessageUpdate_MotionUnit msgUpdate(turnLengthFixed); m_ComponentManager.BroadcastMessage(msgUpdate); } { CMessageUpdate_Final msgUpdate(turnLengthFixed); m_ComponentManager.BroadcastMessage(msgUpdate); } // Process moves resulting from group proxy movement (unit needs to catch up or realign) and any others if (!cmpPathfinder.null()) cmpPathfinder->ProcessSameTurnMoves(); // Clean up any entities destroyed during the simulation update m_ComponentManager.FlushDestroyedComponents(); // if (m_TurnNumber == 0) // m_ComponentManager.GetScriptInterface().DumpHeap(); // Run the GC occasionally // (TODO: we ought to schedule this for a frame where we're not // running the sim update, to spread the load) if (m_TurnNumber % 10 == 0) m_ComponentManager.GetScriptInterface().MaybeGC(); if (m_EnableOOSLog) DumpState(); // Start computing AI for the next turn if (!cmpAIManager.null()) cmpAIManager->StartComputation(); ++m_TurnNumber; return true; // TODO: don't bother with bool return }
virtual CFixedVector3D PickSpawnPoint(entity_id_t spawned) { // Try to find a free space around the building's footprint. // (Note that we use the footprint, not the obstruction shape - this might be a bit dodgy // because the footprint might be inside the obstruction, but it hopefully gives us a nicer // shape.) const CFixedVector3D error(fixed::FromInt(-1), fixed::FromInt(-1), fixed::FromInt(-1)); CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), GetEntityId()); if (!cmpPosition || !cmpPosition->IsInWorld()) return error; CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSimContext(), SYSTEM_ENTITY); if (!cmpObstructionManager) return error; entity_pos_t spawnedRadius; ICmpObstructionManager::tag_t spawnedTag; CmpPtr<ICmpObstruction> cmpSpawnedObstruction(GetSimContext(), spawned); if (cmpSpawnedObstruction) { spawnedRadius = cmpSpawnedObstruction->GetUnitRadius(); spawnedTag = cmpSpawnedObstruction->GetObstruction(); } // else use zero radius // Get passability class from UnitMotion CmpPtr<ICmpUnitMotion> cmpUnitMotion(GetSimContext(), spawned); if (!cmpUnitMotion) return error; ICmpPathfinder::pass_class_t spawnedPass = cmpUnitMotion->GetPassabilityClass(); CmpPtr<ICmpPathfinder> cmpPathfinder(GetSimContext(), SYSTEM_ENTITY); if (!cmpPathfinder) return error; CFixedVector2D initialPos = cmpPosition->GetPosition2D(); entity_angle_t initialAngle = cmpPosition->GetRotation().Y; // Max spawning distance in tiles const i32 maxSpawningDistance = 4; if (m_Shape == CIRCLE) { // Expand outwards from foundation for (i32 dist = 0; dist <= maxSpawningDistance; ++dist) { // The spawn point should be far enough from this footprint to fit the unit, plus a little gap entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist); entity_pos_t radius = m_Size0 + clearance; // Try equally-spaced points around the circle in alternating directions, starting from the front const i32 numPoints = 31 + 2*dist; for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2] { entity_angle_t angle = initialAngle + (entity_angle_t::Pi()*2).Multiply(entity_angle_t::FromInt(i)/(int)numPoints); fixed s, c; sincos_approx(angle, s, c); CFixedVector3D pos (initialPos.X + s.Multiply(radius), fixed::Zero(), initialPos.Y + c.Multiply(radius)); SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Z, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS) return pos; // this position is okay, so return it } } } else { fixed s, c; sincos_approx(initialAngle, s, c); // Expand outwards from foundation for (i32 dist = 0; dist <= maxSpawningDistance; ++dist) { // The spawn point should be far enough from this footprint to fit the unit, plus a little gap entity_pos_t clearance = spawnedRadius + entity_pos_t::FromInt(2 + (int)TERRAIN_TILE_SIZE*dist); for (i32 edge = 0; edge < 4; ++edge) { // Try equally-spaced points along the edge in alternating directions, starting from the middle const i32 numPoints = 9 + 2*dist; // Compute the direction and length of the current edge CFixedVector2D dir; fixed sx, sy; switch (edge) { case 0: dir = CFixedVector2D(c, -s); sx = m_Size0; sy = m_Size1; break; case 1: dir = CFixedVector2D(-s, -c); sx = m_Size1; sy = m_Size0; break; case 2: dir = CFixedVector2D(s, c); sx = m_Size1; sy = m_Size0; break; case 3: dir = CFixedVector2D(-c, s); sx = m_Size0; sy = m_Size1; break; } CFixedVector2D center = initialPos - dir.Perpendicular().Multiply(sy/2 + clearance); dir = dir.Multiply((sx + clearance*2) / (int)(numPoints-1)); for (i32 i = 0; i < (numPoints+1)/2; i = (i > 0 ? -i : 1-i)) // [0, +1, -1, +2, -2, ... (np-1)/2, -(np-1)/2] { CFixedVector2D pos (center + dir*i); SkipTagObstructionFilter filter(spawnedTag); // ignore collisions with the spawned entity if (cmpPathfinder->CheckUnitPlacement(filter, pos.X, pos.Y, spawnedRadius, spawnedPass) == ICmpObstruction::FOUNDATION_CHECK_SUCCESS) return CFixedVector3D(pos.X, fixed::Zero(), pos.Y); // this position is okay, so return it } } } } return error; }