# Interactable NPCs

An `interactable` entity is one the player aims at and interacts with rather than attacks. Interacting fires `onPlayerAltAction` with the entity as the target, and the crosshair tells the player which key to press.

* `interactable` makes the entity pickable on its own, so leave `canAttack` off unless being hit is meant to do something. Aim still lands on the entity; swinging or shooting at it does nothing.
* The alt action is consumed, so a held block is not placed through the NPC and a held throwable is not spent.
* `interactionPrompt` replaces the label beside the key. Leave it unset for the default `Interact`. Describe what interacting does rather than which button to press, since the engine draws the key itself.
* It takes plain text or `CustomTextStyling`, so use a `translationKey` if your world is translated.
* Works on mobs as well as mesh entities.
* Nothing is drawn on touchscreens, where a tap needs no prompt. The interaction itself still works there.

## A shopkeeper

```js
let shopkeeperId = null

tick = () => {
    if (shopkeeperId !== null) {
        return
    }

    if (api.getBlock(8, 4, 8) === "Unloaded") {
        // Wait for the chunk to load before placing the NPC
        return
    }

    shopkeeperId = api.attemptCreateMeshEntity("Person", {
        size: 1,
        pose: "standing",
        textures: { head: "trader_black" },
    }, "Blacksmith")

    if (shopkeeperId === null) {
        // The mesh entity limit was reached
        return
    }

    api.setPosition(shopkeeperId, 8, 5, 8)

    // interactable is what keeps the NPC pickable, so canAttack can stay off
    api.setTargetedPlayerSettingForEveryone(shopkeeperId, "canAttack", false)
    api.setTargetedPlayerSettingForEveryone(shopkeeperId, "interactable", true)
    api.setTargetedPlayerSettingForEveryone(shopkeeperId, "interactionPrompt", "Buy Gear")
}

onPlayerAltAction = (playerId, x, y, z, block, targetEId) => {
    if (targetEId === shopkeeperId) {
        // Toggle off, or holding the key would flicker the shop open and closed
        api.openShop(playerId, false)
    }
}
```

Players now see the interact key and `Buy Gear` at the crosshair whenever they look at the Blacksmith, and pressing it opens the shop. Hitting the NPC does nothing at all.

## Guarding a held key

`onPlayerAltAction` keeps firing while the interact key is held down, roughly every 50-150ms. Reopening an already open shop does nothing, so the example above needs no guard. An interaction that grants an item, charges a currency or advances a conversation is not idempotent, and has to swallow the repeats itself:

```js
const INTERACT_COOLDOWN_MS = 800
const lastInteractAt = {}

onPlayerAltAction = (playerId, x, y, z, block, targetEId) => {
    if (targetEId === questGiverId) {
        const now = api.now()
        if (now - (lastInteractAt[playerId] ?? 0) >= INTERACT_COOLDOWN_MS) {
            lastInteractAt[playerId] = now
            sayNextLine(playerId)
        }
    }
}

onPlayerLeave = (playerId) => {
    delete lastInteractAt[playerId]
}
```

800ms is long enough to swallow a held key and short enough that two deliberate presses still count twice. [A quest giver](https://bloxd.io/docs/how-to-dialogue.md#A-quest-giver) fills in `sayNextLine`.
