I think there's still some insight that can be gained from my scripts. Really, the only key difference I'm seeing between my implementation and what you seek is to replace my hard-coded path movements with a script that does the path-finding dynamically.
Of course, that is by no means a trivial task. When I was doing the planning for how to implement my script, I was thinking it would be nice to be able to draw the paths for the NPCs to follow in the map editor. You could do the next best thing and place the waypoints on the map using invisible custom entities (for example, place a waypoint at every intersection along a path and at every relevant destination). Then you could make use of either a target movement or path finding movement instead of the path movement I used in my script.
This implementation still isn't a slam dunk because I'm assuming that you'd want to only have to specify the end destination and not all the intermediate waypoints on the route to get there, so you'd still need a script to find the ideal sequence of waypoints to traverse to get to the ultimate destination.
There are a few complications that you'll have to consider. One example is when the player enters a map, you'll need a script that will determine exactly where every NPC should be located on the map based on the time when the player entered the map. This can be tricky because the NPCs could be in the middle of their movements, so you will have to position the NPCs accordingly and then start a movement from their current location.
Another complication is the timing aspect. If an NPC takes too long getting to their destination, then the subsequent movement will either be delayed (a problem that can become compounded over time), or there will be discontinuities in the movement where the NPC instantly skips across a segment of the movement in order to catch up. In my case, having a precisely determined path helps mitigate this issue.
One thing you can do to help with this problem is to add slop by having the NPC wandering around randomly for a brief duration between movements. For example, if it takes 20 seconds for an NPC to move from point A to B, you could allocate 30 seconds before having the NPC move to point C, where the NPC wanders randomly in the vicinity of point B until the 30 seconds has elapsed. So then whether it takes 18 seconds or 24 seconds for the NPC to travel from A to B, it makes no difference because the NPC would be wondering randomly near point B for either 12 or 6 seconds according.
To go into more details about how I implemented my event tables...
I used a map based timer for my game clock ( timer starts on map:on_started() ). The advantage of linking the timer to the map is that the timer automatically gets paused whenever a dialog is displayed or the game is paused. The tricky part is transitioning the timer on changes of the map. What I did in on map:on_finished(), save the current time to the save data file and then kill the timer. Then when the next map loads, start a new timer from the time saved to the save data file. See
game_clock.lua map_meta:register_event().
My initial thought was to "register" each NPC's routine when they're created on the map, then have a function that re-arranges this routine in order to place each NPC/waypoint into an hour table.
That is basically the implementation that I used. I have a static table of events for each NPC in my data/events directory. When a map loads (map:on_started()) the relevant table entries corresponding to the current map for any NPCs present are extracted and assembled into a new events_list table with the event start times as keys. (see
game_events.lua game.events:load_map_events()). This is the table structure (keep in mind the table is generated dynamically, hard-coded here for clarity):
local events_list = {
["8:00"] = {
{ --NPC_1
npc_id = "NPC Name #1",
location = {x=96, y=157, layer=0, facing=3},
path = {4,4,4,2,2,2,2,2}, --hard-coded path
target = "to_house", --you could do something like this instead, corresponding to a custom entity with this name
},
{ --NPC_2
npc_id = "NPC Name #2",
location = {x=160, y=237, layer=1, facing=1},
path = {1,2,3,4}, --hard-coded path
},
},
["8:23"] = {
{ --NPC_1
npc_id = "NPC Name #1",
location = {x=216, y=109, layer=1, facing=3},
path = {2,2,2,2,2}, --hard-coded path
},
},
["9:00"] = {
{ --NPC_2
npc_id = "NPC Name #2",
location = {x=72, y=61, layer=1, facing=3},
path = {6,6,8,8,2,2,4,4}, --hard-coded path
},
},
}
Then every in-game minute the event manager script (see
game_events.lua game.events:new_time(time_str)) can get a list of all the events that need to be started at the current time by calling events_list[current_time].