Skip to content
William Bowers edited this page Oct 1, 2021 · 7 revisions

Enfusion AI Project Documentation

Installation

Installing AI on your server is straightforward, but configuring them can take time and patience.

To begin, ensure that you have Community Framework (CF) installed. eAI cannot be installed without CF.

General Installation

In your server root, (or wherever you install your mods,) obtain a copy of the mod as a folder (named @eAI.) Install it alongside your other mods, then add @eAI to your launch line, i.e. -mod=“@CF;@eAI”.

Finally, move the key from @eAI/Keys to your server keys folder. The key will likely be named ralian_vXXX.bikey.

image

Installation on a Managed Server

Your GSP likely has a way to automatically install the mod from the steam workshop. In that case, Subscribe to eAI on the workshop using this link: https://steamcommunity.com/sharedfiles/filedetails/?id=2542638513

Configuration

Before configuring your server, launch it for the first time with eAI enabled. In order to completely initialize it, you should use an admin tool to spawn any AI. (Just search for “eAI” in any admin tools to find the AI versions of each unit.)

This will generate several configuration files, all of which will be in a folder named “eAI” under the Server Profile folder. This folder will contain the following files:

image

All of the “.c” (Enforcescript) files in this directory are code for the AI’s finite state machine. (Don’t touch those.) However, we will need to edit the three files below that.

The first file is eAIAdmins.json. This file dictates who on the server can use the radial debug menu to spawn AI. To add yourself to this, put your admins’ Steam IDs in the empty array the file generated:

[
    "76561198012321222",
    "12361198035721222"
]

(The file should look like this - the two IDs are fake examples of steam users who would have access.)

Next, configure the eAISettings file the way you want. The file initially looks like this:

{ 
 	"eAIDebug": 0,
    "eAIAccuracy": 0.5
}

Leave debug mode at 0. Setting it to 1 enables more verbose logging about AI positions, which will make your log files very large over time.

eAIAccuracy dictates how quickly the AI aim on target, and how sloppy their shooting is. This should be set between 0 and 1. Setting it close to 0 can cause strange behavior, and a more sophisticated difficulty system will be implemented in the future.

Setting Up Loadouts

The last file in the eAI directory is the default loadout file. Initially, it looks like this:

{
    "Shirts": [
        "eAIShirt"
    ],
    "Pants": [
        "SlacksPants_Blue"
    ],
    "Shoes": [
        "HikingBootsLow_Blue"
    ],
    "BackPacks": [
        "TaloonBag_Blue"
    ],
    "Vests": [
        "SmershVest"
    ],
    "Headgear": [
        "BaseballCap_Blue"
    ],
    "Gloves": [
        "SurgicalGloves_Blue"
    ],
    "Misc": [
        "CivilianBelt"
    ],
    "ClothesHealth": [
        75,
        100
    ],
    "WeaponMelee": [
        "MeleeBat"
    ],
    "WeaponRifle": [
        "M4A1",
        "SVD",
        "AKM"
    ],
    "WeaponRifleMagCount": [
        1,
        3
    ],
    "WeaponHandgun": [
        "MakarovIJ70"
    ],
    "WeaponHandgunMagCount": [
        1,
        3
    ],
    "WeaponHealth": [
        75,
        100
    ],
    "Loot": [
        "SodaCan_Cola",
        "Screwdriver",
        "ChernarusMap",
        "NailBox",
        "PeachesCan",
        "Pot",
        "Potato",
        "Pliers"
    ],
    "LootRandom": [
        "Screwdriver"
    ],
    "LootChance": [
        25,
        50,
        100,
        25,
        25,
        25,
        25,
        25
    ],
    "LootHealth": [
        75,
        100
    ],
    "Locked": 0
}
Array Meaning
Shirts A list of classnames of shirts, whichthe AI will be given one of at random.
Pants Same as Shirts, but for the Pants slot.
Shoes Same as Shirts, but for the Shoes slot.
BackPacks Same as Shirts, but for the Backpack slot.
Vests Same as Shirts, but for the Vests slot.
Headgear Same as Shirts, but for the Headgear slot.
Gloves Same as Shirts, but for the Gloves slot.
Misc Not yet implemented.
ClothesHealth The clothes on the AI are randomly assigned a health, between the first and second number.
WeaponMelee Not yet implemented.
WeaponRifle The primary weapon which the AI spawn with, chosen at random from the list. Make sure it is a magazine fed weapon.
WeaponRifleMagCount The amount of magazines randomly given to the AI, between the first number and second number. All magazines are filled.
WeaponHandgun Not yet implemented.
WeaponHealth Similar to previous health options, the minimum and maximum wear on the AI’s weapons, applied at random.
Loot Items in this list are given to the AI when they spawn, with an optional random chance given in LootChance.
LootChance Chance of the corresponding item spawning on each AI with this loadout. 0-100, higher is greater chance.
Locked If set to 1, the AI's inventory will be inaccessible to players after death.

