• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

Can I run an AI script continually (once per "game tick") in the background?

Akela

Active Member
Joined
Dec 14, 2020
Messages
42
Solutions
2
Reaction score
25
My question is related to understanding when and how LUA scripts are called by the game engine.
Sooner or later I'll need to know more, but for the moment I have a specific scenario, which I've explained below.

All I know at the moment (due to testing a few years ago) is that I can associate a LUA script with game objects (I think I coded a button that opened a door), and that monster scripts are executed when a player gets close enough (IIRC I modified an existing custom monster so it said something different :)

For my current scenario I'll definitely need to run scripts that are not called because a player is nearby (as I remember it, but from long ago, this is how think() is called?), nor by an action (like my simple "door-opening" script), nor by being called from a different script that's initiated due to player presence or an action (see the "members vs Alpha" scenario below.

If there's documentation that explains all this somewhere, a link would be a good answer. I don't mind reading first, and then returning to ask better questions.

The scenario I have in mind:

I'm considering writing a "Pack AI" script for monsters like wolves that should be in a group. The idea is to have:
  • A component (LUA script) that models an "Alpha" member that decides the pack's strategy, shares it via global "wolfpack state" variables, and updates it based on information from pack members
  • Pack members that react normally - e.g. their AI script would be triggered by things like a Player Character being a certain distance away. Pack members would do things like:
    • Share a possible target's location, so the rest of the pack could execute the current Alpha strategy (e.g. get together and attack as a group)
    • Execute the current strategy, - which might be ignore, retreat, follow, or any one of a set of active combat strategies
I'd want the "Pack AI" to run every game tick so it could keep track of pack members, and do things like move them around so their location is not necessarily predictable by Players.
Changing pack members location (so they patrol a large area) is (I think) a clear example where I'd need to be running code every tick.
For most ticks it wouldn't do much though (e.g. the game wouldn't have to execute much LUA code to move the pack around within a known territory).
 
Hello there Akela,

perhaps I'd do that in C++, alter basic monster behaviour via Lua isn't really practicable, I mean, you can do this n' that but I don't think it is the best practice.

There are some papers you'll find very interesting if you're trying to go with Lua, here I'll link a few of them:


Also make sure to, if you feel like knowing exactly what the functions you're calling do, check github and read the c++ code, it's written in a very simple way and you are most likely understand it.


Best Wishes
Okke
 
My question is related to understanding when and how LUA scripts are called by the game engine.
Sooner or later I'll need to know more, but for the moment I have a specific scenario, which I've explained below.

All I know at the moment (due to testing a few years ago) is that I can associate a LUA script with game objects (I think I coded a button that opened a door), and that monster scripts are executed when a player gets close enough (IIRC I modified an existing custom monster so it said something different :)

For my current scenario I'll definitely need to run scripts that are not called because a player is nearby (as I remember it, but from long ago, this is how think() is called?), nor by an action (like my simple "door-opening" script), nor by being called from a different script that's initiated due to player presence or an action (see the "members vs Alpha" scenario below.

If there's documentation that explains all this somewhere, a link would be a good answer. I don't mind reading first, and then returning to ask better questions.

The scenario I have in mind:

I'm considering writing a "Pack AI" script for monsters like wolves that should be in a group. The idea is to have:
  • A component (LUA script) that models an "Alpha" member that decides the pack's strategy, shares it via global "wolfpack state" variables, and updates it based on information from pack members
  • Pack members that react normally - e.g. their AI script would be triggered by things like a Player Character being a certain distance away. Pack members would do things like:
    • Share a possible target's location, so the rest of the pack could execute the current Alpha strategy (e.g. get together and attack as a group)
    • Execute the current strategy, - which might be ignore, retreat, follow, or any one of a set of active combat strategies
I'd want the "Pack AI" to run every game tick so it could keep track of pack members, and do things like move them around so their location is not necessarily predictable by Players.
Changing pack members location (so they patrol a large area) is (I think) a clear example where I'd need to be running code every tick.
For most ticks it wouldn't do much though (e.g. the game wouldn't have to execute much LUA code to move the pack around within a known territory).
If i good understand, you can do this by using Game.setStorageValue with onThink.
 
Okke and Ascuas Funkelein - thanks for your responses, and sorry for the delay in reacting.

Okke
I'm a Java coder, and I have work reasons to learn C#, so if I pick up another "general purpose" language C# will be first. I can read C++ code up to a point, but I'm well below the minimum threshold for doing it fast/efficiently
I looked around the OT C++ code, but so far I haven't found anything I can use. I've probably just been unlucky, but I ran out of energy :)

Nice clean code though. Last time I did anything with LUA it was during the project to convert from the original implementation (C and IIRC a pre-OO programming style for LUA) to C++ and OO-style LUA. It looks a lot nicer now!

When I found onThink() (in Interface:MonsterEvents, which may be the wrong place) there wasn't so much information ...

onThink()​

Description: N/A
Parameters: N/A
Accepted return values: N/A
Example:
N/A

... but if "Interface" means approx the same thing in C++ as it does in Java, I guess that if I knew where to look, there's either more documentation elsewhere, or there some C++ code?

I'll certainly try to implement my "pack AI" in LUA first anyway.
OTOH I have some questions related to the OT C++ code. I know there are some sections that would be useful/interesting for me. I'll come back to this later in case you're up for answering them.


