Modding Questions
Hi all,
I played T2 extensively many years ago now, under a different username. Base game, Renegades mod (AFO and Epoch clans), and especially Construction. Construction definitely helped me get started in my career of coding. Periodically I've been checking back in to see what's new and play a few CTF rounds here and there. I'm glad the servers are still up and it's amazing that Krash has been hard at work making the game even better.
I'm on sabbatical from my work right now, and decided to go back and mess around with some modding again for fun. I remember a few things from way back when, and other things I've been figuring out along the way, and overall it has been a tad easier now that I have more programming experience.
I had a few general/ workflow questions for those who have done a lot of T2 modding though. I thought I might ask here, since I know there's some people still active. Any help is appreciated.
1) Is there a programmatic way to enumerate all the .dts shapes and .wav files? I got a list of .dts files using grep/strings, but the same didn't seem to work the same for .wav. More generally, is there a good way to cycle through them to figure out which might fit for a given $widget? I vaguely remember way back, that when making a simple custom gun, I was cycling through audio clips somehow to see what would fit for the spinup state, but I don't remember how I was doing that.
2) In a similar vein, is there a python dir() equivalent for TGEA scripting? I know the console has tab complete which helps, just not sure how that is implemented and if I could do that from .cs scripts. Sometimes it's a roundabout process to get a handle to what I'm trying to inspect from the console.
3) What is the workflow people use to test datablock changes? For one example, I'm looking to speed up the iteration process for trying to make a new gun that's composed of multiple ShapeBaseImageData definitions, so to make sure the layered images look right. As another example, tweaking particle colors. I've realized that for some tweaks, like a shotgun's projectiles, it's easiest if the definition is in an e.g. onFire function, because those reload easily and cleanly. I'm curious if there's a comparable mechanism for datablocks that I'm missing. I have a feeling I need to just make a handful of helpful scripted primitives for the particle color example.
4) I remember Thyth and/ or Linker having a script that could copy another player's construction build pieces. Has anyone attempted to use that sort of technique to build a custom map with e.g. a space ship pre-built in it? I remember blnukem had some combat construction mod (CCM) scenarios, and one involved leading a team assault against a freighter (which honestly I can't remember if that was a construction build or not). I just don't know if he/ his team baked that into a map itself or just loaded it manually from build save. I would bet the latter if it was construction. In any case, if someone hypothetically could bring a construction build into a map, would that map then require download ahead of time, or could it still be loadable by everyone like e.g. Flatdash?
-Arg0