Your job is to customize it so your AI will spawn with the equipment you choose. You can use a list of the classnames in the vanilla game here: https://github.com/CypherMediaGIT/DayZClassNames2020/blob/master/classname2020

You should know that you can also make different loadouts for different AI. Simply copy the file, and rename it to something sensible. An example would be SoldierLoadout.json. We will talk about how to set up patrols with custom loadouts in the following section.

Setting Up Dynamic Patrols

Now that you have set up your loadouts, it’s time to create some patrols. The default mission init file which you can download will be the starting point for our edits. The file contains patrols for chernarus, but you can also find patrols made for other maps.

If you don’t have the default init file, you can find a copy here: https://github.com/ralian/eaiSampleMission

The default list of patrols looks like this:

ref array<vector> patrol_1  = {"5237.821777 9.568775 2157.804199", "4887.835449 9.560701 2578.265625", "4917.622559 9.539299 2599.980469"};

// Patrol 2 = SW Rail Line
ref array<vector> patrol_2  = {"41.383652 6.637133 1568.725464", "167.029785 6.660925 1589.156372", "399.000854 6.637136 1605.268677", "450.407135 6.660660 1618.758667", "482.006226 6.711864 1632.124023", "799.668762 6.740325 1846.262695", "847.898071 5.795653 1886.695801", "923.040100 5.795653 1959.974121", "976.875427 5.795653 2031.899414", "1050.173218 6.664209 2158.251709", "1157.952637 5.795209 2307.297119", "1187.608032 6.407721 2340.853271", "1259.927368 6.011772 2363.105225", "1333.362915 6.637136 2342.896973", "1469.527222 6.721370 2247.018555", "1514.207764 5.795653 2222.483398", "1572.351074 6.581270 2193.880371", "1639.594482 6.438149 2169.135010", "1696.991211 5.795653 2156.455811", "1763.947998 6.373237 2143.247314", "1889.594116 6.014451 2166.340820", "2079.179688 6.529642 2219.987549", "2176.673584 6.011375 2196.734375", "2244.440430 5.995491 2087.459229", "2348.422119 5.795653 2048.190186", "2641.000000 6.692032 1985.662354", "2772.157471 6.697611 1985.912354", "3044.920166 5.795653 2044.465820", "2772.157471 6.697611 1985.912354", "2641.000000 6.692032 1985.662354", "2348.422119 5.795653 2048.190186", "2244.440430 5.995491 2087.459229", "2176.673584 6.011375 2196.734375", "2079.179688 6.529642 2219.987549", "1889.594116 6.014451 2166.340820", "1763.947998 6.373237 2143.247314", "1696.991211 5.795653 2156.455811", "1639.594482 6.438149 2169.135010", "1572.351074 6.581270 2193.880371", "1514.207764 5.795653 2222.483398", "1469.527222 6.721370 2247.018555", "1333.362915 6.637136 2342.896973", "1259.927368 6.011772 2363.105225", "1187.608032 6.407721 2340.853271", "1157.952637 5.795209 2307.297119", "1050.173218 6.664209 2158.251709", "976.875427 5.795653 2031.899414", "923.040100 5.795653 1959.974121", "847.898071 5.795653 1886.695801", "799.668762 6.740325 1846.262695", "482.006226 6.711864 1632.124023", "450.407135 6.660660 1618.758667"};

// Patrol 3 = Cherno Port
ref array<vector> patrol_3  = {"7332.387695 5.712450 2651.121338", "7632.803223 5.507639 3075.661865", "7621.013184 5.892049 3083.053223", "7321.484375 5.448769 2658.244141"};

