mNo edit summary |
No edit summary |
||
Line 126: | Line 126: | ||
| rotationSpeed || number (integer) || 720 || Speed at which the enemy can rotate. Degrees per second. | | rotationSpeed || number (integer) || 720 || Speed at which the enemy can rotate. Degrees per second. | ||
|} | |} | ||
=== Using custom logics === | |||
Logics that you have defined in JSON can be applied to a sprite in the .O file in the same way as other (hardcoded) enemy logics. For example, if you have the following logic | |||
<pre> | |||
"logicName": "Rodian", | |||
"data": { | |||
"alertSound": "rodian1.voc", | |||
.... | |||
} | |||
</pre> | |||
Apply it to a sprite (WAX) like this, and then the sprite should behave with the custom logic. | |||
<pre> | |||
SEQ | |||
LOGIC: Rodian | |||
SEQEND | |||
</pre> | |||
You can also use JSON-defined logics with generators, for example | |||
<pre> | |||
SEQ | |||
LOGIC: GENERATOR Rodian | |||
DELAY: 10 | |||
INTERVAL: 5 | |||
NUM_TERMINATE: 6 | |||
MAX_ALIVE: 1 | |||
MAX_DIST: 800 | |||
MIN_DIST: 50 | |||
SEQEND | |||
</pre> | |||
'''Important''': If a level with a custom logic is run with vanilla Dark Forces or the Dark Forces Remaster, the sprites will be "lifeless". However, if the level has generators with a custom logic, this causes the original game to crash. |
Revision as of 07:17, 28 October 2024
This article explains how to use custom (user-defined) AI logics in TFE.
This feature allows modders to create new enemy logics that behave similarly to the "standard" (non-boss) enemies in Dark Forces, for example Officers, Stormtroopers, Bossk, Probe Droids.
JSON locations
Custom logics are defined in JSON files. TFE will search for and load logics from all logic .JSON files found in these locations
- DARK\Logics
- TheForceEngine\Mods\Logics
where DARK is your Dark Forces game directory which TFE is pointing to.
In addition, if you are running a Mod from a ZIP, TFE will look for a \Logics
subfolder inside the ZIP and load logics from any .JSON files found there.
Logics loaded from a Mod will take preference over any logics found in other locations that have the same name.
You can have as many logic .JSON files as you want, and each .JSON file can contain as many logics as you want. If multiple logics are loaded that have the same name, only the first one encountered with that name will be used. If you have multiple .JSON files, the order in which they are loaded by TFE is unpredictable, so the only way to be sure that your logics work the way you intend is to avoid duplicate names.
Logic JSON structure
A logic JSON should consist of a single array called "logics".
Each element of the "logics" array must consist of two items, in this order
- "logicName" - a string which is the name of the logic
- "data" - an object containing properties that are set for the logic
Example:
{ "logics": [ { "logicName": "LogicA", "data": { "alertSound": "alert1.voc", "painSound": "pain1.voc", "hitpoints": 25, "projectile": "rifle_bolt" } }, { "logicName": "LogicB", "data": { "alertSound": "alert2.voc", "painSound": "pain2.voc", "hitpoints": 39, "speed": 20, "projectile": "thermal_det", "dropItem": "red_key" } } ] }
Logic JSONs are not case sensitive. The text inside the JSON can be all uppercase, all lowercase, or any mix of upper and lowercase; it will all be read the same.
For example, "alertsound", "ALERTSOUND", "AlertSound", "alertSound" will all be accepted.
Further tips about getting the JSON format right can be found on the TFE MOD Overrides article.
Data properties
Here is a list of the properties that you can set for each logic.
You do not need to set every property. Unset properties will default to the values shown.
Properties can be listed in any order. Property names are not case sensitive, but need to be spelled correctly.
Property | Type | Default value | Description |
---|---|---|---|
hasGravity | boolean | true |
If true, will be affected by gravity |
isFlying | boolean | false |
If true, will be a flying enemy (like probe droid) |
fieldOfView | number (integer) | 210 | Field of view, in degrees. If set to 360, the enemy will be able to see you even if you are standing behind it. |
awareRange | number (integer) | 20 | If you are within this distance from the enemy (in DFU), it will be alerted even if you are outside its field of view. (However, this can be affected by other things, such as the ambient light.) |
alertSound | string | no sound | VOC file to play when enemy is alerted |
painSound | string | no sound | VOC file to play when enemy takes pain |
dieSound | string | no sound | VOC file to play when enemy dies |
attack1Sound | string | no sound | VOC file to play for primary attack |
attack2Sound | string | no sound | VOC file to play for secondary attack |
hitPoints | number (integer) | 4 | Hitpoints. The enemy will die when it reaches 0. |
dropItem | string or number | -1 (nothing) | What item is dropped when the enemy dies. See table below for options. |
dieEffect | string or number | -1 (nothing) | An effect (eg. explosion) that will play when the enemy dies. See table below for options. |
hasMeleeAttack | boolean | false |
If true, the enemy will have a melee attack. If the enemy has a melee attack, it will always be the primary attack. |
hasRangedAttack | boolean | true |
If true, the enemy will have a ranged attack. If the enemy has a melee attack, the ranged attack will be the secondary attack. If it does not have a melee attack, the ranged attack will be the primary attack. |
litWithMeleeAttack | boolean | false |
If true, the sprite will light up when it attacks with melee. |
litWithRangedAttack | boolean | true |
If true, the sprite will light up when it does its ranged attack. |
projectile | string or number | "rifle_bolt" |
What projectile the enemy will fire for its ranged attack. See table below for options. |
wanderTime | number (integer) | 300 | How long (in seconds) the enemy will keep looking for the player, before it returns to its idle or "asleep" state. This time is reset when the enemy is re-alerted by the player. |
rangedAttackDelay | number (decimal) | 2 | Time delay in seconds between range attacks (the game engine adds some variability to this) |
meleeAttackDelay | number (decimal) | 0 | Time delay in seconds between melee attacks |
meleeRange | number (integer) | 0 | Range (in DFU) of melee attack - how far the player can be from the enemy to be hit by it. |
meleeDamage | number (integer) | 0 | Damage that each melee attack will inflict. |
minAttackDist | number (integer) | 0 | Minimum distance from the player (DFU) where the enemy will attack with its ranged weapon. |
maxAttackDist | number (integer) | 160 | Maximum distance from the player (DFU) where the enemy will attack with its ranged weapon. |
fireSpread | number (integer) | 30 | Accuracy of fire. Lower numbers are more accurate. |
speed | number (integer) | 4 | Speed of enemy's movement, appears to be in DFU per second. For flying enemies, this is their horizontal speed. |
verticalSpeed | number (integer) | 10 | For flying enemies only, this is their vertical speed. |
rotationSpeed | number (integer) | 720 | Speed at which the enemy can rotate. Degrees per second. |
Using custom logics
Logics that you have defined in JSON can be applied to a sprite in the .O file in the same way as other (hardcoded) enemy logics. For example, if you have the following logic
"logicName": "Rodian", "data": { "alertSound": "rodian1.voc", .... }
Apply it to a sprite (WAX) like this, and then the sprite should behave with the custom logic.
SEQ LOGIC: Rodian SEQEND
You can also use JSON-defined logics with generators, for example
SEQ LOGIC: GENERATOR Rodian DELAY: 10 INTERVAL: 5 NUM_TERMINATE: 6 MAX_ALIVE: 1 MAX_DIST: 800 MIN_DIST: 50 SEQEND
Important: If a level with a custom logic is run with vanilla Dark Forces or the Dark Forces Remaster, the sprites will be "lifeless". However, if the level has generators with a custom logic, this causes the original game to crash.