-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Execution Diagram #49
Comments
This is really cool! I never even envisioned a diagram for the docs! Regarding your question, to execute a program from another program, the proper way to do it is to execute the
Example: npc.programs.register("advanced_npc:farmer:dig_and_replant", function(self, args)
--minetest.log("Got as argument: "..dump(args.pos))
local pos = npc.programs.helper.get_pos_argument(self, args.pos, false)
--minetest.log("Got from helper: "..dump(pos))
if pos then
-- Get node
local node = minetest.get_node_or_nil(pos)
if node then
-- Calculate node name to plant
local plant_name = string.split(node.name, "_")
minetest.log("PLant name: "..dump(plant_name))
--minetest.log("Plant name: "..dump(plant_name))
local new_plant_name = node.name
if plant_name[1] and plant_name[2] then
if plant_name[2] == "8" then
new_plant_name = plant_name[1].."_1"
else
new_plant_name = plant_name[1].."_"..(plant_name[2] + 1)
end
end
npc.log("INFO", "New plant_name: "..dump(new_plant_name))
-- Decide whether to walk to the position or just rotate
-- towards the plant if it's close enough
local npc_pos = vector.round(self.object:getpos())
if vector.distance(npc_pos, pos) > 2 then
-- Walk to position
npc.programs.instr.execute(self, "advanced_npc:interrupt", {
new_program = "advanced_npc:walk_to_pos",
new_args = {
end_pos = {
place_type=npc.locations.data.calculated.target,
use_access_node=true
},
walkable = {}
},
interrupt_options = {}
})
else
-- Rotate towards the plant
npc.programs.instr.execute(self, "advanced_npc:rotate", {
yaw = minetest.dir_to_yaw(vector.direction(npc_pos, pos))
})
end
-- Dig
npc.exec.proc.enqueue(self, "advanced_npc:dig", {
pos = pos,
add_to_inventory = true,
bypass_protection = true
})
-- Rest of program... When this instruction is executed, the current program will be interrupted and new program will start immediately. When this program is done, the scheduler will restore the old program and continue its execution. |
How can you ensure that the old program will return exactly from where it was interrupted? Can you pause a moon function? |
I tried to do this but it is not working npc.programs.register("sunos:interact_furniture", function(self, args)
local places = npc.locations.get_by_type(self, "furniture")
p = places[math.random(1, #places)]
npc.locations.add_shared(self, "sunos_furniture_target", "sunos_target", p.pos, p.access_node)
npc.programs.instr.execute(self, "advanced_npc:interrupt", {
new_program = "advanced_npc:walk_to_pos",
new_args = {
end_pos = {
place_type="sunos_furniture_target",
use_access_node=true
},
walkable = sunos.estruturas.casa.walkable_nodes
},
interrupt_options = {}
})
npc.programs.instr.execute(self, "advanced_npc:interrupt", {
new_program = "sunos:interact",
new_args = {
end_pos = {
place_type="sunos_furniture_target",
use_access_node=true
},
walkable = sunos.estruturas.casa.walkable_nodes
},
interrupt_options = {}
})
end) |
@BrunoMine Instruction state is also stored in the process table. And when an interrupt occurs, the instruction state is stored as well. See here and here Notice the parameter If youe execute the interrupt instruction npc.exec.proc.enqueue(self, "advanced_npc:interrupt"... instead of npc.programs.instr.execute(self, "advanced_npc:interrupt" .... |
This program is apparently working -- Interagir aleatoriamente com a mobilia da casa
npc.programs.register("sunos:interagir_mobilia", function(self, args)
-- Verificar distancia de casa
if verif_dist_pos(self.object:getpos(), self.sunos_fundamento) > 16 then
return
end
local places = npc.locations.get_by_type(self, "mobilia")
p = places[math.random(1, #places)]
npc.locations.add_shared(self, "sunos_alvo_mobilia", "sunos_alvo_mobilia", p.pos, p.access_node)
npc.exec.proc.enqueue(self, "advanced_npc:interrupt", {
new_program = "advanced_npc:walk_to_pos",
new_args = {
end_pos = {
place_type="sunos_alvo_mobilia",
use_access_node=true
},
walkable = sunos.estruturas.casa.walkable_nodes
},
interrupt_options = {}
})
-- Vira para "pos"
npc.exec.proc.enqueue(self, "advanced_npc:rotate", {
start_pos = self.object:getpos(),
end_pos = p.pos,
})
-- Fica parado por um tempo
npc.exec.proc.enqueue(self, "advanced_npc:wait", {
time = 5,
})
end) Code used for send the npc to bed. npc.exec.enqueue_program(self, "advanced_npc:walk_to_pos", {
end_pos = {
place_type="bed_primary",
use_access_node=true
}
})
npc.exec.enqueue_program(self, "advanced_npc:use_bed", {
pos = "bed_primary",
action = npc.programs.const.node_ops.beds.LAY
})
npc.exec.enqueue_program(self, "advanced_npc:idle",
{
acknowledge_nearby_objs = false,
wander_chance = 0
},
{},
true
) NPC occupation npc.occupations.register_occupation("sunos_npc_caseiro", {
dialogues = {},
textures = {},
building_types = {},
surrounding_building_types = {},
walkable_nodes = sunos.estruturas.casa.walkable_nodes,
initial_inventory = {},
schedules_entries = sunos.copy_tb({
-- Durmir/Sleep
[0] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[1] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[2] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[3] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[4] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[5] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[6] = sunos.estruturas.casa.acordar, -- breakfast
-- Mecher em casa/Work (sunos:interagir_mobilia)
[7] = interagir_casa, -- work at house
[8] = interagir_casa,-- work at house
[9] = interagir_casa,-- work at house
[10] = interagir_casa,-- work at house
[11] = interagir_casa,-- work at house
[12] = interagir_casa,-- work at house
[13] = interagir_casa,-- work at house
[14] = interagir_casa,-- work at house
[15] = interagir_casa,-- work at house
[16] = interagir_casa,-- work at house
[17] = interagir_casa,-- work at house
[18] = interagir_casa,-- work at house
[19] = interagir_casa,-- work at house
[20] = interagir_casa,-- work at house
[21] = interagir_casa,-- work at house
-- Durmir
[22] = sunos.estruturas.casa.durmir, -- send to bed if not yet
[23] = sunos.estruturas.casa.durmir -- send to bed if not yet
})
}) The problem is that they keep working even after going to sleep. |
He goes to bed, but then returns to work, then goes to bed (due to an external algorithm), then back to work. This is repeated all night until breakfast. |
Sorry for the delay in answering. First of all, I assume you want: Based on that assumption, I suggest you model your programs like this:
Regarding npc.locations.add_shared(self, "sunos_alvo_mobilia", "sunos_alvo_mobilia", p.pos, p.access_node)
npc.exec.proc.enqueue(self, "advanced_npc:interrupt", { ... You should always execute the first instruction of a program instead of enqueuing it. Why? It makes the process faster and more responsive... apart from that, no other reason. You can enqueue, but I recommend to execute first, enqueue rest. Another point: -- Vira para "pos"
npc.exec.proc.enqueue(self, "advanced_npc:rotate", {
start_pos = self.object:getpos(),
end_pos = p.pos,
}) This instruction, unfortunately, will not work as you think. The reason is (and this is my fault, actually you made me aware in #47 (comment)) that when you give it {future=self.object:getpos} or {future="var_name"} so that future values are properly evaluated. A final point: I'm actually very happy that you are using the programs functionality for your mod. I've noticed that I need to expose myself to use advanced_npc more as an API, so I have started work on a mg_villages NPC mod, basically NPCs specifically tailored for |
Repeat scheduling is required in case the NPC is spawned overnight or day, this ensures that at any time of spawn it is spawned, it receives a task. As you can see, I'm using the program state, but the work at home program continues even after another program state. |
This is essentially because the following: npc.exec.enqueue_program(self, "advanced_npc:idle",
{
acknowledge_nearby_objs = false,
wander_chance = 0
},
{},
true
) Doesn't sets the state program, it just enqueues |
But, the #5 argument of |
True, but the function itself doesn't sets it as the state program, it creates an entry in the process queue that is that of a state program. To explain better what I'm saying: the state program is stored in the variable |
But this really looks like a redundancy. If I already informed you that this is a program state, then the API should do so. |
When the schedule processes the |
Ok, so basically we could add an argument to The state program will indeed stop once a process is in the queue. This is the way the scheduler algorithm works. That's why NPC goes to bed in the first place. The algorithm is:
|
But the |
I understood the sequence, but I did not interrupt the state process, I just added new programs ( |
Basically it is a conceptual issue. The way I conceptualized state programs are that they are not manually enqueued, they are managed only by the process scheduler. As an outsider of the API, the only control you have is to tell (by using Seems like the boolean argument in Would you agree? Does this makes sense? |
About the program state, okay, they are managed only by the process scheduler, but I think this can be manually enqueued too, because your operation is very simple (re-runs while there is nothing in the queue). Yes, the boolean argument in |
Would it be okay if I remove that argument? Or would you prefer that the argument sets the state process instead? I guess either way is sensible |
The argument makes it much simpler. |
Could you improve it? |
Latest push in master (a7d5900) is an attempt at doing this. Hopefully it should work without issues. |
Erro
|
A |
Let me know if it works. |
I've tested everything, the state program is working as expected. |
Here is how I soved the problem with rotation npc.programs.instr.register("sunos:rotate_to_pos", function(self, args)
npc.programs.instr.execute(self, "advanced_npc:rotate", {
start_pos = self.object:getpos(),
end_pos = args.pos,
})
end) In the future we can discuss another solution, but the API is very flexible to get around any situation. |
About program state |
Definition 1 seems more accurate to me. |
I am trying to draw a diagram that exemplifies the whole operation of the NPC.
sketchboard
Can you explain how to insert a program into the instruction flow of another program? Show an example.
The text was updated successfully, but these errors were encountered: