Jump to content
madrussian

The mystical skillFinal formula (reverse engineering it)

Recommended Posts

Trying my hand at reverse engineering how setSkill impacts skillFinal, with respect to CfgAISkill and the Difficulty settings (AI Level section's "Skill" and "Precision" values).

 

I've run a bunch of experiments and gathered the following:

  • Mission creator calls setSkill on a unit
  • ArmA3 engine put's the result on a curve, with the crux-point at setSkill 0.2 (linear to and from this crux-point)
  • CfgAISkill does it's thing (applies it's multiplier)
  • Difficulty Menu's "SKILL" and "PRECISION" does it's thing (applies it's multiplier)

 

I'm trying to determine the order in which these things happen.  Run into small hiccup:

 

How does one get the Difficulty Menu's "SKILL" and "PRECISION" values via script?  Hopefully it's a simple thing?

 

Better yet, anyone have the actual formula or otherwise know the order in which these factors are applied?  (Hoping to arrive at the exact skillFinal formula, will post it here as a reference for all.)

 

I welcome any and all help, even simple things I may have missed.  Thanks!

  • Like 2

Share this post


Link to post
Share on other sites
42 minutes ago, madrussian said:
  • Difficulty Menu's "SKILL" and "PRECISION" does it's thing (applies it's multiplier)

Last time I tried, those sliders didn't do anything (maybe test parameters were at fault).

 

 

Thumbs up for effort though and good luck.

:yay:

 

Cheers

  • Like 1

Share this post


Link to post
Share on other sites

Dev's initially had a stickied thread call'ed something like "AI Configs" which seemed as if by initial intention -to work with community on exactly things like this. Maybe dig it up, cast some stones and pray for a response though death by random crow's beak impaling your eye seems far more likely.

 

Also wishing you luck on this most Holiest of Crusades and should you reach a bridge with a medieval looking wizard with rams horns, the correct answer to his question is....Blue..no wait RED!

  • Haha 4

Share this post


Link to post
Share on other sites

I'm scripting with:

- skill cursor for edited objects (recently a SAM radar);

- setSkill for spawned units

without more detailed capabilities of aiming or shaking...

That's enough for any SM MP scenario. A limited randomization is even better for more fun.

Share this post


Link to post
Share on other sites
1 hour ago, Grumpy Old Man said:

Last time I tried, those sliders didn't do anything (maybe test parameters were at fault).

 

 

They've always done something, the effect is just pretty small currently.

 

I swear the actual formula was posted on the AI thread a while ago. May need some digging to find it.

Share this post


Link to post
Share on other sites

 

 Maybe im a bit jaded here but if your after properly calibrating shooting skills so it actually feels right when say a Sharpshooter takes on a CSAT Line Crew Chef - he actually wins the battle 9/10 - Ive found (thru much less scientific testing but none the less formidable by sheer volume) that the .2 Minimum the game uses is still an Elite level. Meaning a .2 aimingAccuracy vs a .9 does not provide a clear distinction in shooting badassery. The scripts I use which place a value to Class finds that a Basic Soldier with a 0.03 - 0.08 aimAccuracy will lose to a Spec Op with a .2 aimAccuracy a much more believable percent of the time. Bad units such as Crew/Pilot etc get a paltry 0.008 range and when faced with a Sniper 0.7 - 0.9, the Sniper will light them up like sitting ducks.

 

The game gives no special behaviour, animations, etc to better forces so all you can do to keep it interesting, believable and avoid too much samey-ness is find proper calibration of skill in units. *note* I did add animations to SpecOps /Sniper as well as panic reactions to low level troops getting slaughtered and it does add some much needed spice.

 

 

  • Like 1

Share this post


Link to post
Share on other sites

A simple solution would be to introduce some kind of setSkillFinal command that overrides all that mumbo jumbo going on and simply sets the final value.

According to oukej that's not likely to happen:

This post also has a conversion table from old to new system (of sorts):

AI's aimingAccuracy 	0.1 	0.2 	0.3 	0.4 	0.5 	0.6 	0.7 	0.8 	0.9 	1
resulting coef. (old) 	23.5 	11 	6.8 	4.75 	3.5 	2.67 	2.07 	1.63 	1.28 	1
resulting coef. (new) 	9.1 	8.2 	7.3 	6.4 	5.5 	4.6 	3.7 	2.8 	1.9 	1

Note that this is just about AI, you probably need to tweak weapon config as well since it also affects accuracy, AI with 0 skill will still be lethal and extremely accurate in vehicle weapons (even non scoped variants like M2 static etc.).

 

Cheers

Share this post


Link to post
Share on other sites

@all

Great insight, tips, tricks, etc. :don14:

 

@Grumpy Old Man

Wow, interesting videos!  Surprising to say the least.  In my own experience, in terms of how easily enemies notice me, and how accurately they return fire, etc:  I get noticeable change in-game when I adjust the SKILL and PRECISION sliders.  (Note - None of this is anything I can back up with hard data like you posted Grumpy.)

 

I notice perhaps a bigger change when simply running IFA3 (which modifies CfgAISkill values) or not.  Specifically, in my dynamic mission, top player can pick player factions and enemy factions, and while playing with non-IFA3 units, I can tell absolutely whether IFA3 is loaded or not based on how the AI reacts/shoots.

 

In any event, my testing shows that SKILL and PRECISION do indeed impact skillFinal.  Here are a series of good graphs ImperialAlex posted on the AI thread that mirrors my own results:

 

On 6/14/2016 at 6:41 AM, ImperialAlex said:

I did some basic testing to investigate how skillAI and precisionAI transform the values. This is ignoring the interpolation done by CfgAiSkill. Note that CfgAiSkill leaves all but three subskills unmodified anyway. The graphs are to be read as follows:

Y-axis is the transformed final (sub)skill (ignoring CfgAiSkill) that results from setting a skill to X-axis value.

The graphs show skillAI/precisionAI at 1, 0.5 and 0, respectively.

ZDYyx2C.png

 

 
64ZX5QJ.png
 
 
zdWKOs1.png
 
It seems like 0.2 is where the magic happens. The transformation is made up of two linear segments, one from 0 to 0.2 and one from 0.2 to 1. While the first segment always starts a 0 on the y-axis the second segment does not always end at 1 on the y-axis. I.e. setting your skillAI/precisionAI to anything less than 1 will mean that even "unit skill 1" will not give a skillFinal of 1.
Sadly no value of skillAI/precisionAI will ever result in a completely linear transformation (i.e. skill = skillFinal, at least when ignoring CfgAiSkill interpolations that happens to certain subskills). 0.5 is close but not entirely linear, going up to only 0.9 skillFinal.

 

I'll be happy if I can apply skill adjustments and get a consistent desired skillFinal result.  I believe this is 100% achievable, with a step-by-step approach.

 

First, we need a way to get game's Difficulty SKILL and PRECISION settings (as it will impact skillFinal).

 

I've not yet seen a command to get these (and if it's out there, please simplify all this by posting), so I wrote my own function (see below).  My plan was to use setSkill and observe resulting finalSkill, then work backwards to get Difficulty's Skill and Precision.  As CfgAiSkill will impact finalSkill, we must account for it.

 

Here is CfgAiSkill for vanilla game:

aimingAccuracy[] = {0,0,1,1};

aimingShake[] = {0,0,1,1};

aimingSpeed[] = {0,0.5,1,1};

commanding[] = {0,0,1,1};

courage[] = {0,0,1,1};

endurance[] = {0,0,1,1};

general[] = {0,0,1,1};

reloadSpeed[] = {0,0,1,1};

spotDistance[] = {0,0,1,1};

spotTime[] = {0,0,1,1};

 

For comparison, here is CfgAiSkill for IFA3:

aimingAccuracy[] = {0,0,1,0.8};

aimingShake[] = {0,0,1,0.6};

aimingSpeed[] = {0,0,1,0.7};

commanding[] = {0,0,1,0.8};

courage[] = {0,0,1,0.7};

endurance[] = {0,0,1,0.7};

general[] = {0,0,1,0.9};

reloadSpeed[] = {0,0,1,1};

spotDistance[] = {0,0,1,0.9};

spotTime[] = {0,0,1,0.7};

 

To review how CfgAiSkill calculates, here's the example from the wiki:

Quote

spotDistance[] = {0,0.2, 1,0.4}; value in a range 0-1 will change into value in a range 0.2-0.4.setSkill ["spotDistance", 0.5] results in skill "spotDistance" returning 0.3

 

To simply things, hopefully we can make some assumptions about how mods will use CfgAiSkill.  First, the original range should be 0-1 (array's first and third element).  Second, the resulting range (array's second and fourth element) should go from a smaller value to larger value.  (If anyone is aware of a mod that does not conform to these assumptions, please advise and I'll modify function below to account for these too.)

 

Here's my function to determine game's difficulty SKILL and PRECISION values.  Works for my purposes so far (tested with Vanilla only first, then with IFA3 loaded).  Anyone mind confirming this correctly returns SKILL and PRECISION for you too?

MRU_DifficultySkillAndPrecision = {
	private ["_difficultySettings","_general","_aimingAccuracy","_maxGeneral","_maxAimingAccuracy","_SFG_for_SS1_at_DS0",
			"_SFG_for_SS1_at_DS1","_SFAA_for_SS1_at_DS0","_SFAA_for_SS1_at_DS1","_group","_logic","_skillFinalG",
			"_skillFinalAA","_difficultySkill","_difficultyPrecision"];
	
	_difficultySettings = [];

	_general = getArray (configfile >> "CfgAISkill" >> "general");
	_aimingAccuracy = getArray (configfile >> "CfgAISkill" >> "aimingAccuracy");
	if ( ((_general select 0) == 0) and ((_general select 2) == 1) and ((_aimingAccuracy select 0) == 0) and ((_aimingAccuracy select 2) == 1) and
		((_general select 3) > (_general select 1)) and ((_aimingAccuracy select 3) > (_aimingAccuracy select 1)) ) then {
		_maxGeneral = _general select 3;
		_maxAimingAccuracy = _aimingAccuracy select 3;

		// skillFinal "general" for setSkill 1 at Difficulty Skill 0
		_SFG_for_SS1_at_DS0 = _maxGeneral * 0.8;
		// skillFinal "general" for setSkill 1 at Difficulty Skill 1
		_SFG_for_SS1_at_DS1 = _maxGeneral;

		// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 0
		_SFAA_for_SS1_at_DS0 = _maxAimingAccuracy * 0.8;
		// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 1
		_SFAA_for_SS1_at_DS1 = _maxAimingAccuracy;

		_group = createGroup sideLogic;
		_logic = _group createUnit ["Logic", [0,0,0], [], 0, "NONE"];

		_logic setSkill 1;
		_skillFinalG = _logic skillFinal "general";
		_skillFinalAA = _logic skillFinal "aimingAccuracy";

		_difficultySkill = (_skillFinalG - _SFG_for_SS1_at_DS0) / (_SFG_for_SS1_at_DS1 - _SFG_for_SS1_at_DS0);
		_difficultyPrecision = (_skillFinalAA - _SFAA_for_SS1_at_DS0) / (_SFAA_for_SS1_at_DS1 - _SFAA_for_SS1_at_DS0);

		_difficultySettings = [_difficultySkill, _difficultyPrecision];
		
		deleteVehicle _logic;
		deleteGroup _group;
	};

	_difficultySettings
};

 

OK, so far so good.  I'll keep going and report back...

 

  • Like 2
  • Thanks 1

Share this post


Link to post
Share on other sites

Quick update, I'm very close.  Here's a quick snapshot preview (in case I get hit by a bus in the next couple days, so these formulas don't get lost, etc):

 

I've got the part that projects skillFinal working.  Specifically:  Provided with setSkill value, works great for projecting expected skillFinal (accounting for all the factors), woohoo! :yay:

 

Still working on going backward from skillFinal to setSkill (the Holy Grail), and of course that last bit is a real pain in the behind.  In the mean time if you want to take it for a spin, create folder inside mission root and name it "MRU_FinalSkillCalculator".  Then place these files inside folder:

 

Spoiler

 

_prepFSC.sqf


private ["_path","_subSkills","_conforms","_cfg","_subSkill","_ssArray","_general","_aimingAccuracy","_maxGeneral",
		"_maxAimingAccuracy","_SFG_for_SS1_at_DS0","_SFG_for_SS1_at_DS1","_SFAA_for_SS1_at_DS0","_SFAA_for_SS1_at_DS1",
		"_group","_logic","_skillFinalG","_skillFinalAA"];

_path = "MRU_FinalSkillCalculator\";
MRU_FSC_CfgAISkillAdjusted = compile preprocessFile (_path + "CfgAISkillAdjusted.sqf");
MRU_FSC_CfgAISkillReversed = compile preprocessFile (_path + "CfgAISkillReversed.sqf");
MRU_FSC_GenericCurve = compile preprocessFile (_path + "GenericCurve.sqf");
MRU_FSC_ProjectedFinalSkill = compile preprocessFile (_path + "ProjectedFinalSkill.sqf");
MRU_FSC_ProjectedSetSkill = compile preprocessFile (_path + "ProjectedSetSkill.sqf");

_subSkills = [
	"aimingAccuracy",
	"aimingShake",
	"aimingSpeed",
	"endurance",
	"spotDistance",
	"spotTime",
	"courage",
	"reloadSpeed",
	"commanding",
	"general"
];

_conforms = true;

_cfg = configFile >> "CfgAISkill";
{
	_subSkill = _x;
	_ssArray = getArray (_cfg >> _subSkill);
	//if ( ((_ssArray select 0) != 0) or ((_ssArray select 2) != 1) or ((_ssArray select 1) > (_ssArray select 3)) ) exitWith { _conforms = false };
	if ( ((_ssArray select 0) < 0) or ((_ssArray select 1) < 0) or ((_ssArray select 2) < 0) or ((_ssArray select 3) < 0) ) exitWith { _conforms = false };
} foreach _subSkills;

if (_conforms) then {
	_general = getArray (configfile >> "CfgAISkill" >> "general");
	_aimingAccuracy = getArray (configfile >> "CfgAISkill" >> "aimingAccuracy");

	_maxGeneral = _general select 3;
	_maxAimingAccuracy = _aimingAccuracy select 3;

	// skillFinal "general" for setSkill 1 at Difficulty Skill 0
	_SFG_for_SS1_at_DS0 = _maxGeneral * 0.8;
	// skillFinal "general" for setSkill 1 at Difficulty Skill 1
	_SFG_for_SS1_at_DS1 = _maxGeneral;

	// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 0
	_SFAA_for_SS1_at_DS0 = _maxAimingAccuracy * 0.8;
	// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 1
	_SFAA_for_SS1_at_DS1 = _maxAimingAccuracy;

	_group = createGroup sideLogic;
	_logic = _group createUnit ["Logic", [0,0,0], [], 0, "NONE"];

	_logic setSkill 1;
	_skillFinalG = _logic skillFinal "general";
	_skillFinalAA = _logic skillFinal "aimingAccuracy";

	deleteVehicle _logic;
	deleteGroup _group;

	MRU_FSC_DifficultySKILL = (_skillFinalG - _SFG_for_SS1_at_DS0) / (_SFG_for_SS1_at_DS1 - _SFG_for_SS1_at_DS0);
	MRU_FSC_DifficultyPRECISION = (_skillFinalAA - _SFAA_for_SS1_at_DS0) / (_SFAA_for_SS1_at_DS1 - _SFAA_for_SS1_at_DS0);
};

 

CfgAISkillAdjusted.sqf


private ["_ssArray","_min1","_min2","_max1","_max2","_skillAdjusted","_span1","_span2","_progress"];

params ["_skill","_subSkill"];

_ssArray = getArray (configfile >> "CfgAISkill" >> _subSkill);

_min1 = _ssArray select 0;
_min2 = _ssArray select 1;
_max1 = _ssArray select 2;
_max2 = _ssArray select 3;

_skillAdjusted = -1;

if (_skill <= _min1) then {
	_skillAdjusted = _min2;
} else {
	if (_skill >= _max1) then {
		_skillAdjusted = _max2;
	} else {
		_span1 = _max1 - _min1;
		_span2 = _max2 - _min2;
		_progress = if (_span1 == 0) then {
			// Avoiding division by zero, must choose somewhat arbitrary value for progress
			0.5
		} else {
			(_skill - _min1) / _span1
		};
		_skillAdjusted = _min2 + (_span2 * _progress);
	};
};

_skillAdjusted

 

CfgAISkillReversed.sqf


private ["_ssArray","_min1","_min2","_max1","_max2","_skill","_span1","_span2","_progress"];

params ["_skillAdjusted","_subSkill"];

_ssArray = getArray (configfile >> "CfgAISkill" >> _subSkill);

_min1 = _ssArray select 0;
_min2 = _ssArray select 1;
_max1 = _ssArray select 2;
_max2 = _ssArray select 3;

_skill = -1;

if (_skillAdjusted <= _min2) then {
	_skill = _min1;
} else {
	if (_skillAdjusted >= _max2) then {
		_skill = _max1;
	} else {
		_span1 = _max1 - _min1;
		_span2 = _max2 - _min2;
		_progress = if (_span2 == 0) then {
			// Avoiding division by zero, must choose somewhat arbitrary value for progress
			0.5
		} else {
			(_skillAdjusted - _min2) / _span2
		};
		_skill = _min1 + (_span1 * _progress);
	};
};

_skill

 

GenericCurve.sqf


private ["_curve","_var","_difficulty","_min","_inflection","_max"];

params ["_subSkill"];

_curve = [];

_var = if ((toLower _subSkill) in ["aimingaccuracy", "aimingshake"]) then {
	"MRU_FSC_DifficultyPRECISION"
} else {
	"MRU_FSC_DifficultySKILL"
};

_difficulty = missionNamespace getVariable [_var, -1];
if (_difficulty >= 0) then {
	_min = 0;
	//_inflection = 0;
	_inflection = 0.01;
	if (_difficulty >= 0.25) then {
		_inflection = 0.6 * ((_difficulty - 0.25) / 0.75);
	};
	_max = 0.8 + (_difficulty * 0.2);
	_curve = [_min, _inflection, _max];
};

_curve

 

ProjectedFinalSkill.sqf


private ["_projectedFinalSkill","_curve","_min","_inflection","_max","_minX","_inflectionX","_maxX","_floor","_floorX"];

params ["_setSkill","_subSkill"];

_projectedFinalSkill = -1;

_curve = [_subSkill] call MRU_FSC_GenericCurve;

if ((count _curve) > 0) then {
	_min = _curve select 0;
	_inflection = _curve select 1;
	_max = _curve select 2;
	
	_minX = [_min, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
	_inflectionX = [_inflection, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
	_maxX = [_max, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
	
	_projectedFinalSkill = if (_setSkill <= 0.2) then {
		_minX + ((_inflectionX - _minX) * (_setSkill / 0.2));
	} else {
		_inflectionX + ((_maxX - _inflectionX) * ((_setSkill - 0.2) / 0.8))
	};

	_floor = _setSkill / 20;
	if (_floor == 0) then { _floor = 10e-038 };
	_floorX = [_floor, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
	if (_projectedFinalSkill < _floorX) then {
		_projectedFinalSkill = _floorX;
	};	
};

_projectedFinalSkill

 

ProjectedSetSkill.sqf


// TBD - The big one, still working on it!

 

 

In "init.sqf", prep the system (once) via:

call compile preprocessFile "MRU_FinalSkillCalculator\_prepFSC.sqf";

Test via:

_difficultySKILL = missionNamespace getVariable ["MRU_FSC_DifficultySKILL", -1];
_difficultyPRECISION = missionNamespace getVariable ["MRU_FSC_DifficultyPRECISION", -1];

_skill = 0.38;
_subSkill = "general";
//_subSkill = "aimingAccuracy";
//_subSkill = "aimingShake";
//_subSkill = "aimingSpeed";
//_subSkill = "commanding";
//_subSkill = "courage";
//_subSkill = "endurance";
//_subSkill = "reloadSpeed";
//_subSkill = "spotDistance";
//_subSkill = "spotTime";

_projectedFinalSkill = [_skill, _subSkill] call MRU_FSC_ProjectedFinalSkill;
Loon setSkill [_subSkill, _skill];
_actualFinalSkill = Loon skillFinal _subSkill;
hint format ["_difficultySKILL: %1\n_difficultyPRECISION: %2\n\n_skill: %3\n_subSkill: %4\n\n_projectedFinalSkill: %5\n_actualFinalSkill: %6", _difficultySKILL, _difficultyPRECISION, _skill, _subSkill, _projectedFinalSkill, _actualFinalSkill];

If you can, please try out different _skill and _subSkill values (along with different SKILL and PRECISION slider values, along with different mods that modify CfgAISkill) and let me know how it works.  [Disclaimer - Please don't expect 100% exact perfect results, as you might experience some tiny rounding errors on the order of 0.001 at most.  Anything out of whack more than that, please let me know asap.]

 

(Btw - What's the best clean, free file uploading site these days?)

 

Alright, back into "head down" mode. :smile_o:

 

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

I love it when a thing comes together.  Added proper getSetSkill function!  Given a final skill value, correctly computes original setSkill.  Also, I'm getting accuracy of ~ 0.0000001 now both ways, no matter what I feed in.  (I think this is due to recent change of inflection point minimum to 0.01)

 

Overhauled everything a bit (including improving some function names), now in single file format.

 

Please someone try this and report back!  So I can move forward and create a proper release thread. :xmas:

 

New Instructions:

 

Create a file named "MRU_FinalSkillCalculator.sqf" (in mission dir), paste this code inside, and save:

Spoiler

 


// Final Skill Calculator (FSC) by MadRussian - v1.0 (designated 10/24/18)
// Calculates setSkill and skillFinal values.  Accounts for all factors including Difficulty SKILL and PRECISION sliders, and CfgAISkill adjustments (made by addons, etc).
// In my experience, accurate to ~ 0.0000001
// Works with ArmA3 v1.84.144923
// If you find any inconsistancies, please report and I'll do my best to fix.
// Remember to call MRU_FSC_Initiate once first.
// Usage:
// call MRU_FSC_Initiate;
// _finalSkill = [_setSkill, _subSkill] call MRU_FSC_GetFinalSkill;
// _setSkill = [_finalSkill, _subSkill] call MRU_FSC_GetSetSkill;

MRU_FSC_Initiate = {
	private ["_subSkills","_conforms","_cfg","_subSkill","_ssArray","_general","_aimingAccuracy","_maxGeneral",
			"_maxAimingAccuracy","_SFG_for_SS1_at_DS0","_SFG_for_SS1_at_DS1","_SFAA_for_SS1_at_DS0","_SFAA_for_SS1_at_DS1",
			"_group","_logic","_skillFinalG","_skillFinalAA"];

	_subSkills = [
		"aimingAccuracy",
		"aimingShake",
		"aimingSpeed",
		"endurance",
		"spotDistance",
		"spotTime",
		"courage",
		"reloadSpeed",
		"commanding",
		"general"
	];

	_conforms = true;

	_cfg = configFile >> "CfgAISkill";
	{
		_subSkill = _x;
		_ssArray = getArray (_cfg >> _subSkill);
		//if ( ((_ssArray select 0) != 0) or ((_ssArray select 2) != 1) or ((_ssArray select 1) > (_ssArray select 3)) ) exitWith { _conforms = false };
		if ( ((_ssArray select 0) < 0) or ((_ssArray select 1) < 0) or ((_ssArray select 2) < 0) or ((_ssArray select 3) < 0) ) exitWith { _conforms = false };
	} foreach _subSkills;

	if (_conforms) then {
		_general = getArray (configfile >> "CfgAISkill" >> "general");
		_aimingAccuracy = getArray (configfile >> "CfgAISkill" >> "aimingAccuracy");

		_maxGeneral = _general select 3;
		_maxAimingAccuracy = _aimingAccuracy select 3;

		// skillFinal "general" for setSkill 1 at Difficulty Skill 0
		_SFG_for_SS1_at_DS0 = _maxGeneral * 0.8;
		// skillFinal "general" for setSkill 1 at Difficulty Skill 1
		_SFG_for_SS1_at_DS1 = _maxGeneral;

		// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 0
		_SFAA_for_SS1_at_DS0 = _maxAimingAccuracy * 0.8;
		// skillFinal "aimingAccuracy" for setSkill 1 at Difficulty Skill 1
		_SFAA_for_SS1_at_DS1 = _maxAimingAccuracy;

		_group = createGroup sideLogic;
		_logic = _group createUnit ["Logic", [0,0,0], [], 0, "NONE"];

		_logic setSkill 1;
		_skillFinalG = _logic skillFinal "general";
		_skillFinalAA = _logic skillFinal "aimingAccuracy";

		deleteVehicle _logic;
		deleteGroup _group;

		MRU_FSC_DifficultySKILL = (_skillFinalG - _SFG_for_SS1_at_DS0) / (_SFG_for_SS1_at_DS1 - _SFG_for_SS1_at_DS0);
		MRU_FSC_DifficultyPRECISION = (_skillFinalAA - _SFAA_for_SS1_at_DS0) / (_SFAA_for_SS1_at_DS1 - _SFAA_for_SS1_at_DS0);
	};
};

MRU_FSC_CfgAISkillAdjusted = {
	private ["_ssArray","_min1","_min2","_max1","_max2","_skillAdjusted","_span1","_span2","_progress"];

	params ["_skill","_subSkill"];

	_ssArray = getArray (configfile >> "CfgAISkill" >> _subSkill);

	_min1 = _ssArray select 0;
	_min2 = _ssArray select 1;
	_max1 = _ssArray select 2;
	_max2 = _ssArray select 3;

	_skillAdjusted = -1;

	if (_skill <= _min1) then {
		_skillAdjusted = _min2;
	} else {
		if (_skill >= _max1) then {
			_skillAdjusted = _max2;
		} else {
			_span1 = _max1 - _min1;
			_span2 = _max2 - _min2;
			_progress = if (_span1 == 0) then {
				// Avoiding division by zero, must choose somewhat arbitrary value for progress
				0.5
			} else {
				(_skill - _min1) / _span1
			};
			_skillAdjusted = _min2 + (_span2 * _progress);
		};
	};

	_skillAdjusted
};

MRU_FSC_GeneralCurve = {
	private ["_curve","_var","_difficulty","_min","_inflection","_max"];

	params ["_subSkill"];

	_curve = [];

	_var = if ((toLower _subSkill) in ["aimingaccuracy", "aimingshake"]) then {
		"MRU_FSC_DifficultyPRECISION"
	} else {
		"MRU_FSC_DifficultySKILL"
	};

	_difficulty = missionNamespace getVariable [_var, -1];
	if (_difficulty >= 0) then {
		_min = 0;
		//_inflection = 0;
		_inflection = 0.01;
		if (_difficulty >= 0.25) then {
			_inflection = 0.6 * ((_difficulty - 0.25) / 0.75);
		};
		_max = 0.8 + (_difficulty * 0.2);
		_curve = [_min, _inflection, _max];
	};

	_curve
};

MRU_FSC_AdjustedCurve = {
	private ["_adjustedCurve","_curve","_min","_inflection","_max","_minX","_inflectionX","_maxX"];

	params ["_subSkill"];

	_adjustedCurve = [];

	_curve = [_subSkill] call MRU_FSC_GeneralCurve;

	if ((count _curve) > 0) then {
		_min = _curve select 0;
		_inflection = _curve select 1;
		_max = _curve select 2;

		_minX = [_min, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
		_inflectionX = [_inflection, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
		_maxX = [_max, _subSkill] call MRU_FSC_CfgAISkillAdjusted;

		_adjustedCurve = [_minX, _inflectionX, _maxX];
	};

	_adjustedCurve
};

MRU_FSC_GetFinalSkill = {
	private ["_finalSkill","_curve","_min","_inflection","_max","_floor","_floorX"];

	params ["_setSkill","_subSkill"];

	_finalSkill = -1;

	_curve = [_subSkill] call MRU_FSC_AdjustedCurve;

	if ((count _curve) > 0) then {
		_min = _curve select 0;
		_inflection = _curve select 1;
		_max = _curve select 2;
			
		_finalSkill = if (_setSkill <= 0.2) then {
			_min + ((_inflection - _min) * (_setSkill / 0.2));
		} else {
			_inflection + ((_max - _inflection) * ((_setSkill - 0.2) / 0.8))
		};

		_floor = _setSkill / 20;
		if (_floor == 0) then { _floor = 10e-038 };
		_floorX = [_floor, _subSkill] call MRU_FSC_CfgAISkillAdjusted;
		if (_finalSkill < _floorX) then {
			_finalSkill = _floorX;
		};	
	};

	_finalSkill
};

MRU_FSC_GetSetSkill = {
	private ["_setSkill","_curve","_min","_inflection","_max"];

	params ["_finalSkill","_subSkill"];

	_setSkill = -1;

	_curve = [_subSkill] call MRU_FSC_AdjustedCurve;

	if ((count _curve) > 0) then {
		_min = _curve select 0;
		_inflection = _curve select 1;
		_max = _curve select 2;
		
		// Avoid division by zero
		if ((_inflection - _min) == 0) then { _inflection = _inflection + 10e-38 };
		
		_setSkill = ((_finalSkill - _min) / (_inflection - _min)) * 0.2;
		
		if (_setSkill > 0.2) then {
			_setSkill = (((_finalSkill - _inflection) / (_max - _inflection)) * 0.8) + 0.2;
		};
	};

	_setSkill
};

 

 

 

 

Then in "init.sqf", paste this and save:

call compile preprocessFile "MRU_FinalSkillCalculator.sqf";
call MRU_FSC_Initiate;

Test via:

// Create AI man in the editor, and call him "Loon"

_difficultySKILL = missionNamespace getVariable ["MRU_FSC_DifficultySKILL", -1];
_difficultyPRECISION = missionNamespace getVariable ["MRU_FSC_DifficultyPRECISION", -1];

_skill = 0.38;
_subSkill = "general";
//_subSkill = "aimingAccuracy";
//_subSkill = "aimingShake";
//_subSkill = "aimingSpeed";
//_subSkill = "commanding";
//_subSkill = "courage";
//_subSkill = "endurance";
//_subSkill = "reloadSpeed";
//_subSkill = "spotDistance";
//_subSkill = "spotTime";

_projectedFinalSkill = [_skill, _subSkill] call MRU_FSC_GetFinalSkill;
Loon setSkill [_subSkill, _skill];
_actualFinalSkill = Loon skillFinal _subSkill;
_finalSkillDelta = _actualFinalSkill - _projectedFinalSkill;
_checkGetSetSkill = [_actualFinalSkill, _subSkill] call MRU_FSC_GetSetSkill;
hint format ["_difficultySKILL: %1\n_difficultyPRECISION: %2\n\n_skill: %3\n_subSkill: %4\n\n_projectedFinalSkill: %5\n_actualFinalSkill: %6\n_finalSkillDelta: %7\n\n_checkGetSetSkill: %8", _difficultySKILL, _difficultyPRECISION, _skill, _subSkill, _projectedFinalSkill, _actualFinalSkill, _finalSkillDelta, _checkGetSetSkill];

Note - Test expanded & function names updated ^

  • Like 1
  • Thanks 1

Share this post


Link to post
Share on other sites

Would it be difficult for you to do an equivalent but "reversed" script 🙂 ?
For example, that we can enter the skill difficulty values, the "desired" skillFinal, and the script calculates the closest skill values 😉 ?

 

That would be great 😉 !

Share this post


Link to post
Share on other sites

Hi Elgin675, just noticed your latest post.  Not sure I completely follow, but please note the formulas do work both ways.  Specifically:

  • If you know your desired Final Skill value, and wish to know the setSkill value which you'll need to use (by calling setSkill) to obtain that Final Skill value, you'll want to use MRU_FSC_GetSetSkill.
  • Conversely if you are starting with a setSkill value, and wish to know the Final Skill value which will result (by calling setSkill), you'll want to use MRU_FSC_GetFinalSkill.

Hope that clarifies it? 🙂

Share this post


Link to post
Share on other sites

Please sign in to comment

You will be able to leave a comment after signing in



Sign In Now

×