Comments
1: You can query the resource manager's file list through script using
findFirstFile(pattern),findNextFile(pattern)andgetFileCount(pattern). In the patched version, you can use multiple*wildcards, and multiple?single unknown character matches, but in the standard version it's best to just use a single wildcard, i.e.audio/*.wavas your search pattern. The most common way you'll see this used in for enumeration isfor (%file = findFirstFile(%pattern); %file !$= ""; %file = findNextFile(%pattern))and a suuuuper basic way you'd use this to dump a list or cycle through waves could look something like:function findWaves() { %fo = new FileObject(); %fo.openForWrite("wave.txt"); $CurrentWave = 0; $TotalWaves = 0; %search = "*.wav"; for (%wav = findFirstFile(%search); %wav !$= ""; %wav = findNextFile(%search)) { %wavStart = strstr(%wav, "audio/"); if (%wavStart != -1) %wav = getSubStr(%wav, %wavStart + 6, 1000); // if (alxGetWaveLen(%wav) == 0) // continue; %fo.writeLine($TotalWaves TAB %wav); $Wave[$TotalWaves] = %wav; $TotalWaves++; } %fo.close(); %fo.delete(); GlobalActionMap.bind("keyboard", "ctrl ;", "playNextWave"); } $WaveHandle = ""; function playNextWave(%keydown) { if (!%keydown) return; alxStop($WaveHandle); %wav = $Wave[$CurrentWave]; $WaveHandle = alxCreateSource(AudioChat, %wav); alxPlay($WaveHandle); echo("Playing wave file [" @ $CurrentWave @ "/" @ $TotalWaves - 1 @ "]" SPC %wav); if ($CurrentWave++ >= $TotalWaves) $CurrentWave = 0; }Of course, the utility in playing thousands of waves in sequence is a little limiting, so it'd be easier if they were in a GUI element or just played externally.
2:
%obj.dump();will list the fields and methods attached to an object in the console3: If you want to change datablocks while ingame, the server needs to run
%client.transmitDataBlocks(0);after every change to reset the entire set on any client you want to see these changes. You'll also want to make sure that this is avoided if the previous transmit hasn't completed, avoid changing the number of datablocks while running if you can, and to that point avoid referencing any datablock that the client wasn't made aware exists at an earlier point (they'll almost certainly crash if for example you created a new projectile datablock and fired it before they've been sent that datablock).4: Construction mod save files are essentially just scripts with a list of commands to create a set of static shapes and assign certain properties. The CCM/ACCM servers were typically actively managed by the admins with normal save files and a period of setup and chat commands run to keep events going. They weren't baked into the maps because you could set up multiple scenarios or just wipe and reset everything by loading a save at any point.
The saves do have Construction-specific datablocks and function calls included, but generally speaking for a simple building using beams/pads there's nothing that'd prevent them from being included in any mission file, and they don't have to use any content that requires a download. There have been a few mods that've used Construction saves for one thing or another. It'd be a little more advanced to parse the save and convert them into interior blocks to be fully static and cast shadows, but that's also been done in the past.
Perfect, thanks! Added a keybind to trigger datablock transmission; much easier to iterate now
4: With the clarity of sleep, I do think loading from a save is the better option for what I was envisioning. Still interesting and good to know that it is feasible though.
I neglected to mention, but for dts shapes, particularly since you mentioned mounting images, you should look for Cowboy's old dtsview script, which provides a gui to preview the shapes and basic animations. It won't always work perfectly when dealing with things like transparencies or animation sequences that are handled differently, but for considering general item shapes that mount well, it should work great.
I was able to track down a copy, looks like it'll be quite handy, thanks!
I remembered/ realized as well that I should look inside some of the .vl2 zips. That's helped as well for exploring some of the base .cs files and .wavs. Just mentioning if there's others lurking now or later.
Is it possible to essentially
setMuzzleVector(...)on a player? I had a thought/ curiosity of how to make `stateRecoil` work beyond just the animation, but I'm not finding any way to change where (non-AI) players are looking. Closest issetTransform(), which logically only changes the player's side-to-side looking. This is largely inconsequential to what I was working on, this is a tangent, but curious if there's a technique I'm missing.Not in a reliable way that'd present well to a remote client for use as a recoil effect, or at least not a sane one that comes to mind, but I might remember something later when I have the code in front of me.
One thing I do recall putting in a mod many years ago was a harmless explosion/shockwave to "juice" up the shot a bit, only wide enough to affect the player firing the shot, with the camera shake parameters set up to make it feel meaty. It was far from a perfect solution, but it made for an interesting effect.
The reason setTransform isn't applicable here (but would be fine on things like cameras or vehicles) is the player object has three separate relevant rotation components: the body, the arm/weapon mount point, and the head/eye. The body, in normal circumstances, only allows controlling rotation around the central Z (up) axis, and this is what you have access to with setTransform. The arm shares the body's Z axis rotation, but itself rotates around the X axis based on the input submitted by the client every move, the pitch approximately matching (within reason) what the client is simulating locally. The head, while normally facing the same direction as your muzzle, has fully independent (but low precision) control on both X and Z, which you can observe by going into third-person and holding the free look key. Adding a new setMuzzleVector method to servers would require readjusting at least the former two and sending an update to the client to force them to use the new data, which isn't an outright impossibility, but the latency delay and interruption to their local simulation could be too janky for active use.
See, trouble with the server communicating updates about the client's player object is that the active control object is the main thing in the game where any difference between the local and remote simulations is usually going to result in a very noticeable warp: the server forcing an transform update on a player would unceremoniously jerk it into place, and the delay involved in the round trip would probably create for a pretty unpleasant experience -- the client will have already moved their mouse in that time, and there's nothing built-in for the server to indicate a relative change and synchronize it. To be done smoothly it'd need to modify and interpolate the pitch/yaw on the client directly and trigger on a local weapon state transition, rather than waiting for a signal from the server, but obviously that'd require some additional changes to the client.
A very silly experiment to try could be to give a bot control of the player object long enough to run some aiming commands before switching control back to the client... I wouldn't recommend doing it in anything real, though.