Help with Lua Scripts

Asked by Julio on 2012-12-23

I'd like to help further development of the Lua scripts for Wargus.

I've made some small changes already (personal annoyances from playing), such as:
Fixing the results screen (so the player names don't overwrite the stats)
Saving the last successfully connected Server IP into preferences so it doesn't have to be re-entered every time (we usually use the same, or similar, server IPs when we play)

Next I'd like to work on the AI scripts, but I could use a little help getting started:

Is there any kind of Documentation / Highly commented scripts someone could point me at to get me started? I can kinda get the feel for what's going on, but there's more I need to understand before I begin making changes. Alternatively, if someone could just walk me through one of the scripts such as the land_attack.lua or even one of the smaller ones such as sea_attack.lua that should probably be enough to get me going...

We typically play multiplayer (humans vs computer) and I'd like to make the computer players a bit more challenging.

Thanks for any help,
Julio

Question information

Language:
English Edit question
Status:
Answered
For:
Wargus Edit question
Assignee:
No assignee Edit question
Last query:
2012-12-30
Last reply:
2013-02-06
Kyran Jackson (erstmap) said : #1

Functions
DefineAi(name, class, script)
This defines how a special AI works. Each level can use his own AI definition.

name
    Unique name of this AI definitions. The "ai-type" parameter of DefineAiPlayer refers to this.
class
    Class name of this definition. Used to choose the AI for computer players. The map setup file sets e.g. Players[1].AiName = "ai-blitz" and the engine then finds the AI type whose class name is that string. This is also used in the "ai-name" parameter of Player.
script
    A lua function.

Example

    -- Defines the passive computer AI, which does nothing.
DefineAi("passive-ai", "passive", function() AiSleep(10000) end)

DefineAiHelper(list)
The AI is complete configurable, nothing is hardcoded. The AI knows nothing about any units without this table. This table defines F.E. what unit can build and what they can build and many other things that the AI must know.

list
    A list of features:

    {"build", builder-type, building-type-1, ... building-type-n}
        Obsolete since r8101 (2006-06-21): Ignored. The AI instead gets this information from DefineButton calls that have Action = "build".

        builder-type
            Name of the unit-type that can build.
        building-type-1
        building-type-n
            Names of the unit-types that can be built by the builder.

        Note: If more units can build the same buildings you need the same list for all units.
    {"train", trainer-type, unit-type-1, ... unit-type-n}
        Obsolete since r8101 (2006-06-21): Ignored. The AI instead gets this information from DefineButton calls that have Action = "train".

        trainer-type
            Name of the unit-type that can train the units.
        unit-type-1
        unit-type-n
            Names of the unit-types that can be trained by the trainer.

        Note: If more units can train the same unit you need the same list for all units.
    {"repair", worker-type, unit-type-1, ... unit-type-n}
        Obsolete since r8101 (2006-06-21): Ignored. The AI instead gets this information from DefineButton calls that have Action = "repair".

        worker-type
            Name of the unit-type that can repair other units.
        unit-type-1
        unit-type-n
            Names of the unit-types that can be repaired by the worker.

        Note: If more units can repair the same units you need the same list for all units.
    {"unit-equiv", unit-type, unit-type-1, ... unit-type-n}
        Declares unit types as equivalent for AI purposes. When AiForce is asked to train any of them, it instead trains the best one it can; "best" here is controlled by the Priority setting in DefineUnitType.

        unit-type
            Name of the unit-type that have equivalent units.
        unit-type-1
        unit-type-n
            Names of the unit-types that can be used by the AI equivalent.

    {"unit-limit", unit-type, resource}
        Obsolete since r8101 (2006-06-21) and r8658 (2007-04-01): Ignored. The engine no longer limits the number of units according to resources.

        unit-type
            Name of the unit-type that must be produced to increase the unit limit.
        resource
            Name of the resource producing the shortage.

        I think we should swap the arguments: "resource", "unit-type".

Example
A minimal AI helper definition:

DefineAiHelper(
  {"build", "unit-peasant", "unit-farm"},
  {"train", "unit-town-hall", "unit-peasant"},
  {"repair", "unit-peasant", "unit-town-hall"},
  {"unit-limit", "unit-farm", "food"},
  {"unit-equiv", "unit-town-hall", "unit-keep"})

A peasant can build a farm.

The town hall can train a peasant.

A peasant can repair the town hall.

To fix the food shortage the AI must build farms.

For the AI is a keep equivalent to a town hall.
AiAttackWithForce(force)

Attack the opponent with the given force. The place is choosen by the AI. If there are flyers, ships and land units in the force they could attack different goals.

force
    ID of the force to which the attacking units belong. The force ids 0 to 9 are currently supported.
returns
    False.

The force isn't moved as a single unit: faster units attacks first, than later the slower units will attack.
Example

   -- Force 0 is built with one assault unit. The script continues processing, when
   -- the assault unit is ready. When ready, the script attacks the opponent
   -- with force 0.
   {
     function() return AiForce(0, "unit-assault", 1) end,
     function() return AiWaitForce(0) end,
     function() return AiAttackWithForce(0) end
   }

AiCheckForce(force)

Check if a force is complete.

force
    Number of the force to which the units should belong. 0 - 9 is currently supported.
returns
    True if the force is complete, false if not yet.

Example

    -- Force 0 is build with one assault.
    AiForce(0, "unit-assault", 1)
    ...
    -- If the force is ready, use it to attack.
    if (AiCheckForce(0)) then AiAttackWithForce(0) end

AiDebug(flag)

Obsolete since r5923 (2004-01-01): The debug flag no longer affects the engine, and scripts cannot read it.
Enable or disable the debug output of the AI script execution.

flag
    If true enables printing of the AI commands, if false disables it.

Example

    -- Prints now the commands of this computer player.
    AiDebug(true)

AiDebugPlayer(1, 2, 3, ...)

Obsolete since r5923 (2004-01-01): The debug flag no longer affects the engine, and scripts cannot read it.
Activate dump of AI forces and strategy on stdout.
Parameters are player number, or "self", for thisplayer.
"none" will stop all AI debug output.
Example

    AiDebugPlayer("self")

AiForce(force, unit-type-1, count-1, ... ,unit-type-N, count-N)

Define a force, what and how many units should belong to a force. Up to 10 forces are currently supported.
Force 0 is currently fixed to be the defence force. Send to a building or unit under attack.
If there are any unassigned units of the requested unit-type, than they are assigned to the force.
Note: The low-level didn't support the reduce of a force.

force
    Number of the force to which the units should belong. 0 - 9 is currently supported.
unit-type-1
unit-type-N
    Unit-type that should be in the force. Currently only mobile (trained) units are allowed.
count-1
count-N
    How many of this units should be in the force.
returns
    False.

The unit-types should be build in a fixed order. From left to right? or from each unit-type one after the other?
Example

    -- First the force 0 is filled up with 4 assaults and 5 bazoos, after this
    -- force 1 is filled with 3 assaults and 2 bazoos.
    AiForce(0, "unit-assault", 4, "unit-bazoo", 5)
    AiForce(1, "unit-assault", 3, "unit-bazoo", 2)

AiForceRole(force, role)

Obsolete since r5923 (2004-01-01): Roles of forces no longer affect the engine, and scripts cannot read them.
Define the role of a force.

force
    Number of the force to which the units belong. 0 - 9 is currently supported.
role
    The role of the force. Can be either "attack" or "defend".

Example

    -- Sets the role of force 0 to attack.
    AiForceRole(0, "attack")

AiNeed(unit-type)
Tells the AI that it should have a unit of this unit-type. The AI builds or trains units in this order of the AiSet/AiNeed commands. If the unit or an equivalent unit already exists, the AI does nothing. If the unit is lost, it is automatically rebuilt. If the units are requested in wrong order, the AI could hang up. Resources are collected automatic and farms are automatic build, but additional could be requested.

unit-type
    Name of the unit-type required.
returns
    False.

Example

  -- The magmapump must be build before a powerplant.
  AiNeed("unit-magmapump")
  AiNeed("unit-powerplant")

AiPlayer()
Return the player number of the running AI.
Example

    -- If the player of the running AI has insufficient magma
    -- then build a magmapump:
    player = AiPlayer()
    if (Players[player].MagmaStored < 300) then
      AiNeed("unit-magmapump")
    end

AiSet(unit-type, count)
This AiNeed with a number. Tells the AI that it should have a specified number of a unit of this unit-type. The AI builds or trains units in this order of the AiSet/AiNeed commands. If the unit or an equivalent unit already exists, the AI does nothing. If the units are lost, they are automatic rebuild. If the units are requested in wrong order, the AI could hang up. Resources are collected automatic and farms are automatic build, but additional could be requested. In the opposite to AiNeed, which always inserts a request, AiSet modifies the last request to the new number.

unit-type
    Name of the unit-type(s) required.
count
    Number of unit-types(s) required. Zero is not allowed.
returns
    False.

Example

    -- Request two assault units.
    AiSet("unit-assault", 2)

AiSleep(frames)

Wait some frames, to let the opponent (you) recover.

The first time this function is called, the frames value is stored, and "true" is returned. The next times, also "true" is returned, until the timeout is over: then "false" is returned.

The frames parameter is ignored except during the first call.

frames
    How many frames (~1/30s) the AI shouldn't proceed with the script.
returns
    True if we need to wait longer.
    False if the waiting period is over.

Example

    {
      -- Wait 500 frames ~16s.
      function() return AiSleep(500) end,
    }

AiWait(type)
Waits until the *first* request of this unit-type is completed. Don't forget to request a unit-type, before you wait on it.

type
    The unit type name string.
returns
    True if this unit type is not yet available.
    False if at least one instance of this unit-type exists.

Example

    {
      -- Proceed only if an engineer is ready:
      function() return AiNeed("unit-engineer") end,
      function() return AiWait("unit-engineer") end,
      -- Proceed only if two magmapumps are ready:
      function() return AiSet("unit-magmapump", 2) end,
      function() return AiWait("unit-magmapump") end,
    }

AiWaitForce(force)

Check if a force is complete.

The forces are build in force number order. First 0, than 1, last 9.

force
    Number of the force to which the units should belong. 0 - 9 is currently supported.
returns
    False if the force is complete, true if not yet.

Example

    -- Force 0 is build with one assault unitn. The script continues processing, if the
    -- unit is ready trained.
    AiForce(0, "unit-assault", 1)
    AiWaitForce(0)

Notes
The current AI script support is very limited, many new functions are needed. If you want to write this you know who to contact.
Examples

DefineAi("harsh-example", "land-attack",
 function()

This is the AI "harsh-example" usable for land-attack.

     AiNeed("unit-vault")

The first need unit is the great-hall.

     AiNeed("unit-engineer")

The next unit should be the peon.

     AiWait("unit-vault")

Now wait until the vault is available. Could hang if no great vault can be build.

     AiWait("unit-engineer")

Now wait unit we have a worker to build the next things.

Kyran Jackson (erstmap) said : #3

I made a tutorial on making a campaign mission that includes a little bit on the AI:

http://forums.stratagus.com/viewtopic.php?f=29&t=2861&sid=ef6facbaa26deff54461e43d563e3b4f

Joris Dauphin (joris-dauphin) said : #4

For your improvement, patch welcome.

For AI, PATH_TO/stratagus/doc/scripts/ai.html may help you.
(similar of Kyran long answer, but it should be easier to read).

Auto cast of the AI may be modified in wargus/scripts/spells.lua
wargus/scripts/ai.lua is mostly where we define wargus rules to the AI.
The "real" AI scripts are in wargus/scripts/ai/ :
It defines the build order plus the attack composition/timing
In lua script, you can make 'plan' for AI, but most of the detail is done in C++ in the engine.

Note that all users should have the same "custom AI" lua files (to avoid de-synchronisation between players).

Hope this helps to start.

Julio (artofwar) said : #5

Can someone please explain AiSetReserve() in a little more detail.

I'm looking to understand how reserve values affect the AI...

I'm particularly interested in how the first parameter "time" factors in?

Examples, if possible, are always helpful.

Also, with regards to AiSetCollect()

Do I understand correctly that: AiSetCollect(0, 50, 50, 0, 0, 0, 0)
means that the computer will gather gold and wood equally, whereas using AiSetCollect(0, 80, 20, 0, 0, 0, 0) would make the AI put more peasants to gather gold than wood (80/20)?
What would the time parameter do in the case of AiSetCollect()?

Thanks for any and all help.

Kyran Jackson (erstmap) said : #6

I'm guessing that time would either but how long the Ai gathers resources at that rate (most likely), or how long before the Ai gathers resources at that rate.

AiSetCollect(0, 80, 20, 0, 0, 0, 0) would have the peasants gather more gold than wood.

I suppose the reserve would be threshold the Ai would need to surpass before building anything.

Joris Dauphin (joris-dauphin) said : #7

It results of bad interface.
Internally, time is a resource (for unit cost for example)
and we expose all resources...
1rst parameter is not used.

Julio (artofwar) said : #8

Thanks for the help with AiSetCollect.

BTW, I noticed that doc/scripts/ai.html gives the example as:

    -- Set the collect percent to 50% gold and 50% wood.
    AiSetCollect(0, 50, 50, 0, 0, 0, 0)

However, when I try that, stratagus crashes with an "incorrect argument" error. Should the docs be updated to the following instead?

    -- Set the collect percent to 50% gold and 50% wood.
  AiSetCollect({0, 50, 50, 0, 0, 0, 0})

Also, still curious about how the use of AiSetReserve() affects AI player?

Joris Dauphin (joris-dauphin) said : #9

Doc fixed.
AiSetReserve set the amount of resource to keep in reserve (to not use).

 AiSetReserve({0, 100, 50, 0, 0, 0, 0})
tells that AI cannot spent resource to have less than 100 gold or 50 wood.

Julio (artofwar) said : #10

Thank you,

One last question on the topic of AiSetReserve and AiSetCollect:

What are the defaults for both of these?

Joris Dauphin (joris-dauphin) said : #11

Default values:
AiSetCollect({0, 50, 50, 0, 0, 0, 0}) -- 50% gold, 50% wood
AiSetReserve({0, 0, 0, 0, 0, 0, 0}) -- No reserve, use all resource

Launchpad Janitor (janitor) said : #12

This question was expired because it remained in the 'Open' state without activity for the last 15 days.

Pali (pali) said : #13

Mark as answered

Can you help with this problem?

Provide an answer of your own, or ask Julio for more information if necessary.

To post a message you must log in.