Changeset View
Changeset View
Standalone View
Standalone View
source/simulation2/components/CCmpUnitMotion.cpp
/* Copyright (C) 2017 Wildfire Games. | /* Copyright (C) 2018 Wildfire Games. | ||||
* This file is part of 0 A.D. | * This file is part of 0 A.D. | ||||
* | * | ||||
* 0 A.D. is free software: you can redistribute it and/or modify | * 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 | * it under the terms of the GNU General Public License as published by | ||||
* the Free Software Foundation, either version 2 of the License, or | * the Free Software Foundation, either version 2 of the License, or | ||||
* (at your option) any later version. | * (at your option) any later version. | ||||
* | * | ||||
* 0 A.D. is distributed in the hope that it will be useful, | * 0 A.D. is distributed in the hope that it will be useful, | ||||
▲ Show 20 Lines • Show All 479 Lines • ▼ Show 20 Lines | public: | ||||
virtual void SetDebugOverlay(bool enabled) | virtual void SetDebugOverlay(bool enabled) | ||||
{ | { | ||||
m_DebugOverlayEnabled = enabled; | m_DebugOverlayEnabled = enabled; | ||||
UpdateMessageSubscriptions(); | UpdateMessageSubscriptions(); | ||||
} | } | ||||
virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange); | virtual bool MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange); | ||||
virtual bool IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const; | |||||
virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange); | virtual bool MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange); | ||||
virtual bool IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const; | |||||
virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z); | virtual void MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z); | ||||
virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z); | virtual void FaceTowardsPoint(entity_pos_t x, entity_pos_t z); | ||||
virtual void StopMoving() | virtual void StopMoving() | ||||
{ | { | ||||
m_Moving = false; | m_Moving = false; | ||||
m_ExpectedPathTicket = 0; | m_ExpectedPathTicket = 0; | ||||
▲ Show 20 Lines • Show All 942 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::MoveToPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange, entity_id_t target) | ||||
m_FinalGoal = goal; | m_FinalGoal = goal; | ||||
m_Tries = 0; | m_Tries = 0; | ||||
BeginPathing(pos, goal); | BeginPathing(pos, goal); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::IsInPointRange(entity_pos_t x, entity_pos_t z, entity_pos_t minRange, entity_pos_t maxRange) const | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return false; | |||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
bool hasObstruction = false; | |||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); | |||||
ICmpObstructionManager::ObstructionSquare obstruction; | |||||
//TODO if (cmpObstructionManager) | |||||
// hasObstruction = cmpObstructionManager->FindMostImportantObstruction(GetObstructionFilter(), x, z, m_Radius, obstruction); | |||||
if (minRange.IsZero() && maxRange.IsZero() && hasObstruction) | |||||
{ | |||||
// Handle the non-ranged mode: | |||||
CFixedVector2D halfSize(obstruction.hw, obstruction.hh); | |||||
entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize); | |||||
// See if we're too close to the target square | |||||
if (distance < minRange) | |||||
return false; | |||||
// See if we're close enough to the target square | |||||
if (maxRange < entity_pos_t::Zero() || distance <= maxRange) | |||||
return true; | |||||
return false; | |||||
} | |||||
else | |||||
{ | |||||
entity_pos_t distance = (pos - CFixedVector2D(x, z)).Length(); | |||||
if (distance < minRange) | |||||
return false; | |||||
else if (maxRange >= entity_pos_t::Zero() && distance > maxRange) | |||||
return false; | |||||
else | |||||
return true; | |||||
} | |||||
} | |||||
bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const | bool CCmpUnitMotion::ShouldTreatTargetAsCircle(entity_pos_t range, entity_pos_t circleRadius) const | ||||
{ | { | ||||
// Given a square, plus a target range we should reach, the shape at that distance | // Given a square, plus a target range we should reach, the shape at that distance | ||||
// is a round-cornered square which we can approximate as either a circle or as a square. | // is a round-cornered square which we can approximate as either a circle or as a square. | ||||
// Previously, we used the shape that minimized the worst-case error. | // Previously, we used the shape that minimized the worst-case error. | ||||
// However that is unsage in some situations. So let's be less clever and | // However that is unsage in some situations. So let's be less clever and | ||||
// just check if our range is at least three times bigger than the circleradius | // just check if our range is at least three times bigger than the circleradius | ||||
return (range > circleRadius*3); | return (range > circleRadius*3); | ||||
▲ Show 20 Lines • Show All 154 Lines • ▼ Show 20 Lines | bool CCmpUnitMotion::MoveToTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) | ||||
m_FinalGoal = goal; | m_FinalGoal = goal; | ||||
m_Tries = 0; | m_Tries = 0; | ||||
BeginPathing(pos, goal); | BeginPathing(pos, goal); | ||||
return true; | return true; | ||||
} | } | ||||
bool CCmpUnitMotion::IsInTargetRange(entity_id_t target, entity_pos_t minRange, entity_pos_t maxRange) const | |||||
{ | |||||
// This function closely mirrors MoveToTargetRange - it needs to return true | |||||
// after that Move has completed | |||||
CmpPtr<ICmpPosition> cmpPosition(GetEntityHandle()); | |||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | |||||
return false; | |||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | |||||
CmpPtr<ICmpObstructionManager> cmpObstructionManager(GetSystemEntity()); | |||||
if (!cmpObstructionManager) | |||||
return false; | |||||
bool hasObstruction = false; | |||||
ICmpObstructionManager::ObstructionSquare obstruction; | |||||
CmpPtr<ICmpObstruction> cmpObstruction(GetSimContext(), target); | |||||
if (cmpObstruction) | |||||
hasObstruction = cmpObstruction->GetObstructionSquare(obstruction); | |||||
if (hasObstruction) | |||||
{ | |||||
CFixedVector2D halfSize(obstruction.hw, obstruction.hh); | |||||
entity_pos_t distance = Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize, true); | |||||
// Compare with previous obstruction | |||||
ICmpObstructionManager::ObstructionSquare previousObstruction; | |||||
cmpObstruction->GetPreviousObstructionSquare(previousObstruction); | |||||
entity_pos_t previousDistance = Geometry::DistanceToSquare(pos - CFixedVector2D(previousObstruction.x, previousObstruction.z), obstruction.u, obstruction.v, halfSize, true); | |||||
// See if we're too close to the target square | |||||
bool inside = distance.IsZero() && !Geometry::DistanceToSquare(pos - CFixedVector2D(obstruction.x, obstruction.z), obstruction.u, obstruction.v, halfSize).IsZero(); | |||||
if ((distance < minRange && previousDistance < minRange) || inside) | |||||
return false; | |||||
// See if we're close enough to the target square | |||||
if (maxRange < entity_pos_t::Zero() || distance <= maxRange || previousDistance <= maxRange) | |||||
return true; | |||||
entity_pos_t circleRadius = halfSize.Length(); | |||||
if (ShouldTreatTargetAsCircle(maxRange, circleRadius)) | |||||
{ | |||||
// The target is small relative to our range, so pretend it's a circle | |||||
// and see if we're close enough to that. | |||||
// Also check circle around previous position. | |||||
entity_pos_t circleDistance = (pos - CFixedVector2D(obstruction.x, obstruction.z)).Length() - circleRadius; | |||||
entity_pos_t previousCircleDistance = (pos - CFixedVector2D(previousObstruction.x, previousObstruction.z)).Length() - circleRadius; | |||||
return circleDistance <= maxRange || previousCircleDistance <= maxRange; | |||||
} | |||||
// take minimal clearance required in MoveToTargetRange into account, multiplying by 3/2 for diagonals | |||||
entity_pos_t maxDist = std::max(maxRange, (m_Clearance + entity_pos_t::FromInt(TERRAIN_TILE_SIZE)/16)*3/2); | |||||
return distance <= maxDist || previousDistance <= maxDist; | |||||
} | |||||
else | |||||
{ | |||||
CmpPtr<ICmpPosition> cmpTargetPosition(GetSimContext(), target); | |||||
if (!cmpTargetPosition || !cmpTargetPosition->IsInWorld()) | |||||
return false; | |||||
CFixedVector2D targetPos = cmpTargetPosition->GetPreviousPosition2D(); | |||||
entity_pos_t distance = (pos - targetPos).Length(); | |||||
return minRange <= distance && (maxRange < entity_pos_t::Zero() || distance <= maxRange); | |||||
} | |||||
} | |||||
void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) | void CCmpUnitMotion::MoveToFormationOffset(entity_id_t target, entity_pos_t x, entity_pos_t z) | ||||
{ | { | ||||
CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | CmpPtr<ICmpPosition> cmpPosition(GetSimContext(), target); | ||||
if (!cmpPosition || !cmpPosition->IsInWorld()) | if (!cmpPosition || !cmpPosition->IsInWorld()) | ||||
return; | return; | ||||
CFixedVector2D pos = cmpPosition->GetPosition2D(); | CFixedVector2D pos = cmpPosition->GetPosition2D(); | ||||
▲ Show 20 Lines • Show All 63 Lines • Show Last 20 Lines |
Wildfire Games · Phabricator