Index: binaries/data/mods/public/simulation/components/UnitAI.js
===================================================================
--- binaries/data/mods/public/simulation/components/UnitAI.js
+++ binaries/data/mods/public/simulation/components/UnitAI.js
@@ -349,23 +349,22 @@
},
"Order.PickupUnit": function(msg) {
- var cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
- if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull())
+ let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder);
+ let cmpTimer = Engine.QueryInterface(SYSTEM_ENTITY, IID_Timer);
+
+ if (!cmpTimer || !cmpGarrisonHolder || cmpGarrisonHolder.IsFull()
+ || this.lastPickupTime && this.lastPickupTime + 100 > cmpTimer.GetTime())
{
this.FinishOrder();
return;
}
+ this.lastPickupTime = cmpTimer.GetTime(); // for performance, don't repeat pickup for each unit
- // Check if we need to move TODO implement a better way to know if we are on the shoreline
- var needToMove = true;
- var cmpPosition = Engine.QueryInterface(this.entity, IID_Position);
- if (this.lastShorelinePosition && cmpPosition && (this.lastShorelinePosition.x == cmpPosition.GetPosition().x)
- && (this.lastShorelinePosition.z == cmpPosition.GetPosition().z))
- {
- // we were already on the shoreline, and have not moved since
- if (DistanceBetweenEntities(this.entity, this.order.data.target) < 50)
- needToMove = false;
- }
+ // Check if we need to move
+ let range = cmpGarrisonHolder.GetLoadingRange();
+ let needToMove = true;
+ if (Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder).CheckNearShore(this.entity, this.order.data.target, range.max))
+ needToMove = false;
// TODO: what if the units are on a cliff ? the ship will go below the cliff
// and the units won't be able to garrison. Should go to the nearest (accessible) shore
@@ -1159,16 +1158,6 @@
},
"GARRISON":{
- "enter": function() {
- // If the garrisonholder should pickup, warn it so it can take needed action
- var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
- if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
- {
- this.pickup = this.order.data.target; // temporary, deleted in "leave"
- Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
- }
- },
-
"leave": function() {
// If a pickup has been requested and not yet canceled, cancel it
if (this.pickup)
@@ -1193,12 +1182,6 @@
"GARRISONING": {
"enter": function() {
- // If a pickup has been requested, cancel it as it will be requested by members
- if (this.pickup)
- {
- Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
- delete this.pickup;
- }
this.CallMemberFunction("Garrison", [this.order.data.target, false]);
this.SetNextStateAlwaysEntering("MEMBER");
},
@@ -2925,16 +2908,6 @@
},
"GARRISON": {
- "enter": function() {
- // If the garrisonholder should pickup, warn it so it can take needed action
- var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder);
- if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
- {
- this.pickup = this.order.data.target; // temporary, deleted in "leave"
- Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
- }
- },
-
"leave": function() {
// If a pickup has been requested and not yet canceled, cancel it
if (this.pickup)
@@ -3035,10 +3008,6 @@
// If a pickup has been requested, remove it
if (this.pickup)
{
- var cmpHolderPosition = Engine.QueryInterface(target, IID_Position);
- var cmpHolderUnitAI = Engine.QueryInterface(target, IID_UnitAI);
- if (cmpHolderUnitAI && cmpHolderPosition)
- cmpHolderUnitAI.lastShorelinePosition = cmpHolderPosition.GetPosition();
Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
delete this.pickup;
}
@@ -4478,6 +4447,19 @@
return false;
var range = cmpGarrisonHolder.GetLoadingRange();
+ // If a pickup has been requested earlier, cancel it
+ if (this.pickup)
+ {
+ Engine.PostMessage(this.pickup, MT_PickupCanceled, { "entity": this.entity });
+ delete this.pickup;
+ }
+
+ if (cmpGarrisonHolder && cmpGarrisonHolder.CanPickup(this.entity))
+ {
+ this.pickup = this.order.data.target; // temporary, deleted in "leave"
+ Engine.PostMessage(this.pickup, MT_PickupRequested, { "entity": this.entity });
+ }
+
var cmpUnitMotion = Engine.QueryInterface(this.entity, IID_UnitMotion);
return cmpUnitMotion.MoveToTargetRange(target, range.min, range.max);
};
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship.xml
@@ -70,4 +70,13 @@
90
+
+ 1
+ 0
+ FemaleCitizen Infantry Dog
+ Support Infantry Cavalry Dog Siege Elephant
+ 0
+ true
+ 15
+
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_bireme.xml
@@ -34,12 +34,7 @@
20
- 0
- FemaleCitizen Infantry Dog
- Support Infantry Cavalry Dog
- 0
- 10
- true
+ -Siege -Elephant
800
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_fishing.xml
@@ -21,13 +21,7 @@
6.0
- 1
- 0
- FemaleCitizen Infantry
- Support Infantry
- 0
- 10
- true
+ -Dog -Cavalry -Siege -Elephant
Fishing Boat
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_merchant.xml
@@ -13,12 +13,7 @@
15
- 0
- FemaleCitizen Infantry Dog
- Support Infantry Cavalry Dog
- 0
- 10
- true
+ -Siege -Elephant
400
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_quinquereme.xml
@@ -43,12 +43,6 @@
50
- 0
- FemaleCitizen Infantry Dog
- Support Infantry Cavalry Dog Siege Elephant
- 0
- 10
- true
2000
Index: binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
===================================================================
--- binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
+++ binaries/data/mods/public/simulation/templates/template_unit_mechanical_ship_trireme.xml
@@ -34,12 +34,6 @@
30
- 0
- FemaleCitizen Infantry Dog
- Support Infantry Cavalry Dog Siege Elephant
- 0
- 10
- true
1400
Index: source/simulation2/components/CCmpPathfinder.cpp
===================================================================
--- source/simulation2/components/CCmpPathfinder.cpp
+++ source/simulation2/components/CCmpPathfinder.cpp
@@ -36,6 +36,8 @@
#include "simulation2/components/ICmpWaterManager.h"
#include "simulation2/helpers/Rasterize.h"
#include "simulation2/serialization/SerializeTemplates.h"
+#include "simulation2/components/ICmpPosition.h"
+#include "simulation2/components/ICmpUnitMotion.h"
REGISTER_COMPONENT_TYPE(Pathfinder)
@@ -215,6 +217,74 @@
return it->second;
}
+/**
+ * Assuming we're a ship, check if we're near the shore.
+ */
+bool CCmpPathfinder::CheckNearShore(entity_id_t ship, entity_id_t landUnit, fixed pickupRange) const
+{
+ if (pickupRange == fixed::FromInt(0))
+ return false;
+
+ CmpPtr cmpPositionShip(GetSimContext(), ship);
+ if (!cmpPositionShip || !cmpPositionShip->IsInWorld())
+ return false;
+
+ CmpPtr cmpPositionLand(GetSimContext(), landUnit);
+ if (!cmpPositionLand || !cmpPositionLand->IsInWorld())
+ return false;
+
+ CmpPtr cmpUnitMotionLand(GetSimContext(), landUnit);
+ if (!cmpUnitMotionLand)
+ return false;
+
+ CmpPtr cmpUnitMotionShip(GetSimContext(), ship);
+ if (!cmpUnitMotionShip)
+ return false;
+ fixed clearance = cmpUnitMotionShip->GetUnitClearance();
+
+ HierarchicalPathfinder hier = m_LongPathfinder.GetHierarchicalPathfinder();
+
+ CFixedVector2D posShip = cmpPositionShip->GetPosition2D();
+ CFixedVector2D posLand = cmpPositionLand->GetPosition2D();
+ u16 i, j;
+ Pathfinding::NearestNavcell(posLand.X, posLand.Y, i, j, m_TerrainOnlyGrid->m_W, m_TerrainOnlyGrid->m_H);
+ pass_class_t passClass = cmpUnitMotionLand->GetPassabilityClass();
+ HierarchicalPathfinder::RegionID landRegion = hier.Get(i, j, passClass);
+
+ std::unordered_set reachable;
+ hier.FindReachableRegions(landRegion, reachable, passClass);
+ u16 i0, j0, i1, j1; // bounds of square to search for land
+ Pathfinding::NearestNavcell(posShip.X, posShip.Y, i, j, m_TerrainOnlyGrid->m_W, m_TerrainOnlyGrid->m_H);
+ i16 hw = ((pickupRange + clearance) / Pathfinding::NAVCELL_SIZE).ToInt_RoundToInfinity();
+ i0 = i-hw;
+ i1 = i+hw;
+ j0 = j-hw;
+ j1 = j+hw;
+
+ i = i0;
+ j = j0;
+ u16 offset = j1-j0;
+ while (true)
+ {
+ landRegion = hier.Get(i, j, passClass);
+ if (reachable.find(landRegion) != reachable.end())
+ return true;
+ // i,j takes on the values on the border of the square
+ if (i == i0 || i == i1)
+ j++;
+ else
+ j += offset;
+ if (j > j1)
+ {
+ i++;
+ j=j0;
+ }
+ if (i > i1)
+ break;
+ }
+ return false;
+}
+
void CCmpPathfinder::GetPassabilityClasses(std::map& passClasses) const
{
passClasses = m_PassClassMasks;
Index: source/simulation2/components/CCmpPathfinder_Common.h
===================================================================
--- source/simulation2/components/CCmpPathfinder_Common.h
+++ source/simulation2/components/CCmpPathfinder_Common.h
@@ -204,6 +204,8 @@
virtual pass_class_t GetPassabilityClass(const std::string& name) const;
+ virtual bool CheckNearShore(entity_id_t ship, entity_id_t landUnit, fixed pickupRange) const;
+
virtual void GetPassabilityClasses(std::map& passClasses) const;
virtual void GetPassabilityClasses(
std::map& nonPathfindingPassClasses,
Index: source/simulation2/components/ICmpPathfinder.h
===================================================================
--- source/simulation2/components/ICmpPathfinder.h
+++ source/simulation2/components/ICmpPathfinder.h
@@ -68,6 +68,11 @@
*/
virtual pass_class_t GetPassabilityClass(const std::string& name) const = 0;
+ /**
+ * Assuming the entity is a ship, returns true if near shore.
+ */
+ virtual bool CheckNearShore(entity_id_t ship, entity_id_t landUnit, fixed pickupRange) const = 0;
+
virtual entity_pos_t GetClearance(pass_class_t passClass) const = 0;
/**
Index: source/simulation2/components/ICmpPathfinder.cpp
===================================================================
--- source/simulation2/components/ICmpPathfinder.cpp
+++ source/simulation2/components/ICmpPathfinder.cpp
@@ -25,4 +25,5 @@
DEFINE_INTERFACE_METHOD_1("SetDebugOverlay", void, ICmpPathfinder, SetDebugOverlay, bool)
DEFINE_INTERFACE_METHOD_1("SetHierDebugOverlay", void, ICmpPathfinder, SetHierDebugOverlay, bool)
DEFINE_INTERFACE_METHOD_CONST_1("GetPassabilityClass", pass_class_t, ICmpPathfinder, GetPassabilityClass, std::string)
+DEFINE_INTERFACE_METHOD_CONST_3("CheckNearShore", bool, ICmpPathfinder, CheckNearShore, entity_id_t, entity_id_t, fixed)
END_INTERFACE_WRAPPER(Pathfinder)
Index: source/simulation2/helpers/HierarchicalPathfinder.h
===================================================================
--- source/simulation2/helpers/HierarchicalPathfinder.h
+++ source/simulation2/helpers/HierarchicalPathfinder.h
@@ -24,6 +24,8 @@
#include "Render.h"
#include "graphics/SColor.h"
+#include
+
/**
* Hierarchical pathfinder.
*
@@ -74,6 +76,21 @@
}
};
+ struct RegionHash
+ {
+ inline size_t operator()(const RegionID& region) const
+ {
+ // Fowler-Noll-Vo hash function
+ const unsigned char* p = reinterpret_cast(®ion);
+ size_t h = 2166136261;
+
+ for (unsigned int i = 0; i < sizeof(region); ++i)
+ h = (h * 16777619) ^ p[i];
+
+ return h;
+ }
+ };
+
HierarchicalPathfinder();
~HierarchicalPathfinder();
@@ -113,6 +130,8 @@
*/
Grid GetConnectivityGrid(pass_class_t passClass);
+ void FindReachableRegions(RegionID from, std::unordered_set& reachable, pass_class_t passClass);
+
pass_class_t GetPassabilityClass(const std::string& name) const
{
auto it = m_PassClassMasks.find(name);
@@ -150,16 +169,14 @@
void FindEdges(u8 ci, u8 cj, pass_class_t passClass, EdgesMap& edges);
- void FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass);
-
- void FindPassableRegions(std::set& regions, pass_class_t passClass);
+ void FindPassableRegions(std::unordered_set& regions, pass_class_t passClass);
/**
* Updates @p iGoal and @p jGoal to the navcell that is the nearest to the
* initial goal coordinates, in one of the given @p regions.
* (Assumes @p regions is non-empty.)
*/
- void FindNearestNavcellInRegions(const std::set& regions, u16& iGoal, u16& jGoal, pass_class_t passClass);
+ void FindNearestNavcellInRegions(const std::unordered_set& regions, u16& iGoal, u16& jGoal, pass_class_t passClass);
Chunk& GetChunk(u8 ci, u8 cj, pass_class_t passClass)
{
Index: source/simulation2/helpers/HierarchicalPathfinder.cpp
===================================================================
--- source/simulation2/helpers/HierarchicalPathfinder.cpp
+++ source/simulation2/helpers/HierarchicalPathfinder.cpp
@@ -577,7 +577,7 @@
RegionID source = Get(i0, j0, passClass);
// Find everywhere that's reachable
- std::set reachableRegions;
+ std::unordered_set reachableRegions;
FindReachableRegions(source, reachableRegions, passClass);
// Check whether any reachable region contains the goal
@@ -642,12 +642,12 @@
void HierarchicalPathfinder::FindNearestPassableNavcell(u16& i, u16& j, pass_class_t passClass)
{
- std::set regions;
+ std::unordered_set regions;
FindPassableRegions(regions, passClass);
FindNearestNavcellInRegions(regions, i, j, passClass);
}
-void HierarchicalPathfinder::FindNearestNavcellInRegions(const std::set& regions, u16& iGoal, u16& jGoal, pass_class_t passClass)
+void HierarchicalPathfinder::FindNearestNavcellInRegions(const std::unordered_set& regions, u16& iGoal, u16& jGoal, pass_class_t passClass)
{
// Find the navcell in the given regions that's nearest to the goal navcell:
// * For each region, record the (squared) minimal distance to the goal point
@@ -704,7 +704,7 @@
jGoal = jBest;
}
-void HierarchicalPathfinder::FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass)
+void HierarchicalPathfinder::FindReachableRegions(RegionID from, std::unordered_set& reachable, pass_class_t passClass)
{
// Flood-fill the region graph, starting at 'from',
// collecting all the regions that are reachable via edges
@@ -726,7 +726,7 @@
}
}
-void HierarchicalPathfinder::FindPassableRegions(std::set& regions, pass_class_t passClass)
+void HierarchicalPathfinder::FindPassableRegions(std::unordered_set& regions, pass_class_t passClass)
{
// Construct a set of all regions of all chunks for this pass class
for (const Chunk& chunk : m_Chunks[passClass])
@@ -770,7 +770,7 @@
if (from.r == 0)
continue;
- std::set reachable;
+ std::unordered_set reachable;
FindReachableRegions(from, reachable, passClass);
for (const RegionID& region : reachable)
Index: source/simulation2/helpers/LongPathfinder.h
===================================================================
--- source/simulation2/helpers/LongPathfinder.h
+++ source/simulation2/helpers/LongPathfinder.h
@@ -244,6 +244,11 @@
return m_PathfinderHier.GetConnectivityGrid(passClass);
}
+ const HierarchicalPathfinder& GetHierarchicalPathfinder() const
+ {
+ return m_PathfinderHier;
+ }
+
void GetDebugData(u32& steps, double& time, Grid& grid) const
{
GetDebugDataJPS(steps, time, grid);