Invisible, Inc.

Invisible, Inc.

Not enough ratings
Invisible Inc: Modding Notes For Dummies
By Hekateras
This "guide" is a short collection of notes and things I wish I'd known when I first started trying to mod the game, for dummies, by a dummy. If you have a dumb question, chances are I've already asked it and the answer is in this guide!
   
Award
Favorite
Favorited
Unfavorite
Anatomy of a mod
Disclaimer: I'm still quite new to this, but I've had fun learning, and over the past few months I've seen enough of the same questions asked from other inexperienced people that I thought a guide like this might be good to have around. Note that this guide will not address extreme programming basics like loops, functions, types of variables, etc. - there are plenty of online tutorials on that kind of stuff.

What is a mod?
A mod is something that makes changes to the original game code, often through mod-API. The devs provided their own set of mod-API when they made the game mod-accessible, though fanmade API exists as well and is widely made use of. API (Application Programming Interface) is just pre-written code that takes care of adding certain types of content into the game for you with no additional manual work.

In a nutshell, the game gets its logic and definitions - the moving pieces that form a game - from the set of code. By modding the game, you are supplying additional definitions, as well as redefining existing things.

There will be some redundancy with the API Example Mod and its modding notes released by Klei here. Read that first/additionally if you haven't already. You may also want to read through mod-api.lua (located in scripts/client) for a list of the vanilla mod API. I strongly recommend you join the Invisible Inc Discord[discordapp.com] if you're serious about modding, the #mod corner section has a more extensive pinned list of resources that's being updated as needed, including customised KWAD Builder stuff that's more convenient than the Example API version.

Before we begin
Wodzu_93 has made an excellent video tutorial that walks you through many things in this guide and the step by step process of making a new mod. Please check it out!

Mod Contents
  • modinfo.txt (who made the mod, what is it called, optionally version number and filepath for icon)
  • scripts.zip (contains all your lua files)
  • kwad files (contains custom resources like art or sound, optional depending on the mod, but most mods will at least have a gui.kwad that contains the mod icon). For more on making custom kwad files, check out the Visual Appearance Edit guide by Shirsh.
  • Note that the name of the mod folder must not contain spaces.

Scripts.zip contents:
  • modinit.lua (the most important file, the 'skeleton' of the mod. Everything that your mod does is dictated here, all other files can be considered supplementary)
  • other lua files (optional depending on the mod)

Modinit contents:
  • init function (runs at game launch, initialises the mod, this is where you create generation options to use on the campaign setting screen, the generation option is then called in Load or elsewhere) The code examples in this section are taken from the Death Stare mod by Mobbstar.
  • load function (runs when game launches and when you start or load a campaign with the mod enabled)
  • initStrings (technically optional, loads mod strings, should be included if you want to make your mod localisation-friendly i.e. provide support for language packs)








This initStrings function is also a good opportunity to explain dataPath and scriptPath. These are two APIs commands you will commonly see in mods. The dataPath API functions defines the file path for the mod root i.e. the folder your mod files are located in and is usually referenced when loading resources from it, such as kwads. In initStrings, it's instead the string files that are loaded into it and referenced everywhere else in the mod. ScriptPath, on the other hand, explicitly references the mod's files that are inside scripts.zip. In the absence of a scriptPath or dataPath entry, the vanilla code filepath is used instead. For instance:

local agents = include("sim/unitdefs/agentdefs")
... will get you the vanilla list of agent definitions, while...

local modagents = include(scriptPath .. "/agentdefs" )
...will get you the custom agent definitions in your own mod.

OPTIONAL:
(Note: these functions are part of the fan-made mod API, Sim Constructor by Cyberboy2000)
  • unload (runs when you de-select a mod under the 'DLC' box or load a save with that setting)
  • lateLoad (like load, but happens after Load and after the game loads certain assets, this is often necessary when adding things to existing assets, like adding custom abilities to guards/agents)
  • lateUnload, earlyLoad, earlyInit, lateInit... you get the idea.
  • Init can also have the 'requirement' modAPI line which specifies which mods should be loaded BEFORE this one, if they exist - example in the first image with the Init function. Here, the requirement line ensures that Sim Constructor and Contingency Plan, if they are present, will load before this mod.

    • Often important for inter-mod compatibility, this is the main way to set load order for various mods. Until recently, the mod API requirements would influence the order in which all Load-related functions would be called. As of the March 25th 2019 update to Sim Constructor, Init-related functions will also respect load order! What's important is that the mod API requirements line is called in a function that happens at an earlier point. (For example, if you establish load order in earlyInit, Init will run in the correct order.)
    • This is not to be confused with an actual mod requirement that your mod won't function without!

  • Any init, load or unload-related function in modinit must be returned as actual output from the modinit file.

