Assets
Modding
Modding
  • Introduction
  • How to add new items
  • How to add new events
  • How to add new stashes
  • How to add new tutorial message
  • How to add weapon HUD image
  • How to add new weapon and ammo
  • How to edit traders
  • How to add language
  • How to add new quest
  • How to add PDA markers
  • How to change in-game SFX
  • How to add ambient sounds to level
  • How to add new dialogue
  • How to change player data
  • How to add new enemy
Powered by GitBook
On this page

How to add new events

Tutorial "How to add new event key to the game and trigger it"

PreviousHow to add new itemsNextHow to add new stashes

Last updated 5 months ago

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:

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

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

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

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

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:

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

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

p_game.gd
func on_level_changed(level_id):

Add into "intro" level this code:

p_game.gd
"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:

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.

How it looks like
NPC at Vector3.ZERO (0,0,0) coordinates with ZERO rotation
X, Y, Z is position and (0, 130, 0) is rotation
POV of NPC new position X: 5.83, Y: 0, Z: -13.76, rotation (0, -174.82, 0)