Ascuas and Okke
I've talked to the person who told me that monsters' onThink() is only called when there's a player in range. When we discussed it, we realized that while his information was correct from a gameplay perspective, it may have bene wrong from a LUA programming perspective.

Here's our theory - I'd appreciate if you could confirm or deny it (or if necessary explain where I've gone wrong)

Our new theory is that Ascuas's comment (by default, every monster's onThink() is called once per tick) can be correct without the "monsters only react when a player is in range" observation being wrong.

We now believe that onThink() is indeed called every tick, but that the default behavior (i.e. for a monster that does not have a LUA onThink() method with some interesting logic) is that the monster does nothing unless there's a player nearby.
The default behavior is presumably implemented in the C++ somewhere, but I haven't found it yet.

I've seen a couple of interesting things in LUA code imbedded other questions in OTLand support though (my guess at to what they do in italics):
  • function onThink(interval) ------ Maybe a LUA script's onThink() can be run after a specified delay, or at a specific time?
  • addEvent(removeMonster, timeToRemove, monster:getId())) ------ Maybe this uses an Event to queue the execution of a named script for a specific time?
If I'm right, both would be useful for my "pack AI".


/Pack Leader Akela :)
 
Just write a global table with methods and run addEvent in recursion to achieve "onThink" and that's it, you can check whatever you want from this on think. You would have to run one addEvent recursion per monster
 
Okke and Ascuas Funkelein - thanks for your responses, and sorry for the delay in reacting.

Okke
I'm a Java coder, and I have work reasons to learn C#, so if I pick up another "general purpose" language C# will be first. I can read C++ code up to a point, but I'm well below the minimum threshold for doing it fast/efficiently
I looked around the OT C++ code, but so far I haven't found anything I can use. I've probably just been unlucky, but I ran out of energy :)

Nice clean code though. Last time I did anything with LUA it was during the project to convert from the original implementation (C and IIRC a pre-OO programming style for LUA) to C++ and OO-style LUA. It looks a lot nicer now!

When I found onThink() (in Interface:MonsterEvents, which may be the wrong place) there wasn't so much information ...

onThink()​


N/A

... but if "Interface" means approx the same thing in C++ as it does in Java, I guess that if I knew where to look, there's either more documentation elsewhere, or there some C++ code?

I'll certainly try to implement my "pack AI" in LUA first anyway.
OTOH I have some questions related to the OT C++ code. I know there are some sections that would be useful/interesting for me. I'll come back to this later in case you're up for answering them.


Ascuas and Okke
I've talked to the person who told me that monsters' onThink() is only called when there's a player in range. When we discussed it, we realized that while his information was correct from a gameplay perspective, it may have bene wrong from a LUA programming perspective.

Here's our theory - I'd appreciate if you could confirm or deny it (or if necessary explain where I've gone wrong)

Our new theory is that Ascuas's comment (by default, every monster's onThink() is called once per tick) can be correct without the "monsters only react when a player is in range" observation being wrong.

We now believe that onThink() is indeed called every tick, but that the default behavior (i.e. for a monster that does not have a LUA onThink() method with some interesting logic) is that the monster does nothing unless there's a player nearby.
The default behavior is presumably implemented in the C++ somewhere, but I haven't found it yet.

I've seen a couple of interesting things in LUA code imbedded other questions in OTLand support though (my guess at to what they do in italics):
  • function onThink(interval) ------ Maybe a LUA script's onThink() can be run after a specified delay, or at a specific time?
  • addEvent(removeMonster, timeToRemove, monster:getId())) ------ Maybe this uses an Event to queue the execution of a named script for a specific time?
If I'm right, both would be useful for my "pack AI".


/Pack Leader Akela :)
I thinking about this a little and if i were to do this, first what i would do is two types of monster.
Alfa and normal ones called in file "a, b, c" etc.
Then to avoid summoning wolf by alfa(make it summon only when Alfa spawn) and check if wolves live do something like this.
Lua:
function onThink(interval)

    local from = {x = 1000, y = 800, z = 7}
    local to = {x = 1100, y = 900, z = 7}
    local monster = "Alpha Wolf"
    for x = from.x, to.x do
        for y = from.y, to.y do
            for z = from.z, to.z do
                pos = {x = x, y = y, z = z, stackpos = 253}
                v = getThingfromPos(pos).uid
                if isMonster(v) and getCreatureName(v):lower() == monster:lower() then
-- If Alpha live, do nothing or add AI SCRIPT here??
else
local pos1 = {x = 1050, y = 850, z = 7}
local pos2 = {x = 1050, y = 851, z = 7}
local pos3 = {x = 1050, y = 852, z = 7}
local pos4 = {x = 1050, y = 853, z = 7}
    local monster0 = Game.createMonster("Alpha Wolf", pos1)
    local monster1 = Game.createMonster("Wolf", pos2)
    local monster2 = Game.createMonster("Wolf", pos3)
    local monster3 = Game.createMonster("Wolf", pos4)
                for monster1 and  monster2 and monster3 do
                v = getThingfromPos(pos1).uid
                    monster1:setMaster(v)
                    monster2:setMaster(v)
                    monster3:setMaster(v)
                    end

                end
            end
        end
    end
    return
end
Then in this script make "AI script" for wolf pack? Something like this? xD
 
Back
Top