Note that the official DLC, Contingency Plan, is technically also a mod! It's loaded into the game the same way fan-made mods are and has its own modinit, and its content is loaded into the game through the dev-made APIs. It can be a good example to look at if you're struggling, more elaborate than the API example mod!
On Unloading
What is unloading?
"Unloading" for II mods is talked about in two different contexts:
  • Code that runs as part of the Unload() function, as explained in the previous section.
  • More generally, making sure your mod's changes to the game are "unloaded" when the mod is disabled, i.e. any changes the mod makes correctly reset: custom added agents are un-added, modified items are reset to their old definition, etc. This is very important, and often tricky. Let's see why.

The Unload() function helps but is often not enough.
  • Namely, it is only called when you de-select a mod under 'DLC' (or load a corresponding save). When you have the mod enabled under 'DLC', but disable it under its corresponding generation option (which you added in Init), the 'else' argument in your load function is called instead - if you made one. So make one! If you do not, the game will treat your mod as if it were enabled.

    • Here's why: As explained in the previous section, mods are loaded twice: once at game launch, before you get to the title screen (this loading ignores generation options, or rather, treats them as if they are enabled! Presumably it's an "early warning" type of feature designed to catch errors early), and once at campaign generation (new save) or resuming an old save (this load respects generation options). Therefore, if your load() makes changes and you have no 'else' argument, these changes are made always, automatically, at game launch, and then simply nothing else happens when you load your save, meaning that the changes stay in even though the option is disabled. If you have the 'else' argument, the unload code is called when you untick that box, undoing the changes erroneously made on initial load at game launch!

    • Example: This lateLoad block adds a certain ability to all agents if the mod is enabled, and un-adds it (through a previously-defined unload() function) if the mod is disabled.
    local function lateLoad(modApi, options, params, allOptions) if options["alpha_voice"].enabled then local agents = include( "sim/unitdefs/agentdefs" ) for k,v in pairs(agents) do addAbilityToDef(k,v) end else unload() end end

  • Once more with clarity: The game will load EVERY mod at start-up before it gets to the campaign generation screen, and therefore before you enable/disable mods. Any changes that are not correctly unloaded will stay in the game even if the mod is disabled for the campaign! However, there's a way around this.

  • Code under
    if options["option"].enabled then
    will always run twice, once at game launch and a second time (potentially) if that difficulty option is actually selected at campaign start/load.

  • But using
    if options["option"].enabled and params then
    will make sure that block of code only runs on campaign start/load, since that is when the params variable is passed to Load(). Note that depending on the changes you want to make, you may want them to be applied every time the save is loaded, *not* just at campaign start! Also, this will only work if your mod uses/ requires Sim Constructor!

Unloading through mod-API:
  • Some (vanilla and Sim Constructor) API will take care of unloading for you (for instance, the mod API for adding an agent def, or a banter). Others will not (e.g. add animdef). Check and make sure it's unloaded correctly!
  • If it's not unloaded automatically (or you're making a change manually, with no API), then it needs to be unloaded manually. This can mean resetting whatever you're changing to the vanilla value or (ideally), resetting the value to whatever it was before your mod was loaded (therefore, if another mod changes that value, you don't override it by accident on unload!). For example:
