The big .thn/.lua research thread
-
I’ve been meaning to get around to document the Freelancer .thn files and what you can actually do with them for a while. I wrote some (fairly bad) tutorials about it back in the day with a lot of information that I’ve now figured out isn’t quite right and I’d like to clear things up a bit, and also share what I’ve worked out since then.
I’ll post to this thread as I work stuff out, along with an index in the OP if it gets cluttered to make referencing stuff easier. If anyone has any corrections or additions, please please post them here.
First off, some key things:
-
.thn is lua. More specifically, it’s lua 3.2. I originally figured this out by pulling a strings list out of thorn.dll (Attached to this post for anyone who’s interested), but how the scripts behave and what works has only really confirmed this.
-
lua written normally in scripts functions normally. For example, you can call things like system time, set global variables, and even read and write to your own files from outside the game (a working example of all three of these things can be found here)
-
You can call any entity, effect or object defined in the following files:
petaldb.ini
CHARACTERS\bodyparts.ini
EQUIPMENT\misc_equip.ini
EQUIPMENT\prop_equip.ini
EQUIPMENT\st_equip.ini
EQUIPMENT\weapon_equip.ini
FX\effects.ini
FX\ENGINES\engines_ale.ini
FX\EQUIPMENT\equipment_ale.ini
FX\EXPLOSIONS\explosions_ale.ini
FX\MISC\misc_ale.ini
FX\SPACE\space_ale.ini
FX\WEAPONS\weapons_ale.ini
SHIPS\shiparch.ini
SHIPS\rtc_shiparch.ini
(You can actually attach loadouts from loadouts.ini, loadouts_special.ini and loadouts_utility.ini to ships with a line under userprops, but more on that later)
SOLAR\solararch.iniIf you want to call an effect, you will need to initiate it with a START_PSYS events entry (more on that later)
-
I’ve been using this version of FLED-Thorn and the included .bat file to decompile and clean up the scripts when I need to get to the ingame ones. (Many thanks to Adoxa for this)
I’d like to start the thread off by dissecting a few basic entries from a really basic custom script from the alternate menu screen pack I made as a template for folks. The full pack can be found here, but I’ve attached the script in question to this post:
{ entity_name = "scene_seto", --How the entity is referred to in the script type = SCENE, --The entity type.* template_name = "", --The item model or reference defined from one of the .inis mentioned above lt_grp = 0, --Objects will only be lit by lighting in the same lt_grp srt_grp = 0, --This relates to how backdrops merge and overlap, but not 100% on it yet. Jeider from Nomad legacy has a better understanding of this than me usr_flg = 0, --No idea what user_flg does yet spatialprops = { pos = { 0, 0, 0 }, --Position of the object orient = --Orientation of the object in a 3d matrix. { { 1, 0, 0 }, { 0, 1, 0 }, { 0, 0, 1 } } }, up = Y_AXIS, --Defines what ‘up’ for the object is. front = Z_AXIS, --Defines the default direction the object will face. Ambient = { 128, 128, 128 } --Ambient lighting. Numbers are RBG values. As far as I know this is only applicable for SCENE entities },
Things I don’t yet understand the fuction of here are srt_grp and usr_flg.
*Valid options are UNDEFINED_EVENT, PSYS, DELETED, MOTION_PATH, SUB_SCENE, SCENE, HARDPOINT, MARKER, SOUND, LIGHT, MONITOR, CAMERA, DEFORMABLE, COMPOUND and UNKNOWN_ENTITY. Some of these are self-explanatory, but I’m not 100 percent on all of them just yet, notably MONITOR and SUB_SCENE.
Next I’d like to take a quick look at the cameraprops entity for Camera_0
cameraprops = { fovh = 30, --Field of view hvaspect = 1.333333, --Horizontal-vertical aspect ratio(?) nearplane = 1, --Closest the camera will render things farplane = 1000000 –Furthest away the camera will render things }
I mostly understand these, but I wouldn’t mind some confirmation on fovh and hvaspect regarding what exactly they alter as it’s not entirely clear.
After this is a monitor entity. I’m still not 100% certain what these are for. There are events present in many scripts that set monitors as cameras, but changing or removing these entries doesn’t seem to have an effect. If anyone has worked these out, please let me know!
I’m going to break this initial breakdown into a couple of posts. Lighting needs an entire section to itself I think and I’d like to try and keep this relatively neat and avoid rambling if I can.
-
-
Here is unfinished doc I was writing on THN stuff. If you want to continue from where I left off feel free to request writing rights.
-
This is an absolutely brilliant document that has demystified a whole bunch of stuff for me! Thank you so much for sharing it. Going to change the format of what I had planned a bit as you’ve covered pretty much everything I wanted to + the bits I didn’t really understand.
A few questions I still have though:
What do the values in diffuse represent? is it just an RGB value, or is it a bit more complicated than that? (Sorry if this is more of a general 3d lighting question)
I’m still not entirely clear what MONITOR does. Is it actually required to switch cameras, or is it okay to use a marker entity for this?
I’ve run into an issue, particularly in menu screens, where using running_lights = true under userprops results in some odd visuals (boxes around the lights). Have you had any luck getting this particular line to work properly? (Screenshot attached.)
-
Glad to be of help. Just keep in mind while most information here is correct a few things aren’t, I simply never got around to correct them or to continue writing it.
Monitor is meta-object representing your viewport, your screen to put it simple. Switching between cameras is event of SET_CAMERA. You should have only one monitor object in scene.
Pretty much every color there is going to be RGB, however THN is inconsistent when it comes type of values: integers ranging from 0 to 255 or normalized floats from 0.0 to 1.0. Some properties expect integer, others expect float.
Regarding lights it’s just a case of textures not loaded for some reason, so it defaults to plain white color which is then tinted to whatever sprite had for diffuse color.
-
Ah, that makes sense. I’ll play around a bit with diffuse and see what I can get out of it.
Regarding the lights, I assume there isn’t a fix for this. They were never intended to be loaded into the menu screens. I’m going to guess that getting the textures to load in correctly would probably involve an exe hack?
Also I noted in your document you suggested to use 3d modelling software to draw the paths and then import them. Which software were you using for this? Any recommendations?
-
There is a simpler fix for lights textures, you just need a model to embed texture used by those light sprites (matching name too) and push it into scene so it’ll load the resources and display those sprites.
Regarding paths I’d normally just make them in 3Ds MAX. But there isn’t a readily available solution I’m afraid, so it’s not like you can install it, make paths and grab em from there. For my own uses I’ve made a script to export spline knots and capture rotation of a dummy object to THN path key points.
-
FYI I’m attempting (with mild success) to provide an implementation of thn with a player of sorts in Librelancer (https://github.com/Librelancer/Librelancer) but no arbitrary code execution there lol. When it’s done I’ll be able to publish some more thorough docs since I’ll have code to base it off.
RE your lights problem, you could try and make a tiiiny model that references the textures in the txm which would get the game to load it. (e.g. a solar that references efx.txm , and DcDt mats that use “bulb” and “shine”). I haven’t tried this though so YMMV
-
That’s a pretty good idea! I’ll have a go and report back
Honestly it might be worth leaving the arbitrary code in just because it does actually allow you to do some interesting things with variables and makes scripts much easier to customize on the fly.
Something else I’ve run into:
It seems that when loading base/planet scenes, the events section for any ‘hardpoint’ scripts will not execute at all. Unfortunately this prevents the use of ATTACH events in bases. I’ve not yet found a way around this other than putting the models I want to attach things to in the ‘ambient’ scripts.
-
Tried to run thn server-side. Won’t work.
But music from client-side is workingAct_AddAmbient = scripts\Bases\Li_01_Equipment_ambi_s005_music.thn, Equipment, Li01_01_Base ```See https://the-starport.net/freelancer/forum/viewtopic.php?post_id=32782#forumpost32782
-
@Treewyrm Bit late but I’m finally requesting edit access. I’ve started doing menu screens again and figured it would be good to note down what I figure out/remember as I go. I have a few bits on sparam ready to go already, so I’ll add those when you approve it.
-
So, I’m having a very minor issue with my START_MOTION entry:
{ 0, START_MOTION, { "lpi_sugarland_mplatform_1" }, { animation="Sc_rotate living quarters", duration=3600, time_scale=1, weight=1, heading=-1, event_flags=2 } },
The animation (the rotating part for [c]manufacturing_platform_lod.cmp[/c] ) ‘jumps’ inconsistently every now and then. As it’s just a simple rotating part, and it loops for the entire duration of the scene, I’m not really sure what to do to get it fixed. This is the first time I’ve really played with START_MOTION in a scene. Any ideas?
-
How animation plays out depends on several factors. If animation script contains maps for more than one part and each map has different durations the animation will go out of sync as each part will have its animation looped independently. Type of looping is defined by event_flags property (restart, reverse, ping-pong, etc).
-
I assume event_flags=2 is restart. I’ve played around with it and have seen the animation reverse and play once, but it won’t loop smoothly.
You can see the behavior demonstrated here (Around 00:26):
-
Quite frankly I just don’t see what’s wrong there, probably because whatever is supposed to be incorrect is just too far and youtube quality makes it all kinda hard to spot anyway.
Either way it’s all about how map keyframes are set and whether they are looped or not. Some vanilla models have that and for full 360 rotation they have to make a quick jump as maximum rotation between two keyframes is 180 degrees. This jump can become noticable if there’s time delta between its keyframes.
-
Found a fix! Jeider on the Freelancer Discord provided some alternate keyframes for [c]manufacturing_platform_lod.cmp[/c] and that fixed the issue.
For anyone running into a similar problem in the future, they can be found below:
0 0 2 2 4 4 6 0
https://media.discordapp.net/attachments/638985274122174464/692105622124101762/unknown.png
-
That’s kinda stupid. For a revolute part (which quarters_lod1 is) the values that go there are pairs of timestamp (in seconds) and rotation angle (in radians). The ones you list are going to create partial spin back and forth rather than continious one.
Assuming you want to do full 360 spin in 10 seconds the solution is:
0.0 <- Frame 1 timestamp: 0 seconds
0.0 <- Frame 1 value: 0 degrees
5.0 <- Frame 2 timestamp: 5 seconds
3.14159 <- Frame 2 value: 180 degrees
5.0 <- Frame 3 timestamp: 5 seconds
-3.14159 <- Frame 3 value: -180 degrees
10.0 <- Frame 4 timestamp: 10 seconds
0.0 <- Frame 4 value: 0 degreesNotice the doubled keyframe at half duration, a flip occurs there to continue rotation into a loop.
Flip signs at values of mid-keyframes if you want to rotate the other direction. Naturally your Rev part must have min/max angles set to -360 and 360 degrees respectively.
-
That’s kinda stupid.
Because this is my solution. Yes, I did that many times ago and this (by strange reason) really working.
Anyway - your solution isn’t best.
This is best solution:
0 -6,283185 20 6,283185 ```Don't forget about set Frames = 2 By strange reason my version working only with 6,xx values. With 3,xx values this doen't work.
-
Does anyone have a better guide to the lightprops section handy?
I’ve been working off the guide but i’m having trouble working out what does what, and in what format. From a lot of my own testing it appears [c]cutoff[/c] and [c]range[/c] don’t seem to do anything (but apparently they do?) and I don’t really have much of a handle on how any of the parameters should look/be adjusted aside from [c]diffuse[/c] which is fortunately quite obvious.
The end goal is to use the [c]date()[/c] function to adjust the relevant parameters based on the time and parse them into the lightprops table so it’s fairly easy to create working day/night scripts for planets. I’m just not sure what to adjust!
If anyone could help out I’d really appreciate it.
-
Just jumping back to answer my own question for reference! Callum in the Librelancer Discord server kindly provided a structure that lightprops in thorn scenes seems to follow. It can be found here:
https://docs.microsoft.com/en-us/windows/win32/direct3d9/d3dlight9