# How to add new events

In the game, we use an "event keys" system. An event key is like a signal or trigger that the game sends out when something specific happens (like when the player gets an item or reaches a milestone).

* The **`p_game.gd` script** is where events are handled. Inside this script, there’s a function that listens for these event keys.
* The function takes a **`event_key_id`** as a parameter. This ID represents the event key the game is waiting for.
* When the player receives the event key (from anywhere in the game), the function with that `event_key_id` is triggered, and the game executes whatever code is connected to that event key.

So, if you want to create a mod or add new content, you can trigger these event keys to make things happen in the game, like unlocking new features, changing the environment, or starting a special event.

Files & folders needed:

> assets/scripts/p\_game.gd

So basically event key can be received from, scripts, dialogues, items using, trigger zones. So you create a event key ID in <mark style="color:orange;">assets/scripts/p\_game.gd</mark> file, in the function called:

{% code title="" %}

```gdscript
func on_event_key_add(key): # key -> is string ID of event
```

{% endcode %}

there you'll see a **match** statement, that trying to match key. So if game will match your event key ID, it will do something inside the function. Basically this *on\_event\_key\_add* is callback and calls at the end of :

```gdscript
func AddEventKey(key)
```

function from <mark style="color:green;">**GameManager**</mark>. You can manipulate with events keys in scripts by:

{% code title="GameManager.gd" %}

```gdscript
func AddEventKey(key : String) # add new e.key
func DeleteEventKey(key : String) # delete e.key
func DontHasEventKey(key : String) # return true if no e.key
func HasEventKey(key : String) # return true if has e.key
```

{% endcode %}

So let's create event key where we can add NPC to the level. Open <mark style="color:orange;">assets/scripts/p\_game.gd</mark>. Go to *on\_event\_key\_add* function and create new event key ID. Let's name it <mark style="color:green;">my\_event\_key</mark>.

<figure><img src="/files/NwYtc6nEPMFYTfLqOKB6" alt="" width="291"><figcaption><p>How it looks like</p></figcaption></figure>

Unfortunately I didn't create simple function for NPC creating, so we can create it together! Look into source code of the game at <mark style="color:orange;">SOURCE\_PROJECT/scripts/GameManager.gd</mark> and find there function:

{% code title="GameManager.gd" %}

```gdscript
func load_level(id : String,silent = false):
```

{% endcode %}

This function load json levels from files, but there is NPC creation code, that we can rewrite into separate function. Original NPC spawn code looks like:

{% code title="GameManager.gd" %}

```gdscript
var lvl_npc : NPC = preload("res://engine_objects/npc.tscn").instantiate()

await get_tree().process_frame

lvl_npc.name = obj["name"] if "name" in obj else "npc_"+str(lvl_npc.get_instance_id())
lvl_npc.position = Vector3(obj["position"][0],obj["position"][1],obj["position"][2])
lvl_npc.rotation_degrees = Vector3(obj["rotation"][0],obj["rotation"][1],obj["rotation"][2])

if "scale" in obj:
    lvl_npc.scale = Vector3(obj["scale"][0],obj["scale"][1],obj["scale"][2]) if typeof(obj["scale"]) == TYPE_ARRAY else Vector3(obj["scale"],obj["scale"],obj["scale"])
else:
    lvl_npc.scale = Vector3(0.8,0.8,0.8)

lvl_npc.keys = obj
lvl_npc.profile = npc_json[obj["profile"]]
lvl_npc.id = obj["profile"]
lvl_npc.PlayAnim("idle")

if "lit" in obj and obj["lit"]:
    var orig_mat_0 : StandardMaterial3D = lvl_npc.npc_model.get_surface_override_material(0)
    var orig_mat_1 : StandardMaterial3D = lvl_npc.npc_model.get_surface_override_material(1)
    orig_mat_0.shading_mode = StandardMaterial3D.SHADING_MODE_UNSHADED
    orig_mat_1.shading_mode = StandardMaterial3D.SHADING_MODE_UNSHADED
    lvl_npc.npc_model.set_surface_override_material(0,orig_mat_0)
    lvl_npc.npc_model.set_surface_override_material(1,orig_mat_1)

level_node.add_child(lvl_npc)
add_screen_notifier(lvl_npc)
editor_level_objects.append(lvl_npc)
```

{% endcode %}

Lets create our new function in separate \*.gd file near p\_game.gd script. Call new file an you want, for now it will be "my\_mod\_functions.gd" with this code:

{% code title="my\_mod\_functions.gd" %}

