M ineptifex/cfgfunctions.ext +5 -1
@@ 1,5 1,5 @@
// Include something like this in description.ext before including this files.
-//#define INEPTIFEX_DIR "ineptifex"
+//#define INEPTIFEX_DIR ineptifex
class INEPT
{
@@ 20,6 20,8 @@ class INEPT
class popRandom {};
class selectFractionAtRandom {};
class selectFractionAtRandomIP {};
+ class averageNumber {};
+ class averagePosition {};
};
class vector
@@ 70,6 72,7 @@ class INEPT
class processAOForever {};
class processAOOnce {};
class prunePawns {};
+ class hasPawns {};
class rebuildContactIntel {};
};
@@ 96,6 99,7 @@ class INEPT
class objectsInFrontOf {};
class visibilityQuality {};
class isPosUrban {};
+ class buildingDensity {};
};
class inept_pawn
A => ineptifex/fn_averageNumber.sqf +15 -0
@@ 0,0 1,15 @@
+/* INEPT_fnc_averageNumber
+ *
+ * There's no way there isn't a BIS function or something already for this, but
+ * I can't find it ...
+ */
+
+#include "common\defines.h"
+
+VAR(_sum) = 0;
+{
+ _sum = _sum + _x;
+}
+forEach _this;
+if (count _this > 0) exitWith { _sum / count _this };
+_sum
A => ineptifex/fn_averagePosition.sqf +11 -0
@@ 0,0 1,11 @@
+#include "common\defines.h"
+
+params ["_vectors"];
+
+VAR(_r) = [0, 0, 0];
+{
+ VAR(_v) = _x vectorMultiply (1 / (count _vectors));
+ _r = _r vectorAdd _v;
+}
+forEach _vectors;
+_r
A => ineptifex/fn_buildingDensity.sqf +3 -0
@@ 0,0 1,3 @@
+params ["_pos", "_radius", "_max"];
+// nearestBuilding check ensures that the thing really is very buildingy
+1 min (({(nearestBuilding _x) isEqualTo _x} count (_pos nearObjects ["Building", _radius])) / _max)
M ineptifex/fn_getFirepower.sqf +163 -88
@@ 24,14 24,38 @@ VAR(_mags) = (_info select UNIT_INFO_EQU
LOGVAR(_weps);
LOGVAR(_mags);
+VAR(_class_name) = _info select UNIT_INFO_CLASS;
+
+VAR(_is_infantry) = false;
+VAR(_is_mechanized) = false;
+VAR(_is_aircraft) = false;
+
+VAR(_has_countermeasures) = false;
+
+switch (true) do
+{
+ case (_class_name isKindOf "Man"):
+ { _is_infantry = true };
+ case (_class_name isKindOf "Car");
+ case (_class_name isKindOf "Tank"):
+ { _is_mechanized = true };
+ case (_class_name isKindOf "Air"):
+ { _is_aircraft = true };
+};
+
////////////////////////////////////////////////////////////////////////////////
// Lethality
// Somewhere in here we try to evaluate how mean something is by determining if
// it can lock or not. In the past I found it difficult to find comprehensive
-// and accurate information about locking and config file stuff. But the page
-// at the following URL seems to be relatively helpful:
+// and accurate information about locking and config file stuff.
+//
+// Helpful links:
// https://community.bistudio.com/wiki/A3_Locking_Review
+// https://community.bistudio.com/wiki/CfgWeapons_Config_Reference
+// https://community.bistudio.com/wiki/CfgMagazines_Config_Reference
+// https://community.bistudio.com/wiki/CfgAmmo_Config_Reference
+// https://community.bistudio.com/wiki/Arma_3_Damage_Description
// N.B.: Sometimes locking seems not to imply auto-seeking, in the case of the
// autocannon_35mm on the B_APC_Tracked_01_AA_F, it will simply set the range
@@ 78,23 102,38 @@ VAR(_lethality_a) = [];
};
};
- // Select the first mode we find where showToPlayer is true
- VAR(_mode_cfg) = nil;
+ // Separate mode configs where showToPlayer is true
+ VAR(_player_mode_cfg) = [];
+ VAR(_ai_mode_cfg) = [];
{
VAR(_x_mode_cfg) = if (_x == "this") then
{_wep_cfg}
else
{_wep_cfg >> _x};
- if ((getNumber (_x_mode_cfg >> "showToPlayer")) == 1) exitWith
+ if ((getNumber (_x_mode_cfg >> "showToPlayer")) == 1) then
{
- _mode_cfg = _x_mode_cfg
+ _player_mode_cfg pushBack _x_mode_cfg;
+ }
+ else
+ {
+ _ai_mode_cfg pushBack _x_mode_cfg;
};
}
forEach (getArray (_muzzle_cfg >> "modes"));
- if (isNil "_mode_cfg") exitWith
+ LOGVAR(_player_mode_cfg);
+ LOGVAR(_ai_mode_cfg);
+
+ if (_player_mode_cfg isEqualTo []) exitWith
{
- ["No acceptable mode found for weapon %1 muzzle %2", _wep_cfg, _muzzle_cfg] call BIS_fnc_error;
+ ["No showToPlayer modes found for weapon %1 muzzle %2", _wep_cfg, _muzzle_cfg] call BIS_fnc_error;
+ };
+
+ if (_ai_mode_cfg isEqualTo []) then
+ {
+ // This is pretty common for launchers or aircraft weapons.
+ //["No non showToPlayer modes found for weapon %1 muzzle %2 - using showToPlayer ones instead", _wep_cfg, _muzzle_cfg] call BIS_fnc_error;
+ _ai_mode_cfg = _player_mode_cfg;
};
// Everything is going splendidly so far, we should have a muzzle and
@@ 102,38 141,55 @@ VAR(_lethality_a) = [];
// Now add entries for figure out how much firepower we can dispense
// with the magazines we have.
+ // TODO, check optics.
+ VAR(_interval_at_1400) = 0;
+ VAR(_reloadTime) = 0;
+ {
+ _reloadTime = _reloadTime + (getNumber (_x >> "reloadTime"));
+ VAR(_interval) = (getNumber (_x >> "aiRateOfFire")) max (getNumber (_x >> "reloadTime"));
+ VAR(_distance) = getNumber (_x >> "aiRateOfFireDistance");
+ VAR(_burst) = getNumber (_x >> "burst");
+ _interval_at_1400 = _interval_at_1400 + (_interval / _burst) * (1400 / _distance);
+ }
+ forEach _ai_mode_cfg;
+ // Obtain the averages
+ _interval_at_1400 = _interval_at_1400 / (count _ai_mode_cfg);
+ _reloadTime = _reloadTime / (count _ai_mode_cfg);
+ LOGVAR(_interval_at_1400);
+ LOGVAR(_reloadTime);
+
+ // todo, figure out how to determine this reliably
+ VAR(_magazineReloadTime) = 3.5;
+ LOGVAR(_magazineReloadTime);
+
VAR(_cmImmunity) = (getNumber (_wep_cfg >> "cmImmunity"));
- VAR(_round_interval) = (
- (getNumber (_mode_cfg >> "reloadTime"))
- *
- ((getNumber (_mode_cfg >> "burst")) max 1)
- );
-
LOGVAR(_cmImmunity);
- LOGVAR(_round_interval);
{
VAR(_all_mag_count) = count _mags;
_mags = _mags - [_x];
- VAR(_num_found) = _all_mag_count - count _mags;
+ VAR(_num_mags_found) = _all_mag_count - count _mags;
VAR(_mag_cfg) = nil;
VAR(_ammo_cfg) = nil;
VAR(_hit) = nil;
if (
- _num_found > 0
+ _num_mags_found > 0
and
{
_mag_cfg = configFile >> "CfgMagazines" >> _x;
_ammo_cfg = configFile >> "CfgAmmo" >> getText (_mag_cfg >> "ammo");
_hit = (getNumber (_ammo_cfg >> "hit"));
+ // TODO, improve this stuff
+ if !(_has_countermeasures) then
+ {
+ _has_countermeasures =
+ (_hit < 5) and {(getNumber (_ammo_cfg >> "weaponLockSystem")) > 0};
+ };
// If the hit value for this ammunition is too low,
// it's probably a smoke grenade or a flare or a
- // countermeasure or something boring like that
- // Speaking of which, it's probably a good idea to pay
- // attention to these things and use them to help
- // determine our constitution.
+ // countermeasure or something silly like that.
_hit > 5
}
) then
@@ 142,15 198,12 @@ VAR(_lethality_a) = [];
LOGVAR(_ammo_cfg);
VAR(_mag_size) = getNumber (_mag_cfg >> "count");
- VAR(_rounds_avail) = (_mag_size * _num_found);
- // todo, figure out how to determine this reliably
- VAR(_magazineReloadTime) = 3.5;
-
+ VAR(_rounds_avail) = (_mag_size * _num_mags_found);
LOGVAR(_mag_size);
LOGVAR(_rounds_avail);
- LOGVAR(_magazineReloadTime);
- VAR(_airLock) = (getNumber (_ammo_cfg >> "airLock"));
+ VAR(_ammo_cost) = getNumber (_ammo_cfg >> "cost");
+ VAR(_airLock) = getNumber (_ammo_cfg >> "airLock");
VAR(_allowAgainstInfantry) =
if (isNumber (_ammo_cfg >> "allowAgainstInfantry")) then
{
@@ 160,50 213,45 @@ VAR(_lethality_a) = [];
{
1
};
- VAR(_explosive) = (getNumber (_ammo_cfg >> "explosive"));
- VAR(_indirectHit) = (getNumber (_ammo_cfg >> "indirectHit"));
- VAR(_indirectHitRange) = (getNumber (_ammo_cfg >> "indirectHitRange"));
- VAR(_irLock) = (getNumber (_ammo_cfg >> "irLock"));
- VAR(_rps) = 1 / (_round_interval + (_magazineReloadTime / _mag_size));
+ //VAR(_explosive) = (getNumber (_ammo_cfg >> "explosive"));
+ VAR(_indirectHit) = (getNumber (_ammo_cfg >> "indirectHit"));
+ VAR(_indirectHitRange) = (getNumber (_ammo_cfg >> "indirectHitRange"));
+ VAR(_irLock) = (getNumber (_ammo_cfg >> "irLock"));
+ LOGVAR(_ammo_cost);
LOGVAR(_airLock);
LOGVAR(_allowAgainstInfantry);
- LOGVAR(_explosive);
+ //LOGVAR(_explosive);
LOGVAR(_hit);
LOGVAR(_indirectHit);
LOGVAR(_indirectHitRange);
LOGVAR(_irLock);
- LOGVAR(_rps);
- // Seconds of sustainable fire
- VAR(_sustainability) = _rounds_avail / _rps;
-
- // Infantry Damage
+ // Anti-Infantry Effectiveness
if (_allowAgainstInfantry > 0) then
{
// TODO, incorporate explosiveness
- VAR(_this_lethality_i) = (
- (
- // Accuracy (probability of hit)
- 0.15
- // Frequency of fire
- * _rps
- // Deadliness of rounds
- * (1 min (_hit / 20))
- )
- +
- (
- // Probability of near hit
- 0.15 * _indirectHitRange / 3
- * _rps
- * (1 min (_indirectHit / 20))
- )
- );
+ VAR(_round_damage_i) =
+ ((1 min (_hit / 20)) + (0.2 * (3 * _indirectHitRange)^2 * pi * (1 min (_indirectHit / 20))));
+ LOGVAR(_round_damage_i);
+
+ // /4 because infantry combat is at about 350 metres or some shit I guess
+ VAR(_interval_i) = (_reloadTime min (_interval_at_1400 / 4)) + (_magazineReloadTime / _mag_size);
+ LOGVAR(_interval_i);
+
+ // This is really just
+ // 0.7 * shots per second * damage per shot / cost per
+ // shot; where shots in one second is 1 / seconds interval
+ // between shots.
+ VAR(_this_lethality_i) = 0.7 * _round_damage_i / _interval_i / (1 max _ammo_cost);
LOGVAR(_this_lethality_i);
+
if (_this_lethality_i > 0) then
{
- _lethality_i pushBack (NEW_PAIR(_this_lethality_i, _sustainability));
+ VAR(_sustainability_i) = _rounds_avail * _interval_i;
+ LOGVAR(_sustainability_i);
+ _lethality_i pushBack (NEW_PAIR(_this_lethality_i, _sustainability_i));
};
};
@@ 212,20 260,25 @@ VAR(_lethality_a) = [];
VAR(_can_at_lock) = (_wep_canLock >= 2) and (_irLock > 0) and (_airLock <= 1);
LOGVAR(_can_at_lock);
- VAR(_this_lethality_r) = (
- // Accuracy (probability of hit)
- (if (_can_at_lock) then {_cmImmunity} else {.6})
- // Frequency of fire
- * _rps
- // Deadliness of rounds - capping this value by using `min`
- // is important or else things like bombs become excellent
- // AA weapons simply because they have such a high payload
- * (4 min ((_hit - _indirectHit) / 150))
- );
+ VAR(_round_damage_r) = ((((_hit min 180) / 180) ^ 4) + (((_hit - 180) max 0) / 20));
+ LOGVAR(_round_damage_r);
+
+ // / 2 because armour combat is at about 700 metres or some shit I guess
+ VAR(_interval_r) = (_reloadTime min (_interval_at_1400 / 2)) + (_magazineReloadTime / _mag_size);
+ LOGVAR(_interval_r);
+
+ // This is mainly just
+ // shots per second * damage per shot / cost per shot; where
+ // shots in one second is 1 / seconds interval between shots.
+ VAR(_accuracy_r) = if (_can_at_lock) then {_cmImmunity max .6} else {.6};
+ VAR(_this_lethality_r) = _accuracy_r * (40 min _round_damage_r) / _interval_r / (1 max ((sqrt _ammo_cost) / 50));
LOGVAR(_this_lethality_r);
+
if (_this_lethality_r > 0) then
{
- _lethality_r pushBack (NEW_PAIR(_this_lethality_r, _sustainability));
+ VAR(_sustainability_r) = _rounds_avail * _interval_r;
+ LOGVAR(_sustainability_r);
+ _lethality_r pushBack (NEW_PAIR(_this_lethality_r, _sustainability_r));
};
// Air Damage
@@ 237,18 290,25 @@ VAR(_lethality_a) = [];
VAR(_can_aa_lock) = (_wep_canLock >= 2) and (_irLock > 0) and (_airLock >= 1);
LOGVAR(_can_aa_lock);
- VAR(_this_lethality_a) = (
- // Accuracy (probability of hit)
- (if (_can_aa_lock) then {_cmImmunity} else {.01})
- // Frequency of fire
- * _rps
- // Deadliness of rounds
- * _hit
- );
+ VAR(_round_damage_a) = ((((_hit min 40) / 40) ^ 4) + (((_hit - 40) max 0) / 20));
+ LOGVAR(_round_damage_a);
+
+ // because air combat is at about 1400 metres or some shit I guess
+ VAR(_interval_a) = (_reloadTime min (_interval_at_1400)) + (_magazineReloadTime / _mag_size);
+ LOGVAR(_interval_a);
+
+ // This is mainly just
+ // shots per second * damage per shot / cost per shot; where
+ // shots in one second is 1 / seconds interval between shots.
+ VAR(_accuracy_a) = if (_can_aa_lock) then {_cmImmunity max .01} else {.01};
+ VAR(_this_lethality_a) = _accuracy_a * (40 min _round_damage_a) / _interval_a / (1 max (_ammo_cost / 10000));
LOGVAR(_this_lethality_a);
+
if (_this_lethality_a > 0) then
{
- _lethality_a pushBack (NEW_PAIR(_this_lethality_a, _sustainability));
+ VAR(_sustainability_a) = _rounds_avail * _interval_a;
+ LOGVAR(_sustainability_a);
+ _lethality_a pushBack (NEW_PAIR(_this_lethality_a, _sustainability_a));
};
};
}
@@ 276,8 336,7 @@ LOGVAR(_sorted_lethality_a);
// all that crap
VAR(__lethality_fn) =
{
- VAR(_sorted) = ARGUMENT_0;
- VAR(_max) = ARGUMENT_1;
+ params ["_sorted", "_max"];
VAR(_remaining) = _max;
VAR(_r) = 0;
while {!(_sorted isEqualTo [])} do
@@ 306,9 365,9 @@ VAR(__lethality_fn) =
// result up. TODO, I'm not sure that the scaling matters. Except maybe for
// thingies created by the priorty value ... maybe ethat should be changed ...
// XXX FIXME TODO
-_lethality_i = ([_sorted_lethality_i, 50] call __lethality_fn) * 3;
-_lethality_r = ([_sorted_lethality_r, 20] call __lethality_fn) * 2;
-_lethality_a = ([_sorted_lethality_a, 20] call __lethality_fn);
+_lethality_i = ([_sorted_lethality_i, 40] call __lethality_fn);
+_lethality_r = ([_sorted_lethality_r, 15] call __lethality_fn);
+_lethality_a = ([_sorted_lethality_a, 10] call __lethality_fn);
LOGVAR(_lethality_i);
LOGVAR(_lethality_r);
@@ 317,36 376,52 @@ LOGVAR(_lethality_a);
////////////////////////////////////////////////////////////////////////////////
// Constitution - composition and defense and stuff
+LOGVAR(_has_countermeasures);
+
VAR(_constitution_i) = 0;
VAR(_constitution_r) = 0;
VAR(_constitution_a) = 0;
-VAR(_class_name) = _info select UNIT_INFO_CLASS;
-
switch (true) do
{
// TODO, incorporate size as it effects accuracy of attackers
- case (_class_name isKindOf "Man"):
+ case (_is_infantry):
{
// todo, check out the uniform or vest for armor values?
_constitution_i = 1;
+ // ... people could totally have countermeasures ...
+ if (_has_countermeasures) then
+ {
+ _constitution_i = _constitution_i * 2;
+ };
};
- case (_class_name isKindOf "Car");
- case (_class_name isKindOf "Tank"):
+ case (_is_mechanized):
{
VAR(_armor) = getNumber (configFile >> "CfgVehicles" >> _class_name >> "armor");
_constitution_r = _armor / 100;
+ if (_has_countermeasures) then
+ {
+ _constitution_r = _constitution_r * 1.5;
+ };
};
- case (_class_name isKindOf "Air"):
+ case (_is_aircraft):
{
// Shouldn't we check for flairs? Maybe they aren't part of the unit
// info. I thought they were a weapon ...
VAR(_armor) = getNumber (configFile >> "CfgVehicles" >> _class_name >> "armor");
- _constitution_a = _armor;
+ _constitution_a = _armor / 40;
+ if (_has_countermeasures) then
+ {
+ _constitution_a = _constitution_a * 2;
+ };
};
default
{
// Should we do something here?
+ ["%1 is not recognized, constitution uncertain.", _class_name] call BIS_fnc_error;
+ _constitution_i = 0.5;
+ _constitution_r = 0.5;
+ _constitution_a = 0.5;
};
};
M ineptifex/fn_getUnitInfo.sqf +17 -4
@@ 56,7 56,12 @@ if (_unit isKindOf "Man") then
}
else
{
- [_weapons, weapons _unit] call BIS_fnc_arrayPushStack;
+ _weapons append (weapons _unit);
+ _weapons append (_unit weaponsTurret [-1]);
+ {
+ _weapons append (_unit weaponsTurret _x);
+ }
+ forEach (allTurrets [_unit, false]);
};
LOGVAR(_weapons);
@@ 151,9 156,17 @@ if (_info_accuracy == UNIT_INFO_APPEARAN
}
else
{
- [_magazines, magazines _unit] call BIS_fnc_arrayPushStack;
- [_magazines, primaryWeaponMagazine _unit] call BIS_fnc_arrayPushStack;
- [_magazines, secondaryWeaponMagazine _unit] call BIS_fnc_arrayPushStack;
+ _magazines append (magazines _unit);
+ _magazines append (primaryWeaponMagazine _unit);
+ _magazines append (secondaryWeaponMagazine _unit);
+ _magazines append (_unit magazinesTurret [-1]);
+ if !(_unit isKindOf "Man") then
+ {
+ {
+ _magazines append (_unit magazinesTurret _x);
+ }
+ forEach (allTurrets [_unit, false]);
+ };
};
if (_include_cargo == UNIT_INFO_WITH_CARGO) then
A => ineptifex/fn_hasPawns.sqf +15 -0
@@ 0,0 1,15 @@
+/* INEPT_fnc_prunePawns
+ *
+ * Parameters:
+ * 0: An AO instance
+ *
+ * Specification:
+ * Returns true iff the AO has any pawns/groups. Pawns with are removed by
+ * INEPT_fnc_processAOOnce when everyone in them is dead. So when forces
+ * belonging to an AO have been wiped out this should return false.
+ */
+
+#include "data.h"
+
+params ["_ao"];
+not ((_ao select AO_PAWNS) isEqualTo [])
M ineptifex/fn_isPosUrban.sqf +10 -13
@@ 6,20 6,17 @@
* Specification:
* Returns true or false depending on if the position is nearby a couple of
* buildings I guess.
- * Warning, this function may return different results across multiple calls
- * even if you pass the same argument each time.
*/
-#define DEBUG_VAR "INEPT_fnc_isPosUrban_is_verbose"
-
-#include "common\defines.h"
-#include "data.h"
-
-LOGENTRY();
-
-VAR(_pos) = ARGUMENT_0;
+params ["_pos"];
+private _cfg_vehicles = configFile >> "CfgVehicles";
+// nearestBuilding check ensures that the thing really is very buildingy
(
- count (_pos nearObjects ["Building", 30])
- >
- (2 + floor (random 2))
+ {
+ ((nearestBuilding _x) isEqualTo _x) and
+ {not ((getText (configFile >> typeOf _x >> "vehicleClass")) isEqualTo "Structures_Military")}
+ }
+ count (_pos nearObjects ["Building", 40])
)
+>
+3
M ineptifex/fn_popRandom.sqf +1 -31
@@ 11,34 11,4 @@
* Selects and returns an element from an array at random removing the element
* from the array in-place. Behaviour is undefined if you pass an empty array.
*/
-
-#define DEBUG_VAR "INEPT_fnc_popRandom_is_verbose"
-
-#include "common\defines.h"
-
-LOGENTRY();
-
-VAR(_array) = ARGUMENT;
-
-VAR(_num_elements) = count _array;
-
-switch (_num_elements) do
-{
- case 0:
- {
- ["Array has no elements."] call BIS_fnc_error;
- };
- case 1:
- {
- VAR(_r) = _array select 0;
- _array resize 0;
- _r
- };
- default
- {
- VAR(_idx) = floor random _num_elements;
- VAR(_r) = _array select _idx;
- _array deleteAt _idx;
- _r
- };
-}
+_this deleteAt (floor random (count _this))
M ineptifex/fn_prunePawns.sqf +9 -20
@@ 22,25 22,14 @@ MUTEX_LOCK(_mtx);
VAR(_pawns) = _ao select AO_PAWNS;
-{
- VAR(_is_alive) = [
- units (_x select PAWN_GROUP),
- // beware, the _x in the code block just below is different from the _x
- // above - because fuck clarity
- {alive _x}
- ] call INEPT_fnc_any;
- if !(_is_alive) then
- {
- LOGMSG("pawn is kill");
- LOGVAR(_x);
- _pawns set [_forEachIndex, 0];
- };
-}
-forEach _pawns;
-
-if (0 in _pawns) then
-{
- _ao set [AO_PAWNS, _pawns - [0]];
-};
+[ _pawns
+, {
+ [ units (_x select PAWN_GROUP),
+ // beware, the _x in the code block just below is different from the _x
+ // above - because fuck clarity
+ {alive _x}
+ ] call INEPT_fnc_any;
+ }
+] call INEPT_fnc_conditionalSelectIP;
MUTEX_UNLOCK(_mtx);