> For the complete documentation index, see [llms.txt](https://picnic-in-the-oblivion.gitbook.io/assets/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://picnic-in-the-oblivion.gitbook.io/assets/modding/how-to-add-new-events.md).

# 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>
