# Action Toolkit

This is a mod/plugin for the game Invisible Inc by Klei Entertainment.

This mod contains no gameplay content, but shared features for use by other mods.

## Content

### Events

#### Non-blocking event

`EV_NONBLOCKING_EVENT` fires the event specified in `eventData.actualEventType` without blocking the vizthread. Practically, this means any event can behave like a KO-Group. It is therefore possible to let several animations play simultaneously.

Example: Show the hit animation on several units at the same time.
```lua
for _, hitUnit in pairs(hitUnits) do
    sim:dispatchEvent(simdefs.EV_NONBLOCKING_EVENT, {
        unit = hitUnit,
        sourceUnit = userUnit,
        actualEventType = simdefs.EV_UNIT_HIT
    })
end
```

### Units

`simmainframeunit` is a barebones simunit type. It has functions for `activate`, `deactivate`, and `toggle`, which change the mainframe status accordingly.

Note that carryable items should have the mainframe status "off" or not be marked as mainframe items while in inventory. Else the game tries and fails to place the controls.

Example:
```lua
item_mytrap = util.extend(commondefs.item_template) {
    type = "simmainframeunit",
    name = "My Trap",
    uses_mainframe =
    {
        toggle =
        {
            name = "Spring Trap",
            tooltip = "Click here to toggle the trap",
            fn = "toggle",
        },
    },
    traits = {mainframe_item = true, mainframe_status = "active"}
},
```

### Targeting

For all targeting, `game` and `sim` should be the gamestate and simengine, respectively.

All constructor parameters beyond the ones listed here are forwarded to the base targeting class.

#### Delegatable Area-Target

`areaTargetDelegatable(game, range, sim, locTargetFn, locTargetData)` is a simple area target that allows the validation logic to be injected by the caller ability.

`range` is the radius of the area.

`locTargetFn(sim, x, y, locTargetData)` should return true or false, and optionally a reason string.

#### Line-Target

`lineTarget(game, sim, unit)` draws a line between `unit` and the target point.

There are properties to alter the behavior:
- `maxDist` can limit the allowed distance from `unit`.
- `validTargetCellFn(sim, unit, cell)` can invalidate the target by not returning true.
- `mpCostFn(sim, unit, x, y)` previews the amount of AP this action will use.
- `showArc` can be set to true to draw the throwing arc.
- `ignoreStaticImpass` can be set to false to invalidate the target if a static obstacle (e.g. furniture) is along the line.
- `ignoreDynamicImpass` can be set to false to invalidate the target if a dynamic obstacle (e.g. guard) is along the line.

#### Line-Knockback-Target

`lineKnockbackTarget(game, sim, unit)` is a Line-Target that also draws a line of equal length behind the target. Impassable cells are marked red.

This is intended for use with the Dash Ability util.

### Math util

`getCellsBetween(x0, y0, x1, y1)` takes the x/y coordinates of two cells and returns an XY-array of the cell coordinates forming a straight line between. This excludes the input coordinates and ordered starting at the first of the two input cells.

Example: Check if the line of sight is passable.
```lua
local line = mathutil.getCellsBetween(x0, y0, x1, y1)

for i, x2, y2 in util.xypairs(line) do
    local cell = sim:getCell(x2, y2)
    if not cell or cell.impass > 0 then
        return false
    end
end
return true
```

`getCloserSideOfWall(wall, target)` takes a wall position (x or y) and a target position (again, x or y). If the wall position is a valid wall position, it returns the cell position that is closer to the target position.

`getFartherSideOfWall(wall, target)` behaves the same, except it returns the cell position that is farther from the target position.

Example: see `getLastCellOfRaycast`

`getLastCellOfRaycast(x0, y0, x1, y1)` takes the XY-coordinates of a cell and a raycast and returns the XY-coordinates of the cell where the raycast ended.

Example: Find the farthest cell of a raycast.
```lua
local xr, yr = sim:getLOS():raycast(x0, y0, x1, y1)
local x2, y2 = mathutil.getLastCellOfRaycast(x1, y1, xr, x1)
local cell = sim:getCell(x2, y2)
```

### Dash Ability util

#### Process Lasers on Cells

`processLasersOnCells(sim, unit, cellCoords)` takes an XY-array of cell coordinates and applies all lasers within those cells on `unit`.

#### Guards track Movement to Cell

`guardTrackMovementToCell(sim, unit, x0, y0, x1, y1)` simulates `unit` moving from the first cell to the second cell.

Guards will attempt to face the destination if LoS is possible. Else, they will gain an interest point at the last crossed cell within their main vision.

#### Apply Knockback

`applyKnockback(sim, x0, y0, x1, y1)` pushes units from (including) the first cell until (excluding) the second cell.

This method returns a data object, which is a list of affected units. Each entry contains `unit`, `startDist`, and if applicable `impactDist`, where the distances are the number of cells that have been entered before the unit or an obstacle has been reached, respectively.
