What technique should I use for a schedule?

Godot Version

4.6.1

Question

I have NPC’s in my game which will have very simple schedules. Simply a list of locations with times. Let’s say it is currently 7 AM, and an NPC gets loaded in with a schedule.

5 AM - Home

6 AM - Work

3 PM - Park

6 PM - Home

The scene with the NPC gets loaded, it should be at “Work”. What technique should I use to make it easy to detect the location.

A custom-stepped or time-scalled tween that triggers callbacks on scheduled times.

1 Like

Quite a few solutions will work, I image.

I recently started working on a level loading system. While it doesn’t use calendar dates, it does solve a related problem of which level to load in case a player enters a loading zone. (In story-based games it isn’t uncommon for the same loading zone to lead to different levels depending on when in the story the player currently is.)

In broad strokes, I have a whole bunch of ‘story points’ defined that are pretty much glorified booleans. I can save the state of these in a resource every time some important story beat happens. Whenever the level load system receives a request to switch scenes, it receives information from what loading zone the request to switch levels came from. It then uses the stored state of the story points to figure out which level to load next.

Triggering the level switch is just a matter of sending the right signal, so if I had to make my system time-based, I’d probably make some sort of class that periodically broadcasts a signal with the current in-game time. I would then let the level loading code figure out for itself whether to do anything with the received signal or not.

class_name NPCScheduler extends Node

signal location_changed(where: String)

@export var world_hour_duration := 0.2 ## number of realtime seconds that represent one hour of game time
var tween: Tween


func _ready():
	# test usage
	location_changed.connect(func(where): print("Location: ", where))
	start_schedule({ 9: "Work", 17: "Home", 21: "Pub", 23: "Home" })
	

func start_schedule(schedule: Dictionary) -> void:
	schedule.sort()
	tween = create_tween()
	var last_time = 0
	for time in schedule.keys():
		tween.tween_interval(time - last_time)
		tween.tween_callback(func(): location_changed.emit(schedule[time]))
		last_time = time
	tween.tween_interval(clamp(24 - schedule.keys()[-1], 0, 24))
	tween.set_loops()
	tween.set_speed_scale(1.0 / world_hour_duration)
	
	
func stop_schedule() -> void:
	if tween.is_running():
		tween.kill()