-
Notifications
You must be signed in to change notification settings - Fork 3
Description
Redguard stores all (?) its map scripts in the RGM files.
Current state 22-7:
- script data can be read
- script logic can be ran with most "do-things" stubbed out
- task/function/multitask system that can run tasks on the object containing the scripts: implemented!
- tasks are asynchronous, block further processing
- functions are synchronous, should return immediately
- multitasks are tasks ran in parallel to the main task, are not blocking
- family/group system implemented
- run task on named objects
- implement dictionary for objects; me, player and camera (a.o) are special-cases and should be accessible from any script.
TODO: - doublecheck script interpreter, might be missing/faulty/stubbed parts. Can be done on-the-go
- known issue: string parameters dont work, they output as integers
- SOUPDEF tasks:
- ongoing, progress tracked on github projects
- for each task:
- decode what the parameters are
- implement behavior
- known missing systems:
- sound system
- camera system
- player controller
- adjustments to unity lights system
- shaders to switch textures
BACKLOG:
- we have functions that look like they could be used to write a character controller (key inputs, Walk* functions) but they are unused in the RGMs, are they hiding somewhere else?
- do tasks with coroutines so they dont block main thread
QRD on map scripts:
-
RGM files
The RGM files are binary files that contain a bunch of level data for each level in redguard.
Think static objects, scripted objects, animation stuff, even pathfinding node maps.
More interestingly, the actual game logic for in-map objects is also encoded in these files. Think a lever being pulled doing something or a light turning on when you stand on a platform, that kinda stuff. -
The scripts themselves
The map scripts are in some binary format that has been (mostly) decoded already, turns out its written in a pretty simple language, you get if-statements, gotos, doing arithmetic on map-global and script-local variables and most importantly, calling functions that make the in-game objects do something. This last point is the biggest missing puzzle piece in making RG unity playable. -
SOUPDEFS
When the map script calls a function, it just refers to a number eg "function 05 with parameters 37,37". This would be hell to figure out, but luckily someone at bethesda decided to ship the SOUP386.DEF file with the game.
This file contains plaintext names for all functions, map-global variables and script-local attributes. So instead of calling function 1 with parameter map variable 2, we know we want to call sRotate with as parameter "after_catacombs". -
Where we're at with RGU
In RGU, we can currently read the RGM files (a.o.) and parse them into C# classes to be used in unity.
We can put the map objects in-engine in the correct positions, and link scripts to them by name.
The scripts can be read and the control logic seems to work (mostly, i run into bugs there now and then), and there is a mechanism in place to call functions on the script objects.
The only remaining work is implementing the actual functions and any systems they connect to, like the dialog systems (eg menuAddItem), player controller stuff (eg PlayerLineUp), camera system (eg showObj).
There are 367 functions in the SOUPDEF, i got 12 mostly working with some bugs, and about 50 that have their parameters deciphered (mostly the ones that take 0 parameters :D).
- Quick orientation in the code
I have been working on the observatory map in unity, and slowly implementing the game objects in there. This is in the Assets/Scenes/GameTest scene.
Looking at commit 4fdcfca, the implementation of the Light and LightOff functions, we can see the following:
- LoadWorld.cs:
This is my test world loader, all scriptedObjects are in a big dictionary in ModelLoader.scriptedObjects, they have a unique ID read in from the RGM file. Scripts are default disabled, I turn them on for objects I'm currently working on, otherwise they spam the log with errors.
You can also see the scriptstore global flag 195 being set, this maps to OB_TelH, the position the telescope in the observatory. - RGScriptedObject.cs:
This is the big ScriptedObject class, attached to each scripted map object.
This contains an array of 367 function pointers, these will be called from the script. By default they map to the ones defined in soupdeffcn_nimpl, which just throws an error and returns 0.
After implementing a function, you put it in the array and it will get called from the scripts, as you can see happen with the Light and LightOff functions.
In the Light function, you can see I need to add a new unity object for the actual light, because it turns out the light can have a different position from the object itself, fun stuff. - soupdeffcn_nimpl.cs:
This file contains all functions in the soupdef stubbed out. They throw an error printing the name of the function and the values of the parameters, useful for debugging.
I also put comments in there if I figure out what the parameters do and what I think the function should do. - rgmtst and scriptstst.cs
These are just small scripts I use to investigate stuff without having to be in unity, they are very useful sometimes but they need to be compiled separately, and I have no idea if this works for anyone else.
- Additional details
There are 3 types of functions in the soupdef, tasks, functions and multitasks
Tasks take some time to complete and will block the scripts until they are complete, eg a moving platform will not do anything else until the move is complete.
Functions return immediately, eg the playerstand function returns either 1 or 0 if the player is standing on something, no waiting involved
Multitasks are tasks that can run in parallel, eg one of the observatory objects rotates while playing a sound.
A scripted object will run the script until a task is called, then it will block further execution until this task completes. Multitasks will run without blocking the script. Multitasks are usually blocked by a call to WaitOnTasks() later in the script to sync back to the script.
There are examples of tasks and functions already implemented, multitasks are just tasks with extra steps, and are also already implemented.
A script can call functions (or tasks or multitasks) on another object in the map. The same logic with functions/tasks/multitasks applies there, just on a different object. The objects that can be called from another object's script have names attached to them in the RGM (with exceptions to player, camera and self, which are a bit special), this is all taken care of in the ObjectStore and should just work ;).