How to add new events
Tutorial "How to add new event key to the game and trigger it"
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 assets/scripts/p_game.gd file, in the function called:
func on_event_key_add(key): # key -> is string ID of event
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 :
func AddEventKey(key)
function from GameManager. You can manipulate with events keys in scripts by:
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
So let's create event key where we can add NPC to the level. Open assets/scripts/p_game.gd. Go to on_event_key_add function and create new event key ID. Let's name it my_event_key.

Unfortunately I didn't create simple function for NPC creating, so we can create it together! Look into source code of the game at SOURCE_PROJECT/scripts/GameManager.gd and find there function:
func load_level(id : String,silent = false):
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:
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)
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:
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
then go back to p_game.gd script to the on_event_key_add where is our new event key and call SpawnNPC(id,profile,pos,rot) function from my_mod_functions.gd:
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")
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:
func on_level_changed(level_id):
Add into "intro" level this code:
"intro":
# will add event key
GM.AddEventKey("my_event_key")
Start new game and you wll see:

Lets get some coordinates, turn on debug fly and debug mode in assets/scripts/p_app.gd:
func game_init():
# this already exist in the file, just set false to true
GameManager.GameProcess.player.DebugFly = true
# this doesn't exist in file, add it by yourself and set true
GameManager.GameProcess.player.debug_info = true
When we start new game, we will see new window at the right top corner:

Fly at needed position with WASD and rotate camera where you want to NPC to look.
Let's do it near helicopter.


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.
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:

Start new game again.

Last updated