local function addAbilityToDef(id,def) if def.abilities and voicedAgents[id] == nil then voicedAgents[id] = def table.insert(def.abilities, "alpha_voice") end end
This function is called as part of lateLoad, as seen above. It both adds the custom ability to the inputt agentDef (=def) and collects all the modified agentDefs in their own table, voicedAgents.
local function unload() --take away debug voices for id, def in pairs(voicedAgents) do for i, v in ipairs(def.abilities) do if v == "alpha_voice" then table.remove(def.abilities, i) break end end end voicedAgents = {} end
On unload, every entry in voicedAgents is simply checked, and if the agent has an ability that matches the one that we added in lateLoad, it is removed.
Tips and Tricks: Dev mode, debugging, commonly-used files
How to save yourself some frustration and make your life easier:
  • Dev mode. You will want this enabled. Open main.lua (located in the install directory), find a line that says
    DEV = false,
    change the 'false' to 'true'. In-game use Ctrl+ ~ key (tilde, should be located above Tab, or to the left of the numeric keys) to activate debugging mode. You can do this from the map screen or inside a mission. Spawn agents, items, guards, programs, teleport across the facility, save yourself some time. (You can also use the Expanded Cheats mod for even more commands!) Press Ctrl Enter to cycle through agent view of the map, god view, enemy view, select any unit. Use the 'DBG' menu in the corner (by default: DBG - NONE next to a little square box) to access things like unit data and traits for the selected unit. Spawn units by selecting the entry in the debug list, hovering the mouse over an empty tile and hitting Enter. You can also use the debug rewind button to rewind one action at a time.

  • List of hotkeys usable with dev mod enabled (thanks to SapphireFox17 for compiling these):
    Ctrl+A = Increase Alarm Level
    Ctrl+B = Blueprint / outline view of all walls
    Ctrl+E = switch to DBG_DATA view at mouse pointer
    Ctrl+I = Alerts Guard to area at mouse pointer
    Ctrl+K = Kill item at mouse pointer
    Ctrl+L = Abort Mission
    Ctrl+N = Next/End Turn
    Ctrl+R = Dev / FPS bar overlay at the top (simplified version of Ctrl+`)
    Ctrl+T = Teleport selected unit to square at mouse pointer
    Ctrl+U = unlock all rewards prompt
    Ctrl+V = Adds 1 PWR
    Ctrl+W = Win mission
    Ctrl+Z = Adds 100 Credits
    Ctrl+ENTER = Cycles through 3 map views: Total visibility / Enemy view / Normal/player view

    Be careful! Playing with dev mode enabled (or with any other change to the game's files that results in a hash mismatch) will not get you any achievements. In addition, playing with dev mode disables autosave: instead of saving automatically and restoring to that point even if your game crashes or you just close the window manually, it only saves when you leave the game. It's handy for mod testing, but be careful not to lose your mission progress when playing normally!
  • Standard programming principles apply. Outside of commands pre-defined within the language, your code knows only what you tell it. If you want it to search through all agentDefs, you need to first tell it where to find the agentDefs. If you want it to do a spcific function to the agentDefs, it needs to know what the function is. More to the point, you need to be sure that the information is preserved at the right 'tier'. If you define a local variable in Load(), the code will have no idea what you're talking about if you call for it in Unload().

  • The log file is your friend. Use log:write(" stuff here " ) to figure out when what function is firing, if it's firing at all, et cetera. Use log:write(util.stringize(thing,numberoflevels)) to get a stringised version of whatever variable you're interested in, a 'numberoflevels' above 1 will show you not just the variable but whatever sub-tables it has (if any). (tostring can also be used in a similar way). This is probably one of the best debugging tools, as it can make it obvious when a variable doesn't have the structure you expect it to have, or to check what information you can access from a certain function. With enough patience, this will almost always let you get to the bottom of whatever's not working out in your mod, as you'll be able to pintpoint precisely where something went wrong.

  • Note that if you get a crash or an error with your mod, the log file will almost always tell you what line in what file caused the error, as well as what files and functions the game ran through before it got to that error. (Sometimes the actual error is in a different line nearby, this usually happens if there's an , or a { } () too few or too many somewhere and it changes the way the entire block is read). If you're intimidated by the super long log file and don't know where to start, searching for 'error' or 'fatal error' (if there was a crash) and checking the vicinity of that is usually a good start.

  • When in doubt, look at the way the vanilla game and other mods handle things! Let's repeat that for coding purposes, Contingency Plan DLC is treated as a mod, as it has its own modinit file and loads its resources via APIs. (You may quickly discover that some modders document/comment whatever the hell they're doing a lot better than others. Of course, ask for permission and provide credit if you're flat-out copying chunks of someone's code... Etiquette applies, etc.)

    • Depending on the text editor you use to edit files, (e.g. in Notepad++) you can search within an entire folder (and all code files within) for a specific query. Checking how the vanilla game uses a certain command in various instances is incredibly helpful for understanding how to use it yourself!

  • Start small. It may be very counterintuitive how easy something is to mod in. As a rule of thumb, though, it's usually less hassle to add something completely new to the game (new augment or agent or guard or whathaveyou) than to modify existing things.

  • A list of file paths you are likely to be dealing with if you want to mod conventional things (like new agents, items, programs)
    • sim/unitdefs/(entire folder) --for your agent, guard, and item definitions

    • client/animdefs -- the in-game models are defined here

    • sim/abilities/(entire folder) -- agent and enemy abilities like melee, overwatch, observe, go here, but so do abilities tied to augments and items. This also contains npc_abilities which includes mainframe daemons, and mainframe_abilities which define your own programs!

    • sim/missions/(entire folder) -- this contains scripts that handle various events specific to certain missions. 'escape_mission' scripts, on the other hand, are used in every mission type except the final mission, which uses ending_1 instead.
Tips and Tricks: Modding abilities
What are abilities?

Abilities are one of the at least three basic ways units in the game can do, for lack of a better term, special stuff. (The other two being simunits, which tend to be used when new temporary objects are spawned such as shock traps and exploding grenades, and traits, which are just variables that other bits of the game's code can check for.) Most abilities are something you can deliberately activate. However, some abilities do not require activation, because their main function happens in response to a specific trigger. Any object in the game, like an agent, a guard, an item or a prop can have abilities.

Examples of abilities that are mainly active (main action happens during execution):
  • Sprint
  • Shoot
  • Peek
  • Overwatch (actively primes overwatch)
  • Cloak abilities, Prism's disguise ability

Examples of abilities that are mainly trigger-based (main action happens in response to trigger:
  • Shoot while on overwatch (triggers on enemy movement if 'overwatch' is active). Yes, shootOverwatch and Overwatch are separate abilities.
  • Central's augment (triggers on daemon install)
  • Net Downlink (triggers on firewall breaking)

Many abilities have both an active and an onTrigger component, but they can also be exclusively one or the other.

Common functions in abilities

Abilities are so varied that the best way to study them is to look at existing ones, however, there are some parts most abilities share.
  • acquireTargets & isTarget: These two functions usually work together for targeted abilities that change another unit, such as shooting or using a buster chip on a device. They tend to go through all available targets and return a list of acceptable targets, such as only guards, or only drones, or only mainframe devices, or only objects in a certain range.
  • canUseAbility: This sets the conditions for when the ability can be activated. Things like restricted use (only specific agents can use it) or minimum PWR requirements for activation, or cooldown requirements, go here. Note that the result of this function only affects executeAbility and nothing else!
  • executeAbility: Determines what happens when you click that button to activate the ability. Ability will only be executable if canUseAbility returns a true value!
  • onSpawnAbility, onDespawnAbility: These fire when a unit enters or leaves the map. Most units are spawned when the map is first generated. "Living" units despawn when the teleport out, or are killed and leave behind a corpse. If the ability has an onTrigger ability, onSpawn and onDespawn is where the relevant triggers are added! Adding triggers is necessary for the unit to "notice" these triggers being fired in onTrigger.
  • onTrigger: A very powerful function that lets you determine what happens when a certain trigger is registered. Among other things, onTrigger has evData as an input (event data) which contains relevant information that depends on the trigger itself. If you want to know what trigger information is available, besides logwrite, you can search for where this trigger is first generated and see what goes into the evData (usually there is a dispatchEvent line for this!). Note that onTrigger will fire regardless of canUseability!

onTrigger effects can be used very flexibly, a wide variety of triggers is available! The full list can be found in sim/simdefs.lua. You can set onTrigger effects to when a unit moves, spawns or despawns, to when a unit is hit, damaged, or KO, to when the player's or enemy's turn starts or ends, to when a door is opened, etc. While simdefs defines the triggers, they are all actually triggered in different parts of the game's code, e.g. the melee ability flips the 'unit hit' trigger. By setting the right trigger for your ability and checking parameters, you can make specific things happen when that trigger is registered.

If you want to make something new that's not in the game yet rather than just changing a couple of values, chances are, you'll be making a new ability.

Ability owners

One thing to watch out for when comparing code between different abilities is who or what owns the ability and how that is referenced. Abilities that may sound similar may have different code structure, due to the fact that characters, items or props can all have abilities. For instance, the console hijacking ability, the buster chip "icebreak" ability and the data bank hacking ability all involve interacting with a device and need to check that the agent can't activate it while KO or not physically near the device, but they do this in very different ways.
  • Console hijack is an ability given to agents, and the agent is thus the abilityOwner.
  • "Icebreak" is an ability given to items such as buster chips, and the item is the abilityOwner. Checking for the agent means referencing the owner of the abilityOwner.
  • For databank hacking, the data bank prop is abilityOwner and executor of the ability while the agent is the target of this ability, because the data bank hacking ability is given to data banks, not agents!

In short, you need to keep in mind whose "perspective" an ability definition is written from.

When writing a new ability, you have to decide what type of unit will own it. There are different ways to do this. To give abilities directly to units, you will want to add the ability to their "abilities" list in the agent/guard/item/prop definition. However, you can also give an item an 'addAbilities' trait, so that the owner of the item automatically receives that ability, which is how, for example, Central's augment and onfile Dr. Xu's augment work.
Links to resources
Finally, these are some additional links that may be useful for your modding. Some of these links lead to a specific message on the Discord server, which I strongly recommend you join if you're interested in modding the game. Having all the mod creators in the same community really helps deal with bug reports and compatibility issues faster.

Invisible Inc Discord[discordapp.com], (#mod_corner channel)
Playing with mods - what to be aware of
Official Klei guide to modding
Klei's Example API mod[forums.kleientertainment.com]
Extracted graphical assets (release version) [drive.google.com]
Extracted graphical assets (alpha version)[drive.google.com]
Extracted sound and movie assets [drive.google.com]
FMOD sound tutorial (for DST)[discordapp.com]
Beginner-friendly KWAD folder with build.lua and output folders already set up[discordapp.com]
Kanim Viewer[drive.google.com]
Making custom graphics (guide)
Making custom graphics (video tutorial)
Making a language pack (guide)
Spyface (level and prefab design tool)[discordapp.com]