// Patrol 4 = Elektro Yard
ref array<vector> patrol_4  = {"10825.153320 5.894602 2555.271240", "10853.957031 6.433348 2635.236816", "10889.441406 5.977321 2749.464111", "10852.090820 6.012342 2760.362793", "10828.861328 6.012496 2730.357910", "10794.523438 6.012444 2636.897705", "10779.160156 6.012487 2593.712158", "10781.082031 6.004701 2574.147949"};

// Patrol 5 = Solnechny south
ref array<vector> patrol_5  = {"13392.708984 5.809348 5952.577637", "13369.891602 6.012489 5624.214844", "13373.618164 5.943049 5486.794434",  "13369.891602 6.012489 5624.214844"};

// Patrol 6 = Rify Trail
ref array<vector> patrol_6  = {"13342.375977 27.020344 11228.225586", "13469.069336 11.282786 11163.261719", "13567.762695 6.128973 11127.067383", "13620.910156 8.011324 11244.537109", "13567.762695 6.128973 11127.067383", "13469.069336 11.282786 11163.261719"};

// Patrol 7 = Nizhneye
ref array<vector> patrol_7  = {"12879.688477 5.743166 8590.398438", "12983.769531 6.060859 8323.492188", "13000.213867 6.062475 8330.422852", "12967.809570 5.781274 8447.635742"};

// Patrol 8 = NWAF North
ref array<vector> patrol_8  = {"4078.499268 339.012421 10802.854492", "4277.165039 339.012360 10922.452148", "4271.527832 339.008148 10938.139648", "4064.494385 339.012421 10818.675781"};

// Patrol 9 = NWAF south
ref array<vector> patrol_9  = {"4772.428711 338.964355 9583.963867", "4639.255859 339.012390 9815.970703", "4978.569336 339.012451 10004.463867", "4639.255859 339.012390 9815.970703"};

// Patrol 10 = Tisy
ref array<vector> patrol_10 = {"1724.632080 451.730408 14298.412109", "1681.035522 451.784302 14278.791016", "1668.385742 451.730408 14268.739258", "1653.012695 451.784302 14245.443359", "1662.009521 451.784302 14220.044922", "1668.885010 451.730408 14177.100586", "1678.905029 451.784302 14134.460938", "1687.656250 451.784302 14097.293945", "1691.055054 451.728760 14072.710938", "1697.563721 450.094635 14019.088867", "1701.266235 448.636108 13981.079102", "1709.151733 448.368225 13969.950195", "1726.191772 447.882568 13954.740234", "1734.303101 447.342072 13945.562500", "1736.849487 447.158569 13939.061523", "1803.257568 436.318970 13812.399414", "1802.478149 435.682281 13794.238281", "1764.742676 434.271759 13667.715820", "1753.983521 434.111572 13660.847656", "1697.765381 433.031982 13621.431641", "1625.751953 437.192169 13583.775391", "1615.443726 437.193420 13582.367188", "1586.155762 439.010864 13609.286133", "1566.816040 441.081787 13643.261719", "1554.550049 441.587372 13645.919922", "1533.254028 440.683624 13646.856445", "1470.583130 444.685120 13679.353516", "1448.422729 446.229736 13681.502930", "1397.534424 448.255707 13663.409180", "1377.747681 448.799530 13661.333984", "1356.402222 451.658264 13669.961914", "1339.632813 454.855988 13690.362305", "1311.232178 457.548615 13730.733398", "1368.332275 455.560181 13762.721680", "1389.098022 453.795563 13785.572266", "1394.411499 451.237183 13801.572266", "1398.934204 451.644989 13828.162109", "1419.962524 456.061951 13891.482422", "1409.579224 458.430359 13947.408203", "1421.006104 459.254517 13993.981445", "1422.121094 459.807831 14022.114258", "1388.509277 462.222443 14044.217773", "1388.548340 462.354797 14044.183594", "1333.772217 463.948151 14062.096680", "1350.346069 465.658081 14107.701172", "1461.509521 459.874268 14133.793945", "1503.123291 455.995056 14137.425781", "1554.196045 452.867004 14107.243164", "1574.911743 452.540283 14100.198242", "1629.664673 451.792175 14197.793945", "1636.238647 451.792175 14221.260742", "1671.278564 451.792175 14275.020508", "1645.910522 451.807617 14315.247070", "1669.397339 451.792175 14331.668945", "1685.370361 451.792175 14341.243164"};

