How to Dynamically Modify Threat Levels in Unit Blueprints Based on Unit Stats
-
Hi everyone,
I’m working on creating a mod for FAF (Forged Alliance Forever) that dynamically modifies the Threat Levels of all units during runtime, without manually editing .bp files. The goal is to implement a system that calculates new Threat Levels based on the unit's stats (like DPS, range, and HP) and applies these changes automatically.
This idea stems from my desire to make my games with the M28AI mod more interesting and balanced. I love the M28AI, but I’ve noticed that units with very low Threat Levels tend to be too passive, while units with overly high Threat Levels can be excessively aggressive. By dynamically recalculating Threat Levels to better represent the capabilities of each unit, I believe this will not only improve how M28AI plays but also enhance the performance of other AI mods or systems that rely on Threat Levels for decision-making.
The specific section I want to adjust in the .bp files looks like this:
Defense = { AirThreatLevel = 0, ArmorType = 'Normal', EconomyThreatLevel = 0, Health = 13000, MaxHealth = 13000, RegenRate = 0, SubThreatLevel = 0, SurfaceThreatLevel = 50, },
What I Want to Achieve:
- Dynamically calculate Threat Levels using relevant unit stats:
-
- Combat units: Use stats like DPS, range, and HP to compute values for AirThreatLevel, SurfaceThreatLevel, and SubThreatLevel.
-
- Economic units: Use mass and energy production to calculate EconomyThreatLevel.
- Apply these calculations at runtime without manually editing .bp files, ensuring compatibility with all units (including those added by other mods).
- Ensure compatibility with AI mods like M28AI and potentially improve how AI systems interact with units based on their adjusted Threat Levels.
My Current Approach:
I believe this can be achieved by overriding the UnitBlueprint function in Lua, which processes unit blueprints during game loading. The idea would be to intercept each blueprint, extract the relevant stats, calculate new Threat Levels, and update the blueprint accordingly.
Here’s a rough outline of how I imagine the logic:
local OldUnitBlueprint = UnitBlueprint function UnitBlueprint(bp) if bp.Defense then -- Example calculation for combat Threat Levels local dps = 0 local range = 0 local hp = bp.Defense.MaxHealth or 0 if bp.Weapon then for _, weapon in ipairs(bp.Weapon) do dps = dps + (weapon.Damage or 0) * (weapon.RateOfFire or 1) range = math.max(range, weapon.MaxRadius or 0) end end -- Calculate new Threat Levels dynamically if dps > 0 and range > 0 then bp.Defense.SurfaceThreatLevel = (dps * range) / 1000 + (hp / 1000) end -- Example calculation for economic Threat Levels if bp.Economy then local massProd = bp.Economy.ProductionPerSecondMass or 0 local energyProd = bp.Economy.ProductionPerSecondEnergy or 0 bp.Defense.EconomyThreatLevel = (massProd * 10) + (energyProd / 10) end end -- Call the original function to ensure other mods and game features work OldUnitBlueprint(bp) end
Questions:
- Is overriding UnitBlueprint the best way to dynamically adjust Threat Levels based on unit stats?
- For extracting stats like DPS and range from weapons, is there a better approach than iterating over bp.Weapon?
- How can I ensure compatibility with other mods and make sure my mod loads after them?
- Are there any potential performance concerns with dynamically calculating Threat Levels for all units during loading?
- Has anyone implemented a similar system, and are there best practices or caveats I should keep in mind?
Why This Matters:
My main objective is to improve how AI mods like M28AI handle units, but I suspect this would also apply to other AI mods or systems I haven’t yet considered. By dynamically calculating Threat Levels based on unit stats, I hope to make the AI’s decision-making more logical and engaging, resulting in more interesting games.
I’d love to hear any advice, suggestions, or experiences you might have. Thank you in advance for your help!
-
While I'm not sure on the answer to your question, I'd note that if you do find a solution it won't impact M28AI (or M27AI) as they use custom threat assessments, so the threat values in the blueprint have no impact on them.
Blueprint threat values are relevant to AIs that use the built in "IMAP" threat system - at a high level, this divides the map into squares, and calculates the threat of units in that square with a few different threat types. The threat then decays gradually over time when the units leave that square. -
The LOUD project has extensive work on threat values - and as you have already determined, modification of the blueprint values, at runtime, is the most accessible method of doing this. Your intent is aimed in the right direction - without meaningful, and accurate, threat values, no AI can make informed decisions based off of the data - and this is a long standing problem going back to the release of the game. No direction was provided, so many unit authors just dropped in values that made sense to them, and were not related to any particular capability of the units.
Since you have no surefire method of controlling the load order of mods, your blueprint changes are at the mercy of any other unit mods that may alter the values. As Maudlin identified, he sidestepped this issue by performing calculations similar to what you are proposing, but doing so on the fly, during the game. This gets around any question of the values in use, but it may have performance concerns - doing it during loading has no impact on the performance of the game itself.
DPS calculations are going to be an issue, as unit authors are not exactly consistent in how they arrive at the kind of damage output their units will put out. There are MANY combinations and control values that can impact the actual DPS calculation - it goes far beyond the 'RateOfFire' blueprint value, which many consider to be the 'be all and end all' of DPS calculations. It is not - and anyone who has done sufficient weapon 'rigging' can tell you the many pitfalls, in the blueprint values, that can lead to an erroneous DPS value.
One issue that is not addressable is the impact of unit enhancements on DPS - as the blueprint threat values, once set, are static for the duration.
-
For FAF the file
blueprints-ai.lua
already calculates threat levels from unit stats, you'd want to overwrite the functionSetThreatValuesOfUnit
in there to properly replace the threat level calculation. This also means you don't have to worry about mod load order since that threat calculation is done inPostModBlueprints
.There are MANY combinations and control values that can impact the actual DPS calculation - it goes far beyond the 'RateOfFire' blueprint value, which many consider to be the 'be all and end all' of DPS calculations. It is not - and anyone who has done sufficient weapon 'rigging' can tell you the many pitfalls, in the blueprint values, that can lead to an erroneous DPS value.
@Sprouto There's a rather comprehensive DPS calculation in FAF's
unitviewdetail.lua
, I think it's only missing fire rate when it is limited by energy requirements or animation speed, and if I remember correctly some rounding for some stat should be floored instead, but it's still quite complete. I should really put that calculation in a library. -
There are a lot of other factors that can contribute to the 'threat' of a unit, beyond DPS, namely range, mobility & protection (HP) - all have a part to play in how 'threatening' a unit can be - and a discussion of those is likely beyond the scope of the question here. I'm not familiar with the contents of the FAF threat calculation, in as much as dovetailing threat to DPS was a big step forward when we introduced it in LOUD back in the day, so I was pleased to see that idea taken to heart some years later in FAF. It was abundantly clear, especially for AI (and the long standing issues with Sorian AI's in particular) that threat assessment, and understanding how it works internally, has a lot of follow-on impact on how any AI 'reads' a situation, and the map. It impacts almost every aspect of activity, pathfinding for example, and building position and selection, to name just two.
-
The blueprints-ai.lua file that Nomander referenced is what you want to look at.
Your obviously motivated to do something positive here. I don't believe we can dynamically adjust threat during the game as the blueprints are read once on startup sort of thing. BUT, I think the development team would be more than happy to see someone put time into improving the existing threat calculations that we are creating. The current calculations are based on a formula that Balthazar did however long ago and some adjustments since then and while it is ALOT better than what we had originally there is room for improvement.
You'll eventually hit limitations since threat also depends on situational factors (a sniper bot in dangerous unless an enemy Obsidian tank is sitting next to it sort of thing). So why not rather than making a mod look at improving what we are doing now? We're happy to collaborate and provide feedback on the impact it makes on AI games.