Constants and values
What the engine places in your script before its first line. These are not functions: you read them, you do not call them, and none of them returns an error.
Some are real and useful. Others exist only so that a script coming from Ninja does not stop on an unknown symbol, and are worth nothing. The last section says which ones, because mixing them up is expensive.
The four id arrays
| Value | Holds | |
|---|---|---|
ShipsArr | 16 ids | The ships, from the small cargo to the pathfinder. |
DefencesArr | 10 ids | The defences, both missiles included. |
TechnologiesArr | 16 ids | The researches, in the order of the game's tree. |
BuildingsArr | 23 ids | The buildings, planet and moon alike. |
The order is guaranteed and does not change from one start to the next. So you can index into them, build a parallel array, or print a report always in the same order.
for id in ShipsArr {
Print(id) // prints "SmallCargo", not 202
}
An id prints as its name, and that name is in English whatever the language of your universe: it is hard-coded into the game library, not read from your pages. ID2Str(id) does the same thing explicitly, and returns Invalid(99999) for a number it does not know, rather than nothing at all.
SOLARSATELLITE is in BuildingsArr, not in ShipsArr. It is built at the shipyard, but the game counts it among the buildings, so a loop reading a planet's levels has to expect to find it there.
Each script gets its own copy of the four arrays. Sorting ShipsArr therefore disturbs no other script on the account. That said, you really are sorting your ShipsArr, for the whole life of your script.
sort = import("sort")
arr = ShipsArr // arr refers to the same array, not a copy
sort.Slice(arr, func(i, j) { return arr[i] < arr[j] })
// ShipsArr is sorted too, until this script ends.
The derived arrays do not exist. PlanetBuildingsArr, MoonBuildingsArr, LfTechnologiesArr and the lifeform lists are missing: a script that names them stops on undefined symbol. Filter BuildingsArr yourself.
The id names
Every id in the four arrays has its named constant, and the other way round. They are written in upper case.
// Ships
SMALLCARGO LARGECARGO LIGHTFIGHTER HEAVYFIGHTER CRUISER
BATTLESHIP COLONYSHIP RECYCLER ESPIONAGEPROBE BOMBER
DESTROYER DEATHSTAR BATTLECRUISER CRAWLER REAPER
PATHFINDER
// Defences
ROCKETLAUNCHER LIGHTLASER HEAVYLASER GAUSSCANNON
IONCANNON PLASMATURRET SMALLSHIELDDOME LARGESHIELDDOME
ANTIBALLISTICMISSILES INTERPLANETARYMISSILES
// Technologies
ESPIONAGETECHNOLOGY COMPUTERTECHNOLOGY WEAPONSTECHNOLOGY
SHIELDINGTECHNOLOGY ARMOURTECHNOLOGY ENERGYTECHNOLOGY
HYPERSPACETECHNOLOGY COMBUSTIONDRIVE IMPULSEDRIVE
HYPERSPACEDRIVE LASERTECHNOLOGY IONTECHNOLOGY
PLASMATECHNOLOGY INTERGALACTICRESEARCHNETWORK
ASTROPHYSICS GRAVITONTECHNOLOGY
// Buildings
METALMINE CRYSTALMINE DEUTERIUMSYNTHESIZER SOLARPLANT
FUSIONREACTOR SOLARSATELLITE METALSTORAGE CRYSTALSTORAGE
DEUTERIUMTANK SHIELDEDMETALDEN UNDERGROUNDCRYSTALDEN SEABEDDEUTERIUMDEN
ALLIANCEDEPOT ROBOTICSFACTORY SHIPYARD RESEARCHLAB
MISSILESILO NANITEFACTORY TERRAFORMER SPACEDOCK
LUNARBASE SENSORPHALANX JUMPGATE
Anywhere a function expects an id, the raw number works too: ID2Str(204) and ID2Str(LIGHTFIGHTER) give the same result.
Missions
| Constant | Value | |
|---|---|---|
ATTACK | 1 | |
GROUPEDATTACK | 2 | |
TRANSPORT | 3 | |
PARK | 4 | Park on a position of your own. |
PARKINTHATALLY | 5 | Park at an ally's. |
SPY | 6 | |
COLONIZE | 7 | |
RECYCLEDEBRISFIELD | 8 | |
DESTROY | 9 | Moon destruction. |
MISSILEATTACK | 10 | |
EXPEDITION | 15 | |
SEARCHFORLIFEFORMS | 18 | Displays as "SearchForLifeform", singular. |
Like the ids, a mission prints as its name: Print(EXPEDITION) prints "Expedition", not 15.
Speeds
Twenty steps in all. The ten whole ones run from TEN_PERCENT (value 1) to HUNDRED_PERCENT (value 10). The ten half steps slot in between: FIVE_PERCENT is 0.5, FIFTEEN_PERCENT is 1.5, and so on up to NINETY_FIVE_PERCENT, which is 9.5.
TEN_PERCENT TWENTY_PERCENT THIRTY_PERCENT FORTY_PERCENT FIFTY_PERCENT
SIXTY_PERCENT SEVENTY_PERCENT EIGHTY_PERCENT NINETY_PERCENT HUNDRED_PERCENT
FIVE_PERCENT FIFTEEN_PERCENT TWENTY_FIVE_PERCENT THIRTY_FIVE_PERCENT
FORTY_FIVE_PERCENT FIFTY_FIVE_PERCENT SIXTY_FIVE_PERCENT
SEVENTY_FIVE_PERCENT EIGHTY_FIVE_PERCENT NINETY_FIVE_PERCENT
The half steps are only offered in game to the General class. Kepler exposes them to everyone so that a script written for another account does not stop on them. A General account sends them as they are; an account of another class flies at the whole step below, and never under 10 % (FIFTY_FIVE_PERCENT flies at 50 %, FIVE_PERCENT at 10 %). FlightTime, GetDepartureTime and LowestSpeed compute that step, and SetSpeed on a NewFleet() keeps it: the calculation and the flight agree.
They are decimal numbers, and that is exactly what the speed parameter of FlightTime expects.
Celestial types, lifeforms, classes
| Constant | Value | |
|---|---|---|
PLANET_TYPE | 1 | |
DEBRIS_TYPE | 2 | The debris field of a position. |
MOON_TYPE | 3 | |
NONE_LF_TYPE HUMANS ROCKTAL MECHAS KAELESH | 0 to 4 | Lifeforms. |
NO_CLASS COLLECTOR GENERAL DISCOVERER | 0 to 3 | Player class. |
NO_ALLIANCE_CLASS WARRIOR TRADER RESEARCHER | 0 to 3 | Alliance class. |
The three celestial types are genuinely useful: NewCoordinate(4, 128, 6, MOON_TYPE) and GetEmpire(PLANET_TYPE) expect them.
The lifeforms and the classes are not. No function returns these values yet: nothing gives you a planet's lifeform, nor your player class, nor your alliance's, and the lifeform functions are missing (see the table of gaps in the introduction). So today these thirteen constants only serve to keep a script that names them from failing.
The dimensions of the universe
| Value | Holds | |
|---|---|---|
GALAXIES | an integer | The number of galaxies in your universe. |
SYSTEMS | an integer | The number of systems per galaxy. |
These two values are read from your universe's record, but only if the session is open at the moment the script starts. With no session, they are 9 and 499, which are the usual dimensions and not yours.
They are frozen when the script starts. A script launched before login will keep 9 and 499 for its whole life, even once the account is logged in. No function returns these two numbers on the fly: the only cure is to restart the script once logged in.
// Sweep a whole galaxy without hard-coding 499.
for sys = 1; sys <= SYSTEMS; sys++ {
infos, err = GalaxyInfos(4, sys)
if err != nil { LogWarn(err); continue }
SleepRandMs(400, 900)
}
The file, the server, the version
| Value | Holds | |
|---|---|---|
FILE __FILE__ | a string | The name of the current script, extension included: ma-ronde.ank. |
OGAME_SERVER | a record | The server: .Name, .Number, .Language, and .Settings. Fields empty with no session. |
VERSION __VERSION__ | a string | Literally Kepler 0.1. See the warning further down. |
OGAME_SERVER is not a string, it is a record, as in Ninja. The name of the universe reads OGAME_SERVER.Name.
Printf("%s (s%d-%s)", OGAME_SERVER.Name, OGAME_SERVER.Number, OGAME_SERVER.Language)
Print("economy speed:", OGAME_SERVER.Settings.EconomySpeed)
The available fields are Name, Number, Language, PlayerCount, PlayersOnline, Opened, StartDate, ServerClosed, Prefered, SignupClosed, and Settings. That last one carries the universe settings: EconomySpeed, FleetSpeedWar, FleetSpeedPeaceful, FleetSpeedHolding, UniverseSize, PlanetFields, DebrisFieldFactorShips, DebrisFieldFactorDefence, ResearchDurationDivisor, EspionageProbeRaids, WreckField, AKS, ServerLabel, ServerCategory, PremiumValidationGift.
EconomySpeed is not always a number. Depending on the universe, the game puts 8 or "x8" in it. Test before you count with it.
FILE is not there to separate your storage keys. Each script already has its own memory: two scripts storing the key last.system do not tread on each other, and prefixing protects nothing. FILE is there to name yourself, in an alert or a log line.
SendDiscord(MY_WEBHOOK, FILE + ": debris at " + coord)
In !global.ank, FILE is always !global.ank. A function defined over there reads the symbols of the environment where it was written, not of the one calling it: it will name itself !global.ank even when called from ma-ronde.ank.
OGAME_SERVER holds neither the address, nor the number, nor the language of the server, only the name of the universe. It is frozen when the script starts, like GALAXIES and SYSTEMS: a script launched before login will see it empty forever. To get the up-to-date value at the moment you need it, call the functions. GetUniverseName(), GetServerNumber() and GetLang() return a single value, empty or zero with no session. ServerURL() returns two: the address and an error.
VERSION does not follow Kepler's versions. It is a fixed piece of text, written once into the program, that does not change when you update the bot. So a version guard copied from an existing script does not do what it says:
// ✗ Always true here, whatever your version.
if VersionCompare(VERSION, "0.91.8") == -1 {
Print("bot too old")
Exit()
}
VersionCompare keeps only the numbers of a string: from Kepler 0.1 it reads 0 then 1, compares them against 0 then 91, and concludes that your bot is older. The script stops even though there is nothing wrong with your install. Delete tests of that kind.
Waiting for the stop: OnQuitCh
| Value | Holds | |
|---|---|---|
OnQuitCh | a channel | Closes when the script stops. |
It is the only event channel available. Its normal use is to keep a script alive after it has scheduled its tasks: without it, the script reaches the end of its text, finishes, and takes its ExecAt and its CronExec with it.
CronExec("0 3 * * *", func() { Print("it is three in the morning") })
<-OnQuitCh // blocks until the script stops
It closes on Exit(), on Terminate(), on the Stop button, and when the bot shuts down. Ninja only fires it on Exit; here, every stop closes it.
Two precautions:
- The lines that follow
<-OnQuitChdo not run. The channel only releases once the stop is under way, and the engine then refuses the next instruction. Write your farewell line before it, or in anOnExitfunction, which the bot calls afterwards, with its own grace period. - Never use it in
!global.ank. That file defines the shared environment, its channel never closes, and the wait would block the loading of the shared definitions for all your other scripts.
The stub values
The seven values that follow exist for one reason only: so that a script coming from Ninja does not stop on undefined symbol. They are wired to nothing. They read neither your settings, nor your licence, nor your hosting mode.
Six of them carry two names. Ninja writes them wrapped in double underscores, __IS_CLOUD__ rather than IS_CLOUD, and that is the form scripts from there use to read them. Both forms work here and give the same value.
| Value | Always | What that implies |
|---|---|---|
LICENSE_UUID __LICENSE_UUID__ | the empty string | |
LICENSE_USERNAME __LICENSE_USERNAME__ | the empty string | |
LICENSE_EMAIL __LICENSE_EMAIL__ | the empty string | |
LICENSE_BOTS_ALLOWED __LICENSE_BOTS_ALLOWED__ | 0 | Does not mean "no bot allowed". |
IS_CLOUD __IS_CLOUD__ | false | False even on a cloud instance. |
IS_SELF_HOST __IS_SELF_HOST__ | true | True even when it is not the case. |
CookieDomain | the empty string | In Ninja, a cloud instance carries its subdomain there. |
DISCORD_WEBHOOK and TELEGRAM_CHAT_ID are not stubs: they carry what you filled in on the Notifications page, and SendDiscord(DISCORD_WEBHOOK, msg) goes out as is, with nothing more to configure. See notifications.
| Value | Holds | What that implies |
|---|---|---|
DISCORD_WEBHOOK | the Discord webhook from the Notifications page | Empty if none is set: SendDiscord then returns an error. |
TELEGRAM_CHAT_ID | the Telegram chat from the Notifications page | Zero if none is set. |
The costliest trap is elsewhere: if IS_CLOUD { ... } will never run, and a test on LICENSE_BOTS_ALLOWED will always read zero. Remove those branches rather than leave them lying to you in silence.