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 @@ -342,11 +342,15 @@ "Order.PickupUnit": function(msg) { let cmpGarrisonHolder = Engine.QueryInterface(this.entity, IID_GarrisonHolder); - if (!cmpGarrisonHolder || cmpGarrisonHolder.IsFull()) + 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 if (this.CheckRange(this.order.data)) { @@ -355,14 +359,11 @@ } // Check if we need to move - // TODO implement a better way to know if we are on the shoreline let needToMove = true; + let range = cmpGarrisonHolder.GetLoadingRange(); let 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; + if (Engine.QueryInterface(SYSTEM_ENTITY, IID_Pathfinder).CheckNearShore(this.entity, this.order.data.target, range.max)) + needToMove = false; if (needToMove) this.SetNextState("INDIVIDUAL.PICKUP.APPROACHING"); @@ -1031,16 +1032,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) @@ -1075,12 +1066,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.SetNextState("MEMBER"); }, @@ -2850,16 +2835,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) @@ -2944,10 +2919,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; } @@ -4375,6 +4346,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_ship.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship.xml @@ -90,4 +90,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_ship_bireme.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship_bireme.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship_bireme.xml @@ -41,12 +41,7 @@ 20 - 0 - FemaleCitizen Infantry Healer Dog - Support Infantry Cavalry Dog - 0 - 10 - true + -Siege -Elephant 800 Index: binaries/data/mods/public/simulation/templates/template_unit_ship_fishing.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship_fishing.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship_fishing.xml @@ -24,12 +24,7 @@ 1 - 0 - FemaleCitizen Infantry Healer - Support Infantry - 0 - 10 - true + -Dog -Cavalry -Siege -Elephant -ConquestCritical Index: binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship_merchant.xml @@ -13,12 +13,7 @@ 15 - 0 - FemaleCitizen Infantry Healer Dog - Support Infantry Cavalry Dog - 0 - 10 - true + -Siege -Elephant 400 Index: binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship_quinquereme.xml @@ -52,12 +52,6 @@ 50 - 0 - FemaleCitizen Infantry Healer Dog - Support Infantry Cavalry Dog Siege Elephant - 0 - 10 - true 2000 Index: binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml =================================================================== --- binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml +++ binaries/data/mods/public/simulation/templates/template_unit_ship_trireme.xml @@ -41,12 +41,6 @@ 30 - 0 - FemaleCitizen Infantry Healer 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 @@ -40,6 +40,8 @@ #include "simulation2/helpers/Rasterize.h" #include "simulation2/helpers/VertexPathfinder.h" #include "simulation2/serialization/SerializeTemplates.h" +#include "simulation2/components/ICmpPosition.h" +#include "simulation2/components/ICmpUnitMotion.h" REGISTER_COMPONENT_TYPE(Pathfinder) @@ -248,6 +250,73 @@ 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(); + + 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 = m_PathfinderHier->Get(i, j, passClass); + + std::set reachable; + m_PathfinderHier->template 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 = m_PathfinderHier->Get(i, j, passClass); + if (reachable.find(landRegion) != reachable.end()) + return true; + // i,j takes on the values on the border of the square + // centered on the ship, with side width (pickupRange + clearance)*2 + 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 @@ -154,6 +154,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 @@ -81,6 +81,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 @@ -139,6 +139,12 @@ */ Grid GetConnectivityGrid(pass_class_t passClass) const; + /** + * Find all regions reachable from the given region by a unit with the specified pass class. + */ + template + void FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass) const; + pass_class_t GetPassabilityClass(const std::string& name) const { auto it = m_PassClassMasks.find(name); @@ -194,12 +200,6 @@ void UpdateGlobalRegions(const std::map >& needNewGlobalRegionMap); - /** - * Returns all reachable regions, optionally ordered in a specific manner. - */ - template - void FindReachableRegions(RegionID from, std::set& reachable, pass_class_t passClass) const; - struct SortByCenterToPoint { SortByCenterToPoint(u16 i, u16 j): gi(i), gj(j) {};