Modifying the map with a post-processor script (unofficial guide)

Information, How-to's, and discussion about mod'ing Master of Orion II.
User avatar
Tapwater42
Posts: 5
Joined: Sat Jul 22, 2017 12:36 am
Location: Shanghai

Modifying the map with a post-processor script (unofficial guide)

Postby Tapwater42 » Mon Jul 31, 2017 7:23 am

Starting with version 1.50.8 the ability to modify the map with a Lua script was added. I have spent the better part of the week writing a script and learning how this interface works. Since there is only limited documentation on this process, I thought it might be helpful to share my thoughts. I am, however, just a lambda user so please do not think of this as an authoritative reference :wink:

-----------------------------------------------------------
---------------=== Getting started ===---------------
-----------------------------------------------------------

According to the new launcher rules, the script is classified as a map mod and so should have its own directory and configuration file. Even if you want to include it as a part of another mod, it is probably best to follow this convention. Put the file in the \mods\MODNAME\ directory.

In the cfg file you may, and should, use the mapgen commands. They will execute before your post-processor script. Your script itself is activated with the line:

newgame_postprocessor_script = 150\mods\MODNAME\SCRIPT.LUA

If you also set up the header part of the file you can see it in the new launcher and select it under "map".
I would advise you also to uncheck the mapgen switch options in the launcher (not sure what will happen with two different configs feeding conflicting mapgen switches)



-----------------------------------------------------------
-------------=== The API commands ===-------------
-----------------------------------------------------------

Information on these commands is also available in the 150\docs\MANUAL_150.pdf

---=== Stars ===---

get_stars() returns a table of all of the stars currently in the map.
set_stars(startable) accepts startable which is in the same exact format as returned by get_stars()

A star table looks like this:

{{y=426,x=502,color=2,name=Orion,id=0,orbits={1=1,2=0,3=2,4=198},wormhole=-1},
{y=682,x=905,color=2,name=Fieras,id=1,orbits={0=7,1=5,2=6,3=3,4=4},wormhole=-1},
{y=43,x=563,color=4,name=Argus,id=2,orbits={1=29,2=128,3=59,4=103,0=9},wormhole=-1},
{y=41,x=445,color=4,name=Matar,id=3,orbits={1=30,2=122,3=65,4=133,0=8},wormhole=-1},
{y=760,x=966,color=2,name=Obaca,id=4,orbits={1=33,2=131,3=35,4=91,0=13},wormhole=-1},
{y=755,x=838,color=3,name=Waghi,id=5,orbits={1=47,2=178,3=169,4=168,0=21},wormhole=-1},
{y=310,x=43,color=3,name=Pomptina,id=6,orbits={1=46,2=159,3=170,4=52,0=10},wormhole=-1},
{y=429,x=43,color=4,name=Nir,id=7,orbits={1=62,2=160,3=67,4=142,0=24},wormhole=-1},
{y=438,x=437,color=3,name=Scorpio,id=8,orbits={4=137,0=172,2=120,3=27},wormhole=-1},
{y=295,x=512,color=3,name=Corvus,id=9,orbits={0=99,4=26,2=174,3=105},wormhole=-1},
{y=238,x=638,color=1,name=Fuseki,id=10,orbits={0=188,4=53,2=85,3=241},wormhole=-1}}

You may think of it as an array of tables, or a table of tables. Each star has a number, which is its position in the table. The first element is 0 and is always Orion. Although normally arrays and tables in Lua begin counting with 1, Moo2 internal code begins counting with 0. Individual stars can be accessed with startable[i] where i is the number of the star. As you can see, the Mrrshan Homeworld is at star 1. In lua, string titled elements of a table can be accessed in two ways:

startable[1].name == startable[1]["name"] == "Fieras"

The second way can be used in two situations: referencing a variable (A = "name") or accessing a string that is a reserved keyword. The table elements of a particular star have no set order.

