Releases: Pr1nted/Open-Doctrines
Release list
Open Doctrines on TempleOS — 1.2.2a
Open Doctrines' rules, map and interface running natively on TempleOS. No host engine, no bridge: once the world file is read, the machine plays the game by itself.
What's in it
- 1,632 provinces with adjacency, population, armies, industry, harbours and deposits
- All 39 actions the desktop game offers, across war, economy, politics and navy
- All 8 map views — population, industry, defence, relations, army, navy, resources, country names
- 86 research nodes and 59 policies, taken from the desktop game's own definitions; 12 of the 20 policy levers act, and the other 8 are listed with the reason they can't
- A sea map: fleets sail between harbours across open water, and Panama is correctly shut
- raylib's own bitmap font, so the text is the same glyphs the desktop game draws
- 1024×768 in 32-bit colour, through the Bochs DISPI registers
Running it
ODBIN.BIN is the game, compiled ahead of time by TempleOS's own compiler — 882 KB with a TOSB signature. Copy everything into D:/Home, then:
#include "RUN"
U0 (*f)(U8 *w) = ODStart;
(*f)("world.odw");
Three lines rather than one because the loaded symbol arrives as an address rather than a function; templeos/README.md explains exactly why. Or skip the binary and compile from source in about 1.7 seconds: #include "ODGame"; ODStart;
Moving about: click a province, or n to step to a neighbour (anyone's), or h/l to walk your own. ? lists all 39 actions.
Requirements
A linear-framebuffer mode through the Bochs DISPI registers — QEMU, Bochs and VirtualBox provide it; real hardware does not.
Not affiliated with TempleOS or its author. TempleOS is public domain, by Terry A. Davis.
v1.2.2a
game 1.2.2a
Mostly reported bugs, and three of them stopped people playing.
-
Windows could not sign in. The game looks for the system's trusted
certificates in seven places and every one of them is a UNIX path. Windows
does not keep its roots in a file at all — they live in the Schannel store,
reachable only through CryptoAPI — so on Windows the search found nothing and
every HTTPS call was refused with "no system certificate store was found".
Sign-in, the account service, update checks, the announcement board and
feedback: all of them, on Windows only. macOS finds/etc/ssl/cert.pemand
the web build never runs that code because the browser does its own TLS,
which is exactly why it was reported as "works on Mac and web".Refusing was right. Connecting anyway with verification off would have meant
accepting any certificate on the one platform least able to notice. The bug
was that Windows had no way to succeed. -
Swiping a list on a phone now scrolls it. There was no one-finger scroll
gesture at all. Every one of the sixteen scrollable panels reads the mouse
wheel, and the wheel was set in exactly one place: the two-finger pinch. So
the shipped answer to "scroll this list" on a phone was "pinch it", which is
also the zoom gesture, and a swipe did what the touch layer said it did —
moved the cursor and held the left button down. -
Doctrines a player never chose were being enacted. Reported as "sometimes
policies start being implemented when i dont even implement them": enact Free
Press, and Open Borders appears. Every row in the doctrine list hit-tests its
own Enact button, and the list's clipping rectangle clips the drawing and not
the clicks — so a doctrine scrolled out of view kept a live, invisible button
somewhere else on the screen. With the list scrolled it sits under the Close
button. The same fault is fixed in the claims list, where it would have added
or dropped a claim. -
Advanced Shipbuilding does what it says, and so do sixteen other effects.
Reported by a player: "I researched Advanced Shipbuilding, which says it
reduces ship cost by 10%. Yet the cost to build ships was not reduced." It was
not.navyCostPctwas summed correctly by the research resolver and then read
by nothing. Four research nodes and five doctrines advertised it. Three more
levers were in the same state — army upkeep, passive income and the resource
and population income modifiers. All seventeen effects the resolver can sum
are now spent somewhere, andtools/check_effect_fields.pyfails the build if
that stops being true. -
Four screens asked about a province against the wrong country's compass.
Unrest is a province's distance from ITS government, and the short form of
that call answers for whoever the player is. So a mod asking about a province
it did not own, the advisor summarising a seat's own districts, and a map
script readingrebellion_chancewere all given a number about somebody else. -
A world now records what it was last loaded with — the game version and
the mods — so loading a campaign without a mod it was built with says so
instead of quietly dropping what that mod added. -
The mod directory is in the game, the looking-for-a-game board is on the
main menu, and listings post as forum threads rather than messages. -
--no-tunnelno longer publishes a public address anyway. C++ mods build
again. The dev view publishes at the game's own resolution instead of a fixed
480x270.
Gearbox SDK gearbox-v1.3
sdk 1.3
Gearbox 1.3 adds 41 imports and five capability modules. A mod can now put
content in the game's own catalogues, hold fields on a country, claim a map
script command, mark the map, and ask what the process is costing.
-
Content.
content.addputs a doctrine, research node, troop type,
artillery type or district law into the catalogue the game itself loads —
and the definition crosses as the SAME JSONdata/policies.jsonuses, through
the same parser. There is no separate mod format, and no field that works from
the data file but not from a mod. Ids are global within a catalogue, because a
country records what it holds by id; the first mod to claim one keeps it, and
a collision is refused rather than silently overwritten.PERSISTwrites the
definition into the save, so a campaign that adopted your doctrine still knows
what it was after your mod is uninstalled.docs/gearbox-custom-policy.mddocuments every field, andPolicyRulesTest
reads that document's example and checks each claim against thePolicythe
game ends up with — so the guide cannot drift from the parser. -
Country.
country.field_adddeclares a field on every country, keyed by
(mod, name), so two mods may both addmoraleand neither can see the
other's. Persisted fields survive uninstalling the mod that made them. -
Scripts. A mod claims a map-script keyword and handles it.
-
Render. Tints and labels on the map. An alpha of zero REMOVES, so a mod
fading something out cannot grow the draw list by one entry per frame. -
Core.Protected. What the process weighs, and which mods are loaded. The
player is told a mod asked for it, a debug console shows every request, and a
mod cannot tell when that console is open.
All thirteen language SDKs reach all 216 imports. Verified by building every
example and driving it through its real draw path, not by a text lint. Doing that
found three bindings that had never worked:
-
Rust and Zig could not use Assets or Content at all. In both, the declared
symbol IS the wasm import name, so every renamed binding asked for
"gearbox:assets"."asset_size"— a name no host provides — and the module was
refused at instantiation. Rust now emits#[link_name]; Zig declares a renamed
import inside a namespace where it keeps its wire name. -
typewas emitted as a parameter name, which is a keyword in Go, Rust, Zig
and AssemblyScript. Parameter names now carry a per-language escape. -
Every script mod silently lost GameState.Read. The generated bindings gate
each group on the ABI's capability name; the Lua, Python and JavaScript hosts
defaulted a macro nothing reads. A rebuilt mod died on "attempt to call a nil
value (field 'turnNumber')".tools/check_script_bindings.shnow refuses any
capability macro a host names that no generated group reads.
odmod-check keeps a catalogue, so a content mod's adds succeed in the dry
run and it prints what actually landed — and --expect-refusal makes a revoked
capability's refusal the pass, which is what sdk/rust/build.sh had been
reporting as a failure since the day it was written.
Two new example mods. sdk/lua/examples/first-doctrine is one file and one
doctrine with the JSON inline and no generation step: the one to read first.
sdk/examples/custom-doctrine is the same thing in C, with its two doctrines in
a .json the Lua port reads rather than copying.
v1.2.1a
game 1.2.1a
-
Twenty-four more languages, and the twenty already there finished. The
interface shipped in twenty languages at 1.2.0a. It now ships in forty-four --
Albanian, Azerbaijani, Bosnian, Croatian, Danish, Dutch, Esperanto, Estonian,
Finnish, Greek, Hungarian, Armenian, Georgian, Kyrgyz, Latin, Latvian,
Lithuanian, Mongolian, Norwegian Bokmal, Portuguese, Romanian, Serbian,
Swedish and Vietnamese are new.The twenty that were already there did not stay where they were: between them
they gained 5,980 strings, going from 83.6% to 99.1% of the interface measured
against today's string list, which itself grew by 74. Across all forty-four,
83,980 of 84,964 strings are present.Every string that passes through the translation layer is now translated in
all forty-four. What is left cannot be reached from a language file at all:
the advisor's refusal phrases, which are matched against the model's English
output and would stop working if they were translated; four mangled escape
sequences; and a handful of literals drawn straight to the screen without a
lookup.Arabic and Urdu mean right-to-left layout was done properly rather than
skipped, and Armenian, Georgian, Kyrgyz, Mongolian and Latin each bring their
own script. -
Ethnic migration conserves people. A province's minority shares partition
its population, and migration was capped against the province rather than
against the group actually leaving. Move more people than a group has and its
head count went negative, the group was dropped as empty, and its share left
the numerator while the denominator kept the hole — inflating everyone who
stayed, compounding every turn.Replayed over the 116 turns of a conquer-the-world save, one province's shares
reached 6,111,403% and the Ethnic Management panel multiplied an ordinary
population by them: 9.4e20 Belarusians, with the titular Poles — the group that
had been emigrating all game — listed smaller than their own minorities. -
A country can no longer hold both sides of a research fork. The
per-country availability check saw only what was already researched, so an
extra research group could startoff_tacticswhile the main slot was still on
def_tactics, and the country finished with both. Measured at 41, 71 and 52
fork nodes held beside their sibling per 400-turn world. On by default after
128 seeds per arm found no harm on any statistic and a land gain for France in
1914;OD_RESEARCH_MUTEX_FIX=0restores the old rule. -
The release gate stops reading a crash as a skipped step. The check that
asks whether the game can actually start looked at the log before the exit
code, so any death on a machine that had also printed an OpenGL message was
excused — including a real one. A macOS runner took SIGSEGV and the run
reported "built, tested, and played a game". The window opening is now what
separates the two cases, and the decision has its own tests. -
Development instrumentation. Counters for why the AI declines to declare
war, scoped to one country; a bench that fingerprints its binary at both ends
of a run so a rebuild mid-run cannot be mistaken for a result; and hooks for an
external benchmark to time turns. All off by default and read by no decision. -
The map as a globe. Press F7 and the world wraps onto a sphere you can
turn: drag to spin it, wheel to zoom, click a province exactly as before. The
ground you were looking at stays in front of you across the switch, both ways.It costs less than it sounds because of two things the map already was. The
political layer is an equirectangular raster, which is the projection a
sphere wants — so the globe needs no new art and no second copy of the map.
And a province is identified by its pixel colour, so picking is ray→sphere,
sphere→latitude and longitude, then the same lookup the flat map already does.
There is no second pick path to keep in step with the first.Every overlay comes across for the same reason: both views composite the same
stack of full-map textures, so claims, population, resources, districts,
borders and the editor's own layers all arrive without a line of globe-specific
code. The one thing the flat projection never had to say is this point is
behind the planet — that is answered once, where map positions become screen
positions, rather than at each of the places that draw a marker. -
Day and night, and the season. A terminator you can tune: how dark the
unlit half goes, how bright the lit half is, how wide dawn is, and what colour
the sun is.night_floordefaults well above black on purpose — a fully dark
night side makes half an empire invisible, and the map is a working document
before it is a picture.The month moves the sun's declination, not its longitude: longitude is time
of day, declination is the season. In June the northern hemisphere leans into
the light; in December the reverse. -
Weather, in the air rather than on the ground. Cloud sits inside the
atmosphere at a settable height and casts shadows on the ground beneath it —
which is what puts it above the map instead of on it. It thickens as you zoom
in, because from orbit you want the ground through the pattern and close in you
are looking along a much longer path of air.The field is built from cellular noise organised by cyclonic rotation.
Summed smooth noise gives torn wool however many octaves you add: real weather
turns around lows, and a cumulus field is discrete cells with clear air between
them. Both are generated, not shipped, and both are baked on a worker thread —
on a browser, where there are no threads to bake on, at a quarter the size. -
A sky each map carries. Stars, a moon that takes the same light as the
planet and therefore shows a phase, and a sun you can turn towards. Eclipses
fall out of the same term asked from two places: the moon shadowing the ground
is a solar eclipse, the planet shadowing the moon is a lunar one.All of it is authored per map in the editor's Metadata panel and stored as
sky.jsoninside the.odmap. Every field is optional, so a map written
before any of this existed loads with Earth-like defaults rather than being
refused — which matters in a format that already carries 118 files. -
Bots can be asked to act like the countries they are. A map may carry a
history.jsonsaying, for dated windows, which neighbours a power pushed into
and which it left alone. Off by default, under Settings - Experimental, and
inert unless both the switch is on and the map says something.It does not tell the AI what to do. It scales the resolver's own winnability
score, and the ordinary bar is applied to the result: a historical target gets
more tempting, a country that power did not attack gets less, and nothing is
added that the rules refuse or masked off that they allow. -
The world unrolls into the globe instead of cutting to it. F7 no longer
swaps one picture for another: the map lifts off the flat, curls, and closes
into a sphere over about seven tenths of a second, with the camera swinging
round to the longitude you end up looking at. The same vertex knows both of
its homes — where it sits on the sphere, and where it sits on a flat sheet cut
from the same texture coordinates — so the whole move is one blend between
them and costs nothing but the blend. Daylight comes back as it flattens,
because a flat map has no night side, and the cloud and atmosphere shells hold
off until the planet is round enough to wear them. Counters, labels and clicks
sit the animation out: mid-unroll a place is at neither of the two positions
those paths know how to compute, and half a second of nothing reads as part of
the move where half a second of markers in the wrong place reads as a fault. -
A button for the globe, not just a function key. Under Settings in the
right-hand column, labelled with where it will take you rather than where you
are. F7 still works; a function key is a thing you have to be told about. -
The unroll starts from where you were looking. The animation used to open
on a whole-world shot no matter how far in you were zoomed, so its first frame
was itself a jump. The sheet is now framed on a camera that reproduces the 2D
view exactly -- same centre, same zoom -- and the same is done in reverse, with
the flat camera settled on the globe's position and zoom before the sphere
starts to unroll. Asserted rather than eyeballed: the map pixels the 2D view
puts on each screen edge land on those same edges here. -
The night side is darker. The floor it sits at went from 0.34 of full
brightness to 0.18, so the terminator reads as a real edge and a planet at
night looks like one. It is not zero and should not be: the political colours
underneath still have to be readable, because the map is a working document
before it is a picture.The note explaining why it sat so high claimed a dark night side would take
unit markers with it. That was never true -- markers, counters, arrows and
country names are screen-space overlays drawn after the sphere and are not
touched by the terminator at all. Only the map dims, which is a smaller price
than the note had been charging for. -
And the dark side is still a map you can govern from. One multiplier over
the whole night side is a choice nobody wants: dark enough to look like night,
or bright enough to work in. It is a false choice, because the two halves of
the picture are not the same kind of thing. Sea, ice and empty ground are
scenery and can go as dark as looks right; a country's colour is information,
and dimming that is the only part that costs the player anything.So they are dimmed separately -- scenery to 0.18, political colouring to 0.62.
Telling them apart by c...
v1.2.0a
game 1.2.0a
-
OpenDoctrines plays inside Discord. The game is an Activity now: launch it
from the rocket button in a voice channel and it runs in the client, with no
download and no install. Whatever is deployed is what everybody in that channel
is playing, the moment it goes up.The work was mostly in one place, and it is not the obvious one. Inside an
Activity a Content Security Policy allows exactly the hosts named in the app's
URL mappings, so every request the game makes has to be rewritten to go through
Discord's proxy. What is deliberately not rewritten is the account service's
identity: a host compares the issuer named in a join ticket against its own, so
repointing it would have made an Activity player disagree with every host in
existence. The transport is diverted; the identity is not. -
You can join a game with the invite code alone. Leave the address blank and
the game goes through the account service's relay: no port to forward, no tunnel
to keep alive, and the host never learns your IP address — so the panel warning
you about that is shown only when you actually type an address.This is the only way a player in a browser or in Discord can join at all, since
a sandboxed Activity cannot open a socket to somebody's home connection whatever
they paste in. Hosts opt in with Host through the account service on the host
screen, orrelay: truein a dedicated server's config.The trade is stated plainly because it is real: a relayed game needs the account
service for the whole of its life, where a listening host only needs it to start. -
A dedicated server could never open a session, and now can. It skipped
Game::init()— correctly, because that opens an OpenGL window — and that is
also where the game's config is read and the account client is set up. So it ran
with no issuer, no token and no credential, and failed at the last step with
"Sign in and register this server before hosting": advice that could not be
followed, because the machine was signed in and this process had simply never
looked.Two more things were hiding behind that. The failure check ran once,
synchronously, before the asynchronous open could possibly have failed — so a
later refusal was never looked at again and the server sat silent for ever with
no join code and no error. And a server credential that had stopped being valid
was unrecoverable, because registration only ran when the field was empty. All
three are fixed; a failed open now says why, clears a dead credential, and exits
non-zero instead of telling a supervisor it shut down cleanly. -
Telling us how long you played, if you want to. A new Advanced setting,
off by default and off after every update. When it is on, the game sends one
message at the end of a session: how long it lasted as one of five ranges, and
whether you were on web, desktop or Android. That is the entire message — no
account, no nickname, no installation id, no device, no address.Because nothing links two reports, there is no "delete mine" and we do not
pretend to offer one; every report expires by itself after ninety days, and the
whole set can be erased on request. It also means returning players cannot be
counted, which is a real thing given up on purpose: counting them needs an
identifier, and an identifier is the thing being refused. PRIVACY.md and TERMS.md
now say all of this, including the sentence that used to promise no usage
reporting at all. -
Paste works in text fields. Ctrl+V and Cmd+V were handled, but the web build
called a desktop clipboard function that returns nothing in a browser — so in
Discord every field silently ignored a paste, including the one you put an invite
code into. The page now listens for the browser's own paste event, which needs no
permission because the keypress is the consent.Four screens had never called the shared editing code at all, so paste had never
worked in them on any platform: the admin fields, the bug report, the moderator's
note and the account lookup. They keep their own typing rules — the duration
field still refuses anything but a duration — and take the paste through one
shared path. -
The caret sits at the end of what you typed. It was worked out after the
wrapping loop, from a line buffer the loop's last pass had just cleared and a y
position it had just advanced — so it sat at the left margin, one line below the
text. Fixed once in the mail composer months ago; the fix did not travel, because
a copied loop does not inherit a later one. Three more copies had it. All four
fields now draw through one function, and the screenshot that photographs that
screen focuses a field so the caret is actually in the picture. -
The Android build reports its own version.
AndroidManifest.xmlcarried
versionCode="1"andversionName="1.0.6a"as literals while every other
platform stamped itself from theVERSIONfile — so the APK announced 1.0.6a
and every build ever made declared version code 1. That is the value every app
store refuses an update on if it does not increase, so the next upload to any
of them would have been rejected. Both are stamped at packaging time now, and the
build fails if a placeholder survives. -
The hosted web build ships the game, not just the menu. The deploy script
copied four files and leftdata/behind — 61 MB of maps, music, fonts and the
AI model that are served next to the page rather than inside the preload. Every
deploy since hosting was set up put a playable menu online with nothing behind
it. It was invisible because the host answers a missing path withindex.html
and status 200, so the game asked for a 1.2 MB map, was handed 10 KB of HTML, and
reported that the download had failed. The deploy now stagesdata/, ships a
404 page so a missing file fails as one, and checks the live URL afterwards
rather than trusting the upload. -
Mail: letters between countries, delivered when the turn resolves. A new
Mail button opens a correspondence with any country you may write to — one
thread each, read like a messenger. Write now, it arrives next turn, and until
the turn resolves it is still on your desk: you can rewrite it or tear it
up. Once delivered it is history and cannot be touched.That delay is the whole design rather than a limitation. It is the one thing a
chat box cannot offer — the chance to change your mind — and it is what makes
an AI correspondent fair, because a bot that answered instantly would out-talk
every human at the table.The button appears only when somebody could answer: on a server, or with
the language-model module loaded. In a single-player game with neither, it is
absent rather than greyed out — a greyed control invites you to work out how to
enable it, and there is nothing to enable.Hosts choose who may write: nobody, players only, advisors only, or everyone.
Players choose who may write to them, and that always wins — a host can
narrow who speaks on their server, but nothing a host permits forces a letter
on somebody who has shut their door. Hosts can also keep a list of words their
server will not carry, which is a blunt instrument and is documented as one.Map scripts can read
game.llm_diplomacyandgame.mail_enabled, so a
scenario written around bargaining with a machine can tell. -
AI advisors: countries that answer their own mail. An optional module
points the game at a language-model runner — one it installs for you, one you
already run, or a remote API with your key. Countries then write back, in your
own language, and they talk to each other as well as to you.They are allowed to lie. A diplomat who cannot is not a diplomat, so they
bluff, flatter and promise things their country will not do. What they may
never do is pretend to be human: every letter carries a bot tag, in every
language, on every screen that shows it. The lying is a game mechanic;
concealing the speaker would be a lie about the game itself.Each correspondence is sealed. The model writing as Britain to France sees
the Britain–France thread and nothing else — not Britain's other letters, not
the world state, not your orders. It is given a coarse word for relative
strength rather than a number, because a correspondent who can quote your army
size is reading the save file and every player can tell.It writes letters and nothing else. The existing AI still runs the war, the
budget and the diplomacy — that AI is measured and deterministic, and handing a
language model the levers would trade all of that away for something nobody
can bench.Installing a runner downloads and executes third-party software, so: the
version is pinned in the source, the archive is checked against a
SHA-256 compiled into the game before anything is unpacked, it comes over
HTTPS from the project's own release host, you see the URL and size before a
byte moves, and it lives under the game's data directory so uninstalling is
deleting one folder. An unpinned build refuses to install at all rather
than running something nobody verified. -
Reporting a player, and moderation. A message can be reported two ways,
and they do different things: to the server owner, who can remove that
person from their game, or to the account service, which can ban or time
out the account everywhere. An account carrying the developer badge gets a
review queue in-game, with the message, whatever context the reporter
attached, and three outcomes — ban, timeout, or dismiss.Dismissing is a real outcome. A queue whose only clearing action is a
punishment is a queue that punishes people.Bans and timeouts are announced to a Discord channel, saying *who and until
when and nothing else...
Gearbox SDK gearbox-v1.2
sdk 1.1
Gearbox 1.1. Nine new capability modules and 94 new imports, taking the ABI to
22 modules and 147 functions. Nothing in 1.0 changed.
What is new
1.0 was enough to write an overlay and nowhere near enough to write a total
conversion. There were no ships, no armies, no research, no politics, no
economy, no way to author a scenario, and a UI that could draw rectangles and
14pt text.
| Module | What it grants |
|---|---|
Military.Read |
Ships -- type, position, health, crew, range -- army stacks per province, fortification and port levels |
Military.Write |
Army moves and ship move / engage / bombard orders |
Research.Read |
The technology tree, per-country completion, funding |
Research.Write |
Set research funding |
Politics.Read |
Political compass, policies, province unrest, minorities |
Politics.Write |
Enact and cancel policies |
Economy.Read |
Gross and net income, army and navy upkeep, bankruptcy, industry level and specialisation, province resources |
Economy.Write |
Set province industry level |
MapEditor |
Read and write the open map editor project |
Expanded: UI gains lines, circles, sized text, measure_text, panel
geometry, your own images, and the accent colour. Map gains coastline,
sea routes and a land test. Neural gains the AI's decision space by name.
Net gains peer enumeration.
The two rules
Every read is bounds-checked and returns a neutral value -- 0, or an empty
string -- for an id that does not exist, rather than trapping. A mod iterating a
count that changed under it cannot crash the game.
Every write goes through the same resolver the player's own click goes
through. Nothing reaches into a container directly. An army order is still
checked for adjacency, a ship order is still clamped to range and routed around
land, a policy is still paid for and still subject to its prerequisites and the
per-turn cap. Granting Military.Write lets a mod issue orders, not fabricate
outcomes.
Reskinning
Three levers, cheapest first:
ui/set_theme_accentrestyles the whole interface in one call -- the accent
is read at over a hundred sites. It is not persisted and is dropped as soon as
no mod is running, so it cannot outlive uninstalling your mod.ui/draw_imagedraws artwork from your own.odmodand nothing else. With
lines, circles and sized text you can build an interface that looks nothing
like this one.MapEditorreaches the same per-province data the editor's own tools write,
so a generator can author a scenario in a loop instead of four thousand brush
clicks.
Still out of reach: replacing textures the game itself draws outside a mod
panel. There is no central texture registry to hook, and adding one is a
renderer change rather than an ABI change.
Compatibility
A mod built against 1.0 runs unchanged, and that is tested.
sdk/compat/abi-1.0.json freezes the 1.0 surface and tools/check_abi_compat.py
fails the build if any symbol in it is removed, re-signed, or moved to a
different capability. Within a major version the ABI may only be added to.
The existing conformance test could not have caught that on its own: it checks
that abi.json describes the host, and both files move together, so deleting a
function from both passed. All 13 shipped example mods declare
"gearbox": "1.0" and still load.
Fixed
gearbox_is_multiplayer and gearbox_is_server always lied. Both read a
field nothing ever assigned, so they answered "single player, and you are the
authority" in every session. If you wrote a mod that checked before mutating --
the correct thing to do -- you got the wrong answer every time. The same dead
field gated the manifest's "side", so a "side": "server" mod was never
masked off on a client.
There is deliberately no net/is_multiplayer import: those two already answer
it, and a second way to ask one question is worse than none.
A mod's accent colour could outlive it. It was written into the saved
config, so it survived uninstalling the mod with no way back but the reset
button. It now goes to a field the config file does not store.
The docs and the manifest warning were wrong about what happens when a mod
imports something the host does not have. It does not "trap on first call" --
an unresolved import cannot be linked, so there is no instance and no first
call. The failure is at load and it names the symbol.
Dedicated server server-v1.2.0a
The OpenDoctrines dedicated server: a console server that needs no
graphics card, no display and no X11.
Unpack it, run ./OpenDoctrinesServer, and type help.
Hosting requires a signed-in account and a registered server
credential. Run --check first to verify your config and content
without opening a session.
This is released separately from the game and on its own schedule.
v1.1.2a
game 1.1.2a
- The browser version no longer dies when you process a turn. It was
overflowing the WebAssembly stack on the very first turn — every scenario,
every country, spectator included. - Turns stop getting slower the longer you play. The save was rewritten and
recompressed whole every turn: 4.5 seconds a turn by turn 150, now under one
and flat. Quitting mid-save can no longer destroy the save either. - The map tells the truth again. Conquered provinces change colour, the
world behind a dialog is the right way up, and the mouse keeps working after
you touch the screen. - Things you can reach. Settings has a button in the sidebar, the country
finder has a way out, and UI Scale and colour-blind mode answer a click
instead of only the arrow keys. - The AI stops signing away its country for a trade that returns nothing. A
ceasefire may still cede ground — ending a losing war is worth paying for.
v1.1.1a
game 1.1.1a
- Processing a turn no longer blacks out the screen. The world stays
visible behind the popup while the turn resolves. - Armies stay out of countries at peace. The rule the land march obeys now
covers amphibious landings too, which were the way round it. - The AI stops spending itself into revolt. It was placating its minorities
without limit — a bill nothing ever scaled down — until it went bankrupt, cut
every settlement at once, and faced the rebellions that followed. Two fifths
fewer bankruptcies, a quarter fewer breakaway states, and it plays
measurably better for it.
Licence
Nothing to install: this changed in the repository, between releases, and
applies to anyone opening a pull request.
The licence is version 1.1, and contributions are assigned rather than
licensed. Clause 4 used to say a contributor keeps the copyright in their
patch; it now points at CLA.md, which assigns it to the project —
with a licence back, so a contributor keeps the right to use their own work
anywhere else, commercially included. The reason is that clause 5 lets the
licence change later, and that power reaches only rights the project holds.
Playing is unaffected, and so are mods, maps, saves and videos: clause 3 still
says those are yours, and the CLA says it again. Every commit in the repository
predates this, which is the only moment such a thing can arrive without asking
somebody to re-sign for work they already gave.
v1.1.0a
game 1.1.0a
- Twenty-one languages — interface, country names and dialogue. Arabic,
Hindi and Urdu are properly shaped, not drawn letter by letter. - Advisors and officers speak to you, drawn in your country's own colours.
- A tutorial, with a map built for it.
- The AI holds its ground when attacked. Recruits come out of a province's
population, its action cap is gone (requests are limited instead),
coalitions no longer form on the hardest difficulty, and it fortifies a
threatened border. Where the last build was reduced to almost nothing in a
world that attacks without pause, this one keeps its starting share — rules
and trained model both changed, so that is the two together. - A score you can measure yourself against.
tools/od_bench.pyplays six
fixed seats; 100 means a seat kept its ground. The AI scores 107: strong in
a peaceful world (Sweden, France 200), weak in a hostile one (China 8). - Map scripting, version 2. Conditions are real expressions now —
arithmetic,and/or, parentheses,min/max/clamp— where before a
condition was one comparison and nothing else. New:elseif,unless,
for i = 1 to N,repeat N,break,continue,print, and assignment
the C way —x++,x--,x += 1, withsetoptional. Version 1 scripts
run unchanged. Fixed: anif
with anelseran both halves. - A map can bring its own characters.
comms/cast.json, portraits and
.oddlgdialogue inside the .odmap, played withdialog <name>— the same
speaking window, live eyes and accent colouring the tutorial uses, scoped to
the map that carries them. - Fixed: map scripts never ran. Starting a new game takes the asynchronous
loading path, and only the synchronous one ever started the script engine —
so a map's scripts did nothing in ordinary play. - Scripts can find a country without knowing its code. A generated world
hands out codes the mapmaker never sees, soforeach country in world,
country.of_province.42,country.largestandcountry.playername one by
what it is instead. Maps can also switch rules off:
set rules.rebellions false. - Scripts can ask what mods are installed.
if mod.com.example.extra, and
.version/.namealongside it, so a map can offer something extra without
requiring it. Alsotry/catch,label/jump/stop, andspawnfor
running several flows in one script — cooperative, not threaded, so it
behaves the same in a browser. - Scripts can be edited as blocks. A Text/Blocks toggle in the map
editor's script IDE: drag statements around, add them from a palette, and
switch back. The two views are the same file: statements, comments and blank
lines all survive the trip, and only decorative indentation is re-aligned to
the nesting the blocks show. - Greater Diplomacy 5 translation. Exporting a map in the browser no
longer loses large worlds: the download link was being released in the same
tick it was clicked, before the browser had finished reading it. - Stability and assorted bug fixes.