Index: source/simulation2/components/CCmpUnitMotion.cpp =================================================================== --- source/simulation2/components/CCmpUnitMotion.cpp +++ source/simulation2/components/CCmpUnitMotion.cpp @@ -797,24 +797,25 @@ if (m_State == STATE_IDLE) return; - switch (m_PathState) - { - case PATHSTATE_NONE: - { - // If we're not pathing, do nothing + if (m_PathState == PATHSTATE_NONE || + m_PathState == PATHSTATE_WAITING_REQUESTING_LONG || + m_PathState == PATHSTATE_WAITING_REQUESTING_SHORT) return; - } - case PATHSTATE_WAITING_REQUESTING_LONG: - case PATHSTATE_WAITING_REQUESTING_SHORT: - { - // If we're waiting for a path and don't have one yet, do nothing + CmpPtr cmpPosition(GetEntityHandle()); + if (!cmpPosition || !cmpPosition->IsInWorld()) return; - } - case PATHSTATE_FOLLOWING: - case PATHSTATE_FOLLOWING_REQUESTING_SHORT: - case PATHSTATE_FOLLOWING_REQUESTING_LONG: + CFixedVector2D initialPos = cmpPosition->GetPosition2D(); + + // Keep track of the current unit's position during the update + CFixedVector2D pos = initialPos; + + bool wasObstructed = false; + + if (m_PathState == PATHSTATE_FOLLOWING || + m_PathState == PATHSTATE_FOLLOWING_REQUESTING_SHORT || + m_PathState == PATHSTATE_FOLLOWING_REQUESTING_LONG) { // TODO: there's some asymmetry here when units look at other // units' positions - the result will depend on the order of execution. @@ -825,21 +826,12 @@ if (!cmpPathfinder) return; - CmpPtr cmpPosition(GetEntityHandle()); - if (!cmpPosition || !cmpPosition->IsInWorld()) - return; - - CFixedVector2D initialPos = cmpPosition->GetPosition2D(); - // If we're chasing a potentially-moving unit and are currently close // enough to its current position, and we can head in a straight line // to it, then throw away our current path and go straight to it if (m_PathState == PATHSTATE_FOLLOWING) TryGoingStraightToTargetEntity(initialPos); - // Keep track of the current unit's position during the update - CFixedVector2D pos = initialPos; - fixed basicSpeed = m_Speed; // If in formation, run to keep up; otherwise just walk if (IsFormationMember()) @@ -853,8 +845,6 @@ fixed maxSpeed = basicSpeed.Multiply(terrainSpeed); - bool wasObstructed = false; - // We want to move (at most) maxSpeed*dt units from pos towards the next waypoint fixed timeLeft = dt; @@ -916,133 +906,132 @@ break; } } + } - // Update the Position component after our movement (if we actually moved anywhere) - if (pos != initialPos) + // Update the Position component after our movement (if we actually moved anywhere) + if (pos != initialPos) + { + CFixedVector2D offset = pos - initialPos; + + // Face towards the target + entity_angle_t angle = atan2_approx(offset.X, offset.Y); + cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); + + // Calculate the mean speed over this past turn. + m_CurSpeed = cmpPosition->GetDistanceTravelled() / dt; + } + + if (wasObstructed) + { + // Oops, we hit something (very likely another unit). + // This is when we might easily get stuck wrongly. + + // check if we've arrived. + if (CloseEnoughFromDestinationToStop(pos)) { - CFixedVector2D offset = pos - initialPos; + MoveSucceeded(); - // Face towards the target - entity_angle_t angle = atan2_approx(offset.X, offset.Y); - cmpPosition->MoveAndTurnTo(pos.X,pos.Y, angle); - - // Calculate the mean speed over this past turn. - m_CurSpeed = cmpPosition->GetDistanceTravelled() / dt; + return; } - if (wasObstructed) + // If we still have long waypoints, try and compute a short path + // This will get us around units, amongst others. + // However in some cases a long waypoint will be in located in the obstruction of + // an idle unit. In that case, we need to scrap that waypoint or we might never be able to reach it. + // I am not sure why this happens but the following code seems to work. + if (!m_LongPath.m_Waypoints.empty()) { - // Oops, we hit something (very likely another unit). - // This is when we might easily get stuck wrongly. - - // check if we've arrived. - if (CloseEnoughFromDestinationToStop(pos)) + CmpPtr cmpObstructionManager(GetSystemEntity()); + if (cmpObstructionManager) { - MoveSucceeded(); - return; + // create a fake obstruction to represent our waypoint. + ICmpObstructionManager::ObstructionSquare square; + square.hh = m_Clearance; + square.hw = m_Clearance; + square.u = CFixedVector2D(entity_pos_t::FromInt(1),entity_pos_t::FromInt(0)); + square.v = CFixedVector2D(entity_pos_t::FromInt(0),entity_pos_t::FromInt(1)); + square.x = m_LongPath.m_Waypoints.back().x; + square.z = m_LongPath.m_Waypoints.back().z; + std::vector unitOnGoal; + // don't ignore moving units as those might be units like us, ie not really moving. + cmpObstructionManager->GetUnitsOnObstruction(square, unitOnGoal, GetObstructionFilter(), true); + if (!unitOnGoal.empty()) + m_LongPath.m_Waypoints.pop_back(); } - - // If we still have long waypoints, try and compute a short path - // This will get us around units, amongst others. - // However in some cases a long waypoint will be in located in the obstruction of - // an idle unit. In that case, we need to scrap that waypoint or we might never be able to reach it. - // I am not sure why this happens but the following code seems to work. if (!m_LongPath.m_Waypoints.empty()) { - CmpPtr cmpObstructionManager(GetSystemEntity()); - if (cmpObstructionManager) + PathGoal goal; + if (m_LongPath.m_Waypoints.size() > 1 || m_FinalGoal.DistanceToPoint(pos) > LONG_PATH_MIN_DIST) + goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z }; + else { - // create a fake obstruction to represent our waypoint. - ICmpObstructionManager::ObstructionSquare square; - square.hh = m_Clearance; - square.hw = m_Clearance; - square.u = CFixedVector2D(entity_pos_t::FromInt(1),entity_pos_t::FromInt(0)); - square.v = CFixedVector2D(entity_pos_t::FromInt(0),entity_pos_t::FromInt(1)); - square.x = m_LongPath.m_Waypoints.back().x; - square.z = m_LongPath.m_Waypoints.back().z; - std::vector unitOnGoal; - // don't ignore moving units as those might be units like us, ie not really moving. - cmpObstructionManager->GetUnitsOnObstruction(square, unitOnGoal, GetObstructionFilter(), true); - if (!unitOnGoal.empty()) - m_LongPath.m_Waypoints.pop_back(); - } - if (!m_LongPath.m_Waypoints.empty()) - { - PathGoal goal; - if (m_LongPath.m_Waypoints.size() > 1 || m_FinalGoal.DistanceToPoint(pos) > LONG_PATH_MIN_DIST) - goal = { PathGoal::POINT, m_LongPath.m_Waypoints.back().x, m_LongPath.m_Waypoints.back().z }; - else - { - UpdateFinalGoal(); - goal = m_FinalGoal; - m_LongPath.m_Waypoints.clear(); - CFixedVector2D target = goal.NearestPointOnGoal(pos); - m_LongPath.m_Waypoints.emplace_back(Waypoint{ target.X, target.Y }); - } - RequestShortPath(pos, goal, true); - m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT; - return; + UpdateFinalGoal(); + goal = m_FinalGoal; + m_LongPath.m_Waypoints.clear(); + CFixedVector2D target = goal.NearestPointOnGoal(pos); + m_LongPath.m_Waypoints.emplace_back(Waypoint{ target.X, target.Y }); } + RequestShortPath(pos, goal, true); + m_PathState = PATHSTATE_WAITING_REQUESTING_SHORT; + return; } - // Else, just entirely recompute - UpdateFinalGoal(); - BeginPathing(pos, m_FinalGoal); - - // potential TODO: We could switch the short-range pathfinder for something else entirely. - return; } + // Else, just entirely recompute + UpdateFinalGoal(); + BeginPathing(pos, m_FinalGoal); - // We successfully moved along our path, until running out of - // waypoints or time. + // potential TODO: We could switch the short-range pathfinder for something else entirely. + return; + } - if (m_PathState == PATHSTATE_FOLLOWING) + // We successfully moved along our path, until running out of + // waypoints or time. + + if (m_PathState == PATHSTATE_FOLLOWING) + { + // If we're not currently computing any new paths: + if (m_LongPath.m_Waypoints.empty() && m_ShortPath.m_Waypoints.empty()) { - // If we're not currently computing any new paths: - if (m_LongPath.m_Waypoints.empty() && m_ShortPath.m_Waypoints.empty()) - { - if (IsFormationMember()) - { - // We've reached our assigned position. If the controller - // is idle, send a notification in case it should disband, - // otherwise continue following the formation next turn. - CmpPtr cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); - if (cmpUnitMotion && !cmpUnitMotion->IsMoving()) - { - CmpPtr cmpObstruction(GetEntityHandle()); - if (cmpObstruction) - cmpObstruction->SetMovingFlag(false); - - CMessageMotionChanged msg(false); - GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); - return; - } - } - else - { - CmpPtr cmpObstructionManager(GetSystemEntity()); - CmpPtr cmpUnitMotion(GetSimContext(), m_MoveRequest.m_TargetEntity); - if (!cmpUnitMotion || cmpObstructionManager->IsInTargetRange(GetEntityId(), m_MoveRequest.m_TargetEntity, m_MoveRequest.m_TargetMinRange, m_MoveRequest.m_TargetMaxRange, false)) - { - m_State = STATE_IDLE; - MoveSucceeded(); - - if (m_FacePointAfterMove) - FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); - // TODO: if the goal was a square building, we ought to point towards the - // nearest point on the square, not towards its center - } - } - } - - // If we have a target entity, and we're not miles away from the end of - // our current path, and the target moved enough, then recompute our - // whole path if (IsFormationMember()) - CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION); + { + // We've reached our assigned position. If the controller + // is idle, send a notification in case it should disband, + // otherwise continue following the formation next turn. + CmpPtr cmpUnitMotion(GetSimContext(), m_MoveRequest.m_Entity); + if (cmpUnitMotion && !cmpUnitMotion->IsMoving()) + { + CmpPtr cmpObstruction(GetEntityHandle()); + if (cmpObstruction) + cmpObstruction->SetMovingFlag(false); + + CMessageMotionChanged msg(false); + GetSimContext().GetComponentManager().PostMessage(GetEntityId(), msg); + } + } else - CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA); + { + CmpPtr cmpObstructionManager(GetSystemEntity()); + CmpPtr cmpUnitMotion(GetSimContext(), m_MoveRequest.m_TargetEntity); + if (!cmpUnitMotion || cmpObstructionManager->IsInTargetRange(GetEntityId(), m_MoveRequest.m_TargetEntity, m_MoveRequest.m_TargetMinRange, m_MoveRequest.m_TargetMaxRange, false)) + { + m_State = STATE_IDLE; + MoveSucceeded(); + + if (m_FacePointAfterMove) + FaceTowardsPointFromPos(pos, m_FinalGoal.x, m_FinalGoal.z); + // TODO: if the goal was a square building, we ought to point towards the + // nearest point on the square, not towards its center + } + } } - } + + // If we have a target entity, and we're not miles away from the end of + // our current path, and the target moved enough, then recompute our + // whole path + if (IsFormationMember()) + CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA_FORMATION); + else + CheckTargetMovement(pos, CHECK_TARGET_MOVEMENT_MIN_DELTA); } }