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:
funcon_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 :
funcAddEventKey(key)
function from GameManager. You can manipulate with events keys in scripts by:
GameManager.gd
funcAddEventKey(key:String)# add new e.keyfuncDeleteEventKey(key:String)# delete e.keyfuncDontHasEventKey(key:String)# return true if no e.keyfuncHasEventKey(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.
How it looks like
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:
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:
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:
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:
Then let's add event key at the start of new game. Go to function:
Add into "intro" level this code:
Start new game and you wll see:
NPC at Vector3.ZERO (0,0,0) coordinates with ZERO rotation
Lets get some coordinates, turn on debug fly and debug mode in assets/scripts/p_app.gd:
When we start new game, we will see new window at the right top corner:
X, Y, Z is position and (0, 130, 0) is rotation
Fly at needed position with WASD and rotate camera where you want to NPC to look.
Let's do it near helicopter.
POV of NPC new position X: 5.83, Y: 0, Z: -13.76, rotation (0, -174.82, 0)
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().
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)
my_mod_functions.gd
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
p_game.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
p_game.gd
func on_level_changed(level_id):
p_game.gd
"intro":
# will add event key
GM.AddEventKey("my_event_key")
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