> For the complete documentation index, see [llms.txt](https://docs.rainmad.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.rainmad.com/resources/3d-interactive-minigames-bundle/configuration/hack-types.md).

# Hack Types

A **hack type** is the surface a hack point uses. Each one is a preset in `cfg.hacks.types` that binds a world prop, an animation, a placement mode and a set of on-screen behaviour. Six ship in, and adding a seventh is a config entry not a code change.

Every type is picked from the dropdown in `/create_minigame`, or referenced by its key in a hand-written `data/saved_minigames.lua` entry.

***

### `fingerprint`

Fullscreen minigame with a USB and phone sync-scene playing in front of the player.

```lua
cfg.hacks.types.fingerprint = {
    propModel = 'ch_prop_fingerprint_scanner_01e',
    animDict  = 'anim_heist@hs3f@ig1_hack_keypad@arcade@male@',
    objects   = { 'ch_prop_ch_usb_drive01x', 'prop_phone_ing' },
    scenes    = { ... 4 scenes ... },
    timings   = { intro = 4000, loop = 2000, outcome = 5000 },
    placement = 'screen',
}
```

The `scenes` list drives a four-stage `NetworkSynchronisedScene`:

1. **intro** (`action_var_01`), USB goes in, minigame is not visible yet.
2. **loop** (`hack_loop_var_01`), phone and USB idle, minigame is on screen.
3. **success** (`success_react_exit_var_01`), player pulls the USB back out.
4. **fail** (`fail_react`), player throws the USB and swears.

`timings.intro/loop/outcome` control how long the ped stays in each pose. The minigame runs during `loop`, so the outcome anim only starts once the player succeeds or fails.

**Use it for:** classic hack-into-a-terminal moments where you want a full scene, not just a UI.

***

### `laptop`

Same idea as `fingerprint`, but heavier. Wall panel, laptop, bag and hack card, with a three-stage scene.

```lua
cfg.hacks.types.laptop = {
    propModel = 'hei_prop_hei_securitypanel',
    animDict  = 'anim@heists@ornate_bank@hack',
    objects   = { 'hei_p_m_bag_var22_arm_s', 'hei_prop_hst_laptop', 'hei_prop_heist_card_hack_02' },
    scenes    = { ... 3 scenes ... },
    timings   = { intro = 6300, loop = 2000, outcome = 4600 },
    placement = 'screen',
}
```

Player drops a bag, sets up the laptop, plays the minigame, then packs everything back up. Longer intro means it feels weightier than `fingerprint`, so use it for the payoff hack in a heist rather than every terminal.

***

### `tablet`

The player's own tablet pops out on E and the minigame is drawn onto the tablet screen. No fullscreen overlay, no sync-scene, just the player standing in place holding a tablet.

```lua
cfg.hacks.types.tablet = {
    propModel = 'ch_prop_fingerprint_scanner_01e',
    placement = 'prop',
    preset    = 'tablet',
}
```

The world marker (`propModel`) is only there for the interaction range. Once the player presses E, the tablet from `cfg.propPresets.tablet` takes over.

**Use it for:** discreet hacks that should not attract attention, or for handheld reads on a device (guard's tablet, delivery scanner, and so on).

***

### `tablet_wall`

A wall-mounted tablet, drawn onto the world prop itself instead of pulled into the player's hand. Camera slides in for a close read, minigame lives on the wall.

```lua
cfg.hacks.types.tablet_wall = {
    propModel     = 'rm_tablet_02',
    placement     = 'monitor',
    monitorPreset = 'tabletWall',
}
```

Uses `renderMode = 'quad'` under the hood, so no texture is globally replaced and every `rm_tablet_02` in the world stays untouched.

**Use it for:** control-room panels, apartment door tablets, mounted diagnostics screens.

***

### `laptop_open`

A closed laptop sits in the world. On E, it opens, the camera slides in, and the minigame appears on the screen.

```lua
cfg.hacks.types.laptop_open = {
    propModel       = 'p_laptop_02_s',
    networked       = true,
    animDict        = 'switch@franklin@on_laptop',
    animName        = '001927_01_fras_v2_4_on_laptop_exit_laptop',
    defaultAnimTime = 1.0,   -- 1 = closed
    hackAnimTime    = 0.0,   -- 0 = open
    replaceTexture  = 'script_rt_tvscreen',
    placement       = 'monitor',
    monitorPreset   = 'laptop',
}
```

`defaultAnimTime` and `hackAnimTime` are anim phase values, not durations. The laptop lives frozen at `defaultAnimTime` until a player interacts, at which point it slides to `hackAnimTime` and back on close.

`networked = true` means the server spawns the prop and syncs it to every client through a state bag, so all players see the same open / closed state.

**Use it for:** desktop workstations, tech-lab machines, anywhere the laptop should be part of the scenery until someone uses it.

***

### `monitor`

A world monitor. E slides the camera in, the screen turns into the minigame via `AddReplaceTexture`.

```lua
cfg.hacks.types.monitor = {
    propModel      = 'sf_prop_sf_monitor_01a',
    networked      = true,
    replaceTexture = 'prop_monitor_fib_01_d',
    placement      = 'monitor',
}
```

**Use it for:** CCTV consoles, security desks, arcade machines, dispatch terminals.

{% hint style="warning" %}
`AddReplaceTexture` is model-global. Every instance of `sf_prop_sf_monitor_01a` in the streamed world will show the minigame while the hack is running. That is fine when only one instance is placed per area, and it is why the shipped `tablet_wall` type uses quad-render instead.
{% endhint %}

***

### Field reference

| Field               | Applies to                        | Meaning                                         |
| ------------------- | --------------------------------- | ----------------------------------------------- |
| `propModel`         | all                               | World prop hash                                 |
| `placement`         | all                               | `screen`, `prop` or `monitor`                   |
| `preset`            | prop                              | Key in `cfg.propPresets`                        |
| `monitorPreset`     | monitor                           | Key in `cfg.monitorPresets`                     |
| `networked`         | monitor                           | true = server-spawned + state-bag synced        |
| `animDict/animName` | fingerprint, laptop, laptop\_open | Prop or ped anim                                |
| `defaultAnimTime`   | laptop\_open                      | Phase 0..1 when idle                            |
| `hackAnimTime`      | laptop\_open                      | Phase 0..1 while hacking                        |
| `replaceTexture`    | monitor (non-quad)                | Texture key on the model to overwrite           |
| `objects`           | fingerprint, laptop               | Extra props spawned for the sync-scene          |
| `scenes`            | fingerprint, laptop               | Ordered list of animation names per scene       |
| `timings`           | fingerprint, laptop               | `intro`, `loop`, `outcome` durations in ms      |
| `onSuccess/onFail`  | all                               | Per-type default hooks. Per-point overrides win |

***

### Adding a new type

1. Add a new key under `cfg.hacks.types` with the fields above.
2. Add `{ value = 'yourType', label = 'Your Type' }` to the `HACK_TYPES` list at the top of `client/creator.lua` so it appears in the dropdown.
3. Restart the resource.

The framework picks it up automatically. Server-spawned entities only need `networked = true`, the server handles the spawn and state bag on its own.

If you need a placement that is not `screen`, `prop` or `monitor`, you also need a branch in `runMinigame` in `client/client.lua`.
