Skip to main content
GRPG’s content scripting system allows you to create interactive objects and NPCs with custom behavior. This guide walks you through creating game content from scratch.

Overview

Game content in GRPG is created through Go code that registers event handlers for objects and NPCs. The scripting system provides contexts with helper methods for common operations like managing object states, player inventory, XP, and dialogue.

Content Types

GRPG supports two main types of interactive content:
  • Interactive Objects - Stateful objects that players can interact with
  • NPCs - Non-player characters that can talk to players and wander within an area

Creating Interactive Objects

1

Define Object Constants

First, define constants for your objects in server-go/scripts/constants.go:
server-go/scripts/constants.go
type ObjConstant uint16
type ItemConstant uint16

const (
    _ ObjConstant = iota
    BERRY_BUSH
)

const (
    _ ItemConstant = iota
    BERRIES
)
Object IDs start from 1 (using iota with blank identifier). ID 0 is reserved.
2

Create Content File

Create a new file in server-go/content/ for your object behavior:
server-go/content/berry_bush.go
package content

import (
    "server/scripts"
    "server/shared"
)

func init() {
    scripts.OnObjInteract(scripts.BERRY_BUSH, func(ctx *scripts.ObjInteractCtx) {
        if ctx.GetObjState() == 0 {
            ctx.SetObjState(1)
            ctx.PlayerInvAdd(scripts.BERRIES)
            ctx.PlayerAddXp(shared.Foraging, 100)

            ctx.AddTimer(100, func() {
                ctx.SetObjState(0)
            })
        }
    })
}
This creates a berry bush that:
  • Checks if it’s in the “full” state (state 0)
  • When harvested, switches to “empty” state (state 1)
  • Gives the player berries and foraging XP
  • Resets to full state after 100 game ticks
3

Available Context Methods

The ObjInteractCtx provides these methods:
MethodDescription
GetObjState()Returns the current state of the object (uint8)
SetObjState(new uint8)Updates object state and notifies all players in the chunk
PlayerInvAdd(itemId ItemConstant)Adds an item to the player’s inventory
PlayerAddXp(skill Skill, amount uint32)Gives the player XP in a specific skill
AddTimer(ticks uint32, fn TimerFunc)Schedules a function to run after N game ticks

Creating NPCs

1

Define NPC Constants

Add your NPC to the constants file:
server-go/scripts/constants.go
type NpcConstant uint16

const (
    _ NpcConstant = iota
    TEST
    MERCHANT
    QUEST_GIVER
)
2

Spawn and Register NPC

Create a content file that spawns the NPC and registers its dialogue:
server-go/content/test_npc.go
package content

import "server/scripts"

func init() {
    // Spawn NPC at position (3, 3) with wander range of 2 tiles
    scripts.SpawnNpc(scripts.TEST, 3, 3, 2)

    scripts.OnTalkNpc(scripts.TEST, func(ctx *scripts.NpcTalkCtx) {
        ctx.ClearDialogueQueue()

        ctx.TalkPlayer("hello, test")
        ctx.TalkNpc("...")
        ctx.TalkPlayer("C U")

        ctx.StartDialogue()
    })
}
3

NPC Context Methods

The NpcTalkCtx provides these methods:
MethodDescription
TalkPlayer(msg string)Adds a player dialogue line to the queue
TalkNpc(msg string)Adds an NPC dialogue line to the queue
ClearDialogueQueue()Clears all pending dialogue
StartDialogue()Sends the dialogue to the player’s client
Always call ClearDialogueQueue() at the start to ensure clean state, then add dialogue lines, and finish with StartDialogue().

Advanced Examples

Stateful Object with Multiple States

scripts.OnObjInteract(scripts.CROP, func(ctx *scripts.ObjInteractCtx) {
    currentState := ctx.GetObjState()
    
    switch currentState {
    case 0: // Unplanted
        ctx.SetObjState(1) // Planted
        ctx.AddTimer(200, func() {
            ctx.SetObjState(2) // Growing
        })
    case 2: // Growing
        ctx.AddTimer(200, func() {
            ctx.SetObjState(3) // Harvestable
        })
    case 3: // Harvestable
        ctx.PlayerInvAdd(scripts.WHEAT)
        ctx.PlayerAddXp(shared.Farming, 150)
        ctx.SetObjState(0) // Reset to unplanted
    }
})

Complex NPC Dialogue

scripts.OnTalkNpc(scripts.QUEST_GIVER, func(ctx *scripts.NpcTalkCtx) {
    ctx.ClearDialogueQueue()
    
    ctx.TalkNpc("Greetings, adventurer!")
    ctx.TalkNpc("I have a task that needs doing.")
    ctx.TalkPlayer("What do you need?")
    ctx.TalkNpc("Collect 10 berries from the forest.")
    ctx.TalkPlayer("I'll see what I can do.")
    
    ctx.StartDialogue()
})

Best Practices

Name your constants descriptively to make your content code self-documenting:
// Good
BERRY_BUSH
OAK_TREE
FISHING_SPOT

// Avoid
OBJ_1
THING_A
For stateful objects, always check the current state before performing actions:
if ctx.GetObjState() == 0 {
    // Only interact when in correct state
    ctx.SetObjState(1)
    // ...
}
Use AddTimer to create respawning resources:
ctx.AddTimer(100, func() {
    ctx.SetObjState(0) // Reset to full
})
Timer duration is in game ticks (server update cycles).
Always clear the dialogue queue before building new conversations:
ctx.ClearDialogueQueue() // Prevents duplicate/stale dialogue
// Build dialogue...
ctx.StartDialogue()

Next Steps

Custom Objects

Learn how to create custom object types with the data packer

Map Creation

Place your content on the map using the map editor

Build docs developers (and LLMs) love