Open csinkers opened 2 years ago
Hi Cam. Thanks for your valuable input. At the moment I decode every single map event bit of Ambermoon. I am almost done with it.
The branching of the condition/query event is known. The same is possible with the dice roll event (0xF) and the question/decision popup event (0x13). Moreover the door and chest events (0x2 and 0x3) can specify an event index for the "unlock failed" situation. It is used when lockpicking or trap disarming fails.
So in total there are 5 possible branching event types in Ambermoon.
Condition/query event sub-type 9 stands for "last event result". This way you can branch dependent on won battles, correct riddlemouth solutions or fully looted treasures.
The scripting language looks nice. I wrote a small console app to edit the event chains of maps and NPC of Ambermoon. But as you mentioned it can get quite difficult to understand if branching is involved.
I will have a closer look at the NPC flags too. But Albion uses an extended version of the data structures of Ambermoon, so this might be specific to Albion.
Hey, thought I'd put some notes here based on my investigations into Albion, hopefully they'll be useful.
NpcFlags
Bit 6 of the NPC flags in Albion acts as 'noclip', i.e. NPCs ignore walls. Maybe one of the unknown Ambermoon flags does the same.
Also, in Albion there seems to be a concept of "version 1" and "version 2" NPC data. Which one is used depends on a map flag (0x2000, if the first two bytes of the map data are interpreted as a 16-bit flags enum). Might be a candidate for the unknown flag 5 that's always set in Ambermoon (all maps except some unused test maps in Albion have 0x2000 set).
In Albion, when the flag is not set (only test maps + my own investigations):
When it is set (every real map):
Map Events
These aren't actually in a linked list structure. Because 'Query' events (the name "Query" comes from debug strings left in the Albion exe, in this repo they're referred to as Condition events, type number 0xD) will jump to the two-byte event index at offset 0x6 when the condition is false and to the index stored at offset 0x8 when true, the appropriate data structure is actually a directed graph of maximal out-degree 2. Because of this, any arbitrary programming control flow structure can be represented, making things much more complicated.
To make Albion modding easier I've been working on a higher-level scripting language for these events, along with a compiler and decompiler so the directed graphs encoded in the map and event-set data can be converted into nice human-readable scripts (and back again). The language itself is quite simple: there's no type system, it's really just some control flow structures put on top of the Albion script events, things like if-else, while, goto etc. As an example here's a decompiled Albion script involving conditions and looping:
The code for the scripting language compiler & decompiler is in https://github.com/csinkers/ualbion/tree/master/src/Scripting - perhaps it could be useful for Ambermoon as well. The only dependencies of the scripting sub-project are Superpower (a C# parsing library) and a couple of fairly simple types in the UAlbion.Api project (IEvent, IEventNode, IBranchNode are the main ones), so it might be feasible to fork it and replicate the required interfaces from UAlbion.Api, although it does also require having a system for reliably round-tripping events between binary and text forms, and that the text form of the events adhere to certain conditions.
In its current state it can't handle every possible control flow structure, but it does handle all the ones that occur in Albion's data files, so hopefully it would be general enough.
I'll keep an eye out for any other correspondences between data structures, but it looks like most of the parts that are the same in the two games have already been decoded, and the unknown parts don't line up.