The orbits are a table within a table within a table. Unlike the star table itself, the orbits can be missing (value nil). Each orbit points to a planet from the planet table, or is nil. These orbits show the planet ids and are for informational purposes. The actual orbit positions of planets comes from the planet data. You have to use brackets for the orbit numbers due to Lua syntax rules. In this star table,

startable[8].orbits[2] == 120

add_stars ({id1[,id2,[id3...]]}) accepts a list of numbers, each being the id of a new star.
del_stars ({id1[,id2,[id3...]]}) accepts a list of numbers, each being the id of an existing star.

Add_stars creates stars which are white, with no name, at coords (0,0). These stars will have ids starting after the last current star.
del_stars deletes stars as well as any planets orbiting that star. This will cause the star and planet ids to be renumbered.

The main function of these commands is to make the total number of stars in your star table and in the actual map identical. If they are different, you will get an error when you try to set_stars

General notes on stars:

* There is no way to determine a special, including black hole. None of the current commands supply this functionality.
* If you set the x or y coordinate of a star near the maximum or minimum the star will be touching the edge of the map.
* Although you can get the player systems from the name, there is a better way (see below)
* A distance of about 30 units is 1 parsec
* Apparently if you set the star color to 6 it becomes a black hole. 0 = blue, 4 = red, 5 = brown, etc.
* #startable will return the number of the last star, which is 1 less than the total stars.
* maximum number of stars is 71. This is the number of stars on a cluster or huge map, so you can't increase it.

---=== Planets ===---

get_planets() returns a table of all of the planets currently on the map.
set_planets(planettable) accepts a planet table in the exact same format.

{...,{gravity=1,star=16,minerals=0,climate=2,size=3,id=58,orbit=3,image=2,food_base=0,type=3,colony=-1}, {gravity=1,star=16,minerals=2,climate=8,size=2,id=59,orbit=0,image=0,food_base=4,type=3,colony=6}, gravity=1,star=16,minerals=1,climate=2,size=3,id=60,orbit=4,image=1,food_base=0,type=3,colony=-1},...}

Above are three out of over 200 planets in a planet table that I generated. Many of the fields are self-explanatory. Modifying planet data is the same syntax as for stars. I will include a reference to the attributes in the next reply.

image is a number between 0 and 2. Seems to determine, along with size, climate, and type, what the planet looks like. food_base is 2 times the food output per farmer. This will be ignored. Only the climate will be used.

add_planets({star1[,star2[,star3...]]}) adds 1 planet to the lowest open orbit for each star in the list.
add_planets({{star1, orbit1}[,{star2, orbit2}...]}) adds planets to the specified orbit.
del_planets({id1[,id2[, id3]]}) deletes the planets with the given ids.

When adding planets, be careful that the space for the planet is available IN THE CURRENT MAP. If you don't check, planets will overwrite each other and make it impossible to set_planets ().

It seems the "id" element of a planet's data is not necessary. The planet number actually comes from the index of the entry. In other words you don't need to supply this, and when setting individual planets with set_planets, you must set up the table with commands like newplanets[id] = {star=3, climate =5, ...} rather than just a table {{star=3, climate=5, id=...}...} because in the second case the index will be assumed to start at 1 and the game will try to write your data to planet 1 instead of the planet you pick with id.

General notes on planets:

* If you move a race's homeworld to a different star the fleet will not move with it.
* Planets created with add_planets() will be asteroid belts
* If you create a planet at a star that doesn't exist, the star will be added in the top left corner
* The maximum number of planets (including asteroid belts and gas giants) is 255.
* #planettable will return the number of the last planet, which is 1 less than the total number.

---=== Other Commands ===---

get_game() returns a table of information about the game.
get_players() returns a table of information about the players (races), numbered 0 to n - 1.
You can get the dimensions of the map from gametable.map_max_x and gametable.map_max_y
You can get the number of players with #playertable + 1
You can find the homeworlds by using planettable[playertable[i].home_planet].star
To concatenate strings use ..

Final notes:

