Modding CoW AI - Robosturm/Commander_Wars GitHub Wiki

Note this page requires CoW Beta Release 26 or newer.

Understanding and modding what units the ai builds

In previous releases the ai was able to build all units from custom mods. In the new system you need to tell the ai when to build your new units.

The ai has 3 callback routines you can mod/customize either for a map or for your mod. Those functions are:

  • initializeSimpleProductionSystem : function(system, ai, map)

    This function is called at the start of a match and must return true if the ai should use this system or fallback to the build system of the earlier releases

  • onNewBuildQueue : function(system, ai, buildings, units, enemyUnits, enemyBuildings, map)

    This function is called at the beginning of each production cycle and can be used to customize what the ai should prioritize to build

  • buildUnitSimpleProductionSystem : function(system, ai, buildings, units, enemyUnits, enemyBuildings, map)

    This function is called every time the ai should build a unit and must return true if a unit was build or false otherwise normally you stick with the default implementation here

There are 3 steps in which the ai tries to build a unit which can be manipulated via the js-scripts.

  • Step 1 is the initial production you can tell the ai to build a given amount of units from a certain type ignoring all following rules

    system.addInitialProduction(["INFANTRY"], 6);

    This call forces the ai to build 6 infantry over everything else, till it has build 6 infantries. This production is maintained over savegames and only removed from the build stack once all given units where build.

  • Step 2 if it was unable to build a unit from the inital production or this list is empty the ai tries to build units from the forced production list.

    system.addForcedProduction(["FLAK", "FIGHTER", "MISSILE"], -1, -1);

    The first argument is an array of units which are tried to be produced from left to right. The ai will try to build an anti-air (FLAK) if this wasn't possible it tries to build a fighter(FIGHTER) and so on. The next two arguments are the position at which the ai will try to build the unit -1 or no arguments means the ai will try to build the unit at any production building on the map.

    Once a unit from the list was build the forced production will be popped from the build stack. The list of forced productions is reset at the start of each production cycle. So you can safely force the ai to build anti air units for a turn and the ai will default back to the normal production the next turn.

  • Step 3 This is the most complex step and also the easiest to manipulate. The script coreai.js contains several groups like the following, which can be extended via modding or a map script.

infantryGroup : ["INFANTRY_GROUP", ["INFANTRY", "MECH", "SNIPER", "MOTORBIKE", "ZCOUNIT_PARTISAN", "ZCOUNIT_COMMANDO", "ZCOUNIT_RANGER", "ZCOUNIT_AT_CYCLE"], [70, 10, 20, 30, 10, 30, 10, 10], 40, 0, "", 0.3]

  • The first entry is a fixed string that can be used to identity the group in the c++ code.

  • The second entry is a list of units assigned to the group

  • The third entry is the chance for each unit to be build. Those values are influenced by the co meaning max will build more tanks and grit more artilleries. The ai rolls a dice against the sum of all entries and builds the rolled unit. E.g. 20 40 40 means the ai will build the first unit with chance of 20% the second unit with a chance of 40 % and the third unit with a chance of 40%. If a unit can't be build by the ai e.g. cause it's a co unit not buildable by the co the chance for that unit is set to 0.

  • The fourth argument is the distribution of this group for the whole army. E.g. if the ai has 4 groups with a value of 50. The ai will try to have the same distribution of each group in the army. Groups can be disabled e.g. naval units won't be build on ground units meaning the value sin't representing a percentage.

  • The fived argument is the build mode this is used for a simple activation/deactivation of a group based on the income and current day.

  • The sixth argument is the name of a js-function which can be used for a more complex activation/deactivation of a group normally you can set this to an empty string.

  • The ai will build units based on the unit group it's missing the most units to reach the target distribution however if it can't build a unit of that group it will build units of the next group it's missing a distribution. E.g. if it should build a high tier unit and it's missing funds it'lll build a low tier unit for example. This leads to the ai to spam infantry-group units if it can't build other units. To prevent that the seventh argument is the maximum distribution the ai will aim to build with a unit. E.g. a value of 0.3 means the ai will not have more than 30% units of that group on the battlefield.

  • The final table you may want to modify is the table which determines which tables defined previously are active. You can add or remove entries in the following table:

fundsModes : [[0, 0, 0, 0], [2, 12000, 0, 1], [3, 9000, 0, 1], [3, 18000, 0, 2], [0, 22000, 0, 3], [6, 22000, 1, 3], [6, 30000, 2, 3],],

  • The first value is the minium match day for a column to be active
  • The second is the minimum funds the ai needs to have
  • The third is the minimum active mode defined in the which a group needs to have to be used by the ai
  • The fourth is the maximum active mode defined in the which a group needs to have to be used by the ai

The ai is programmed to build landers and naval/air units on a higher rate once it detects that ground units can't reach the enemy around turn 3 and on going. It'll also build infantry transporters (t-copter, blackboats) if it finds enemey/neutral buildings on islands and tries to capture them with those copters in most cases.

Refer to the coreai.js to the vanilla groups or to get an idea on how to mod the build system Refer to the header file SimpleProductionSystem.h to see which functions you can use to influence the ai-build system

Understanding and modding what the ai does with a unit

You can mod/overwrite what the ai does with any unit using the predefined ai behaviour, by implementing one of the following or both functions either for a map script or in the corresponding ai-script.

  • GenericPredefinedUnitMapScriptBehaviour : function(ai, action, unit, enemies, enemyBuildings, map)

    This overwrites the behaviour of any unit using the default ai. If no move is done with the unit the ai will default to it's normal behaviour

  • PredefinedUnitMapScriptBehaviour = function(ai, action, unit, enemies, enemyBuildings, map)

    This overwrites/defines the behaviour of units assigned with the predefined ai behaviour.

In both functions you can do the same things the only difference is the timing and context when one of those functions gets called.

The following functions allow you to get all points to which the ai can move with a unit in this turn:

var pfs = unit.createUnitPathFindingSystem(unit.getOwner()); var points = pfs.getAllQmlVectorPoints();

The following would make the ai move the unit to the coordinates given by target and let it wait there:

var path = pfs.getPath(target.x, target.y); action.setActionID("ACTION_WAIT"); action.setMovepath(path, pfs.getCosts(path));

Another useful ai function is the following, which is used by the ai to move towards targets which are more than 1 turn away.

var pfs = ai.createTargetedPfs(unit, [Qt.vector3d(0, 0, 1), Qt.vector3d(1, 1, 2)])

Each Qt.vector3d(0, 0, 1) call is a pair of x,y coordinates and a virtual distance multiplier greater or equal to 1. The greater the distance multiplier the less likely the ai will move towards that target, The returned path finding system can be used to let the move towards targets further away.

Both functions need to return true in order to execute the action or false to skip it and do nothing with that unit for now.

As reference for which functions you can call. You can use the file coreai.h.