Desperate attempt to improve pawn overwatch waypoint decision making.

For some reason in my test mission nobody goes to the other half of one of the
AOIs even though it seems that there is really good visibility there.
M ineptifex/cfgfunctions.ext +4 -2
@@ 64,17 64,18 @@ class INEPT
         class internalAddContactReports {};
         class addGroup {};
         class ageAndPruneContacts {};
+        class aoiFromContacts {};
         class contactAOIs {};
-        class aoiFromContacts {};
         class delegateAOIs {};
         class generateAOIs {};
+        class hasPawns {};
         class newAO {};
         class newAOFromEditor {};
         class processAOForever {};
         class processAOOnce {};
         class prunePawns {};
-        class hasPawns {};
         class rebuildContactIntel {};
+        class hasVision {};
     };
 
     class inept_contact

          
@@ 109,6 110,7 @@ class INEPT
         class assignAOI {};
         class configurePawnWaypoint {};
         class findWaypointByName {};
+        class currentPawnDestination {};
         class getPawnFirepower {};
         class getPawnPos {};
         class processPawnOnce {};

          
M ineptifex/fn_assignAOI.sqf +2 -2
@@ 33,7 33,7 @@ VAR(_new_logic) = [_new_aoi] call INEPT_
 VAR(_logic_is_changing) = !([_new_logic, _curr_logic] call INEPT_fnc_isSameInstance);
 if (_logic_is_changing) then
 {
-    [_pawn] call (_curr_logic select BEHAVIOR_LOGIC_UNINSTALL);
+    [_ao, _pawn] call (_curr_logic select BEHAVIOR_LOGIC_UNINSTALL);
     _pawn set [PAWN_BEHAVIOR_STATE, NULL_BEHAVIOR_STATE()];
 };
 

          
@@ 41,7 41,7 @@ if (_logic_is_changing) then
 
 if (_logic_is_changing) then
 {
-    [_pawn] call (_new_logic select BEHAVIOR_LOGIC_INSTALL);
+    [_ao, _pawn] call (_new_logic select BEHAVIOR_LOGIC_INSTALL);
 };
 
 MUTEX_UNLOCK(_pawn select PAWN_MUTEX);

          
M ineptifex/fn_behaviorAssaultInstall.sqf +2 -2
@@ 8,6 8,6 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
-VAR(_grp) = _pawn select PAWN_GROUP;
+params ["_ao", "_pawn"];
+private _grp = _pawn select PAWN_GROUP;
 (leader _grp) setBehaviour "AWARE";

          
M ineptifex/fn_behaviorAssaultTick.sqf +17 -17
@@ 11,23 11,23 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
+params ["_ao", "_pawn"];
 LOGVAR(_pawn);
 
-VAR(_aoi) = _pawn select PAWN_AOI;
+private _aoi = _pawn select PAWN_AOI;
 LOGVAR(_aoi);
 
-//VAR(_state) = _pawn select PAWN_BEHAVIOR_STATE;
+//private _state = _pawn select PAWN_BEHAVIOR_STATE;
 //_state BEHAVIOR_STATE_
 
-VAR(_grp) = _pawn select PAWN_GROUP;
+private _grp = _pawn select PAWN_GROUP;
 LOGVAR(_grp);
 
-VAR(_wp) = NULL_WP();
-VAR(_wps) = waypoints _grp;
-VAR(_is_idle) = (currentWaypoint _grp) == (count _wps);
-VAR(_reconfigure_wp) = _is_idle;
-VAR(_is_on_task) = false;
+private _wp = NULL_WP();
+private _wps = waypoints _grp;
+private _is_idle = (currentWaypoint _grp) == (count _wps);
+private _reconfigure_wp = _is_idle;
+private _is_on_task = false;
 
 if !(_is_idle) then
 {

          
@@ 63,7 63,7 @@ if !(_is_idle) then
     };
 };
 