* "Lua" means "moon" in the language of Portugal. It's not an acronym, but is a name, so it isn't LUA or lua but Lua.
* All numbers in THIS implementation of Lua are integers (whole numbers) but they can be BIG (brazillions)
* Division will round down the result
* I spent the better part of a day (yesterday) writing a set of functions to multiply matrices so I could do rotations
* It worked but I was tired afterwards and couldn't stop thinking about the terrible students who failed my class
* print(stuff) outputs stuff to the log file orion2.log.
* Alex set it up so that it automatically prints tables (unlike normal Lua).

---=== Final Thoughts ===---

I use notepad++ to code my lua scripts (as well as the cfg files, and for looking at the log, and for the minecraft open terrain generator files that I edit, and for fixing subtitle files)

Lua is a great language but since it doesn't throw errors easily you can make bigger mistakes. One particular thing in Lua is you can't reference an element of a table inside a not-yet declared element of a table or array. So if you want to make the next element of the table declare it empty {} or with dummy values first. Oh, and 0 evaluates to true.

Thanks to Alex 150 for making the API that this post is about.
Last edited by Tapwater42 on Wed Aug 02, 2017 4:13 am, edited 3 times in total.
If you don't pick demo then you clearly have authoritarian tendencies :|

User avatar
Tapwater42
Posts: 5
Joined: Sat Jul 22, 2017 12:36 am
Location: Shanghai

Re: Modifying the map with a post-processor script (unofficial guide)

Postby Tapwater42 » Mon Jul 31, 2017 7:37 am

Planet attributes reference:

Code: Select all

 Climates:        Minerals:        Size:         Type:              Colony:
 0   Toxic        0   Ultrapoor    0   Tiny      1   Asteroids      -1      uninhabited
 1   Radiated     1   Poor         1   Small     2   Gas Giant      0 to 7  player's
 2   Barren       2   Abundant     2   Medium    3   Normal Planet
 3   Desert       3   Rich         3   Large
 4   Tundra       4   Ultrarich    4   Huge
 5   Ocean
 6   Swamp
 7   Arid
 8   Terran
 9   Gaia
 

Alex
Posts: 12
Joined: Mon Oct 10, 2016 9:09 am

Re: Modifying the map with a post-processor script (unofficial guide)

Postby Alex » Tue Aug 01, 2017 7:40 am

Great job and sorry for scripting documentation being sparse. Below are my comments on some obscure details. In time they will make their way to the manual.

Tapwater42 wrote: * There is no way to determine a special, including black hole. None of the current commands supply this functionality.


Black holes are just stars of color 6, so setting star color to 6 will make it a black hole.

Tapwater42 wrote:food_base is 2 times the food output per farmer. Obviously this should match the climate but I don't know if it must match.


It's undocumented, but if you change planet's climate, then the correct food_base is set automatically and passed food_base is ignored. This is done to simplify climate change. You still can set inappropriate food_base if you wish by changing just it and not changing the climate, but this is a strange thing to do. Perhaps I will even make food_base read-only in future release.

Tapwater42 wrote:It seems, though I am not sure, that the planet orbit assignments override those created by set_stars().


Correct, star.orbits property is for informational purposes only, passing it to set_stars has no effect. The idea behind is to simplify usage, so that you do not have to modify planet.orbit, star.orbit.0 and star.orbit.1 to move planet from orbit 0 to 1, instead you just change planet.orbit and call set_planets.

Tapwater42 wrote: * If you move a race's homeworld to a different star the fleet will not move with it.


It is advisable to move the whole home star, in such case the fleet will be moved too.

Tapwater42 wrote: * Access the type of a planet with ["type"] since type is a Lua command


Even though a built-in, you can still use type after dot: get_planets()[5].type

Thanks for testing this feature, it means a lot for improving both code and documentation. We'll do a fixup release based on your reports, sometime this week probably.


Return to “Game Modifications”

Who is online

Users browsing this forum: No registered users and 2 guests