```gdscript
extends Node

func SpawnNPC(object_id : String = "", profile_id : String = "",position : Vector3 = Vector3.ZERO, rotation : Vector3 = Vector3.ZERO):
    # create variable that hold reference to newly created NPC object
    var new_npc : NPC = preload("res://engine_objects/npc.tscn").instantiate()
    # set npc level keys data
    new_npc.keys = {
        "id": "npc",
        "profile": profile_id
    }
    # set npc object data
    # read profile JSON from Game Manager npc_json preloaded config
    new_npc.profile = GameManager.npc_json[profile_id]
    new_npc.id = profile_id
    # set object name (we can find NPC by this name)
    new_npc.name = object_id if object_id != "" else "npc_"+str(new_npc.get_instance_id())
    
    # wait one frame
    await get_tree().process_frame

    # add NPC object at the current level
    GameManager.current_level.add_child(new_npc)

    # play idle animation after spawn
    new_npc.PlayAnim(new_npc.profile["idle_animation"])

    # set npc position and rotation
    new_npc.position = position
    new_npc.scale = Vector3(0.8,0.8,0.8)
    new_npc.rotation_degrees = rotation

    # return NPC as object so we can reference it elsewhere 
    return new_npc
```

{% endcode %}

then go back to p\_game.gd script to the *<mark style="color:blue;">on\_event\_key\_add</mark>* where is our new event key and call <mark style="color:blue;">SpawnNPC(id,profile,pos,rot)</mark> function from <mark style="color:orange;">my\_mod\_functions.gd</mark>:

{% code title="p\_game.gd" %}

```gdscript
func on_event_key_add(key):
    # event keys match for logic
    match key:
	"my_event_key":
	    var my_mod_functions = GameAPI.RunOutsideScript("my_mod_functions")
	    my_mod_functions.SpawnNPC("my_test_npc","village_trader_profile")
```

{% endcode %}

```
my_test_npc <- ID for NPC finding. Like get_node("my_test_npc")
village_trader_profile <- ID from assets/creatures/charactres.json
```

Then let's add event key at the start of new game. Go to function:

{% code title="p\_game.gd" %}

```gdscript
func on_level_changed(level_id):
```

{% endcode %}

Add into "intro" level this code:

{% code title="p\_game.gd" %}

```gdscript
"intro":
    # will add event key
    GM.AddEventKey("my_event_key")
```

{% endcode %}

Start new game and you wll see:

<figure><img src="/files/Mnl2S6vcleEuzByOYVri" alt=""><figcaption><p>NPC at Vector3.ZERO (0,0,0) coordinates with ZERO rotation</p></figcaption></figure>

Lets get some coordinates, turn on debug fly and debug mode in <mark style="color:orange;">assets/scripts/p\_app.gd</mark>:

<pre class="language-gdscript" data-title="p_app.gd"><code class="lang-gdscript"><strong>func game_init():
</strong><strong>    # this already exist in the file, just set false to true
</strong><strong>    GameManager.GameProcess.player.DebugFly = true
</strong><strong>    # this doesn't exist in file, add it by yourself and set true
</strong><strong>    GameManager.GameProcess.player.debug_info = true
</strong></code></pre>

When we start new game, we will see new window at the right top corner:

<figure><img src="/files/gB7d5Sh0HqyG6P8OV1uG" alt=""><figcaption><p>X, Y, Z is position and (0, 130, 0) is rotation</p></figcaption></figure>

Fly at needed position with WASD and rotate camera where you want to NPC to look.

Let's do it near helicopter.

<figure><img src="/files/yuxX7baBNnkanCMVB6tp" alt=""><figcaption></figcaption></figure>

<figure><img src="/files/5YHC7SQpFqJc07Sff3cZ" alt=""><figcaption><p>POV of NPC new position X: 5.83, Y: 0, Z: -13.76, rotation (0, -174.82, 0)</p></figcaption></figure>

{% hint style="warning" %}
Keep in mind, player's rotation a little opposite to NPC rotation data, at screenshot you see -174 Y coordinate, but for NPC it must be 0 if it's equal to player 180 degrees, if you put -174 or 174 Y coordinate to NPC rotation then it will stands with back to where player was looked. My bad.
{% endhint %}

Add inside your event key code position and rotation for SpawnNPC().

```
my_mod_functions.SpawnNPC("my_test_npc","village_trader_profile",Vector3(5.83,0,-13.76),Vector3(0,0,0))
```

It will be looks like this:

<figure><img src="/files/JMeC9rrJ7LwVntdUJYv1" alt=""><figcaption></figcaption></figure>

Start new game again.

<figure><img src="/files/nyq2CEyAbTJNN2ku0S0o" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://picnic-in-the-oblivion.gitbook.io/assets/modding/how-to-add-new-events.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
