> ## Documentation Index
> Fetch the complete documentation index at: https://v5.rpgjs.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Server Event Hooks

> Guide for Server Event Hooks in RPGJS.

# Server Event Hooks

Event hooks allow you to customize the behavior of events on the server side. These hooks are defined in the `event` property of your server module and apply to all events in your game.

## Usage

```ts theme={null}
import { RpgEvent, RpgPlayer, RpgMap, RpgEventHooks, defineModule } from '@rpgjs/server'

const event: RpgEventHooks = {
    onInit(event: RpgEvent) {
        console.log(`Event ${event.id} initialized`)
    },
    onAction(event: RpgEvent, player: RpgPlayer) {
        console.log(`Player ${player.name} interacted with ${event.id}`)
    }
}

export default defineModule({
    event
})
```

## Available Hooks

### onInit

**Description:** Called when an event is initialized on the map.

Use `onInit` for base event setup that does not depend on a change-detection cycle yet: initial graphic, movement route, speed, direction, attached shapes, or default metadata.

**Parameters:**

* `event: RpgEvent` - The event instance

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onInit(event: RpgEvent) {
        console.log(`🎯 Event ${event.id} initialized`)
    }
}
```

### onAction

**Description:** Called when a player interacts with an event (typically by pressing the action key)

**Parameters:**

* `event: RpgEvent` - The event instance
* `player: RpgPlayer` - The player who interacted with the event

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    async onAction(event: RpgEvent, player: RpgPlayer) {
        console.log(`${player.name} interacted with ${event.name}`)

        switch (event.name) {
            case 'treasure-chest':
                if (!event.getVariable('isOpened')) {
                    event.setVariable('isOpened', true)
                    player.addItem('gold', 100)
                    player.showText('You found 100 gold!')
                    event.setGraphic('opened-chest')
                } else {
                    player.showText('The chest is empty.')
                }
                break
                
            case 'save-point':
                player.save()
                await player.showText('Game saved!')
                event.showAnimation('save-effect')
                break
                
            case 'teleporter':
                player.changeMap('other_map', {
                    x: event.x,
                    y: event.y
                })
                break
        }
    }
}
```

### onBeforeCreated

**Description:** Called before an event is created, allowing you to modify the event object or return a custom event configuration

**Parameters:**

* `object: any` - The raw event data from the map, typically `{ id?, x?, y?, event }` for positioned events
* `map: RpgMap` - The map instance where the event will be created

**Returns:**

* `any` - Modified event configuration

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onBeforeCreated(object: any, map: RpgMap) {
        console.log(`Creating event ${object.name} on map ${map.id}`)
    }
}
```

### onPlayerTouch

**Description:** Called when a player touches an event (collision detection)

**Parameters:**

* `event: RpgEvent` - The event instance
* `player: RpgPlayer` - The player who touched the event

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onPlayerTouch(event: RpgEvent, player: RpgPlayer) {
        console.log(`${player.name} touched ${event.name}`)
    }
}
```

### onTouch / onTouchEnd

**Description:** Called when an event starts or stops touching a player or another event.

`onTouch` is called once when a collision pair begins. `onTouchEnd` is called once when that pair separates. For player/event collisions, `onTouch` runs before `onPlayerTouch` so existing `onPlayerTouch` code keeps working. For event/event collisions, both events receive the hook.

**Parameters:**

* `other: RpgPlayer | RpgEvent` - The player or event touching this event
* `context: RpgTouchContext` - Collision context with `self`, `other`, `otherType`, `player`, `phase`, `pairId`, and `map`

**Example:**

```ts theme={null}
import type { EventDefinition, RpgTouchContext } from '@rpgjs/server'
import type { RpgEvent, RpgPlayer } from '@rpgjs/server'

export const PressurePlate: EventDefinition = {
    name: 'PressurePlate',
    onTouch(other: RpgPlayer | RpgEvent, context: RpgTouchContext) {
        if (other.name === 'Stone') {
            context.map.setVariable('temple.door.open', true)
        }
    },
    onTouchEnd(other: RpgPlayer | RpgEvent, context: RpgTouchContext) {
        if (other.name === 'Stone') {
            context.map.setVariable('temple.door.open', false)
        }
    }
}
```

### onDetectInShape / onDetectOutShape

**Description:** Called when a player enters or leaves an event's detection shape

**Parameters:**

* `event: RpgEvent` - The event instance
* `player: RpgPlayer` - The player who entered/left the shape
* `shape: RpgShape` - The shape instance

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onDetectInShape(event: RpgEvent, player: RpgPlayer, shape: RpgShape) {

    },
    
    onDetectOutShape(event: RpgEvent, player: RpgPlayer, shape: RpgShape) {
        
    }
}
```

### onInShape / onOutShape

**Description:** Called when the event itself enters or leaves a shape

**Parameters:**

* `event: RpgEvent` - The event instance
* `shape: RpgShape` - The shape instance

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onInShape(event: RpgEvent, shape: RpgShape) {
        
    },
    
    onOutShape(event: RpgEvent, shape: RpgShape) {
        
    }
}
```

### onChanges

**Description:** Called during the change-detection cycle for a player.

This hook is the reactive part of event design. When player state changes, especially player variables, RPGJS can re-run the cycle and call `onChanges(event, player)` so the event can recompute its state for that player. You can also force that cycle with `player.syncChanges()`.

**Parameters:**

* `event: RpgEvent` - The event instance
* `player: RpgPlayer` - The player who caused the change (if applicable)

**Example:**

```ts theme={null}
const event: RpgEventHooks = {
    onChanges(event: RpgEvent, player: RpgPlayer) {
        const isOpened = player.getVariable('chest-opened')
        event.setGraphic(isOpened ? 'chest-opened' : 'chest-closed')
    }
}
```

## Designing `onInit` and `onChanges` together

In practice, these two hooks often share similar logic:

* `onInit` sets a correct initial state when the event appears
* `onChanges` keeps that state synchronized with player data later

Typical example: a chest.

```ts theme={null}
const event: RpgEventHooks = {
    onInit(event: RpgEvent) {
        event.setGraphic('chest-closed')
    },
    onChanges(event: RpgEvent, player: RpgPlayer) {
        const isOpened = player.getVariable('chest-opened')
        event.setGraphic(isOpened ? 'chest-opened' : 'chest-closed')
    }
}
```

Use player variables for this kind of state, because they persist with the player and travel with the player snapshot across saves and map transfers.

Use map variables instead when the state belongs to the shared map room:

```ts theme={null}
import type { EventDefinition } from '@rpgjs/server'

const Door: EventDefinition = {
    name: 'Door',
    onChanges() {
        const map = this.getCurrentMap()
        const open = map?.getVariable<boolean>('temple.door.open') === true

        this.through = open
        this.setGraphic(open ? 'door-open' : 'door-closed')
    }
}
```

`map.setVariable()`, `map.removeVariable()`, and `map.clearVariables()` automatically re-run `onChanges` for visible players and events on the map. Player variable writes automatically re-run the same cycle for that player.