// If you add another patrol array, add it to this list
ref array<array<vector>> patrol_list = {patrol_1, patrol_2, patrol_3, patrol_4, patrol_5, patrol_6, patrol_7, patrol_8, patrol_9, patrol_10};

// IMPORTANT: If you add an entry to the above list, pick the loadout for it by adding the loadout filename to this list
ref array<string> patrol_loadouts = {"HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json"};

// you may change this quantity as well
const int NUMBER_PER_PATROL = 2;

// channel your inner M.C. Hammer - can't touch this
autoptr array<autoptr eAIDynamicPatrol> patrols = {};

void InitDynamicPatrols() {
	for (int i = 0; i < patrol_list.Count(); i++) {
		string loadout = "SoldierLoadout.json"; // default
		if (i < patrol_loadouts.Count()) loadout = patrol_loadouts[i];
		autoptr eAIDynamicPatrol pat = new eAIDynamicPatrol(patrol_list[i][0], patrol_list[i], loadout, NUMBER_PER_PATROL);
		patrols.Insert(pat);
		pat.UpdateTriggers();
	}
}

To create a new patrol, you should follow these steps:

  1. Create a list of 3D coordinates which you want the patrol to follow. Name it something sensible, like patrol_11.
  2. After you have created this patrol between line 28 and 30, you need to add it to the master patrol array on line 31. The new array would read ref array<array<vector>> patrol_list = {patrol_1, patrol_2, patrol_3, patrol_4, patrol_5, patrol_6, patrol_7, patrol_8, patrol_9, patrol_10, patrol_11};
  3. If you have made a custom loadout file for this patrol - let’s say you did, and named it SpecOpsLoadout.json - then add that to the loadouts array. The new line 34 would look like this: ref array<string> patrol_loadouts = {"HumanLoadout.json", "HumanLoadout.json","HumanLoadout.json","HumanLoadout.json", "HumanLoadout.json","HumanLoadout.json","HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "HumanLoadout.json", "SpecOpsLoadout.json"};
  4. If you want, you can modify the amount of AI per patrol in NUMBER_PER_PATROL. You can also embed that parameter in an array, just like what has been done for the loadout.
  5. Do not forget to call the InitDynamicPatrols() function in your main function.

There are actually a few more parameters to the eAIDynamicPatrol system. You can create new individual dynamic patrols, but make sure you store it in a hard reference (autoptr Dynamic_Patrol_2 = new eAIDynamicPatrol(...) for instance) or it will not be persistent.

The minR parameter is especially important. If a player is within 300m of where the patrol would spawn, it will not spawn to avoid instantly targeting and killing them.

Because of this, you can’t just expect to teleport to the location where you put your AI; they will NOT spawn if you do that.

Considerations

The AI aren’t perfect. However, here are a few pointers to help you get the most out of them.

  • The AI can currently only use weapons which have a magazine (no single shot loading is implemented.) Because of this, many shotguns (such as the BK-43) and other specialized weapons will not yet function.
  • Because of the way aim synchronization has to happen between the server and client, the AI will often have trouble aiming or even spin if a client’s ping is too high (>150.) If the AI seem unplayable to you, try playing on a server in your region.
  • To avoid players farming AI endlessly, do not give them elite tier weapons and loot. Giving them damaged weapons may also help.

Reporting Bugs

Please report bugs to our github page. Ensure there is not an open issue with the same crash or problem before you post. https://github.com/ralian/eai/issues

All issues should include client and server logs when possible. They can be found in your profiles folder (Usually the ServerProfile folder on a server), and located in C:\Users\wtb0019\AppData\Local\DayZ for the client.

Include the latest log named script_XYZ.log unless asked for the RPT file instead.

Credits

Developers

Ralian

Darc

Jacob Mango

Graphics

Darc

Special Thanks

Wardog

MarioE

Sovran

Locnar

Wilmson

Everyone on the DayZ Expansion Team

All our beta testers (All 150 of you!)