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,20 @@ return false; var range = cmpGarrisonHolder.GetLoadingRange(); + var cmpGarrisonHolder = Engine.QueryInterface(this.order.data.target, IID_GarrisonHolder); + // 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::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 @@ -113,6 +113,8 @@ */ Grid GetConnectivityGrid(pass_class_t passClass); + void FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass); + pass_class_t GetPassabilityClass(const std::string& name) const { auto it = m_PassClassMasks.find(name); @@ -150,8 +152,6 @@ 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); /** 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);