-VAR(_pawn_pos) = [_pawn] call INEPT_fnc_getPawnPos;
+private _pawn_pos = [_pawn] call INEPT_fnc_getPawnPos;
 
 if (_reconfigure_wp) then
 {

          
@@ 75,11 75,11 @@ if (_reconfigure_wp) then
         _wp = _grp addWaypoint [_pawn_pos, 0];
     };
 
-    VAR(_is_in_aoi) = [_aoi, _pawn_pos, WP_SUBDIVISION_RADIUS + 2 * FLANK_RADIUS] call INEPT_fnc_isPosNearAOI;
+    private _is_in_aoi = [_aoi, _pawn_pos, WP_SUBDIVISION_RADIUS + 2 * FLANK_RADIUS] call INEPT_fnc_isPosNearAOI;
 
     if (_is_in_aoi) then
     {
-        if ([_aoi, _pawn, _wp, WP_TYPE_ASSAULT] call INEPT_fnc_configurePawnWaypoint) then
+        if ([_ao, _aoi, _pawn, _wp, WP_TYPE_ASSAULT] call INEPT_fnc_configurePawnWaypoint) then
         {
             _wp setWaypointType "sad"; // Search & Destroy
             _wp setWaypointName ASSAULT_WP_NAME;

          
@@ 89,7 89,7 @@ if (_reconfigure_wp) then
     }
     else
     {
-        if ([_aoi, _pawn, _wp, WP_TYPE_FLANK] call INEPT_fnc_configurePawnWaypoint) then
+        if ([_ao, _aoi, _pawn, _wp, WP_TYPE_FLANK] call INEPT_fnc_configurePawnWaypoint) then
         {
             _wp setWaypointName FLANK_WP_NAME;
             _grp setCurrentWaypoint _wp;

          
@@ 111,13 111,13 @@ if (_is_on_task) then
 {
     // Set behaviour
 
-    VAR(_is_in_combat) = [_grp] call INEPT_fnc_isGroupInCombat;
+    private _is_in_combat = [_grp] call INEPT_fnc_isGroupInCombat;
     LOGVAR(_is_in_combat);
-    //VAR(_speed) = if (_is_in_combat) then {"NORMAL"} else {"FULL"};
-    VAR(_speed) = "NORMAL";
+    //private _speed = if (_is_in_combat) then {"NORMAL"} else {"FULL"};
+    private _speed = "NORMAL";
     LOGVAR(_speed);
 
-    VAR(_is_urban) = [getPosATL leader _grp] call INEPT_fnc_isPosUrban;
+    private _is_urban = [getPosATL leader _grp] call INEPT_fnc_isPosUrban;
     LOGVAR(_is_urban);
 
     _grp setSpeedMode (_speed);

          
M ineptifex/fn_behaviorAssaultUninstall.sqf +2 -2
@@ 8,8 8,8 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
-VAR(_wp) = [_pawn, ASSAULT_WP_NAME] call INEPT_fnc_findWaypointByName;
+params ["_ao", "_pawn"];
+private _wp = [_pawn, ASSAULT_WP_NAME] call INEPT_fnc_findWaypointByName;
 if !(_wp isEqualTo NULL_WP()) then
 {
     deleteWaypoint _wp;

          
M ineptifex/fn_behaviorDefendInstall.sqf +2 -2
@@ 8,7 8,7 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
+params ["_ao", "_pawn"];
 _pawn set [PAWN_BEHAVIOR_STATE, NEW_DEFEND_BEHAVIOR_STATE()];
-VAR(_grp) = _pawn select PAWN_GROUP;
+private _grp = _pawn select PAWN_GROUP;
 (leader _grp) setBehaviour "SAFE";

          
M ineptifex/fn_behaviorDefendTick.sqf +14 -14
@@ 8,15 8,15 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
+params ["_ao", "_pawn"];
 LOGVAR(_pawn);
 
-VAR(_aoi) = _pawn select PAWN_AOI;
+private _aoi = _pawn select PAWN_AOI;
 LOGVAR(_aoi);
 
-VAR(_state) = _pawn select PAWN_BEHAVIOR_STATE;
+private _state = _pawn select PAWN_BEHAVIOR_STATE;
 
-VAR(_wp) = [_pawn, DEFEND_WP_NAME] call INEPT_fnc_findWaypointByName;
+private _wp = [_pawn, DEFEND_WP_NAME] call INEPT_fnc_findWaypointByName;
 LOGVAR(_wp);
 
 if !(_wp isEqualTo NULL_WP()) then

          
@@ 30,14 30,14 @@ if !(_wp isEqualTo NULL_WP()) then
     };
 };
 
-VAR(_grp) = _pawn select PAWN_GROUP;
+private _grp = _pawn select PAWN_GROUP;
 LOGVAR(_grp);
 
 // refactor INEPT_fnc_isPawnIdle
-VAR(_is_idle) = (currentWaypoint _grp) == (count waypoints _grp);
+private _is_idle = (currentWaypoint _grp) == (count waypoints _grp);
 LOGVAR(_is_idle);
 // refactor INEPT_fnc_isPawnOnTask
-VAR(_is_on_task) = if (_is_idle or {_wp isEqualTo NULL_WP()}) then {false}
+private _is_on_task = if (_is_idle or {_wp isEqualTo NULL_WP()}) then {false}
                    else {(currentWaypoint _grp) == (_wp select BIS_WP_INDEX)};
 LOGVAR(_is_on_task);
 // refactor INEPT_fnc_something

          
@@ 53,7 53,7 @@ if (
     _wp = NULL_WP();
 };
 
-VAR(_pawn_pos) = [_pawn] call INEPT_fnc_getPawnPos;
+private _pawn_pos = [_pawn] call INEPT_fnc_getPawnPos;
 
 // Check if we need to create a new waypoint
 if (_is_idle) then

          
@@ 63,7 63,7 @@ if (_is_idle) then
 
     // Do some sort of defending related thing
     // TODO, determine this based on our vehicle composition
-    VAR(_wp) = _grp addWaypoint [_pawn_pos, 0];
+    private _wp = _grp addWaypoint [_pawn_pos, 0];
     switch (true) do
     {
     case (

          
@@ 71,7 71,7 @@ if (_is_idle) then
         // Don't garrison twice in a row
         and {!((_state select DEFEND_BEHAVIOR_STATE_LAST_WP_TYPE) isEqualTo WP_TYPE_GARRISON)}
         and {[_aoi, _pawn_pos, WP_SUBDIVISION_RADIUS] call INEPT_fnc_isPosNearAOI}
-        and {[_aoi, _pawn, _wp, WP_TYPE_GARRISON] call INEPT_fnc_configurePawnWaypoint}
+        and {[_ao, _aoi, _pawn, _wp, WP_TYPE_GARRISON] call INEPT_fnc_configurePawnWaypoint}
         ):
     {
         _wp setWaypointTimeout [30, 200, 300];

          
@@ 80,7 80,7 @@ if (_is_idle) then
         _is_on_task = true;
         _state set [DEFEND_BEHAVIOR_STATE_LAST_WP_TYPE, WP_TYPE_GARRISON];
     };
-    case ([_aoi, _pawn, _wp, WP_TYPE_OVERWATCH] call INEPT_fnc_configurePawnWaypoint):
+    case ([_ao, _aoi, _pawn, _wp, WP_TYPE_OVERWATCH] call INEPT_fnc_configurePawnWaypoint):
     {
         _wp setWaypointTimeout [0, 15, 45];
         _wp setWaypointName DEFEND_WP_NAME;

          
@@ 104,10 104,10 @@ if (_is_on_task) then
 {
     // Set behaviour
 
-    VAR(_is_in_combat) = [_grp] call INEPT_fnc_isGroupInCombat;
+    private _is_in_combat = [_grp] call INEPT_fnc_isGroupInCombat;
     LOGVAR(_is_in_combat);
 
-    VAR(_speed) = switch (true) do
+    private _speed = switch (true) do
         {
         case _is_in_combat:
         {

          
@@ 124,7 124,7 @@ if (_is_on_task) then
         };
     LOGVAR(_speed);
 
-    VAR(_is_urban) = [getPosATL leader _grp] call INEPT_fnc_isPosUrban;
+    private _is_urban = [getPosATL leader _grp] call INEPT_fnc_isPosUrban;
     LOGVAR(_is_urban);
 
     _grp setSpeedMode (_speed);

          
M ineptifex/fn_behaviorDefendUninstall.sqf +2 -2
@@ 8,8 8,8 @@ 
 
 LOGENTRY();
 
-VAR(_pawn) = ARGUMENT_0;
-VAR(_wp) = [_pawn, DEFEND_WP_NAME] call INEPT_fnc_findWaypointByName;
+params ["_ao", "_pawn"];
+private _wp = [_pawn, DEFEND_WP_NAME] call INEPT_fnc_findWaypointByName;
 if !(_wp isEqualTo NULL_WP()) then
 {
     deleteWaypoint _wp;

          
M ineptifex/fn_configurePawnWaypoint.sqf +31 -65
@@ 1,10 1,11 @@ 
 /* INEPT_fnc_configurePawnWaypoint
  *
  * Parameters:
- *  0: An AOI instance
- *  1: A pawn instance
- *  2: A waypoint
- *  3: A waypoint type, see WP_TYPE_* in data.h
+ *  0: An AO instance
+ *  1: An AOI instance
+ *  2: A pawn instance
+ *  3: A waypoint
+ *  4: A waypoint type, see WP_TYPE_* in data.h
  *
  * Specification:
  *  Configures the passed waypoint, setting the position, type, completion

          
@@ 24,15 25,7 @@ 
 
 LOGENTRY();
 
-VAR(_aoi) = ARGUMENT_0;
-VAR(_pawn) = ARGUMENT_1;
-VAR(_wp) = ARGUMENT_2;
-VAR(_wp_type) = ARGUMENT_3;
-
-LOGVAR(_aoi);
-LOGVAR(_pawn);
-LOGVAR(_wp);
-LOGVAR(_wp_type);
+params ["_ao", "_aoi", "_pawn", "_wp", "_wp_type"];
 
 VAR(_grp) = _pawn select PAWN_GROUP;
 LOGVAR(_grp);

          
@@ 200,60 193,33 @@ case WP_TYPE_OVERWATCH:
         _aoi select AOI_POS,
         _aoi select AOI_DISPERSION
         );
-    // TODO, we are using a magic number, give it a name
-    if (_aoi select AOI_DISPERSION > 250) then
+
+    private _num_samples = ceil (((_aoi select AOI_DISPERSION) ^ 0.70) / 8);
+    private _samples = [];
+    while {_num_samples = _num_samples - 1; _num_samples >= 0} do
     {
-        // TODO - It's probably better if we just avoid picking spots that
-        // other people are going to....
-        //
-        // We do this because it helps even things out. If our AOI is pretty
-        // big and meant to cover a fairly large region, but there is a
-        // relatively small area that has very high quality observation points,
-        // the tendency will be for subdivisions in that fruitful area to
-        // always be picked, which results in the rest of the AOI not being
-        // covered. This tries to eliminate some of that bias and even things
-        // out a bit more.
-        //VAR(_areas) = [_area, 150] call INEPT_fnc_subdivide;
-        private _origin = [_aoi select AOI_POS, (_aoi select AOI_DISPERSION) - 250] call INEPT_fnc_randomPointInCircle;
-        _area = NEW_CIRCLE(_origin, 250);
-        LOGMSG("Shits too big, picked a smaller one");
-        LOGVAR(_area);
+        _samples pushBack
+            ([_aoi select AOI_POS, _aoi select AOI_DISPERSION]
+                call INEPT_fnc_randomPointInCircle);
     };
-    VAR(_subareas) = [_area, WP_SUBDIVISION_RADIUS] call INEPT_fnc_subdivide;
-    // Wiggle the positions a little bit, note that this does not produce a
-    // uniform distribution
-    [_subareas, {[_x, random WP_SUBDIVISION_RADIUS] call INEPT_fnc_deviatedVector}
-    ] call INEPT_fnc_applyToAllIP;
-    // Only consider areas above sea level
-    [_subareas, {getTerrainHeightASL _x > 0}] call INEPT_fnc_conditionalSelectIP;
-    // And we throw some randomness into it just to make shit interesting
-    [_subareas, 0.3] call INEPT_fnc_selectFractionAtRandomIP;
-    LOGTICK();
-    VAR(_dest) = if (_subareas isEqualTo []) then
-        {
-            _aoi select AOI_POS
-        }
-        else
-        {
-            LOGVAR(_subareas);
-            _subareas select (
-                [
-                    _subareas,
-                    {
-                        // This is goofy since INEPT_fnc_visibilityQuality does
-                        // not consider buildings, only terrain
-                        [
-                            [
-                                _x select 0,
-                                _x select 1,
-                                getTerrainHeightASL _x
-                            ]
-                        ] call INEPT_fnc_visibilityQuality
-                    }
-                ] call INEPT_fnc_indexOfBest
-                )
-        };
-    LOGTICK();
+    [_samples, {getTerrainHeightASL _x > 0}]
+        call INEPT_fnc_conditionalSelectIP;
+    LOGVAR(_samples);
+    private _idx =
+        [_samples, {
+            private _asl_pos = [_x select 0, _x select 1, getTerrainHeightASL _x];
+            LOGVAR(_asl_pos);
+            private _quality = [_asl_pos] call INEPT_fnc_visibilityQuality;
+            private _vision = 1 + ([_ao, _asl_pos, _pawn] call INEPT_fnc_hasVision);
+            LOGVAR(_quality);
+            LOGVAR(_vision);
+            (_quality / _vision)
+        }] call INEPT_fnc_indexOfBest;
+
+    LOGVAR(_idx);
+    if (_idx == -1) exitWith {};
+
+    private _dest = _samples select _idx;
 
     LOGVAR(_dest);
     _wp setWpPos _dest;

          
A => ineptifex/fn_currentPawnDestination.sqf +15 -0
@@ 0,0 1,15 @@ 
+#include "common\defines.h"
+#include "data.h"
+#include "behaviordata.h"
+
+params ["_pawn"];
+private _grp = _pawn select PAWN_GROUP;
+private _wps = waypoints _grp;
+if ((currentWaypoint _grp) >= (count _wps)) then
+{
+    [_pawn] call INEPT_fnc_getPawnPos
+}
+else
+{
+    getWPPos (_wps select (currentWaypoint _grp))
+}

          
A => ineptifex/fn_hasVision.sqf +44 -0
@@ 0,0 1,44 @@ 
+/* INEPT_fnc_hasVision
+ *
+ * Parameters:
+ *  0: An ASL position
+ *
+ * Returns:
+ *  Number of pawns that are moving to a place where they will have vision on
+ *  the given position.
+ */
+
+//#define DEBUG_VAR "INEPT_fnc_hasVision_is_verbose"
+
+#define ELEVATION   1
+
+#include "common\defines.h"
+#include "data.h"
+#include "behaviordata.h"
+
+params ["_ao", "_to_pos", "_omit_pawn"];
+
+_to_pos = _to_pos vectorAdd [0, 0, ELEVATION];
+
+private _count = 0;
+private _pawns = (_ao select AO_PAWNS) + [];
+if !(isNil "_omit_pawn") then
+{
+    [_pawns, {!([_x, _omit_pawn] call INEPT_fnc_isSameInstance)}]
+        call INEPT_fnc_conditionalSelectIP;
+};
+private _pawn_dests_asl =
+    [_pawns, {ATLtoASL ([_x] call INEPT_fnc_currentPawnDestination)}]
+        call INEPT_fnc_applyToAll;
+{
+    // TODO
+    // It would be smart if this added a value > 0 and < 1 depending on how
+    // visible surrounding terrain is. But that would get very expensive.
+    private _from_pos = _x vectorAdd [0, 0, ELEVATION];
+    if (((_from_pos distance _to_pos) < 100) or {!(terrainIntersectASL [_from_pos, _to_pos])}) then
+    {
+        _count = _count + 1;
+    };
+}
+forEach _pawn_dests_asl;
+_count

          
M ineptifex/fn_indexOfBest.sqf +15 -13
@@ 16,24 16,26 @@ 
 
 LOGENTRY();
 
-VAR(_list) = ARGUMENT_0;
+private _list = ARGUMENT_0;
 
 if (_list isEqualTo []) exitWith {-1};
 if (1 == count _list) exitWith {0};
 
-VAR(_best_index) = 0;
-VAR(_x) = (_list select _best_index);
-VAR(_best_quality) = call (ARGUMENT_1);
+private _best_index = 0;
+private _x = _list select 0;
+private _best_quality = call (ARGUMENT_1);
+private __forEachIndex = 0;
+while {__forEachIndex = __forEachIndex + 1; __forEachIndex < (count _list)} do
 {
-    if (_forEachIndex != 0) then
+    _x = _list select __forEachIndex;
+    // Silly hack because of dynamic typing issues where this variable is being
+    // clobbered by something else.
+    private _forEachIndex = __forEachIndex;
+    private _quality = call (ARGUMENT_1);
+    if (_quality > _best_quality) then
     {
-        VAR(_quality) = call (ARGUMENT_1);
-        if (_quality > _best_quality) then
-        {
-            _best_index = _forEachIndex;
-            _best_quality = _quality;
-        };
+        _best_index = __forEachIndex;
+        _best_quality = _quality;
     };
-}
-forEach _list;
+};
 _best_index

          
M ineptifex/fn_processPawnOnce.sqf +1 -1
@@ 45,6 45,6 @@ VAR(_aoi) = _pawn select PAWN_AOI;
 LOGVAR(_aoi);
 VAR(_logic) = [_aoi] call INEPT_fnc_getBehaviorLogic;
 if (isNil "_logic") then {LOGMSG("fuck...")};
-[_pawn] call (_logic select BEHAVIOR_LOGIC_TICK);
+[_ao, _pawn] call (_logic select BEHAVIOR_LOGIC_TICK);
 
 MUTEX_UNLOCK(_pawn select PAWN_MUTEX);

          
M ineptifex/fn_visibilityQuality.sqf +11 -14
@@ 1,4 1,4 @@ 
-/* fn_visibilityQuality
+/* INEPT_fnc_visibilityQuality
  *
  * Parameters:
  *  0: An ASL position

          
@@ 25,30 25,27 @@ 
 
 LOGENTRY();
 
-VAR(_origin) = ARGUMENT_0;
-LOGVAR(_origin);
+params ["_origin"];
 
 // This is kind of a hack, elevate by an amount so that if our given origin was
 // underneath the terrain then we won't intersect it
 _origin = _origin vectorAdd [0, 0, ELEVATION];
 
-VAR(_inc) = 360 / NUM_SLICES;
-LOGVAR(_inc);
-VAR(_q) = 0;
+private _inc = 360 / NUM_SLICES;
+private _q = 0;
 
 {
-    VAR(_vec) = [0, _x, 0];
+    private _vec = [0, _x, 0];
     for "_i" from 0 to (NUM_SLICES - 1) do
     {
-        VAR(_deg) = _inc * _i;
-        VAR(_delta) = [_vec, _deg] call INEPT_fnc_rotatedVector;
-        LOGVAR(_delta);
-        VAR(_dest) = _origin vectorAdd _delta;
-        _dest set [2, (getTerrainHeightASL _dest) + ELEVATION];
+        private _delta = [_vec, _inc * _i] call INEPT_fnc_rotatedVector;
+        private _dest = _origin vectorAdd _delta;
+        private _terrain_height = getTerrainHeightASL _dest;
+        _dest set [2, (0 max _terrain_height) + ELEVATION];
         if !(terrainIntersectASL [_origin, _dest]) then
         {
-            VAR(_score) = 1 / NUM_SLICES / 2;
-            if ((getTerrainHeightASL _dest) <= 0) then
+            private _score = 1 / NUM_SLICES / 2;
+            if (_terrain_height <= 0) then
             {
                 // Water counts for less, we don't really need to watch it
                 // because they would never attack by sea ...