[Solved] Struggling to get NPCs spawned and patrol on a randomly generated map
Curt A. P.
31 Jan 2022, 13:15For map regeneration I use this script from a previous post: https://textadventures.co.uk/forum/quest/topic/td5yfgkdpu__qojdlp1xmq/solved-the-compass-doesnt-show-diagonal-exits-directions
(With a bug fix since then)
I try to find a way to spawn NPCs at random in the Before first enter script for each room. I have it working with stackable items, but I don't find a good way to spawn a random amount of NPC that don't stack.
In my first try I want to clone and move a random amount of sheep. Then when they are cloned and moved I want to implement the patrolling script, but first things first. For now I want to get them spawned. So far I didn't get any errors, but also no sheep spawning.
Before first enter called by a script attribute:
foreach (obj, GetDirectChildren( z_vault_npc_peaceful)) {
if (obj.spawn_plains > 0) {
random_chance = GetRandomInt(1,1000)
if (random_chance <= obj.spawn_plains) {
obj.amount_to_spawn = GetRandomInt(obj.spawn_plains_amount_min, obj.spawn_plains_amount_max)
}
}
}
This is what I have to get the chance and amount and I struggle to spawn a few sheep.
mrangel
31 Jan 2022, 13:50I assume that z_valut_npc_peaceful
is a room/container containing the prototype sheep?
That all seems valid.
So far I didn't get any errors, but also no sheep spawning.
You didn't show us the code that's supposed to spawn the sheep. So far, you're picking a couple of random numbers. I would have expected it to be something like:
foreach (obj, GetDirectChildren( z_vault_npc_peaceful)) {
if (obj.spawn_plains > 0) {
random_chance = GetRandomInt(1,1000)
if (random_chance <= obj.spawn_plains) {
obj.amount_to_spawn = GetRandomInt(obj.spawn_plains_amount_min, obj.spawn_plains_amount_max)
for (n, 1, obj.amount_to_spawn) {
newclone = CloneObjectAndMove(obj, this)
// "start patrolling" or path initialisation code would go here if needed
}
}
}
}
Curt A. P.
31 Jan 2022, 14:13I assume that z_valut_npc_peaceful is a room/container containing the prototype sheep?
That's correct.
You didn't show us the code that's supposed to spawn the sheep. So far, you're picking a couple of random numbers.
Yes, because I couldn't figure out how to to do it in the same script.
So, and I have no clue what the for
function is doing. I took a look at it every now and then over the last years but never understood...
mrangel
31 Jan 2022, 15:16So, and I have no clue what the for function is doing. I took a look at it every now and then over the last years but never understood...
It's like foreach
, but instead of going over a list of objects it's just doing numbers.
In this case, if obj.amount_to_spawn
is 5, the stuff inside the loop will be run once with n
being 1
, then 2
, 3
, 4
, and 5
. So… it does the contents of the loop 5 times. I assume that's what "amount to spawn" is for.
Curt A. P.
31 Jan 2022, 15:23In this case, if obj.amount_to_spawn is 5, the stuff inside the loop will be run once with n being 1, then 2, 3, 4, and 5. So… it does the contents of the loop 5 times.
for (n, 1, obj.amount_to_spawn)
Then, what's n
and what's 1
referring to? Just need to understand a bit more...
mrangel
31 Jan 2022, 17:20n
is a variable that contains the number.
The second parameter, 1, is where the count starts.
So for example,
for (number, 4, 9) {
msg ("The variable is " + number)
}
would give you the output:
The variable is 4
The variable is 5
The variable is 6
The variable is 7
The variable is 8
The variable is 9
In the spawning example, we never use n
inside the function because we just want to do the same thing several times.
There's also an optional "step" parameter, which isn't often used. It lets you have the number increase by different amounts. For example, you could make it negative to count backwards:
for (countdown, 5, 1, -1) {
msg ("Launching in " + countdown)
}
would give you:
Launching in 5
Launching in 4
Launching in 3
Launching in 2
Launching in 1
Curt A. P.
02 Feb 2022, 13:58Oh man, I had a great time figuring out why the script wasn't working... I had some typos and wondered why I didn't get any error messages. Well, because I have the screen cleared when moving. So, found some more typos thanks to the error messages and it is working.
Still, I can't say I understand the "for" function completely... does the variable matters for anything else? And don't know where the variable gets it number from...
The first parameter is where the count starts, but I don't know what's being counted. I really try to understand this function... Sorry
So, now I have sheep spawning and want them to patrol.
this.patrol => {
exit = PickOneUnlockedExit (this.parent)
this.parent = exit.to
I want to add a convenient way to call the script after a certain amount of time has passed. I am still using the in-game clock via the game.epoch attribute.
this.move_turn_created = false
this.changedmove_turn_created => {
create turnscript (this.name + "_patrol_turnscript")
}
These are just some snippets how I wanted it to work but I don't find a way to start the cycle.
if (StartsWith(this.parent name, "area_rdm_plains")) {
this.old_epoch_patrol = game.epoch
this.time_staying_here = GetRandomInt(1800,43200)
SetTurnScript (this.name + "_patrol_turnscript") {
if (this.time_staying_here >= this.old_epoch_moving + this.time_staying_here) {
do (this, "patrol")
}
EnableTurnScript (this.name + "_turnscript")
}
}
Or, is a turnscript even a good way to do this?
mrangel
02 Feb 2022, 15:56I'm not sure what all the variables in this script are for.
If you're using an epoch time system and want multiple NPCs to be patrolling, I suspect it would be simpler to use one changescript to handle all of them.
An example of how I'd handle it:
Script to spawn an NPC:
foreach (obj, GetDirectChildren( z_vault_npc_peaceful)) {
if (obj.spawn_plains > 0) {
random_chance = GetRandomInt(1,1000)
if (random_chance <= obj.spawn_plains) {
obj.amount_to_spawn = GetRandomInt(obj.spawn_plains_amount_min, obj.spawn_plains_amount_max)
for (n, 1, obj.amount_to_spawn) {
newclone = CloneObjectAndMove(obj, this)
newclone.patrol_after = game.epoch + GetRandomInt(obj.min_patrol_wait, obj.max_patrol_wait)
}
}
}
}
And in game.changedepoch
:
foreach (obj, AllObjects()) {
if (HasInt (obj, "patrol_after")) {
while (game.epoch > obj.patrol_after) {
params = QuickParams ("patrol_time", obj.patrol_after)
obj.patrol_after = obj.patrol_after + GetRandomInt(obj.min_patrol_wait, obj.max_patrol_wait)
if (HasScript (obj, "patrol")) {
do (obj, "patrol, params)
}
else {
exit = PickOneUnlockedExit (obj.parent)
if (not exit = null and HasObject(exit, "to")) {
obj.parent = exit.to
}
}
}
}
}
Giving an object a patrol_after
attribute causes it to move after the epoch reaches that number.
This makes a patrolling object move through a random unlocked exit. If you want a particular NPC to have keys for unlocking doors or similar, you can give it a "patrol" script attribute and it will use that instead.
I made this slightly more complex to allow different NPCs to patrol at different speeds using the same script. Just give them numeric min_patrol_wait
and max_patrol_wait
attributes. I also let the patrol
scripts for NPCs have access to a variable named patrol_time
which gives the epoch value for the time they moved at. This means that they can override their own patrol_after attribute if you want them to wait longer in particular rooms or something.
Curt A. P.
03 Feb 2022, 09:49This line is genius. So simple against what I tried to get the same
newclone.patrol_after = game.epoch + GetRandomInt(obj.min_patrol_wait, obj.max_patrol_wait)
What does
while ()
do?
while
while (expression) { script }
Run a script while the given expression returns true.
do (obj, "patrol", params) // had a small typo
The test yesterday with sheep spawning before adding the patrol scripts went well.
The patrol scripts are working fine but the game's running significantly slower as soon as 1-5 sheep are spawned. 1 sheep slow. 4 sheep veeeeeery slow. Where I generated dozens of rooms with many more sheeps in yesterday's test, now I got very long loading times from the start. (When sheeps are spawned). Not sure what to thing of it...
mrangel
03 Feb 2022, 09:58I used while
instead of if
because I don't know if you have any occasions where the epoch will increase by a lot (like the player is asleep for 8 hours). It makes the NPCs keep on patrolling until their patrol_after
time is in the future again, taking multiple steps if necessary.
The patrol scripts are working fine but the game's running significantly slower as soon as 1-5 sheep are spawned.
I'm not sure why that would happen. Have you got the game somewhere I can take a look at it?
Curt A. P.
17 Feb 2022, 08:19Sorry for the late answer. I moved into a new apartment and didn't find the time to continue this project.
I'm not sure why that would happen. Have you got the game somewhere I can take a look at it?
I just tested it again and now it seems to be running fine. Normal loading times, so I assume it was just a server sided issue (?)